From 21f23b600b1633d7eea0f62ce0ad54fddf497728 Mon Sep 17 00:00:00 2001 From: Peter Avalos Date: Sun, 21 Aug 2011 14:45:13 -0700 Subject: [PATCH] sh: Add case statement fallthrough (with ';&' instead of ';;'). Replacing ;; with the new control operator ;& will cause the next list to be executed as well without checking its pattern, continuing until a list ends with ;; or until the end of the case statement. This is like omitting "break" in a C "switch" statement. The sequence ;& was formerly invalid. This feature is proposed for the next POSIX issue in Austin Group issue #449. Obtained-from: FreeBSD 223186 --- bin/sh/eval.c | 10 +++++- bin/sh/mktokens | 3 +- bin/sh/nodetypes | 5 +-- bin/sh/parser.c | 16 +++++++--- bin/sh/sh.1 | 9 ++++-- tools/regression/bin/sh/builtins/case9.0 | 39 ++++++++++++++++++++++++ 6 files changed, 71 insertions(+), 11 deletions(-) create mode 100644 tools/regression/bin/sh/builtins/case9.0 diff --git a/bin/sh/eval.c b/bin/sh/eval.c index ecc53f4c46..32ee05ec8f 100644 --- a/bin/sh/eval.c +++ b/bin/sh/eval.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)eval.c 8.9 (Berkeley) 6/8/95 - * $FreeBSD: src/bin/sh/eval.c,v 1.110 2011/06/16 21:50:28 jilles Exp $ + * $FreeBSD: src/bin/sh/eval.c,v 1.111 2011/06/17 13:03:49 jilles Exp $ */ #include @@ -387,6 +387,14 @@ evalcase(union node *n, int flags) for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) { for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) { if (casematch(patp, arglist.list->text)) { + while (cp->nclist.next && + cp->type == NCLISTFALLTHRU) { + if (evalskip != 0) + break; + evaltree(cp->nclist.body, + flags & ~EV_EXIT); + cp = cp->nclist.next; + } if (evalskip == 0) { evaltree(cp->nclist.body, flags); } diff --git a/bin/sh/mktokens b/bin/sh/mktokens index a5c4471a6b..9e79eab808 100644 --- a/bin/sh/mktokens +++ b/bin/sh/mktokens @@ -36,7 +36,7 @@ # SUCH DAMAGE. # # @(#)mktokens 8.1 (Berkeley) 5/31/93 -# $FreeBSD: src/bin/sh/mktokens,v 1.11 2011/05/22 15:24:56 jilles Exp $ +# $FreeBSD: src/bin/sh/mktokens,v 1.12 2011/06/17 13:03:49 jilles Exp $ # The following is a list of tokens. The second column is nonzero if the # token marks the end of a list. The third column is the name to print in @@ -54,6 +54,7 @@ TPIPE 0 "|" TLP 0 "(" TRP 1 ")" TENDCASE 1 ";;" +TFALLTHRU 1 ";&" TREDIR 0 redirection TWORD 0 word TIF 0 "if" diff --git a/bin/sh/nodetypes b/bin/sh/nodetypes index f04d77dcdc..e311f23c20 100644 --- a/bin/sh/nodetypes +++ b/bin/sh/nodetypes @@ -34,7 +34,7 @@ # SUCH DAMAGE. # # @(#)nodetypes 8.2 (Berkeley) 5/4/95 -# $FreeBSD: src/bin/sh/nodetypes,v 1.10 2005/01/10 08:39:25 imp Exp $ +# $FreeBSD: src/bin/sh/nodetypes,v 1.11 2011/06/17 13:03:49 jilles Exp $ # This file describes the nodes used in parse trees. Unindented lines # contain a node type followed by a structure tag. Subsequent indented @@ -100,12 +100,13 @@ NCASE ncase # a case statement expr nodeptr # the word to switch on cases nodeptr # the list of cases (NCLIST nodes) -NCLIST nclist # a case +NCLIST nclist # a case ending with ;; type int next nodeptr # the next case in list pattern nodeptr # list of patterns for this case body nodeptr # code to execute for this case +NCLISTFALLTHRU nclist # a case ending with ;& NDEFUN narg # define a function. The "next" field contains # the body of the function. diff --git a/bin/sh/parser.c b/bin/sh/parser.c index 82a2a770ec..1aaaac9148 100644 --- a/bin/sh/parser.c +++ b/bin/sh/parser.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)parser.c 8.7 (Berkeley) 5/16/95 - * $FreeBSD: src/bin/sh/parser.c,v 1.113 2011/06/09 23:12:23 jilles Exp $ + * $FreeBSD: src/bin/sh/parser.c,v 1.114 2011/06/17 13:03:49 jilles Exp $ */ #include @@ -541,10 +541,13 @@ TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); checkkwd = CHKNL | CHKKWD | CHKALIAS; if ((t = readtoken()) != TESAC) { - if (t != TENDCASE) - synexpect(TENDCASE); + if (t == TENDCASE) + ; + else if (t == TFALLTHRU) + cp->type = NCLISTFALLTHRU; else - checkkwd = CHKNL | CHKKWD, readtoken(); + synexpect(TENDCASE); + checkkwd = CHKNL | CHKKWD, readtoken(); } cpp = &cp->nclist.next; } @@ -930,8 +933,11 @@ xxreadtoken(void) pungetc(); RETURN(TPIPE); case ';': - if (pgetc() == ';') + c = pgetc(); + if (c == ';') RETURN(TENDCASE); + else if (c == '&') + RETURN(TFALLTHRU); pungetc(); RETURN(TSEMI); case '(': diff --git a/bin/sh/sh.1 b/bin/sh/sh.1 index d2cf51693f..cdb0e7addd 100644 --- a/bin/sh/sh.1 +++ b/bin/sh/sh.1 @@ -34,7 +34,7 @@ .\" SUCH DAMAGE. .\" .\" from: @(#)sh.1 8.6 (Berkeley) 5/4/95 -.\" $FreeBSD: src/bin/sh/sh.1,v 1.169 2011/06/15 21:48:10 jilles Exp $ +.\" $FreeBSD: src/bin/sh/sh.1,v 1.170 2011/06/17 13:03:49 jilles Exp $ .\" .Dd August 21, 2011 .Dt SH 1 @@ -383,7 +383,7 @@ The following is a list of valid operators: .It Control operators: .Bl -column "XXX" "XXX" "XXX" "XXX" "XXX" -offset center -compact .It Li & Ta Li && Ta Li ( Ta Li ) Ta Li \en -.It Li ;; Ta Li ; Ta Li | Ta Li || +.It Li ;; Ta Li ;& Ta Li ; Ta Li | Ta Li || .El .It Redirection operators: .Bl -column "XXX" "XXX" "XXX" "XXX" "XXX" -offset center -compact @@ -1001,6 +1001,11 @@ described later), separated by .Ql \&| characters. +If the selected list is terminated by the control operator +.Ql ;& +instead of +.Ql ;; , +execution continues with the next list. The exit code of the .Ic case command is the exit code of the last command executed in the list or diff --git a/tools/regression/bin/sh/builtins/case9.0 b/tools/regression/bin/sh/builtins/case9.0 new file mode 100644 index 0000000000..19b18fd3f0 --- /dev/null +++ b/tools/regression/bin/sh/builtins/case9.0 @@ -0,0 +1,39 @@ +# $FreeBSD: src/tools/regression/bin/sh/builtins/case9.0,v 1.1 2011/06/17 13:03:49 jilles Exp $ + +errors=0 + +f() { + result= + case $1 in + a) result=${result}a ;; + b) result=${result}b ;& + c) result=${result}c ;& + d) result=${result}d ;; + e) result=${result}e ;& + esac +} + +check() { + f "$1" + if [ "$result" != "$2" ]; then + printf "For %s, expected %s got %s\n" "$1" "$2" "$result" + errors=$((errors + 1)) + fi +} + +check '' '' +check a a +check b bcd +check c cd +check d d +check e e + +if ! (case 1 in + 1) false ;& + 2) true ;; +esac) then + echo "Subshell bad" + errors=$((errors + 1)) +fi + +exit $((errors != 0)) -- 2.41.0