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.
This commit is contained in:
Peter Stephenson 2015-05-18 09:56:00 +01:00
parent 34a1489f43
commit 52aeb9aaeb
14 changed files with 162 additions and 126 deletions

View File

@ -1,3 +1,16 @@
2015-05-18 Peter Stephenson <p.stephenson@samsung.com>
* 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 <git@thequod.de>
* 35126: Completion/Unix/Command/_git: __git_recent_commits:

View File

@ -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

View File

@ -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' \

View File

@ -25,7 +25,7 @@ case $basename in
ltype=gnu
;;
(*illegal option*)
(*"illegal option"*)
if [[ $OSTYPE == (freebsd|openbsd|dragonfly|darwin)* ]]; then
ltype=bsd
else

View File

@ -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

View File

@ -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"
;;

View File

@ -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]"

View File

@ -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
;;

View File

@ -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'

View File

@ -761,6 +761,8 @@ gettok(void)
lexstop = 0;
return BAR;
case LX1_INPAR:
if (incasepat == 2)
return INPAR;
d = hgetc();
if (d == '(') {
if (infor) {

View File

@ -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);
}
while (!patok && nalts) {
npat = state->pc[1];
spprog = state->prog->pats + npat;
pprog = NULL;
pat = NULL;
if (isset(XTRACE)) {
int htok = 0;
pat = dupstring(ecrawstr(state->prog, state->pc, &htok));
if (htok)
singsub(&pat);
if (ialt++)
fprintf(stderr, " | ");
quote_tokenized_output(pat, xtrerr);
}
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);
}
state->pc += 2;
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 - 2, &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)) {
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));
}

View File

@ -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);
} else if (tok != BAR)
YYERRORV(oecused);
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;
}
}
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);

View File

@ -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();

View File

@ -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