From 2ef3dff65a9fc0bc69446374473ad08e6fff4755 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Mon, 11 Sep 2017 10:12:17 +0100 Subject: 41668: New --emulate option on invocation. This sets the shell emulation mode similarly to ARGV0=... which doesn't work from other shells. Note that this gives more comprehensive emulation than running emulate within the shell. --- Src/builtin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Src/builtin.c') diff --git a/Src/builtin.c b/Src/builtin.c index 2e72ba20a..0c2a62a88 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -5905,7 +5905,7 @@ bin_emulate(char *nam, char **argv, Options ops, UNUSED(int func)) savehackchar = keyboardhackchar; emulate(shname, opt_R, &new_emulation, new_opts); optlist = newlinklist(); - if (parseopts(nam, &argv, new_opts, &cmd, optlist)) { + if (parseopts(nam, &argv, new_opts, &cmd, optlist, 0)) { ret = 1; goto restore; } -- cgit v1.2.3 From 54b395844030342213cacba4c569a6c5e6781c46 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Wed, 13 Sep 2017 20:54:00 +0100 Subject: First go at var=([key]=value) syntax. Works for both normal and typeset case, also var+=... Still to do: allow to be mixed with straight array assignment, improve typeset -p, implement [key]+=value. --- ChangeLog | 8 +++++ Doc/Zsh/params.yo | 27 +++++++++++++++- Src/builtin.c | 70 +++++++++++++++++++++++++++------------- Src/exec.c | 86 ++++++++++++++++++++++++++++++++++++++++++++------ Src/params.c | 66 ++++++++++++++++++++++++++++++++++++++ Src/zsh.h | 17 ++++++++-- Test/B02typeset.ztst | 55 ++++++++++++++++++++++++++++++++ Test/D04parameter.ztst | 68 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 363 insertions(+), 34 deletions(-) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index 3d0982b24..15e331c90 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2017-09-13 Peter Stephenson + + * 41698: Doc/Zsh/params.yo, Src/builtin.c, Src/exec.c, + Src/params.c, Src/zsh.h, Test/B02typeset.ztst, + Test/D04parameter.ztst: add ([key]=val) assignment syntax. + Still to do: allow to mix with other syntax, improve typeset -p, + implement [key]+=value. + 2017-09-13 Peter Stephenson * 41688: Src/jobs.c: "wait" for all active jobs should ignore diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index 817496b8a..31266a7d0 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -91,13 +91,36 @@ cindex(array assignment) ifzman() indent(tt(set -A) var(name) var(value) ...) indent(var(name)tt(=LPAR())var(value) ...tt(RPAR())) +indent(var(name)tt(=LPAR())tt([)var(key)tt(]=)var(value) ...tt(RPAR())) + +In the third form, var(key) is an expression that will be evaluated in +arithmetic context (in its simplest form, an integer) that gives the +index of the element to be assigned with var(value). In this form any +elements not explicitly mentioned that come before the largest index to +which a value is assigned will be assigned an empty string. The indices +may be in any order. Note that this syntax is strict: tt([) and tt(]=) must +not be quoted, while var(key) may not consist of the unquoted string +tt(]=), but is otherwise treated as a simple string. Furthermore, all +elements must match this form or an error is genereted; likewise, if the +first entry does not match this form any later entry that does is taken +as a simple value rather than a key / value pair. The enhanced forms of +subscript expression that may be used when directly subscripting a +variable name, described in the section Array Subscripts below, are not +available. Both var(key) and var(value) undergo all forms of expansion +allowed for single word substitutions (this does not include filename +generation). If no parameter var(name) exists, an ordinary array parameter is created. If the parameter var(name) exists and is a scalar, it is replaced by a new array. To append to an array without changing the existing values, use -the syntax: +one of the following: ifzman() indent(var(name)tt(+=LPAR())var(value) ...tt(RPAR())) +indent(var(name)tt(+=LPAR())tt([)var(key)tt(]=)var(value) ...tt(RPAR())) + +In the second form var(key) may specify an existing index as well as an +index off the end of the old array; any existing value is overwritten by +var(value). Within the parentheses on the right hand side of either form of the assignment, newlines and semicolons are treated the same as white space, @@ -118,12 +141,14 @@ is interpreted as alternating keys and values: ifzman() indent(tt(set -A) var(name) var(key) var(value) ...) indent(var(name)tt(=LPAR())var(key) var(value) ...tt(RPAR())) +indent(var(name)tt(=LPAR())tt([)var(key)tt(]=)var(value) ...tt(RPAR())) Every var(key) must have a var(value) in this case. Note that this assigns to the entire array, deleting any elements that do not appear in the list. The append syntax may also be used with an associative array: ifzman() indent(var(name)tt(+=LPAR())var(key) var(value) ...tt(RPAR())) +indent(var(name)tt(+=LPAR())tt([)var(key)tt(]=)var(value) ...tt(RPAR())) This adds a new key/value pair if the key is not already present, and replaces the value for the existing key if it is. diff --git a/Src/builtin.c b/Src/builtin.c index 0c2a62a88..f5ccf52f8 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -450,15 +450,35 @@ execbuiltin(LinkList args, LinkList assigns, Builtin bn) Asgment asg = (Asgment)node; fputc(' ', xtrerr); quotedzputs(asg->name, xtrerr); - if (asg->is_array) { - LinkNode arrnode; + if (asg->flags & ASG_ARRAY) { fprintf(xtrerr, "=("); if (asg->value.array) { - for (arrnode = firstnode(asg->value.array); - arrnode; - incnode(arrnode)) { - fputc(' ', xtrerr); - quotedzputs((char *)getdata(arrnode), xtrerr); + if (asg->flags & ASG_KEY_VALUE) { + LinkNode keynode, valnode; + keynode = firstnode(asg->value.array); + for (;;) { + if (!keynode) + break; + valnode = nextnode(keynode); + if (!valnode) + break; + fputc('[', xtrerr); + quotedzputs((char *)getdata(keynode), + xtrerr); + fprintf(stderr, "]="); + quotedzputs((char *)getdata(valnode), + xtrerr); + keynode = nextnode(valnode); + } + } else { + LinkNode arrnode; + for (arrnode = firstnode(asg->value.array); + arrnode; + incnode(arrnode)) { + fputc(' ', xtrerr); + quotedzputs((char *)getdata(arrnode), + xtrerr); + } } } fprintf(xtrerr, " )"); @@ -1519,7 +1539,7 @@ bin_fc(char *nam, char **argv, Options ops, int func) asgl = a; } a->name = *argv; - a->is_array = 0; + a->flags = 0; a->value.scalar = s; a->node.next = a->node.prev = NULL; argv++; @@ -1910,7 +1930,7 @@ getasg(char ***argvp, LinkList assigns) return NULL; } asg.name = s; - asg.is_array = 0; + asg.flags = 0; /* search for `=' */ for (; *s && *s != '='; s++); @@ -2171,7 +2191,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), * ii. we are creating a new local parameter */ if (usepm) { - if (asg->is_array ? + if ((asg->flags & ASG_ARRAY) ? !(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) : (asg->value.scalar && (PM_TYPE(pm->node.flags & (PM_ARRAY|PM_HASHED))))) { @@ -2241,10 +2261,11 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), if (asg->value.scalar && !(pm = assignsparam(pname, ztrdup(asg->value.scalar), 0))) return NULL; - } else if (asg->is_array) { + } else if (asg->flags & ASG_ARRAY) { + int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0; if (!(pm = assignaparam(pname, asg->value.array ? zlinklist2array(asg->value.array) : - mkarray(NULL), 0))) + mkarray(NULL), flags))) return NULL; } if (errflag) @@ -2255,7 +2276,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), return pm; } - if (asg->is_array ? + if ((asg->flags & ASG_ARRAY) ? !(on & (PM_ARRAY|PM_HASHED)) : (asg->value.scalar && (on & (PM_ARRAY|PM_HASHED)))) { zerrnam(cname, "%s: inconsistent type for assignment", pname); @@ -2287,7 +2308,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), */ if (!ASG_VALUEP(asg) && !((pm->node.flags|on) & (PM_ARRAY|PM_HASHED))) { asg->value.scalar = dupstring(getsparam(pname)); - asg->is_array = 0; + asg->flags = 0; } /* pname may point to pm->nam which is about to disappear */ pname = dupstring(pname); @@ -2396,13 +2417,14 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), ztrdup(asg->value.scalar ? asg->value.scalar : ""), 0))) return NULL; dont_set = 1; - asg->is_array = 0; + asg->flags = 0; keeplocal = 0; on = pm->node.flags; } else if (PM_TYPE(on) == PM_ARRAY && ASG_ARRAYP(asg)) { + int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0; if (!(pm = assignaparam(pname, asg->value.array ? zlinklist2array(asg->value.array) : - mkarray(NULL), 0))) + mkarray(NULL), flags))) return NULL; dont_set = 1; keeplocal = 0; @@ -2479,6 +2501,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), Param ipm = pm; if (pm->node.flags & (PM_ARRAY|PM_HASHED)) { char **arrayval; + int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0; if (!ASG_ARRAYP(asg)) { /* * Attempt to assign a scalar value to an array. @@ -2497,7 +2520,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), arrayval = zlinklist2array(asg->value.array); else arrayval = mkarray(NULL); - if (!(pm=assignaparam(pname, arrayval, 0))) + if (!(pm=assignaparam(pname, arrayval, flags))) return NULL; } else { DPUTS(ASG_ARRAYP(asg), "BUG: inconsistent array value for scalar"); @@ -2750,13 +2773,15 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) * Already tied in the fashion requested. */ struct tieddata *tdp = (struct tieddata*)pm->u.data; + int flags = (asg->flags & ASG_KEY_VALUE) ? + ASSPM_KEY_VALUE : 0; /* Update join character */ tdp->joinchar = joinchar; if (asg0.value.scalar) assignsparam(asg0.name, ztrdup(asg0.value.scalar), 0); else if (asg->value.array) assignaparam( - asg->name, zlinklist2array(asg->value.array), 0); + asg->name, zlinklist2array(asg->value.array),flags); return 0; } else { zwarnnam(name, "can't tie already tied scalar: %s", @@ -2778,7 +2803,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) * to be exported properly. */ asg2.name = asg->name; - asg2.is_array = 0; + asg2.flags = 0; asg2.value.array = (LinkList)0; if (!(apm=typeset_single(name, asg->name, (Param)paramtab->getnode(paramtab, @@ -2816,9 +2841,10 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) if (apm->ename) zsfree(apm->ename); apm->ename = ztrdup(asg0.name); - if (asg->value.array) - assignaparam(asg->name, zlinklist2array(asg->value.array), 0); - else if (oldval) + if (asg->value.array) { + int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0; + assignaparam(asg->name, zlinklist2array(asg->value.array), flags); + } else if (oldval) assignsparam(asg0.name, oldval, 0); unqueue_signals(); diff --git a/Src/exec.c b/Src/exec.c index e2432fda4..d1367660c 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2389,6 +2389,60 @@ addfd(int forked, int *save, struct multio **mfds, int fd1, int fd2, int rflag, } } +/* Check for array assignent with entries like [key]=val. + * + * All entries or none must match this form, else error and return 0. + * + * Convert list to alternate key / val form, perform + * appropriate substitution, and return 1 if found. + * + * Caller to check errflag. + */ + +/**/ +static int +keyvalpairarray(LinkList vl, int htok) +{ + char *start, *end, *dat; + LinkNode ve, next; + + if (vl && + (ve = firstnode(vl)) && + (start = (char *)getdata(ve)) && + start[0] == Inbrack && + (end = strchr(start+1, Outbrack)) && + end[1] == Equals) { + for (;;) { + *end = '\0'; + next = nextnode(ve); + + dat = start + 1; + if (htok) + singsub(&dat); + untokenize(dat); + setdata(ve, dat); + dat = end + 2; + if (htok) + singsub(&dat); + untokenize(dat); + insertlinknode(vl, ve, dat); + ve = next; + if (!ve) + break; + if (!(start = (char *)getdata(ve)) || + start[0] != Inbrack || + !(end = strchr(start+1, Outbrack)) || + end[1] != Equals) { + zerr("bad array element, expected [key]=value: %s", + start); + return 0; + } + } + return 1; + } + return 0; +} + /**/ static void addvars(Estate state, Wordcode pc, int addflags) @@ -2428,8 +2482,17 @@ addvars(Estate state, Wordcode pc, int addflags) if ((isstr = (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR))) { init_list1(svl, ecgetstr(state, EC_DUPTOK, &htok)); vl = &svl; - } else + } else { vl = ecgetlist(state, WC_ASSIGN_NUM(ac), EC_DUPTOK, &htok); + if (keyvalpairarray(vl, htok)) { + myflags |= ASSPM_KEY_VALUE; + htok = 0; + } + if (errflag) { + state->pc = opc; + return; + } + } if (vl && htok) { prefork(vl, (isstr ? (PREFORK_SINGLE|PREFORK_ASSIGN) : @@ -3914,7 +3977,7 @@ execcmd_exec(Estate state, Execcmd_params eparams, while ((data = ugetnode(&svl))) { char *ptr; asg = (Asgment)zhalloc(sizeof(struct asgment)); - asg->is_array = 0; + asg->flags = 0; if ((ptr = strchr(data, '='))) { *ptr++ = '\0'; asg->name = data; @@ -3936,7 +3999,7 @@ execcmd_exec(Estate state, Execcmd_params eparams, asg->name = name; if (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR) { char *val = ecgetstr(state, EC_DUPTOK, &htok); - asg->is_array = 0; + asg->flags = 0; if (WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC) { /* Fake assignment, no value */ asg->value.scalar = NULL; @@ -3961,18 +4024,23 @@ execcmd_exec(Estate state, Execcmd_params eparams, asg->value.scalar = val; } } else { - asg->is_array = 1; + asg->flags = ASG_ARRAY; asg->value.array = ecgetlist(state, WC_ASSIGN_NUM(ac), EC_DUPTOK, &htok); if (asg->value.array) { - prefork(asg->value.array, PREFORK_ASSIGN, NULL); - if (errflag) { - state->pc = opc; - break; + if (keyvalpairarray(asg->value.array, 1)) + asg->flags |= ASG_KEY_VALUE; + else if (!errflag) { + prefork(asg->value.array, PREFORK_ASSIGN, + NULL); + if (errflag) { + state->pc = opc; + break; + } + globlist(asg->value.array, 0); } - globlist(asg->value.array, 0); if (errflag) { state->pc = opc; break; diff --git a/Src/params.c b/Src/params.c index 6fbee880c..e0aaaf620 100644 --- a/Src/params.c +++ b/Src/params.c @@ -3185,6 +3185,72 @@ assignaparam(char *s, char **val, int flags) if (flags & ASSPM_WARN) check_warn_pm(v->pm, "array", created, may_warn_about_nested_vars); + + if ((flags & ASSPM_KEY_VALUE) && (PM_TYPE(v->pm->node.flags) & PM_ARRAY)) { + /* + * This is an ordinary array with key / value pairs. + */ + int maxlen, origlen; + char **aptr, **fullval; + zlong *subscripts = (zlong *)zhalloc(arrlen(val) * sizeof(zlong)); + zlong *iptr = subscripts; + if (flags & ASSPM_AUGMENT) { + maxlen = origlen = arrlen(v->pm->gsu.a->getfn(v->pm)); + } else { + maxlen = origlen = 0; + } + for (aptr = val; *aptr && aptr[1]; aptr += 2) { + *iptr = mathevali(*aptr); + if (*iptr < 0 || + (!isset(KSHARRAYS) && *iptr == 0)) { + unqueue_signals(); + zerr("bad subscript for direct array assignment: %s", *aptr); + return NULL; + } + if (!isset(KSHARRAYS)) + --*iptr; + if (*iptr + 1 > maxlen) + maxlen = *iptr + 1; + ++iptr; + } + fullval = zshcalloc((maxlen+1) * sizeof(char *)); + if (!fullval) { + zerr("array too large"); + return NULL; + } + fullval[maxlen] = NULL; + if (flags & ASSPM_AUGMENT) { + char **srcptr = v->pm->gsu.a->getfn(v->pm); + for (aptr = fullval; aptr <= fullval + origlen; aptr++) { + *aptr = ztrdup(*srcptr); + srcptr++; + } + } + iptr = subscripts; + for (aptr = val; *aptr && aptr[1]; aptr += 2) { + zsfree(*aptr); + fullval[*iptr] = aptr[1]; + ++iptr; + } + if (*aptr) { /* Shouldn't be possible */ + DPUTS(1, "Extra element in key / value array"); + zsfree(*aptr); + } + free(val); + for (aptr = fullval; aptr < fullval + maxlen; aptr++) { + /* + * Remember we don't have sparse arrays but and they're null + * terminated --- so any value we don't set has to be an + * empty string. + */ + if (!*aptr) + *aptr = ztrdup(""); + } + setarrvalue(v, fullval); + unqueue_signals(); + return v->pm; + } + if (flags & ASSPM_AUGMENT) { if (v->start == 0 && v->end == -1) { if (PM_TYPE(v->pm->node.flags) & PM_ARRAY) { diff --git a/Src/zsh.h b/Src/zsh.h index 1e982a632..27642f239 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1217,17 +1217,25 @@ struct alias { struct asgment { struct linknode node; char *name; - int is_array; + int flags; union { char *scalar; LinkList array; } value; }; +/* Flags for flags element of asgment */ +enum { + /* Array value */ + ASG_ARRAY = 1, + /* Key / value array pair */ + ASG_KEY_VALUE = 2 +}; + /* * Assignment is array? */ -#define ASG_ARRAYP(asg) ((asg)->is_array) +#define ASG_ARRAYP(asg) ((asg)->flags & ASG_ARRAY) /* * Assignment has value? @@ -2060,6 +2068,11 @@ enum { ASSPM_WARN = (ASSPM_WARN_CREATE|ASSPM_WARN_NESTED), /* Import from environment, so exercise care evaluating value */ ASSPM_ENV_IMPORT = 1 << 3, + /* Array is key / value pairs. + * This is normal for associative arrays but variant behaviour for + * normal arrays. + */ + ASSPM_KEY_VALUE = 1 << 4 }; /* node for named directory hash table (nameddirtab) */ diff --git a/Test/B02typeset.ztst b/Test/B02typeset.ztst index b27bb4f6b..ae218047c 100644 --- a/Test/B02typeset.ztst +++ b/Test/B02typeset.ztst @@ -721,3 +721,58 @@ # 'date' did not run. >Status is printed, 1 *?*: failed to change user ID: * + + typeset -A keyvalhash=([one]=eins [two]=zwei) + keyvalhash+=([three]=drei) + for key in ${(ok)keyvalhash}; do + print $key $keyvalhash[$key] + done +0:[key]=val for hashes +>one eins +>three drei +>two zwei + + local keyvalarray=([1]=one [3]=three) + print -l "${keyvalarray[@]}" + keyvalarray+=([2]=two) + print -l "${keyvalarray[@]}" + local keyvalarray=([1]=one [3]=three) + print -l "${keyvalarray[@]}" +0:[key]=val for normal arrays +>one +> +>three +>one +>two +>three +>one +> +>three + + touch foo Xnot_globbedX + inkey="another key" val="another value" + typeset -A keyvalhash=([$(echo the key)]=$(echo the value) + [$inkey]=$val + [*]=?not_globbed?) + for key in ${(ok)keyvalhash}; do + print -l $key $keyvalhash[$key] + done + typeset -A keyvalhash=([$(echo the key)]=$(echo the value) + [$inkey]=$val + [*]=?not_globbed?) + for key in ${(ok)keyvalhash}; do + print -l $key $keyvalhash[$key] + done +0:Substitution in [key]=val syntax +>* +>?not_globbed? +>another key +>another value +>the key +>the value +>* +>?not_globbed? +>another key +>another value +>the key +>the value diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index 8dbc1e8b8..367bca120 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -2207,3 +2207,71 @@ F:behavior, see http://austingroupbugs.net/view.php?id=888 0:(z) splitting with remaining tokens >foo-bar*thingy? + typeset -A keyvalhash + keyvalhash=([one]=eins [two]=zwei) + keyvalhash+=([three]=drei) + for key in ${(ok)keyvalhash}; do + print $key $keyvalhash[$key] + done +0:[key]=val for hashes +>one eins +>three drei +>two zwei + + local keyvalarray + keyvalarray=([1]=one [3]=three) + print -l "${keyvalarray[@]}" + keyvalarray+=([2]=two) + print -l "${keyvalarray[@]}" +0:[key]=val for normal arrays +>one +> +>three +>one +>two +>three + + typeset -A keyvalhash + touch foo Xnot_globbedX + key="another key" val="another value" + keyvalhash=([$(echo the key)]=$(echo the value) + [$key]=$val + [*]=?not_globbed?) + for key in ${(ok)keyvalhash}; do + print -l $key $keyvalhash[$key] + done +0:Substitution in [key]=val syntax +>* +>?not_globbed? +>another key +>another value +>the key +>the value + + local keyvalarray + keyvalarray=(1 2 3) + keyvalarray+=([5]=5 [7]=7) + keyvalarray+=([4]=4 [6]=6) + print $#keyvalarray + print $keyvalarray +0:append to normal array using [key]=val +>7 +>1 2 3 4 5 6 7 + + local -A keyvalhash + keyvalhash=(['1first element!']=first' 'value + ["2second element?"]=second" "value + [$'3third element#']=third$' 'value + [\4\f\o\u\r\t\h\ \e\l\e\m\e\n\t\\]=fourth\ value) + for key in ${(ok)keyvalhash}; do + print -rl -- $key $keyvalhash[$key] + done +0:quoting in [key]=value syntax +>1first element! +>first value +>2second element? +>second value +>3third element# +>third value +>4fourth element\ +>fourth value -- cgit v1.2.3 From 728f2adfc8daf2c65c5d4130a903635a58c99fb5 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Sat, 30 Sep 2017 13:35:19 +0100 Subject: Add typeset -p1, like typeset -p with newlines --- ChangeLog | 7 +++++++ Doc/Zsh/builtins.yo | 11 ++++++++--- Src/builtin.c | 27 +++++++++++++++++++------- Src/params.c | 53 +++++++++++++++++++++++++++++++++++++--------------- Src/subst.c | 4 +++- Src/zsh.h | 11 ++++++----- Test/B02typeset.ztst | 26 ++++++++++++++++++++++++++ 7 files changed, 108 insertions(+), 31 deletions(-) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index 9b1567d23..2019a0fa7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2017-10-01 Peter Stephenson + + * 41797 (tweaked): Doc/Zsh/builtins.yo, Src/builtin.c, + Src/params.c, Src/subst.c, Src/zsh.h, Test/B02typeset.ztst: + typeset -p1 outputs arrays and associative arrays with newlines + and indented elements. + 2017-09-29 Peter Stephenson * 41789: Src/exec.c: Don't try to mark an FD as saved if it diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index d6aa078a0..a2ddd4495 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -1817,10 +1817,10 @@ findex(typeset) cindex(parameters, setting) cindex(parameters, declaring) redef(SPACES)(0)(tt(ifztexi(NOTRANS(@ @ @ @ @ @ @ @ ))ifnztexi( ))) -xitem(tt(typeset )[ {tt(PLUS())|tt(-)}tt(AHUaghlmprtux) ] \ -[ {tt(PLUS())|tt(-)}tt(EFLRZi) [ var(n) ] ]) +xitem(tt(typeset )[ {tt(PLUS())|tt(-)}tt(AHUaghlmrtux) ] \ +[ {tt(PLUS())|tt(-)}tt(EFLRZip) [ var(n) ] ]) xitem(SPACES()[ tt(+) ] [ var(name)[tt(=)var(value)] ... ]) -xitem(tt(typeset )tt(-T) [ {tt(PLUS())|tt(-)}tt(Uglprux) ] [ {tt(PLUS())|tt(-)}tt(LRZ) [ var(n) ] ]) +xitem(tt(typeset )tt(-T) [ {tt(PLUS())|tt(-)}tt(Uglrux) ] [ {tt(PLUS())|tt(-)}tt(LRZp) [ var(n) ] ]) xitem(SPACES()[ tt(+) | var(SCALAR)[tt(=)var(value)] var(array)[tt(=LPAR())var(value) ...tt(RPAR())] [ var(sep) ] ]) item(tt(typeset) tt(-f) [ {tt(PLUS())|tt(-)}tt(TUkmtuz) ] [ tt(+) ] [ var(name) ... ])( Set or display attributes and values for shell parameters. @@ -1985,6 +1985,11 @@ If the tt(-p) option is given, parameters and values are printed in the form of a typeset command with an assignment, regardless of other flags and options. Note that the tt(-H) flag on parameters is respected; no value will be shown for these parameters. + +tt(-p) may be followed by an optional integer argument. Currently +only the value 1 is supported. In this case arrays and associative +arrays are printed with newlines between indented elements for +readability. ) item(tt(-T) [ var(scalar)[tt(=)var(value)] var(array)[tt(=LPAR())var(value) ...tt(RPAR())] [ var(sep) ] ])( This flag has a different meaning when used with tt(-f); see below. diff --git a/Src/builtin.c b/Src/builtin.c index f5ccf52f8..84a2beee0 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -53,7 +53,7 @@ static struct builtin builtins[] = BUILTIN("cd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL), BUILTIN("chdir", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL), BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL), - BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmprtuxz", NULL), + BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmp:%rtuxz", NULL), BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "clpv", NULL), BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmprs", NULL), BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL), @@ -62,7 +62,7 @@ static struct builtin builtins[] = BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmprs", NULL), BUILTIN("eval", BINF_PSPECIAL, bin_eval, 0, -1, BIN_EVAL, NULL, NULL), BUILTIN("exit", BINF_PSPECIAL, bin_break, 0, 1, BIN_EXIT, NULL, NULL), - BUILTIN("export", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%TUZ:%afhi:%lprtu", "xg"), + BUILTIN("export", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%TUZ:%afhi:%lp:%rtu", "xg"), BUILTIN("false", 0, bin_false, 0, -1, 0, NULL, NULL), /* * We used to behave as if the argument to -e was optional. @@ -71,7 +71,7 @@ static struct builtin builtins[] = */ BUILTIN("fc", 0, bin_fc, 0, -1, BIN_FC, "aAdDe:EfiIlLmnpPrRt:W", NULL), BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL), - BUILTIN("float", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%Z:%ghlprtux", "E"), + BUILTIN("float", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%Z:%ghlp:%rtux", "E"), BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmMstTuUWx:z", NULL), BUILTIN("getln", 0, bin_read, 0, -1, 0, "ecnAlE", "zr"), BUILTIN("getopts", 0, bin_getopts, 2, -1, 0, NULL, NULL), @@ -82,11 +82,11 @@ static struct builtin builtins[] = #endif BUILTIN("history", 0, bin_fc, 0, -1, BIN_FC, "adDEfiLmnpPrt:", "l"), - BUILTIN("integer", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "HL:%R:%Z:%ghi:%lprtux", "i"), + BUILTIN("integer", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "HL:%R:%Z:%ghi:%lp:%rtux", "i"), BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL), BUILTIN("kill", BINF_HANDLES_OPTS, bin_kill, 0, -1, 0, NULL, NULL), BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL), - BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lprtux", NULL), + BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lp:%rtux", NULL), BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL), BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL), @@ -120,7 +120,7 @@ static struct builtin builtins[] = BUILTIN("trap", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_trap, 0, -1, 0, NULL, NULL), BUILTIN("true", 0, bin_true, 0, -1, 0, NULL, NULL), BUILTIN("type", 0, bin_whence, 0, -1, 0, "ampfsSw", "v"), - BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klprtuxmz", NULL), + BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klp:%rtuxmz", NULL), BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL), BUILTIN("unalias", 0, bin_unhash, 0, -1, BIN_UNALIAS, "ams", NULL), BUILTIN("unfunction", 0, bin_unhash, 1, -1, BIN_UNFUNCTION, "m", "f"), @@ -2645,8 +2645,21 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) queue_signals(); /* Given no arguments, list whatever the options specify. */ - if (OPT_ISSET(ops,'p')) + if (OPT_ISSET(ops,'p')) { printflags |= PRINT_TYPESET; + if (OPT_HASARG(ops,'p')) { + char *eptr; + int pflag = (int)zstrtol(OPT_ARG(ops,'p'), &eptr, 10); + if (pflag == 1 && !*eptr) + printflags |= PRINT_LINE; + else if (pflag || *eptr) { + zwarnnam(name, "bad argument to -p: %s", OPT_ARG(ops,'p')); + unqueue_signals(); + return 1; + } + /* -p0 treated as -p for consistency */ + } + } hasargs = *argv != NULL || (assigns && firstnode(assigns)); if (!hasargs) { if (!OPT_ISSET(ops,'p')) { diff --git a/Src/params.c b/Src/params.c index 3236f7165..ddf3ce164 100644 --- a/Src/params.c +++ b/Src/params.c @@ -5682,40 +5682,60 @@ printparamvalue(Param p, int printflags) /* array */ if (!(printflags & PRINT_KV_PAIR)) { putchar('('); - putchar(' '); + if (!(printflags & PRINT_LINE)) + putchar(' '); } u = p->gsu.a->getfn(p); if(*u) { + if (printflags & PRINT_LINE) { + if (printflags & PRINT_KV_PAIR) + printf(" "); + else + printf("\n "); + } quotedzputs(*u++, stdout); while (*u) { - putchar(' '); + if (printflags & PRINT_LINE) + printf("\n "); + else + putchar(' '); quotedzputs(*u++, stdout); } + if ((printflags & (PRINT_LINE|PRINT_KV_PAIR)) == PRINT_LINE) + putchar('\n'); } if (!(printflags & PRINT_KV_PAIR)) { - putchar(' '); + if (!(printflags & PRINT_LINE)) + putchar(' '); putchar(')'); } break; case PM_HASHED: /* association */ - if (!(printflags & PRINT_KV_PAIR)) { - putchar('('); - putchar(' '); - } { - HashTable ht = p->gsu.h->getfn(p); + HashTable ht; + int found = 0; + if (!(printflags & PRINT_KV_PAIR)) { + putchar('('); + if (!(printflags & PRINT_LINE)) + putchar(' '); + } + ht = p->gsu.h->getfn(p); if (ht) - scanhashtable(ht, 1, 0, PM_UNSET, - ht->printnode, PRINT_KV_PAIR); + found = scanhashtable(ht, 1, 0, PM_UNSET, + ht->printnode, PRINT_KV_PAIR | + (printflags & PRINT_LINE)); + if (!(printflags & PRINT_KV_PAIR)) { + if (found && (printflags & PRINT_LINE)) + putchar('\n'); + putchar(')'); + } } - if (!(printflags & PRINT_KV_PAIR)) - putchar(')'); break; } - if (printflags & PRINT_KV_PAIR) + if ((printflags & (PRINT_KV_PAIR|PRINT_LINE)) == PRINT_KV_PAIR) putchar(' '); - else + else if (!(printflags & PRINT_KV_PAIR)) putchar('\n'); } @@ -5809,8 +5829,11 @@ printparamnode(HashNode hn, int printflags) zputs(p->node.nam, stdout); putchar('\n'); } else { - if (printflags & PRINT_KV_PAIR) + if (printflags & PRINT_KV_PAIR) { + if (printflags & PRINT_LINE) + printf("\n "); putchar('['); + } quotedzputs(p->node.nam, stdout); if (printflags & PRINT_KV_PAIR) printf("]="); diff --git a/Src/subst.c b/Src/subst.c index eef0dc75b..2d3eeb2c3 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -158,7 +158,9 @@ prefork(LinkList list, int flags, int *ret_flags) filesub(&cptr, flags & (PREFORK_TYPESET|PREFORK_ASSIGN)); setdata(node, cptr); } - } else if (!(flags & PREFORK_SINGLE) && !keep) + } else if (!(flags & PREFORK_SINGLE) && + !(*ret_flags & PREFORK_KEY_VALUE) && + !keep) uremnode(list, node); if (errflag) { unqueue_signals(); diff --git a/Src/zsh.h b/Src/zsh.h index c1138bfab..24d06ba06 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -2128,13 +2128,14 @@ typedef groupset *Groupset; #define PRINT_KV_PAIR (1<<3) #define PRINT_INCLUDEVALUE (1<<4) #define PRINT_TYPESET (1<<5) +#define PRINT_LINE (1<<6) /* flags for printing for the whence builtin */ -#define PRINT_WHENCE_CSH (1<<6) -#define PRINT_WHENCE_VERBOSE (1<<7) -#define PRINT_WHENCE_SIMPLE (1<<8) -#define PRINT_WHENCE_FUNCDEF (1<<9) -#define PRINT_WHENCE_WORD (1<<10) +#define PRINT_WHENCE_CSH (1<<7) +#define PRINT_WHENCE_VERBOSE (1<<8) +#define PRINT_WHENCE_SIMPLE (1<<9) +#define PRINT_WHENCE_FUNCDEF (1<<10) +#define PRINT_WHENCE_WORD (1<<11) /* Return values from loop() */ diff --git a/Test/B02typeset.ztst b/Test/B02typeset.ztst index 13f0d5e30..996af064f 100644 --- a/Test/B02typeset.ztst +++ b/Test/B02typeset.ztst @@ -793,3 +793,29 @@ local -A keyvalhash=(1 one [2]=two 3 three) 1:Mixed syntax with [key]=val not allowed for hash. ?(eval):1: bad [key]=value syntax for associative array + + local -a myarray + typeset -p1 myarray + myarray=("&" sand '""' "" plugh) + typeset -p1 myarray +0:typeset -p1 output for array +>typeset -a myarray=() +>typeset -a myarray=( +> '&' +> sand +> '""' +> '' +> plugh +>) + + local -A myhash + typeset -p1 myhash + myhash=([one]=two [three]= [four]="[]") + typeset -p1 myhash +0:typeset -p1 output for associative array +>typeset -A myhash=() +>typeset -A myhash=( +> [four]='[]' +> [one]=two +> [three]='' +>) -- cgit v1.2.3 From bf04ffb54286c44deef6e4ceb6445d6af30897f9 Mon Sep 17 00:00:00 2001 From: Martijn Dekker Date: Wed, 11 Oct 2017 20:44:54 +0200 Subject: 41866: No error unsetting nonexistent function for POSIXBUILTINS --- ChangeLog | 5 +++++ Src/builtin.c | 6 +++++- Src/hashtable.h | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index c5d2bc32d..edf8b0721 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2017-11-02 Peter Stephenson + + * Martijn: 41866: Src/builtin.c, Src/hashtable.c: no error for + unsetting nonexistent function if POSIXBUILTINS. + 2017-10-31 Oliver Kiddle * 41965: Completion/Zsh/Command/_typeset: complete functions -W diff --git a/Src/builtin.c b/Src/builtin.c index 84a2beee0..0ff9e4f16 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -125,7 +125,7 @@ static struct builtin builtins[] = BUILTIN("unalias", 0, bin_unhash, 0, -1, BIN_UNALIAS, "ams", NULL), BUILTIN("unfunction", 0, bin_unhash, 1, -1, BIN_UNFUNCTION, "m", "f"), BUILTIN("unhash", 0, bin_unhash, 1, -1, BIN_UNHASH, "adfms", NULL), - BUILTIN("unset", BINF_PSPECIAL, bin_unset, 1, -1, 0, "fmv", NULL), + BUILTIN("unset", BINF_PSPECIAL, bin_unset, 1, -1, BIN_UNSET, "fmv", NULL), BUILTIN("unsetopt", 0, bin_setopt, 0, -1, BIN_UNSETOPT, NULL, NULL), BUILTIN("wait", 0, bin_fg, 0, -1, BIN_WAIT, NULL, NULL), BUILTIN("whence", 0, bin_whence, 0, -1, 0, "acmpvfsSwx:", NULL), @@ -4158,6 +4158,10 @@ bin_unhash(char *name, char **argv, Options ops, int func) for (; *argv; argv++) { if ((hn = ht->removenode(ht, *argv))) { ht->freenode(hn); + } else if (func == BIN_UNSET && isset(POSIXBUILTINS)) { + /* POSIX: unset: "Unsetting a variable or function that was * + * not previously set shall not be considered an error." */ + returnval = 0; } else { zwarnnam(name, "no such hash table element: %s", *argv); returnval = 1; diff --git a/Src/hashtable.h b/Src/hashtable.h index 3606e9785..21398e17c 100644 --- a/Src/hashtable.h +++ b/Src/hashtable.h @@ -62,6 +62,7 @@ #define BIN_UNHASH 28 #define BIN_UNALIAS 29 #define BIN_UNFUNCTION 30 +#define BIN_UNSET 31 /* These currently depend on being 0 and 1. */ #define BIN_SETOPT 0 -- cgit v1.2.3 From 755f8571d49a07e85dc77d01f6ccb3944a3abdcb Mon Sep 17 00:00:00 2001 From: dana Date: Fri, 1 Dec 2017 01:12:15 -0600 Subject: 42063: getopts errors now report "+" in front of option where appropriate --- ChangeLog | 5 +++++ Src/builtin.c | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index 3efa686c6..e3aaac737 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2017-12-01 Peter Stephenson + + * dana: 42063: Src/builtin.c: getopts errors now report "+" if + appropriate for option. + 2017-11-24 Daniel Shahaf * 42031 + 42048: Doc/Zsh/cond.yo, Doc/Zsh/options.yo, README, diff --git a/Src/builtin.c b/Src/builtin.c index 0ff9e4f16..f002b9912 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -5442,8 +5442,9 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun if(quiet) { zoptarg = metafy(optbuf, lenoptbuf, META_DUP); } else { - zwarn(*p == '?' ? "bad option: -%c" : - "argument expected after -%c option", opch); + zwarn(*p == '?' ? "bad option: %c%c" : + "argument expected after %c%c option", + "?-+"[lenoptbuf], opch); zoptarg=ztrdup(""); } return 0; -- cgit v1.2.3 From 5f6a52c06ca956cf61eacd7f45f270899337d45e Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Fri, 22 Dec 2017 20:00:54 +0000 Subject: 42156: new CHECK_RUNNING_JOBS option demanded by bash groupies Also new job options. Also suppress debug error if rows or columns are reported as zero as this is normal without a physical terminal. --- ChangeLog | 10 +++ Doc/Zsh/options.yo | 17 ++++- Src/builtin.c | 3 +- Src/options.c | 1 + Src/utils.c | 4 +- Src/zsh.h | 1 + Test/W02jobs.ztst | 186 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 217 insertions(+), 5 deletions(-) create mode 100644 Test/W02jobs.ztst (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index 1b9b0be68..5a3d0ec97 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,15 @@ 2017-12-22 Peter Stephenson + * dana: 42156 with tweak as per 42158: Doc/Zsh/options.yo, + Src/builtin.c, Src/options.c, Src/utils.c, Src/zsh.h, + Test/W02jobs.ztst: add CHECK_RUNNING_JOBS opion and job tests; + remove debug error when rows or columns are zero as this is + normal without a physical terminal. + + * dana: 42156: Doc/Zsh/options.yo, Src/builtin.c, + Src/options.c, Src/zsh.h, Test/W02jobs.ztst: new + CHECK_RUNNING_JOBS option demanded by bash groupies. + * danda: 42155: Completion/Unix/Command/_ssh: various improvements for OpenSSH. diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo index d043cf398..25b3d5736 100644 --- a/Doc/Zsh/options.yo +++ b/Doc/Zsh/options.yo @@ -817,7 +817,7 @@ zsh sessions will all have the new entries from their history lists added to the history file, in the order that they exit. The file will still be periodically re-written to trim it when the number of lines grows 20% beyond the value specified by -tt($SAVEHIST) (see also the HIST_SAVE_BY_COPY option). +tt($SAVEHIST) (see also the tt(HIST_SAVE_BY_COPY) option). ) pindex(BANG_HIST) pindex(NO_BANG_HIST) @@ -1429,6 +1429,19 @@ ifnzman(the section Special Functions in noderef(Functions))\ ifzman(the section SPECIAL FUNCTIONS in zmanref(zshmisc)) is not counted for this purpose. ) +pindex(CHECK_RUNNING_JOBS) +pindex(NO_CHECK_RUNNING_JOBS) +pindex(CHECKRUNNINGJOBS) +pindex(NOCHECKRUNNINGJOBS) +cindex(exiting, checking running jobs when) +cindex(logging out, checking running jobs when) +item(tt(CHECK_RUNNING_JOBS) )( +Check for both running and suspended jobs when tt(CHECK_JOBS) is enabled. +When this option is disabled, zsh checks only for suspended jobs, which +matches the default behavior of bash. + +This option has no effect unless tt(CHECK_JOBS) is set. +) pindex(HUP) pindex(NO_HUP) pindex(NOHUP) @@ -1443,7 +1456,7 @@ pindex(LONGLISTJOBS) pindex(NOLONGLISTJOBS) cindex(jobs, list format) item(tt(LONG_LIST_JOBS) (tt(-R)))( -List jobs in the long format by default. +Print job notifications in the long format by default. ) pindex(MONITOR) pindex(NO_MONITOR) diff --git a/Src/builtin.c b/Src/builtin.c index f002b9912..0211f2721 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -5594,7 +5594,8 @@ checkjobs(void) for (i = 1; i <= maxjob; i++) if (i != thisjob && (jobtab[i].stat & STAT_LOCKED) && - !(jobtab[i].stat & STAT_NOPRINT)) + !(jobtab[i].stat & STAT_NOPRINT) && + (isset(CHECKRUNNINGJOBS) || jobtab[i].stat & STAT_STOPPED)) break; if (i <= maxjob) { if (jobtab[i].stat & STAT_STOPPED) { diff --git a/Src/options.c b/Src/options.c index 2b5795bab..590652ea9 100644 --- a/Src/options.c +++ b/Src/options.c @@ -111,6 +111,7 @@ static struct optname optns[] = { {{NULL, "chasedots", OPT_EMULATE}, CHASEDOTS}, {{NULL, "chaselinks", OPT_EMULATE}, CHASELINKS}, {{NULL, "checkjobs", OPT_EMULATE|OPT_ZSH}, CHECKJOBS}, +{{NULL, "checkrunningjobs", OPT_EMULATE|OPT_ZSH}, CHECKRUNNINGJOBS}, {{NULL, "clobber", OPT_EMULATE|OPT_ALL}, CLOBBER}, {{NULL, "combiningchars", 0}, COMBININGCHARS}, {{NULL, "completealiases", 0}, COMPLETEALIASES}, diff --git a/Src/utils.c b/Src/utils.c index 4c0ebe6f5..74fdac31f 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1832,7 +1832,7 @@ adjustlines(int signalled) else shttyinfo.winsize.ws_row = zterm_lines; #endif /* TIOCGWINSZ */ - if (zterm_lines <= 0) { + if (zterm_lines < 0) { DPUTS(signalled, "BUG: Impossible TIOCGWINSZ rows"); zterm_lines = tclines > 0 ? tclines : 24; } @@ -1856,7 +1856,7 @@ adjustcolumns(int signalled) else shttyinfo.winsize.ws_col = zterm_columns; #endif /* TIOCGWINSZ */ - if (zterm_columns <= 0) { + if (zterm_columns < 0) { DPUTS(signalled, "BUG: Impossible TIOCGWINSZ cols"); zterm_columns = tccolumns > 0 ? tccolumns : 80; } diff --git a/Src/zsh.h b/Src/zsh.h index 22ae95480..92f75769a 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -2339,6 +2339,7 @@ enum { CHASEDOTS, CHASELINKS, CHECKJOBS, + CHECKRUNNINGJOBS, CLOBBER, APPENDCREATE, COMBININGCHARS, diff --git a/Test/W02jobs.ztst b/Test/W02jobs.ztst new file mode 100644 index 000000000..65b860072 --- /dev/null +++ b/Test/W02jobs.ztst @@ -0,0 +1,186 @@ +# Tests for interactive job control + +%prep + + if [[ $OSTYPE == cygwin ]]; then + ZTST_unimplemented='the zsh/zpty module does not work on Cygwin' + elif zmodload zsh/zpty 2> /dev/null; then + zpty_start() { + export PS1= PS2= + zpty -d + zpty zsh "${(q)ZTST_testdir}/../Src/zsh -fiV +Z" + } + zpty_input() { + zpty -w zsh "${(F)@}" $'\n' + } + zpty_stop() { + # exit twice in case of check_jobs + zpty -w zsh $'exit\nexit\n' + # zpty gives no output when piped without these braces (?) + { zpty -r zsh } | sed $'/[^[:space:]]/!d; s/\r$//;' + zpty -d + : + } + else + ZTST_unimplemented='the zsh/zpty module is not available' + fi + +%test + + zpty_start + zpty_input 'setopt no_long_list_jobs' + zpty_input ': &' + zpty_input 'wait' + zpty_stop +0:job notification with no_long_list_jobs +*>\[1] [0-9]## +*>\[1] + done[[:space:]]##: + + zpty_start + zpty_input 'setopt long_list_jobs' + zpty_input ': &' + zpty_input 'wait' + zpty_stop +0:job notification with long_list_jobs +*>\[1] [0-9]## +*>\[1] + [0-9]## done[[:space:]]##: + + zpty_start + zpty_input 'setopt no_hup no_check_jobs' + zpty_input 'sleep 3 &' + zpty_stop +0:running job with no_hup + no_check_jobs +*>\[1] [0-9]## + + zpty_start + zpty_input 'setopt no_check_jobs' + zpty_input 'sleep 3 &' + zpty_stop +0:running job with no_check_jobs +*>\[1] [0-9]## +*>zsh:*SIGHUPed* + + zpty_start + zpty_input 'setopt check_jobs no_check_running_jobs' + zpty_input 'sleep 3 &' + zpty_stop +0:running job with check_jobs + no_check_running_jobs +*>\[1] [0-9]## +*>zsh:*SIGHUPed* + + zpty_start + zpty_input 'setopt check_jobs check_running_jobs' + zpty_input 'sleep 3 &' + zpty_stop +0:running job with check_jobs + check_running_jobs +*>\[1] [0-9]## +*>zsh:*running jobs* +*>zsh:*SIGHUPed* + + zpty_start + zpty_input 'setopt check_jobs no_check_running_jobs' + zpty_input 'sleep 3' + sleep 0.1 + zpty_input $'\C-z' + zpty_stop +0:suspended job with check_jobs + no_check_running_jobs +*>zsh:*(stopped|suspended)*sleep* +*>zsh:*(stopped|suspended) jobs* +# no 'SIGHUPed' message for suspended jobs + + zpty_start + zpty_input 'setopt check_jobs check_running_jobs' + zpty_input 'sleep 3' + sleep 0.1 + zpty_input $'\C-z' + zpty_stop +0:suspended job with check_jobs + check_running_jobs +*>zsh:*(stopped|suspended)*sleep* +*>zsh:*(stopped|suspended) jobs* +# no 'SIGHUPed' message for suspended jobs + + zpty_start + zpty_input 'sleep 5 & sleep 4 & sleep 3 &' + zpty_input 'jobs' + zpty_stop +0:`jobs` (misc.) with multiple running jobs +*>\[1] [0-9]## +*>\[2] [0-9]## +*>\[3] [0-9]## +*>\[1] running*sleep 5* +*>\[2] - running*sleep 4* +*>\[3] + running*sleep 3* +*>zsh:*SIGHUPed* + + zpty_start + zpty_input 'sleep 3 &' + zpty_input 'jobs -l' + zpty_input 'jobs -p' + zpty_stop +0:`jobs -l` and `jobs -p` with running job +*>\[1] [0-9]## +*>\[1] + [0-9]## running*sleep* +*>\[1] + [0-9]## running*sleep* +*>zsh:*SIGHUPed* + + zpty_start + zpty_input 'sleep 3 &' + zpty_input 'jobs -d' + zpty_stop +0:`jobs -d` with running job +*>\[1] [0-9]## +*>\[1] + running*sleep* +*>\(pwd : ?*\) +*>zsh:*SIGHUPed* + + zpty_start + zpty_input 'sleep 3 &' + zpty_input 'jobs -r' + zpty_input 'print -- -' + zpty_input 'jobs -s' + zpty_stop +0:`jobs -r` and `jobs -s` with running job +*>\[1] [0-9]## +*>\[1] + running*sleep* +*>- +*>zsh:*SIGHUPed* + + zpty_start + zpty_input 'sleep 5' + sleep 0.1 + zpty_input $'\C-z' + zpty_input 'jobs -r' + zpty_input 'print -- -' + zpty_input 'jobs -s' + zpty_stop +0:`jobs -r` and `jobs -s` with suspended job +*>zsh:*(stopped|suspended)*sleep* +*>- +*>\[1] + (stopped|suspended)*sleep* +# no 'SIGHUPed' message for suspended jobs + + zpty_start + zpty_input 'sleep 10 & sleep 9 & sleep 8 & sleep 7 &' + sleep 0.1 + zpty_input 'kill %4' + sleep 0.1 + zpty_input 'kill -HUP %3' + sleep 0.1 + zpty_input 'kill -INT %2' + sleep 0.1 + zpty_input 'kill -KILL %1' + sleep 0.1 + zpty_stop +0:various `kill` signals with multiple running jobs +*>\[1] [0-9]## +*>\[2] [0-9]## +*>\[3] [0-9]## +*>\[4] [0-9]## +*>\[4] ? terminate*sleep* +*>\[3] ? hangup*sleep* +*>\[2] ? interrupt*sleep* +*>\[1] ? kill*sleep* + +%clean + + zmodload -ui zsh/zpty -- cgit v1.2.3 From 2cbf6b6a19716cd4f03c820929710499576aa809 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Wed, 31 Jan 2018 09:14:40 +0000 Subject: 42332: Special case unsigned printf formats. For constants we can avoid a conversion to signed by examining the expression before passing to math eval. --- ChangeLog | 6 ++++++ Src/builtin.c | 5 ++++- Src/utils.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index ae20911f9..24df71d49 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2018-01-31 Peter Stephenson + + * 42332: Src/builtin.c, Src/utils.c: special case printf for + unsigned integers by looking for constansts, avoiding conversion + to unsigned. + 2018-01-24 Daniel Hahler * 42323: Completion/Unix/Command/_git: _git: move "local" statement diff --git a/Src/builtin.c b/Src/builtin.c index 0211f2721..fb59738f3 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -5243,7 +5243,10 @@ bin_print(char *name, char **args, Options ops, int func) *d++ = 'l'; #endif *d++ = 'l', *d++ = *c, *d = '\0'; - zulongval = (curarg) ? mathevali(curarg) : 0; + if (!curarg) + zulongval = (zulong)0; + else if (!zstrtoul_underscore(curarg, &zulongval)) + zulongval = mathevali(curarg); if (errflag) { zulongval = 0; errflag &= ~ERRFLAG_ERROR; diff --git a/Src/utils.c b/Src/utils.c index 74fdac31f..3b589aa35 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -2455,6 +2455,67 @@ zstrtol_underscore(const char *s, char **t, int base, int underscore) return neg ? -(zlong)calc : (zlong)calc; } +/* + * If s represents a complete unsigned integer (and nothing else) + * return 1 and set retval to the value. Otherwise return 0. + * + * Underscores are always allowed. + * + * Sensitive to OCTAL_ZEROES. + */ + +/**/ +mod_export int +zstrtoul_underscore(const char *s, zulong *retval) +{ + zulong calc = 0, newcalc = 0, base; + + if (*s == '+') + s++; + + if (*s != '0') + base = 10; + else if (*++s == 'x' || *s == 'X') + base = 16, s++; + else if (*s == 'b' || *s == 'B') + base = 2, s++; + else + base = isset(OCTALZEROES) ? 8 : 10; + if (base < 2 || base > 36) { + return 0; + } else if (base <= 10) { + for (; (*s >= '0' && *s < ('0' + base)) || + *s == '_'; s++) { + if (*s == '_') + continue; + newcalc = calc * base + *s - '0'; + if (newcalc < calc) + { + return 0; + } + calc = newcalc; + } + } else { + for (; idigit(*s) || (*s >= 'a' && *s < ('a' + base - 10)) + || (*s >= 'A' && *s < ('A' + base - 10)) + || *s == '_'; s++) { + if (*s == '_') + continue; + newcalc = calc*base + (idigit(*s) ? (*s - '0') : (*s & 0x1f) + 9); + if (newcalc < calc) + { + return 0; + } + calc = newcalc; + } + } + + if (*s) + return 0; + *retval = calc; + return 1; +} + /**/ mod_export int setblock_fd(int turnonblocking, int fd, long *modep) -- cgit v1.2.3