ash: more general format ${var:EXPR:EXPR}
authorDenys Vlasenko <vda.linux@googlemail.com>
Mon, 17 Jul 2017 15:49:11 +0000 (17:49 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Mon, 17 Jul 2017 15:49:11 +0000 (17:49 +0200)
function                                             old     new   delta
subevalvar                                          1171    1202     +31
localcmd                                             364     366      +2

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

index eb5156bea48651987820babd1bf8d6c452daf517..aaf0561b883d9ebd6eadae04d297fd22325766fc 100644 (file)
@@ -6612,7 +6612,6 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
        char *loc;
        char *rmesc, *rmescend;
        char *str;
-       IF_BASH_SUBSTR(int pos, len, orig_len;)
        int amount, resetloc;
        IF_BASH_PATTERN_SUBST(int workloc;)
        IF_BASH_PATTERN_SUBST(char *repl = NULL;)
@@ -6641,14 +6640,23 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
                /* NOTREACHED */
 
 #if BASH_SUBSTR
-       case VSSUBSTR:
-//TODO: support more general format ${v:EXPR:EXPR},
-// where EXPR follows $(()) rules
+       case VSSUBSTR: {
+               int pos, len, orig_len;
+               char *colon;
+
                loc = str = stackblock() + strloc;
+
+# if !ENABLE_FEATURE_SH_MATH
+#  define ash_arith number
+# endif
                /* Read POS in ${var:POS:LEN} */
-               pos = atoi(loc); /* number(loc) errors out on "1:4" */
-               len = str - startp - 1;
+               colon = strchr(loc, ':');
+               if (colon) *colon = '\0';
+               pos = ash_arith(loc);
+               if (colon) *colon = ':';
 
+               /* Read LEN in ${var:POS:LEN} */
+               len = str - startp - 1;
                /* *loc != '\0', guaranteed by parser */
                if (quotes) {
                        char *ptr;
@@ -6662,26 +6670,21 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
                        }
                }
                orig_len = len;
-
                if (*loc++ == ':') {
                        /* ${var::LEN} */
-                       len = number(loc);
+                       len = ash_arith(loc);
                } else {
                        /* Skip POS in ${var:POS:LEN} */
                        len = orig_len;
                        while (*loc && *loc != ':') {
-                               /* TODO?
-                                * bash complains on: var=qwe; echo ${var:1a:123}
-                               if (!isdigit(*loc))
-                                       ash_msg_and_raise_error(msg_illnum, str);
-                                */
                                loc++;
                        }
                        if (*loc++ == ':') {
-                               len = number(loc);
+                               len = ash_arith(loc);
                        }
-//TODO: number() chokes on "-n". In bash, LEN=-n means strlen()-n
                }
+#  undef ash_arith
+
                if (pos < 0) {
                        /* ${VAR:$((-n)):l} starts n chars from the end */
                        pos = orig_len + pos;
@@ -6689,12 +6692,16 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
                if ((unsigned)pos >= orig_len) {
                        /* apart from obvious ${VAR:999999:l},
                         * covers ${VAR:$((-9999999)):l} - result is ""
-                        * (bash-compat)
+                        * (bash compat)
                         */
                        pos = 0;
                        len = 0;
                }
-               if (len > (orig_len - pos))
+               if (len < 0) {
+                       /* ${VAR:N:-M} sets LEN to strlen()-M */
+                       len = (orig_len - pos) + len;
+               }
+               if ((unsigned)len > (orig_len - pos))
                        len = orig_len - pos;
 
                for (str = startp; pos; str++, pos--) {
@@ -6710,6 +6717,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
                amount = loc - expdest;
                STADJUST(amount, expdest);
                return loc;
+       }
 #endif /* BASH_SUBSTR */
        }
 
@@ -6754,6 +6762,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
 #if BASH_PATTERN_SUBST
        workloc = expdest - (char *)stackblock();
        if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
+               int len;
                char *idx, *end;
 
                if (!repl) {
diff --git a/shell/ash_test/ash-vars/var_bash1b.right b/shell/ash_test/ash-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/ash_test/ash-vars/var_bash1b.tests b/shell/ash_test/ash-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:$?