ash: fix handling of ${VAR: -2}
authorDenys Vlasenko <vda.linux@googlemail.com>
Mon, 25 Jul 2016 01:56:00 +0000 (03:56 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Mon, 25 Jul 2016 01:56:00 +0000 (03:56 +0200)
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
shell/ash.c
shell/ash_test/ash-vars/var_bash1a.right [new file with mode: 0644]
shell/ash_test/ash-vars/var_bash1a.tests [new file with mode: 0755]

index 4f6376f787ad2446e1b63eae2d5c726299cf4226..496167fbe6593de3a0f72e5b19948ac2ee965865 100644 (file)
@@ -6323,6 +6323,8 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
 
 #if ENABLE_ASH_BASH_COMPAT
        case VSSUBSTR:
+//TODO: support more general format ${v:EXPR:EXPR},
+// where EXPR follows $(()) rules
                loc = str = stackblock() + strloc;
                /* Read POS in ${var:POS:LEN} */
                pos = atoi(loc); /* number(loc) errors out on "1:4" */
@@ -11577,15 +11579,18 @@ parsesub: {
                STPUTC('=', out);
                flags = 0;
                if (subtype == 0) {
+                       static const char types[] ALIGN1 = "}-+?=";
                        /* ${VAR...} but not $VAR or ${#VAR} */
                        /* c == first char after VAR */
                        switch (c) {
                        case ':':
                                c = pgetc();
 #if ENABLE_ASH_BASH_COMPAT
-                               if (c == ':' || c == '$' || isdigit(c)) {
-//TODO: support more general format ${v:EXPR:EXPR},
-// where EXPR follows $(()) rules
+                               /* This check is only needed to not misinterpret
+                                * ${VAR:-WORD}, ${VAR:+WORD}, ${VAR:=WORD}, ${VAR:?WORD}
+                                * constructs.
+                                */
+                               if (!strchr(types, c)) {
                                        subtype = VSSUBSTR;
                                        pungetc();
                                        break; /* "goto do_pungetc" is bigger (!) */
@@ -11594,7 +11599,6 @@ parsesub: {
                                flags = VSNUL;
                                /*FALLTHROUGH*/
                        default: {
-                               static const char types[] ALIGN1 = "}-+?=";
                                const char *p = strchr(types, c);
                                if (p == NULL)
                                        goto badsub;
diff --git a/shell/ash_test/ash-vars/var_bash1a.right b/shell/ash_test/ash-vars/var_bash1a.right
new file mode 100644 (file)
index 0000000..1965b5c
--- /dev/null
@@ -0,0 +1,6 @@
+parameter     'abcdef'
+varoffset2    'cdef'
+varoffset-2   'ef'
+literal '2'   'cdef'
+literal '-2'  'abcdef'
+literal ' -2' 'ef'
diff --git a/shell/ash_test/ash-vars/var_bash1a.tests b/shell/ash_test/ash-vars/var_bash1a.tests
new file mode 100755 (executable)
index 0000000..551dd9a
--- /dev/null
@@ -0,0 +1,11 @@
+parameter=abcdef
+offset=2
+noffset=-2
+echo "parameter     '${parameter}'"
+echo "varoffset2    '${parameter:${offset}}'"
+echo "varoffset-2   '${parameter:${noffset}}'"
+echo "literal '2'   '${parameter:2}'"
+# This is not inrpreted as ${VAR:POS{:LEN}},
+# but as ${VAR:=WORD} - if VAR is unset or null, substitute WORD
+echo "literal '-2'  '${parameter:-2}'"
+echo "literal ' -2' '${parameter: -2}'"