hush: implement negative start in the ${v: -n[:m]} idiom
authorDenys Vlasenko <vda.linux@googlemail.com>
Thu, 6 Jul 2017 17:48:20 +0000 (19:48 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Thu, 6 Jul 2017 17:48:20 +0000 (19:48 +0200)
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
shell/ash_test/ash-vars/var_bash6.right [new file with mode: 0644]
shell/ash_test/ash-vars/var_bash6.tests [new file with mode: 0755]
shell/hush.c
shell/hush_test/hush-vars/var_bash1a.right [new file with mode: 0644]
shell/hush_test/hush-vars/var_bash1a.tests [new file with mode: 0755]

diff --git a/shell/ash_test/ash-vars/var_bash6.right b/shell/ash_test/ash-vars/var_bash6.right
new file mode 100644 (file)
index 0000000..63fc23d
--- /dev/null
@@ -0,0 +1,5 @@
+Expected Actual
+a*z    : a*z
+\z     : \z
+a1z a2z: a1z a2z
+z      : z
diff --git a/shell/ash_test/ash-vars/var_bash6.tests b/shell/ash_test/ash-vars/var_bash6.tests
new file mode 100755 (executable)
index 0000000..cf2e4f0
--- /dev/null
@@ -0,0 +1,9 @@
+# This testcase checks globbing correctness in ${v/a/b}
+
+>a1z; >a2z;
+          echo 'Expected' 'Actual'
+v='a bz'; echo 'a*z    :' "${v/a*z/a*z}"
+v='a bz'; echo '\z     :' "${v/a*z/\z}"
+v='a bz'; echo 'a1z a2z:' ${v/a*z/a*z}
+v='a bz'; echo 'z      :' ${v/a*z/\z}
+rm a1z a2z
index 64b33cf1c87d57d7d02d90b1cefe6ba8126f5721..f6b50dec6e67884ad1ba70efb142b37884a45260 100644 (file)
@@ -5619,8 +5619,12 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
                                goto arith_err;
                        debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len);
                        if (len >= 0) { /* bash compat: len < 0 is illegal */
-                               if (beg < 0) /* bash compat */
-                                       beg = 0;
+                               if (beg < 0) {
+                                       /* negative beg counts from the end */
+                                       beg = (arith_t)strlen(val) + beg;
+                                       if (beg < 0) /* ${v: -999999} is "" */
+                                               beg = len = 0;
+                               }
                                debug_printf_varexp("from val:'%s'\n", val);
                                if (len == 0 || !val || beg >= strlen(val)) {
  arith_err:
diff --git a/shell/hush_test/hush-vars/var_bash1a.right b/shell/hush_test/hush-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/hush_test/hush-vars/var_bash1a.tests b/shell/hush_test/hush-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}'"