aboutsummaryrefslogtreecommitdiff
path: root/shell/hush.c
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2019-05-27 11:56:52 +0100
committerRon Yorston <rmy@pobox.com>2019-05-27 11:56:52 +0100
commita61949401890cbb33a9d6c4571b51c53460ad438 (patch)
tree64dedaddb89896d5b1670a421af123670ca2120b /shell/hush.c
parent03a7b173605a890e1db5177ecd5b8dd591081c41 (diff)
parentbcb1fc3e6ca6fe902610f507eaf9b0b58a5c583a (diff)
downloadbusybox-w32-a61949401890cbb33a9d6c4571b51c53460ad438.tar.gz
busybox-w32-a61949401890cbb33a9d6c4571b51c53460ad438.tar.bz2
busybox-w32-a61949401890cbb33a9d6c4571b51c53460ad438.zip
Merge branch 'busybox' into merge
Diffstat (limited to 'shell/hush.c')
-rw-r--r--shell/hush.c241
1 files changed, 123 insertions, 118 deletions
diff --git a/shell/hush.c b/shell/hush.c
index b3ae73b9b..4b08232a4 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -466,9 +466,9 @@
466 466
467#define JOB_STATUS_FORMAT "[%u] %-22s %.40s\n" 467#define JOB_STATUS_FORMAT "[%u] %-22s %.40s\n"
468 468
469#define _SPECIAL_VARS_STR "_*@$!?#" 469#define _SPECIAL_VARS_STR "_*@$!?#-"
470#define SPECIAL_VARS_STR ("_*@$!?#" + 1) 470#define SPECIAL_VARS_STR ("_*@$!?#-" + 1)
471#define NUMERIC_SPECVARS_STR ("_*@$!?#" + 3) 471#define NUMERIC_SPECVARS_STR ("_*@$!?#-" + 3)
472#if BASH_PATTERN_SUBST 472#if BASH_PATTERN_SUBST
473/* Support / and // replace ops */ 473/* Support / and // replace ops */
474/* Note that // is stored as \ in "encoded" string representation */ 474/* Note that // is stored as \ in "encoded" string representation */
@@ -854,8 +854,7 @@ struct globals {
854 /* 'interactive_fd' is a fd# open to ctty, if we have one 854 /* 'interactive_fd' is a fd# open to ctty, if we have one
855 * _AND_ if we decided to act interactively */ 855 * _AND_ if we decided to act interactively */
856 int interactive_fd; 856 int interactive_fd;
857 const char *PS1; 857 IF_NOT_FEATURE_EDITING_FANCY_PROMPT(char *PS1;)
858 IF_FEATURE_EDITING_FANCY_PROMPT(const char *PS2;)
859# define G_interactive_fd (G.interactive_fd) 858# define G_interactive_fd (G.interactive_fd)
860#else 859#else
861# define G_interactive_fd 0 860# define G_interactive_fd 0
@@ -903,6 +902,7 @@ struct globals {
903#else 902#else
904# define G_x_mode 0 903# define G_x_mode 0
905#endif 904#endif
905 char opt_s;
906#if ENABLE_HUSH_INTERACTIVE 906#if ENABLE_HUSH_INTERACTIVE
907 smallint promptmode; /* 0: PS1, 1: PS2 */ 907 smallint promptmode; /* 0: PS1, 1: PS2 */
908#endif 908#endif
@@ -968,8 +968,8 @@ struct globals {
968 smallint we_have_children; 968 smallint we_have_children;
969#endif 969#endif
970#if ENABLE_HUSH_LINENO_VAR 970#if ENABLE_HUSH_LINENO_VAR
971 unsigned lineno; 971 unsigned parse_lineno;
972 char *lineno_var; 972 unsigned execute_lineno;
973#endif 973#endif
974 HFILE *HFILE_list; 974 HFILE *HFILE_list;
975 /* Which signals have non-DFL handler (even with no traps set)? 975 /* Which signals have non-DFL handler (even with no traps set)?
@@ -1009,6 +1009,7 @@ struct globals {
1009 int debug_indent; 1009 int debug_indent;
1010#endif 1010#endif
1011 struct sigaction sa; 1011 struct sigaction sa;
1012 char optstring_buf[sizeof("eixs")];
1012#if BASH_EPOCH_VARS 1013#if BASH_EPOCH_VARS
1013 char epoch_buf[sizeof("%lu.nnnnnn") + sizeof(long)*3]; 1014 char epoch_buf[sizeof("%lu.nnnnnn") + sizeof(long)*3];
1014#endif 1015#endif
@@ -1448,13 +1449,6 @@ static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch)
1448#endif 1449#endif
1449 1450
1450 1451
1451#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1452static void cmdedit_update_prompt(void);
1453#else
1454# define cmdedit_update_prompt() ((void)0)
1455#endif
1456
1457
1458/* Utility functions 1452/* Utility functions
1459 */ 1453 */
1460/* Replace each \x with x in place, return ptr past NUL. */ 1454/* Replace each \x with x in place, return ptr past NUL. */
@@ -2039,7 +2033,8 @@ static sighandler_t pick_sighandler(unsigned sig)
2039static void hush_exit(int exitcode) 2033static void hush_exit(int exitcode)
2040{ 2034{
2041#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 2035#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
2042 save_history(G.line_input_state); 2036 if (G.line_input_state)
2037 save_history(G.line_input_state);
2043#endif 2038#endif
2044 2039
2045 fflush_all(); 2040 fflush_all();
@@ -2229,6 +2224,10 @@ static const char* FAST_FUNC get_local_var_value(const char *name)
2229 if (strcmp(name, "RANDOM") == 0) 2224 if (strcmp(name, "RANDOM") == 0)
2230 return utoa(next_random(&G.random_gen)); 2225 return utoa(next_random(&G.random_gen));
2231#endif 2226#endif
2227#if ENABLE_HUSH_LINENO_VAR
2228 if (strcmp(name, "LINENO") == 0)
2229 return utoa(G.execute_lineno);
2230#endif
2232#if BASH_EPOCH_VARS 2231#if BASH_EPOCH_VARS
2233 { 2232 {
2234 const char *fmt = NULL; 2233 const char *fmt = NULL;
@@ -2248,32 +2247,20 @@ static const char* FAST_FUNC get_local_var_value(const char *name)
2248 return NULL; 2247 return NULL;
2249} 2248}
2250 2249
2250#if ENABLE_HUSH_GETOPTS
2251static void handle_changed_special_names(const char *name, unsigned name_len) 2251static void handle_changed_special_names(const char *name, unsigned name_len)
2252{ 2252{
2253 if (ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT 2253 if (name_len == 6) {
2254 && name_len == 3 && name[0] == 'P' && name[1] == 'S'
2255 ) {
2256 cmdedit_update_prompt();
2257 return;
2258 }
2259
2260 if ((ENABLE_HUSH_LINENO_VAR || ENABLE_HUSH_GETOPTS)
2261 && name_len == 6
2262 ) {
2263#if ENABLE_HUSH_LINENO_VAR
2264 if (strncmp(name, "LINENO", 6) == 0) {
2265 G.lineno_var = NULL;
2266 return;
2267 }
2268#endif
2269#if ENABLE_HUSH_GETOPTS
2270 if (strncmp(name, "OPTIND", 6) == 0) { 2254 if (strncmp(name, "OPTIND", 6) == 0) {
2271 G.getopt_count = 0; 2255 G.getopt_count = 0;
2272 return; 2256 return;
2273 } 2257 }
2274#endif
2275 } 2258 }
2276} 2259}
2260#else
2261/* Do not even bother evaluating arguments */
2262# define handle_changed_special_names(...) ((void)0)
2263#endif
2277 2264
2278/* str holds "NAME=VAL" and is expected to be malloced. 2265/* str holds "NAME=VAL" and is expected to be malloced.
2279 * We take ownership of it. 2266 * We take ownership of it.
@@ -2289,6 +2276,7 @@ static int set_local_var(char *str, unsigned flags)
2289 char *free_me = NULL; 2276 char *free_me = NULL;
2290 char *eq_sign; 2277 char *eq_sign;
2291 int name_len; 2278 int name_len;
2279 int retval;
2292 unsigned local_lvl = (flags >> SETFLAG_VARLVL_SHIFT); 2280 unsigned local_lvl = (flags >> SETFLAG_VARLVL_SHIFT);
2293 2281
2294 eq_sign = strchr(str, '='); 2282 eq_sign = strchr(str, '=');
@@ -2402,24 +2390,24 @@ static int set_local_var(char *str, unsigned flags)
2402#endif 2390#endif
2403 if (flags & SETFLAG_EXPORT) 2391 if (flags & SETFLAG_EXPORT)
2404 cur->flg_export = 1; 2392 cur->flg_export = 1;
2393 retval = 0;
2405 if (cur->flg_export) { 2394 if (cur->flg_export) {
2406 if (flags & SETFLAG_UNEXPORT) { 2395 if (flags & SETFLAG_UNEXPORT) {
2407 cur->flg_export = 0; 2396 cur->flg_export = 0;
2408 /* unsetenv was already done */ 2397 /* unsetenv was already done */
2409 } else { 2398 } else {
2410 int i;
2411 debug_printf_env("%s: putenv '%s'/%u\n", __func__, cur->varstr, cur->var_nest_level); 2399 debug_printf_env("%s: putenv '%s'/%u\n", __func__, cur->varstr, cur->var_nest_level);
2412 i = putenv(cur->varstr); 2400 retval = putenv(cur->varstr);
2413 /* only now we can free old exported malloced string */ 2401 /* fall through to "free(free_me)" -
2414 free(free_me); 2402 * only now we can free old exported malloced string
2415 return i; 2403 */
2416 } 2404 }
2417 } 2405 }
2418 free(free_me); 2406 free(free_me);
2419 2407
2420 handle_changed_special_names(cur->varstr, name_len - 1); 2408 handle_changed_special_names(cur->varstr, name_len - 1);
2421 2409
2422 return 0; 2410 return retval;
2423} 2411}
2424 2412
2425static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) 2413static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val)
@@ -2462,7 +2450,7 @@ static int unset_local_var_len(const char *name, int name_len)
2462 cur_pp = &cur->next; 2450 cur_pp = &cur->next;
2463 } 2451 }
2464 2452
2465 /* Handle "unset PS1" et al even if did not find the variable to unset */ 2453 /* Handle "unset LINENO" et al even if did not find the variable to unset */
2466 handle_changed_special_names(name, name_len); 2454 handle_changed_special_names(name, name_len);
2467 2455
2468 return EXIT_SUCCESS; 2456 return EXIT_SUCCESS;
@@ -2581,36 +2569,27 @@ static void reinit_unicode_for_hush(void)
2581 * \ 2569 * \
2582 * It exercises a lot of corner cases. 2570 * It exercises a lot of corner cases.
2583 */ 2571 */
2584# if ENABLE_FEATURE_EDITING_FANCY_PROMPT
2585static void cmdedit_update_prompt(void)
2586{
2587 G.PS1 = get_local_var_value("PS1");
2588 if (G.PS1 == NULL)
2589 G.PS1 = "";
2590 G.PS2 = get_local_var_value("PS2");
2591 if (G.PS2 == NULL)
2592 G.PS2 = "";
2593}
2594# endif
2595static const char *setup_prompt_string(void) 2572static const char *setup_prompt_string(void)
2596{ 2573{
2597 const char *prompt_str; 2574 const char *prompt_str;
2598 2575
2599 debug_printf_prompt("%s promptmode:%d\n", __func__, G.promptmode); 2576 debug_printf_prompt("%s promptmode:%d\n", __func__, G.promptmode);
2600 2577
2601 IF_FEATURE_EDITING_FANCY_PROMPT( prompt_str = G.PS2;) 2578# if ENABLE_FEATURE_EDITING_FANCY_PROMPT
2602 IF_NOT_FEATURE_EDITING_FANCY_PROMPT(prompt_str = "> ";) 2579 prompt_str = get_local_var_value(G.promptmode == 0 ? "PS1" : "PS2");
2580 if (!prompt_str)
2581 prompt_str = "";
2582# else
2583 prompt_str = "> "; /* if PS2, else... */
2603 if (G.promptmode == 0) { /* PS1 */ 2584 if (G.promptmode == 0) { /* PS1 */
2604 if (!ENABLE_FEATURE_EDITING_FANCY_PROMPT) { 2585 /* No fancy prompts supported, (re)generate "CURDIR $ " by hand */
2605 /* No fancy prompts supported, (re)generate "CURDIR $ " by hand */ 2586 free(G.PS1);
2606 free((char*)G.PS1); 2587 /* bash uses $PWD value, even if it is set by user.
2607 /* bash uses $PWD value, even if it is set by user. 2588 * It uses current dir only if PWD is unset.
2608 * It uses current dir only if PWD is unset. 2589 * We always use current dir. */
2609 * We always use current dir. */ 2590 G.PS1 = xasprintf("%s %c ", get_cwd(0), (geteuid() != 0) ? '$' : '#');
2610 G.PS1 = xasprintf("%s %c ", get_cwd(0), (geteuid() != 0) ? '$' : '#');
2611 }
2612 prompt_str = G.PS1;
2613 } 2591 }
2592# endif
2614 debug_printf("prompt_str '%s'\n", prompt_str); 2593 debug_printf("prompt_str '%s'\n", prompt_str);
2615 return prompt_str; 2594 return prompt_str;
2616} 2595}
@@ -2747,8 +2726,8 @@ static int i_getch(struct in_str *i)
2747 i->last_char = ch; 2726 i->last_char = ch;
2748#if ENABLE_HUSH_LINENO_VAR 2727#if ENABLE_HUSH_LINENO_VAR
2749 if (ch == '\n') { 2728 if (ch == '\n') {
2750 G.lineno++; 2729 G.parse_lineno++;
2751 debug_printf_parse("G.lineno++ = %u\n", G.lineno); 2730 debug_printf_parse("G.parse_lineno++ = %u\n", G.parse_lineno);
2752 } 2731 }
2753#endif 2732#endif
2754 return ch; 2733 return ch;
@@ -3750,8 +3729,8 @@ static int done_command(struct parse_context *ctx)
3750 clear_and_ret: 3729 clear_and_ret:
3751 memset(command, 0, sizeof(*command)); 3730 memset(command, 0, sizeof(*command));
3752#if ENABLE_HUSH_LINENO_VAR 3731#if ENABLE_HUSH_LINENO_VAR
3753 command->lineno = G.lineno; 3732 command->lineno = G.parse_lineno;
3754 debug_printf_parse("command->lineno = G.lineno (%u)\n", G.lineno); 3733 debug_printf_parse("command->lineno = G.parse_lineno (%u)\n", G.parse_lineno);
3755#endif 3734#endif
3756 return pi->num_cmds; /* used only for 0/nonzero check */ 3735 return pi->num_cmds; /* used only for 0/nonzero check */
3757} 3736}
@@ -4198,7 +4177,7 @@ static int done_word(struct parse_context *ctx)
4198#if ENABLE_HUSH_LOOPS 4177#if ENABLE_HUSH_LOOPS
4199 if (ctx->ctx_res_w == RES_FOR) { 4178 if (ctx->ctx_res_w == RES_FOR) {
4200 if (ctx->word.has_quoted_part 4179 if (ctx->word.has_quoted_part
4201 || !is_well_formed_var_name(command->argv[0], '\0') 4180 || endofname(command->argv[0])[0] != '\0'
4202 ) { 4181 ) {
4203 /* bash says just "not a valid identifier" */ 4182 /* bash says just "not a valid identifier" */
4204 syntax_error("not a valid identifier in for"); 4183 syntax_error("not a valid identifier in for");
@@ -4912,6 +4891,7 @@ static int parse_dollar(o_string *as_string,
4912 case '#': /* number of args */ 4891 case '#': /* number of args */
4913 case '*': /* args */ 4892 case '*': /* args */
4914 case '@': /* args */ 4893 case '@': /* args */
4894 case '-': /* $- option flags set by set builtin or shell options (-i etc) */
4915 goto make_one_char_var; 4895 goto make_one_char_var;
4916 case '{': { 4896 case '{': {
4917 char len_single_ch; 4897 char len_single_ch;
@@ -5086,11 +5066,10 @@ static int parse_dollar(o_string *as_string,
5086 case '_': 5066 case '_':
5087 goto make_var; 5067 goto make_var;
5088#if 0 5068#if 0
5089 /* TODO: $_ and $-: */ 5069 /* TODO: $_: */
5090 /* $_ Shell or shell script name; or last argument of last command 5070 /* $_ Shell or shell script name; or last argument of last command
5091 * (if last command wasn't a pipe; if it was, bash sets $_ to ""); 5071 * (if last command wasn't a pipe; if it was, bash sets $_ to "");
5092 * but in command's env, set to full pathname used to invoke it */ 5072 * but in command's env, set to full pathname used to invoke it */
5093 /* $- Option flags set by set builtin or shell options (-i etc) */
5094 ch = i_getch(input); 5073 ch = i_getch(input);
5095 nommu_addchr(as_string, ch); 5074 nommu_addchr(as_string, ch);
5096 ch = i_peek_and_eat_bkslash_nl(input); 5075 ch = i_peek_and_eat_bkslash_nl(input);
@@ -5372,7 +5351,7 @@ static struct pipe *parse_stream(char **pstring,
5372 if ((ctx.is_assignment == MAYBE_ASSIGNMENT 5351 if ((ctx.is_assignment == MAYBE_ASSIGNMENT
5373 || ctx.is_assignment == WORD_IS_KEYWORD) 5352 || ctx.is_assignment == WORD_IS_KEYWORD)
5374 && ch == '=' 5353 && ch == '='
5375 && is_well_formed_var_name(ctx.word.data, '=') 5354 && endofname(ctx.word.data)[0] == '='
5376 ) { 5355 ) {
5377 ctx.is_assignment = DEFINITELY_ASSIGNMENT; 5356 ctx.is_assignment = DEFINITELY_ASSIGNMENT;
5378 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); 5357 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
@@ -6119,6 +6098,12 @@ static int encode_then_append_var_plusminus(o_string *output, int n,
6119 /* string has no special chars 6098 /* string has no special chars
6120 * && string has no $IFS chars 6099 * && string has no $IFS chars
6121 */ 6100 */
6101 if (dquoted) {
6102 /* Prints 1 (quoted expansion is a "" word, not nothing):
6103 * set -- "${notexist-}"; echo $#
6104 */
6105 output->has_quoted_part = 1;
6106 }
6122 return expand_vars_to_list(output, n, str); 6107 return expand_vars_to_list(output, n, str);
6123 } 6108 }
6124 6109
@@ -6415,6 +6400,26 @@ static NOINLINE int expand_one_var(o_string *output, int n,
6415 case '#': /* argc */ 6400 case '#': /* argc */
6416 val = utoa(G.global_argc ? G.global_argc-1 : 0); 6401 val = utoa(G.global_argc ? G.global_argc-1 : 0);
6417 break; 6402 break;
6403 case '-': { /* active options */
6404 /* Check set_mode() to see what option chars we support */
6405 char *cp;
6406 val = cp = G.optstring_buf;
6407 if (G.o_opt[OPT_O_ERREXIT])
6408 *cp++ = 'e';
6409 if (G_interactive_fd)
6410 *cp++ = 'i';
6411 if (G_x_mode)
6412 *cp++ = 'x';
6413 /* If G.o_opt[OPT_O_NOEXEC] is true,
6414 * commands read but are not executed,
6415 * so $- can not execute too, 'n' is never seen in $-.
6416 */
6417 if (G.opt_s)
6418 *cp++ = 's';
6419//TODO: show 'c' if executed via "hush -c 'CMDS'" (bash only, not ash)
6420 *cp = '\0';
6421 break;
6422 }
6418 default: 6423 default:
6419 val = get_local_var_value(var); 6424 val = get_local_var_value(var);
6420 } 6425 }
@@ -7275,22 +7280,22 @@ static void parse_and_run_stream(struct in_str *inp, int end_trigger)
7275static void parse_and_run_string(const char *s) 7280static void parse_and_run_string(const char *s)
7276{ 7281{
7277 struct in_str input; 7282 struct in_str input;
7278 //IF_HUSH_LINENO_VAR(unsigned sv = G.lineno;) 7283 //IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;)
7279 7284
7280 setup_string_in_str(&input, s); 7285 setup_string_in_str(&input, s);
7281 parse_and_run_stream(&input, '\0'); 7286 parse_and_run_stream(&input, '\0');
7282 //IF_HUSH_LINENO_VAR(G.lineno = sv;) 7287 //IF_HUSH_LINENO_VAR(G.parse_lineno = sv;)
7283} 7288}
7284 7289
7285static void parse_and_run_file(HFILE *fp) 7290static void parse_and_run_file(HFILE *fp)
7286{ 7291{
7287 struct in_str input; 7292 struct in_str input;
7288 IF_HUSH_LINENO_VAR(unsigned sv = G.lineno;) 7293 IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;)
7289 7294
7290 IF_HUSH_LINENO_VAR(G.lineno = 1;) 7295 IF_HUSH_LINENO_VAR(G.parse_lineno = 1;)
7291 setup_file_in_str(&input, fp); 7296 setup_file_in_str(&input, fp);
7292 parse_and_run_stream(&input, ';'); 7297 parse_and_run_stream(&input, ';');
7293 IF_HUSH_LINENO_VAR(G.lineno = sv;) 7298 IF_HUSH_LINENO_VAR(G.parse_lineno = sv;)
7294} 7299}
7295 7300
7296#if ENABLE_HUSH_TICK 7301#if ENABLE_HUSH_TICK
@@ -7598,10 +7603,10 @@ static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp)
7598 avoid_fd = 9; 7603 avoid_fd = 9;
7599 7604
7600#if ENABLE_HUSH_INTERACTIVE 7605#if ENABLE_HUSH_INTERACTIVE
7601 if (fd == G.interactive_fd) { 7606 if (fd == G_interactive_fd) {
7602 /* Testcase: "ls -l /proc/$$/fd 255>&-" should work */ 7607 /* Testcase: "ls -l /proc/$$/fd 255>&-" should work */
7603 G.interactive_fd = xdup_CLOEXEC_and_close(G.interactive_fd, avoid_fd); 7608 G_interactive_fd = xdup_CLOEXEC_and_close(G_interactive_fd, avoid_fd);
7604 debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G.interactive_fd); 7609 debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G_interactive_fd);
7605 return 1; /* "we closed fd" */ 7610 return 1; /* "we closed fd" */
7606 } 7611 }
7607#endif 7612#endif
@@ -7677,7 +7682,7 @@ static void restore_redirects(struct squirrel *sq)
7677 free(sq); 7682 free(sq);
7678 } 7683 }
7679 7684
7680 /* If moved, G.interactive_fd stays on new fd, not restoring it */ 7685 /* If moved, G_interactive_fd stays on new fd, not restoring it */
7681} 7686}
7682 7687
7683#if ENABLE_FEATURE_SH_STANDALONE && BB_MMU 7688#if ENABLE_FEATURE_SH_STANDALONE && BB_MMU
@@ -7694,7 +7699,7 @@ static int internally_opened_fd(int fd, struct squirrel *sq)
7694 int i; 7699 int i;
7695 7700
7696#if ENABLE_HUSH_INTERACTIVE 7701#if ENABLE_HUSH_INTERACTIVE
7697 if (fd == G.interactive_fd) 7702 if (fd == G_interactive_fd)
7698 return 1; 7703 return 1;
7699#endif 7704#endif
7700 /* If this one of script's fds? */ 7705 /* If this one of script's fds? */
@@ -8989,8 +8994,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
8989 struct variable *old_vars; 8994 struct variable *old_vars;
8990 8995
8991#if ENABLE_HUSH_LINENO_VAR 8996#if ENABLE_HUSH_LINENO_VAR
8992 if (G.lineno_var) 8997 G.execute_lineno = command->lineno;
8993 strcpy(G.lineno_var + sizeof("LINENO=")-1, utoa(command->lineno));
8994#endif 8998#endif
8995 8999
8996 if (argv[command->assignment_cnt] == NULL) { 9000 if (argv[command->assignment_cnt] == NULL) {
@@ -9223,8 +9227,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
9223 xpiped_pair(pipefds); 9227 xpiped_pair(pipefds);
9224 9228
9225#if ENABLE_HUSH_LINENO_VAR 9229#if ENABLE_HUSH_LINENO_VAR
9226 if (G.lineno_var) 9230 G.execute_lineno = command->lineno;
9227 strcpy(G.lineno_var + sizeof("LINENO=")-1, utoa(command->lineno));
9228#endif 9231#endif
9229 9232
9230 command->pid = BB_MMU ? fork() : vfork(); 9233 command->pid = BB_MMU ? fork() : vfork();
@@ -9908,14 +9911,6 @@ int hush_main(int argc, char **argv)
9908 /* Export PWD */ 9911 /* Export PWD */
9909 set_pwd_var(SETFLAG_EXPORT); 9912 set_pwd_var(SETFLAG_EXPORT);
9910 9913
9911#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT
9912 /* Set (but not export) PS1/2 unless already set */
9913 if (!get_local_var_value("PS1"))
9914 set_local_var_from_halves("PS1", "\\w \\$ ");
9915 if (!get_local_var_value("PS2"))
9916 set_local_var_from_halves("PS2", "> ");
9917#endif
9918
9919#if BASH_HOSTNAME_VAR 9914#if BASH_HOSTNAME_VAR
9920 /* Set (but not export) HOSTNAME unless already set */ 9915 /* Set (but not export) HOSTNAME unless already set */
9921 if (!get_local_var_value("HOSTNAME")) { 9916 if (!get_local_var_value("HOSTNAME")) {
@@ -9927,6 +9922,11 @@ int hush_main(int argc, char **argv)
9927 /* IFS is not inherited from the parent environment */ 9922 /* IFS is not inherited from the parent environment */
9928 set_local_var_from_halves("IFS", defifs); 9923 set_local_var_from_halves("IFS", defifs);
9929 9924
9925 if (!get_local_var_value("PATH"))
9926 set_local_var_from_halves("PATH", bb_default_root_path);
9927
9928 /* PS1/PS2 are set later, if we determine that we are interactive */
9929
9930 /* bash also exports SHLVL and _, 9930 /* bash also exports SHLVL and _,
9931 * and sets (but doesn't export) the following variables: 9931 * and sets (but doesn't export) the following variables:
9932 * BASH=/bin/bash 9932 * BASH=/bin/bash
@@ -9960,21 +9960,7 @@ int hush_main(int argc, char **argv)
9960 * PS4='+ ' 9960 * PS4='+ '
9961 */ 9961 */
9962 9962
9963#if ENABLE_HUSH_LINENO_VAR
9964 if (ENABLE_HUSH_LINENO_VAR) {
9965 char *p = xasprintf("LINENO=%*s", (int)(sizeof(int)*3), "");
9966 set_local_var(p, /*flags*/ 0);
9967 G.lineno_var = p; /* can't assign before set_local_var("LINENO=...") */
9968 }
9969#endif
9970
9971#if ENABLE_FEATURE_EDITING
9972 G.line_input_state = new_line_input_t(FOR_SHELL);
9973#endif
9974
9975 /* Initialize some more globals to non-zero values */ 9963 /* Initialize some more globals to non-zero values */
9976 cmdedit_update_prompt();
9977
9978 die_func = restore_ttypgrp_and__exit; 9964 die_func = restore_ttypgrp_and__exit;
9979 9965
9980 /* Shell is non-interactive at first. We need to call 9966 /* Shell is non-interactive at first. We need to call
@@ -10192,6 +10178,7 @@ int hush_main(int argc, char **argv)
10192#endif 10178#endif
10193 goto final_return; 10179 goto final_return;
10194 } 10180 }
10181 G.opt_s = 1;
10195 10182
10196 /* Up to here, shell was non-interactive. Now it may become one. 10183 /* Up to here, shell was non-interactive. Now it may become one.
10197 * NB: don't forget to (re)run install_special_sighandlers() as needed. 10184 * NB: don't forget to (re)run install_special_sighandlers() as needed.
@@ -10260,6 +10247,9 @@ int hush_main(int argc, char **argv)
10260 } 10247 }
10261 enable_restore_tty_pgrp_on_exit(); 10248 enable_restore_tty_pgrp_on_exit();
10262 10249
10250# if ENABLE_FEATURE_EDITING
10251 G.line_input_state = new_line_input_t(FOR_SHELL);
10252# endif
10263# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0 10253# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0
10264 { 10254 {
10265 const char *hp = get_local_var_value("HISTFILE"); 10255 const char *hp = get_local_var_value("HISTFILE");
@@ -10308,14 +10298,23 @@ int hush_main(int argc, char **argv)
10308 * (--norc turns this off, --rcfile <file> overrides) 10298 * (--norc turns this off, --rcfile <file> overrides)
10309 */ 10299 */
10310 10300
10311 if (!ENABLE_FEATURE_SH_EXTRA_QUIET && G_interactive_fd) { 10301 if (G_interactive_fd) {
10312 /* note: ash and hush share this string */ 10302#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT
10313 printf("\n\n%s %s\n" 10303 /* Set (but not export) PS1/2 unless already set */
10314 IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n") 10304 if (!get_local_var_value("PS1"))
10315 "\n", 10305 set_local_var_from_halves("PS1", "\\w \\$ ");
10316 bb_banner, 10306 if (!get_local_var_value("PS2"))
10317 "hush - the humble shell" 10307 set_local_var_from_halves("PS2", "> ");
10318 ); 10308#endif
10309 if (!ENABLE_FEATURE_SH_EXTRA_QUIET) {
10310 /* note: ash and hush share this string */
10311 printf("\n\n%s %s\n"
10312 IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n")
10313 "\n",
10314 bb_banner,
10315 "hush - the humble shell"
10316 );
10317 }
10319 } 10318 }
10320 10319
10321 parse_and_run_file(hfopen(NULL)); /* stdin */ 10320 parse_and_run_file(hfopen(NULL)); /* stdin */
@@ -10378,7 +10377,8 @@ static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM)
10378#if MAX_HISTORY && ENABLE_FEATURE_EDITING 10377#if MAX_HISTORY && ENABLE_FEATURE_EDITING
10379static int FAST_FUNC builtin_history(char **argv UNUSED_PARAM) 10378static int FAST_FUNC builtin_history(char **argv UNUSED_PARAM)
10380{ 10379{
10381 show_history(G.line_input_state); 10380 if (G.line_input_state)
10381 show_history(G.line_input_state);
10382 return EXIT_SUCCESS; 10382 return EXIT_SUCCESS;
10383} 10383}
10384#endif 10384#endif
@@ -10687,9 +10687,7 @@ static int helper_export_local(char **argv, unsigned flags)
10687{ 10687{
10688 do { 10688 do {
10689 char *name = *argv; 10689 char *name = *argv;
10690 char *name_end = strchrnul(name, '='); 10690 const char *name_end = endofname(name);
10691
10692 /* So far we do not check that name is valid (TODO?) */
10693 10691
10694 if (*name_end == '\0') { 10692 if (*name_end == '\0') {
10695 struct variable *var, **vpp; 10693 struct variable *var, **vpp;
@@ -10747,8 +10745,15 @@ static int helper_export_local(char **argv, unsigned flags)
10747 */ 10745 */
10748 name = xasprintf("%s=", name); 10746 name = xasprintf("%s=", name);
10749 } else { 10747 } else {
10748 if (*name_end != '=') {
10749 bb_error_msg("'%s': bad variable name", name);
10750 /* do not parse following argv[]s: */
10751 return 1;
10752 }
10750 /* (Un)exporting/making local NAME=VALUE */ 10753 /* (Un)exporting/making local NAME=VALUE */
10751 name = xstrdup(name); 10754 name = xstrdup(name);
10755 /* Testcase: export PS1='\w \$ ' */
10756 unbackslash(name);
10752 } 10757 }
10753 debug_printf_env("%s: set_local_var('%s')\n", __func__, name); 10758 debug_printf_env("%s: set_local_var('%s')\n", __func__, name);
10754 if (set_local_var(name, flags)) 10759 if (set_local_var(name, flags))