diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-02-15 15:02:15 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-02-15 15:02:15 +0000 |
commit | 0e6f661e23d358cca104c24f8438d0ec64df32f1 (patch) | |
tree | c591108ea6f9617192c4a082e603561af14aadd9 | |
parent | 977bc6a1374ed468ec96e2202757be0c8b3592ed (diff) | |
download | busybox-w32-0e6f661e23d358cca104c24f8438d0ec64df32f1.tar.gz busybox-w32-0e6f661e23d358cca104c24f8438d0ec64df32f1.tar.bz2 busybox-w32-0e6f661e23d358cca104c24f8438d0ec64df32f1.zip |
ash: handle "A=1 A=2 B=$A; echo $B". closes bug 947.
-rw-r--r-- | shell/ash.c | 111 | ||||
-rw-r--r-- | shell/ash_test/ash-vars/var1.right | 6 | ||||
-rwxr-xr-x | shell/ash_test/ash-vars/var1.tests | 14 | ||||
-rw-r--r-- | shell/ash_test/ash-vars/var2.right | 1 | ||||
-rwxr-xr-x | shell/ash_test/ash-vars/var2.tests | 1 | ||||
-rwxr-xr-x | shell/ash_test/run-all | 2 |
6 files changed, 96 insertions, 39 deletions
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 @@ | |||
42 | * a quit signal will generate a core dump. | 42 | * a quit signal will generate a core dump. |
43 | */ | 43 | */ |
44 | #define DEBUG 0 | 44 | #define DEBUG 0 |
45 | #define IFS_BROKEN | ||
46 | #define PROFILE 0 | 45 | #define PROFILE 0 |
47 | #if ENABLE_ASH_JOB_CONTROL | 46 | |
48 | #define JOBS 1 | 47 | #define IFS_BROKEN |
49 | #else | 48 | |
50 | #define JOBS 0 | 49 | #define JOBS ENABLE_ASH_JOB_CONTROL |
51 | #endif | ||
52 | 50 | ||
53 | #if DEBUG | 51 | #if DEBUG |
54 | #ifndef _GNU_SOURCE | 52 | #ifndef _GNU_SOURCE |
55 | #define _GNU_SOURCE | 53 | #define _GNU_SOURCE |
56 | #endif | 54 | #endif |
57 | #endif | 55 | #endif |
56 | |||
58 | #include "busybox.h" /* for applet_names */ | 57 | #include "busybox.h" /* for applet_names */ |
59 | #include <paths.h> | 58 | #include <paths.h> |
60 | #include <setjmp.h> | 59 | #include <setjmp.h> |
@@ -5501,15 +5500,19 @@ expari(int quotes) | |||
5501 | #endif | 5500 | #endif |
5502 | 5501 | ||
5503 | /* argstr needs it */ | 5502 | /* argstr needs it */ |
5504 | static char *evalvar(char *p, int flag); | 5503 | static char *evalvar(char *p, int flag, struct strlist *var_str_list); |
5505 | 5504 | ||
5506 | /* | 5505 | /* |
5507 | * Perform variable and command substitution. If EXP_FULL is set, output CTLESC | 5506 | * Perform variable and command substitution. If EXP_FULL is set, output CTLESC |
5508 | * characters to allow for further processing. Otherwise treat | 5507 | * characters to allow for further processing. Otherwise treat |
5509 | * $@ like $* since no splitting will be performed. | 5508 | * $@ like $* since no splitting will be performed. |
5509 | * | ||
5510 | * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence | ||
5511 | * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it | ||
5512 | * for correct expansion of "B=$A" word. | ||
5510 | */ | 5513 | */ |
5511 | static void | 5514 | static void |
5512 | argstr(char *p, int flag) | 5515 | argstr(char *p, int flag, struct strlist *var_str_list) |
5513 | { | 5516 | { |
5514 | static const char spclchars[] ALIGN1 = { | 5517 | static const char spclchars[] ALIGN1 = { |
5515 | '=', | 5518 | '=', |
@@ -5611,7 +5614,7 @@ argstr(char *p, int flag) | |||
5611 | p[5] == CTLQUOTEMARK | 5614 | p[5] == CTLQUOTEMARK |
5612 | )) | 5615 | )) |
5613 | ) { | 5616 | ) { |
5614 | p = evalvar(p + 1, flag) + 1; | 5617 | p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1; |
5615 | goto start; | 5618 | goto start; |
5616 | } | 5619 | } |
5617 | inquotes = !inquotes; | 5620 | inquotes = !inquotes; |
@@ -5627,7 +5630,7 @@ argstr(char *p, int flag) | |||
5627 | length++; | 5630 | length++; |
5628 | goto addquote; | 5631 | goto addquote; |
5629 | case CTLVAR: | 5632 | case CTLVAR: |
5630 | p = evalvar(p, flag); | 5633 | p = evalvar(p, flag, var_str_list); |
5631 | goto start; | 5634 | goto start; |
5632 | case CTLBACKQ: | 5635 | case CTLBACKQ: |
5633 | c = 0; | 5636 | c = 0; |
@@ -5731,7 +5734,8 @@ varunset(const char *end, const char *var, const char *umsg, int varflags) | |||
5731 | } | 5734 | } |
5732 | 5735 | ||
5733 | static const char * | 5736 | static const char * |
5734 | subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes) | 5737 | subevalvar(char *p, char *str, int strloc, int subtype, |
5738 | int startloc, int varflags, int quotes, struct strlist *var_str_list) | ||
5735 | { | 5739 | { |
5736 | char *startp; | 5740 | char *startp; |
5737 | char *loc; | 5741 | char *loc; |
@@ -5743,7 +5747,8 @@ subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varfla | |||
5743 | char *(*scan)(char *, char *, char *, char *, int , int); | 5747 | char *(*scan)(char *, char *, char *, char *, int , int); |
5744 | 5748 | ||
5745 | herefd = -1; | 5749 | herefd = -1; |
5746 | argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0); | 5750 | argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0, |
5751 | var_str_list); | ||
5747 | STPUTC('\0', expdest); | 5752 | STPUTC('\0', expdest); |
5748 | herefd = saveherefd; | 5753 | herefd = saveherefd; |
5749 | argbackq = saveargbackq; | 5754 | argbackq = saveargbackq; |
@@ -5802,7 +5807,7 @@ subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varfla | |||
5802 | * Add the value of a specialized variable to the stack string. | 5807 | * Add the value of a specialized variable to the stack string. |
5803 | */ | 5808 | */ |
5804 | static ssize_t | 5809 | static ssize_t |
5805 | varvalue(char *name, int varflags, int flags) | 5810 | varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) |
5806 | { | 5811 | { |
5807 | int num; | 5812 | int num; |
5808 | char *p; | 5813 | char *p; |
@@ -5899,6 +5904,30 @@ varvalue(char *name, int varflags, int flags) | |||
5899 | p = num ? shellparam.p[num - 1] : arg0; | 5904 | p = num ? shellparam.p[num - 1] : arg0; |
5900 | goto value; | 5905 | goto value; |
5901 | default: | 5906 | default: |
5907 | /* NB: name has form "VAR=..." */ | ||
5908 | |||
5909 | /* "A=a B=$A" case: var_str_list is a list of "A=a" strings | ||
5910 | * which should be considered before we check variables. */ | ||
5911 | if (var_str_list) { | ||
5912 | unsigned name_len = (strchrnul(name, '=') - name) + 1; | ||
5913 | p = NULL; | ||
5914 | do { | ||
5915 | char *str = var_str_list->text; | ||
5916 | char *eq = strchr(str, '='); | ||
5917 | if (!eq) /* stop at first non-assignment */ | ||
5918 | break; | ||
5919 | eq++; | ||
5920 | if (name_len == (eq - str) | ||
5921 | && strncmp(str, name, name_len) == 0) { | ||
5922 | p = eq; | ||
5923 | /* goto value; - WRONG! */ | ||
5924 | /* think "A=1 A=2 B=$A" */ | ||
5925 | } | ||
5926 | var_str_list = var_str_list->next; | ||
5927 | } while (var_str_list); | ||
5928 | if (p) | ||
5929 | goto value; | ||
5930 | } | ||
5902 | p = lookupvar(name); | 5931 | p = lookupvar(name); |
5903 | value: | 5932 | value: |
5904 | if (!p) | 5933 | if (!p) |
@@ -5920,20 +5949,17 @@ varvalue(char *name, int varflags, int flags) | |||
5920 | * input string. | 5949 | * input string. |
5921 | */ | 5950 | */ |
5922 | static char * | 5951 | static char * |
5923 | evalvar(char *p, int flag) | 5952 | evalvar(char *p, int flag, struct strlist *var_str_list) |
5924 | { | 5953 | { |
5925 | int subtype; | 5954 | char varflags; |
5926 | int varflags; | 5955 | char subtype; |
5956 | char quoted; | ||
5957 | char easy; | ||
5927 | char *var; | 5958 | char *var; |
5928 | int patloc; | 5959 | int patloc; |
5929 | int c; | ||
5930 | int startloc; | 5960 | int startloc; |
5931 | ssize_t varlen; | 5961 | ssize_t varlen; |
5932 | int easy; | ||
5933 | int quotes; | ||
5934 | int quoted; | ||
5935 | 5962 | ||
5936 | quotes = flag & (EXP_FULL | EXP_CASE); | ||
5937 | varflags = *p++; | 5963 | varflags = *p++; |
5938 | subtype = varflags & VSTYPE; | 5964 | subtype = varflags & VSTYPE; |
5939 | quoted = varflags & VSQUOTE; | 5965 | quoted = varflags & VSQUOTE; |
@@ -5943,7 +5969,7 @@ evalvar(char *p, int flag) | |||
5943 | p = strchr(p, '=') + 1; | 5969 | p = strchr(p, '=') + 1; |
5944 | 5970 | ||
5945 | again: | 5971 | again: |
5946 | varlen = varvalue(var, varflags, flag); | 5972 | varlen = varvalue(var, varflags, flag, var_str_list); |
5947 | if (varflags & VSNUL) | 5973 | if (varflags & VSNUL) |
5948 | varlen--; | 5974 | varlen--; |
5949 | 5975 | ||
@@ -5957,7 +5983,8 @@ evalvar(char *p, int flag) | |||
5957 | if (varlen < 0) { | 5983 | if (varlen < 0) { |
5958 | argstr( | 5984 | argstr( |
5959 | p, flag | EXP_TILDE | | 5985 | p, flag | EXP_TILDE | |
5960 | (quoted ? EXP_QWORD : EXP_WORD) | 5986 | (quoted ? EXP_QWORD : EXP_WORD), |
5987 | var_str_list | ||
5961 | ); | 5988 | ); |
5962 | goto end; | 5989 | goto end; |
5963 | } | 5990 | } |
@@ -5968,7 +5995,11 @@ evalvar(char *p, int flag) | |||
5968 | 5995 | ||
5969 | if (subtype == VSASSIGN || subtype == VSQUESTION) { | 5996 | if (subtype == VSASSIGN || subtype == VSQUESTION) { |
5970 | if (varlen < 0) { | 5997 | if (varlen < 0) { |
5971 | if (subevalvar(p, var, 0, subtype, startloc, varflags, 0)) { | 5998 | if (subevalvar(p, var, /* strloc: */ 0, |
5999 | subtype, startloc, varflags, | ||
6000 | /* quotes: */ 0, | ||
6001 | var_str_list) | ||
6002 | ) { | ||
5972 | varflags &= ~VSNUL; | 6003 | varflags &= ~VSNUL; |
5973 | /* | 6004 | /* |
5974 | * Remove any recorded regions beyond | 6005 | * Remove any recorded regions beyond |
@@ -5993,10 +6024,8 @@ evalvar(char *p, int flag) | |||
5993 | } | 6024 | } |
5994 | 6025 | ||
5995 | if (subtype == VSNORMAL) { | 6026 | if (subtype == VSNORMAL) { |
5996 | if (!easy) | 6027 | if (easy) |
5997 | goto end; | 6028 | goto record; |
5998 | record: | ||
5999 | recordregion(startloc, expdest - (char *)stackblock(), quoted); | ||
6000 | goto end; | 6029 | goto end; |
6001 | } | 6030 | } |
6002 | 6031 | ||
@@ -6019,8 +6048,11 @@ evalvar(char *p, int flag) | |||
6019 | */ | 6048 | */ |
6020 | STPUTC('\0', expdest); | 6049 | STPUTC('\0', expdest); |
6021 | patloc = expdest - (char *)stackblock(); | 6050 | patloc = expdest - (char *)stackblock(); |
6022 | if (subevalvar(p, NULL, patloc, subtype, | 6051 | if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype, |
6023 | startloc, varflags, quotes) == 0) { | 6052 | startloc, varflags, |
6053 | /* quotes: */ flag & (EXP_FULL | EXP_CASE), | ||
6054 | var_str_list) | ||
6055 | ) { | ||
6024 | int amount = expdest - ( | 6056 | int amount = expdest - ( |
6025 | (char *)stackblock() + patloc - 1 | 6057 | (char *)stackblock() + patloc - 1 |
6026 | ); | 6058 | ); |
@@ -6028,14 +6060,15 @@ evalvar(char *p, int flag) | |||
6028 | } | 6060 | } |
6029 | /* Remove any recorded regions beyond start of variable */ | 6061 | /* Remove any recorded regions beyond start of variable */ |
6030 | removerecordregions(startloc); | 6062 | removerecordregions(startloc); |
6031 | goto record; | 6063 | record: |
6064 | recordregion(startloc, expdest - (char *)stackblock(), quoted); | ||
6032 | } | 6065 | } |
6033 | 6066 | ||
6034 | end: | 6067 | end: |
6035 | if (subtype != VSNORMAL) { /* skip to end of alternative */ | 6068 | if (subtype != VSNORMAL) { /* skip to end of alternative */ |
6036 | int nesting = 1; | 6069 | int nesting = 1; |
6037 | for (;;) { | 6070 | for (;;) { |
6038 | c = *p++; | 6071 | char c = *p++; |
6039 | if (c == CTLESC) | 6072 | if (c == CTLESC) |
6040 | p++; | 6073 | p++; |
6041 | else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { | 6074 | else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { |
@@ -6420,7 +6453,8 @@ expandarg(union node *arg, struct arglist *arglist, int flag) | |||
6420 | STARTSTACKSTR(expdest); | 6453 | STARTSTACKSTR(expdest); |
6421 | ifsfirst.next = NULL; | 6454 | ifsfirst.next = NULL; |
6422 | ifslastp = NULL; | 6455 | ifslastp = NULL; |
6423 | argstr(arg->narg.text, flag); | 6456 | argstr(arg->narg.text, flag, |
6457 | /* var_str_list: */ arglist ? arglist->list : NULL); | ||
6424 | p = _STPUTC('\0', expdest); | 6458 | p = _STPUTC('\0', expdest); |
6425 | expdest = p - 1; | 6459 | expdest = p - 1; |
6426 | if (arglist == NULL) { | 6460 | if (arglist == NULL) { |
@@ -6486,7 +6520,8 @@ casematch(union node *pattern, char *val) | |||
6486 | argbackq = pattern->narg.backquote; | 6520 | argbackq = pattern->narg.backquote; |
6487 | STARTSTACKSTR(expdest); | 6521 | STARTSTACKSTR(expdest); |
6488 | ifslastp = NULL; | 6522 | ifslastp = NULL; |
6489 | argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); | 6523 | argstr(pattern->narg.text, EXP_TILDE | EXP_CASE, |
6524 | /* var_str_list: */ NULL); | ||
6490 | STACKSTRNUL(expdest); | 6525 | STACKSTRNUL(expdest); |
6491 | result = patmatch(stackblock(), val); | 6526 | result = patmatch(stackblock(), val); |
6492 | popstackmark(&smark); | 6527 | popstackmark(&smark); |
@@ -8249,8 +8284,8 @@ bltincmd(int argc, char **argv) | |||
8249 | static void | 8284 | static void |
8250 | evalcommand(union node *cmd, int flags) | 8285 | evalcommand(union node *cmd, int flags) |
8251 | { | 8286 | { |
8252 | static const struct builtincmd bltin = { | 8287 | static const struct builtincmd null_bltin = { |
8253 | "\0\0", bltincmd | 8288 | "\0\0", bltincmd /* why three NULs? */ |
8254 | }; | 8289 | }; |
8255 | struct stackmark smark; | 8290 | struct stackmark smark; |
8256 | union node *argp; | 8291 | union node *argp; |
@@ -8276,7 +8311,7 @@ evalcommand(union node *cmd, int flags) | |||
8276 | back_exitstatus = 0; | 8311 | back_exitstatus = 0; |
8277 | 8312 | ||
8278 | cmdentry.cmdtype = CMDBUILTIN; | 8313 | cmdentry.cmdtype = CMDBUILTIN; |
8279 | cmdentry.u.cmd = &bltin; | 8314 | cmdentry.u.cmd = &null_bltin; |
8280 | varlist.lastp = &varlist.list; | 8315 | varlist.lastp = &varlist.list; |
8281 | *varlist.lastp = NULL; | 8316 | *varlist.lastp = NULL; |
8282 | arglist.lastp = &arglist.list; | 8317 | arglist.lastp = &arglist.list; |
@@ -8352,7 +8387,7 @@ evalcommand(union node *cmd, int flags) | |||
8352 | } | 8387 | } |
8353 | sp = arglist.list; | 8388 | sp = arglist.list; |
8354 | } | 8389 | } |
8355 | full_write(preverrout_fd, "\n", 1); | 8390 | safe_write(preverrout_fd, "\n", 1); |
8356 | } | 8391 | } |
8357 | 8392 | ||
8358 | cmd_is_exec = 0; | 8393 | 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 @@ | |||
1 | a=a A=a | ||
2 | a=a A=a | ||
3 | a= A= | ||
4 | a= A= | ||
5 | a=a A=a | ||
6 | 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 @@ | |||
1 | # check that first assignment has proper effect on second one | ||
2 | |||
3 | ( | ||
4 | a=a A=$a | ||
5 | echo a=$a A=$A | ||
6 | ) | ||
7 | (a=a A=$a; echo a=$a A=$A) | ||
8 | (a=a A=$a echo a=$a A=$A) | ||
9 | (a=a A=$a /bin/echo a=$a A=$A) | ||
10 | |||
11 | f() { echo a=$a A=$A; } | ||
12 | |||
13 | (a=a A=$a f) | ||
14 | (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 | |||
17 | do_test() | 17 | do_test() |
18 | { | 18 | { |
19 | test -d "$1" || return 0 | 19 | test -d "$1" || return 0 |
20 | echo do_test "$1" | ||
20 | ( | 21 | ( |
21 | cd "$1" || { echo "cannot cd $1!"; exit 1; } | 22 | cd "$1" || { echo "cannot cd $1!"; exit 1; } |
22 | for x in run-*; do | 23 | for x in run-*; do |
@@ -53,7 +54,6 @@ if [ $# -lt 1 ]; then | |||
53 | modules=`ls -d ash-*` | 54 | modules=`ls -d ash-*` |
54 | 55 | ||
55 | for module in $modules; do | 56 | for module in $modules; do |
56 | echo do_test $module | ||
57 | do_test $module | 57 | do_test $module |
58 | done | 58 | done |
59 | else | 59 | else |