diff --git a/NEWS b/NEWS index e03d5a2a7..1be10d9c8 100644 --- a/NEWS +++ b/NEWS @@ -3,13 +3,6 @@ For full details, see the git log at: https://github.com/ksh93/ksh Any uppercase BUG_* names are modernish shell bug IDs. -2021-11-29: - -- Ksh no longer behaves badly when parsing a type definition command - ('typeset -T' or 'enum') without executing it or when executing it in - a subshell. Types can now safely be defined in subshells and defined - conditionally as in 'if condition; then enum ...; fi'. - 2021-11-24: - The --posix mode was amended to stop the '.' command (but not 'source') from diff --git a/src/cmd/ksh93/bltins/enum.c b/src/cmd/ksh93/bltins/enum.c index c84794385..782ecfd51 100644 --- a/src/cmd/ksh93/bltins/enum.c +++ b/src/cmd/ksh93/bltins/enum.c @@ -21,7 +21,7 @@ #pragma prototyped #include "defs.h" -#define ENUM_ID "enum (ksh 93u+m) 2021-11-29" +#define ENUM_ID "enum (ksh 93u+m) 2021-11-23" const char sh_optenum[] = "[-?@(#)$Id: " ENUM_ID " $\n]" @@ -239,10 +239,6 @@ int b_enum(int argc, char** argv, Shbltin_t *context) error(ERROR_USAGE|2, "%s", optusage(NiL)); return 1; } -#ifndef STANDALONE - if(shp->subshell && !shp->subshare) - sh_subfork(); -#endif while(cp = *argv++) { if(!(np = nv_open(cp, (void*)0, NV_VARNAME|NV_NOADD)) || !(ap=nv_arrayptr(np)) || ap->fun || (sz=ap->nelem&(((1L<lastline) #ifndef NIL @@ -187,10 +183,13 @@ static void typeset_order(const char *str,int line) } /* - * This function handles linting for 'typeset' options via typeset_order(). + * Pre-add type declaration built-ins at parse time to avoid + * syntax errors when using -c, shcomp, '.'/source or eval. * - * Also, upon parsing typeset -T or enum, it pre-adds the type declaration built-ins that these would create to - * an internal tree to avoid syntax errors upon pre-execution parsing of assignment-arguments with parentheses. + * This hack has a bad side effect: defining a type with 'typeset -T' or 'enum' + * in a subshell or an 'if false' block will cause an inconsistent state. But + * as these built-ins alter the syntax of the shell, it's necessary for making + * them work if we're parsing an entire script before or without executing it. * * intypeset==1 for typeset & friends; intypeset==2 for enum */ @@ -251,43 +250,6 @@ static void check_typedef(struct comnod *tp, char intypeset) if(cp) nv_onattr(sh_addbuiltin(cp, (Shbltin_f)SYSTRUE->nvalue.bfp, NIL(void*)), NV_BLTIN|BLT_DCL); } -/* - * (De)activate an internal declaration built-ins tree into which check_typedef() can pre-add dummy type - * declaration command nodes, allowing correct parsing of assignment-arguments with parentheses for custom - * type declaration commands before actually executing the commands that create those commands. - * - * A viewpath from the main built-ins tree to this internal tree is added, unifying the two for search - * purposes and causing new nodes to be added to the internal tree. When parsing is done, we close that - * viewpath. This hides those pre-added nodes at execution time, avoiding an inconsistent state if a type - * creation command is parsed but not executed. - */ -static void dcl_hacktivate(void) -{ - if(!dcl_tree) - dcl_tree = dtopen(&_Nvdisc, Dtoset); - if(dcl_recursion++) - return; - dtview(sh.bltin_tree, dcl_tree); - orig_exit = error_info.exit; - error_info.exit = dcl_exit; -} -static void dcl_dehacktivate(void) -{ -#if !_AST_ksh_release - if(!dcl_recursion || !dcl_tree) - abort(); -#endif - if(--dcl_recursion) - return; - error_info.exit = orig_exit; - dtview(sh.bltin_tree, NIL(Dt_t*)); -} -static noreturn void dcl_exit(int e) -{ - dcl_dehacktivate(); - (*error_info.exit)(e); - UNREACHABLE(); -} /* * Make a parent node for fork() or io-redirection @@ -546,7 +508,6 @@ static Shnode_t *sh_cmd(Lex_t *lexp, register int sym, int flag) { register Shnode_t *left, *right; register int type = FINT|FAMP; - dcl_hacktivate(); if(sym==NL) lexp->lasttok = 0; left = list(lexp,flag); @@ -588,7 +549,6 @@ static Shnode_t *sh_cmd(Lex_t *lexp, register int sym, int flag) sh_syntax(lexp); } } - dcl_dehacktivate(); return(left); } @@ -1090,19 +1050,8 @@ static struct argnod *assign(Lex_t *lexp, register struct argnod *ap, int type) if(array && type==NV_TYPE) { struct argnod *arg = lexp->arg; - int save_recursion = dcl_recursion; - int p; - /* - * Forcibly deactivate the dummy declaration built-ins tree as path_search() does an - * FPATH search, which may cause arbitrary ksh code to be executed. Yes, at parse time. - */ n = lexp->token; - dcl_recursion = 1; - dcl_dehacktivate(); - p = path_search(lexp->sh,lexp->arg->argval,NIL(Pathcomp_t**),1); - dcl_hacktivate(); - dcl_recursion = save_recursion; - if(p && (np=nv_search(lexp->arg->argval,lexp->sh->fun_tree,0)) && nv_isattr(np,BLT_DCL)) + if(path_search(lexp->sh,lexp->arg->argval,NIL(Pathcomp_t**),1) && (np=nv_search(lexp->arg->argval,lexp->sh->fun_tree,0)) && nv_isattr(np,BLT_DCL)) { lexp->token = n; lexp->arg = arg; diff --git a/src/cmd/ksh93/sh/xec.c b/src/cmd/ksh93/sh/xec.c index 8610667fb..87525fc9c 100644 --- a/src/cmd/ksh93/sh/xec.c +++ b/src/cmd/ksh93/sh/xec.c @@ -1090,8 +1090,6 @@ int sh_exec(register const Shnode_t *t, int flags) #if SHOPT_TYPEDEF else if(argn>=3 && checkopt(com,'T')) { - if(shp->subshell && !shp->subshare) - sh_subfork(); # if SHOPT_NAMESPACE if(shp->namespace) { diff --git a/src/cmd/ksh93/tests/enum.sh b/src/cmd/ksh93/tests/enum.sh index d8402492d..437752b86 100755 --- a/src/cmd/ksh93/tests/enum.sh +++ b/src/cmd/ksh93/tests/enum.sh @@ -155,11 +155,6 @@ got=$(eval 2>&1 'command command command enum -i -i -iii --igno -ii PARSER_t=(r exp='PARSER_t -r -A hack=([C]=g)' [[ $got == "$exp" ]] || err_exit "incorrect typeset output for enum with command prefix and options" \ "(expected $(printf %q "$exp"); got $(printf %q "$got"))" -PATH=/dev/null command -v PARSER_t >/dev/null && err_exit "PARSER_t leaked out of subshell" -if false -then enum PARSER2_t=(a b) -fi -PATH=/dev/null command -v PARSER2_t >/dev/null && err_exit "PARSER2_t incompletely defined though definition was never executed" # ====== exit $((Errors<125?Errors:125)) diff --git a/src/cmd/ksh93/tests/types.sh b/src/cmd/ksh93/tests/types.sh index 142f861ca..1b1c1a1d3 100755 --- a/src/cmd/ksh93/tests/types.sh +++ b/src/cmd/ksh93/tests/types.sh @@ -452,24 +452,20 @@ cd "$tmp" FPATH=$PWD PATH=$PWD:$PATH cat > A_t <<- \EOF - if false - then typeset -T Parser_shenanigans=(typeset -i foo) - fi typeset -T A_t=( B_t b ) EOF cat > B_t <<- \EOF - PATH=/dev/null command -v Parser_shenanigans typeset -T B_t=( integer n=5 ) EOF unset n -if n=$(FPATH=$PWD PATH=$PWD:$PATH "$SHELL" -c 'A_t a; print ${a.b.n}' 2>&1) -then [[ $n == '5' ]] || err_exit "dynamic loading of types gives wrong result (got $(printf %q "$n"))" -else err_exit "unable to load types dynamically (got $(printf %q "$n"))" +if n=$(FPATH=$PWD PATH=$PWD:$PATH $SHELL 2> /dev/null -c 'A_t a; print ${a.b.n}') +then (( n==5 )) || err_exit 'dynamic loading of types gives wrong result' +else err_exit 'unable to load types dynamically' fi # check that typeset -T reproduces a type. @@ -643,19 +639,5 @@ got=$($SHELL -c 'enum Foo_t=(foo bar); typeset -T') [[ -z $got ]] || err_exit "Types created by enum are listed with 'typeset -T'" \ "(got $(printf %q "$got"))" -# ====== -# Parser shenanigans. -if false -then typeset -T PARSER_t=(typeset name=foobar) -fi -PATH=/dev/null command -v PARSER_t >/dev/null && err_exit "PARSER_t incompletely defined though definition was never executed" - -unset v -got=$( set +x; redirect 2>&1; typeset -T Subsh_t=(typeset -i x); Subsh_t -a v=( (x=1) (x=2) (x=3) ); typeset -p v ) -exp='Subsh_t -a v=((typeset -i x=1) (typeset -i x=2) (typeset -i x=3))' -[[ $got == "$exp" ]] || err_exit "bad typeset output for Subsh_t" \ - "(expected $(printf %q "$exp"), got $(printf %q "$got"))" -PATH=/dev/null command -v Subsh_t >/dev/null && err_exit "Subsh_t leaked out of subshell" - # ====== exit $((Errors<125?Errors:125))