Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/cmd/ksh93/data/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const Shtable_t shtab_options[] =
"braceexpand", SH_BRACEEXPAND,
#endif
"noclobber", SH_NOCLOBBER,
"comsub_brace_greedy", SH_COMSUB_BRACE_GREEDY, /* bug-691: phi: */
#if SHOPT_ESH
"emacs", SH_EMACS,
#endif
Expand Down
2 changes: 2 additions & 0 deletions src/cmd/ksh93/include/shell.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ typedef union Shnode_u Shnode_t;
#define SH_MULTILINE 47
#define SH_NOBACKSLCTRL 48
#endif
/* bug-691: Phi: ${ list } } [;\n] } eat '}' until we get one followin [;\n] */
#define SH_COMSUB_BRACE_GREEDY 49 /* Bug-691: */
#define SH_LOGIN_SHELL 67

#if _BLD_ksh
Expand Down
1 change: 1 addition & 0 deletions src/cmd/ksh93/include/shlex.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ struct _shlex_pvt_lexstate_
{
char incase; /* 1 for case pattern, 2 after case */
char intest; /* 1 inside [[ ... ]] */
char inbracecomsub; /* 1 inside ${ ... [;\n]} bug-691: Phi:*/
char testop1; /* 1 when unary test op legal */
char testop2; /* 1 when binary test op legal */
char reservok; /* >0 for reserved word legal */
Expand Down
75 changes: 73 additions & 2 deletions src/cmd/ksh93/sh/lex.c
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,20 @@ int sh_lex(Lex_t* lp)
state = sh_lexstates[mode];
do {
n = STATE(state,c);
/*
* Bug-691: Phi gross hack
* Greedy ${list} will treat all '}' not on a reserved
* word (i.e after [;\n]) as regular char.
* Non greedy close ${ on first '}' (ATT code)
*/
if( sh_isoption(SH_COMSUB_BRACE_GREEDY) &&
lp->lex.inbracecomsub && c=='}' &&
( state==sh_lexstates[ ST_BEGIN] ||
state==sh_lexstates[ST_NORM]
)
)
{ n=S_REG ; /* treat '}' as reg char */
}
if (varnametry)
varnamecount += LEN;
} while (n == 0);
Expand Down Expand Up @@ -430,6 +444,10 @@ int sh_lex(Lex_t* lp)
lp->lex.skipword = 0;
/* FALLTHROUGH */
case S_NL:
/* bug-691: Phi: */
if( sh_isoption(SH_COMSUB_BRACE_GREEDY) )
lp->lex.inbracecomsub=0;

/* skip over new-lines */
lp->lex.last_quote = 0;
while(sh.inlineno++,fcget()=='\n');
Expand All @@ -453,6 +471,10 @@ int sh_lex(Lex_t* lp)
}
continue;
case S_OP:
/* bug-691: Phi: */
if( sh_isoption(SH_COMSUB_BRACE_GREEDY) )
lp->lex.inbracecomsub=0;

/* return operator token */
if(c=='<' || c=='>')
{
Expand Down Expand Up @@ -1001,7 +1023,7 @@ int sh_lex(Lex_t* lp)
continue;
if((n=sh_lexstates[ST_BEGIN][c])==0 || n==S_OP || n==S_NLTOK)
{
c = LBRACE;
c = LBRACE;
goto do_comsub;
}
}
Expand Down Expand Up @@ -1259,6 +1281,42 @@ int sh_lex(Lex_t* lp)
if(!(state=lp->lexd.first))
state = fcfirst();
n = fcseek(0)-(char*)state;
/*
* bug-691: Phi:
* In the perf path (ksh interpreter) we don't enter the following if()
* For shcomp we catch "set [+-]o comsub_brace_greedy" and set sh_option
* accordingly, allowing compile to proceed in greedy mode if needed.
*/
if(sh.shcomp && lp->lex.reservok && n==3 &&
state[0]=='s' && state[1]=='e'&& state[2]=='t' )
{
int o=0;
const char *p=state+3;
while(*p && *p!=';' && *p!='\n')
{
if(p[0]=='+' && p[1]=='o')
{
o=1;
}
if(p[0]=='-' && p[1]=='o')
{
o=2;
}
if( o && strncmp(p,"comsub_brace_greedy",19)==0)
{
if(o==1)
{
sh_offoption(SH_COMSUB_BRACE_GREEDY);
}
if(o==2)
{
sh_onoption(SH_COMSUB_BRACE_GREEDY);
}
break;
}
p++;
}
}
if(!lp->arg)
lp->arg = stkseek(sh.stk,ARGVAL);
if(n>0)
Expand All @@ -1281,7 +1339,12 @@ int sh_lex(Lex_t* lp)
if(n==LBRACT)
c = 0;
else if(n==RBRACE && lp->comsub)
return lp->token=n;
{ /* Bug-691: Phi: AT&T original non greedy path */
if( ! (sh_isoption(SH_COMSUB_BRACE_GREEDY) &&
lp->lex.inbracecomsub))
return lp->token=n;
/* Bug-691: Phi: Greedy continue... */
}
else if(n=='~')
c = ARG_MAC;
else
Expand Down Expand Up @@ -1552,6 +1615,11 @@ static int comsub(Lex_t *lp, int endtok)
int off, messages=0, assignok=lp->assignok, csub;
struct _shlex_pvt_lexstate_ save = lp->lex;
csub = lp->comsub;

/* bug-691: Phi: */
if( sh_isoption(SH_COMSUB_BRACE_GREEDY) && *cp=='{' )
lp->lex.inbracecomsub = 1;

sh_lexopen(lp,1);
lp->lexd.dolparen++;
lp->lexd.dolparen_arithexp = endtok==LPAREN && fcpeek(1)==LPAREN; /* $(( */
Expand Down Expand Up @@ -1622,13 +1690,16 @@ static int comsub(Lex_t *lp, int endtok)
if(endtok==LBRACE && !lp->lex.incase)
{
lp->comsub = 0;
/* bug-691: Phi: */
if(! sh_isoption(SH_COMSUB_BRACE_GREEDY))
count++;
}
break;
case RBRACE:
rbrace:
if(endtok==LBRACE && --count<=0)
goto done;

if(count==1)
lp->comsub = endtok==LBRACE;
break;
Expand Down
1 change: 1 addition & 0 deletions src/cmd/ksh93/sh/parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@ Shnode_t *sh_dolparen(Lex_t* lp)
t = sh_cmd(lp,RPAREN,SH_NL|SH_EMPTY);
break;
case LBRACE:
lp->lex.inbracecomsub=1; /* bug-691: Phi: */
t = sh_cmd(lp,RBRACE,SH_NL|SH_EMPTY);
break;
}
Expand Down
79 changes: 79 additions & 0 deletions src/cmd/ksh93/tests/greedy_comsub.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
########################################################################
# #
# This file is part of the ksh 93u+m package #
# Copyright (c) 2022-2025 Contributors to ksh 93u+m #
# and is licensed under the #
# Eclipse Public License, Version 2.0 #
# #
# A copy of the License is available at #
# https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html #
# (with md5 checksum 84283fa8859daf213bdda5a9f8d1be1d) #
# #
# Phi <[email protected]> #
# Martijn Dekker <[email protected]> #
# #
########################################################################


. "${SHTESTS_COMMON:-${0%/*}/_common}"


alias T='do_test "$LINENO"'

# ======
# Tests for greedy comsub, i.e ${ list } [;\n]}
# | |
# | +-> Reserved word (keyword)
# +--------> ! Reserved word (keyword)
# https://github.com/ksh93/ksh/issues/691

function do_test # $LINENO "${ list ;}" "expect"
{ [ "$2" = "$3" ] ||
\err_exit "$1" "expected '$3', got '$2'"
}


# ======

set -o | grep -q comsub_brace_greedy ||
{ warning \
"This SHELL doesn't support 'set -o comsub_brace_greedy '; skipping tests"
alias T=:
}
set -o comsub_brace_greedy

T "${ echo A ; }" "A"
T "${ echo A ;}" "A"
T "${ echo A;}" "A"
# ======

T "${ echo A ${ echo B ;} ;}" "A B" # err_exit
T "${ echo A ${ echo B ;};}" "A B"
T "${ echo A ${ echo B;};}" "A B"
T "${ echo A { ${ echo B ;} C } ;}" "A { B C }"
T "${ echo A { ${ echo B;} C } ;}" "A { B C }"
T "${ echo A { ${ echo B;} C};}" "A { B C}"
T "${ echo A ;} ${ echo B ;}" "A B"
T "${ echo A ;} ${ echo B;}" "A B"
T "${ echo A ;}${ echo B;}" "AB"
T "${ echo A;}${ echo B;}" "AB"

# @stephane-chazelas test cases
# https://github.com/ksh93/ksh/issues/691
T "${ echo {a.b}; }" "{a.b}"
T "${ echo '{a,b}c' ;}" "{a,b}c"
T "${ echo ${ echo {fd[0]}< /dev/null; } ;}" ""

# Variation on @stephane-chazelas
a=1 a.c=1
T "${ echo ${a.c}; }" "1"
T "${ echo {a..c}; }" "a b c"
a=x b=z
T "${ echo {$a..$b}; }" "x y z"
T "${ echo {${a}..${b}}; }" "x y z"
a='x;' b='z;'
T "${ echo {${a//;}..${b//;}}; }" "x y z"


# ======
exit $((Errors<125?Errors:125))
1 change: 1 addition & 0 deletions src/cmd/ksh93/tests/shtests
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ do [[ $i == *.sh ]] || i+='.sh'
glob.sh) grep -c '^[[:blank:]]*test_[a-z]\{3,\}' $i ;;
leaks.sh) grep -c ^TEST $i ;;
printf.sh) grep -c -E '([[:blank:]]err_exit|^[[:blank:]]*T)[[:blank:]]' $i ;;
greedy_comsub.sh) grep -c '^T ' $i ;; # bug-691: Phi:
pty.sh) grep -c 'tst ' $i ;;
*) grep -c err_exit $i ;;
esac )
Expand Down