hush: fix remaining known two bugs with IFS expansion. Closes 4027.
function old new delta expand_vars_to_list 1054 1140 +86 parse_stream 2425 2479 +54 expand_on_ifs 258 310 +52 builtin_umask 133 132 -1 done_word 820 779 -41 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 3/2 up/down: 192/-42) Total: 150 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
4fb53fb08c
commit
6e42b89b8d
82
shell/hush.c
82
shell/hush.c
@ -3265,14 +3265,6 @@ static int done_word(o_string *word, struct parse_context *ctx)
|
|||||||
) {
|
) {
|
||||||
p += 3;
|
p += 3;
|
||||||
}
|
}
|
||||||
if (p == word->data || p[0] != '\0') {
|
|
||||||
/* saw no "$@", or not only "$@" but some
|
|
||||||
* real text is there too */
|
|
||||||
/* insert "empty variable" reference, this makes
|
|
||||||
* e.g. "", $empty"" etc to not disappear */
|
|
||||||
o_addchr(word, SPECIAL_VAR_SYMBOL);
|
|
||||||
o_addchr(word, SPECIAL_VAR_SYMBOL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
command->argv = add_string_to_strings(command->argv, xstrdup(word->data));
|
command->argv = add_string_to_strings(command->argv, xstrdup(word->data));
|
||||||
debug_print_strings("word appended to argv", command->argv);
|
debug_print_strings("word appended to argv", command->argv);
|
||||||
@ -4516,20 +4508,30 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
break;
|
break;
|
||||||
case '\'':
|
case '\'':
|
||||||
dest.has_quoted_part = 1;
|
dest.has_quoted_part = 1;
|
||||||
while (1) {
|
if (next == '\'' && !ctx.pending_redirect) {
|
||||||
ch = i_getch(input);
|
insert_empty_quoted_str_marker:
|
||||||
if (ch == EOF) {
|
nommu_addchr(&ctx.as_string, next);
|
||||||
syntax_error_unterm_ch('\'');
|
i_getch(input); /* eat second ' */
|
||||||
goto parse_error;
|
o_addchr(&dest, SPECIAL_VAR_SYMBOL);
|
||||||
|
o_addchr(&dest, SPECIAL_VAR_SYMBOL);
|
||||||
|
} else {
|
||||||
|
while (1) {
|
||||||
|
ch = i_getch(input);
|
||||||
|
if (ch == EOF) {
|
||||||
|
syntax_error_unterm_ch('\'');
|
||||||
|
goto parse_error;
|
||||||
|
}
|
||||||
|
nommu_addchr(&ctx.as_string, ch);
|
||||||
|
if (ch == '\'')
|
||||||
|
break;
|
||||||
|
o_addqchr(&dest, ch);
|
||||||
}
|
}
|
||||||
nommu_addchr(&ctx.as_string, ch);
|
|
||||||
if (ch == '\'')
|
|
||||||
break;
|
|
||||||
o_addqchr(&dest, ch);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '"':
|
case '"':
|
||||||
dest.has_quoted_part = 1;
|
dest.has_quoted_part = 1;
|
||||||
|
if (next == '"' && !ctx.pending_redirect)
|
||||||
|
goto insert_empty_quoted_str_marker;
|
||||||
if (dest.o_assignment == NOT_ASSIGNMENT)
|
if (dest.o_assignment == NOT_ASSIGNMENT)
|
||||||
dest.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS;
|
dest.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS;
|
||||||
if (!encode_string(&ctx.as_string, &dest, input, '"', /*process_bkslash:*/ 1))
|
if (!encode_string(&ctx.as_string, &dest, input, '"', /*process_bkslash:*/ 1))
|
||||||
@ -4751,9 +4753,14 @@ static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len
|
|||||||
|
|
||||||
/* Store given string, finalizing the word and starting new one whenever
|
/* Store given string, finalizing the word and starting new one whenever
|
||||||
* we encounter IFS char(s). This is used for expanding variable values.
|
* we encounter IFS char(s). This is used for expanding variable values.
|
||||||
* End-of-string does NOT finalize word: think about 'echo -$VAR-' */
|
* End-of-string does NOT finalize word: think about 'echo -$VAR-'.
|
||||||
static int expand_on_ifs(o_string *output, int n, const char *str)
|
* Return in *ended_with_ifs:
|
||||||
|
* 1 - ended with IFS char, else 0 (this includes case of empty str).
|
||||||
|
*/
|
||||||
|
static int expand_on_ifs(int *ended_with_ifs, o_string *output, int n, const char *str)
|
||||||
{
|
{
|
||||||
|
int last_is_ifs = 0;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
int word_len;
|
int word_len;
|
||||||
|
|
||||||
@ -4774,27 +4781,36 @@ static int expand_on_ifs(o_string *output, int n, const char *str)
|
|||||||
/*o_addblock(output, str, word_len); - WRONG: "v='\*'; echo Z$v" prints "Z*" instead of "Z\*" */
|
/*o_addblock(output, str, word_len); - WRONG: "v='\*'; echo Z$v" prints "Z*" instead of "Z\*" */
|
||||||
/*o_addqblock(output, str, word_len); - WRONG: "v='*'; echo Z$v" prints "Z*" instead of Z* files */
|
/*o_addqblock(output, str, word_len); - WRONG: "v='*'; echo Z$v" prints "Z*" instead of Z* files */
|
||||||
}
|
}
|
||||||
|
last_is_ifs = 0;
|
||||||
str += word_len;
|
str += word_len;
|
||||||
if (!*str) /* EOL - do not finalize word */
|
if (!*str) /* EOL - do not finalize word */
|
||||||
break;
|
break;
|
||||||
goto finalize; /* optimization (can just fall thru) */
|
|
||||||
}
|
}
|
||||||
/* Case "v=' a'; echo ''$v": we do need to finalize empty word */
|
|
||||||
|
/* We know str here points to at least one IFS char */
|
||||||
|
last_is_ifs = 1;
|
||||||
|
str += strspn(str, G.ifs); /* skip IFS chars */
|
||||||
|
if (!*str) /* EOL - do not finalize word */
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Start new word... but not always! */
|
||||||
|
/* Case "v=' a'; echo ''$v": we do need to finalize empty word: */
|
||||||
if (output->has_quoted_part
|
if (output->has_quoted_part
|
||||||
/* Case "v=' a'; echo $v":
|
/* Case "v=' a'; echo $v":
|
||||||
* here nothing precedes the space in $v expansion,
|
* here nothing precedes the space in $v expansion,
|
||||||
* therefore we should not finish the word
|
* therefore we should not finish the word
|
||||||
* (IOW: if there *is* word to finalize, only then do it)
|
* (IOW: if there *is* word to finalize, only then do it):
|
||||||
*/
|
*/
|
||||||
|| (output->length && output->data[output->length - 1])
|
|| (n > 0 && output->data[output->length - 1])
|
||||||
) {
|
) {
|
||||||
finalize:
|
|
||||||
o_addchr(output, '\0');
|
o_addchr(output, '\0');
|
||||||
debug_print_list("expand_on_ifs", output, n);
|
debug_print_list("expand_on_ifs", output, n);
|
||||||
n = o_save_ptr(output, n);
|
n = o_save_ptr(output, n);
|
||||||
}
|
}
|
||||||
str += strspn(str, G.ifs); /* skip IFS chars */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ended_with_ifs)
|
||||||
|
*ended_with_ifs = last_is_ifs;
|
||||||
debug_print_list("expand_on_ifs[1]", output, n);
|
debug_print_list("expand_on_ifs[1]", output, n);
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
@ -5209,6 +5225,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
|
|||||||
* expansion of right-hand side of assignment == 1-element expand.
|
* expansion of right-hand side of assignment == 1-element expand.
|
||||||
*/
|
*/
|
||||||
char cant_be_null = 0; /* only bit 0x80 matters */
|
char cant_be_null = 0; /* only bit 0x80 matters */
|
||||||
|
int ended_in_ifs = 0; /* did last unquoted expansion end with IFS chars? */
|
||||||
char *p;
|
char *p;
|
||||||
|
|
||||||
debug_printf_expand("expand_vars_to_list: arg:'%s' singleword:%x\n", arg,
|
debug_printf_expand("expand_vars_to_list: arg:'%s' singleword:%x\n", arg,
|
||||||
@ -5227,6 +5244,13 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
|
|||||||
#if ENABLE_SH_MATH_SUPPORT
|
#if ENABLE_SH_MATH_SUPPORT
|
||||||
char arith_buf[sizeof(arith_t)*3 + 2];
|
char arith_buf[sizeof(arith_t)*3 + 2];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (ended_in_ifs) {
|
||||||
|
o_addchr(output, '\0');
|
||||||
|
n = o_save_ptr(output, n);
|
||||||
|
ended_in_ifs = 0;
|
||||||
|
}
|
||||||
|
|
||||||
o_addblock(output, arg, p - arg);
|
o_addblock(output, arg, p - arg);
|
||||||
debug_print_list("expand_vars_to_list[1]", output, n);
|
debug_print_list("expand_vars_to_list[1]", output, n);
|
||||||
arg = ++p;
|
arg = ++p;
|
||||||
@ -5255,7 +5279,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
|
|||||||
cant_be_null |= first_ch; /* do it for "$@" _now_, when we know it's not empty */
|
cant_be_null |= first_ch; /* do it for "$@" _now_, when we know it's not empty */
|
||||||
if (!(first_ch & 0x80)) { /* unquoted $* or $@ */
|
if (!(first_ch & 0x80)) { /* unquoted $* or $@ */
|
||||||
while (G.global_argv[i]) {
|
while (G.global_argv[i]) {
|
||||||
n = expand_on_ifs(output, n, G.global_argv[i]);
|
n = expand_on_ifs(NULL, output, n, G.global_argv[i]);
|
||||||
debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1);
|
debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1);
|
||||||
if (G.global_argv[i++][0] && G.global_argv[i]) {
|
if (G.global_argv[i++][0] && G.global_argv[i]) {
|
||||||
/* this argv[] is not empty and not last:
|
/* this argv[] is not empty and not last:
|
||||||
@ -5332,7 +5356,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
|
|||||||
debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val,
|
debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val,
|
||||||
!!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
|
!!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
|
||||||
if (val && val[0]) {
|
if (val && val[0]) {
|
||||||
n = expand_on_ifs(output, n, val);
|
n = expand_on_ifs(&ended_in_ifs, output, n, val);
|
||||||
val = NULL;
|
val = NULL;
|
||||||
}
|
}
|
||||||
} else { /* quoted $VAR, val will be appended below */
|
} else { /* quoted $VAR, val will be appended below */
|
||||||
@ -5361,6 +5385,10 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
|
|||||||
} /* end of "while (SPECIAL_VAR_SYMBOL is found) ..." */
|
} /* end of "while (SPECIAL_VAR_SYMBOL is found) ..." */
|
||||||
|
|
||||||
if (arg[0]) {
|
if (arg[0]) {
|
||||||
|
if (ended_in_ifs) {
|
||||||
|
o_addchr(output, '\0');
|
||||||
|
n = o_save_ptr(output, n);
|
||||||
|
}
|
||||||
debug_print_list("expand_vars_to_list[a]", output, n);
|
debug_print_list("expand_vars_to_list[a]", output, n);
|
||||||
/* this part is literal, and it was already pre-quoted
|
/* this part is literal, and it was already pre-quoted
|
||||||
* if needed (much earlier), do not use o_addQstr here! */
|
* if needed (much earlier), do not use o_addQstr here! */
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
Should be printed
|
Should be printed
|
||||||
|
Would not be printed by bash
|
||||||
|
Would not be printed by bash
|
||||||
|
Would not be printed by bash
|
||||||
Should be printed
|
Should be printed
|
||||||
Empty:
|
Empty:
|
||||||
Empty:
|
Empty:
|
||||||
|
@ -8,9 +8,9 @@ for a in "$@"; do echo Should not be printed; done
|
|||||||
# Yes, believe it or not, bash is mesmerized by "$@" and stops
|
# Yes, believe it or not, bash is mesmerized by "$@" and stops
|
||||||
# treating "" as "this word cannot be expanded to nothing,
|
# treating "" as "this word cannot be expanded to nothing,
|
||||||
# but must be at least null string". Now it can be expanded to nothing.
|
# but must be at least null string". Now it can be expanded to nothing.
|
||||||
for a in "$@"""; do echo Should not be printed; done
|
for a in "$@"""; do echo Would not be printed by bash; done
|
||||||
for a in """$@"; do echo Should not be printed; done
|
for a in """$@"; do echo Would not be printed by bash; done
|
||||||
for a in """$@"''"$@"''; do echo Should not be printed; done
|
for a in """$@"''"$@"''; do echo Would not be printed by bash; done
|
||||||
for a in ""; do echo Should be printed; done
|
for a in ""; do echo Should be printed; done
|
||||||
|
|
||||||
# Bug 207: "$@" expands to nothing, and we erroneously glob "%s\n" twice:
|
# Bug 207: "$@" expands to nothing, and we erroneously glob "%s\n" twice:
|
||||||
|
Loading…
Reference in New Issue
Block a user