ash: handle "A=1 A=2 B=$A; echo $B". closes bug 947.
This commit is contained in:
parent
977bc6a137
commit
0e6f661e23
111
shell/ash.c
111
shell/ash.c
@ -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;
|
||||||
|
6
shell/ash_test/ash-vars/var1.right
Normal file
6
shell/ash_test/ash-vars/var1.right
Normal 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
|
14
shell/ash_test/ash-vars/var1.tests
Executable file
14
shell/ash_test/ash-vars/var1.tests
Executable 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)
|
1
shell/ash_test/ash-vars/var2.right
Normal file
1
shell/ash_test/ash-vars/var2.right
Normal file
@ -0,0 +1 @@
|
|||||||
|
bus/usb/1/2
|
1
shell/ash_test/ash-vars/var2.tests
Executable file
1
shell/ash_test/ash-vars/var2.tests
Executable file
@ -0,0 +1 @@
|
|||||||
|
X=usbdev1.2 X=${X#usbdev} B=${X%%.*} D=${X#*.}; echo bus/usb/$B/$D
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user