aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2019-05-14 18:53:24 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2019-05-14 18:56:04 +0200
commitd8bd7012a30c6ce9efe26d06880ac223143709ad (patch)
treee7cfd214658e2fbece84619b03e193fbe6f659cf
parent875ce094cf2d421ba05bed6cfd6c948084d52abe (diff)
downloadbusybox-d8bd7012a30c6ce9efe26d06880ac223143709ad.tar.gz
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 <vda.linux@googlemail.com>
-rw-r--r--shell/hush.c55
-rw-r--r--shell/shell_common.c15
-rw-r--r--shell/shell_common.h2
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)
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))
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 @@
const char defifsvar[] ALIGN1 = "IFS= \t\n";
const char defoptindvar[] ALIGN1 = "OPTIND=1";
-
-int FAST_FUNC is_well_formed_var_name(const char *s, char terminator)
-{
- if (!s || !(isalpha(*s) || *s == '_'))
- return 0;
-
- do
- s++;
- while (isalnum(*s) || *s == '_');
-
- return *s == terminator;
-}
-
/* read builtin */
/* Needs to be interruptible: shell must handle traps and shell-special signals
@@ -70,7 +57,7 @@ shell_builtin_read(struct builtin_read_params *params)
argv = params->argv;
pp = argv;
while (*pp) {
- if (!is_well_formed_var_name(*pp, '\0')) {
+ if (endofname(*pp)[0] != '\0') {
/* Mimic bash message */
bb_error_msg("read: '%s': not a valid identifier", *pp);
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" */
extern const char defoptindvar[] ALIGN1; /* "OPTIND=1" */
-int FAST_FUNC is_well_formed_var_name(const char *s, char terminator);
-
/* Builtins */
struct builtin_read_params {