diff options
Diffstat (limited to 'shell/hush.c')
-rw-r--r-- | shell/hush.c | 340 |
1 files changed, 245 insertions, 95 deletions
diff --git a/shell/hush.c b/shell/hush.c index e3c6e2de9..881331c5b 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -990,7 +990,16 @@ struct globals { | |||
990 | #if ENABLE_HUSH_MEMLEAK | 990 | #if ENABLE_HUSH_MEMLEAK |
991 | unsigned long memleak_value; | 991 | unsigned long memleak_value; |
992 | #endif | 992 | #endif |
993 | #if HUSH_DEBUG | 993 | #if ENABLE_HUSH_MODE_X |
994 | unsigned x_mode_depth; | ||
995 | /* "set -x" output should not be redirectable with subsequent 2>FILE. | ||
996 | * We dup fd#2 to x_mode_fd when "set -x" is executed, and use it | ||
997 | * for all subsequent output. | ||
998 | */ | ||
999 | int x_mode_fd; | ||
1000 | o_string x_mode_buf; | ||
1001 | #endif | ||
1002 | #if HUSH_DEBUG >= 2 | ||
994 | int debug_indent; | 1003 | int debug_indent; |
995 | #endif | 1004 | #endif |
996 | struct sigaction sa; | 1005 | struct sigaction sa; |
@@ -1212,7 +1221,7 @@ static const struct built_in_command bltins2[] = { | |||
1212 | 1221 | ||
1213 | /* Debug printouts. | 1222 | /* Debug printouts. |
1214 | */ | 1223 | */ |
1215 | #if HUSH_DEBUG | 1224 | #if HUSH_DEBUG >= 2 |
1216 | /* prevent disasters with G.debug_indent < 0 */ | 1225 | /* prevent disasters with G.debug_indent < 0 */ |
1217 | # define indent() fdprintf(2, "%*s", (G.debug_indent * 2) & 0xff, "") | 1226 | # define indent() fdprintf(2, "%*s", (G.debug_indent * 2) & 0xff, "") |
1218 | # define debug_enter() (G.debug_indent++) | 1227 | # define debug_enter() (G.debug_indent++) |
@@ -1657,6 +1666,12 @@ static int move_HFILEs_on_redirect(int fd, int avoid_fd) | |||
1657 | } | 1666 | } |
1658 | fl = fl->next_hfile; | 1667 | fl = fl->next_hfile; |
1659 | } | 1668 | } |
1669 | #if ENABLE_HUSH_MODE_X | ||
1670 | if (G.x_mode_fd > 0 && fd == G.x_mode_fd) { | ||
1671 | G.x_mode_fd = xdup_CLOEXEC_and_close(fd, avoid_fd); | ||
1672 | return 1; /* "found and moved" */ | ||
1673 | } | ||
1674 | #endif | ||
1660 | return 0; /* "not in the list" */ | 1675 | return 0; /* "not in the list" */ |
1661 | } | 1676 | } |
1662 | #if ENABLE_FEATURE_SH_STANDALONE && BB_MMU | 1677 | #if ENABLE_FEATURE_SH_STANDALONE && BB_MMU |
@@ -1668,9 +1683,15 @@ static void close_all_HFILE_list(void) | |||
1668 | * It is disastrous if we share memory with a vforked parent. | 1683 | * It is disastrous if we share memory with a vforked parent. |
1669 | * I'm not sure we never come here after vfork. | 1684 | * I'm not sure we never come here after vfork. |
1670 | * Therefore just close fd, nothing more. | 1685 | * Therefore just close fd, nothing more. |
1686 | * | ||
1687 | * ">" instead of ">=": we don't close fd#0, | ||
1688 | * interactive shell uses hfopen(NULL) as stdin input | ||
1689 | * which has fl->fd == 0, but fd#0 gets redirected in pipes. | ||
1690 | * If we'd close it here, then e.g. interactive "set | sort" | ||
1691 | * with NOFORKed sort, would have sort's input fd closed. | ||
1671 | */ | 1692 | */ |
1672 | /*hfclose(fl); - unsafe */ | 1693 | if (fl->fd > 0) |
1673 | if (fl->fd >= 0) | 1694 | /*hfclose(fl); - unsafe */ |
1674 | close(fl->fd); | 1695 | close(fl->fd); |
1675 | fl = fl->next_hfile; | 1696 | fl = fl->next_hfile; |
1676 | } | 1697 | } |
@@ -2376,6 +2397,12 @@ static int set_local_var(char *str, unsigned flags) | |||
2376 | return 0; | 2397 | return 0; |
2377 | } | 2398 | } |
2378 | 2399 | ||
2400 | static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) | ||
2401 | { | ||
2402 | char *var = xasprintf("%s=%s", name, val); | ||
2403 | set_local_var(var, /*flag:*/ 0); | ||
2404 | } | ||
2405 | |||
2379 | /* Used at startup and after each cd */ | 2406 | /* Used at startup and after each cd */ |
2380 | static void set_pwd_var(unsigned flag) | 2407 | static void set_pwd_var(unsigned flag) |
2381 | { | 2408 | { |
@@ -2422,15 +2449,6 @@ static int unset_local_var(const char *name) | |||
2422 | } | 2449 | } |
2423 | #endif | 2450 | #endif |
2424 | 2451 | ||
2425 | #if BASH_HOSTNAME_VAR || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_READ || ENABLE_HUSH_GETOPTS \ | ||
2426 | || (ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT) | ||
2427 | static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) | ||
2428 | { | ||
2429 | char *var = xasprintf("%s=%s", name, val); | ||
2430 | set_local_var(var, /*flag:*/ 0); | ||
2431 | } | ||
2432 | #endif | ||
2433 | |||
2434 | 2452 | ||
2435 | /* | 2453 | /* |
2436 | * Helpers for "var1=val1 var2=val2 cmd" feature | 2454 | * Helpers for "var1=val1 var2=val2 cmd" feature |
@@ -2900,6 +2918,11 @@ static void o_addstr(o_string *o, const char *str) | |||
2900 | o_addblock(o, str, strlen(str)); | 2918 | o_addblock(o, str, strlen(str)); |
2901 | } | 2919 | } |
2902 | 2920 | ||
2921 | static void o_addstr_with_NUL(o_string *o, const char *str) | ||
2922 | { | ||
2923 | o_addblock(o, str, strlen(str) + 1); | ||
2924 | } | ||
2925 | |||
2903 | #if !BB_MMU | 2926 | #if !BB_MMU |
2904 | static void nommu_addchr(o_string *o, int ch) | 2927 | static void nommu_addchr(o_string *o, int ch) |
2905 | { | 2928 | { |
@@ -2910,10 +2933,36 @@ static void nommu_addchr(o_string *o, int ch) | |||
2910 | # define nommu_addchr(o, str) ((void)0) | 2933 | # define nommu_addchr(o, str) ((void)0) |
2911 | #endif | 2934 | #endif |
2912 | 2935 | ||
2913 | static void o_addstr_with_NUL(o_string *o, const char *str) | 2936 | #if ENABLE_HUSH_MODE_X |
2937 | static void x_mode_addchr(int ch) | ||
2914 | { | 2938 | { |
2915 | o_addblock(o, str, strlen(str) + 1); | 2939 | o_addchr(&G.x_mode_buf, ch); |
2940 | } | ||
2941 | static void x_mode_addstr(const char *str) | ||
2942 | { | ||
2943 | o_addstr(&G.x_mode_buf, str); | ||
2916 | } | 2944 | } |
2945 | static void x_mode_addblock(const char *str, int len) | ||
2946 | { | ||
2947 | o_addblock(&G.x_mode_buf, str, len); | ||
2948 | } | ||
2949 | static void x_mode_prefix(void) | ||
2950 | { | ||
2951 | int n = G.x_mode_depth; | ||
2952 | do x_mode_addchr('+'); while (--n >= 0); | ||
2953 | } | ||
2954 | static void x_mode_flush(void) | ||
2955 | { | ||
2956 | int len = G.x_mode_buf.length; | ||
2957 | if (len <= 0) | ||
2958 | return; | ||
2959 | if (G.x_mode_fd > 0) { | ||
2960 | G.x_mode_buf.data[len] = '\n'; | ||
2961 | full_write(G.x_mode_fd, G.x_mode_buf.data, len + 1); | ||
2962 | } | ||
2963 | G.x_mode_buf.length = 0; | ||
2964 | } | ||
2965 | #endif | ||
2917 | 2966 | ||
2918 | /* | 2967 | /* |
2919 | * HUSH_BRACE_EXPANSION code needs corresponding quoting on variable expansion side. | 2968 | * HUSH_BRACE_EXPANSION code needs corresponding quoting on variable expansion side. |
@@ -3068,6 +3117,13 @@ static int o_save_ptr_helper(o_string *o, int n) | |||
3068 | o->data = xrealloc(o->data, o->maxlen + 1); | 3117 | o->data = xrealloc(o->data, o->maxlen + 1); |
3069 | list = (char**)o->data; | 3118 | list = (char**)o->data; |
3070 | memmove(list + n + 0x10, list + n, string_len); | 3119 | memmove(list + n + 0x10, list + n, string_len); |
3120 | /* | ||
3121 | * expand_on_ifs() has a "previous argv[] ends in IFS?" | ||
3122 | * check. (grep for -prev-ifs-check-). | ||
3123 | * Ensure that argv[-1][last] is not garbage | ||
3124 | * but zero bytes, to save index check there. | ||
3125 | */ | ||
3126 | list[n + 0x10 - 1] = 0; | ||
3071 | o->length += 0x10 * sizeof(list[0]); | 3127 | o->length += 0x10 * sizeof(list[0]); |
3072 | } else { | 3128 | } else { |
3073 | debug_printf_list("list[%d]=%d string_start=%d\n", | 3129 | debug_printf_list("list[%d]=%d string_start=%d\n", |
@@ -3095,6 +3151,41 @@ static int o_get_last_ptr(o_string *o, int n) | |||
3095 | return ((int)(uintptr_t)list[n-1]) + string_start; | 3151 | return ((int)(uintptr_t)list[n-1]) + string_start; |
3096 | } | 3152 | } |
3097 | 3153 | ||
3154 | /* | ||
3155 | * Globbing routines. | ||
3156 | * | ||
3157 | * Most words in commands need to be globbed, even ones which are | ||
3158 | * (single or double) quoted. This stems from the possiblity of | ||
3159 | * constructs like "abc"* and 'abc'* - these should be globbed. | ||
3160 | * Having a different code path for fully-quoted strings ("abc", | ||
3161 | * 'abc') would only help performance-wise, but we still need | ||
3162 | * code for partially-quoted strings. | ||
3163 | * | ||
3164 | * Unfortunately, if we want to match bash and ash behavior in all cases, | ||
3165 | * the logic can't be "shell-syntax argument is first transformed | ||
3166 | * to a string, then globbed, and if globbing does not match anything, | ||
3167 | * it is used verbatim". Here are two examples where it fails: | ||
3168 | * | ||
3169 | * echo 'b\*'? | ||
3170 | * | ||
3171 | * The globbing can't be avoided (because of '?' at the end). | ||
3172 | * The glob pattern is: b\\\*? - IOW, both \ and * are literals | ||
3173 | * and are glob-escaped. If this does not match, bash/ash print b\*? | ||
3174 | * - IOW: they "unbackslash" the glob pattern. | ||
3175 | * Now, look at this: | ||
3176 | * | ||
3177 | * v='\\\*'; echo b$v? | ||
3178 | * | ||
3179 | * The glob pattern is the same here: b\\\*? - the unquoted $v expansion | ||
3180 | * should be used as glob pattern with no changes. However, if glob | ||
3181 | * does not match, bash/ash print b\\\*? - NOT THE SAME as first example! | ||
3182 | * | ||
3183 | * ash implements this by having an encoded representation of the word | ||
3184 | * to glob, which IS NOT THE SAME as the glob pattern - it has more data. | ||
3185 | * Glob pattern is derived from it. If glob fails, the decision what result | ||
3186 | * should be is made using that encoded representation. Not glob pattern. | ||
3187 | */ | ||
3188 | |||
3098 | #if ENABLE_HUSH_BRACE_EXPANSION | 3189 | #if ENABLE_HUSH_BRACE_EXPANSION |
3099 | /* There in a GNU extension, GLOB_BRACE, but it is not usable: | 3190 | /* There in a GNU extension, GLOB_BRACE, but it is not usable: |
3100 | * first, it processes even {a} (no commas), second, | 3191 | * first, it processes even {a} (no commas), second, |
@@ -4877,6 +4968,15 @@ static int parse_dollar(o_string *as_string, | |||
4877 | end_ch = '}' * 0x100 + '/'; | 4968 | end_ch = '}' * 0x100 + '/'; |
4878 | } | 4969 | } |
4879 | o_addchr(dest, ch); | 4970 | o_addchr(dest, ch); |
4971 | /* The pattern can't be empty. | ||
4972 | * IOW: if the first char after "${v//" is a slash, | ||
4973 | * it does not terminate the pattern - it's the first char of the pattern: | ||
4974 | * v=/dev/ram; echo ${v////-} prints -dev-ram (pattern is "/") | ||
4975 | * v=/dev/ram; echo ${v///r/-} prints /dev-am (pattern is "/r") | ||
4976 | */ | ||
4977 | if (i_peek(input) == '/') { | ||
4978 | o_addchr(dest, i_getch(input)); | ||
4979 | } | ||
4880 | again: | 4980 | again: |
4881 | if (!BB_MMU) | 4981 | if (!BB_MMU) |
4882 | pos = dest->length; | 4982 | pos = dest->length; |
@@ -5796,12 +5896,16 @@ static int expand_on_ifs(o_string *output, int n, const char *str) | |||
5796 | /* Start new word... but not always! */ | 5896 | /* Start new word... but not always! */ |
5797 | /* Case "v=' a'; echo ''$v": we do need to finalize empty word: */ | 5897 | /* Case "v=' a'; echo ''$v": we do need to finalize empty word: */ |
5798 | if (output->has_quoted_part | 5898 | if (output->has_quoted_part |
5799 | /* Case "v=' a'; echo $v": | 5899 | /* |
5900 | * Case "v=' a'; echo $v": | ||
5800 | * here nothing precedes the space in $v expansion, | 5901 | * here nothing precedes the space in $v expansion, |
5801 | * therefore we should not finish the word | 5902 | * therefore we should not finish the word |
5802 | * (IOW: if there *is* word to finalize, only then do it): | 5903 | * (IOW: if there *is* word to finalize, only then do it): |
5904 | * It's okay if this accesses the byte before first argv[]: | ||
5905 | * past call to o_save_ptr() cleared it to zero byte | ||
5906 | * (grep for -prev-ifs-check-). | ||
5803 | */ | 5907 | */ |
5804 | || (n > 0 && output->data[output->length - 1]) | 5908 | || output->data[output->length - 1] |
5805 | ) { | 5909 | ) { |
5806 | new_word: | 5910 | new_word: |
5807 | o_addchr(output, '\0'); | 5911 | o_addchr(output, '\0'); |
@@ -5856,6 +5960,26 @@ static char *encode_then_expand_string(const char *str) | |||
5856 | return exp_str; | 5960 | return exp_str; |
5857 | } | 5961 | } |
5858 | 5962 | ||
5963 | static const char *first_special_char_in_vararg(const char *cp) | ||
5964 | { | ||
5965 | for (;;) { | ||
5966 | if (!*cp) return NULL; /* string has no special chars */ | ||
5967 | if (*cp == '$') return cp; | ||
5968 | if (*cp == '\\') return cp; | ||
5969 | if (*cp == '\'') return cp; | ||
5970 | if (*cp == '"') return cp; | ||
5971 | #if ENABLE_HUSH_TICK | ||
5972 | if (*cp == '`') return cp; | ||
5973 | #endif | ||
5974 | /* dquoted "${x:+ARG}" should not glob, therefore | ||
5975 | * '*' et al require some non-literal processing: */ | ||
5976 | if (*cp == '*') return cp; | ||
5977 | if (*cp == '?') return cp; | ||
5978 | if (*cp == '[') return cp; | ||
5979 | cp++; | ||
5980 | } | ||
5981 | } | ||
5982 | |||
5859 | /* Expanding ARG in ${var#ARG}, ${var%ARG}, or ${var/ARG/ARG}. | 5983 | /* Expanding ARG in ${var#ARG}, ${var%ARG}, or ${var/ARG/ARG}. |
5860 | * These can contain single- and double-quoted strings, | 5984 | * These can contain single- and double-quoted strings, |
5861 | * and treated as if the ARG string is initially unquoted. IOW: | 5985 | * and treated as if the ARG string is initially unquoted. IOW: |
@@ -5875,19 +5999,10 @@ static char *encode_then_expand_vararg(const char *str, int handle_squotes, int | |||
5875 | char *exp_str; | 5999 | char *exp_str; |
5876 | struct in_str input; | 6000 | struct in_str input; |
5877 | o_string dest = NULL_O_STRING; | 6001 | o_string dest = NULL_O_STRING; |
5878 | const char *cp; | ||
5879 | 6002 | ||
5880 | cp = str; | 6003 | if (!first_special_char_in_vararg(str)) { |
5881 | for (;;) { | 6004 | /* string has no special chars */ |
5882 | if (!*cp) return NULL; /* string has no special chars */ | 6005 | return NULL; |
5883 | if (*cp == '$') break; | ||
5884 | if (*cp == '\\') break; | ||
5885 | if (*cp == '\'') break; | ||
5886 | if (*cp == '"') break; | ||
5887 | #if ENABLE_HUSH_TICK | ||
5888 | if (*cp == '`') break; | ||
5889 | #endif | ||
5890 | cp++; | ||
5891 | } | 6006 | } |
5892 | 6007 | ||
5893 | setup_string_in_str(&input, str); | 6008 | setup_string_in_str(&input, str); |
@@ -5968,26 +6083,19 @@ static char *encode_then_expand_vararg(const char *str, int handle_squotes, int | |||
5968 | /* Expanding ARG in ${var+ARG}, ${var-ARG} | 6083 | /* Expanding ARG in ${var+ARG}, ${var-ARG} |
5969 | */ | 6084 | */ |
5970 | static int encode_then_append_var_plusminus(o_string *output, int n, | 6085 | static int encode_then_append_var_plusminus(o_string *output, int n, |
5971 | const char *str, int dquoted) | 6086 | char *str, int dquoted) |
5972 | { | 6087 | { |
5973 | struct in_str input; | 6088 | struct in_str input; |
5974 | o_string dest = NULL_O_STRING; | 6089 | o_string dest = NULL_O_STRING; |
5975 | 6090 | ||
5976 | #if 0 //todo? | 6091 | if (!first_special_char_in_vararg(str) |
5977 | const char *cp; | 6092 | && '\0' == str[strcspn(str, G.ifs)] |
5978 | cp = str; | 6093 | ) { |
5979 | for (;;) { | 6094 | /* string has no special chars |
5980 | if (!*cp) return NULL; /* string has no special chars */ | 6095 | * && string has no $IFS chars |
5981 | if (*cp == '$') break; | 6096 | */ |
5982 | if (*cp == '\\') break; | 6097 | return expand_vars_to_list(output, n, str); |
5983 | if (*cp == '\'') break; | ||
5984 | if (*cp == '"') break; | ||
5985 | #if ENABLE_HUSH_TICK | ||
5986 | if (*cp == '`') break; | ||
5987 | #endif | ||
5988 | cp++; | ||
5989 | } | 6098 | } |
5990 | #endif | ||
5991 | 6099 | ||
5992 | setup_string_in_str(&input, str); | 6100 | setup_string_in_str(&input, str); |
5993 | 6101 | ||
@@ -7182,11 +7290,8 @@ static int generate_stream_from_string(const char *s, pid_t *pid_p) | |||
7182 | + (1 << SIGTTIN) | 7290 | + (1 << SIGTTIN) |
7183 | + (1 << SIGTTOU) | 7291 | + (1 << SIGTTOU) |
7184 | , SIG_IGN); | 7292 | , SIG_IGN); |
7185 | CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */ | ||
7186 | close(channel[0]); /* NB: close _first_, then move fd! */ | 7293 | close(channel[0]); /* NB: close _first_, then move fd! */ |
7187 | xmove_fd(channel[1], 1); | 7294 | xmove_fd(channel[1], 1); |
7188 | /* Prevent it from trying to handle ctrl-z etc */ | ||
7189 | IF_HUSH_JOB(G.run_list_level = 1;) | ||
7190 | # if ENABLE_HUSH_TRAP | 7295 | # if ENABLE_HUSH_TRAP |
7191 | /* Awful hack for `trap` or $(trap). | 7296 | /* Awful hack for `trap` or $(trap). |
7192 | * | 7297 | * |
@@ -7233,7 +7338,12 @@ static int generate_stream_from_string(const char *s, pid_t *pid_p) | |||
7233 | } | 7338 | } |
7234 | # endif | 7339 | # endif |
7235 | # if BB_MMU | 7340 | # if BB_MMU |
7341 | /* Prevent it from trying to handle ctrl-z etc */ | ||
7342 | IF_HUSH_JOB(G.run_list_level = 1;) | ||
7343 | CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */ | ||
7236 | reset_traps_to_defaults(); | 7344 | reset_traps_to_defaults(); |
7345 | IF_HUSH_MODE_X(G.x_mode_depth++;) | ||
7346 | //bb_error_msg("%s: ++x_mode_depth=%d", __func__, G.x_mode_depth); | ||
7237 | parse_and_run_string(s); | 7347 | parse_and_run_string(s); |
7238 | _exit(G.last_exitcode); | 7348 | _exit(G.last_exitcode); |
7239 | # else | 7349 | # else |
@@ -8014,28 +8124,65 @@ static void execvp_or_die(char **argv) | |||
8014 | } | 8124 | } |
8015 | 8125 | ||
8016 | #if ENABLE_HUSH_MODE_X | 8126 | #if ENABLE_HUSH_MODE_X |
8127 | static void x_mode_print_optionally_squoted(const char *str) | ||
8128 | { | ||
8129 | unsigned len; | ||
8130 | const char *cp; | ||
8131 | |||
8132 | cp = str; | ||
8133 | |||
8134 | /* the set of chars which-cause-string-to-be-squoted mimics bash */ | ||
8135 | /* test a char with: bash -c 'set -x; echo "CH"' */ | ||
8136 | if (str[strcspn(str, "\\\"'`$(){}[]<>;#&|~*?!^" | ||
8137 | " " "\001\002\003\004\005\006\007" | ||
8138 | "\010\011\012\013\014\015\016\017" | ||
8139 | "\020\021\022\023\024\025\026\027" | ||
8140 | "\030\031\032\033\034\035\036\037" | ||
8141 | ) | ||
8142 | ] == '\0' | ||
8143 | ) { | ||
8144 | /* string has no special chars */ | ||
8145 | x_mode_addstr(str); | ||
8146 | return; | ||
8147 | } | ||
8148 | |||
8149 | cp = str; | ||
8150 | for (;;) { | ||
8151 | /* print '....' up to EOL or first squote */ | ||
8152 | len = (int)(strchrnul(cp, '\'') - cp); | ||
8153 | if (len != 0) { | ||
8154 | x_mode_addchr('\''); | ||
8155 | x_mode_addblock(cp, len); | ||
8156 | x_mode_addchr('\''); | ||
8157 | cp += len; | ||
8158 | } | ||
8159 | if (*cp == '\0') | ||
8160 | break; | ||
8161 | /* string contains squote(s), print them as \' */ | ||
8162 | x_mode_addchr('\\'); | ||
8163 | x_mode_addchr('\''); | ||
8164 | cp++; | ||
8165 | } | ||
8166 | } | ||
8017 | static void dump_cmd_in_x_mode(char **argv) | 8167 | static void dump_cmd_in_x_mode(char **argv) |
8018 | { | 8168 | { |
8019 | if (G_x_mode && argv) { | 8169 | if (G_x_mode && argv) { |
8020 | /* We want to output the line in one write op */ | 8170 | unsigned n; |
8021 | char *buf, *p; | ||
8022 | int len; | ||
8023 | int n; | ||
8024 | 8171 | ||
8025 | len = 3; | 8172 | /* "+[+++...][ cmd...]\n\0" */ |
8173 | x_mode_prefix(); | ||
8026 | n = 0; | 8174 | n = 0; |
8027 | while (argv[n]) | 8175 | while (argv[n]) { |
8028 | len += strlen(argv[n++]) + 1; | 8176 | x_mode_addchr(' '); |
8029 | buf = xmalloc(len); | 8177 | if (argv[n][0] == '\0') { |
8030 | buf[0] = '+'; | 8178 | x_mode_addchr('\''); |
8031 | p = buf + 1; | 8179 | x_mode_addchr('\''); |
8032 | n = 0; | 8180 | } else { |
8033 | while (argv[n]) | 8181 | x_mode_print_optionally_squoted(argv[n]); |
8034 | p += sprintf(p, " %s", argv[n++]); | 8182 | } |
8035 | *p++ = '\n'; | 8183 | n++; |
8036 | *p = '\0'; | 8184 | } |
8037 | fputs(buf, stderr); | 8185 | x_mode_flush(); |
8038 | free(buf); | ||
8039 | } | 8186 | } |
8040 | } | 8187 | } |
8041 | #else | 8188 | #else |
@@ -8821,16 +8968,25 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
8821 | restore_redirects(squirrel); | 8968 | restore_redirects(squirrel); |
8822 | 8969 | ||
8823 | /* Set shell variables */ | 8970 | /* Set shell variables */ |
8824 | if (G_x_mode) | ||
8825 | bb_putchar_stderr('+'); | ||
8826 | i = 0; | 8971 | i = 0; |
8827 | while (i < command->assignment_cnt) { | 8972 | while (i < command->assignment_cnt) { |
8828 | char *p = expand_string_to_string(argv[i], | 8973 | char *p = expand_string_to_string(argv[i], |
8829 | EXP_FLAG_ESC_GLOB_CHARS, | 8974 | EXP_FLAG_ESC_GLOB_CHARS, |
8830 | /*unbackslash:*/ 1 | 8975 | /*unbackslash:*/ 1 |
8831 | ); | 8976 | ); |
8832 | if (G_x_mode) | 8977 | #if ENABLE_HUSH_MODE_X |
8833 | fprintf(stderr, " %s", p); | 8978 | if (G_x_mode) { |
8979 | char *eq; | ||
8980 | if (i == 0) | ||
8981 | x_mode_prefix(); | ||
8982 | x_mode_addchr(' '); | ||
8983 | eq = strchrnul(p, '='); | ||
8984 | if (*eq) eq++; | ||
8985 | x_mode_addblock(p, (eq - p)); | ||
8986 | x_mode_print_optionally_squoted(eq); | ||
8987 | x_mode_flush(); | ||
8988 | } | ||
8989 | #endif | ||
8834 | debug_printf_env("set shell var:'%s'->'%s'\n", *argv, p); | 8990 | debug_printf_env("set shell var:'%s'->'%s'\n", *argv, p); |
8835 | if (set_local_var(p, /*flag:*/ 0)) { | 8991 | if (set_local_var(p, /*flag:*/ 0)) { |
8836 | /* assignment to readonly var / putenv error? */ | 8992 | /* assignment to readonly var / putenv error? */ |
@@ -8838,8 +8994,6 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
8838 | } | 8994 | } |
8839 | i++; | 8995 | i++; |
8840 | } | 8996 | } |
8841 | if (G_x_mode) | ||
8842 | bb_putchar_stderr('\n'); | ||
8843 | /* Redirect error sets $? to 1. Otherwise, | 8997 | /* Redirect error sets $? to 1. Otherwise, |
8844 | * if evaluating assignment value set $?, retain it. | 8998 | * if evaluating assignment value set $?, retain it. |
8845 | * Else, clear $?: | 8999 | * Else, clear $?: |
@@ -9288,11 +9442,11 @@ static int run_list(struct pipe *pi) | |||
9288 | }; /* argv list with one element: "$@" */ | 9442 | }; /* argv list with one element: "$@" */ |
9289 | char **vals; | 9443 | char **vals; |
9290 | 9444 | ||
9445 | G.last_exitcode = rcode = EXIT_SUCCESS; | ||
9291 | vals = (char**)encoded_dollar_at_argv; | 9446 | vals = (char**)encoded_dollar_at_argv; |
9292 | if (pi->next->res_word == RES_IN) { | 9447 | if (pi->next->res_word == RES_IN) { |
9293 | /* if no variable values after "in" we skip "for" */ | 9448 | /* if no variable values after "in" we skip "for" */ |
9294 | if (!pi->next->cmds[0].argv) { | 9449 | if (!pi->next->cmds[0].argv) { |
9295 | G.last_exitcode = rcode = EXIT_SUCCESS; | ||
9296 | debug_printf_exec(": null FOR: exitcode EXIT_SUCCESS\n"); | 9450 | debug_printf_exec(": null FOR: exitcode EXIT_SUCCESS\n"); |
9297 | break; | 9451 | break; |
9298 | } | 9452 | } |
@@ -9626,6 +9780,7 @@ static int set_mode(int state, char mode, const char *o_opt) | |||
9626 | break; | 9780 | break; |
9627 | case 'x': | 9781 | case 'x': |
9628 | IF_HUSH_MODE_X(G_x_mode = state;) | 9782 | IF_HUSH_MODE_X(G_x_mode = state;) |
9783 | IF_HUSH_MODE_X(if (G.x_mode_fd <= 0) G.x_mode_fd = dup_CLOEXEC(2, 10);) | ||
9629 | break; | 9784 | break; |
9630 | case 'o': | 9785 | case 'o': |
9631 | if (!o_opt) { | 9786 | if (!o_opt) { |
@@ -9731,6 +9886,10 @@ int hush_main(int argc, char **argv) | |||
9731 | uname(&uts); | 9886 | uname(&uts); |
9732 | set_local_var_from_halves("HOSTNAME", uts.nodename); | 9887 | set_local_var_from_halves("HOSTNAME", uts.nodename); |
9733 | } | 9888 | } |
9889 | #endif | ||
9890 | /* IFS is not inherited from the parent environment */ | ||
9891 | set_local_var_from_halves("IFS", defifs); | ||
9892 | |||
9734 | /* bash also exports SHLVL and _, | 9893 | /* bash also exports SHLVL and _, |
9735 | * and sets (but doesn't export) the following variables: | 9894 | * and sets (but doesn't export) the following variables: |
9736 | * BASH=/bin/bash | 9895 | * BASH=/bin/bash |
@@ -9761,10 +9920,8 @@ int hush_main(int argc, char **argv) | |||
9761 | * TERM=dumb | 9920 | * TERM=dumb |
9762 | * OPTERR=1 | 9921 | * OPTERR=1 |
9763 | * OPTIND=1 | 9922 | * OPTIND=1 |
9764 | * IFS=$' \t\n' | ||
9765 | * PS4='+ ' | 9923 | * PS4='+ ' |
9766 | */ | 9924 | */ |
9767 | #endif | ||
9768 | 9925 | ||
9769 | #if ENABLE_HUSH_LINENO_VAR | 9926 | #if ENABLE_HUSH_LINENO_VAR |
9770 | if (ENABLE_HUSH_LINENO_VAR) { | 9927 | if (ENABLE_HUSH_LINENO_VAR) { |
@@ -10226,6 +10383,8 @@ static int FAST_FUNC builtin_eval(char **argv) | |||
10226 | if (!argv[0]) | 10383 | if (!argv[0]) |
10227 | return EXIT_SUCCESS; | 10384 | return EXIT_SUCCESS; |
10228 | 10385 | ||
10386 | IF_HUSH_MODE_X(G.x_mode_depth++;) | ||
10387 | //bb_error_msg("%s: ++x_mode_depth=%d", __func__, G.x_mode_depth); | ||
10229 | if (!argv[1]) { | 10388 | if (!argv[1]) { |
10230 | /* bash: | 10389 | /* bash: |
10231 | * eval "echo Hi; done" ("done" is syntax error): | 10390 | * eval "echo Hi; done" ("done" is syntax error): |
@@ -10255,6 +10414,8 @@ static int FAST_FUNC builtin_eval(char **argv) | |||
10255 | parse_and_run_string(str); | 10414 | parse_and_run_string(str); |
10256 | free(str); | 10415 | free(str); |
10257 | } | 10416 | } |
10417 | IF_HUSH_MODE_X(G.x_mode_depth--;) | ||
10418 | //bb_error_msg("%s: --x_mode_depth=%d", __func__, G.x_mode_depth); | ||
10258 | return G.last_exitcode; | 10419 | return G.last_exitcode; |
10259 | } | 10420 | } |
10260 | 10421 | ||
@@ -10374,40 +10535,29 @@ static int FAST_FUNC builtin_type(char **argv) | |||
10374 | static int FAST_FUNC builtin_read(char **argv) | 10535 | static int FAST_FUNC builtin_read(char **argv) |
10375 | { | 10536 | { |
10376 | const char *r; | 10537 | const char *r; |
10377 | char *opt_n = NULL; | 10538 | struct builtin_read_params params; |
10378 | char *opt_p = NULL; | 10539 | |
10379 | char *opt_t = NULL; | 10540 | memset(¶ms, 0, sizeof(params)); |
10380 | char *opt_u = NULL; | ||
10381 | char *opt_d = NULL; /* optimized out if !BASH */ | ||
10382 | const char *ifs; | ||
10383 | int read_flags; | ||
10384 | 10541 | ||
10385 | /* "!": do not abort on errors. | 10542 | /* "!": do not abort on errors. |
10386 | * Option string must start with "sr" to match BUILTIN_READ_xxx | 10543 | * Option string must start with "sr" to match BUILTIN_READ_xxx |
10387 | */ | 10544 | */ |
10388 | read_flags = getopt32(argv, | 10545 | params.read_flags = getopt32(argv, |
10389 | #if BASH_READ_D | 10546 | #if BASH_READ_D |
10390 | "!srn:p:t:u:d:", &opt_n, &opt_p, &opt_t, &opt_u, &opt_d | 10547 | "!srn:p:t:u:d:", ¶ms.opt_n, ¶ms.opt_p, ¶ms.opt_t, ¶ms.opt_u, ¶ms.opt_d |
10391 | #else | 10548 | #else |
10392 | "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u | 10549 | "!srn:p:t:u:", ¶ms.opt_n, ¶ms.opt_p, ¶ms.opt_t, ¶ms.opt_u |
10393 | #endif | 10550 | #endif |
10394 | ); | 10551 | ); |
10395 | if (read_flags == (uint32_t)-1) | 10552 | if ((uint32_t)params.read_flags == (uint32_t)-1) |
10396 | return EXIT_FAILURE; | 10553 | return EXIT_FAILURE; |
10397 | argv += optind; | 10554 | argv += optind; |
10398 | ifs = get_local_var_value("IFS"); /* can be NULL */ | 10555 | params.argv = argv; |
10556 | params.setvar = set_local_var_from_halves; | ||
10557 | params.ifs = get_local_var_value("IFS"); /* can be NULL */ | ||
10399 | 10558 | ||
10400 | again: | 10559 | again: |
10401 | r = shell_builtin_read(set_local_var_from_halves, | 10560 | r = shell_builtin_read(¶ms); |
10402 | argv, | ||
10403 | ifs, | ||
10404 | read_flags, | ||
10405 | opt_n, | ||
10406 | opt_p, | ||
10407 | opt_t, | ||
10408 | opt_u, | ||
10409 | opt_d | ||
10410 | ); | ||
10411 | 10561 | ||
10412 | if ((uintptr_t)r == 1 && errno == EINTR) { | 10562 | if ((uintptr_t)r == 1 && errno == EINTR) { |
10413 | unsigned sig = check_and_run_traps(); | 10563 | unsigned sig = check_and_run_traps(); |