From 02eb1595e8687fd0facd4a8b0e4a19c61a35b664 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Thu, 22 Dec 2016 16:12:42 +0100 Subject: 40162: _arguments support for a match spec in combination with sets --- Src/Zle/computil.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Src/Zle/computil.c') diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index 192ddeab9..d2f0c999b 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -1288,7 +1288,7 @@ parse_cadef(char *nam, char **args) ret->ndopts = ndopts; ret->nodopts = nodopts; set_cadef_opts(ret); - ret = ret->snext = alloc_cadef(NULL, single, NULL, nonarg, flags); + ret = ret->snext = alloc_cadef(NULL, single, match, nonarg, flags); optp = &(ret->opts); nopts = ndopts = nodopts = 0; anum = 1; -- cgit v1.2.3 From a69b19d215798c7cfd89a307abd959fb970dd679 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Wed, 4 Jan 2017 14:41:52 +0100 Subject: 40226: tidy up some of the _arguments set code Remove old code for applying explicit exclusions between sets which fixes some odd behaviour. Some struct members were unused. Also added some comments and test cases. --- ChangeLog | 5 +++ Src/Zle/computil.c | 99 +++++++++++++++----------------------------------- Test/Y03arguments.ztst | 59 +++++++++++++++++++++++++++++- 3 files changed, 91 insertions(+), 72 deletions(-) (limited to 'Src/Zle/computil.c') diff --git a/ChangeLog b/ChangeLog index 0170dc6ef..2f6023fdc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2017-01-04 Oliver Kiddle + + * 40226: Src/Zle/computil.c, Test/Y03arguments.ztst: + tidy up some of the _arguments set code + 2017-01-03 Peter Stephenson * 40265: Src/pattern.c: fix continuing problems with Meta characters diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index d2f0c999b..cc879c445 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -917,7 +917,6 @@ struct cadef { int argsactive; /* if normal arguments are still allowed */ /* used while parsing a command line */ char *set; /* set name prefix (-), shared */ - char *sname; /* set name */ int flags; /* see CDF_* below */ char *nonarg; /* pattern for non-args (-A argument) */ }; @@ -956,7 +955,7 @@ struct caarg { char *end; /* end-pattern for :::... */ char *opt; /* option name if for an option */ int num; /* it's the num'th argument */ - int min; /* it's also this argument, using opt. args */ + int min; /* earliest possible arg pos, given optional args */ int direct; /* true if argument number was given explicitly */ int active; /* still allowed on command line */ char *set; /* set name, shared */ @@ -1020,7 +1019,6 @@ freecadef(Cadef d) s = d->snext; zsfree(d->match); zsfree(d->set); - zsfree(d->sname); if (d->defs) freearray(d->defs); @@ -1147,8 +1145,11 @@ alloc_cadef(char **args, int single, char *match, char *nonarg, int flags) ret->defs = NULL; ret->ndefs = 0; } + ret->nopts = 0; + ret->ndopts = 0; + ret->nodopts = 0; ret->lastt = time(0); - ret->set = ret->sname = NULL; + ret->set = NULL; if (single) { ret->single = (Caopt *) zalloc(256 * sizeof(Caopt)); memset(ret->single, 0, 256 * sizeof(Caopt)); @@ -1184,11 +1185,9 @@ parse_cadef(char *nam, char **args) char **orig_args = args, *p, *q, *match = "r:|[_-]=* r:|=*", **xor, **sargs; char *adpre, *adsuf, *axor = NULL, *doset = NULL, **setp = NULL; char *nonarg = NULL; - int single = 0, anum = 1, xnum, nopts, ndopts, nodopts, flags = 0; + int single = 0, anum = 1, xnum, flags = 0; int state = 0, not = 0; - nopts = ndopts = nodopts = 0; - /* First string is the auto-description definition. */ for (p = args[0]; *p && (p[0] != '%' || p[1] != 'd'); p++); @@ -1255,8 +1254,6 @@ parse_cadef(char *nam, char **args) all = ret = alloc_cadef(orig_args, single, match, nonarg, flags); optp = &(ret->opts); - anum = 1; - sargs = args; /* Get the definitions. */ @@ -1267,8 +1264,9 @@ parse_cadef(char *nam, char **args) char *p; int l; - if (setp) + if (setp) /* got to first set: skip to ours */ args = setp; + /* else we were the first set so don't need to skip */ p = *++args; l = strlen(p) - 1; if (*p == '(' && p[l] == ')') { @@ -1277,20 +1275,15 @@ parse_cadef(char *nam, char **args) } else axor = NULL; ret->set = doset = tricat(p, "-", ""); - ret->sname = ztrdup(p); state = 1; - } else { + } else { /* starting new set */ setp = args; state = 0; args = sargs - 1; doset = NULL; - ret->nopts = nopts; - ret->ndopts = ndopts; - ret->nodopts = nodopts; set_cadef_opts(ret); ret = ret->snext = alloc_cadef(NULL, single, match, nonarg, flags); optp = &(ret->opts); - nopts = ndopts = nodopts = 0; anum = 1; } continue; @@ -1526,13 +1519,13 @@ parse_cadef(char *nam, char **args) opt->xor = (again == 1 && xor ? zarrdup(xor) : xor); opt->type = otype; opt->args = oargs; - opt->num = nopts++; + opt->num = ret->nopts++; opt->not = not; if (otype == CAO_DIRECT || otype == CAO_EQUAL) - ndopts++; + ret->ndopts++; else if (otype == CAO_ODIRECT || otype == CAO_OEQUAL) - nodopts++; + ret->nodopts++; /* If this is for single-letter option we also store a * pointer for the definition in the array for fast lookup. @@ -1584,7 +1577,7 @@ parse_cadef(char *nam, char **args) continue; if ((direct = idigit(*p))) { - /* Argment number is given. */ + /* Argument number is given. */ int num = 0; while (*p && idigit(*p)) @@ -1630,9 +1623,6 @@ parse_cadef(char *nam, char **args) ret->args = arg; } } - ret->nopts = nopts; - ret->ndopts = ndopts; - ret->nodopts = nodopts; set_cadef_opts(ret); return all; @@ -1776,12 +1766,9 @@ ca_get_arg(Cadef d, int n) * d: option definitions for a set * pass either: * xor: a list if exclusions - * opts: if set, all options excluded leaving only nornal/rest arguments - * if ca_xor list initialised, exclusions are added to it */ - -static LinkList ca_xor; + * opts: if set, all options excluded leaving only nornal/rest arguments */ -static int +static void ca_inactive(Cadef d, char **xor, int cur, int opts, char *optname) { if ((xor || opts) && cur <= compcurrent) { @@ -1792,8 +1779,6 @@ ca_inactive(Cadef d, char **xor, int cur, int opts, char *optname) for (; (x = (opts ? "-" : *xor)); xor++) { if (optname && optname[0] == x[0] && strcmp(optname, x)) continue; - if (ca_xor) - addlinknode(ca_xor, x); set = 0; if (sl > 0) { if (strpfx(d->set, x)) { @@ -1846,7 +1831,6 @@ ca_inactive(Cadef d, char **xor, int cur, int opts, char *optname) break; } } - return 0; } /* State when parsing a command line. */ @@ -1875,7 +1859,6 @@ struct castate { int curpos; /* current word position */ int argend; /* total number of words */ int inopt; /* set to current word pos if word is a recognised option */ - int inrest; /* unused */ int inarg; /* in a normal argument */ int nth; /* number of current normal arg */ int doff; /* length of current option */ @@ -1978,7 +1961,7 @@ ca_parse_line(Cadef d, int multi, int first) state.argbeg = state.optbeg = state.nargbeg = state.restbeg = state.actopts = state.nth = state.inopt = state.inarg = state.opt = state.arg = 1; state.argend = argend = arrlen(compwords) - 1; - state.inrest = state.doff = state.singles = state.oopt = 0; + state.doff = state.singles = state.oopt = 0; state.curpos = compcurrent; state.args = znewlinklist(); state.oargs = (LinkList *) zalloc(d->nopts * sizeof(LinkList)); @@ -2025,10 +2008,9 @@ ca_parse_line(Cadef d, int multi, int first) remnulargs(line); untokenize(line); - if (ca_inactive(d, argxor, cur, 0, NULL) || - ((d->flags & CDF_SEP) && cur != compcurrent && !strcmp(line, "--"))) { - if (ca_inactive(d, NULL, cur, 1, NULL)) - return 1; + ca_inactive(d, argxor, cur, 0, NULL); + if ((d->flags & CDF_SEP) && cur != compcurrent && !strcmp(line, "--")) { + ca_inactive(d, NULL, cur, 1, NULL); continue; } @@ -2104,9 +2086,8 @@ ca_parse_line(Cadef d, int multi, int first) if (!state.oargs[state.curopt->num]) state.oargs[state.curopt->num] = znewlinklist(); - if (ca_inactive(d, state.curopt->xor, cur, 0, - (cur == compcurrent ? state.curopt->name : NULL))) - return 1; + ca_inactive(d, state.curopt->xor, cur, 0, + (cur == compcurrent ? state.curopt->name : NULL)); /* Collect the argument strings. Maybe. */ @@ -2159,9 +2140,8 @@ ca_parse_line(Cadef d, int multi, int first) if (!state.oargs[tmpopt->num]) state.oargs[tmpopt->num] = znewlinklist(); - if (ca_inactive(d, tmpopt->xor, cur, 0, - (cur == compcurrent ? tmpopt->name : NULL))) - return 1; + ca_inactive(d, tmpopt->xor, cur, 0, + (cur == compcurrent ? tmpopt->name : NULL)); } } if (state.def && @@ -2194,9 +2174,8 @@ ca_parse_line(Cadef d, int multi, int first) else if (state.arg && (!napat || cur <= compcurrent || !pattry(napat, line))) { /* Otherwise it's a normal argument. */ - if (napat && cur <= compcurrent && - ca_inactive(d, NULL, cur + 1, 1, NULL)) - return 1; + if (napat && cur <= compcurrent) + ca_inactive(d, NULL, cur + 1, 1, NULL); arglast = 1; /* if this is the first normal arg after an option, may have been @@ -2231,7 +2210,6 @@ ca_parse_line(Cadef d, int multi, int first) if (ca_laststate.def) break; - state.inrest = 0; state.opt = (cur == state.nargbeg + 1 && (!multi || !*line || *line == '-' || *line == '+')); @@ -2539,45 +2517,27 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) if (compcurrent > 1 && compwords[0]) { Cadef def; int cap = ca_parsed, multi, first = 1, use, ret = 0; - LinkList cax = ca_xor, nx; - LinkNode node; Castate states = NULL, sp; - char *xor[2]; ca_parsed = 0; - xor[1] = NULL; if (!(def = get_cadef(nam, args + 1))) return 1; multi = !!def->snext; /* if we have sets */ ca_parsed = cap; - ca_xor = (multi ? newlinklist() : NULL); while (def) { /* for each set */ use = !ca_parse_line(def, multi, first); - nx = ca_xor; - ca_xor = NULL; /* don't want to duplicate the xors in the list */ - while ((def = def->snext)) { - if (nx) { - for (node = firstnode(nx); node; incnode(node)) { - xor[0] = (char *) getdata(node); - if (!strcmp(xor[0], def->sname) || - ca_inactive(def, xor, compcurrent, 0, NULL)) - break; /* exclude this whole set */ - } - if (!node) /* continue with this set */ - break; - } - /* entire set was excluded, continue to next set */ - } - ca_xor = nx; + def = def->snext; if (use && def) { + /* entry needed so save it into list */ sp = (Castate) zalloc(sizeof(*sp)); memcpy(sp, &ca_laststate, sizeof(*sp)); sp->snext = states; states = sp; } else if (!use && !def) { + /* final entry not needed */ if (states) { freecastate(&ca_laststate); memcpy(&ca_laststate, states, sizeof(*sp)); @@ -2589,7 +2549,6 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) } first = 0; } - ca_xor = cax; ca_parsed = 1; ca_laststate.snext = states; @@ -2602,7 +2561,7 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) * things _arguments has to execute at this place on the line (the * sub-contexts are used as tags). * The return value is particularly important here, it says if - * there are arguments to completely at all. */ + * there are arguments to complete at all. */ { LinkList descr, act, subc; Caarg arg; diff --git a/Test/Y03arguments.ztst b/Test/Y03arguments.ztst index d09b118a2..0b797ac03 100644 --- a/Test/Y03arguments.ztst +++ b/Test/Y03arguments.ztst @@ -64,6 +64,20 @@ >line: {tst arg1 }{} >MESSAGE:{no more arguments} + tst_arguments -a '2:desc2:(arg2)' + comptest $'tst a1\t \t' +0:second argument but no first argument +>line: {tst a1}{} +>MESSAGE:{no more arguments} +>line: {tst a1 arg2 }{} + + tst_arguments '2:desc2:(arg2)' '*:rest:(rest)' + comptest $'tst \t\t\t' +0:second and rest arguments but no first argument +>line: {tst rest }{} +>line: {tst rest arg2 }{} +>line: {tst rest arg2 rest }{} + tst_arguments '-\+[opt]' comptest $'tst -\C-d' 0:-+ @@ -82,6 +96,14 @@ >line: {tst +o -o }{} >MESSAGE:{no arguments} + tst_arguments +-o + comptest $'tst \t' +0:option beginning with + and -, specified the other way around +>line: {tst }{} +>DESCRIPTION:{option} +>NO:{+o} +>NO:{-o} + tst_arguments '-o:1:(a):2:(b)' comptest $'tst \t\t\t' 0:two option arguments @@ -206,6 +228,15 @@ 0:opt_args with multiple arguments and quoting of colons and backslashes >line: {tst -a 1:x \2 1\:x:\\2 }{} + tst_arguments -a -b + comptest $'tst rest -\t\C-w\eb\C-b-\t' +0:option completion with rest arguments on the line but not in the specs +>line: {tst rest -}{} +>line: {tst -}{ rest } +>DESCRIPTION:{option} +>NO:{-a} +>NO:{-b} + tst_arguments '-a' '*::rest:{compadd - -b}' comptest $'tst arg -\t' 0:rest arguments @@ -409,7 +440,6 @@ 0:exclude own set from an option >line: {tst -m -a }{} -# the following two tests only verify the current questionable behaviour tst_arguments - set1 '(set2)-a' -m -n - set2 -a -t -u comptest $'tst -a -\t' 0:exclude later set from an option common to both @@ -417,10 +447,12 @@ >DESCRIPTION:{option} >NO:{-m} >NO:{-n} +>NO:{-t} +>NO:{-u} tst_arguments - set2 -a -t -u - set1 '(set2)-a' -m -n comptest $'tst -a -\t' -0:exclude later set from an option common to both +0:exclude earlier set from an option common to both >line: {tst -a -}{} >DESCRIPTION:{option} >NO:{-m} @@ -428,6 +460,11 @@ >NO:{-t} >NO:{-u} + tst_arguments -x - '(set1)' -a -b - '(set2)' -m -n + comptest $'tst -m -\t' +0:single option sets are still mutually exclusive +>line: {tst -m -x }{} + tst_arguments '(-)-h' -a -b -c comptest $'tst -h -\t' 0:exclude all other options @@ -459,6 +496,24 @@ F:shouldn't offer -b as it is already on the command-line >NO:{-d} F:the first tab press shouldn't offer -d since -a is on the command line + tst_arguments -s : -ad '(-d)-a' -b -ca -d + comptest $'tst -ad\t-b\t' +0:option clumping mixed with real option that looks like clump +>line: {tst -ad }{} +>line: {tst -ad -b}{} +>DESCRIPTION:{option} +>NO:{-a} +>NO:{-d} + + tst_arguments -s : '(-d)-a+:arg:(b)' '(-c)-b' -c -d + comptest $'tst -ab\t-\t' +0:option clumping mixed with direct argument +>line: {tst -ab }{} +>line: {tst -ab -}{} +>DESCRIPTION:{option} +>NO:{-b} +>NO:{-c} + tst_arguments '-a:arg' -b '(-b)-c' comptest $'tst -a -c -\t' 0:exclusion with option argument that looks like an option -- cgit v1.2.3 From 8be732cbcc248abfa82ee1b4d0031f684e79aa8b Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Wed, 4 Jan 2017 14:50:56 +0100 Subject: 40227: handle _arguments sets and rest arguments starting with a dash This is a new approach to the problem first covered by 39611: checking to see if an option-like argument belongs to one of the other sets. --- ChangeLog | 3 +++ Src/Zle/computil.c | 37 ++++++++++++++++++++++++++----------- Test/Y03arguments.ztst | 16 +++++++--------- 3 files changed, 36 insertions(+), 20 deletions(-) (limited to 'Src/Zle/computil.c') diff --git a/ChangeLog b/ChangeLog index 2f6023fdc..dee6ad7dd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2017-01-04 Oliver Kiddle + * 40227: Src/Zle/computil.c, Test/Y03arguments.ztst: new approach + to 39611 (_arguments sets and rest arguments starting with a dash) + * 40226: Src/Zle/computil.c, Test/Y03arguments.ztst: tidy up some of the _arguments set code diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index cc879c445..82144656d 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -1741,6 +1741,27 @@ ca_get_sopt(Cadef d, char *line, char **end, LinkList *lp) return pp; } +/* Search for an option in all sets except the current one. + * Return true if found */ + +static int +ca_foreign_opt(Cadef curset, Cadef all, char *option) +{ + Cadef d; + Caopt p; + + for (d = all; d; d = d->snext) { + if (d == curset) + continue; + + for (p = d->opts; p; p = p->next) { + if (!strcmp(p->name, option)) + return 1; + } + } + return 0; +} + /* Return the n'th argument definition. */ static Caarg @@ -1917,7 +1938,7 @@ ca_opt_arg(Caopt opt, char *line) * existing options on the line. */ static int -ca_parse_line(Cadef d, int multi, int first) +ca_parse_line(Cadef d, Cadef all, int multi, int first) { Caarg adef, ddef; Caopt ptr, wasopt = NULL, dopt; @@ -2163,13 +2184,7 @@ ca_parse_line(Cadef d, int multi, int first) else state.curopt = NULL; } else if (multi && (*line == '-' || *line == '+') && cur != compcurrent -#if 0 - /**** Ouch. Using this will disable the mutual exclusion - of different sets. Not using it will make the -A - pattern be effectively ignored with multiple sets. */ - && (!napat || !pattry(napat, line)) -#endif - ) + && (ca_foreign_opt(d, all, line))) return 1; else if (state.arg && (!napat || cur <= compcurrent || !pattry(napat, line))) { @@ -2515,20 +2530,20 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) * auto-description string, the optional -s, -S, -A and -M options * given to _arguments and the specs. */ if (compcurrent > 1 && compwords[0]) { - Cadef def; + Cadef def, all; int cap = ca_parsed, multi, first = 1, use, ret = 0; Castate states = NULL, sp; ca_parsed = 0; - if (!(def = get_cadef(nam, args + 1))) + if (!(def = all = get_cadef(nam, args + 1))) return 1; multi = !!def->snext; /* if we have sets */ ca_parsed = cap; while (def) { /* for each set */ - use = !ca_parse_line(def, multi, first); + use = !ca_parse_line(def, all, multi, first); def = def->snext; if (use && def) { /* entry needed so save it into list */ diff --git a/Test/Y03arguments.ztst b/Test/Y03arguments.ztst index 0b797ac03..47f4747b8 100644 --- a/Test/Y03arguments.ztst +++ b/Test/Y03arguments.ztst @@ -358,26 +358,24 @@ 0:repeatable options >line: {tst -v -v }{} -# necessary to exclude the rest arguments for the other set because -# it is currently any unknown option rather than options from another -# set that causes a set to be excluded tst_arguments -A '-*' - help -h -V - other -a '*: :(-x more)' comptest $'tst -a -x m\t' 0:continue completion after rest argument that looks like an option (with sets) ->line: {tst -a -x m}{} -#>line: {tst -a -x more }{} +>line: {tst -a -x more }{} tst_arguments - '(help)' -h -V - other -a '*:rest:(1 2 3)' - comptest $'tst -h \t' -0:unknown option disables whole set (without -A) + comptest $'tst -h \t-a \t' +0:foreign option disables whole set (without -A) >line: {tst -h }{} >MESSAGE:{no arguments} +>line: {tst -h -a }{} tst_arguments -A "-*" - '(help)' -h -V - other -a '*:rest:(1 2 3)' - comptest $'tst -h \t' -0:unknown option disables whole set (with -A) + comptest $'tst -h \t-a \t' +0:foreign option disables whole set (with -A) >line: {tst -h }{} >MESSAGE:{no arguments} +>line: {tst -h -a }{} tst_arguments '(-C)-a' - set1 -C -v - set2 '(-a)-C' -w comptest $'tst -a -\t' $'\C-w\C-w-C -\t' -- cgit v1.2.3 From c2b7c5abbe615bfbd20a910d4093ac3ccdf5525f Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Thu, 5 Jan 2017 13:31:13 +0100 Subject: 40269: handle option exclusion within current word for clumped options This replaces the change made in 13999 with an alternative approach. --- ChangeLog | 5 +++++ Src/Zle/computil.c | 26 +++++++++++++------------- Test/Y03arguments.ztst | 25 +++++++++++++++++++------ 3 files changed, 37 insertions(+), 19 deletions(-) (limited to 'Src/Zle/computil.c') diff --git a/ChangeLog b/ChangeLog index dee6ad7dd..49b7da6b4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2017-01-05 Oliver Kiddle + + * 40269: Src/Zle/computil.c, Test/Y03arguments.ztst: + handle option exclusion within current word for clumped options + 2017-01-04 Oliver Kiddle * 40227: Src/Zle/computil.c, Test/Y03arguments.ztst: new approach diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index 82144656d..12aa8950f 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -1790,16 +1790,17 @@ ca_get_arg(Cadef d, int n) * opts: if set, all options excluded leaving only nornal/rest arguments */ static void -ca_inactive(Cadef d, char **xor, int cur, int opts, char *optname) +ca_inactive(Cadef d, char **xor, int cur, int opts) { if ((xor || opts) && cur <= compcurrent) { Caopt opt; char *x; int sl = (d->set ? (int)strlen(d->set) : -1), set = 0; + /* current word could be a prefix of a longer one so only do + * exclusions for single-letter options (for option clumping) */ + int single = (cur == compcurrent); for (; (x = (opts ? "-" : *xor)); xor++) { - if (optname && optname[0] == x[0] && strcmp(optname, x)) - continue; set = 0; if (sl > 0) { if (strpfx(d->set, x)) { @@ -1809,7 +1810,7 @@ ca_inactive(Cadef d, char **xor, int cur, int opts, char *optname) Caopt p; for (p = d->opts; p; p = p->next) - if (p->set) + if (p->set && !(single && *p->name && p->name[1] && p->name[2])) p->active = 0; x = ":"; @@ -1831,7 +1832,7 @@ ca_inactive(Cadef d, char **xor, int cur, int opts, char *optname) Caopt p; for (p = d->opts; p; p = p->next) - if (!set || p->set) + if ((!set || p->set) && !(single && *p->name && p->name[1] && p->name[2])) p->active = 0; } else if (x[0] == '*' && !x[1]) { if (d->rest && (!set || d->rest->set)) @@ -1845,7 +1846,8 @@ ca_inactive(Cadef d, char **xor, int cur, int opts, char *optname) if (a && a->num == n && (!set || a->set)) a->active = 0; - } else if ((opt = ca_get_opt(d, x, 1, NULL)) && (!set || opt->set)) + } else if ((opt = ca_get_opt(d, x, 1, NULL)) && (!set || opt->set) && + !(single && *opt->name && opt->name[1] && opt->name[2])) opt->active = 0; if (opts) @@ -2029,9 +2031,9 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first) remnulargs(line); untokenize(line); - ca_inactive(d, argxor, cur, 0, NULL); + ca_inactive(d, argxor, cur, 0); if ((d->flags & CDF_SEP) && cur != compcurrent && !strcmp(line, "--")) { - ca_inactive(d, NULL, cur, 1, NULL); + ca_inactive(d, NULL, cur, 1); continue; } @@ -2107,8 +2109,7 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first) if (!state.oargs[state.curopt->num]) state.oargs[state.curopt->num] = znewlinklist(); - ca_inactive(d, state.curopt->xor, cur, 0, - (cur == compcurrent ? state.curopt->name : NULL)); + ca_inactive(d, state.curopt->xor, cur, 0); /* Collect the argument strings. Maybe. */ @@ -2161,8 +2162,7 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first) if (!state.oargs[tmpopt->num]) state.oargs[tmpopt->num] = znewlinklist(); - ca_inactive(d, tmpopt->xor, cur, 0, - (cur == compcurrent ? tmpopt->name : NULL)); + ca_inactive(d, tmpopt->xor, cur, 0); } } if (state.def && @@ -2190,7 +2190,7 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first) (!napat || cur <= compcurrent || !pattry(napat, line))) { /* Otherwise it's a normal argument. */ if (napat && cur <= compcurrent) - ca_inactive(d, NULL, cur + 1, 1, NULL); + ca_inactive(d, NULL, cur + 1, 1); arglast = 1; /* if this is the first normal arg after an option, may have been diff --git a/Test/Y03arguments.ztst b/Test/Y03arguments.ztst index 47f4747b8..5957bc306 100644 --- a/Test/Y03arguments.ztst +++ b/Test/Y03arguments.ztst @@ -479,12 +479,9 @@ F:shouldn't offer -b as it is already on the command-line tst_arguments -s : '(-d)-a' -b -c -d - comptest $'tst -ab\t -\t\eb\eb \C-b-\t' + comptest $'tst -ab\t\C-h -\t\eb\eb \C-b-\t' 0:exclusion with clumped options, in, after and before ->line: {tst -ab}{} ->DESCRIPTION:{option} ->NO:{-c} ->NO:{-d} +>line: {tst -abc}{} >line: {tst -ab -c }{} >line: {tst -}{ -ab -c} >DESCRIPTION:{option} @@ -492,7 +489,23 @@ F:shouldn't offer -b as it is already on the command-line >NO:{-b} >NO:{-c} >NO:{-d} -F:the first tab press shouldn't offer -d since -a is on the command line + + tst_arguments -s '(-conf)-c' '-conf' '-f' '(-)--long' --longer + comptest $'tst -c\t\C-h-long\t' +0:don't prematurely exclude option that current word is a prefix of +>line: {tst -c}{} +>DESCRIPTION:{option} +>NO:{-conf} +>NO:{-f} +>line: {tst --long}{} +>DESCRIPTION:{option} +>NO:{--long} +>NO:{--longer} + + tst_arguments -s '(set)-c' - set '-conf' '-f' + comptest $'tst -c\t' +0:don't prematurely exclude option that current word is a prefix of (with sets) +>line: {tst -conf }{} tst_arguments -s : -ad '(-d)-a' -b -ca -d comptest $'tst -ad\t-b\t' -- cgit v1.2.3 From b6082cd1e2bbeb3f0538789c244e59eca4838ce8 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Wed, 11 Jan 2017 20:49:32 +0100 Subject: 40321: _arguments option groups --- ChangeLog | 5 ++ Doc/Zsh/compsys.yo | 81 ++++++++++++--------- Src/Zle/computil.c | 191 +++++++++++++++++++++++++++++++------------------ Test/Y03arguments.ztst | 77 ++++++++++++++++++++ 4 files changed, 248 insertions(+), 106 deletions(-) (limited to 'Src/Zle/computil.c') diff --git a/ChangeLog b/ChangeLog index da1ece42a..78f590c87 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2017-01-11 Oliver Kiddle + + * 40321: Doc/Zsh/compsys.yo, Src/Zle/computil.c, + Test/Y03arguments.ztst: _arguments option groups + 2017-01-11 Peter Stephenson * unposted: Src/builtin.c, Src/exec.c: be more careful to free diff --git a/Doc/Zsh/compsys.yo b/Doc/Zsh/compsys.yo index 953d51c4c..2a112ed15 100644 --- a/Doc/Zsh/compsys.yo +++ b/Doc/Zsh/compsys.yo @@ -3573,13 +3573,11 @@ This function can be used to give a complete specification for completion for a command whose arguments follow standard UNIX option and argument conventions. -em(Options overview) +em(Options Overview) Options to tt(_arguments) itself must be in separate words, i.e. tt(-s -w), not tt(-sw). The options are followed by var(spec)s that describe options and -arguments of the analyzed command. var(spec)s that describe option flags must -precede var(spec)s that describe non-option ("positional" or "normal") -arguments of the analyzed line. To avoid ambiguity, all +arguments of the analyzed command. To avoid ambiguity, all options to tt(_arguments) itself may be separated from the var(spec) forms by a single colon. @@ -3997,18 +3995,48 @@ example(local curcontext="$curcontext") This is useful where it is not possible for multiple states to be valid together. -em(Specifying multiple sets of options) +em(Grouping Options) -It is possible to specify multiple sets of options and -arguments with the sets separated by single hyphens. The specifications -before the first hyphen (if any) are shared by all the remaining sets. -The first word in every other set provides a name for the -set which may appear in exclusion lists in specifications, -either alone or before one of the possible values described above. -In the second case a `tt(-)' should appear between this name and the -remainder. +Options can be grouped to simplify exclusion lists. A group is +introduced with `tt(PLUS())' followed by a name for the group in the +subsequent word. Whole groups can then be referenced in an exclusion +list or a group name can be used to disambiguate between two forms of +the same option. For example: -For example: +example(_arguments \ + '(group2--x)-a' \ + PLUS() group1 \ + -m \ + '(group2)-n' \ + PLUS() group2 \ + -x -y) + +If the name of a group is specified in the form +`tt(LPAR())var(name)tt(RPAR())' then only one value from that group +will ever be completed; more formally, all specifications are mutually +exclusive to all other specifications in that group. This is useful for +defining options that are aliases for each other. For example: + +example(_arguments \ + -a -b \ + PLUS() '(operation)' \ + {-c,--compress}'[compress]' \ + {-d,--decompress}'[decompress]' \ + {-l,--list}'[list]') + +If an option in a group appears on the command line, it is stored in the +associative array `tt(opt_args)' with 'var(group)tt(-)var(option)' +as a key. In the example above, a key `tt(operation--c)' is used if the option +`tt(-c)' is present on the command line. + +em(Specifying Multiple Sets of Arguments) + +It is possible to specify multiple sets of options and arguments with +the sets separated by single hyphens. This differs from groups in that +sets are considered to be mutually exclusive of each other. + +Specifications before the first set and from any group are common to +all sets. For example: example(_arguments \ -a \ @@ -4024,28 +4052,11 @@ possible completions. When it contains `tt(-d)' or an argument, the option `tt(-c)' will not be considered. However, after `tt(-a)' both sets will still be considered valid. -If an option in a set appears on the command line, it is stored in the -associative array `tt(opt_args)' with 'var(set)tt(-)var(option)' -as a key. In the example above, a key `tt(set1--c)' is used if the option -`tt(-c)' is on the command line. - -If the name given for one of the mutually exclusive sets is of the form -`tt(LPAR())var(name)tt(RPAR())' then only one value from each set will ever -be completed; more formally, all specifications are mutually -exclusive to all other specifications in the same set. This is -useful for defining multiple sets of options which are mutually -exclusive and in which the options are aliases for each other. For -example: - -example(_arguments \ - -a -b \ - - '(compress)' \ - {-c,--compress}'[compress]' \ - - '(uncompress)' \ - {-d,--decompress}'[decompress]') +As for groups, the name of a set may appear in exclusion lists, either +alone or preceding a normal option or argument specification. -As the completion code has to parse the command line separately for each -set this form of argument is slow and should only be used when necessary. +The completion code has to parse the command line separately for each +set. This can be slow so sets should only be used when necessary. A useful alternative is often an option specification with rest-arguments (as in `tt(-foo:*:...)'); here the option tt(-foo) swallows up all remaining arguments as described by the var(optarg) definitions. diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index 12aa8950f..7bf95351f 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -934,7 +934,7 @@ struct caopt { Caarg args; /* option arguments */ int active; /* still allowed on command line */ int num; /* it's the num'th option */ - char *set; /* set name, shared */ + char *gsname; /* group or set name, shared */ int not; /* don't complete this option (`!...') */ }; @@ -958,7 +958,7 @@ struct caarg { int min; /* earliest possible arg pos, given optional args */ int direct; /* true if argument number was given explicitly */ int active; /* still allowed on command line */ - char *set; /* set name, shared */ + char *gsname; /* group or set name, shared */ }; #define CAA_NORMAL 1 @@ -1096,7 +1096,7 @@ parse_caarg(int mult, int type, int num, int opt, char *oname, char **def, ret->type = type; ret->opt = ztrdup(oname); ret->direct = 0; - ret->set = set; + ret->gsname = set; /* Get the description. */ @@ -1183,10 +1183,10 @@ parse_cadef(char *nam, char **args) Cadef all, ret; Caopt *optp; char **orig_args = args, *p, *q, *match = "r:|[_-]=* r:|=*", **xor, **sargs; - char *adpre, *adsuf, *axor = NULL, *doset = NULL, **setp = NULL; + char *adpre, *adsuf, *axor = NULL, *doset = NULL, **pendset = NULL, **curset = NULL; char *nonarg = NULL; int single = 0, anum = 1, xnum, flags = 0; - int state = 0, not = 0; + int foreignset = 0, not = 0; /* First string is the auto-description definition. */ @@ -1249,7 +1249,6 @@ parse_cadef(char *nam, char **args) if (nonarg) tokenize(nonarg = dupstring(nonarg)); - /* Looks good. Optimistically allocate the cadef structure. */ all = ret = alloc_cadef(orig_args, single, match, nonarg, flags); @@ -1258,36 +1257,64 @@ parse_cadef(char *nam, char **args) /* Get the definitions. */ - for (; *args; args++) { + for (; *args || pendset; args++) { + if (!*args) { + /* start new set */ + args = sargs; /* go back and repeat parse of common options */ + doset = NULL; + set_cadef_opts(ret); + ret = ret->snext = alloc_cadef(NULL, single, match, nonarg, flags); + optp = &(ret->opts); + anum = 1; + foreignset = 0; + curset = pendset; + pendset = 0; + } if (args[0][0] == '-' && !args[0][1] && args[1]) { - if (!state) { - char *p; - int l; - - if (setp) /* got to first set: skip to ours */ - args = setp; - /* else we were the first set so don't need to skip */ - p = *++args; - l = strlen(p) - 1; + if ((foreignset = curset && args != curset)) { + if (!pendset && args > curset) + pendset = args; /* mark pointer to next pending set */ + ++args; + } else { + /* Carrying on: this is the current set */ + char *p = *++args; + int l = strlen(p) - 1; + if (*p == '(' && p[l] == ')') { axor = p = dupstring(p + 1); p[l - 1] = '\0'; } else axor = NULL; + if (!*p) { + freecadef(all); + zwarnnam(nam, "empty set name"); + return NULL; + } ret->set = doset = tricat(p, "-", ""); - state = 1; - } else { /* starting new set */ - setp = args; - state = 0; - args = sargs - 1; - doset = NULL; - set_cadef_opts(ret); - ret = ret->snext = alloc_cadef(NULL, single, match, nonarg, flags); - optp = &(ret->opts); - anum = 1; + curset = args; /* needed for the first set */ } continue; - } + } else if (args[0][0] == '+' && !args[0][1] && args[1]) { + char *p; + int l; + + foreignset = 0; /* group not in any set, don't want to skip it */ + p = *++args; + l = strlen(p) - 1; + if (*p == '(' && p[l] == ')') { + axor = p = dupstring(p + 1); + p[l - 1] = '\0'; + } else + axor = NULL; + if (!*p) { + freecadef(all); + zwarnnam(nam, "empty group name"); + return NULL; + } + doset = tricat(p, "-", ""); + continue; + } else if (foreignset) /* skipping over a different set */ + continue; p = dupstring(*args); xnum = 0; if ((not = (*p == '!'))) @@ -1499,7 +1526,7 @@ parse_cadef(char *nam, char **args) optp = &((*optp)->next); opt->next = NULL; - opt->set = doset; + opt->gsname = doset; opt->name = ztrdup(rembslashcolon(name)); if (descr) opt->descr = ztrdup(descr); @@ -1795,63 +1822,85 @@ ca_inactive(Cadef d, char **xor, int cur, int opts) if ((xor || opts) && cur <= compcurrent) { Caopt opt; char *x; - int sl = (d->set ? (int)strlen(d->set) : -1), set = 0; /* current word could be a prefix of a longer one so only do * exclusions for single-letter options (for option clumping) */ int single = (cur == compcurrent); for (; (x = (opts ? "-" : *xor)); xor++) { - set = 0; - if (sl > 0) { - if (strpfx(d->set, x)) { - x += sl; - set = 1; - } else if (!strncmp(d->set, x, sl - 1)) { - Caopt p; - - for (p = d->opts; p; p = p->next) - if (p->set && !(single && *p->name && p->name[1] && p->name[2])) - p->active = 0; - - x = ":"; - set = 1; + int excludeall = 0; + char *grp = NULL; + size_t grplen; + char *next, *sep = x; + + while (*sep != '+' && *sep != '-' && *sep != ':' && *sep != '*' && !idigit(*sep)) { + if (!(next = strchr(sep, '-')) || !*++next) { + /* exclusion is just the name of a set or group */ + excludeall = 1; /* excluding options and args */ + sep += strlen(sep); + /* A trailing '-' is included in the various gsname fields but is not + * there for this branch. This is why we add excludeall to grplen + * when checking for the null in a few places below */ + break; } + sep = next; + } + if (sep > x) { /* exclusion included a set or group name */ + grp = x; + grplen = sep - grp; + x = sep; } - if (x[0] == ':' && !x[1]) { - if (set) { + + if (excludeall || (x[0] == ':' && !x[1])) { + if (grp) { Caarg a; for (a = d->args; a; a = a->next) - if (a->set) + if (a->gsname && !strncmp(a->gsname, grp, grplen) && + !a->gsname[grplen + excludeall]) a->active = 0; - if (d->rest && (!set || d->rest->set)) + if (d->rest && d->rest->gsname && + !strncmp(d->rest->gsname, grp, grplen) && + !d->rest->gsname[grplen + excludeall]) d->rest->active = 0; } else d->argsactive = 0; - } else if (x[0] == '-' && !x[1]) { + } + + if (excludeall || (x[0] == '-' && !x[1])) { Caopt p; for (p = d->opts; p; p = p->next) - if ((!set || p->set) && !(single && *p->name && p->name[1] && p->name[2])) + if ((!grp || (p->gsname && !strncmp(p->gsname, grp, grplen) && + !p->gsname[grplen + excludeall])) && + !(single && *p->name && p->name[1] && p->name[2])) p->active = 0; - } else if (x[0] == '*' && !x[1]) { - if (d->rest && (!set || d->rest->set)) - d->rest->active = 0; - } else if (idigit(x[0])) { - int n = atoi(x); - Caarg a = d->args; - - while (a && a->num < n) - a = a->next; + } - if (a && a->num == n && (!set || a->set)) - a->active = 0; - } else if ((opt = ca_get_opt(d, x, 1, NULL)) && (!set || opt->set) && - !(single && *opt->name && opt->name[1] && opt->name[2])) - opt->active = 0; + if (excludeall || (x[0] == '*' && !x[1])) { + if (d->rest && (!grp || (d->rest->gsname && + !strncmp(d->rest->gsname, grp, grplen) && + !d->rest->gsname[grplen + excludeall]))) + d->rest->active = 0; + } - if (opts) - break; + if (!excludeall) { + if (idigit(x[0])) { + int n = atoi(x); + Caarg a = d->args; + + while (a && a->num < n) + a = a->next; + + if (a && a->num == n && (!grp || (a->gsname && + !strncmp(a->gsname, grp, grplen)))) + a->active = 0; + } else if ((opt = ca_get_opt(d, x, 1, NULL)) && + (!grp || (opt->gsname && !strncmp(opt->gsname, grp, grplen))) && + !(single && *opt->name && opt->name[1] && opt->name[2])) + opt->active = 0; + if (opts) + break; + } } } } @@ -2414,19 +2463,19 @@ ca_set_data(LinkList descr, LinkList act, LinkList subc, restrict_range(ca_laststate.argbeg, ca_laststate.argend); } if (arg->opt) { - buf = (char *) zhalloc((arg->set ? strlen(arg->set) : 0) + + buf = (char *) zhalloc((arg->gsname ? strlen(arg->gsname) : 0) + strlen(arg->opt) + 40); if (arg->num > 0 && arg->type < CAA_REST) sprintf(buf, "%soption%s-%d", - (arg->set ? arg->set : ""), arg->opt, arg->num); + (arg->gsname ? arg->gsname : ""), arg->opt, arg->num); else sprintf(buf, "%soption%s-rest", - (arg->set ? arg->set : ""), arg->opt); + (arg->gsname ? arg->gsname : ""), arg->opt); } else if (arg->num > 0) { sprintf(nbuf, "argument-%d", arg->num); - buf = (arg->set ? dyncat(arg->set, nbuf) : dupstring(nbuf)); + buf = (arg->gsname ? dyncat(arg->gsname, nbuf) : dupstring(nbuf)); } else - buf = (arg->set ? dyncat(arg->set, "argument-rest") : + buf = (arg->gsname ? dyncat(arg->gsname, "argument-rest") : dupstring("argument-rest")); addlinknode(subc, buf); @@ -2779,7 +2828,7 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) for (s = lstate; s; s = s->snext) for (o = s->d->opts, a = s->oargs; o; o = o->next, a++) if (*a) { - *p++ = (o->set ? tricat(o->set, o->name, "") : + *p++ = (o->gsname ? tricat(o->gsname, o->name, "") : ztrdup(o->name)); *p++ = ca_colonlist(*a); } diff --git a/Test/Y03arguments.ztst b/Test/Y03arguments.ztst index 5957bc306..25bb96b84 100644 --- a/Test/Y03arguments.ztst +++ b/Test/Y03arguments.ztst @@ -463,6 +463,14 @@ 0:single option sets are still mutually exclusive >line: {tst -m -x }{} + tst_arguments '(set-c set-g)-a' '(set)-b' -c + grp -g - set -s + comptest $'tst -a -b -\t' +0:excluding a set doesn't exclude common options as part of the set +>line: {tst -a -b -}{} +>DESCRIPTION:{option} +>NO:{-c} +>NO:{-g} + tst_arguments '(-)-h' -a -b -c comptest $'tst -h -\t' 0:exclude all other options @@ -536,6 +544,75 @@ F:>DESCRIPTION:{option} F:>NO:{-b} F:>NO:{-c} + tst_arguments + grp1 -a -b - onlyset '(-a grp3--y grp2 grp4--)-m' -n + grp2 -u -v + grp3 -x -y + grp4 -0 ':rest' + comptest $'tst -m -\t' +0:exclude group options +>line: {tst -m -}{} +>DESCRIPTION:{rest} +>DESCRIPTION:{option} +>NO:{-b} +>NO:{-n} +>NO:{-x} + + tst_arguments -x + '(grp1)' -a -b + '(grp2)' -m -n + comptest $'tst -m -\t' +0:single option groups are not mutually exclusive +>line: {tst -m -}{} +>DESCRIPTION:{option} +>NO:{-a} +>NO:{-b} +>NO:{-x} + + tst_arguments '(grp1 grp2-2)-a' '(grp1-*)-b' + grp1 ':first' '*:rest' + grp2 ':second' + comptest $'tst -a \t\eb\C-w\C-e x y \t' +0:exclude rest args listed within a group +>line: {tst -a -b }{} +>line: {tst -b x y -a }{} + + tst_arguments '(grp--m)-a' + grp '-m:value:(a b c)' + agrp '-m:other:(1 2 3)' + comptest $'tst -m \t\C-w-a -m \t' +0:two forms of same option in different groups +>line: {tst -m }{} +>DESCRIPTION:{value} +>NO:{a} +>NO:{b} +>NO:{c} +>line: {tst -a -m }{} +>DESCRIPTION:{other} +>NO:{1} +>NO:{2} +>NO:{3} +F:should offer both sets of arguments in first case + + tst_arguments '(grp--m)-x' + '(grp)' -a -b -c ':val:(1 2 3)' + comptest $'tst 1 -\t' +0:normal argument excludes options in internally mutually exclusive group +>line: {tst 1 -x }{} + + tst_arguments -s -a - set1 -c -s - set2 -c -t + grp -d + comptest $'tst -s\t -\t' +0:mix sets, groups and option stacking +>line: {tst -s}{} +>DESCRIPTION:{option} +>NO:{-a} +>NO:{-c} +>NO:{-d} +>NO:{-t} +>line: {tst -s -}{} +>DESCRIPTION:{option} +>NO:{-a} +>NO:{-c} +>NO:{-d} +F:shouldn't offer -t in the first case (with stacked options) + + tst_arguments -s '(set-a set--b grp-a grp--b grp-)-a' - set-a -s - set--b -t + grp-a -g + grp--b -h + grp- -i + comptest $'tst -a\t' +0:sets and groups with - in their name +>line: {tst -a}{} +>DESCRIPTION:{option} +>NO:{-h} +>NO:{-t} + tst_arguments --abc --aah :arg: comptesteval 'setopt bashautolist automenu' comptest $'tst --a\t\t\t' -- cgit v1.2.3 From ebd7fdd9326cf36feda3b0ca906d7b9b8bf79cb5 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Sun, 15 Jan 2017 17:15:04 +0000 Subject: 40362: Fix setting of parameter values in compvalues --- ChangeLog | 6 ++++++ Src/Zle/computil.c | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'Src/Zle/computil.c') diff --git a/ChangeLog b/ChangeLog index 37b1a0b1e..02a4469c3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2017-01-15 Peter Stephenson + + * 40362: Src/Zle/computil.c: need duplicated values for setting + parameter in compvalues. Was causing obscure but surprisingly + rare crashes in value completion. + 2017-01-13 Eric Cook * earnestly: 40355: Completion/Unix/Command/_mpc: improve diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index 7bf95351f..5b9ceec1a 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -3569,8 +3569,8 @@ bin_compvalues(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) Cvval val = cv_get_val(cv_laststate.d, args[1]); if (val && val->arg) { - setsparam(args[2], val->arg->descr); - setsparam(args[3], val->arg->action); + setsparam(args[2], ztrdup(val->arg->descr)); + setsparam(args[3], ztrdup(val->arg->action)); if (args[4]) setsparam(args[4], ztrdup(val->name)); -- cgit v1.2.3 From e51c9c17af51e4055efb5a2cc36739d1d7ae457f Mon Sep 17 00:00:00 2001 From: "Barton E. Schaefer" Date: Sun, 29 Jan 2017 08:30:14 -0800 Subject: 40453: signal handler safety for callers of patcompile(PAT_STATIC), which is not re-entrant. --- ChangeLog | 6 ++++++ Src/Modules/zpty.c | 14 ++++++++++---- Src/Modules/zutil.c | 20 +++++++++++++++++--- Src/Zle/compctl.c | 32 +++++++++++++++++++++++++------- Src/Zle/complete.c | 10 ++++++++++ Src/Zle/computil.c | 8 ++++++++ Src/Zle/zle_hist.c | 10 ++++++---- Src/builtin.c | 37 ++++++++++++++++++++----------------- Src/cond.c | 5 +++++ Src/glob.c | 17 ++++++++++++----- Src/loop.c | 6 +++++- Src/options.c | 2 +- Src/parse.c | 1 + 13 files changed, 126 insertions(+), 42 deletions(-) (limited to 'Src/Zle/computil.c') diff --git a/ChangeLog b/ChangeLog index 87a3bbd56..abaa39471 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2017-01-28 Barton E. Schaefer + * 40453: Src/Modules/zpty.c, Src/Modules/zutil.c, + Src/Zle/compctl.c, Src/Zle/complete.c, Src/Zle/computil.c, + Src/Zle/zle_hist.c, Src/builtin.c, Src/cond.c, Src/glob.c, + Src/loop.c, Src/options.c, Src/parse.c: signal handler safety + for callers of patcompile(PAT_STATIC), which is not re-entrant. + * 40439: Src/zsh.h: PAT_HEAPDUP definition just for clarity 2017-01-28 Peter Stephenson diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c index 0ef753915..2c87be16f 100644 --- a/Src/Modules/zpty.c +++ b/Src/Modules/zpty.c @@ -544,7 +544,8 @@ ptyread(char *nam, Ptycmd cmd, char **args, int noblock, int mustmatch) p = dupstring(args[1]); tokenize(p); remnulargs(p); - if (!(prog = patcompile(p, PAT_STATIC, NULL))) { + /* Signals handlers might stomp PAT_STATIC */ + if (!(prog = patcompile(p, PAT_ZDUP, NULL))) { zwarnnam(nam, "bad pattern: %s", args[1]); return 1; } @@ -682,9 +683,14 @@ ptyread(char *nam, Ptycmd cmd, char **args, int noblock, int mustmatch) write_loop(1, buf, used); } - if (seen && (!prog || matchok || !mustmatch)) - return 0; - return cmd->fin + 1; + { + int ret = cmd->fin + 1; + if (seen && (!prog || matchok || !mustmatch)) + ret = 0; + if (prog) + freepatprog(prog); + return ret; + } } static int diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c index d95c0c254..19a8306b5 100644 --- a/Src/Modules/zutil.c +++ b/Src/Modules/zutil.c @@ -510,25 +510,33 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) zwarnnam(nam, "too many arguments"); return 1; } + + queue_signals(); /* Protect PAT_STATIC */ + if (context) { tokenize(context); zstyle_contprog = patcompile(context, PAT_STATIC, NULL); - if (!zstyle_contprog) + if (!zstyle_contprog) { + unqueue_signals(); return 1; + } } else zstyle_contprog = NULL; if (stylename) { s = (Style)zstyletab->getnode2(zstyletab, stylename); - if (!s) + if (!s) { + unqueue_signals(); return 1; + } zstyletab->printnode(&s->node, list); } else { scanhashtable(zstyletab, 1, 0, 0, zstyletab->printnode, list); } + unqueue_signals(); return 0; } switch (args[0][1]) { @@ -675,14 +683,20 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) char **vals; Patprog prog; + queue_signals(); /* Protect PAT_STATIC */ + tokenize(args[3]); if ((vals = lookupstyle(args[1], args[2])) && (prog = patcompile(args[3], PAT_STATIC, NULL))) { while (*vals) - if (pattry(prog, *vals++)) + if (pattry(prog, *vals++)) { + unqueue_signals(); return 0; + } } + + unqueue_signals(); return 1; } break; diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index 52c6f1233..9e6ccb404 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -99,7 +99,7 @@ freecompctlp(HashNode hn) } /**/ -void +static void freecompctl(Compctl cc) { if (cc == &cc_default || @@ -142,7 +142,7 @@ freecompctl(Compctl cc) } /**/ -void +static void freecompcond(void *a) { Compcond cc = (Compcond) a; @@ -186,7 +186,7 @@ freecompcond(void *a) } /**/ -int +static int compctlread(char *name, char **args, Options ops, char *reply) { char *buf, *bptr; @@ -1564,6 +1564,8 @@ bin_compctl(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) Compctl cc = NULL; int ret = 0; + queue_signals(); + /* clear static flags */ cclist = 0; showmask = 0; @@ -1571,12 +1573,15 @@ bin_compctl(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) /* Parse all the arguments */ if (*argv) { /* Let's see if this is a global matcher definition. */ - if ((ret = get_gmatcher(name, argv))) + if ((ret = get_gmatcher(name, argv))) { + unqueue_signals(); return ret - 1; + } cc = (Compctl) zshcalloc(sizeof(*cc)); if (get_compctl(name, &argv, cc, 1, 0, 0)) { freecompctl(cc); + unqueue_signals(); return 1; } @@ -1604,6 +1609,7 @@ bin_compctl(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) printcompctl((cclist & COMP_LIST) ? "" : "DEFAULT", &cc_default, 0, 0); printcompctl((cclist & COMP_LIST) ? "" : "FIRST", &cc_first, 0, 0); print_gmatcher((cclist & COMP_LIST)); + unqueue_signals(); return ret; } @@ -1642,6 +1648,7 @@ bin_compctl(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) printcompctl("", &cc_first, 0, 0); if (cclist & COMP_LISTMATCH) print_gmatcher(COMP_LIST); + unqueue_signals(); return ret; } @@ -1656,6 +1663,7 @@ bin_compctl(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) compctl_process_cc(argv, cc); } + unqueue_signals(); return ret; } @@ -1667,12 +1675,18 @@ bin_compctl(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) static int bin_compcall(char *name, UNUSED(char **argv), Options ops, UNUSED(int func)) { + int ret; + if (incompfunc != 1) { zwarnnam(name, "can only be called from completion function"); return 1; } - return makecomplistctl((OPT_ISSET(ops,'T') ? 0 : CFN_FIRST) | - (OPT_ISSET(ops,'D') ? 0 : CFN_DEFAULT)); + + queue_signals(); + ret = makecomplistctl((OPT_ISSET(ops,'T') ? 0 : CFN_FIRST) | + (OPT_ISSET(ops,'D') ? 0 : CFN_DEFAULT)); + unqueue_signals(); + return ret; } /* @@ -1756,6 +1770,8 @@ ccmakehookfn(UNUSED(Hookdef dummy), struct ccmakedat *dat) int onm = nmatches, odm = diffmatches, osi = movefd(0); LinkNode n; + queue_signals(); + /* We build a copy of the list of matchers to use to make sure that this * works even if a shell function called from the completion code changes * the global matchers. */ @@ -1883,6 +1899,8 @@ ccmakehookfn(UNUSED(Hookdef dummy), struct ccmakedat *dat) } redup(osi, 0); dat->lst = 1; + + unqueue_signals(); return 0; } @@ -2044,7 +2062,7 @@ maketildelist(void) /* This does the check for compctl -x `n' and `N' patterns. */ /**/ -int +static int getcpat(char *str, int cpatindex, char *cpat, int class) { char *s, *t, *p; diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c index 48fcd4751..49b338f29 100644 --- a/Src/Zle/complete.c +++ b/Src/Zle/complete.c @@ -896,6 +896,8 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod) int i, l = arrlen(compwords), t = 0, b = 0, e = l - 1; Patprog pp; + queue_signals(); /* Protect PAT_STATIC */ + i = compcurrent - 1; if (i < 0 || i >= l) return 0; @@ -930,6 +932,9 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod) t = 0; if (t && mod) restrict_range(b, e); + + unqueue_signals(); + return t; } case CVT_PRENUM: @@ -952,6 +957,8 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod) { Patprog pp; + queue_signals(); /* Protect PAT_STATIC */ + if (!na) return 0; @@ -1036,6 +1043,9 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod) if (mod) ignore_suffix(ol - (p - compsuffix)); } + + unqueue_signals(); + return 1; } } diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index 5b9ceec1a..325da6ddb 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -3928,6 +3928,8 @@ bin_comptry(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) if (*q) { char *qq, *qqq; + queue_signals(); + if (c) *c = '\0'; @@ -3999,6 +4001,8 @@ bin_comptry(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) } if (c) *c = ':'; + + unqueue_signals(); } } if (num) { @@ -4708,6 +4712,8 @@ cfp_add_sdirs(LinkList final, LinkList orig, char *skipped, if (!*p) continue; + queue_signals(); /* Protect PAT_STATIC */ + tokenize(f); pprog = patcompile(f, PAT_STATIC, NULL); untokenize(f); @@ -4740,6 +4746,8 @@ cfp_add_sdirs(LinkList final, LinkList orig, char *skipped, } } } + + unqueue_signals(); } } } diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c index 434735de9..581ca4979 100644 --- a/Src/Zle/zle_hist.c +++ b/Src/Zle/zle_hist.c @@ -1220,10 +1220,12 @@ doisearch(char **args, int dir, int pattern) char *patbuf = ztrdup(sbuf); char *patstring; /* - * Do not use static pattern buffer (PAT_STATIC) since we call zle hooks, - * which might call other pattern functions. Use PAT_ZDUP instead. - * Use PAT_NOANCH because we don't need the match - * anchored to the end, even if it is at the start. + * Do not use static pattern buffer (PAT_STATIC) since we + * call zle hooks, which might call other pattern + * functions. Use PAT_ZDUP because we re-use the pattern + * in subsequent loops, so we can't pushheap/popheap. + * Use PAT_NOANCH because we don't need the match anchored + * to the end, even if it is at the start. */ int patflags = PAT_ZDUP|PAT_NOANCH; if (sbuf[0] == '^') { diff --git a/Src/builtin.c b/Src/builtin.c index 219fbc98f..394d2069e 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -539,18 +539,18 @@ bin_enable(char *name, char **argv, Options ops, int func) /* With -m option, treat arguments as glob patterns. */ if (OPT_ISSET(ops,'m')) { for (; *argv; argv++) { + queue_signals(); + /* parse pattern */ tokenize(*argv); - if ((pprog = patcompile(*argv, PAT_STATIC, 0))) { - queue_signals(); + if ((pprog = patcompile(*argv, PAT_STATIC, 0))) match += scanmatchtable(ht, pprog, 0, 0, 0, scanfunc, 0); - unqueue_signals(); - } else { untokenize(*argv); zwarnnam(name, "bad pattern : %s", *argv); returnval = 1; } + unqueue_signals(); } /* If we didn't match anything, we return 1. */ if (!match) @@ -3136,9 +3136,9 @@ bin_functions(char *name, char **argv, Options ops, int func) } else if (OPT_ISSET(ops,'m')) { /* List matching functions. */ for (; *argv; argv++) { + queue_signals(); tokenize(*argv); if ((pprog = patcompile(*argv, PAT_STATIC, 0))) { - queue_signals(); for (p = mathfuncs, q = NULL; p; q = p) { MathFunc next; do { @@ -3157,12 +3157,12 @@ bin_functions(char *name, char **argv, Options ops, int func) if (p) p = p->next; } - unqueue_signals(); } else { untokenize(*argv); zwarnnam(name, "bad pattern : %s", *argv); returnval = 1; } + unqueue_signals(); } } else if (OPT_PLUS(ops,'M')) { /* Delete functions. -m is allowed but is handled above. */ @@ -3312,11 +3312,11 @@ bin_functions(char *name, char **argv, Options ops, int func) if (OPT_ISSET(ops,'m')) { on &= ~PM_UNDEFINED; for (; *argv; argv++) { + queue_signals(); /* expand argument */ tokenize(*argv); if ((pprog = patcompile(*argv, PAT_STATIC, 0))) { /* with no options, just print all functions matching the glob pattern */ - queue_signals(); if (!(on|off) && !OPT_ISSET(ops,'X')) { scanmatchshfunc(pprog, 1, 0, DISABLED, shfunctab->printnode, pflags, expand); @@ -3336,12 +3336,12 @@ bin_functions(char *name, char **argv, Options ops, int func) } } } - unqueue_signals(); } else { untokenize(*argv); zwarnnam(name, "bad pattern : %s", *argv); returnval = 1; } + unqueue_signals(); } return returnval; } @@ -3469,11 +3469,11 @@ bin_unset(char *name, char **argv, Options ops, int func) /* with -m option, treat arguments as glob patterns */ if (OPT_ISSET(ops,'m')) { while ((s = *argv++)) { + queue_signals(); /* expand */ tokenize(s); if ((pprog = patcompile(s, PAT_STATIC, NULL))) { /* Go through the parameter table, and unset any matches */ - queue_signals(); for (i = 0; i < paramtab->hsize; i++) { for (pm = (Param) paramtab->nodes[i]; pm; pm = next) { /* record pointer to next, since we may free this one */ @@ -3486,12 +3486,12 @@ bin_unset(char *name, char **argv, Options ops, int func) } } } - unqueue_signals(); } else { untokenize(s); zwarnnam(name, "bad pattern : %s", s); returnval = 1; } + unqueue_signals(); } /* If we didn't match anything, we return 1. */ if (!match) @@ -3655,6 +3655,7 @@ bin_whence(char *nam, char **argv, Options ops, int func) pushheap(); matchednodes = newlinklist(); } + queue_signals(); for (; *argv; argv++) { /* parse the pattern */ tokenize(*argv); @@ -3664,7 +3665,6 @@ bin_whence(char *nam, char **argv, Options ops, int func) returnval = 1; continue; } - queue_signals(); if (!OPT_ISSET(ops,'p')) { /* -p option is for path search only. * * We're not using it, so search for ... */ @@ -3695,9 +3695,9 @@ bin_whence(char *nam, char **argv, Options ops, int func) scanmatchtable(cmdnamtab, pprog, 1, 0, 0, (all ? fetchcmdnamnode : cmdnamtab->printnode), printflags); - - unqueue_signals(); + run_queued_signals(); } + unqueue_signals(); if (all) { allmatched = argv = zlinklist2array(matchednodes); matchednodes = NULL; @@ -4024,11 +4024,11 @@ bin_unhash(char *name, char **argv, Options ops, int func) * "unhash -m '*'" is legal, but not recommended. */ if (OPT_ISSET(ops,'m')) { for (; *argv; argv++) { + queue_signals(); /* expand argument */ tokenize(*argv); if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) { /* remove all nodes matching glob pattern */ - queue_signals(); for (i = 0; i < ht->hsize; i++) { for (hn = ht->nodes[i]; hn; hn = nhn) { /* record pointer to next, since we may free this one */ @@ -4039,12 +4039,12 @@ bin_unhash(char *name, char **argv, Options ops, int func) } } } - unqueue_signals(); } else { untokenize(*argv); zwarnnam(name, "bad pattern : %s", *argv); returnval = 1; } + unqueue_signals(); } /* If we didn't match anything, we return 1. */ if (!match) @@ -4127,18 +4127,18 @@ bin_alias(char *name, char **argv, Options ops, UNUSED(int func)) * glob patterns of aliases to display. */ if (OPT_ISSET(ops,'m')) { for (; *argv; argv++) { + queue_signals(); tokenize(*argv); /* expand argument */ if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) { /* display the matching aliases */ - queue_signals(); scanmatchtable(ht, pprog, 1, flags1, flags2, ht->printnode, printflags); - unqueue_signals(); } else { untokenize(*argv); zwarnnam(name, "bad pattern : %s", *argv); returnval = 1; } + unqueue_signals(); } return returnval; } @@ -4348,10 +4348,12 @@ bin_print(char *name, char **args, Options ops, int func) zwarnnam(name, "no pattern specified"); return 1; } + queue_signals(); tokenize(*args); if (!(pprog = patcompile(*args, PAT_STATIC, NULL))) { untokenize(*args); zwarnnam(name, "bad pattern: %s", *args); + unqueue_signals(); return 1; } for (t = p = ++args; *p; p++) @@ -4359,6 +4361,7 @@ bin_print(char *name, char **args, Options ops, int func) *t++ = *p; *t = NULL; first = args; + unqueue_signals(); if (fmt && !*args) return 0; } /* compute lengths, and interpret according to -P, -D, -e, etc. */ diff --git a/Src/cond.c b/Src/cond.c index 42e9de30f..8ab019307 100644 --- a/Src/cond.c +++ b/Src/cond.c @@ -295,6 +295,8 @@ evalcond(Estate state, char *fromtest) int test, npat = state->pc[1]; Patprog pprog = state->prog->pats[npat]; + queue_signals(); + if (pprog == dummy_patprog1 || pprog == dummy_patprog2) { char *opat; int save; @@ -308,6 +310,7 @@ evalcond(Estate state, char *fromtest) if (!(pprog = patcompile(right, (save ? PAT_ZDUP : PAT_STATIC), NULL))) { zwarnnam(fromtest, "bad pattern: %s", right); + unqueue_signals(); return 2; } else if (save) @@ -316,6 +319,8 @@ evalcond(Estate state, char *fromtest) state->pc += 2; test = (pprog && pattry(pprog, left)); + unqueue_signals(); + return !(ctype == COND_STRNEQ ? !test : test); } case COND_STRLT: diff --git a/Src/glob.c b/Src/glob.c index 623e6f1d6..ff6b2583b 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -2462,13 +2462,20 @@ xpandbraces(LinkList list, LinkNode *np) int matchpat(char *a, char *b) { - Patprog p = patcompile(b, PAT_STATIC, NULL); + Patprog p; + int ret; - if (!p) { + queue_signals(); /* Protect PAT_STATIC */ + + if (!(p = patcompile(b, PAT_STATIC, NULL))) { zerr("bad pattern: %s", b); - return 0; - } - return pattry(p, a); + ret = 0; + } else + ret = pattry(p, a); + + unqueue_signals(); + + return ret; } /* do the ${foo%%bar}, ${foo#bar} stuff */ diff --git a/Src/loop.c b/Src/loop.c index ae87b2f5f..f7eae307b 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -620,7 +620,9 @@ execcase(Estate state, int do_exec) spprog = state->prog->pats + npat; pprog = NULL; pat = NULL; - + + queue_signals(); + if (isset(XTRACE)) { int htok = 0; pat = dupstring(ecrawstr(state->prog, state->pc, &htok)); @@ -657,6 +659,8 @@ execcase(Estate state, int do_exec) patok = anypatok = 1; state->pc += 2; nalts--; + + unqueue_signals(); } state->pc += 2 * nalts; if (isset(XTRACE)) { diff --git a/Src/options.c b/Src/options.c index e0b67d205..2b5795bab 100644 --- a/Src/options.c +++ b/Src/options.c @@ -647,7 +647,7 @@ bin_setopt(char *nam, char **args, UNUSED(Options ops), int isun) /* Expand the current arg. */ tokenize(s); - if (!(pprog = patcompile(s, PAT_STATIC, NULL))) { + if (!(pprog = patcompile(s, PAT_HEAPDUP, NULL))) { zwarnnam(nam, "bad pattern: %s", *args); continue; } diff --git a/Src/parse.c b/Src/parse.c index 314cc09d3..699ea49a2 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -3413,6 +3413,7 @@ build_cur_dump(char *nam, char *dump, char **names, int match, int map, for (; *names; names++) { tokenize(pat = dupstring(*names)); + /* Signal-safe here, caller queues signals */ if (!(pprog = patcompile(pat, PAT_STATIC, NULL))) { zwarnnam(nam, "bad pattern: %s", *names); close(dfd); -- cgit v1.2.3 From 071017965f469c88b10467205f30ea3e609e56dc Mon Sep 17 00:00:00 2001 From: "Barton E. Schaefer" Date: Wed, 8 Mar 2017 21:26:55 -0800 Subject: 40763: count wide characters and Cmatcher pointers more sanely in cfp_matcher_pats(), and count characters in pattern_match() the same way to stay in sync Might not fix wide-char matching in completion matcher-lists but should avoid wild pointer crash --- ChangeLog | 8 ++++++++ Src/Zle/compmatch.c | 57 +++++------------------------------------------------ Src/Zle/computil.c | 19 ++++++++++++------ Src/utils.c | 42 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 58 deletions(-) (limited to 'Src/Zle/computil.c') diff --git a/ChangeLog b/ChangeLog index 4219b1510..65ef5cc90 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2017-03-08 Barton E. Schaefer + + * 40763: Src/Zle/compmatch.c, Src/Zle/computil.c, Src/utils.c: + count wide characters and Cmatcher pointers more sanely in + cfp_matcher_pats(), and count characters in pattern_match() + the same way to stay in sync; might not fix wide-char matching + in completion matcher-lists but should avoid wild pointer crash + 2017-03-08 Daniel Shahaf * 40745 + 40753: Src/init.c, Src/params.c: Fix 'unset diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c index aedf463fc..1cdbb8a48 100644 --- a/Src/Zle/compmatch.c +++ b/Src/Zle/compmatch.c @@ -1548,27 +1548,11 @@ pattern_match(Cpattern p, char *s, Cpattern wp, char *ws) { convchar_t c, wc; convchar_t ind, wind; - int len = 0, wlen, mt, wmt; -#ifdef MULTIBYTE_SUPPORT - mbstate_t lstate, wstate; - - memset(&lstate, 0, sizeof(lstate)); - memset(&wstate, 0, sizeof(wstate)); -#endif + int len = 0, wlen = 0, mt, wmt; while (p && wp && *s && *ws) { /* First test the word character */ -#ifdef MULTIBYTE_SUPPORT - wlen = mb_metacharlenconv_r(ws, &wc, &wstate); -#else - if (*ws == Meta) { - wc = STOUC(ws[1]) ^ 32; - wlen = 2; - } else { - wc = STOUC(*ws); - wlen = 1; - } -#endif + wc = unmeta_one(ws, &wlen); wind = pattern_match1(wp, wc, &wmt); if (!wind) return 0; @@ -1576,18 +1560,7 @@ pattern_match(Cpattern p, char *s, Cpattern wp, char *ws) /* * Now the line character. */ -#ifdef MULTIBYTE_SUPPORT - len = mb_metacharlenconv_r(s, &c, &lstate); -#else - /* We have the character itself. */ - if (*s == Meta) { - c = STOUC(s[1]) ^ 32; - len = 2; - } else { - c = STOUC(*s); - len = 1; - } -#endif + c = unmeta_one(s, &len); /* * If either is "?", they match each other; no further tests. * Apply this even if the character wasn't convertable; @@ -1627,17 +1600,7 @@ pattern_match(Cpattern p, char *s, Cpattern wp, char *ws) } while (p && *s) { -#ifdef MULTIBYTE_SUPPORT - len = mb_metacharlenconv_r(s, &c, &lstate); -#else - if (*s == Meta) { - c = STOUC(s[1]) ^ 32; - len = 2; - } else { - c = STOUC(*s); - len = 1; - } -#endif + c = unmeta_one(s, &len); if (!pattern_match1(p, c, &mt)) return 0; p = p->next; @@ -1645,17 +1608,7 @@ pattern_match(Cpattern p, char *s, Cpattern wp, char *ws) } while (wp && *ws) { -#ifdef MULTIBYTE_SUPPORT - wlen = mb_metacharlenconv_r(ws, &wc, &wstate); -#else - if (*ws == Meta) { - wc = STOUC(ws[1]) ^ 32; - wlen = 2; - } else { - wc = STOUC(*ws); - wlen = 1; - } -#endif + wc = unmeta_one(ws, &wlen); if (!pattern_match1(wp, wc, &wmt)) return 0; wp = wp->next; diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index 325da6ddb..e704f9ffa 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -4465,17 +4465,24 @@ cfp_matcher_pats(char *matcher, char *add) if (m && m != pcm_err) { char *tmp; int al = strlen(add), zl = ztrlen(add), tl, cl; - VARARR(Cmatcher, ms, zl); + VARARR(Cmatcher, ms, zl); /* One Cmatcher per character */ Cmatcher *mp; Cpattern stopp; int stopl = 0; + /* zl >= (number of wide characters) is guaranteed */ memset(ms, 0, zl * sizeof(Cmatcher)); for (; m && *add; m = m->next) { stopp = NULL; if (!(m->flags & (CMF_LEFT|CMF_RIGHT))) { if (m->llen == 1 && m->wlen == 1) { + /* + * In this loop and similar loops below we step + * through tmp one (possibly wide) character at a + * time. pattern_match() compares only the first + * character using unmeta_one() so keep in step. + */ for (tmp = add, tl = al, mp = ms; tl; ) { if (pattern_match(m->line, tmp, NULL, NULL)) { if (*mp) { @@ -4485,10 +4492,10 @@ cfp_matcher_pats(char *matcher, char *add) } else *mp = m; } - cl = (*tmp == Meta) ? 2 : 1; + (void) unmeta_one(tmp, &cl); tl -= cl; tmp += cl; - mp += cl; + mp++; } } else { stopp = m->line; @@ -4505,10 +4512,10 @@ cfp_matcher_pats(char *matcher, char *add) } else *mp = m; } - cl = (*tmp == Meta) ? 2 : 1; + (void) unmeta_one(tmp, &cl); tl -= cl; tmp += cl; - mp += cl; + mp++; } } else if (m->llen) { stopp = m->line; @@ -4531,7 +4538,7 @@ cfp_matcher_pats(char *matcher, char *add) al = tmp - add; break; } - cl = (*tmp == Meta) ? 2 : 1; + (void) unmeta_one(tmp, &cl); tl -= cl; tmp += cl; } diff --git a/Src/utils.c b/Src/utils.c index 9669944f6..b3fa3d24c 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -4787,6 +4787,48 @@ unmeta(const char *file_name) return fn; } +/* + * Unmetafy just one character and store the number of bytes it occupied. + */ +/**/ +mod_export convchar_t +unmeta_one(const char *in, int *sz) +{ + convchar_t wc; + int newsz; +#ifdef MULTIBYTE_SUPPORT + int ulen; + mbstate_t wstate; +#endif + + if (!sz) + sz = &newsz; + *sz = 0; + + if (!in || !*in) + return 0; + +#ifdef MULTIBYTE_SUPPORT + memset(&wstate, 0, sizeof(wstate)); + ulen = mb_metacharlenconv_r(in, &wc, &wstate); + while (ulen-- > 0) { + if (in[*sz] == Meta) + *sz += 2; + else + *sz += 1; + } +#else + if (in[0] == Meta) { + *sz = 2; + wc = STOUC(in[1] ^ 32); + } else { + *sz = 1; + wc = STOUC(in[0]); + } +#endif + return wc; +} + /* * Unmetafy and compare two strings, comparing unsigned character values. * "a\0" sorts after "a". -- cgit v1.2.3