diff options
-rw-r--r-- | shell/hush.c | 55 | ||||
-rw-r--r-- | shell/shell_common.c | 15 | ||||
-rw-r--r-- | shell/shell_common.h | 2 |
3 files changed, 40 insertions, 32 deletions
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) | |||
2248 | return NULL; | 2248 | return NULL; |
2249 | } | 2249 | } |
2250 | 2250 | ||
2251 | #if (ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT) \ | ||
2252 | || (ENABLE_HUSH_LINENO_VAR || ENABLE_HUSH_GETOPTS) | ||
2251 | static void handle_changed_special_names(const char *name, unsigned name_len) | 2253 | static void handle_changed_special_names(const char *name, unsigned name_len) |
2252 | { | 2254 | { |
2253 | if (ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT | 2255 | if (ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT |
2256 | && G_interactive_fd | ||
2254 | && name_len == 3 && name[0] == 'P' && name[1] == 'S' | 2257 | && name_len == 3 && name[0] == 'P' && name[1] == 'S' |
2255 | ) { | 2258 | ) { |
2256 | cmdedit_update_prompt(); | 2259 | cmdedit_update_prompt(); |
@@ -2274,6 +2277,10 @@ static void handle_changed_special_names(const char *name, unsigned name_len) | |||
2274 | #endif | 2277 | #endif |
2275 | } | 2278 | } |
2276 | } | 2279 | } |
2280 | #else | ||
2281 | /* Do not even bother evaluating arguments */ | ||
2282 | # define handle_changed_special_names(...) ((void)0) | ||
2283 | #endif | ||
2277 | 2284 | ||
2278 | /* str holds "NAME=VAL" and is expected to be malloced. | 2285 | /* str holds "NAME=VAL" and is expected to be malloced. |
2279 | * We take ownership of it. | 2286 | * We take ownership of it. |
@@ -2289,6 +2296,7 @@ static int set_local_var(char *str, unsigned flags) | |||
2289 | char *free_me = NULL; | 2296 | char *free_me = NULL; |
2290 | char *eq_sign; | 2297 | char *eq_sign; |
2291 | int name_len; | 2298 | int name_len; |
2299 | int retval; | ||
2292 | unsigned local_lvl = (flags >> SETFLAG_VARLVL_SHIFT); | 2300 | unsigned local_lvl = (flags >> SETFLAG_VARLVL_SHIFT); |
2293 | 2301 | ||
2294 | eq_sign = strchr(str, '='); | 2302 | eq_sign = strchr(str, '='); |
@@ -2402,24 +2410,24 @@ static int set_local_var(char *str, unsigned flags) | |||
2402 | #endif | 2410 | #endif |
2403 | if (flags & SETFLAG_EXPORT) | 2411 | if (flags & SETFLAG_EXPORT) |
2404 | cur->flg_export = 1; | 2412 | cur->flg_export = 1; |
2413 | retval = 0; | ||
2405 | if (cur->flg_export) { | 2414 | if (cur->flg_export) { |
2406 | if (flags & SETFLAG_UNEXPORT) { | 2415 | if (flags & SETFLAG_UNEXPORT) { |
2407 | cur->flg_export = 0; | 2416 | cur->flg_export = 0; |
2408 | /* unsetenv was already done */ | 2417 | /* unsetenv was already done */ |
2409 | } else { | 2418 | } else { |
2410 | int i; | ||
2411 | debug_printf_env("%s: putenv '%s'/%u\n", __func__, cur->varstr, cur->var_nest_level); | 2419 | debug_printf_env("%s: putenv '%s'/%u\n", __func__, cur->varstr, cur->var_nest_level); |
2412 | i = putenv(cur->varstr); | 2420 | retval = putenv(cur->varstr); |
2413 | /* only now we can free old exported malloced string */ | 2421 | /* fall through to "free(free_me)" - |
2414 | free(free_me); | 2422 | * only now we can free old exported malloced string |
2415 | return i; | 2423 | */ |
2416 | } | 2424 | } |
2417 | } | 2425 | } |
2418 | free(free_me); | 2426 | free(free_me); |
2419 | 2427 | ||
2420 | handle_changed_special_names(cur->varstr, name_len - 1); | 2428 | handle_changed_special_names(cur->varstr, name_len - 1); |
2421 | 2429 | ||
2422 | return 0; | 2430 | return retval; |
2423 | } | 2431 | } |
2424 | 2432 | ||
2425 | static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) | 2433 | 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) | |||
2492 | } else { | 2500 | } else { |
2493 | debug_printf_env("%s: restoring variable '%s'/%u\n", __func__, var->varstr, var->var_nest_level); | 2501 | debug_printf_env("%s: restoring variable '%s'/%u\n", __func__, var->varstr, var->var_nest_level); |
2494 | } | 2502 | } |
2503 | /* Testcase (interactive): | ||
2504 | * f() { local PS1='\w \$ '; }; f | ||
2505 | * the below call is needed to notice restored PS1 when f returns. | ||
2506 | */ | ||
2507 | handle_changed_special_names(var->varstr, endofname(var->varstr) - var->varstr); | ||
2495 | var = next; | 2508 | var = next; |
2496 | } | 2509 | } |
2497 | } | 2510 | } |
@@ -4198,7 +4211,7 @@ static int done_word(struct parse_context *ctx) | |||
4198 | #if ENABLE_HUSH_LOOPS | 4211 | #if ENABLE_HUSH_LOOPS |
4199 | if (ctx->ctx_res_w == RES_FOR) { | 4212 | if (ctx->ctx_res_w == RES_FOR) { |
4200 | if (ctx->word.has_quoted_part | 4213 | if (ctx->word.has_quoted_part |
4201 | || !is_well_formed_var_name(command->argv[0], '\0') | 4214 | || endofname(command->argv[0])[0] != '\0' |
4202 | ) { | 4215 | ) { |
4203 | /* bash says just "not a valid identifier" */ | 4216 | /* bash says just "not a valid identifier" */ |
4204 | syntax_error("not a valid identifier in for"); | 4217 | syntax_error("not a valid identifier in for"); |
@@ -5372,7 +5385,7 @@ static struct pipe *parse_stream(char **pstring, | |||
5372 | if ((ctx.is_assignment == MAYBE_ASSIGNMENT | 5385 | if ((ctx.is_assignment == MAYBE_ASSIGNMENT |
5373 | || ctx.is_assignment == WORD_IS_KEYWORD) | 5386 | || ctx.is_assignment == WORD_IS_KEYWORD) |
5374 | && ch == '=' | 5387 | && ch == '=' |
5375 | && is_well_formed_var_name(ctx.word.data, '=') | 5388 | && endofname(ctx.word.data)[0] == '=' |
5376 | ) { | 5389 | ) { |
5377 | ctx.is_assignment = DEFINITELY_ASSIGNMENT; | 5390 | ctx.is_assignment = DEFINITELY_ASSIGNMENT; |
5378 | debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); | 5391 | 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) | |||
7598 | avoid_fd = 9; | 7611 | avoid_fd = 9; |
7599 | 7612 | ||
7600 | #if ENABLE_HUSH_INTERACTIVE | 7613 | #if ENABLE_HUSH_INTERACTIVE |
7601 | if (fd == G.interactive_fd) { | 7614 | if (fd == G_interactive_fd) { |
7602 | /* Testcase: "ls -l /proc/$$/fd 255>&-" should work */ | 7615 | /* Testcase: "ls -l /proc/$$/fd 255>&-" should work */ |
7603 | G.interactive_fd = xdup_CLOEXEC_and_close(G.interactive_fd, avoid_fd); | 7616 | 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); | 7617 | debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G_interactive_fd); |
7605 | return 1; /* "we closed fd" */ | 7618 | return 1; /* "we closed fd" */ |
7606 | } | 7619 | } |
7607 | #endif | 7620 | #endif |
@@ -7677,7 +7690,7 @@ static void restore_redirects(struct squirrel *sq) | |||
7677 | free(sq); | 7690 | free(sq); |
7678 | } | 7691 | } |
7679 | 7692 | ||
7680 | /* If moved, G.interactive_fd stays on new fd, not restoring it */ | 7693 | /* If moved, G_interactive_fd stays on new fd, not restoring it */ |
7681 | } | 7694 | } |
7682 | 7695 | ||
7683 | #if ENABLE_FEATURE_SH_STANDALONE && BB_MMU | 7696 | #if ENABLE_FEATURE_SH_STANDALONE && BB_MMU |
@@ -7694,7 +7707,7 @@ static int internally_opened_fd(int fd, struct squirrel *sq) | |||
7694 | int i; | 7707 | int i; |
7695 | 7708 | ||
7696 | #if ENABLE_HUSH_INTERACTIVE | 7709 | #if ENABLE_HUSH_INTERACTIVE |
7697 | if (fd == G.interactive_fd) | 7710 | if (fd == G_interactive_fd) |
7698 | return 1; | 7711 | return 1; |
7699 | #endif | 7712 | #endif |
7700 | /* If this one of script's fds? */ | 7713 | /* If this one of script's fds? */ |
@@ -7885,6 +7898,11 @@ static void remove_nested_vars(void) | |||
7885 | *cur_pp = cur->next; | 7898 | *cur_pp = cur->next; |
7886 | /* Free */ | 7899 | /* Free */ |
7887 | if (!cur->max_len) { | 7900 | if (!cur->max_len) { |
7901 | /* Testcase (interactive): | ||
7902 | * f() { local PS1='\w \$ '; }; f | ||
7903 | * we should forget local PS1: | ||
7904 | */ | ||
7905 | handle_changed_special_names(cur->varstr, endofname(cur->varstr) - cur->varstr); | ||
7888 | debug_printf_env("freeing nested '%s'/%u\n", cur->varstr, cur->var_nest_level); | 7906 | debug_printf_env("freeing nested '%s'/%u\n", cur->varstr, cur->var_nest_level); |
7889 | free(cur->varstr); | 7907 | free(cur->varstr); |
7890 | } | 7908 | } |
@@ -10687,9 +10705,7 @@ static int helper_export_local(char **argv, unsigned flags) | |||
10687 | { | 10705 | { |
10688 | do { | 10706 | do { |
10689 | char *name = *argv; | 10707 | char *name = *argv; |
10690 | char *name_end = strchrnul(name, '='); | 10708 | const char *name_end = endofname(name); |
10691 | |||
10692 | /* So far we do not check that name is valid (TODO?) */ | ||
10693 | 10709 | ||
10694 | if (*name_end == '\0') { | 10710 | if (*name_end == '\0') { |
10695 | struct variable *var, **vpp; | 10711 | struct variable *var, **vpp; |
@@ -10747,8 +10763,15 @@ static int helper_export_local(char **argv, unsigned flags) | |||
10747 | */ | 10763 | */ |
10748 | name = xasprintf("%s=", name); | 10764 | name = xasprintf("%s=", name); |
10749 | } else { | 10765 | } else { |
10766 | if (*name_end != '=') { | ||
10767 | bb_error_msg("'%s': bad variable name", name); | ||
10768 | /* do not parse following argv[]s: */ | ||
10769 | return 1; | ||
10770 | } | ||
10750 | /* (Un)exporting/making local NAME=VALUE */ | 10771 | /* (Un)exporting/making local NAME=VALUE */ |
10751 | name = xstrdup(name); | 10772 | name = xstrdup(name); |
10773 | /* Testcase: export PS1='\w \$ ' */ | ||
10774 | unbackslash(name); | ||
10752 | } | 10775 | } |
10753 | debug_printf_env("%s: set_local_var('%s')\n", __func__, name); | 10776 | debug_printf_env("%s: set_local_var('%s')\n", __func__, name); |
10754 | if (set_local_var(name, flags)) | 10777 | if (set_local_var(name, flags)) |
diff --git a/shell/shell_common.c b/shell/shell_common.c index da3165329..e0582adfb 100644 --- a/shell/shell_common.c +++ b/shell/shell_common.c | |||
@@ -22,19 +22,6 @@ | |||
22 | const char defifsvar[] ALIGN1 = "IFS= \t\n"; | 22 | const char defifsvar[] ALIGN1 = "IFS= \t\n"; |
23 | const char defoptindvar[] ALIGN1 = "OPTIND=1"; | 23 | const char defoptindvar[] ALIGN1 = "OPTIND=1"; |
24 | 24 | ||
25 | |||
26 | int FAST_FUNC is_well_formed_var_name(const char *s, char terminator) | ||
27 | { | ||
28 | if (!s || !(isalpha(*s) || *s == '_')) | ||
29 | return 0; | ||
30 | |||
31 | do | ||
32 | s++; | ||
33 | while (isalnum(*s) || *s == '_'); | ||
34 | |||
35 | return *s == terminator; | ||
36 | } | ||
37 | |||
38 | /* read builtin */ | 25 | /* read builtin */ |
39 | 26 | ||
40 | /* Needs to be interruptible: shell must handle traps and shell-special signals | 27 | /* Needs to be interruptible: shell must handle traps and shell-special signals |
@@ -70,7 +57,7 @@ shell_builtin_read(struct builtin_read_params *params) | |||
70 | argv = params->argv; | 57 | argv = params->argv; |
71 | pp = argv; | 58 | pp = argv; |
72 | while (*pp) { | 59 | while (*pp) { |
73 | if (!is_well_formed_var_name(*pp, '\0')) { | 60 | if (endofname(*pp)[0] != '\0') { |
74 | /* Mimic bash message */ | 61 | /* Mimic bash message */ |
75 | bb_error_msg("read: '%s': not a valid identifier", *pp); | 62 | bb_error_msg("read: '%s': not a valid identifier", *pp); |
76 | return (const char *)(uintptr_t)1; | 63 | return (const char *)(uintptr_t)1; |
diff --git a/shell/shell_common.h b/shell/shell_common.h index a1323021d..7b478f1df 100644 --- a/shell/shell_common.h +++ b/shell/shell_common.h | |||
@@ -26,8 +26,6 @@ extern const char defifsvar[] ALIGN1; /* "IFS= \t\n" */ | |||
26 | 26 | ||
27 | extern const char defoptindvar[] ALIGN1; /* "OPTIND=1" */ | 27 | extern const char defoptindvar[] ALIGN1; /* "OPTIND=1" */ |
28 | 28 | ||
29 | int FAST_FUNC is_well_formed_var_name(const char *s, char terminator); | ||
30 | |||
31 | /* Builtins */ | 29 | /* Builtins */ |
32 | 30 | ||
33 | struct builtin_read_params { | 31 | struct builtin_read_params { |