aboutsummaryrefslogtreecommitdiff
path: root/shell/hush.c
diff options
context:
space:
mode:
Diffstat (limited to 'shell/hush.c')
-rw-r--r--shell/hush.c340
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
2400static 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 */
2380static void set_pwd_var(unsigned flag) 2407static 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)
2427static 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
2921static 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
2904static void nommu_addchr(o_string *o, int ch) 2927static 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
2913static void o_addstr_with_NUL(o_string *o, const char *str) 2936#if ENABLE_HUSH_MODE_X
2937static 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}
2941static void x_mode_addstr(const char *str)
2942{
2943 o_addstr(&G.x_mode_buf, str);
2916} 2944}
2945static void x_mode_addblock(const char *str, int len)
2946{
2947 o_addblock(&G.x_mode_buf, str, len);
2948}
2949static void x_mode_prefix(void)
2950{
2951 int n = G.x_mode_depth;
2952 do x_mode_addchr('+'); while (--n >= 0);
2953}
2954static 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
5963static 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 */
5970static int encode_then_append_var_plusminus(o_string *output, int n, 6085static 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
8127static 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}
8017static void dump_cmd_in_x_mode(char **argv) 8167static 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)
10374static int FAST_FUNC builtin_read(char **argv) 10535static 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(&params, 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:", &params.opt_n, &params.opt_p, &params.opt_t, &params.opt_u, &params.opt_d
10391#else 10548#else
10392 "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u 10549 "!srn:p:t:u:", &params.opt_n, &params.opt_p, &params.opt_t, &params.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(&params);
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();