From fbafc5b509e311efee064bbd12396a2e207f3393 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Fri, 16 Sep 2016 00:00:28 +0200 Subject: 39332: support ksh's [[ -v varname ]] condition for checking if variables are set --- ChangeLog | 4 ++++ Completion/Zsh/Context/_condition | 3 +++ Doc/Zsh/cond.yo | 3 +++ Src/cond.c | 2 ++ Src/params.c | 30 ++++++++++++++++++++++++++++++ Src/parse.c | 4 ++-- Test/C02cond.ztst | 19 +++++++++++++++++++ 7 files changed, 63 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1c749ae64..f54d78ca3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2016-09-15 Oliver Kiddle + * 39332: Doc/Zsh/cond.yo, Src/cond.c, Src/params.c, Src/parse.c, + Completion/Zsh/Context/_condition, Test/C02cond.ztst: support + ksh's [[ -v varname ]] condition for checking if variables are set + * unposted: Src/parse.c: remove duplicated assignment 2016-09-14 Peter Stephenson diff --git a/Completion/Zsh/Context/_condition b/Completion/Zsh/Context/_condition index 6f5e601f0..0285911ac 100644 --- a/Completion/Zsh/Context/_condition +++ b/Completion/Zsh/Context/_condition @@ -8,6 +8,8 @@ elif [[ "$prev" = -([a-hkprsuwxLOGSN]|[no]t|ef) ]]; then _tags -C "$prev" files && _files elif [[ "$prev" = -t ]]; then _file_descriptors +elif [[ "$prev" = -v ]]; then + _parameters -r "\= \t\n\[\-" else if [[ "$PREFIX" = -* ]] || ! zstyle -T ":completion:${curcontext}:options" prefix-needed; then @@ -30,6 +32,7 @@ else -s:non-empty\ file -t:terminal\ file\ descriptor -u:setuid\ bit + -v:set\ variable -w:writable\ file -x:executable\ file -z:empty\ string diff --git a/Doc/Zsh/cond.yo b/Doc/Zsh/cond.yo index 474baa1ec..e08fc0d36 100644 --- a/Doc/Zsh/cond.yo +++ b/Doc/Zsh/cond.yo @@ -63,6 +63,9 @@ is open and associated with a terminal device. item(tt(-u) var(file))( true if var(file) exists and has its setuid bit set. ) +item(tt(-v) var(varname))( +true if shell variable var(varname) is set. +) item(tt(-w) var(file))( true if var(file) exists and is writable by current process. ) diff --git a/Src/cond.c b/Src/cond.c index f25ebd4a3..42e9de30f 100644 --- a/Src/cond.c +++ b/Src/cond.c @@ -351,6 +351,8 @@ evalcond(Estate state, char *fromtest) return (!S_ISSOCK(dostat(left))); case 'u': return (!(dostat(left) & S_ISUID)); + case 'v': + return (!issetvar(left)); case 'w': return (!doaccess(left, W_OK)); case 'x': diff --git a/Src/params.c b/Src/params.c index 842b2f0d1..384c30a1a 100644 --- a/Src/params.c +++ b/Src/params.c @@ -626,6 +626,36 @@ getvaluearr(Value v) return NULL; } +/* Return whether the variable is set * + * checks that array slices are within range * + * used for [[ -v ... ]] condition test */ + +/**/ +int +issetvar(char *name) +{ + struct value vbuf; + Value v; + int slice; + char **arr; + + if (!(v = getvalue(&vbuf, &name, 1)) || *name) + return 0; /* no value or more chars after the variable name */ + if (v->isarr & ~SCANPM_ARRONLY) + return v->end > 1; /* for extracted elements, end gives us a count */ + + slice = v->start != 0 || v->end != -1; + if (PM_TYPE(v->pm->node.flags) != PM_ARRAY || !slice) + return !slice && !(v->pm->node.flags & PM_UNSET); + + if (!v->end) /* empty array slice */ + return 0; + /* get the array and check end is within range */ + if (!(arr = getvaluearr(v))) + return 0; + return arrlen_ge(arr, v->end < 0 ? - v->end : v->end); +} + /* * Split environment string into (name, value) pair. * this is used to avoid in-place editing of environment table diff --git a/Src/parse.c b/Src/parse.c index d3c418184..24b8cd97f 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -2385,7 +2385,7 @@ par_cond_2(void) s1 = tokstr; dble = (s1 && *s1 == '-' && (!n_testargs - || strspn(s1+1, "abcdefghknoprstuwxzLONGS") == 1) + || strspn(s1+1, "abcdefghknoprstuvwxzLONGS") == 1) && !s1[2]); if (tok != STRING) { /* Check first argument for [[ STRING ]] re-interpretation */ @@ -2464,7 +2464,7 @@ par_cond_double(char *a, char *b) { if (a[0] != '-' || !a[1]) COND_ERROR("parse error: condition expected: %s", a); - else if (!a[2] && strspn(a+1, "abcdefgknoprstuwxzhLONGS") == 1) { + else if (!a[2] && strspn(a+1, "abcdefgknoprstuvwxzhLONGS") == 1) { ecadd(WCB_COND(a[1], 0)); ecstr(b); } else { diff --git a/Test/C02cond.ztst b/Test/C02cond.ztst index 78e644a72..40e4dfb0b 100644 --- a/Test/C02cond.ztst +++ b/Test/C02cond.ztst @@ -269,6 +269,25 @@ F:Failures in these cases do not indicate a problem in the shell. 0:-nt shouldn't abort on non-existent files >status = 1 + str='string' empty='' + [[ -v IFS && -v str && -v empty && ! -v str[3] && ! -v not_a_variable ]] +0:-v cond + + arr=( 1 2 3 4 ) empty=() + [[ -v arr && -v arr[1,4] && -v arr[1] && -v arr[4] && -v arr[-4] && + -v arr[(i)3] && ! -v arr[(i)x] && + ! -v arr[0] && ! -v arr[5] && ! -v arr[-5] && ! -v arr[2][1] && + ! -v arr[3]extra && -v empty && ! -v empty[1] ]] +0:-v cond with array + + typeset -A assoc=( key val num 4 ) + [[ -v assoc && -v assoc[key] && -v assoc[(i)*] && -v assoc[(I)*] && + ! -v assoc[x] && ! -v assoc[key][1] ]] +0:-v cond with association + + () { [[ -v 0 && -v 1 && -v 2 && ! -v 3 ]] } arg '' +0:-v cond with positional parameters + # core dumps on failure if zmodload zsh/regex 2>/dev/null; then echo >regex_test.sh 'if [[ $# = 1 ]]; then -- cgit 1.4.1