aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2018-08-05 14:29:58 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2018-08-05 14:29:58 +0200
commit440da97ed79841b55f0b61e4108a336b61642bff (patch)
tree9cb31a0c1dabd368d8451d92be5506c15a81c8c5
parent67dae152f4bf5456e4ea7950f4b55356aa37ec6c (diff)
downloadbusybox-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.c38
-rw-r--r--shell/ash_test/ash-vars/var_wordsplit_ifs5.right1
-rwxr-xr-xshell/ash_test/ash-vars/var_wordsplit_ifs5.tests4
-rw-r--r--shell/hush_test/hush-vars/var_wordsplit_ifs5.right1
-rwxr-xr-xshell/hush_test/hush-vars/var_wordsplit_ifs5.tests4
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 */
7177static NOINLINE ssize_t 7177static NOINLINE ssize_t
7178varvalue(char *name, int varflags, int flags, int *quotedp) 7178varvalue(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, &quoted); 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 @@
1IFS=
2set --
3set -- $@ $*
4echo 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 @@
1IFS=
2set --
3set -- $@ $*
4echo Zero:$#