From 0e6f661e23d358cca104c24f8438d0ec64df32f1 Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Fri, 15 Feb 2008 15:02:15 +0000 Subject: [PATCH] ash: handle "A=1 A=2 B=$A; echo $B". closes bug 947. --- shell/ash.c | 111 +++++++++++++++++++---------- shell/ash_test/ash-vars/var1.right | 6 ++ shell/ash_test/ash-vars/var1.tests | 14 ++++ shell/ash_test/ash-vars/var2.right | 1 + shell/ash_test/ash-vars/var2.tests | 1 + shell/ash_test/run-all | 2 +- 6 files changed, 96 insertions(+), 39 deletions(-) create mode 100644 shell/ash_test/ash-vars/var1.right create mode 100755 shell/ash_test/ash-vars/var1.tests create mode 100644 shell/ash_test/ash-vars/var2.right create mode 100755 shell/ash_test/ash-vars/var2.tests diff --git a/shell/ash.c b/shell/ash.c index 10eb90d30..4f9caa434 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -42,19 +42,18 @@ * a quit signal will generate a core dump. */ #define DEBUG 0 -#define IFS_BROKEN #define PROFILE 0 -#if ENABLE_ASH_JOB_CONTROL -#define JOBS 1 -#else -#define JOBS 0 -#endif + +#define IFS_BROKEN + +#define JOBS ENABLE_ASH_JOB_CONTROL #if DEBUG #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #endif + #include "busybox.h" /* for applet_names */ #include #include @@ -5501,15 +5500,19 @@ expari(int quotes) #endif /* 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 * characters to allow for further processing. Otherwise treat * $@ 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 -argstr(char *p, int flag) +argstr(char *p, int flag, struct strlist *var_str_list) { static const char spclchars[] ALIGN1 = { '=', @@ -5611,7 +5614,7 @@ argstr(char *p, int flag) p[5] == CTLQUOTEMARK )) ) { - p = evalvar(p + 1, flag) + 1; + p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1; goto start; } inquotes = !inquotes; @@ -5627,7 +5630,7 @@ argstr(char *p, int flag) length++; goto addquote; case CTLVAR: - p = evalvar(p, flag); + p = evalvar(p, flag, var_str_list); goto start; case CTLBACKQ: c = 0; @@ -5731,7 +5734,8 @@ varunset(const char *end, const char *var, const char *umsg, int varflags) } 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 *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); 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); herefd = saveherefd; 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. */ static ssize_t -varvalue(char *name, int varflags, int flags) +varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) { int num; char *p; @@ -5899,6 +5904,30 @@ varvalue(char *name, int varflags, int flags) p = num ? shellparam.p[num - 1] : arg0; goto value; 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); value: if (!p) @@ -5920,20 +5949,17 @@ varvalue(char *name, int varflags, int flags) * input string. */ static char * -evalvar(char *p, int flag) +evalvar(char *p, int flag, struct strlist *var_str_list) { - int subtype; - int varflags; + char varflags; + char subtype; + char quoted; + char easy; char *var; int patloc; - int c; int startloc; ssize_t varlen; - int easy; - int quotes; - int quoted; - quotes = flag & (EXP_FULL | EXP_CASE); varflags = *p++; subtype = varflags & VSTYPE; quoted = varflags & VSQUOTE; @@ -5943,7 +5969,7 @@ evalvar(char *p, int flag) p = strchr(p, '=') + 1; again: - varlen = varvalue(var, varflags, flag); + varlen = varvalue(var, varflags, flag, var_str_list); if (varflags & VSNUL) varlen--; @@ -5957,7 +5983,8 @@ evalvar(char *p, int flag) if (varlen < 0) { argstr( p, flag | EXP_TILDE | - (quoted ? EXP_QWORD : EXP_WORD) + (quoted ? EXP_QWORD : EXP_WORD), + var_str_list ); goto end; } @@ -5968,7 +5995,11 @@ evalvar(char *p, int flag) if (subtype == VSASSIGN || subtype == VSQUESTION) { 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; /* * Remove any recorded regions beyond @@ -5993,10 +6024,8 @@ evalvar(char *p, int flag) } if (subtype == VSNORMAL) { - if (!easy) - goto end; - record: - recordregion(startloc, expdest - (char *)stackblock(), quoted); + if (easy) + goto record; goto end; } @@ -6019,8 +6048,11 @@ evalvar(char *p, int flag) */ STPUTC('\0', expdest); patloc = expdest - (char *)stackblock(); - if (subevalvar(p, NULL, patloc, subtype, - startloc, varflags, quotes) == 0) { + if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype, + startloc, varflags, + /* quotes: */ flag & (EXP_FULL | EXP_CASE), + var_str_list) + ) { int amount = expdest - ( (char *)stackblock() + patloc - 1 ); @@ -6028,14 +6060,15 @@ evalvar(char *p, int flag) } /* Remove any recorded regions beyond start of variable */ removerecordregions(startloc); - goto record; + record: + recordregion(startloc, expdest - (char *)stackblock(), quoted); } end: if (subtype != VSNORMAL) { /* skip to end of alternative */ int nesting = 1; for (;;) { - c = *p++; + char c = *p++; if (c == CTLESC) p++; else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { @@ -6420,7 +6453,8 @@ expandarg(union node *arg, struct arglist *arglist, int flag) STARTSTACKSTR(expdest); ifsfirst.next = NULL; ifslastp = NULL; - argstr(arg->narg.text, flag); + argstr(arg->narg.text, flag, + /* var_str_list: */ arglist ? arglist->list : NULL); p = _STPUTC('\0', expdest); expdest = p - 1; if (arglist == NULL) { @@ -6486,7 +6520,8 @@ casematch(union node *pattern, char *val) argbackq = pattern->narg.backquote; STARTSTACKSTR(expdest); ifslastp = NULL; - argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); + argstr(pattern->narg.text, EXP_TILDE | EXP_CASE, + /* var_str_list: */ NULL); STACKSTRNUL(expdest); result = patmatch(stackblock(), val); popstackmark(&smark); @@ -8249,8 +8284,8 @@ bltincmd(int argc, char **argv) static void evalcommand(union node *cmd, int flags) { - static const struct builtincmd bltin = { - "\0\0", bltincmd + static const struct builtincmd null_bltin = { + "\0\0", bltincmd /* why three NULs? */ }; struct stackmark smark; union node *argp; @@ -8276,7 +8311,7 @@ evalcommand(union node *cmd, int flags) back_exitstatus = 0; cmdentry.cmdtype = CMDBUILTIN; - cmdentry.u.cmd = &bltin; + cmdentry.u.cmd = &null_bltin; varlist.lastp = &varlist.list; *varlist.lastp = NULL; arglist.lastp = &arglist.list; @@ -8352,7 +8387,7 @@ evalcommand(union node *cmd, int flags) } sp = arglist.list; } - full_write(preverrout_fd, "\n", 1); + safe_write(preverrout_fd, "\n", 1); } cmd_is_exec = 0; diff --git a/shell/ash_test/ash-vars/var1.right b/shell/ash_test/ash-vars/var1.right new file mode 100644 index 000000000..2a01291bb --- /dev/null +++ b/shell/ash_test/ash-vars/var1.right @@ -0,0 +1,6 @@ +a=a A=a +a=a A=a +a= A= +a= A= +a=a A=a +a=a A=a diff --git a/shell/ash_test/ash-vars/var1.tests b/shell/ash_test/ash-vars/var1.tests new file mode 100755 index 000000000..802e489bd --- /dev/null +++ b/shell/ash_test/ash-vars/var1.tests @@ -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) diff --git a/shell/ash_test/ash-vars/var2.right b/shell/ash_test/ash-vars/var2.right new file mode 100644 index 000000000..8fed138eb --- /dev/null +++ b/shell/ash_test/ash-vars/var2.right @@ -0,0 +1 @@ +bus/usb/1/2 diff --git a/shell/ash_test/ash-vars/var2.tests b/shell/ash_test/ash-vars/var2.tests new file mode 100755 index 000000000..07feaeb8b --- /dev/null +++ b/shell/ash_test/ash-vars/var2.tests @@ -0,0 +1 @@ +X=usbdev1.2 X=${X#usbdev} B=${X%%.*} D=${X#*.}; echo bus/usb/$B/$D diff --git a/shell/ash_test/run-all b/shell/ash_test/run-all index 416900aa2..e02333888 100755 --- a/shell/ash_test/run-all +++ b/shell/ash_test/run-all @@ -17,6 +17,7 @@ export THIS_SH do_test() { test -d "$1" || return 0 + echo do_test "$1" ( cd "$1" || { echo "cannot cd $1!"; exit 1; } for x in run-*; do @@ -53,7 +54,6 @@ if [ $# -lt 1 ]; then modules=`ls -d ash-*` for module in $modules; do - echo do_test $module do_test $module done else