ash: [EXPAND] Split unquoted $@/$* correctly when IFS is set but empty
authorDenys Vlasenko <vda.linux@googlemail.com>
Sat, 1 Oct 2016 18:35:10 +0000 (20:35 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Sat, 1 Oct 2016 18:35:10 +0000 (20:35 +0200)
Upstream commit:

    Date: Wed, 8 Oct 2014 15:24:23 +0800
    [EXPAND] Split unquoted $@/$* correctly when IFS is set but empty

    Currently we do not field-split $@/$* when it isn't quoted and IFS
    is set but empty.  This is obviously wrong.  This patch fixes this.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
shell/ash.c
shell/ash_test/ash-vars/var_wordsplit_ifs1.right [new file with mode: 0644]
shell/ash_test/ash-vars/var_wordsplit_ifs1.tests [new file with mode: 0755]
shell/hush_test/hush-vars/var_wordsplit_ifs1.right [new file with mode: 0644]
shell/hush_test/hush-vars/var_wordsplit_ifs1.tests [new file with mode: 0755]

index e4349ccadd2b593bed639b13cbfe18582f966ab0..56dbcb7d13dde227934c71e0793331a50eef929d 100644 (file)
@@ -6606,7 +6606,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
  * ash -c 'echo ${#1#}'  name:'1=#'
  */
 static NOINLINE ssize_t
-varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
+varvalue(char *name, int varflags, int flags, struct strlist *var_str_list, int *nulonly)
 {
        const char *p;
        int num;
@@ -6619,7 +6619,8 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
        int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL;
        int syntax = quoted ? DQSYNTAX : BASESYNTAX;
 
-       sep = quoted ? ((flags & EXP_FULL) << CHAR_BIT) : 0;
+       sep = *nulonly ? (flags & EXP_FULL) << CHAR_BIT : 0;
+       *nulonly = 0;
 
        switch (*name) {
        case '$':
@@ -6664,10 +6665,11 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
                }
                /* fall through */
        case '*':
-               sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' ';
+               sep |= ifsset() ? (unsigned char)(ifsval()[0]) : ' ';
  param:
                ap = shellparam.p;
                sepc = sep;
+               *nulonly = !sepc;
                if (!ap)
                        return -1;
                while ((p = *ap++) != NULL) {
@@ -6757,6 +6759,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
        char subtype;
        int quoted;
        char easy;
+       int nulonly;
        char *var;
        int patloc;
        int startloc;
@@ -6767,11 +6770,12 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
        quoted = flags & EXP_QUOTED;
        var = p;
        easy = (!quoted || (*var == '@' && shellparam.nparam));
+       nulonly = easy;
        startloc = expdest - (char *)stackblock();
        p = strchr(p, '=') + 1; //TODO: use var_end(p)?
 
  again:
-       varlen = varvalue(var, varflags, flags, var_str_list);
+       varlen = varvalue(var, varflags, flags, var_str_list, &nulonly);
        if (varflags & VSNUL)
                varlen--;
 
@@ -6865,7 +6869,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
                /* Remove any recorded regions beyond start of variable */
                removerecordregions(startloc);
  record:
-               recordregion(startloc, expdest - (char *)stackblock(), quoted);
+               recordregion(startloc, expdest - (char *)stackblock(), nulonly);
        }
 
  end:
diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs1.right b/shell/ash_test/ash-vars/var_wordsplit_ifs1.right
new file mode 100644 (file)
index 0000000..efdafc7
--- /dev/null
@@ -0,0 +1,25 @@
+Testing: !IFS $*
+.abc.
+.d.
+.e.
+Testing: !IFS $@
+.abc.
+.d.
+.e.
+Testing: !IFS "$*"
+.abc d e.
+Testing: !IFS "$@"
+.abc.
+.d e.
+Testing: IFS="" $*
+.abc.
+.d e.
+Testing: IFS="" $@
+.abc.
+.d e.
+Testing: IFS="" "$*"
+.abcd e.
+Testing: IFS="" "$@"
+.abc.
+.d e.
+Finished
diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs1.tests b/shell/ash_test/ash-vars/var_wordsplit_ifs1.tests
new file mode 100755 (executable)
index 0000000..532ab99
--- /dev/null
@@ -0,0 +1,21 @@
+set -- abc "d e"
+
+echo 'Testing: !IFS $*'
+unset IFS; for a in $*; do echo ".$a."; done
+echo 'Testing: !IFS $@'
+unset IFS; for a in $@; do echo ".$a."; done
+echo 'Testing: !IFS "$*"'
+unset IFS; for a in "$*"; do echo ".$a."; done
+echo 'Testing: !IFS "$@"'
+unset IFS; for a in "$@"; do echo ".$a."; done
+
+echo 'Testing: IFS="" $*'
+IFS=""; for a in $*; do echo ".$a."; done
+echo 'Testing: IFS="" $@'
+IFS=""; for a in $@; do echo ".$a."; done
+echo 'Testing: IFS="" "$*"'
+IFS=""; for a in "$*"; do echo ".$a."; done
+echo 'Testing: IFS="" "$@"'
+IFS=""; for a in "$@"; do echo ".$a."; done
+
+echo Finished
diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs1.right b/shell/hush_test/hush-vars/var_wordsplit_ifs1.right
new file mode 100644 (file)
index 0000000..efdafc7
--- /dev/null
@@ -0,0 +1,25 @@
+Testing: !IFS $*
+.abc.
+.d.
+.e.
+Testing: !IFS $@
+.abc.
+.d.
+.e.
+Testing: !IFS "$*"
+.abc d e.
+Testing: !IFS "$@"
+.abc.
+.d e.
+Testing: IFS="" $*
+.abc.
+.d e.
+Testing: IFS="" $@
+.abc.
+.d e.
+Testing: IFS="" "$*"
+.abcd e.
+Testing: IFS="" "$@"
+.abc.
+.d e.
+Finished
diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs1.tests b/shell/hush_test/hush-vars/var_wordsplit_ifs1.tests
new file mode 100755 (executable)
index 0000000..532ab99
--- /dev/null
@@ -0,0 +1,21 @@
+set -- abc "d e"
+
+echo 'Testing: !IFS $*'
+unset IFS; for a in $*; do echo ".$a."; done
+echo 'Testing: !IFS $@'
+unset IFS; for a in $@; do echo ".$a."; done
+echo 'Testing: !IFS "$*"'
+unset IFS; for a in "$*"; do echo ".$a."; done
+echo 'Testing: !IFS "$@"'
+unset IFS; for a in "$@"; do echo ".$a."; done
+
+echo 'Testing: IFS="" $*'
+IFS=""; for a in $*; do echo ".$a."; done
+echo 'Testing: IFS="" $@'
+IFS=""; for a in $@; do echo ".$a."; done
+echo 'Testing: IFS="" "$*"'
+IFS=""; for a in "$*"; do echo ".$a."; done
+echo 'Testing: IFS="" "$@"'
+IFS=""; for a in "$@"; do echo ".$a."; done
+
+echo Finished