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>
This commit is contained in:
parent
875ce094cf
commit
d8bd7012a3
55
shell/hush.c
55
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))
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user