diff options
author | jilles <jilles@FreeBSD.org> | 2012-07-15 10:19:43 +0000 |
---|---|---|
committer | jilles <jilles@FreeBSD.org> | 2012-07-15 10:19:43 +0000 |
commit | 689774f8e79cc120c6606a52baf8a4a0a17ed6b6 (patch) | |
tree | 2e7b1c68d295b04b7db1b3b87ea0c5a9c84a9291 /bin/sh/eval.c | |
parent | 057a8ab456da82a3e40e6fba4629ba852dd79255 (diff) | |
download | FreeBSD-src-689774f8e79cc120c6606a52baf8a4a0a17ed6b6.zip FreeBSD-src-689774f8e79cc120c6606a52baf8a4a0a17ed6b6.tar.gz |
sh: Expand assignment-like words specially for export/readonly/local.
Examples:
export x=~
now expands the tilde
local y=$1
is now safe, even if $1 contains IFS characters or metacharacters.
For a word to "look like an assignment", it must start with a name followed
by an equals sign, none of which may be quoted.
The special treatment applies when the first word (potentially after
"command") is "export", "readonly" or "local". There may be quoting
characters but no expansions. If "local" is overridden with a function there
is no special treatment ("export" and "readonly" cannot be overridden with a
function).
If things like
local arr=(1 2 3)
are ever allowed in the future, they cannot call a "local" function. This
would either be a run-time error or it would call the builtin.
This matches Austin Group bug #351, planned for the next issue of POSIX.1.
PR: bin/166771
Diffstat (limited to 'bin/sh/eval.c')
-rw-r--r-- | bin/sh/eval.c | 53 |
1 files changed, 50 insertions, 3 deletions
diff --git a/bin/sh/eval.c b/bin/sh/eval.c index 2d90921..c9e814b 100644 --- a/bin/sh/eval.c +++ b/bin/sh/eval.c @@ -672,6 +672,52 @@ out: result->fd, result->buf, result->nleft, result->jp)); } +static int +mustexpandto(const char *argtext, const char *mask) +{ + for (;;) { + if (*argtext == CTLQUOTEMARK || *argtext == CTLQUOTEEND) { + argtext++; + continue; + } + if (*argtext == CTLESC) + argtext++; + else if (BASESYNTAX[(int)*argtext] == CCTL) + return (0); + if (*argtext != *mask) + return (0); + if (*argtext == '\0') + return (1); + argtext++; + mask++; + } +} + +static int +isdeclarationcmd(struct narg *arg) +{ + int have_command = 0; + + if (arg == NULL) + return (0); + while (mustexpandto(arg->text, "command")) { + have_command = 1; + arg = &arg->next->narg; + if (arg == NULL) + return (0); + /* + * To also allow "command -p" and "command --" as part of + * a declaration command, add code here. + * We do not do this, as ksh does not do it either and it + * is not required by POSIX. + */ + } + return (mustexpandto(arg->text, "export") || + mustexpandto(arg->text, "readonly") || + (mustexpandto(arg->text, "local") && + (have_command || !isfunc("local")))); +} + /* * Check if a builtin can safely be executed in the same process, * even though it should be in a subshell (command substitution). @@ -743,11 +789,12 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) exitstatus = 0; for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { if (varflag && isassignment(argp->narg.text)) { - expandarg(argp, &varlist, EXP_VARTILDE); + expandarg(argp, varflag == 1 ? &varlist : &arglist, + EXP_VARTILDE); continue; - } + } else if (varflag == 1) + varflag = isdeclarationcmd(&argp->narg) ? 2 : 0; expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); - varflag = 0; } *arglist.lastp = NULL; *varlist.lastp = NULL; |