hush: support ${VAR:N:-M}
authorDenys Vlasenko <vda.linux@googlemail.com>
Mon, 17 Jul 2017 14:46:57 +0000 (16:46 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Mon, 17 Jul 2017 14:46:57 +0000 (16:46 +0200)
function                                             old     new   delta
expand_one_var                                      1602    1615     +13
builtin_type                                         114     116      +2

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
shell/hush.c
shell/hush_test/hush-vars/var_bash1b.right [new file with mode: 0644]
shell/hush_test/hush-vars/var_bash1b.tests [new file with mode: 0755]

index fd2a3d0f57936067a03e79ca30c60cdb49cf9f49..836f3b83c708d3f916b79d14d2fce363beb89e91 100644 (file)
@@ -5723,32 +5723,34 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
                        if (errmsg)
                                goto arith_err;
                        debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len);
-                       if (len >= 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)) {
+                       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) {
+                               /* in bash, len=-n means strlen()-n */
+                               len = (arith_t)strlen(val) - beg + len;
+                               if (len < 0) /* bash compat */
+                                       die_if_script("%s: substring expression < 0", var);
+                       }
+                       if (len == 0 || !val || beg >= strlen(val)) {
  arith_err:
-                                       val = NULL;
-                               } else {
-                                       /* Paranoia. What if user entered 9999999999999
-                                        * which fits in arith_t but not int? */
-                                       if (len >= INT_MAX)
-                                               len = INT_MAX;
-                                       val = to_be_freed = xstrndup(val + beg, len);
-                               }
-                               debug_printf_varexp("val:'%s'\n", val);
-                       } else
-//TODO: in bash, len=-n means strlen()-n
-#endif /* HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH */
-                       {
-                               die_if_script("malformed ${%s:...}", var);
                                val = NULL;
+                       } else {
+                               /* Paranoia. What if user entered 9999999999999
+                                * which fits in arith_t but not int? */
+                               if (len >= INT_MAX)
+                                       len = INT_MAX;
+                               val = to_be_freed = xstrndup(val + beg, len);
                        }
+                       debug_printf_varexp("val:'%s'\n", val);
+#else /* not (HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH) */
+                       die_if_script("malformed ${%s:...}", var);
+                       val = NULL;
+#endif
                } else { /* one of "-=+?" */
                        /* Standard-mandated substitution ops:
                         * ${var?word} - indicate error if unset
diff --git a/shell/hush_test/hush-vars/var_bash1b.right b/shell/hush_test/hush-vars/var_bash1b.right
new file mode 100644 (file)
index 0000000..fafc0f0
--- /dev/null
@@ -0,0 +1,23 @@
+all    |0123456
+4:     |456
+4:2    |45
+4:-1   |45
+4:-2   |4
+4:-3   |
+-4:    |3456
+-4:2   |34
+-4:-1  |345
+-4:-2  |34
+-4:-3  |3
+-4:-4  |
+-4:i=2 |34
+-4:i=-2|34
+-4:i=-3|3
+-4:i=-4|
+-5:    |23456
+-6:    |123456
+-7:    |0123456
+-8:    |
+-9:    |
+-9:-99 |
+Ok:0
diff --git a/shell/hush_test/hush-vars/var_bash1b.tests b/shell/hush_test/hush-vars/var_bash1b.tests
new file mode 100755 (executable)
index 0000000..efbdef3
--- /dev/null
@@ -0,0 +1,24 @@
+set -- 0123456
+       echo "all    |"$1
+       echo "4:     |"${1:4}
+       echo "4:2    |"${1:4:2}
+       echo "4:-1   |"${1:4:-1}
+       echo "4:-2   |"${1:4:-2}
+       echo "4:-3   |"${1:4:-3}
+       echo "-4:    |"${1: -4}
+       echo "-4:2   |"${1: -4:2}
+       echo "-4:-1  |"${1: -4:-1}
+       echo "-4:-2  |"${1: -4:-2}
+       echo "-4:-3  |"${1: -4:-3}
+       echo "-4:-4  |"${1: -4:-4}
+i=2;   echo "-4:i=2 |"${1: -4:i}
+i=-2;  echo "-4:i=-2|"${1: -4:i}
+i=-3;  echo "-4:i=-3|"${1: -4:i}
+i=-4;  echo "-4:i=-4|"${1: -4:i}
+       echo "-5:    |"${1: -5}
+       echo "-6:    |"${1: -6}
+       echo "-7:    |"${1: -7}
+       echo "-8:    |"${1: -8}
+       echo "-9:    |"${1: -9}
+       echo "-9:-99 |"${1: -9:-99}
+echo Ok:$?