hush: fix hush-bugs/glob_and_vars.tests testcase:

globbing is now done _after_ variable/`cmd` substitution

function                                             old     new   delta
expand_strvec_to_strvec                                7     353    +346
expand_variables                                    1348    1383     +35
add_string_to_strings                                  -      28     +28
globhack                                             114       -    -114
done_word                                            778     579    -199
------------------------------------------------------------------------------
(add/remove: 1/1 grow/shrink: 2/1 up/down: 409/-313)           Total: 96 bytes
This commit is contained in:
Denis Vlasenko 2008-06-16 12:47:11 +00:00
parent 31e1286365
commit 82dfec3e4e
6 changed files with 70 additions and 47 deletions

View File

@ -481,10 +481,6 @@ static int run_list(struct pipe *pi);
static void pseudo_exec_argv(char **ptrs2free, char **argv) ATTRIBUTE_NORETURN; static void pseudo_exec_argv(char **ptrs2free, char **argv) ATTRIBUTE_NORETURN;
static void pseudo_exec(char **ptrs2free, struct child_prog *child) ATTRIBUTE_NORETURN; static void pseudo_exec(char **ptrs2free, struct child_prog *child) ATTRIBUTE_NORETURN;
static int run_pipe(struct pipe *pi); static int run_pipe(struct pipe *pi);
/* extended glob support: */
static char **globhack(const char *src, char **strings);
static int glob_needed(const char *s);
static int xglob(o_string *dest, char ***pglob);
/* variable assignment: */ /* variable assignment: */
static int is_assignment(const char *s); static int is_assignment(const char *s);
/* data structure manipulation: */ /* data structure manipulation: */
@ -591,6 +587,17 @@ static char **alloc_ptrs(char **argv)
} }
#endif #endif
#ifdef DEBUG_EXPAND
static void debug_print_strings(const char *prefix, char **vv)
{
fprintf(stderr, "%s:\n", prefix);
while (*vv)
fprintf(stderr, " '%s'\n", *vv++);
}
#else
#define debug_print_strings(prefix, vv) ((void)0)
#endif
/* Function prototypes for builtins */ /* Function prototypes for builtins */
static int builtin_cd(char **argv); static int builtin_cd(char **argv);
@ -2280,7 +2287,9 @@ static int run_list(struct pipe *pi)
if (!pi->next->progs->argv) if (!pi->next->progs->argv)
continue; continue;
/* create list of variable values */ /* create list of variable values */
debug_print_strings("for_list made from", pi->next->progs->argv);
for_list = expand_strvec_to_strvec(pi->next->progs->argv); for_list = expand_strvec_to_strvec(pi->next->progs->argv);
debug_print_strings("for_list", for_list);
for_lcur = for_list; for_lcur = for_list;
for_varname = pi->progs->argv[0]; for_varname = pi->progs->argv[0];
pi->progs->argv[0] = NULL; pi->progs->argv[0] = NULL;
@ -2465,13 +2474,10 @@ static int run_and_free_list(struct pipe *pi)
return rcode; return rcode;
} }
/* The API for glob is arguably broken. This routine pushes a non-matching /* Remove non-backslashed backslashes and add to "strings" vector.
* string into the output structure, removing non-backslashed backslashes.
* If someone can prove me wrong, by performing this function within the
* original glob(3) api, feel free to rewrite this routine into oblivion.
* XXX broken if the last character is '\\', check that before calling. * XXX broken if the last character is '\\', check that before calling.
*/ */
static char **globhack(const char *src, char **strings) static char **add_unq_string_to_strings(char **strings, const char *src)
{ {
int cnt; int cnt;
const char *s; const char *s;
@ -2503,33 +2509,20 @@ static int glob_needed(const char *s)
return 0; return 0;
} }
static int xglob(o_string *dest, char ***pglob) static int xglob(char ***pglob, const char *pattern)
{ {
/* short-circuit for null word */ if (glob_needed(pattern)) {
/* we can code this better when the debug_printf's are gone */
if (dest->length == 0) {
if (dest->nonnull) {
/* bash man page calls this an "explicit" null */
*pglob = globhack(dest->data, *pglob);
}
return 0;
}
if (glob_needed(dest->data)) {
glob_t globdata; glob_t globdata;
int gr; int gr;
memset(&globdata, 0, sizeof(globdata)); memset(&globdata, 0, sizeof(globdata));
gr = glob(dest->data, 0, NULL, &globdata); gr = glob(pattern, 0, NULL, &globdata);
debug_printf("glob returned %d\n", gr); debug_printf("glob returned %d\n", gr);
if (gr == GLOB_NOSPACE) if (gr == GLOB_NOSPACE)
bb_error_msg_and_die("out of memory during glob"); bb_error_msg_and_die("out of memory during glob");
if (gr == GLOB_NOMATCH) { if (gr == GLOB_NOMATCH) {
debug_printf("globhack returned %d\n", gr);
/* quote removal, or more accurately, backslash removal */
*pglob = globhack(dest->data, *pglob);
globfree(&globdata); globfree(&globdata);
return 0; goto literal;
} }
if (gr != 0) { /* GLOB_ABORTED ? */ if (gr != 0) { /* GLOB_ABORTED ? */
bb_error_msg("glob(3) error %d", gr); bb_error_msg("glob(3) error %d", gr);
@ -2540,7 +2533,10 @@ static int xglob(o_string *dest, char ***pglob)
return gr; return gr;
} }
*pglob = globhack(dest->data, *pglob); literal:
/* quote removal, or more accurately, backslash removal */
*pglob = add_unq_string_to_strings(*pglob, pattern);
debug_print_strings("after xglob", *pglob);
return 0; return 0;
} }
@ -2733,7 +2729,7 @@ static char **expand_variables(char **argv, char or_mask)
n = expand_vars_to_list(&output, n, *v++, or_mask); n = expand_vars_to_list(&output, n, *v++, or_mask);
o_debug_list("expand_variables", &output, n); o_debug_list("expand_variables", &output, n);
/* output.data (malloced) gets returned in "list" */ /* output.data (malloced in one block) gets returned in "list" */
list = o_finalize_list(&output, n); list = o_finalize_list(&output, n);
#ifdef DEBUG_EXPAND #ifdef DEBUG_EXPAND
@ -2750,9 +2746,27 @@ static char **expand_variables(char **argv, char or_mask)
static char **expand_strvec_to_strvec(char **argv) static char **expand_strvec_to_strvec(char **argv)
{ {
return expand_variables(argv, 0); char **exp;
char **res = NULL;
debug_print_strings("expand_strvec_to_strvec: pre expand", argv);
exp = argv = expand_variables(argv, 0);
debug_print_strings("expand_strvec_to_strvec: post expand", argv);
while (*argv) {
int r = xglob(&res, *argv);
if (r)
bb_error_msg("xglob returned %d on '%s'", r, *argv);
//TODO: testcase for bad glob pattern behavior
argv++;
}
free(exp);
debug_print_strings("expand_strvec_to_strvec: res", res);
return res;
} }
/* used for expansion of right hand of assignments */
/* NB: should NOT do globbing! "export v=/bin/c*; env | grep ^v=" outputs
* "v=/bin/c*" */
static char *expand_string_to_string(const char *str) static char *expand_string_to_string(const char *str)
{ {
char *argv[2], **list; char *argv[2], **list;
@ -2769,6 +2783,7 @@ static char *expand_string_to_string(const char *str)
return (char*)list; return (char*)list;
} }
/* used for eval */
static char* expand_strvec_to_string(char **argv) static char* expand_strvec_to_string(char **argv)
{ {
char **list; char **list;
@ -3106,7 +3121,6 @@ static int done_word(o_string *word, struct p_context *ctx)
{ {
struct child_prog *child = ctx->child; struct child_prog *child = ctx->child;
char ***glob_target; char ***glob_target;
int gr;
debug_printf_parse("done_word entered: '%s' %p\n", word->data, child); debug_printf_parse("done_word entered: '%s' %p\n", word->data, child);
if (word->length == 0 && !word->nonnull) { if (word->length == 0 && !word->nonnull) {
@ -3131,12 +3145,10 @@ static int done_word(o_string *word, struct p_context *ctx)
} }
glob_target = &child->argv; glob_target = &child->argv;
} }
//BUG! globbing should be done after variable expansion!
//See glob_and_vars testcase if (word->length || word->nonnull) {
gr = xglob(word, glob_target); *glob_target = add_string_to_strings(*glob_target, xstrdup(word->data));
if (gr != 0) { debug_print_strings("glob_target appended", *glob_target);
debug_printf_parse("done_word return 1: xglob returned %d\n", gr);
return 1;
} }
o_reset(word); o_reset(word);
@ -3371,6 +3383,11 @@ static int process_command_subs(o_string *dest,
o_addQchr(dest, '\n'); o_addQchr(dest, '\n');
eol_cnt--; eol_cnt--;
} }
/* Even unquoted `echo '\'` results in two backslashes
* (which are converted into one by globbing later) */
if (!dest->o_quote && ch == '\\') {
o_addchr(dest, ch);
}
o_addQchr(dest, ch); o_addQchr(dest, ch);
} }
@ -3440,7 +3457,7 @@ static void add_till_single_quote(o_string *dest, struct in_str *input)
break; break;
if (ch == '\'') if (ch == '\'')
break; break;
o_addqchr(dest, ch); o_addchr(dest, ch);
} }
} }
/* "...\"...`..`...." - do we need to handle "...$(..)..." too? */ /* "...\"...`..`...." - do we need to handle "...$(..)..." too? */
@ -3451,15 +3468,15 @@ static void add_till_double_quote(o_string *dest, struct in_str *input)
if (ch == '"') if (ch == '"')
break; break;
if (ch == '\\') { /* \x. Copy both chars. */ if (ch == '\\') { /* \x. Copy both chars. */
o_addqchr(dest, ch); o_addchr(dest, ch);
ch = i_getch(input); ch = i_getch(input);
} }
if (ch == EOF) if (ch == EOF)
break; break;
o_addqchr(dest, ch); o_addchr(dest, ch);
if (ch == '`') { if (ch == '`') {
add_till_backquote(dest, input); add_till_backquote(dest, input);
o_addqchr(dest, ch); o_addchr(dest, ch);
continue; continue;
} }
//if (ch == '$') ... //if (ch == '$') ...
@ -3488,12 +3505,12 @@ static void add_till_backquote(o_string *dest, struct in_str *input)
if (ch == '\\') { /* \x. Copy both chars unless it is \` */ if (ch == '\\') { /* \x. Copy both chars unless it is \` */
int ch2 = i_getch(input); int ch2 = i_getch(input);
if (ch2 != '`' && ch2 != '$' && ch2 != '\\') if (ch2 != '`' && ch2 != '$' && ch2 != '\\')
o_addqchr(dest, ch); o_addchr(dest, ch);
ch = ch2; ch = ch2;
} }
if (ch == EOF) if (ch == EOF)
break; break;
o_addqchr(dest, ch); o_addchr(dest, ch);
} }
} }
/* Process $(cmd) - copy contents until ")" is seen. Complicated by /* Process $(cmd) - copy contents until ")" is seen. Complicated by
@ -3520,22 +3537,22 @@ static void add_till_closing_curly_brace(o_string *dest, struct in_str *input)
if (ch == ')') if (ch == ')')
if (--count < 0) if (--count < 0)
break; break;
o_addqchr(dest, ch); o_addchr(dest, ch);
if (ch == '\'') { if (ch == '\'') {
add_till_single_quote(dest, input); add_till_single_quote(dest, input);
o_addqchr(dest, ch); o_addchr(dest, ch);
continue; continue;
} }
if (ch == '"') { if (ch == '"') {
add_till_double_quote(dest, input); add_till_double_quote(dest, input);
o_addqchr(dest, ch); o_addchr(dest, ch);
continue; continue;
} }
if (ch == '\\') { /* \x. Copy verbatim. Important for \(, \) */ if (ch == '\\') { /* \x. Copy verbatim. Important for \(, \) */
ch = i_getch(input); ch = i_getch(input);
if (ch == EOF) if (ch == EOF)
break; break;
o_addqchr(dest, ch); o_addchr(dest, ch);
continue; continue;
} }
} }

View File

@ -0,0 +1 @@
OK: 0

View File

@ -0,0 +1,3 @@
false
for a in; do echo "HELLO"; done
echo OK: $?

View File

@ -1,2 +1,2 @@
v=. v=.
echo $v/glob_and_vars.* echo $v/glob_and_vars.[tr]*

View File

@ -0,0 +1,2 @@
v=.
echo $v/glob_and_vars.[tr]*