hush: fix segfault in ${?:N:M}
authorDenys Vlasenko <vda.linux@googlemail.com>
Sat, 22 May 2010 04:20:26 +0000 (06:20 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Sat, 22 May 2010 04:20:26 +0000 (06:20 +0200)
function                                             old     new   delta
expand_vars_to_list                                 2374    2409     +35
builtin_umask                                        132     133      +1
builtin_exit                                          47      48      +1
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 3/0 up/down: 37/0)               Total: 37 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
shell/hush.c
shell/hush_test/hush-vars/param_expand_alt.right
shell/hush_test/hush-vars/param_expand_alt.tests
shell/hush_test/hush-vars/param_expand_bash_substring.right
shell/hush_test/hush-vars/param_expand_bash_substring.tests

index 7645a34a4f8a1d351d4295ae872d1538b81369ab..08e63785ddf2e0d18fafb6996e004f86cc76abb7 100644 (file)
 
 #define ERR_PTR ((void*)(long)1)
 
-#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
+#define JOB_STATUS_FORMAT    "[%d] %-22s %.40s\n"
 
-#define SPECIAL_VAR_SYMBOL 3
+#define _SPECIAL_VARS_STR     "_*@$!?#"
+#define SPECIAL_VARS_STR     ("_*@$!?#" + 1)
+#define NUMERIC_SPECVARS_STR ("_*@$!?#" + 3)
+
+#define SPECIAL_VAR_SYMBOL   3
 
 struct variable;
 
@@ -2472,21 +2476,6 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
 
                switch (first_ch & 0x7f) {
                /* Highest bit in first_ch indicates that var is double-quoted */
-               case '$': /* pid */
-                       val = utoa(G.root_pid);
-                       break;
-               case '!': /* bg pid */
-                       val = G.last_bg_pid ? utoa(G.last_bg_pid) : (char*)"";
-                       break;
-               case '?': /* exitcode */
-                       val = utoa(G.last_exitcode);
-                       break;
-               case '#': /* argc */
-                       if (arg[1] != SPECIAL_VAR_SYMBOL)
-                               /* actually, it's a ${#var} */
-                               goto case_default;
-                       val = utoa(G.global_argc ? G.global_argc-1 : 0);
-                       break;
                case '*':
                case '@':
                        i = 1;
@@ -2581,27 +2570,35 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
                        break;
                }
 #endif
-               default: /* <SPECIAL_VAR_SYMBOL>varname<SPECIAL_VAR_SYMBOL> */
-               case_default: {
-                       char *var = arg;
-                       char exp_len; /* '#' if it's ${#var} */
+               default: { /* <SPECIAL_VAR_SYMBOL>varname<SPECIAL_VAR_SYMBOL> */
+                       char *var;
+                       char first_char;
                        char exp_op;
                        char exp_save = exp_save; /* for compiler */
-                       char *exp_saveptr = exp_saveptr; /* points to expansion operator */
+                       char *exp_saveptr; /* points to expansion operator */
                        char *exp_word = exp_word; /* for compiler */
 
+                       var = arg;
                        *p = '\0';
-                       arg[0] = first_ch & 0x7f;
-
-                       /* prepare for expansions */
+                       exp_saveptr = arg[1] ? strchr("%#:-=+?", arg[1]) : NULL;
+                       first_char = arg[0] = first_ch & 0x7f;
                        exp_op = 0;
-                       exp_len = var[0];
-                       if (exp_len == '#') {
+
+                       if (first_char == '#' && arg[1] && !exp_saveptr) {
                                /* handle length expansion ${#var} */
                                var++;
+                               exp_op = 'L';
                        } else {
                                /* maybe handle parameter expansion */
-                               exp_saveptr = var + strcspn(var, "%#:-=+?");
+                               if (exp_saveptr /* if 2nd char is one of expansion operators */
+                                && strchr(NUMERIC_SPECVARS_STR, first_char) /* 1st char is special variable */
+                               ) {
+                                       /* ${?:0}, ${#[:]%0} etc */
+                                       exp_saveptr = var + 1;
+                               } else {
+                                       /* ${?}, ${var}, ${var:0}, ${var[:]%0} etc */
+                                       exp_saveptr = var+1 + strcspn(var+1, "%#:-=+?");
+                               }
                                exp_op = exp_save = *exp_saveptr;
                                if (exp_op) {
                                        exp_word = exp_saveptr + 1;
@@ -2616,7 +2613,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
                                                }
                                        }
                                        *exp_saveptr = '\0';
-                               }
+                               } /* else: it's not an expansion op, but bare ${var} */
                        }
 
                        /* lookup the variable in question */
@@ -2626,11 +2623,27 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
                                if (i < G.global_argc)
                                        val = G.global_argv[i];
                                /* else val remains NULL: $N with too big N */
-                       } else
-                               val = get_local_var_value(var);
+                       } else {
+                               switch (var[0]) {
+                               case '$': /* pid */
+                                       val = utoa(G.root_pid);
+                                       break;
+                               case '!': /* bg pid */
+                                       val = G.last_bg_pid ? utoa(G.last_bg_pid) : (char*)"";
+                                       break;
+                               case '?': /* exitcode */
+                                       val = utoa(G.last_exitcode);
+                                       break;
+                               case '#': /* argc */
+                                       val = utoa(G.global_argc ? G.global_argc-1 : 0);
+                                       break;
+                               default:
+                                       val = get_local_var_value(var);
+                               }
+                       }
 
                        /* handle any expansions */
-                       if (exp_len == '#') {
+                       if (exp_op == 'L') {
                                debug_printf_expand("expand: length(%s)=", val);
                                val = utoa(val ? strlen(val) : 0);
                                debug_printf_expand("%s\n", val);
@@ -2761,7 +2774,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
                                                        }
                                                }
                                        }
-                               }
+                               } /* one of "-=+?" */
 
                                *exp_saveptr = exp_save;
                        } /* if (exp_op) */
@@ -6031,7 +6044,7 @@ static int handle_dollar(o_string *as_string,
                 * or even ${?+subst} - operator acting on a special variable,
                 * or the beginning of variable name.
                 */
-               if (!strchr("$!?#*@_", ch) && !isalnum(ch)) { /* not one of those */
+               if (!strchr(_SPECIAL_VARS_STR, ch) && !isalnum(ch)) { /* not one of those */
  bad_dollar_syntax:
                        syntax_error_unterm_str("${name}");
                        debug_printf_parse("handle_dollar return 1: unterminated ${name}\n");
index 4d2197a5e92354d144a6a173e99688212fbda29c..67f18d69c8ec68099dfbf4b67d66c7e166b15169 100644 (file)
@@ -1,6 +1,6 @@
 hush: syntax error: unterminated ${name}
 hush: syntax error: unterminated ${name}
-_0 _0
+__ __
 _ _ _ _ _
 _aaaa _ _ _word _word
 _ _ _ _ _
index dcdca86d4446cb867574b1f914707d250f2b11ed..3b646b142c85f170832f2bd434ec345552183ef1 100755 (executable)
@@ -2,8 +2,8 @@
 "$THIS_SH" -c 'echo ${+}  ; echo moo'
 "$THIS_SH" -c 'echo ${:+} ; echo moo'
 
-# now some funky ones
-echo _${#+} _${#:+}
+# now some funky ones. (bash doesn't accept ${#+})
+echo _${#+}_ _${#:+}_
 
 # now some valid ones
 set --
index 53b8836ff8a40282a46f0877e89ca1a921c3afe3..2f4c51d06217b7de0437fa583a6ced69ad63f4e9 100644 (file)
@@ -39,6 +39,19 @@ f:1:2=|12|
 f::2 =|01|
 f:1: =||
 f::  =||
+Substrings from special vars
+?    =|0|
+?:1  =||
+?:1:2=||
+?::2 =|0|
+?:1: =||
+?::  =||
+#    =|11|
+#:1  =|1|
+#:1:2=|1|
+#::2 =|11|
+#:1: =||
+#::  =||
 Substrings with expressions
 f            =|01234567|
 f:1+1:2+2    =|2345|
index a80523add1d5d2dfde0f21a2c6a89f4b28954409..5c9552dbabc13e01939142bfaf9159ca87bf9878 100755 (executable)
@@ -55,6 +55,21 @@ f=0123456789; echo "f::2 =|${f::2}|"
 f=0123456789; echo "f:1: =|${f:1:}|"
 f=0123456789; echo "f::  =|${f::}|"
 
+echo "Substrings from special vars"
+echo '?    '"=|$?|"
+echo '?:1  '"=|${?:1}|"
+echo '?:1:2'"=|${?:1:2}|"
+echo '?::2 '"=|${?::2}|"
+echo '?:1: '"=|${?:1:}|"
+echo '?::  '"=|${?::}|"
+set -- 1 2 3 4 5 6 7 8 9 10 11
+echo '#    '"=|$#|"
+echo '#:1  '"=|${#:1}|"
+echo '#:1:2'"=|${#:1:2}|"
+echo '#::2 '"=|${#::2}|"
+echo '#:1: '"=|${#:1:}|"
+echo '#::  '"=|${#::}|"
+
 echo "Substrings with expressions"
 f=01234567; echo 'f            '"=|$f|"
 f=01234567; echo 'f:1+1:2+2    '"=|${f:1+1:2+2}|"