mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
Re-fix defining types conditionally or in subshells (re: f508660d)
New version. I'm pretty sure the problems that forced me to revert
it earlier are fixed.
This commit mitigates the effects of the hack explained in the
referenced commit so that dummy built-in command nodes added by the
parser for declaration/assignment purposes do not leak out into the
execution level, except in a relatively harmless corner case.
Something like
if false; then
typeset -T Foo_t=(integer -i bar)
fi
will no longer leave a broken dummy Foo_t declaration command. The
same applies to declaration commands created with enum.
The corner case remaining is:
$ ksh -c 'false && enum E_t=(a b c); E_t -a x=(b b a c)'
ksh: E_t: not found
Since the 'enum' command is not executed, this should have thrown
a syntax error on the 'E_t -a' declaration:
ksh: syntax error at line 1: `(' unexpected
This is because the -c script is parsed entirely before being
executed, so E_t is recognised as a declaration built-in at parse
time. However, the 'not found' error shows that it was successfully
eliminated at execution time, so the inconsistent state will no
longer persist.
This fix now allows another fix to be effective as well: since
built-ins do not know about virtual subshells, fork a virtual
subshell into a real subshell before adding any built-ins.
src/cmd/ksh93/sh/parse.c:
- Add a pair of functions, dcl_hactivate() and dcl_dehacktivate(),
that (de)activate an internal declaration built-ins tree into
which check_typedef() can pre-add dummy type declaration command
nodes. 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. Since the parser is sometimes called recursively
(e.g. for command substitutions), keep track of this and only
activate and deactivate at the first level.
(Fixed compared to previous version of this commit: calling
dcl_dehacktivate() when the recursion level is already zero is
now a harmless no-op. Since this only occurs in error handling
conditions, who cares.)
- We also need to catch errors. This is done by setting libast's
error_info.exit variable to a dcl_exit() function that tidies up
and then passes control to the original (usually sh_exit()).
(Fixed compared to previous version of this commit: dcl_exit()
immediately deactivates the hack, no matter the recursion level,
and restores the regular sh_exit(). This is the right thing to
do when we're in the process of erroring out.)
- sh_cmd(): This is the most central function in the parser. You'd
think it was sh_parse(), but $(modern)-form command substitutions
use sh_dolparen() instead. Both call sh_cmd(). So let's simply
add a dcl_hacktivate() call at the beginning and a
dcl_deactivate() call at the end.
- assign(): This function calls path_search(), which among many
other things executes an FPATH search, which may execute
arbitrary code at parse time (!!!). So, regardless of recursion
level, forcibly dehacktivate() to avoid those ugly parser side
effects returning in that context.
src/cmd/ksh93/bltins/enum.c: b_enum():
- Fork a virtual subshell before adding a built-in.
src/cmd/ksh93/sh/xec.c: sh_exec():
- Fork a virtual subshell when detecting typeset's -T option.
Improves fix to https://github.com/ksh93/ksh/issues/256
This commit is contained in:
parent
2bc1d814c9
commit
e67df29c07
10 changed files with 98 additions and 18 deletions
|
|
@ -60,6 +60,10 @@ static Shnode_t *test_and(Lex_t*);
|
|||
static Shnode_t *test_or(Lex_t*);
|
||||
static Shnode_t *test_primary(Lex_t*);
|
||||
|
||||
static void dcl_hacktivate(void), dcl_dehacktivate(void), (*orig_exit)(int), dcl_exit(int);
|
||||
static Dt_t *dcl_tree;
|
||||
static unsigned dcl_recursion;
|
||||
|
||||
#define sh_getlineno(lp) (lp->lastline)
|
||||
|
||||
#ifndef NIL
|
||||
|
|
@ -169,13 +173,10 @@ static void typeset_order(const char *str,int line)
|
|||
}
|
||||
|
||||
/*
|
||||
* Pre-add type declaration built-ins at parse time to avoid
|
||||
* syntax errors when using -c, shcomp, '.'/source or eval.
|
||||
* This function handles linting for 'typeset' options via typeset_order().
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* intypeset==1 for typeset & friends; intypeset==2 for enum
|
||||
*/
|
||||
|
|
@ -236,6 +237,40 @@ 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_recursion++)
|
||||
return;
|
||||
if(!dcl_tree)
|
||||
dcl_tree = dtopen(&_Nvdisc, Dtoset);
|
||||
dtview(sh.bltin_tree, dcl_tree);
|
||||
orig_exit = error_info.exit;
|
||||
error_info.exit = dcl_exit;
|
||||
}
|
||||
static void dcl_dehacktivate(void)
|
||||
{
|
||||
if(!dcl_recursion || --dcl_recursion)
|
||||
return;
|
||||
error_info.exit = orig_exit;
|
||||
dtview(sh.bltin_tree, NIL(Dt_t*));
|
||||
}
|
||||
static noreturn void dcl_exit(int e)
|
||||
{
|
||||
dcl_recursion = 1;
|
||||
dcl_dehacktivate();
|
||||
(*error_info.exit)(e);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
/*
|
||||
* Make a parent node for fork() or io-redirection
|
||||
|
|
@ -503,6 +538,7 @@ 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);
|
||||
|
|
@ -544,6 +580,7 @@ static Shnode_t *sh_cmd(Lex_t *lexp, register int sym, int flag)
|
|||
sh_syntax(lexp);
|
||||
}
|
||||
}
|
||||
dcl_dehacktivate();
|
||||
return(left);
|
||||
}
|
||||
|
||||
|
|
@ -1045,8 +1082,19 @@ static struct argnod *assign(Lex_t *lexp, register struct argnod *ap, int type)
|
|||
if(array && type==NV_TYPE)
|
||||
{
|
||||
struct argnod *arg = lexp->arg;
|
||||
unsigned save_recursion = dcl_recursion;
|
||||
int p;
|
||||
/*
|
||||
* Forcibly deactivate the dummy declaration built-ins tree as path_search()
|
||||
* does an FPATH search, which may execute arbitrary code at parse time.
|
||||
*/
|
||||
n = lexp->token;
|
||||
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))
|
||||
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))
|
||||
{
|
||||
lexp->token = n;
|
||||
lexp->arg = arg;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue