ash: handle "A=1 A=2 B=$A; echo $B". closes bug 947.

This commit is contained in:
Denis Vlasenko 2008-02-15 15:02:15 +00:00
parent 977bc6a137
commit 0e6f661e23
6 changed files with 96 additions and 39 deletions

View File

@ -42,19 +42,18 @@
* a quit signal will generate a core dump. * a quit signal will generate a core dump.
*/ */
#define DEBUG 0 #define DEBUG 0
#define IFS_BROKEN
#define PROFILE 0 #define PROFILE 0
#if ENABLE_ASH_JOB_CONTROL
#define JOBS 1 #define IFS_BROKEN
#else
#define JOBS 0 #define JOBS ENABLE_ASH_JOB_CONTROL
#endif
#if DEBUG #if DEBUG
#ifndef _GNU_SOURCE #ifndef _GNU_SOURCE
#define _GNU_SOURCE #define _GNU_SOURCE
#endif #endif
#endif #endif
#include "busybox.h" /* for applet_names */ #include "busybox.h" /* for applet_names */
#include <paths.h> #include <paths.h>
#include <setjmp.h> #include <setjmp.h>
@ -5501,15 +5500,19 @@ expari(int quotes)
#endif #endif
/* argstr needs it */ /* argstr needs it */
static char *evalvar(char *p, int flag); static char *evalvar(char *p, int flag, struct strlist *var_str_list);
/* /*
* Perform variable and command substitution. If EXP_FULL is set, output CTLESC * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
* characters to allow for further processing. Otherwise treat * characters to allow for further processing. Otherwise treat
* $@ like $* since no splitting will be performed. * $@ like $* since no splitting will be performed.
*
* var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
* over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
* for correct expansion of "B=$A" word.
*/ */
static void static void
argstr(char *p, int flag) argstr(char *p, int flag, struct strlist *var_str_list)
{ {
static const char spclchars[] ALIGN1 = { static const char spclchars[] ALIGN1 = {
'=', '=',
@ -5611,7 +5614,7 @@ argstr(char *p, int flag)
p[5] == CTLQUOTEMARK p[5] == CTLQUOTEMARK
)) ))
) { ) {
p = evalvar(p + 1, flag) + 1; p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
goto start; goto start;
} }
inquotes = !inquotes; inquotes = !inquotes;
@ -5627,7 +5630,7 @@ argstr(char *p, int flag)
length++; length++;
goto addquote; goto addquote;
case CTLVAR: case CTLVAR:
p = evalvar(p, flag); p = evalvar(p, flag, var_str_list);
goto start; goto start;
case CTLBACKQ: case CTLBACKQ:
c = 0; c = 0;
@ -5731,7 +5734,8 @@ varunset(const char *end, const char *var, const char *umsg, int varflags)
} }
static const char * static const char *
subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes) subevalvar(char *p, char *str, int strloc, int subtype,
int startloc, int varflags, int quotes, struct strlist *var_str_list)
{ {
char *startp; char *startp;
char *loc; char *loc;
@ -5743,7 +5747,8 @@ subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varfla
char *(*scan)(char *, char *, char *, char *, int , int); char *(*scan)(char *, char *, char *, char *, int , int);
herefd = -1; herefd = -1;
argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0); argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
var_str_list);
STPUTC('\0', expdest); STPUTC('\0', expdest);
herefd = saveherefd; herefd = saveherefd;
argbackq = saveargbackq; argbackq = saveargbackq;
@ -5802,7 +5807,7 @@ subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varfla
* Add the value of a specialized variable to the stack string. * Add the value of a specialized variable to the stack string.
*/ */
static ssize_t static ssize_t
varvalue(char *name, int varflags, int flags) varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
{ {
int num; int num;
char *p; char *p;
@ -5899,6 +5904,30 @@ varvalue(char *name, int varflags, int flags)
p = num ? shellparam.p[num - 1] : arg0; p = num ? shellparam.p[num - 1] : arg0;
goto value; goto value;
default: default:
/* NB: name has form "VAR=..." */
/* "A=a B=$A" case: var_str_list is a list of "A=a" strings
* which should be considered before we check variables. */
if (var_str_list) {
unsigned name_len = (strchrnul(name, '=') - name) + 1;
p = NULL;
do {
char *str = var_str_list->text;
char *eq = strchr(str, '=');
if (!eq) /* stop at first non-assignment */
break;
eq++;
if (name_len == (eq - str)
&& strncmp(str, name, name_len) == 0) {
p = eq;
/* goto value; - WRONG! */
/* think "A=1 A=2 B=$A" */
}
var_str_list = var_str_list->next;
} while (var_str_list);
if (p)
goto value;
}
p = lookupvar(name); p = lookupvar(name);
value: value:
if (!p) if (!p)
@ -5920,20 +5949,17 @@ varvalue(char *name, int varflags, int flags)
* input string. * input string.
*/ */
static char * static char *
evalvar(char *p, int flag) evalvar(char *p, int flag, struct strlist *var_str_list)
{ {
int subtype; char varflags;
int varflags; char subtype;
char quoted;
char easy;
char *var; char *var;
int patloc; int patloc;
int c;
int startloc; int startloc;
ssize_t varlen; ssize_t varlen;
int easy;
int quotes;
int quoted;
quotes = flag & (EXP_FULL | EXP_CASE);
varflags = *p++; varflags = *p++;
subtype = varflags & VSTYPE; subtype = varflags & VSTYPE;
quoted = varflags & VSQUOTE; quoted = varflags & VSQUOTE;
@ -5943,7 +5969,7 @@ evalvar(char *p, int flag)
p = strchr(p, '=') + 1; p = strchr(p, '=') + 1;
again: again:
varlen = varvalue(var, varflags, flag); varlen = varvalue(var, varflags, flag, var_str_list);
if (varflags & VSNUL) if (varflags & VSNUL)
varlen--; varlen--;
@ -5957,7 +5983,8 @@ evalvar(char *p, int flag)
if (varlen < 0) { if (varlen < 0) {
argstr( argstr(
p, flag | EXP_TILDE | p, flag | EXP_TILDE |
(quoted ? EXP_QWORD : EXP_WORD) (quoted ? EXP_QWORD : EXP_WORD),
var_str_list
); );
goto end; goto end;
} }
@ -5968,7 +5995,11 @@ evalvar(char *p, int flag)
if (subtype == VSASSIGN || subtype == VSQUESTION) { if (subtype == VSASSIGN || subtype == VSQUESTION) {
if (varlen < 0) { if (varlen < 0) {
if (subevalvar(p, var, 0, subtype, startloc, varflags, 0)) { if (subevalvar(p, var, /* strloc: */ 0,
subtype, startloc, varflags,
/* quotes: */ 0,
var_str_list)
) {
varflags &= ~VSNUL; varflags &= ~VSNUL;
/* /*
* Remove any recorded regions beyond * Remove any recorded regions beyond
@ -5993,10 +6024,8 @@ evalvar(char *p, int flag)
} }
if (subtype == VSNORMAL) { if (subtype == VSNORMAL) {
if (!easy) if (easy)
goto end; goto record;
record:
recordregion(startloc, expdest - (char *)stackblock(), quoted);
goto end; goto end;
} }
@ -6019,8 +6048,11 @@ evalvar(char *p, int flag)
*/ */
STPUTC('\0', expdest); STPUTC('\0', expdest);
patloc = expdest - (char *)stackblock(); patloc = expdest - (char *)stackblock();
if (subevalvar(p, NULL, patloc, subtype, if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
startloc, varflags, quotes) == 0) { startloc, varflags,
/* quotes: */ flag & (EXP_FULL | EXP_CASE),
var_str_list)
) {
int amount = expdest - ( int amount = expdest - (
(char *)stackblock() + patloc - 1 (char *)stackblock() + patloc - 1
); );
@ -6028,14 +6060,15 @@ evalvar(char *p, int flag)
} }
/* Remove any recorded regions beyond start of variable */ /* Remove any recorded regions beyond start of variable */
removerecordregions(startloc); removerecordregions(startloc);
goto record; record:
recordregion(startloc, expdest - (char *)stackblock(), quoted);
} }
end: end:
if (subtype != VSNORMAL) { /* skip to end of alternative */ if (subtype != VSNORMAL) { /* skip to end of alternative */
int nesting = 1; int nesting = 1;
for (;;) { for (;;) {
c = *p++; char c = *p++;
if (c == CTLESC) if (c == CTLESC)
p++; p++;
else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
@ -6420,7 +6453,8 @@ expandarg(union node *arg, struct arglist *arglist, int flag)
STARTSTACKSTR(expdest); STARTSTACKSTR(expdest);
ifsfirst.next = NULL; ifsfirst.next = NULL;
ifslastp = NULL; ifslastp = NULL;
argstr(arg->narg.text, flag); argstr(arg->narg.text, flag,
/* var_str_list: */ arglist ? arglist->list : NULL);
p = _STPUTC('\0', expdest); p = _STPUTC('\0', expdest);
expdest = p - 1; expdest = p - 1;
if (arglist == NULL) { if (arglist == NULL) {
@ -6486,7 +6520,8 @@ casematch(union node *pattern, char *val)
argbackq = pattern->narg.backquote; argbackq = pattern->narg.backquote;
STARTSTACKSTR(expdest); STARTSTACKSTR(expdest);
ifslastp = NULL; ifslastp = NULL;
argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
/* var_str_list: */ NULL);
STACKSTRNUL(expdest); STACKSTRNUL(expdest);
result = patmatch(stackblock(), val); result = patmatch(stackblock(), val);
popstackmark(&smark); popstackmark(&smark);
@ -8249,8 +8284,8 @@ bltincmd(int argc, char **argv)
static void static void
evalcommand(union node *cmd, int flags) evalcommand(union node *cmd, int flags)
{ {
static const struct builtincmd bltin = { static const struct builtincmd null_bltin = {
"\0\0", bltincmd "\0\0", bltincmd /* why three NULs? */
}; };
struct stackmark smark; struct stackmark smark;
union node *argp; union node *argp;
@ -8276,7 +8311,7 @@ evalcommand(union node *cmd, int flags)
back_exitstatus = 0; back_exitstatus = 0;
cmdentry.cmdtype = CMDBUILTIN; cmdentry.cmdtype = CMDBUILTIN;
cmdentry.u.cmd = &bltin; cmdentry.u.cmd = &null_bltin;
varlist.lastp = &varlist.list; varlist.lastp = &varlist.list;
*varlist.lastp = NULL; *varlist.lastp = NULL;
arglist.lastp = &arglist.list; arglist.lastp = &arglist.list;
@ -8352,7 +8387,7 @@ evalcommand(union node *cmd, int flags)
} }
sp = arglist.list; sp = arglist.list;
} }
full_write(preverrout_fd, "\n", 1); safe_write(preverrout_fd, "\n", 1);
} }
cmd_is_exec = 0; cmd_is_exec = 0;

View File

@ -0,0 +1,6 @@
a=a A=a
a=a A=a
a= A=
a= A=
a=a A=a
a=a A=a

View File

@ -0,0 +1,14 @@
# check that first assignment has proper effect on second one
(
a=a A=$a
echo a=$a A=$A
)
(a=a A=$a; echo a=$a A=$A)
(a=a A=$a echo a=$a A=$A)
(a=a A=$a /bin/echo a=$a A=$A)
f() { echo a=$a A=$A; }
(a=a A=$a f)
(a=a A=$a; f)

View File

@ -0,0 +1 @@
bus/usb/1/2

View File

@ -0,0 +1 @@
X=usbdev1.2 X=${X#usbdev} B=${X%%.*} D=${X#*.}; echo bus/usb/$B/$D

View File

@ -17,6 +17,7 @@ export THIS_SH
do_test() do_test()
{ {
test -d "$1" || return 0 test -d "$1" || return 0
echo do_test "$1"
( (
cd "$1" || { echo "cannot cd $1!"; exit 1; } cd "$1" || { echo "cannot cd $1!"; exit 1; }
for x in run-*; do for x in run-*; do
@ -53,7 +54,6 @@ if [ $# -lt 1 ]; then
modules=`ls -d ash-*` modules=`ls -d ash-*`
for module in $modules; do for module in $modules; do
echo do_test $module
do_test $module do_test $module
done done
else else