From de272e0309bc1739f13cc8271a2f94bcde7ba23c Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Fri, 19 Jan 2007 21:36:00 +0000 Subject: 23115: ";|" at end of case clause causes later patterns to be tested --- ChangeLog | 6 ++++++ Doc/Zsh/grammar.yo | 17 +++++++++++++---- Src/lex.c | 4 ++++ Src/loop.c | 7 ++++--- Src/parse.c | 12 +++++++----- Src/text.c | 28 ++++++++++++++++++++++++++-- Src/zsh.h | 33 ++++++++++++++++++--------------- Test/A01grammar.ztst | 23 +++++++++++++++++++++++ 8 files changed, 101 insertions(+), 29 deletions(-) diff --git a/ChangeLog b/ChangeLog index 62f64a236..a56df9b9d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2007-01-19 Peter Stephenson + + * 23115: Doc/Zsh/grammar.yo, Src/lex.c, Src/loop.c, Src/parse.c, + Src/text.c, Src/zsh.h, Test/A01grammar.ztst: add ";|" + at end of case statement to cause testing of later patterns. + 2007-01-18 Peter Stephenson * unposted: Doc/Zsh/calsys.yo: typo with parentheses. diff --git a/Doc/Zsh/grammar.yo b/Doc/Zsh/grammar.yo index dd5da93b4..6138a8b9b 100644 --- a/Doc/Zsh/grammar.yo +++ b/Doc/Zsh/grammar.yo @@ -199,14 +199,23 @@ var(list) is then executed var(n) times. findex(case) cindex(case selection) cindex(selection, case) -item(tt(case) var(word) tt(in) [ [tt(LPAR())] var(pattern) [ tt(|) var(pattern) ] ... tt(RPAR()) var(list) (tt(;;)|tt(;&)) ] ... tt(esac))( +item(tt(case) var(word) tt(in) [ [tt(LPAR())] var(pattern) [ tt(|) var(pattern) ] ... tt(RPAR()) var(list) (tt(;;)|tt(;&)|tt(;|)) ] ... tt(esac))( Execute the var(list) associated with the first var(pattern) that matches var(word), if any. The form of the patterns is the same as that used for filename generation. See noderef(Filename Generation). + If the var(list) that is executed is terminated with tt(;&) rather than -tt(;;), the following list is also executed. This continues until either -a list is terminated with tt(;;) or the tt(esac) is reached. +tt(;;), the following list is also executed. The rule for +the terminator of the following list tt(;;), tt(;&) or tt(;|) is +applied unless the tt(esac) is reached. + +If the var(list) that is executed is terminated with tt(;|) the +shell continues to scan the var(pattern)s looking for the next match, +executing the corresponding var(list), and applying the rule for +the corresponding terminator tt(;;), tt(;&) or tt(;|). +Note that var(word) is not re-expanded; all applicable var(pattern)s +are tested with the same var(word). ) findex(select) cindex(user selection) @@ -390,7 +399,7 @@ var(list) mentioned above. item(tt(repeat) var(word) var(sublist))( This is a short form of tt(repeat). ) -item(tt(case) var(word) tt({) [ [tt(LPAR())] var(pattern) [ tt(|) var(pattern) ] ... tt(RPAR()) var(list) (tt(;;)|tt(;&)) ] ... tt(}))( +item(tt(case) var(word) tt({) [ [tt(LPAR())] var(pattern) [ tt(|) var(pattern) ] ... tt(RPAR()) var(list) (tt(;;)|tt(;&)|tt(;|)) ] ... tt(}))( An alternative form of tt(case). ) item(tt(select) var(name) [ tt(in) var(word) var(term) ] var(sublist))( diff --git a/Src/lex.c b/Src/lex.c index 7a0bf2b1c..095d58150 100644 --- a/Src/lex.c +++ b/Src/lex.c @@ -155,6 +155,7 @@ mod_export char *tokstrings[WHILE + 1] = { "))", /* DOUTPAR */ "&|", /* AMPERBANG 30 */ ";&", /* SEMIAMP */ + ";|", /* SEMIBAR */ }; /* lexical state */ @@ -381,6 +382,7 @@ ctxtlex(void) case SEMI: case DSEMI: case SEMIAMP: + case SEMIBAR: case AMPER: case AMPERBANG: case INPAR: @@ -713,6 +715,8 @@ gettok(void) return DSEMI; else if(d == '&') return SEMIAMP; + else if (d == '|') + return SEMIBAR; hungetc(d); lexstop = 0; return SEMI; diff --git a/Src/loop.c b/Src/loop.c index 7037ddc82..b20e379b8 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -606,9 +606,10 @@ execcase(Estate state, int do_exec) execlist(state, 1, ((WC_CASE_TYPE(code) == WC_CASE_OR) && do_exec)); } - break; - } else - state->pc = next; + if (WC_CASE_TYPE(code) != WC_CASE_TESTAND) + break; + } + state->pc = next; } cmdpop(); diff --git a/Src/parse.c b/Src/parse.c index a25eb3efa..afaa0d944 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -169,9 +169,9 @@ struct heredocs *hdocs; * * WC_CASE * - first CASE is always of type HEAD, data contains offset to esac - * - after that CASEs of type OR (;;) and AND (;&), data is offset to - * next case - * - each OR/AND case is followed by pattern, pattern-number, list + * - after that CASEs of type OR (;;), AND (;&) and TESTAND (;|), + * data is offset to next case + * - each OR/AND/TESTAND case is followed by pattern, pattern-number, list * * WC_IF * - first IF is of type HEAD, data contains offset to fi @@ -1014,7 +1014,7 @@ par_for(int *complex) /* * case : CASE STRING { SEPER } ( "in" | INBRACE ) { { SEPER } STRING { BAR STRING } OUTPAR - list [ DSEMI | SEMIAMP ] } + list [ DSEMI | SEMIAMP | SEMIBAR ] } { SEPER } ( "esac" | OUTBRACE ) */ @@ -1141,10 +1141,12 @@ par_case(int *complex) n++; if (tok == SEMIAMP) type = WC_CASE_AND; + else if (tok == SEMIBAR) + type = WC_CASE_TESTAND; ecbuf[pp] = WCB_CASE(type, ecused - 1 - pp); if ((tok == ESAC && !brflag) || (tok == OUTBRACE && brflag)) break; - if (tok != DSEMI && tok != SEMIAMP) + if (tok != DSEMI && tok != SEMIAMP && tok != SEMIBAR) YYERRORV(oecused); incasepat = 1; incmdpos = 0; diff --git a/Src/text.c b/Src/text.c index 0079e9fea..181fea083 100644 --- a/Src/text.c +++ b/Src/text.c @@ -537,7 +537,19 @@ gettext2(Estate state) } } else if (state->pc < s->u._case.end) { tindent--; - taddstr(WC_CASE_TYPE(code) == WC_CASE_OR ? " ;;" : ";&"); + switch (WC_CASE_TYPE(code)) { + case WC_CASE_OR: + taddstr(" ;;"); + break; + + case WC_CASE_AND: + taddstr(";&"); + break; + + default: + taddstr(";|"); + break; + } if (tnewlins) taddnl(); else @@ -553,7 +565,19 @@ gettext2(Estate state) s->u._case.end); } else { tindent--; - taddstr(WC_CASE_TYPE(code) == WC_CASE_OR ? " ;;" : ";&"); + switch (WC_CASE_TYPE(code)) { + case WC_CASE_OR: + taddstr(" ;;"); + break; + + case WC_CASE_AND: + taddstr(";&"); + break; + + default: + taddstr(";|"); + break; + } tindent--; if (tnewlins) taddnl(); diff --git a/Src/zsh.h b/Src/zsh.h index 27c344809..e2eb2544a 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -246,37 +246,38 @@ enum { DOUTPAR, AMPERBANG, /* 30 */ SEMIAMP, + SEMIBAR, DOUTBRACK, STRING, - ENVSTRING, - ENVARRAY, /* 35 */ + ENVSTRING, /* 35 */ + ENVARRAY, ENDINPUT, LEXERR, /* Tokens for reserved words */ BANG, /* ! */ - DINBRACK, /* [[ */ - INBRACE, /* { */ /* 40 */ + DINBRACK, /* [[ */ /* 40 */ + INBRACE, /* { */ OUTBRACE, /* } */ CASE, /* case */ COPROC, /* coproc */ - DOLOOP, /* do */ - DONE, /* done */ /* 45 */ + DOLOOP, /* do */ /* 45 */ + DONE, /* done */ ELIF, /* elif */ ELSE, /* else */ ZEND, /* end */ - ESAC, /* esac */ - FI, /* fi */ /* 50 */ + ESAC, /* esac */ /* 50 */ + FI, /* fi */ FOR, /* for */ FOREACH, /* foreach */ FUNC, /* function */ - IF, /* if */ - NOCORRECT, /* nocorrect */ /* 55 */ + IF, /* if */ /* 55 */ + NOCORRECT, /* nocorrect */ REPEAT, /* repeat */ SELECT, /* select */ THEN, /* then */ - TIME, /* time */ - UNTIL, /* until */ /* 60 */ + TIME, /* time */ /* 60 */ + UNTIL, /* until */ WHILE /* while */ }; @@ -783,12 +784,14 @@ struct eccstr { #define WC_TRY_SKIP(C) wc_data(C) #define WCB_TRY(O) wc_bld(WC_TRY, (O)) -#define WC_CASE_TYPE(C) (wc_data(C) & 3) +#define WC_CASE_TYPE(C) (wc_data(C) & 7) #define WC_CASE_HEAD 0 #define WC_CASE_OR 1 #define WC_CASE_AND 2 -#define WC_CASE_SKIP(C) (wc_data(C) >> 2) -#define WCB_CASE(T,O) wc_bld(WC_CASE, ((T) | ((O) << 2))) +#define WC_CASE_TESTAND 3 +#define WC_CASE_FREE (3) /* Next bit available in integer */ +#define WC_CASE_SKIP(C) (wc_data(C) >> WC_CASE_FREE) +#define WCB_CASE(T,O) wc_bld(WC_CASE, ((T) | ((O) << WC_CASE_FREE))) #define WC_IF_TYPE(C) (wc_data(C) & 3) #define WC_IF_HEAD 0 diff --git a/Test/A01grammar.ztst b/Test/A01grammar.ztst index 093ef5635..42ff8d405 100644 --- a/Test/A01grammar.ztst +++ b/Test/A01grammar.ztst @@ -413,6 +413,29 @@ 0:`case' with braces >schmavo + for word in artichoke bladderwort chrysanthemum Zanzibar + case $word in + (*der*) print $word contains the forbidden incantation der + ;; + (a*) print $word begins with a + ;& + ([[:upper:]]*) print $word either begins with a or an upper case letter + ;| + ([[:lower:]]*) print $word begins with a lower case letter + ;| + (*e*) print $word contains an e + ;; + esac +0:`case' with mixed ;& and ;| +>artichoke begins with a +>artichoke either begins with a or an upper case letter +>artichoke begins with a lower case letter +>artichoke contains an e +>bladderwort contains the forbidden incantation der +>chrysanthemum begins with a lower case letter +>chrysanthemum contains an e +>Zanzibar either begins with a or an upper case letter + print 'This test hangs the shell when it fails...' >&8 name=0 # The number 4375 here is chosen to produce more than 16384 bytes of output -- cgit 1.4.1