From 52aeb9aaeb4799b760138a7c34b18ede4b47242a Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Mon, 18 May 2015 09:56:00 +0100 Subject: 35168: Improve parsing of case patterns. "|" is now found properly by looking for words that come from the lexical analyser, rather than hacking a pattern returned in one dollop. Update some completion functions that need extra quoting as a result. Add test for new parsing. Update version number to 5.0.8-dev-3 because of wordcode incompatibility. --- ChangeLog | 13 ++++++ Completion/Unix/Command/_ant | 3 +- Completion/Unix/Command/_cp | 2 +- Completion/Unix/Command/_locate | 2 +- Completion/Unix/Command/_make | 6 +-- Completion/Unix/Command/_tar | 2 +- Completion/Unix/Type/_path_commands | 2 +- Completion/X/Command/_xrandr | 2 +- Config/version.mk | 4 +- Src/lex.c | 2 + Src/loop.c | 88 +++++++++++++++++++++--------------- Src/parse.c | 89 ++++++++++--------------------------- Src/text.c | 28 ++++++++---- Test/A01grammar.ztst | 39 ++++++++++++++++ 14 files changed, 159 insertions(+), 123 deletions(-) diff --git a/ChangeLog b/ChangeLog index 68b644823..8848ce237 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2015-05-18 Peter Stephenson + + * 35168: Completion/Unix/Command/_ant, + Completion/Unix/Command/_cp, Completion/Unix/Command/_locate, + Completion/Unix/Command/_make, Completion/Unix/Command/_tar, + Completion/Unix/Type/_path_commands, + Completion/X/Command/_xrandr, Config/version.mk, Src/lex.c, + Src/loop.c, Src/parse.c, Src/text.c, Test/A01grammar.ztst: + Fix parsing of case patterns so "|" is extracted by looking + for words; quote completion functions where needed in + consequence; add test; updated version number to 5.0.7-dev-3 + because of wordcode incompatibility. + 2015-05-18 Daniel Hahler * 35126: Completion/Unix/Command/_git: __git_recent_commits: diff --git a/Completion/Unix/Command/_ant b/Completion/Unix/Command/_ant index 8e4bd82af..ee9f7d910 100644 --- a/Completion/Unix/Command/_ant +++ b/Completion/Unix/Command/_ant @@ -123,8 +123,7 @@ case $state in # Output target again indicating its the default one. print -n "'${default_target}:(Default target) ' " ;; - (Searching:*|Main:targets:|Subtargets:|BUILD:SUCCESSFUL|Total:time: - *) + (Searching:*|Main:targets:|Subtargets:|BUILD:SUCCESSFUL|Total:time:*) ;; (*) # Return target and description diff --git a/Completion/Unix/Command/_cp b/Completion/Unix/Command/_cp index 4c4dea2e9..7087b4e6c 100644 --- a/Completion/Unix/Command/_cp +++ b/Completion/Unix/Command/_cp @@ -13,7 +13,7 @@ if _pick_variant gnu=GNU unix --version; then '-H[follow command-line symbolic links]' \ '(-l --link)'{-l,--link}'[link files instead of copying]' \ '(-L --dereference)'{-L,--dereference}'[always follow symbolic links]' \ - (-n --no-clobber -i --interactive){-n,--no-clobber}"[don't overwrite an existing file]" \ + '(-n --no-clobber -i --interactive)'{-n,--no-clobber}"[don't overwrite an existing file]" \ '(-P --no-dereference)'{-P,--no-dereference}'[never follow symbolic links]' \ '-p[same as --preserve=mode,ownership,timestamps]' \ '--preserve=-[preserve specified attributes]:: :_values -s , attribute mode timestamps ownership links context xattr all' \ diff --git a/Completion/Unix/Command/_locate b/Completion/Unix/Command/_locate index 0b35b7cdd..694f506c3 100644 --- a/Completion/Unix/Command/_locate +++ b/Completion/Unix/Command/_locate @@ -25,7 +25,7 @@ case $basename in ltype=gnu ;; - (*illegal option*) + (*"illegal option"*) if [[ $OSTYPE == (freebsd|openbsd|dragonfly|darwin)* ]]; then ltype=bsd else diff --git a/Completion/Unix/Command/_make b/Completion/Unix/Command/_make index 225c0af85..c14a34c58 100644 --- a/Completion/Unix/Command/_make +++ b/Completion/Unix/Command/_make @@ -65,7 +65,7 @@ _make-parseMakefile () { do case "$input " in # VARIABLE = value OR VARIABLE ?= value - ([[:alnum:]][[:alnum:]_]#[ $TAB]#(\?|)=*) + ([[:alnum:]][[:alnum:]_]#[" "$TAB]#(\?|)=*) var=${input%%[ $TAB]#(\?|)=*} val=${input#*=} val=${val##[ $TAB]#} @@ -74,7 +74,7 @@ _make-parseMakefile () { # VARIABLE := value OR VARIABLE ::= value # Evaluated immediately - ([[:alnum:]][[:alnum:]_]#[ $TAB]#:(:|)=*) + ([[:alnum:]][[:alnum:]_]#[" "$TAB]#:(:|)=*) var=${input%%[ $TAB]#:(:|)=*} val=${input#*=} val=${val##[ $TAB]#} @@ -97,7 +97,7 @@ _make-parseMakefile () { ;; # Include another makefile - (${~incl} *) + (${~incl}" "*) local f=${input##${~incl} ##} if [[ $incl == '.include' ]] then diff --git a/Completion/Unix/Command/_tar b/Completion/Unix/Command/_tar index ce5852453..1e99ac013 100644 --- a/Completion/Unix/Command/_tar +++ b/Completion/Unix/Command/_tar @@ -23,7 +23,7 @@ local _tar_cmd tf tmp tmpb del index if _pick_variant gnu=GNU unix --version; then case "$($service --version)" in - (tar \(GNU tar\) (#b)([0-9.-]##)*) + ("tar (GNU tar) "(#b)([0-9.-]##)*) autoload -z is-at-least is-at-least 1.14.91 "$match[1]" || _cmd_variant[$service]="gnu-old" ;; diff --git a/Completion/Unix/Type/_path_commands b/Completion/Unix/Type/_path_commands index 22d2aaeba..423563c0d 100644 --- a/Completion/Unix/Type/_path_commands +++ b/Completion/Unix/Type/_path_commands @@ -25,7 +25,7 @@ return 1 _call_whatis() { case "$(whatis --version)" in - (whatis from *) + ("whatis from "*) local -A args zparseopts -D -A args s: r: apropos "${args[-r]:-"$@"}" | fgrep "($args[-s]" diff --git a/Completion/X/Command/_xrandr b/Completion/X/Command/_xrandr index 9d9323c69..b0851569e 100644 --- a/Completion/X/Command/_xrandr +++ b/Completion/X/Command/_xrandr @@ -51,7 +51,7 @@ _arguments -C \ case $state in value) case $words[CURRENT-1] in - (scaling* mode) + (scaling*" mode") _description value expl "output property 'scaling mode'" compadd "$@" "$expl[@]" None Full Center Full\ aspect && return 0 ;; diff --git a/Config/version.mk b/Config/version.mk index d04d57d87..acab748e6 100644 --- a/Config/version.mk +++ b/Config/version.mk @@ -27,5 +27,5 @@ # This must also serve as a shell script, so do not add spaces around the # `=' signs. -VERSION=5.0.7-dev-2 -VERSION_DATE='May 5, 2015' +VERSION=5.0.7-dev-3 +VERSION_DATE='May 17, 2015' diff --git a/Src/lex.c b/Src/lex.c index 841fb0b86..87b0cd3af 100644 --- a/Src/lex.c +++ b/Src/lex.c @@ -761,6 +761,8 @@ gettok(void) lexstop = 0; return BAR; case LX1_INPAR: + if (incasepat == 2) + return INPAR; d = hgetc(); if (d == '(') { if (infor) { diff --git a/Src/loop.c b/Src/loop.c index d025fbb9f..e4e8e2df8 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -545,7 +545,7 @@ execcase(Estate state, int do_exec) Wordcode end, next; wordcode code = state->pc[-1]; char *word, *pat; - int npat, save; + int npat, save, nalts, ialt, patok; Patprog *spprog, pprog; end = state->pc + WC_CASE_SKIP(code); @@ -561,60 +561,74 @@ execcase(Estate state, int do_exec) if (wc_code(code) != WC_CASE) break; - pat = NULL; - pprog = NULL; save = 0; - npat = state->pc[1]; - spprog = state->prog->pats + npat; - next = state->pc + WC_CASE_SKIP(code); + nalts = *state->pc++; + ialt = patok = 0; if (isset(XTRACE)) { - char *opat; - - pat = dupstring(opat = ecrawstr(state->prog, state->pc, NULL)); - singsub(&pat); - save = (!(state->prog->flags & EF_HEAP) && - !strcmp(pat, opat) && *spprog != dummy_patprog2); - printprompt4(); fprintf(xtrerr, "case %s (", word); - quote_tokenized_output(pat, xtrerr); - fprintf(xtrerr, ")\n"); - fflush(xtrerr); } - state->pc += 2; - if (*spprog != dummy_patprog1 && *spprog != dummy_patprog2) - pprog = *spprog; - - if (!pprog) { - if (!pat) { - char *opat; + while (!patok && nalts) { + npat = state->pc[1]; + spprog = state->prog->pats + npat; + pprog = NULL; + pat = NULL; + + if (isset(XTRACE)) { int htok = 0; - - pat = dupstring(opat = ecrawstr(state->prog, - state->pc - 2, &htok)); + pat = dupstring(ecrawstr(state->prog, state->pc, &htok)); if (htok) singsub(&pat); - save = (!(state->prog->flags & EF_HEAP) && - !strcmp(pat, opat) && *spprog != dummy_patprog2); + + if (ialt++) + fprintf(stderr, " | "); + quote_tokenized_output(pat, xtrerr); } - if (!(pprog = patcompile(pat, (save ? PAT_ZDUP : PAT_STATIC), - NULL))) - zerr("bad pattern: %s", pat); - else if (save) - *spprog = pprog; + + if (*spprog != dummy_patprog1 && *spprog != dummy_patprog2) + pprog = *spprog; + + if (!pprog) { + if (!pat) { + char *opat; + int htok = 0; + + pat = dupstring(opat = ecrawstr(state->prog, + state->pc, &htok)); + if (htok) + singsub(&pat); + save = (!(state->prog->flags & EF_HEAP) && + !strcmp(pat, opat) && *spprog != dummy_patprog2); + } + if (!(pprog = patcompile(pat, (save ? PAT_ZDUP : PAT_STATIC), + NULL))) + zerr("bad pattern: %s", pat); + else if (save) + *spprog = pprog; + } + if (pprog && pattry(pprog, word)) + patok = 1; + state->pc += 2; + nalts--; + } + state->pc += 2 * nalts; + if (isset(XTRACE)) { + fprintf(xtrerr, ")\n"); + fflush(xtrerr); } - if (pprog && pattry(pprog, word)) { + if (patok) { execlist(state, 1, ((WC_CASE_TYPE(code) == WC_CASE_OR) && do_exec)); while (!retflag && wc_code(code) == WC_CASE && WC_CASE_TYPE(code) == WC_CASE_AND) { state->pc = next; - code = *state->pc; - state->pc += 3; - next = state->pc + WC_CASE_SKIP(code) - 2; + code = *state->pc++; + next = state->pc + WC_CASE_SKIP(code); + nalts = *state->pc++; + state->pc += 2 * nalts; execlist(state, 1, ((WC_CASE_TYPE(code) == WC_CASE_OR) && do_exec)); } diff --git a/Src/parse.c b/Src/parse.c index 985eb8e71..c938d2dce 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -349,9 +349,8 @@ ecadd(wordcode c) eclen += a; } ecbuf[ecused] = c; - ecused++; - return ecused - 1; + return ecused++; } /* Delete a wordcode. */ @@ -1128,7 +1127,7 @@ par_for(int *cmplx) static void par_case(int *cmplx) { - int oecused = ecused, brflag, p, pp, n = 1, type; + int oecused = ecused, brflag, p, pp, palts, type, nalts; int ona, onc; p = ecadd(0); @@ -1153,7 +1152,7 @@ par_case(int *cmplx) YYERRORV(oecused); } brflag = (tok == INBRACE); - incasepat = 1; + incasepat = 2; incmdpos = 0; noaliases = ona; nocorrect = onc; @@ -1166,8 +1165,10 @@ par_case(int *cmplx) zshlex(); if (tok == OUTBRACE) break; - if (tok == INPAR) + if (tok == INPAR) { + incasepat = 1; zshlex(); + } if (tok != STRING) YYERRORV(oecused); if (!strcmp(tokstr, "esac")) @@ -1176,89 +1177,45 @@ par_case(int *cmplx) incasepat = 0; incmdpos = 1; type = WC_CASE_OR; + pp = ecadd(0); + palts = ecadd(0); + nalts = 0; for (;;) { + ecstr(str); + ecadd(ecnpats++); + nalts++; + zshlex(); if (tok == OUTPAR) { incasepat = 0; incmdpos = 1; zshlex(); break; - } else if (tok == BAR) { - char *str2; - int sl = strlen(str); - - incasepat = 1; - incmdpos = 0; - str2 = hcalloc(sl + 2); - strcpy(str2, str); - str2[sl] = Bar; - str2[sl+1] = '\0'; - str = str2; - } else { - int sl = strlen(str); - - if (!sl || str[sl - 1] != Bar) { - /* POSIX allows (foo*) patterns */ - int pct; - char *s; - - for (s = str, pct = 0; *s; s++) { - if (*s == Inpar) - pct++; - if (!pct) - break; - if (pct == 1) { - if (*s == Bar || *s == Inpar) - while (iblank(s[1])) - chuck(s+1); - if (*s == Bar || *s == Outpar) - while (iblank(s[-1]) && - (s < str + 1 || s[-2] != Meta)) - chuck(--s); - } - if (*s == Outpar) - pct--; - } - if (*s || pct || s == str) - YYERRORV(oecused); - /* Simplify pattern by removing surrounding (...) */ - sl = strlen(str); - DPUTS(*str != Inpar || str[sl - 1] != Outpar, - "BUG: strange case pattern"); - str[sl - 1] = '\0'; - chuck(str); - break; - } else { - char *str2; - - if (tok != STRING) - YYERRORV(oecused); - str2 = hcalloc(sl + strlen(tokstr) + 1); - strcpy(str2, str); - strcpy(str2 + sl, tokstr); - str = str2; - } - } + } else if (tok != BAR) + YYERRORV(oecused); + + zshlex(); + if (tok != STRING) + YYERRORV(oecused); + str = dupstring(tokstr); } - pp = ecadd(0); - ecstr(str); - ecadd(ecnpats++); par_save_list(cmplx); - n++; if (tok == SEMIAMP) type = WC_CASE_AND; else if (tok == SEMIBAR) type = WC_CASE_TESTAND; ecbuf[pp] = WCB_CASE(type, ecused - 1 - pp); + ecbuf[palts] = nalts; if ((tok == ESAC && !brflag) || (tok == OUTBRACE && brflag)) break; if (tok != DSEMI && tok != SEMIAMP && tok != SEMIBAR) YYERRORV(oecused); - incasepat = 1; + incasepat = 2; incmdpos = 0; zshlex(); } incmdpos = 1; + incasepat = 0; zshlex(); ecbuf[p] = WCB_CASE(WC_CASE_HEAD, ecused - 1 - p); diff --git a/Src/text.c b/Src/text.c index b58c2516d..958303c68 100644 --- a/Src/text.c +++ b/Src/text.c @@ -602,6 +602,7 @@ gettext2(Estate state) case WC_CASE: if (!s) { Wordcode end = state->pc + WC_CASE_SKIP(code); + wordcode nalts; taddstr("case "); taddstr(ecgetstr(state, EC_NODUP, NULL)); @@ -622,8 +623,13 @@ gettext2(Estate state) taddchr(' '); taddstr("("); code = *state->pc++; - taddstr(ecgetstr(state, EC_NODUP, NULL)); - state->pc++; + nalts = *state->pc++; + while (nalts--) { + taddstr(ecgetstr(state, EC_NODUP, NULL)); + state->pc++; + if (nalts) + taddstr(" | "); + } taddstr(") "); tindent++; n = tpush(code, 0); @@ -631,6 +637,7 @@ gettext2(Estate state) n->pop = (state->pc - 2 + WC_CASE_SKIP(code) >= end); } } else if (state->pc < s->u._case.end) { + wordcode nalts; dec_tindent(); switch (WC_CASE_TYPE(code)) { case WC_CASE_OR: @@ -638,11 +645,11 @@ gettext2(Estate state) break; case WC_CASE_AND: - taddstr(";&"); + taddstr(" ;&"); break; default: - taddstr(";|"); + taddstr(" ;|"); break; } if (tnewlins) @@ -651,8 +658,13 @@ gettext2(Estate state) taddchr(' '); taddstr("("); code = *state->pc++; - taddstr(ecgetstr(state, EC_NODUP, NULL)); - state->pc++; + nalts = *state->pc++; + while (nalts--) { + taddstr(ecgetstr(state, EC_NODUP, NULL)); + state->pc++; + if (nalts) + taddstr(" | "); + } taddstr(") "); tindent++; s->code = code; @@ -666,11 +678,11 @@ gettext2(Estate state) break; case WC_CASE_AND: - taddstr(";&"); + taddstr(" ;&"); break; default: - taddstr(";|"); + taddstr(" ;|"); break; } dec_tindent(); diff --git a/Test/A01grammar.ztst b/Test/A01grammar.ztst index 2de291906..1ba0a54d7 100644 --- a/Test/A01grammar.ztst +++ b/Test/A01grammar.ztst @@ -572,6 +572,7 @@ $ZTST_testdir/../Src/zsh -f myscript 127q:PATHSCRIPT option not used. ?$ZTST_testdir/../Src/zsh: can't open input file: myscript +# ' $ZTST_testdir/../Src/zsh -fc 'echo $0; echo $1' myargzero myargone 0:$0 is traditionally if bizarrely set to the first argument with -c @@ -611,3 +612,41 @@ >BEGIN >mytrue >END + + fn() { + case $1 in + ( one | two | three ) + print Matched $1 + ;; + ( fo* | fi* | si* ) + print Pattern matched $1 + ;; + ( []x | a[b]* ) + print Character class matched $1 + ;; + esac + } + which fn + fn one + fn two + fn three + fn four + fn five + fn six + fn abecedinarian + fn xylophone +0: case word handling +>fn () { +> case $1 in +> (one | two | three) print Matched $1 ;; +> (fo* | fi* | si*) print Pattern matched $1 ;; +> ([]x | a[b]*) print Character class matched $1 ;; +> esac +>} +>Matched one +>Matched two +>Matched three +>Pattern matched four +>Pattern matched five +>Pattern matched six +>Character class matched abecedinarian -- cgit 1.4.1