From d8bd7012a30c6ce9efe26d06880ac223143709ad Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 14 May 2019 18:53:24 +0200 Subject: hush: fix "export PS1=xyz" and "local PS1=xyz" messing up prompt function old new delta helper_export_local 215 253 +38 leave_var_nest_level 107 127 +20 run_pipe 1840 1857 +17 handle_changed_special_names 101 105 +4 shell_builtin_read 1399 1398 -1 done_word 767 766 -1 parse_stream 2249 2245 -4 set_local_var 437 430 -7 is_well_formed_var_name 66 - -66 ------------------------------------------------------------------------------ (add/remove: 0/1 grow/shrink: 4/4 up/down: 79/-79) Total: 0 bytes text data bss dec hex filename 952376 485 7296 960157 ea69d busybox_old 952400 485 7296 960181 ea6b5 busybox_unstripped Signed-off-by: Denys Vlasenko --- shell/hush.c | 55 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 16 deletions(-) (limited to 'shell/hush.c') diff --git a/shell/hush.c b/shell/hush.c index b3ae73b9b..b612c80da 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -2248,9 +2248,12 @@ static const char* FAST_FUNC get_local_var_value(const char *name) return NULL; } +#if (ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT) \ + || (ENABLE_HUSH_LINENO_VAR || ENABLE_HUSH_GETOPTS) static void handle_changed_special_names(const char *name, unsigned name_len) { if (ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT + && G_interactive_fd && name_len == 3 && name[0] == 'P' && name[1] == 'S' ) { cmdedit_update_prompt(); @@ -2274,6 +2277,10 @@ static void handle_changed_special_names(const char *name, unsigned name_len) #endif } } +#else +/* Do not even bother evaluating arguments */ +# define handle_changed_special_names(...) ((void)0) +#endif /* str holds "NAME=VAL" and is expected to be malloced. * We take ownership of it. @@ -2289,6 +2296,7 @@ static int set_local_var(char *str, unsigned flags) char *free_me = NULL; char *eq_sign; int name_len; + int retval; unsigned local_lvl = (flags >> SETFLAG_VARLVL_SHIFT); eq_sign = strchr(str, '='); @@ -2402,24 +2410,24 @@ static int set_local_var(char *str, unsigned flags) #endif if (flags & SETFLAG_EXPORT) cur->flg_export = 1; + retval = 0; if (cur->flg_export) { if (flags & SETFLAG_UNEXPORT) { cur->flg_export = 0; /* unsetenv was already done */ } else { - int i; debug_printf_env("%s: putenv '%s'/%u\n", __func__, cur->varstr, cur->var_nest_level); - i = putenv(cur->varstr); - /* only now we can free old exported malloced string */ - free(free_me); - return i; + retval = putenv(cur->varstr); + /* fall through to "free(free_me)" - + * only now we can free old exported malloced string + */ } } free(free_me); handle_changed_special_names(cur->varstr, name_len - 1); - return 0; + return retval; } static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) @@ -2492,6 +2500,11 @@ static void add_vars(struct variable *var) } else { debug_printf_env("%s: restoring variable '%s'/%u\n", __func__, var->varstr, var->var_nest_level); } + /* Testcase (interactive): + * f() { local PS1='\w \$ '; }; f + * the below call is needed to notice restored PS1 when f returns. + */ + handle_changed_special_names(var->varstr, endofname(var->varstr) - var->varstr); var = next; } } @@ -4198,7 +4211,7 @@ static int done_word(struct parse_context *ctx) #if ENABLE_HUSH_LOOPS if (ctx->ctx_res_w == RES_FOR) { if (ctx->word.has_quoted_part - || !is_well_formed_var_name(command->argv[0], '\0') + || endofname(command->argv[0])[0] != '\0' ) { /* bash says just "not a valid identifier" */ syntax_error("not a valid identifier in for"); @@ -5372,7 +5385,7 @@ static struct pipe *parse_stream(char **pstring, if ((ctx.is_assignment == MAYBE_ASSIGNMENT || ctx.is_assignment == WORD_IS_KEYWORD) && ch == '=' - && is_well_formed_var_name(ctx.word.data, '=') + && endofname(ctx.word.data)[0] == '=' ) { ctx.is_assignment = DEFINITELY_ASSIGNMENT; debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); @@ -7598,10 +7611,10 @@ static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp) avoid_fd = 9; #if ENABLE_HUSH_INTERACTIVE - if (fd == G.interactive_fd) { + if (fd == G_interactive_fd) { /* Testcase: "ls -l /proc/$$/fd 255>&-" should work */ - G.interactive_fd = xdup_CLOEXEC_and_close(G.interactive_fd, avoid_fd); - debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G.interactive_fd); + G_interactive_fd = xdup_CLOEXEC_and_close(G_interactive_fd, avoid_fd); + debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G_interactive_fd); return 1; /* "we closed fd" */ } #endif @@ -7677,7 +7690,7 @@ static void restore_redirects(struct squirrel *sq) free(sq); } - /* If moved, G.interactive_fd stays on new fd, not restoring it */ + /* If moved, G_interactive_fd stays on new fd, not restoring it */ } #if ENABLE_FEATURE_SH_STANDALONE && BB_MMU @@ -7694,7 +7707,7 @@ static int internally_opened_fd(int fd, struct squirrel *sq) int i; #if ENABLE_HUSH_INTERACTIVE - if (fd == G.interactive_fd) + if (fd == G_interactive_fd) return 1; #endif /* If this one of script's fds? */ @@ -7885,6 +7898,11 @@ static void remove_nested_vars(void) *cur_pp = cur->next; /* Free */ if (!cur->max_len) { + /* Testcase (interactive): + * f() { local PS1='\w \$ '; }; f + * we should forget local PS1: + */ + handle_changed_special_names(cur->varstr, endofname(cur->varstr) - cur->varstr); debug_printf_env("freeing nested '%s'/%u\n", cur->varstr, cur->var_nest_level); free(cur->varstr); } @@ -10687,9 +10705,7 @@ static int helper_export_local(char **argv, unsigned flags) { do { char *name = *argv; - char *name_end = strchrnul(name, '='); - - /* So far we do not check that name is valid (TODO?) */ + const char *name_end = endofname(name); if (*name_end == '\0') { struct variable *var, **vpp; @@ -10747,8 +10763,15 @@ static int helper_export_local(char **argv, unsigned flags) */ name = xasprintf("%s=", name); } else { + if (*name_end != '=') { + bb_error_msg("'%s': bad variable name", name); + /* do not parse following argv[]s: */ + return 1; + } /* (Un)exporting/making local NAME=VALUE */ name = xstrdup(name); + /* Testcase: export PS1='\w \$ ' */ + unbackslash(name); } debug_printf_env("%s: set_local_var('%s')\n", __func__, name); if (set_local_var(name, flags)) -- cgit v1.2.3-55-g6feb From 9e0adb9b09ec14afe2e5a222dda1c447f336ea06 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 15 May 2019 13:39:19 +0200 Subject: hush: fix quoted "${notexist-}" expansion to not disappear function old new delta expand_one_var 2296 2311 +15 Signed-off-by: Denys Vlasenko --- shell/ash_test/ash-vars/param_expand_default.right | 2 ++ shell/ash_test/ash-vars/param_expand_default.tests | 5 +++++ shell/hush.c | 6 ++++++ shell/hush_test/hush-vars/param_expand_default.right | 2 ++ shell/hush_test/hush-vars/param_expand_default.tests | 5 +++++ 5 files changed, 20 insertions(+) (limited to 'shell/hush.c') diff --git a/shell/ash_test/ash-vars/param_expand_default.right b/shell/ash_test/ash-vars/param_expand_default.right index 3eecd1375..7a42f67e8 100644 --- a/shell/ash_test/ash-vars/param_expand_default.right +++ b/shell/ash_test/ash-vars/param_expand_default.right @@ -5,3 +5,5 @@ _aaaa _aaaa _aaaa _aaaa _aaaa _ _ _ _word _word _ _ _ _ _word _fff _fff _fff _fff _fff +1:1 +0:0 diff --git a/shell/ash_test/ash-vars/param_expand_default.tests b/shell/ash_test/ash-vars/param_expand_default.tests index 5e42d30e3..b5edfe1c5 100755 --- a/shell/ash_test/ash-vars/param_expand_default.tests +++ b/shell/ash_test/ash-vars/param_expand_default.tests @@ -21,3 +21,8 @@ echo _$f _${f-} _${f:-} _${f-word} _${f:-word} f=fff echo _$f _${f-} _${f:-} _${f-word} _${f:-word} + +set -- +set -- "${1-}"; echo 1:$# +set -- +set -- ${1-}; echo 0:$# diff --git a/shell/hush.c b/shell/hush.c index b612c80da..a103e8169 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -6132,6 +6132,12 @@ static int encode_then_append_var_plusminus(o_string *output, int n, /* string has no special chars * && string has no $IFS chars */ + if (dquoted) { + /* Prints 1 (quoted expansion is a "" word, not nothing): + * set -- "${notexist-}"; echo $# + */ + output->has_quoted_part = 1; + } return expand_vars_to_list(output, n, str); } diff --git a/shell/hush_test/hush-vars/param_expand_default.right b/shell/hush_test/hush-vars/param_expand_default.right index acc717205..dbade3003 100644 --- a/shell/hush_test/hush-vars/param_expand_default.right +++ b/shell/hush_test/hush-vars/param_expand_default.right @@ -6,3 +6,5 @@ _aaaa _aaaa _aaaa _aaaa _aaaa _ _ _ _word _word _ _ _ _ _word _fff _fff _fff _fff _fff +1:1 +0:0 diff --git a/shell/hush_test/hush-vars/param_expand_default.tests b/shell/hush_test/hush-vars/param_expand_default.tests index 16e5f8efe..754827ab3 100755 --- a/shell/hush_test/hush-vars/param_expand_default.tests +++ b/shell/hush_test/hush-vars/param_expand_default.tests @@ -22,3 +22,8 @@ echo _$f _${f-} _${f:-} _${f-word} _${f:-word} f=fff echo _$f _${f-} _${f:-} _${f-word} _${f:-word} + +set -- +set -- "${1-}"; echo 1:$# +set -- +set -- ${1-}; echo 0:$# -- cgit v1.2.3-55-g6feb From 0ee0b658b3b651d952965dd85a7b1dd0ac5312dc Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 16 May 2019 11:32:26 +0200 Subject: hush: small speedup in handle_changed_special_names() Signed-off-by: Denys Vlasenko --- shell/hush.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'shell/hush.c') diff --git a/shell/hush.c b/shell/hush.c index a103e8169..ce341632a 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -2253,10 +2253,10 @@ static const char* FAST_FUNC get_local_var_value(const char *name) static void handle_changed_special_names(const char *name, unsigned name_len) { if (ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT - && G_interactive_fd && name_len == 3 && name[0] == 'P' && name[1] == 'S' ) { - cmdedit_update_prompt(); + if (G_interactive_fd) + cmdedit_update_prompt(); return; } -- cgit v1.2.3-55-g6feb From 4ebcdf7396b8e19ddf4e8b12a84b186fcbccabb8 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 16 May 2019 15:39:19 +0200 Subject: hush: remove code to track PS1/2 values dynamically - it's too much work Assignments / exports / unsets of variables are far more frequent than prompt printing, and if we show prompt, we are likely to be limited by user typing speed - do not optimize for that scenario. Just re-query $PS1 / $PS2 values when need to show the prompt. function old new delta fgetc_interactive 236 259 +23 set_vars_and_save_old 150 147 -3 pseudo_exec_argv 597 594 -3 hush_main 1110 1105 -5 enter_var_nest_level 38 32 -6 builtin_local 56 50 -6 run_pipe 1857 1834 -23 leave_var_nest_level 127 98 -29 handle_changed_special_names 111 79 -32 cmdedit_update_prompt 57 - -57 ------------------------------------------------------------------------------ (add/remove: 0/1 grow/shrink: 1/8 up/down: 23/-164) Total: -141 bytes Signed-off-by: Denys Vlasenko --- shell/hush.c | 82 ++++++++++++++++-------------------------------------------- 1 file changed, 21 insertions(+), 61 deletions(-) (limited to 'shell/hush.c') diff --git a/shell/hush.c b/shell/hush.c index ce341632a..e2927afc4 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -854,8 +854,7 @@ struct globals { /* 'interactive_fd' is a fd# open to ctty, if we have one * _AND_ if we decided to act interactively */ int interactive_fd; - const char *PS1; - IF_FEATURE_EDITING_FANCY_PROMPT(const char *PS2;) + IF_NOT_FEATURE_EDITING_FANCY_PROMPT(char *PS1;) # define G_interactive_fd (G.interactive_fd) #else # define G_interactive_fd 0 @@ -1448,13 +1447,6 @@ static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch) #endif -#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT -static void cmdedit_update_prompt(void); -#else -# define cmdedit_update_prompt() ((void)0) -#endif - - /* Utility functions */ /* Replace each \x with x in place, return ptr past NUL. */ @@ -2248,33 +2240,22 @@ static const char* FAST_FUNC get_local_var_value(const char *name) return NULL; } -#if (ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT) \ - || (ENABLE_HUSH_LINENO_VAR || ENABLE_HUSH_GETOPTS) +#if ENABLE_HUSH_LINENO_VAR || ENABLE_HUSH_GETOPTS static void handle_changed_special_names(const char *name, unsigned name_len) { - if (ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT - && name_len == 3 && name[0] == 'P' && name[1] == 'S' - ) { - if (G_interactive_fd) - cmdedit_update_prompt(); - return; - } - - if ((ENABLE_HUSH_LINENO_VAR || ENABLE_HUSH_GETOPTS) - && name_len == 6 - ) { -#if ENABLE_HUSH_LINENO_VAR + if (name_len == 6) { +# if ENABLE_HUSH_LINENO_VAR if (strncmp(name, "LINENO", 6) == 0) { G.lineno_var = NULL; return; } -#endif -#if ENABLE_HUSH_GETOPTS +# endif +# if ENABLE_HUSH_GETOPTS if (strncmp(name, "OPTIND", 6) == 0) { G.getopt_count = 0; return; } -#endif +# endif } } #else @@ -2470,7 +2451,7 @@ static int unset_local_var_len(const char *name, int name_len) cur_pp = &cur->next; } - /* Handle "unset PS1" et al even if did not find the variable to unset */ + /* Handle "unset LINENO" et al even if did not find the variable to unset */ handle_changed_special_names(name, name_len); return EXIT_SUCCESS; @@ -2500,11 +2481,6 @@ static void add_vars(struct variable *var) } else { debug_printf_env("%s: restoring variable '%s'/%u\n", __func__, var->varstr, var->var_nest_level); } - /* Testcase (interactive): - * f() { local PS1='\w \$ '; }; f - * the below call is needed to notice restored PS1 when f returns. - */ - handle_changed_special_names(var->varstr, endofname(var->varstr) - var->varstr); var = next; } } @@ -2594,36 +2570,27 @@ static void reinit_unicode_for_hush(void) * \ * It exercises a lot of corner cases. */ -# if ENABLE_FEATURE_EDITING_FANCY_PROMPT -static void cmdedit_update_prompt(void) -{ - G.PS1 = get_local_var_value("PS1"); - if (G.PS1 == NULL) - G.PS1 = ""; - G.PS2 = get_local_var_value("PS2"); - if (G.PS2 == NULL) - G.PS2 = ""; -} -# endif static const char *setup_prompt_string(void) { const char *prompt_str; debug_printf_prompt("%s promptmode:%d\n", __func__, G.promptmode); - IF_FEATURE_EDITING_FANCY_PROMPT( prompt_str = G.PS2;) - IF_NOT_FEATURE_EDITING_FANCY_PROMPT(prompt_str = "> ";) +# if ENABLE_FEATURE_EDITING_FANCY_PROMPT + prompt_str = get_local_var_value(G.promptmode == 0 ? "PS1" : "PS2"); + if (!prompt_str) + prompt_str = ""; +# else + prompt_str = "> "; /* if PS2, else... */ if (G.promptmode == 0) { /* PS1 */ - if (!ENABLE_FEATURE_EDITING_FANCY_PROMPT) { - /* No fancy prompts supported, (re)generate "CURDIR $ " by hand */ - free((char*)G.PS1); - /* bash uses $PWD value, even if it is set by user. - * It uses current dir only if PWD is unset. - * We always use current dir. */ - G.PS1 = xasprintf("%s %c ", get_cwd(0), (geteuid() != 0) ? '$' : '#'); - } - prompt_str = G.PS1; + /* No fancy prompts supported, (re)generate "CURDIR $ " by hand */ + free(G.PS1); + /* bash uses $PWD value, even if it is set by user. + * It uses current dir only if PWD is unset. + * We always use current dir. */ + G.PS1 = xasprintf("%s %c ", get_cwd(0), (geteuid() != 0) ? '$' : '#'); } +# endif debug_printf("prompt_str '%s'\n", prompt_str); return prompt_str; } @@ -7904,11 +7871,6 @@ static void remove_nested_vars(void) *cur_pp = cur->next; /* Free */ if (!cur->max_len) { - /* Testcase (interactive): - * f() { local PS1='\w \$ '; }; f - * we should forget local PS1: - */ - handle_changed_special_names(cur->varstr, endofname(cur->varstr) - cur->varstr); debug_printf_env("freeing nested '%s'/%u\n", cur->varstr, cur->var_nest_level); free(cur->varstr); } @@ -9997,8 +9959,6 @@ int hush_main(int argc, char **argv) #endif /* Initialize some more globals to non-zero values */ - cmdedit_update_prompt(); - die_func = restore_ttypgrp_and__exit; /* Shell is non-interactive at first. We need to call -- cgit v1.2.3-55-g6feb From 08fb82c80cf06b776822b8388c3863e7c5565a74 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 19 May 2019 15:26:05 +0200 Subject: hush: handle LINENO the same way as RANDOM: variable is "ephemeral" "env - hush" invocation (that is, with empty environment) should not show LINENO in "set" output. function old new delta get_local_var_value 263 294 +31 hush_main 1105 1070 -35 handle_changed_special_names 79 38 -41 run_pipe 1834 1765 -69 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/3 up/down: 31/-145) Total: -114 bytes Signed-off-by: Denys Vlasenko --- shell/hush.c | 50 ++++++++++++++++++-------------------------------- 1 file changed, 18 insertions(+), 32 deletions(-) (limited to 'shell/hush.c') diff --git a/shell/hush.c b/shell/hush.c index e2927afc4..629b7ff92 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -967,8 +967,8 @@ struct globals { smallint we_have_children; #endif #if ENABLE_HUSH_LINENO_VAR - unsigned lineno; - char *lineno_var; + unsigned parse_lineno; + unsigned execute_lineno; #endif HFILE *HFILE_list; /* Which signals have non-DFL handler (even with no traps set)? @@ -2221,6 +2221,10 @@ static const char* FAST_FUNC get_local_var_value(const char *name) if (strcmp(name, "RANDOM") == 0) return utoa(next_random(&G.random_gen)); #endif +#if ENABLE_HUSH_LINENO_VAR + if (strcmp(name, "LINENO") == 0) + return utoa(G.execute_lineno); +#endif #if BASH_EPOCH_VARS { const char *fmt = NULL; @@ -2240,22 +2244,14 @@ static const char* FAST_FUNC get_local_var_value(const char *name) return NULL; } -#if ENABLE_HUSH_LINENO_VAR || ENABLE_HUSH_GETOPTS +#if ENABLE_HUSH_GETOPTS static void handle_changed_special_names(const char *name, unsigned name_len) { if (name_len == 6) { -# if ENABLE_HUSH_LINENO_VAR - if (strncmp(name, "LINENO", 6) == 0) { - G.lineno_var = NULL; - return; - } -# endif -# if ENABLE_HUSH_GETOPTS if (strncmp(name, "OPTIND", 6) == 0) { G.getopt_count = 0; return; } -# endif } } #else @@ -2727,8 +2723,8 @@ static int i_getch(struct in_str *i) i->last_char = ch; #if ENABLE_HUSH_LINENO_VAR if (ch == '\n') { - G.lineno++; - debug_printf_parse("G.lineno++ = %u\n", G.lineno); + G.parse_lineno++; + debug_printf_parse("G.parse_lineno++ = %u\n", G.parse_lineno); } #endif return ch; @@ -3730,8 +3726,8 @@ static int done_command(struct parse_context *ctx) clear_and_ret: memset(command, 0, sizeof(*command)); #if ENABLE_HUSH_LINENO_VAR - command->lineno = G.lineno; - debug_printf_parse("command->lineno = G.lineno (%u)\n", G.lineno); + command->lineno = G.parse_lineno; + debug_printf_parse("command->lineno = G.parse_lineno (%u)\n", G.parse_lineno); #endif return pi->num_cmds; /* used only for 0/nonzero check */ } @@ -7261,22 +7257,22 @@ static void parse_and_run_stream(struct in_str *inp, int end_trigger) static void parse_and_run_string(const char *s) { struct in_str input; - //IF_HUSH_LINENO_VAR(unsigned sv = G.lineno;) + //IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;) setup_string_in_str(&input, s); parse_and_run_stream(&input, '\0'); - //IF_HUSH_LINENO_VAR(G.lineno = sv;) + //IF_HUSH_LINENO_VAR(G.parse_lineno = sv;) } static void parse_and_run_file(HFILE *fp) { struct in_str input; - IF_HUSH_LINENO_VAR(unsigned sv = G.lineno;) + IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;) - IF_HUSH_LINENO_VAR(G.lineno = 1;) + IF_HUSH_LINENO_VAR(G.parse_lineno = 1;) setup_file_in_str(&input, fp); parse_and_run_stream(&input, ';'); - IF_HUSH_LINENO_VAR(G.lineno = sv;) + IF_HUSH_LINENO_VAR(G.parse_lineno = sv;) } #if ENABLE_HUSH_TICK @@ -8975,8 +8971,7 @@ static NOINLINE int run_pipe(struct pipe *pi) struct variable *old_vars; #if ENABLE_HUSH_LINENO_VAR - if (G.lineno_var) - strcpy(G.lineno_var + sizeof("LINENO=")-1, utoa(command->lineno)); + G.execute_lineno = command->lineno; #endif if (argv[command->assignment_cnt] == NULL) { @@ -9209,8 +9204,7 @@ static NOINLINE int run_pipe(struct pipe *pi) xpiped_pair(pipefds); #if ENABLE_HUSH_LINENO_VAR - if (G.lineno_var) - strcpy(G.lineno_var + sizeof("LINENO=")-1, utoa(command->lineno)); + G.execute_lineno = command->lineno; #endif command->pid = BB_MMU ? fork() : vfork(); @@ -9946,14 +9940,6 @@ int hush_main(int argc, char **argv) * PS4='+ ' */ -#if ENABLE_HUSH_LINENO_VAR - if (ENABLE_HUSH_LINENO_VAR) { - char *p = xasprintf("LINENO=%*s", (int)(sizeof(int)*3), ""); - set_local_var(p, /*flags*/ 0); - G.lineno_var = p; /* can't assign before set_local_var("LINENO=...") */ - } -#endif - #if ENABLE_FEATURE_EDITING G.line_input_state = new_line_input_t(FOR_SHELL); #endif -- cgit v1.2.3-55-g6feb From 0c3601936915d3d625683388c62c561de96a47da Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 19 May 2019 15:37:50 +0200 Subject: hush: set default PS1/2 only if we interactive "env - hush SCRIPT" invocation (that is, with empty environment) should not show PS1/2 in "set" output. function old new delta hush_main 1070 1075 +5 Signed-off-by: Denys Vlasenko --- shell/hush.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) (limited to 'shell/hush.c') diff --git a/shell/hush.c b/shell/hush.c index 629b7ff92..2b9abbdfd 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -9888,14 +9888,6 @@ int hush_main(int argc, char **argv) /* Export PWD */ set_pwd_var(SETFLAG_EXPORT); -#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT - /* Set (but not export) PS1/2 unless already set */ - if (!get_local_var_value("PS1")) - set_local_var_from_halves("PS1", "\\w \\$ "); - if (!get_local_var_value("PS2")) - set_local_var_from_halves("PS2", "> "); -#endif - #if BASH_HOSTNAME_VAR /* Set (but not export) HOSTNAME unless already set */ if (!get_local_var_value("HOSTNAME")) { @@ -9907,6 +9899,8 @@ int hush_main(int argc, char **argv) /* IFS is not inherited from the parent environment */ set_local_var_from_halves("IFS", defifs); + /* PS1/PS2 are set later, if we determine that we are interactive */ + /* bash also exports SHLVL and _, * and sets (but doesn't export) the following variables: * BASH=/bin/bash @@ -10278,14 +10272,23 @@ int hush_main(int argc, char **argv) * (--norc turns this off, --rcfile overrides) */ - if (!ENABLE_FEATURE_SH_EXTRA_QUIET && G_interactive_fd) { - /* note: ash and hush share this string */ - printf("\n\n%s %s\n" - IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n") - "\n", - bb_banner, - "hush - the humble shell" - ); + if (G_interactive_fd) { +#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT + /* Set (but not export) PS1/2 unless already set */ + if (!get_local_var_value("PS1")) + set_local_var_from_halves("PS1", "\\w \\$ "); + if (!get_local_var_value("PS2")) + set_local_var_from_halves("PS2", "> "); +#endif + if (!ENABLE_FEATURE_SH_EXTRA_QUIET) { + /* note: ash and hush share this string */ + printf("\n\n%s %s\n" + IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n") + "\n", + bb_banner, + "hush - the humble shell" + ); + } } parse_and_run_file(hfopen(NULL)); /* stdin */ -- cgit v1.2.3-55-g6feb From ef8985c688e9e5e9e8bc29f38d1f05f543c30acb Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 19 May 2019 16:29:09 +0200 Subject: hush: implement $-, set default PATH if it is not set on startup function old new delta expand_one_var 2311 2362 +51 hush_main 1075 1104 +29 parse_dollar 790 791 +1 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 3/0 up/down: 81/0) Total: 81 bytes Signed-off-by: Denys Vlasenko --- shell/hush.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) (limited to 'shell/hush.c') diff --git a/shell/hush.c b/shell/hush.c index 2b9abbdfd..9dd893aa6 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -466,9 +466,9 @@ #define JOB_STATUS_FORMAT "[%u] %-22s %.40s\n" -#define _SPECIAL_VARS_STR "_*@$!?#" -#define SPECIAL_VARS_STR ("_*@$!?#" + 1) -#define NUMERIC_SPECVARS_STR ("_*@$!?#" + 3) +#define _SPECIAL_VARS_STR "_*@$!?#-" +#define SPECIAL_VARS_STR ("_*@$!?#-" + 1) +#define NUMERIC_SPECVARS_STR ("_*@$!?#-" + 3) #if BASH_PATTERN_SUBST /* Support / and // replace ops */ /* Note that // is stored as \ in "encoded" string representation */ @@ -1008,6 +1008,7 @@ struct globals { int debug_indent; #endif struct sigaction sa; + char optstring_buf[sizeof("eix")]; #if BASH_EPOCH_VARS char epoch_buf[sizeof("%lu.nnnnnn") + sizeof(long)*3]; #endif @@ -4888,6 +4889,7 @@ static int parse_dollar(o_string *as_string, case '#': /* number of args */ case '*': /* args */ case '@': /* args */ + case '-': /* $- option flags set by set builtin or shell options (-i etc) */ goto make_one_char_var; case '{': { char len_single_ch; @@ -5062,11 +5064,10 @@ static int parse_dollar(o_string *as_string, case '_': goto make_var; #if 0 - /* TODO: $_ and $-: */ + /* TODO: $_: */ /* $_ Shell or shell script name; or last argument of last command * (if last command wasn't a pipe; if it was, bash sets $_ to ""); * but in command's env, set to full pathname used to invoke it */ - /* $- Option flags set by set builtin or shell options (-i etc) */ ch = i_getch(input); nommu_addchr(as_string, ch); ch = i_peek_and_eat_bkslash_nl(input); @@ -6397,6 +6398,23 @@ static NOINLINE int expand_one_var(o_string *output, int n, case '#': /* argc */ val = utoa(G.global_argc ? G.global_argc-1 : 0); break; + case '-': { /* active options */ + /* Check set_mode() to see what option chars we support */ + char *cp; + val = cp = G.optstring_buf; + if (G.o_opt[OPT_O_ERREXIT]) + *cp++ = 'e'; + if (G_interactive_fd) + *cp++ = 'i'; + if (G_x_mode) + *cp++ = 'x'; + /* If G.o_opt[OPT_O_NOEXEC] is true, + * commands read but are not executed, + * so $- can not execute too, 'n' is never seen in $-. + */ + *cp = '\0'; + break; + } default: val = get_local_var_value(var); } @@ -9899,6 +9917,9 @@ int hush_main(int argc, char **argv) /* IFS is not inherited from the parent environment */ set_local_var_from_halves("IFS", defifs); + if (!get_local_var_value("PATH")) + set_local_var_from_halves("PATH", bb_default_root_path); + /* PS1/PS2 are set later, if we determine that we are interactive */ /* bash also exports SHLVL and _, -- cgit v1.2.3-55-g6feb From 76a4e8361a84efca256d4286cf36561c42c14d64 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 19 May 2019 18:24:52 +0200 Subject: hush: allocate line edit buffer only for interactive shell function old new delta builtin_history 16 20 +4 Signed-off-by: Denys Vlasenko --- shell/hush.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'shell/hush.c') diff --git a/shell/hush.c b/shell/hush.c index 9dd893aa6..4dd940222 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -2032,7 +2032,8 @@ static sighandler_t pick_sighandler(unsigned sig) static void hush_exit(int exitcode) { #if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT - save_history(G.line_input_state); + if (G.line_input_state) + save_history(G.line_input_state); #endif fflush_all(); @@ -6412,6 +6413,8 @@ static NOINLINE int expand_one_var(o_string *output, int n, * commands read but are not executed, * so $- can not execute too, 'n' is never seen in $-. */ +//TODO: show 'c' if executed via "hush -c 'CMDS'" (bash only, not ash) +//TODO: show 's' if executed via "hush -s ARG1 ARG2", or if there were no args except options (ash does this too) *cp = '\0'; break; } @@ -9955,9 +9958,6 @@ int hush_main(int argc, char **argv) * PS4='+ ' */ -#if ENABLE_FEATURE_EDITING - G.line_input_state = new_line_input_t(FOR_SHELL); -#endif /* Initialize some more globals to non-zero values */ die_func = restore_ttypgrp_and__exit; @@ -10245,6 +10245,9 @@ int hush_main(int argc, char **argv) } enable_restore_tty_pgrp_on_exit(); +# if ENABLE_FEATURE_EDITING + G.line_input_state = new_line_input_t(FOR_SHELL); +# endif # if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0 { const char *hp = get_local_var_value("HISTFILE"); @@ -10372,7 +10375,8 @@ static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM) #if MAX_HISTORY && ENABLE_FEATURE_EDITING static int FAST_FUNC builtin_history(char **argv UNUSED_PARAM) { - show_history(G.line_input_state); + if (G.line_input_state) + show_history(G.line_input_state); return EXIT_SUCCESS; } #endif -- cgit v1.2.3-55-g6feb From d8740b265a4d4e428b3494089d5a86e1ec90238a Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 19 May 2019 19:11:21 +0200 Subject: hush: show 's' in $- function old new delta expand_one_var 2362 2375 +13 hush_main 1104 1108 +4 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 17/0) Total: 17 bytes Signed-off-by: Denys Vlasenko --- shell/hush.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'shell/hush.c') diff --git a/shell/hush.c b/shell/hush.c index 4dd940222..4b08232a4 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -902,6 +902,7 @@ struct globals { #else # define G_x_mode 0 #endif + char opt_s; #if ENABLE_HUSH_INTERACTIVE smallint promptmode; /* 0: PS1, 1: PS2 */ #endif @@ -1008,7 +1009,7 @@ struct globals { int debug_indent; #endif struct sigaction sa; - char optstring_buf[sizeof("eix")]; + char optstring_buf[sizeof("eixs")]; #if BASH_EPOCH_VARS char epoch_buf[sizeof("%lu.nnnnnn") + sizeof(long)*3]; #endif @@ -6413,8 +6414,9 @@ static NOINLINE int expand_one_var(o_string *output, int n, * commands read but are not executed, * so $- can not execute too, 'n' is never seen in $-. */ + if (G.opt_s) + *cp++ = 's'; //TODO: show 'c' if executed via "hush -c 'CMDS'" (bash only, not ash) -//TODO: show 's' if executed via "hush -s ARG1 ARG2", or if there were no args except options (ash does this too) *cp = '\0'; break; } @@ -9958,7 +9960,6 @@ int hush_main(int argc, char **argv) * PS4='+ ' */ - /* Initialize some more globals to non-zero values */ die_func = restore_ttypgrp_and__exit; @@ -10177,6 +10178,7 @@ int hush_main(int argc, char **argv) #endif goto final_return; } + G.opt_s = 1; /* Up to here, shell was non-interactive. Now it may become one. * NB: don't forget to (re)run install_special_sighandlers() as needed. -- cgit v1.2.3-55-g6feb