diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2018-08-05 14:29:58 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2018-08-05 14:29:58 +0200 |
commit | 440da97ed79841b55f0b61e4108a336b61642bff (patch) | |
tree | 9cb31a0c1dabd368d8451d92be5506c15a81c8c5 | |
parent | 67dae152f4bf5456e4ea7950f4b55356aa37ec6c (diff) | |
download | busybox-w32-440da97ed79841b55f0b61e4108a336b61642bff.tar.gz busybox-w32-440da97ed79841b55f0b61e4108a336b61642bff.tar.bz2 busybox-w32-440da97ed79841b55f0b61e4108a336b61642bff.zip |
ash: expand: Fix ghost fields with unquoted $@/$*
Upstream commit:
Date: Fri, 23 Mar 2018 18:58:47 +0800
expand: Fix ghost fields with unquoted $@/$*
You're right. The proper fix to this is to ensure that nulonly
is not set in varvalue for $*. It should only be set for $@ when
it's inside double quotes.
In fact there is another bug while we're playing with $@/$*.
When IFS is set to a non-whitespace character such as :, $*
outside quotes won't remove empty fields as it should.
This patch fixes both problems.
Reported-by: Martijn Dekker <martijn@inlv.org>
Suggested-by: Harald van Dijk <harald@gigawatt.nl>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
function old new delta
argstr 1111 1113 +2
evalvar 571 569 -2
varvalue 579 576 -3
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/2 up/down: 2/-5) Total: -3 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | shell/ash.c | 38 | ||||
-rw-r--r-- | shell/ash_test/ash-vars/var_wordsplit_ifs5.right | 1 | ||||
-rwxr-xr-x | shell/ash_test/ash-vars/var_wordsplit_ifs5.tests | 4 | ||||
-rw-r--r-- | shell/hush_test/hush-vars/var_wordsplit_ifs5.right | 1 | ||||
-rwxr-xr-x | shell/hush_test/hush-vars/var_wordsplit_ifs5.tests | 4 |
5 files changed, 36 insertions, 12 deletions
diff --git a/shell/ash.c b/shell/ash.c index 6ef0a7ac2..4641dfd19 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -5902,7 +5902,7 @@ static int substr_atoi(const char *s) | |||
5902 | #define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ | 5902 | #define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ |
5903 | #define EXP_VARTILDE2 0x20 /* expand tildes after colons only */ | 5903 | #define EXP_VARTILDE2 0x20 /* expand tildes after colons only */ |
5904 | #define EXP_WORD 0x40 /* expand word in parameter expansion */ | 5904 | #define EXP_WORD 0x40 /* expand word in parameter expansion */ |
5905 | #define EXP_QUOTED 0x80 /* expand word in double quotes */ | 5905 | #define EXP_QUOTED 0x100 /* expand word in double quotes */ |
5906 | /* | 5906 | /* |
5907 | * rmescape() flags | 5907 | * rmescape() flags |
5908 | */ | 5908 | */ |
@@ -7175,14 +7175,13 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
7175 | * ash -c 'echo ${#1#}' name:'1=#' | 7175 | * ash -c 'echo ${#1#}' name:'1=#' |
7176 | */ | 7176 | */ |
7177 | static NOINLINE ssize_t | 7177 | static NOINLINE ssize_t |
7178 | varvalue(char *name, int varflags, int flags, int *quotedp) | 7178 | varvalue(char *name, int varflags, int flags, int quoted) |
7179 | { | 7179 | { |
7180 | const char *p; | 7180 | const char *p; |
7181 | int num; | 7181 | int num; |
7182 | int i; | 7182 | int i; |
7183 | ssize_t len = 0; | 7183 | ssize_t len = 0; |
7184 | int sep; | 7184 | int sep; |
7185 | int quoted = *quotedp; | ||
7186 | int subtype = varflags & VSTYPE; | 7185 | int subtype = varflags & VSTYPE; |
7187 | int discard = subtype == VSPLUS || subtype == VSLENGTH; | 7186 | int discard = subtype == VSPLUS || subtype == VSLENGTH; |
7188 | int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL; | 7187 | int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL; |
@@ -7230,13 +7229,27 @@ varvalue(char *name, int varflags, int flags, int *quotedp) | |||
7230 | case '*': { | 7229 | case '*': { |
7231 | char **ap; | 7230 | char **ap; |
7232 | char sepc; | 7231 | char sepc; |
7232 | char c; | ||
7233 | 7233 | ||
7234 | if (quoted) | 7234 | /* We will set c to 0 or ~0 depending on whether |
7235 | sep = 0; | 7235 | * we're doing field splitting. We won't do field |
7236 | sep |= ifsset() ? ifsval()[0] : ' '; | 7236 | * splitting if either we're quoted or sep is zero. |
7237 | * | ||
7238 | * Instead of testing (quoted || !sep) the following | ||
7239 | * trick optimises away any branches by using the | ||
7240 | * fact that EXP_QUOTED (which is the only bit that | ||
7241 | * can be set in quoted) is the same as EXP_FULL << | ||
7242 | * CHAR_BIT (which is the only bit that can be set | ||
7243 | * in sep). | ||
7244 | */ | ||
7245 | #if EXP_QUOTED >> CHAR_BIT != EXP_FULL | ||
7246 | #error The following two lines expect EXP_QUOTED == EXP_FULL << CHAR_BIT | ||
7247 | #endif | ||
7248 | c = !((quoted | ~sep) & EXP_QUOTED) - 1; | ||
7249 | sep &= ~quoted; | ||
7250 | sep |= ifsset() ? (unsigned char)(c & ifsval()[0]) : ' '; | ||
7237 | param: | 7251 | param: |
7238 | sepc = sep; | 7252 | sepc = sep; |
7239 | *quotedp = !sepc; | ||
7240 | ap = shellparam.p; | 7253 | ap = shellparam.p; |
7241 | if (!ap) | 7254 | if (!ap) |
7242 | return -1; | 7255 | return -1; |
@@ -7301,7 +7314,6 @@ evalvar(char *p, int flag) | |||
7301 | char varflags; | 7314 | char varflags; |
7302 | char subtype; | 7315 | char subtype; |
7303 | int quoted; | 7316 | int quoted; |
7304 | char easy; | ||
7305 | char *var; | 7317 | char *var; |
7306 | int patloc; | 7318 | int patloc; |
7307 | int startloc; | 7319 | int startloc; |
@@ -7315,12 +7327,11 @@ evalvar(char *p, int flag) | |||
7315 | 7327 | ||
7316 | quoted = flag & EXP_QUOTED; | 7328 | quoted = flag & EXP_QUOTED; |
7317 | var = p; | 7329 | var = p; |
7318 | easy = (!quoted || (*var == '@' && shellparam.nparam)); | ||
7319 | startloc = expdest - (char *)stackblock(); | 7330 | startloc = expdest - (char *)stackblock(); |
7320 | p = strchr(p, '=') + 1; //TODO: use var_end(p)? | 7331 | p = strchr(p, '=') + 1; //TODO: use var_end(p)? |
7321 | 7332 | ||
7322 | again: | 7333 | again: |
7323 | varlen = varvalue(var, varflags, flag, "ed); | 7334 | varlen = varvalue(var, varflags, flag, quoted); |
7324 | if (varflags & VSNUL) | 7335 | if (varflags & VSNUL) |
7325 | varlen--; | 7336 | varlen--; |
7326 | 7337 | ||
@@ -7366,8 +7377,11 @@ evalvar(char *p, int flag) | |||
7366 | 7377 | ||
7367 | if (subtype == VSNORMAL) { | 7378 | if (subtype == VSNORMAL) { |
7368 | record: | 7379 | record: |
7369 | if (!easy) | 7380 | if (quoted) { |
7370 | goto end; | 7381 | quoted = *var == '@' && shellparam.nparam; |
7382 | if (!quoted) | ||
7383 | goto end; | ||
7384 | } | ||
7371 | recordregion(startloc, expdest - (char *)stackblock(), quoted); | 7385 | recordregion(startloc, expdest - (char *)stackblock(), quoted); |
7372 | goto end; | 7386 | goto end; |
7373 | } | 7387 | } |
diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs5.right b/shell/ash_test/ash-vars/var_wordsplit_ifs5.right new file mode 100644 index 000000000..46ffcece7 --- /dev/null +++ b/shell/ash_test/ash-vars/var_wordsplit_ifs5.right | |||
@@ -0,0 +1 @@ | |||
Zero:0 | |||
diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs5.tests b/shell/ash_test/ash-vars/var_wordsplit_ifs5.tests new file mode 100755 index 000000000..d382116df --- /dev/null +++ b/shell/ash_test/ash-vars/var_wordsplit_ifs5.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | IFS= | ||
2 | set -- | ||
3 | set -- $@ $* | ||
4 | echo Zero:$# | ||
diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs5.right b/shell/hush_test/hush-vars/var_wordsplit_ifs5.right new file mode 100644 index 000000000..46ffcece7 --- /dev/null +++ b/shell/hush_test/hush-vars/var_wordsplit_ifs5.right | |||
@@ -0,0 +1 @@ | |||
Zero:0 | |||
diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs5.tests b/shell/hush_test/hush-vars/var_wordsplit_ifs5.tests new file mode 100755 index 000000000..d382116df --- /dev/null +++ b/shell/hush_test/hush-vars/var_wordsplit_ifs5.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | IFS= | ||
2 | set -- | ||
3 | set -- $@ $* | ||
4 | echo Zero:$# | ||