Skip to content

Commit fc89d20

Browse files
committed
[v1.1] Add ;;& control operator to 'case' statement
@tungstengmd asks: > would ;;& a la bash be a good addition for case statements ? > instead of just falling through like a ;& it tests any subsequent > case and falls through upon a match To add this, we need three things: 1. The lexer (lex.c) needs to learn to recognise ;;& as a symbol. 2. The parser (parse.c) needs to recognise that symbol and flag it up in 'case' pattern nodes (struct regnod). 3. The executor (xec.c) needs to actually have the functionality. src/cmd/ksh93/include/shlex.h, src/cmd/ksh93/sh/lex.c: - sh_lex(): - Add FALLMATCHSYM which is a repeated (SYMREP) ';' with a trailing ampersand (SYMAMP). - Recognise a trailing & after a SYMREP symbol and addthe SYMAMP bit if found. Any repeated S_OP character (see lexstates.c) can now be combined with a trailing & to make a symbol, but for now we're only using this for FALLMATCHSYM. - Set lex.incase flag also in case of FALLMATCHSYM. - fmttoken(): - Support SYMAMP in combination with SYMREP to generate repeated operators with trailing & for error messages. This is for "syntax error: `;;&' unexpected" and the like. src/cmd/ksh93/sh/parse.c: - syncase(): - The regflag member of struct regnod is 1 for ;&. Add a new value 2 for ;;& by increasing it twice in that case. - Refactor a little for legibility using a switch statement. This avoids the need to duplicate the recursive syncase call. - inout(): - A side effect of the lex.c changes was that >>&foo and <<&foo were no longer detected as a syntax error. To fix this in a future-proof manner, check for unhandled SYM* bits in the token and throw a syntax error if there are any. src/cmd/ksh93/sh/xec.c: sh_exec(): case TSW: - Implementing the functionality requires just a couple of tweaks here. Detect ;& by checking for regflag==1 instead of nonzero. Avoid breaking the outer loop if regflag is neither 0 nor 1, so the pattern matching walk will resume upon encountering ;;&. src/cmd/ksh93/sh/deparse.c: p_switch(): - Add support for ";;&" when generating code from a parse tree. Resolves: #847
1 parent 1038079 commit fc89d20

File tree

11 files changed

+89
-18
lines changed

11 files changed

+89
-18
lines changed

ANNOUNCE

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ New command line editor features:
2929

3030
New shell language features:
3131

32+
- The 'case' compound command now supports the ;;& control operator as on
33+
bash 4.0+. The ;;& operator falls through like ;& but executes the next
34+
matching list instead of simply executing the next list.
35+
3236
- The appending redirection operator &>>FILE is now available. It is a
3337
shorthand for: >>FILE 2>&1
3438

NEWS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ This documents significant changes in the dev branch of ksh 93u+m.
22
For full details, see the git log at: https://github.com/ksh93/ksh
33
Uppercase BUG_* IDs are shell bug IDs as used by the Modernish shell library.
44

5+
2025-04-29:
6+
7+
- [v1.1] The 'case' compound command now supports the ;;& control operator
8+
as on bash 4.0+. The ;;& operator falls through like ;& but executes the
9+
next matching list instead of simply executing the next list.
10+
511
2025-04-27:
612

713
- [v1.1] The emacs mode now accepts a numeric repeat parameter for ordinary

src/cmd/ksh93/include/shlex.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* *
33
* This software is part of the ast package *
44
* Copyright (c) 1982-2011 AT&T Intellectual Property *
5-
* Copyright (c) 2020-2024 Contributors to ksh 93u+m *
5+
* Copyright (c) 2020-2025 Contributors to ksh 93u+m *
66
* and is licensed under the *
77
* Eclipse Public License, Version 2.0 *
88
* *
@@ -133,6 +133,7 @@ typedef struct _shlex_
133133
#define IOMOV0SYM (SYMAMP|'<')
134134
#define IOMOV1SYM (SYMAMP|'>')
135135
#define FALLTHRUSYM (SYMAMP|';')
136+
#define FALLMATCHSYM (SYMREP|SYMAMP|';')
136137
#define COOPSYM (SYMAMP|'|')
137138
#define IORDWRSYM (SYMGT|'<')
138139
#define IORDWRSYMT (SYMSEMI|'<')

src/cmd/ksh93/include/version.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
#include <ast_release.h>
1919
#include "git.h"
2020

21-
#define SH_RELEASE_DATE "2025-04-27" /* must be in this format for $((.sh.version)) */
21+
#define SH_RELEASE_DATE "2025-04-29" /* must be in this format for $((.sh.version)) */
2222
/*
2323
* This comment keeps SH_RELEASE_DATE a few lines away from SH_RELEASE_SVER to avoid
2424
* merge conflicts when cherry-picking dev branch commits onto a release branch.

src/cmd/ksh93/sh.1

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,15 @@ If
443443
is used in place of
444444
.B ;;
445445
the next subsequent list, if any, is executed.
446+
If
447+
.B ;;&
448+
is used in place of
449+
.B ;;
450+
the subsequent list whose
451+
.I pattern\^
452+
matches
453+
.IR word ,
454+
if any, is executed.
446455
.TP
447456
\f3if\fP \f2list\^\fP \f3;then\fP \f2list\^\fP \*(OK \
448457
\f3;elif\fP \f2list\^\fP \f3;then\fP \f2list\^\fP \*(CK .\|.\|. \

src/cmd/ksh93/sh/deparse.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* *
33
* This software is part of the ast package *
44
* Copyright (c) 1982-2011 AT&T Intellectual Property *
5-
* Copyright (c) 2020-2024 Contributors to ksh 93u+m *
5+
* Copyright (c) 2020-2025 Contributors to ksh 93u+m *
66
* and is licensed under the *
77
* Eclipse Public License, Version 2.0 *
88
* *
@@ -597,7 +597,9 @@ static void p_switch(const struct regnod *reg)
597597
if(reg->regcom)
598598
p_tree(reg->regcom,0);
599599
level++;
600-
if(reg->regflag)
600+
if(reg->regflag > 1)
601+
p_keyword(";;&",END);
602+
else if(reg->regflag)
601603
p_keyword(";&",END);
602604
else
603605
p_keyword(";;",END);

src/cmd/ksh93/sh/lex.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* *
33
* This software is part of the ast package *
44
* Copyright (c) 1982-2012 AT&T Intellectual Property *
5-
* Copyright (c) 2020-2024 Contributors to ksh 93u+m *
5+
* Copyright (c) 2020-2025 Contributors to ksh 93u+m *
66
* and is licensed under the *
77
* Eclipse Public License, Version 2.0 *
88
* *
@@ -509,6 +509,12 @@ int sh_lex(Lex_t* lp)
509509
return r;
510510
}
511511
c |= SYMREP;
512+
/* Check for repeated operator character plus &, like ;;& (FALLMATCHSYM) */
513+
fcgetc();
514+
if(fcpeek(0)=='&')
515+
c |= SYMAMP;
516+
else
517+
fcseek(-1);
512518
/* Here document redirection operator '<<' */
513519
if(c==IODOCSYM)
514520
lp->lexd.docword = 1;
@@ -578,7 +584,7 @@ int sh_lex(Lex_t* lp)
578584
if(n)
579585
{
580586
fcseek(1);
581-
lp->lex.incase = (c==BREAKCASESYM || c==FALLTHRUSYM);
587+
lp->lex.incase = (c==BREAKCASESYM || c==FALLTHRUSYM || c==FALLMATCHSYM);
582588
}
583589
else
584590
{
@@ -2070,7 +2076,11 @@ static char *fmttoken(Lex_t *lp, int sym)
20702076
stkfreeze(sh.stk,0);
20712077
sfputc(sh.stk,sym);
20722078
if(sym&SYMREP)
2079+
{
20732080
sfputc(sh.stk,sym);
2081+
if(sym&SYMAMP)
2082+
sfputc(sh.stk,'&');
2083+
}
20742084
else
20752085
{
20762086
switch(sym&SYMMASK)

src/cmd/ksh93/sh/parse.c

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* *
33
* This software is part of the ast package *
44
* Copyright (c) 1982-2012 AT&T Intellectual Property *
5-
* Copyright (c) 2020-2024 Contributors to ksh 93u+m *
5+
* Copyright (c) 2020-2025 Contributors to ksh 93u+m *
66
* and is licensed under the *
77
* Eclipse Public License, Version 2.0 *
88
* *
@@ -707,15 +707,18 @@ static struct regnod* syncase(Lex_t *lexp,int esym)
707707
sh_syntax(lexp,0);
708708
}
709709
r->regcom=sh_cmd(lexp,0,SH_NL|SH_EMPTY|SH_SEMI);
710-
if((tok=lexp->token)==BREAKCASESYM)
711-
r->regnxt=syncase(lexp,esym);
712-
else if(tok==FALLTHRUSYM)
710+
switch (tok = lexp->token)
713711
{
712+
case FALLMATCHSYM: /* ;;& */
713+
r->regflag++;
714+
/* FALLTHROUGH */
715+
case FALLTHRUSYM: /* ;& */
714716
r->regflag++;
717+
/* FALLTHROUGH */
718+
case BREAKCASESYM: /* ;; */
715719
r->regnxt=syncase(lexp,esym);
716-
}
717-
else
718-
{
720+
break;
721+
default:
719722
if(tok!=esym && tok!=EOFSYM)
720723
sh_syntax(lexp,0);
721724
r->regnxt=0;
@@ -1687,6 +1690,8 @@ static struct ionod *inout(Lex_t *lexp,struct ionod *lastio,int flag)
16871690
else if(n>0)
16881691
fcseek(-1);
16891692
}
1693+
else if((token&UCHAR_MAX)!=token) /* unhandled SYM* bits */
1694+
sh_syntax(lexp,0);
16901695
break;
16911696

16921697
case '>':
@@ -1706,6 +1711,8 @@ static struct ionod *inout(Lex_t *lexp,struct ionod *lastio,int flag)
17061711
iof |= IOLSEEK;
17071712
else if((token&SYMSEMI) == SYMSEMI)
17081713
iof |= IOREWRITE;
1714+
else if((token&UCHAR_MAX)!=token) /* unhandled SYM* bits */
1715+
sh_syntax(lexp,0);
17091716
break;
17101717

17111718
default:

src/cmd/ksh93/sh/xec.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2221,10 +2221,11 @@ int sh_exec(const Shnode_t *t, int flags)
22212221
s = rex->argval;
22222222
if(raw && strcmp(r,s)==0 || !raw && strmatch(r,s))
22232223
{
2224-
do
2224+
do /* execute; keep going while ;& */
22252225
sh_exec(t->reg.regcom, t->reg.regflag ? eflag : flags);
2226-
while(t->reg.regflag && (t = (Shnode_t*)t->reg.regnxt));
2227-
t=0;
2226+
while(t->reg.regflag==1 && (t = (Shnode_t*)t->reg.regnxt));
2227+
if(t && t->reg.regflag==0) /* if not end or ;;& */
2228+
t = 0; /* break outer loop */
22282229
break;
22292230
}
22302231
else

src/cmd/ksh93/tests/case.sh

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# #
33
# This software is part of the ast package #
44
# Copyright (c) 1982-2011 AT&T Intellectual Property #
5-
# Copyright (c) 2020-2024 Contributors to ksh 93u+m #
5+
# Copyright (c) 2020-2025 Contributors to ksh 93u+m #
66
# and is licensed under the #
77
# Eclipse Public License, Version 2.0 #
88
# #
@@ -195,5 +195,29 @@ got=$(set +x; eval '
195195
[[ $got == "$exp" ]] || err_exit "spurious syntax error in case with extended expression" \
196196
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
197197

198+
# ======
199+
# ;;& -- fall through to next matching pattern -- new as of 93u+m/1.1
200+
got=
201+
case ${.sh.version} in
202+
*93u+m/1.[!0]* | *93u+m/[!1].* | 93u+m/?[!.]* )
203+
eval '
204+
case foo.txt in
205+
foo*) got+=yes1 ;;&
206+
bar*) got+=no1 ;;
207+
*.txt) got+=yes2 ;&
208+
*.asc) got+=yes3 ;;&
209+
*.jpg) got+=no2 ;;
210+
*.gif) got+=no3 ;;
211+
*.tx*) got+=yes4 ;;
212+
*) got+=no4 ;;
213+
esac
214+
'
215+
exp=yes1yes2yes3yes4
216+
[[ $got == "$exp" ]] || err_exit ";;& case operator" \
217+
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
218+
;;
219+
*) warning ";;& operator supported as of 93u+m/1.1 but we're on ${.sh.version} -- skipping test"
220+
esac
221+
198222
# ======
199223
exit $((Errors<125?Errors:125))

0 commit comments

Comments
 (0)