ash: handle "A=1 A=2 B=$A; echo $B". closes bug 947.
diff --git a/shell/ash.c b/shell/ash.c
index 10eb90d..4f9caa4 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 <paths.h>
#include <setjmp.h>
@@ -5501,15 +5500,19 @@
#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 @@
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 @@
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 @@
}
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 @@
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 @@
* 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 @@
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 @@
* 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 @@
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 @@
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 @@
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 @@
}
if (subtype == VSNORMAL) {
- if (!easy)
- goto end;
- record:
- recordregion(startloc, expdest - (char *)stackblock(), quoted);
+ if (easy)
+ goto record;
goto end;
}
@@ -6019,8 +6048,11 @@
*/
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 @@
}
/* 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 @@
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 @@
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 @@
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 @@
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 @@
}
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 0000000..2a01291
--- /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 0000000..802e489
--- /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 0000000..8fed138
--- /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 0000000..07feaeb
--- /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 416900a..e023338 100755
--- a/shell/ash_test/run-all
+++ b/shell/ash_test/run-all
@@ -17,6 +17,7 @@
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 @@
modules=`ls -d ash-*`
for module in $modules; do
- echo do_test $module
do_test $module
done
else