From 602d13cba552fadb8481283aa7872a4b9f206c48 Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Sun, 13 May 2007 18:34:53 +0000 Subject: [PATCH] hush: fix '{ false; echo $?; }' bug. hush: expand testsuite. variable expansion is still very broken --- shell/hush.c | 204 +++++++++++---------- shell/hush_test/hush-bugs/argv0.right | 1 + shell/hush_test/hush-bugs/argv0.tests | 4 + shell/hush_test/hush-bugs/quote1.right | 1 + shell/hush_test/hush-bugs/quote1.tests | 2 + shell/hush_test/hush-bugs/quote2.right | 1 + shell/hush_test/hush-bugs/quote2.tests | 2 + shell/hush_test/hush-bugs/starquoted.right | 1 + shell/hush_test/hush-bugs/starquoted.tests | 4 + shell/hush_test/hush-vars/star.right | 5 + shell/hush_test/hush-vars/star.tests | 8 + shell/hush_test/hush-vars/var.right | 2 +- shell/hush_test/hush-vars/var.tests | 1 - 13 files changed, 138 insertions(+), 98 deletions(-) create mode 100644 shell/hush_test/hush-bugs/argv0.right create mode 100755 shell/hush_test/hush-bugs/argv0.tests create mode 100644 shell/hush_test/hush-bugs/quote1.right create mode 100755 shell/hush_test/hush-bugs/quote1.tests create mode 100644 shell/hush_test/hush-bugs/quote2.right create mode 100755 shell/hush_test/hush-bugs/quote2.tests create mode 100644 shell/hush_test/hush-bugs/starquoted.right create mode 100755 shell/hush_test/hush-bugs/starquoted.tests create mode 100644 shell/hush_test/hush-vars/star.right create mode 100755 shell/hush_test/hush-vars/star.tests diff --git a/shell/hush.c b/shell/hush.c index 04fb00dc7..dfb819122 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -371,7 +371,7 @@ static int b_check_space(o_string *o, int len); static int b_addchr(o_string *o, int ch); static void b_reset(o_string *o); static int b_addqchr(o_string *o, int ch, int quote); -static int b_adduint(o_string *o, unsigned i); +//static int b_adduint(o_string *o, unsigned i); /* in_str manipulations: */ static int static_get(struct in_str *i); static int static_peek(struct in_str *i); @@ -413,7 +413,7 @@ static int parse_group(o_string *dest, struct p_context *ctx, struct in_str *inp static const char *lookup_param(const char *src); static char *make_string(char **inp); static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input); -static int parse_string(o_string *dest, struct p_context *ctx, const char *src); +//static int parse_string(o_string *dest, struct p_context *ctx, const char *src); static int parse_stream(o_string *dest, struct p_context *ctx, struct in_str *input0, const char *end_trigger); /* setup: */ static int parse_stream_outer(struct in_str *inp, int parse_flag); @@ -1015,17 +1015,17 @@ static int b_addqchr(o_string *o, int ch, int quote) return b_addchr(o, ch); } -static int b_adduint(o_string *o, unsigned i) -{ - int r; - char buf[sizeof(unsigned)*3 + 1]; - char *p = buf; - *(utoa_to_buf(i, buf, sizeof(buf))) = '\0'; - /* no escape checking necessary */ - do r = b_addchr(o, *p++); while (r == 0 && *p); - return r; -} - +//static int b_adduint(o_string *o, unsigned i) +//{ +// int r; +// char buf[sizeof(unsigned)*3 + 1]; +// char *p = buf; +// *(utoa_to_buf(i, buf, sizeof(buf))) = '\0'; +// /* no escape checking necessary */ +// do r = b_addchr(o, *p++); while (r == 0 && *p); +// return r; +//} +// static int static_get(struct in_str *i) { int ch = *i->p++; @@ -1288,9 +1288,7 @@ static void pseudo_exec_argv(char **argv) getpid(), argv[i]); // FIXME: vfork case?? p = insert_var_value(argv[i]); - putenv(strdup(p)); - if (p != argv[i]) - free(p); + putenv(p == argv[i] ? xstrdup(p) : p); } argv += i; /* If a variable is assigned in a forest, and nobody listens, @@ -1699,10 +1697,11 @@ static int run_pipe_real(struct pipe *pi) } for (i = 0; is_assignment(argv[i]); i++) { p = insert_var_value(argv[i]); - putenv(strdup(p)); if (p != argv[i]) { child->sp--; - free(p); + putenv(p); + } else { + putenv(xstrdup(p)); } } if (child->sp) { @@ -2292,39 +2291,42 @@ static int xglob(o_string *dest, int flags, glob_t *pglob) static char **make_list_in(char **inp, char *name) { int len, i; +#if 0 int name_len = strlen(name); - int n = 0; +#endif + int n; char **list; char *p1, *p2, *p3; /* create list of variable values */ list = xmalloc(sizeof(*list)); + n = 0; for (i = 0; inp[i]; i++) { p3 = insert_var_value(inp[i]); p1 = p3; while (*p1) { - if ((*p1 == ' ')) { + if (*p1 == ' ') { p1++; continue; } - p2 = strchr(p1, ' '); - if (p2) { - len = p2 - p1; - } else { - len = strlen(p1); - p2 = p1 + len; - } + p2 = strchrnul(p1, ' '); + len = p2 - p1; /* we use n + 2 in realloc for list, because we add * new element and then we will add NULL element */ list = xrealloc(list, sizeof(*list) * (n + 2)); + list[n] = xasprintf("%s=%.*s", name, len, p1); +#if 0 /* faster, but more code */ list[n] = xmalloc(2 + name_len + len); strcpy(list[n], name); - strcat(list[n], "="); - strncat(list[n], p1, len); - list[n++][name_len + len + 1] = '\0'; + list[n][name_len] = '='; + strncat(&(list[n][name_len + 1]), p1, len); + list[n][name_len + len + 1] = '\0'; +#endif + n++; p1 = p2; } - if (p3 != inp[i]) free(p3); + if (p3 != inp[i]) + free(p3); } list[n] = NULL; return list; @@ -2335,8 +2337,10 @@ static char *insert_var_value(char *inp) int res_str_len = 0; int len; int done = 0; - char *p, *res_str = NULL; + int i; const char *p1; + char *p, *p2; + char *res_str = NULL; while ((p = strchr(inp, SPECIAL_VAR_SYMBOL))) { if (p != inp) { @@ -2348,11 +2352,42 @@ static char *insert_var_value(char *inp) inp = ++p; p = strchr(inp, SPECIAL_VAR_SYMBOL); *p = '\0'; - p1 = lookup_param(inp); + + switch (inp[0]) { + case '$': + /* FIXME: (echo $$) should still print pid of main shell */ + p1 = utoa(getpid()); + break; + case '!': + p1 = last_bg_pid ? utoa(last_bg_pid) : (char*)""; + break; + case '?': + p1 = utoa(last_return_code); + break; + case '#': + p1 = utoa(global_argc ? global_argc-1 : 0); + break; + case '*': + case '@': /* FIXME: we treat $@ as $* for now */ + len = 1; + for (i = 1; i < global_argc; i++) + len += strlen(global_argv[i]) + 1; + p1 = p2 = alloca(--len); + for (i = 1; i < global_argc; i++) { + strcpy(p2, global_argv[i]); + p2 += strlen(global_argv[i]); + *p2++ = ifs[0]; + } + *--p2 = '\0'; + break; + default: + p1 = lookup_param(inp); + } + if (p1) { len = res_str_len + strlen(p1); - res_str = xrealloc(res_str, (1 + len)); - strcpy((res_str + res_str_len), p1); + res_str = xrealloc(res_str, 1 + len); + strcpy(res_str + res_str_len, p1); res_str_len = len; } *p = SPECIAL_VAR_SYMBOL; @@ -2956,21 +2991,20 @@ static char* make_string(char ** inp) char *p; char *str = NULL; int n; - int len = 2; + int val_len; + int len = 0; for (n = 0; inp[n]; n++) { p = insert_var_value(inp[n]); - str = xrealloc(str, (len + strlen(p))); - if (n) { - strcat(str, " "); - } else { - *str = '\0'; - } - strcat(str, p); - len = strlen(str) + 3; + val_len = strlen(p); + str = xrealloc(str, len + val_len + 3); /* +3: space, '\n', */ + str[len++] = ' '; + strcpy(str + len, p); + len += val_len; if (p != inp[n]) free(p); } - len = strlen(str); + /* We do not check for case where loop had no iterations at all + * - cannot happen? */ str[len] = '\n'; str[len+1] = '\0'; return str; @@ -2979,46 +3013,39 @@ static char* make_string(char ** inp) /* return code: 0 for OK, 1 for syntax error */ static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input) { - int i, advance = 0; - char sep[] = " "; - int ch = input->peek(input); /* first character after the $ */ +// int i; +// char sep[] = " "; + int ch = b_peek(input); /* first character after the $ */ debug_printf_parse("handle_dollar entered: ch='%c'\n", ch); - if (isalpha(ch)) { + if (isalpha(ch) || ch == '?') { b_addchr(dest, SPECIAL_VAR_SYMBOL); ctx->child->sp++; while (1) { - ch = b_peek(input); - if (!isalnum(ch) && ch != '_') - break; debug_printf_parse(": '%c'\n", ch); b_getch(input); b_addchr(dest, ch); + ch = b_peek(input); + if (!isalnum(ch) && ch != '_') + break; } b_addchr(dest, SPECIAL_VAR_SYMBOL); } else if (isdigit(ch)) { - i = ch - '0'; /* XXX is $0 special? */ - if (i < global_argc) { - parse_string(dest, ctx, global_argv[i]); /* recursion */ - } - advance = 1; + make_one_char_var: + b_addchr(dest, SPECIAL_VAR_SYMBOL); + ctx->child->sp++; + debug_printf_parse(": '%c'\n", ch); + b_getch(input); + b_addchr(dest, ch); + b_addchr(dest, SPECIAL_VAR_SYMBOL); } else switch (ch) { - case '$': - b_adduint(dest, getpid()); - advance = 1; - break; - case '!': - if (last_bg_pid > 0) b_adduint(dest, last_bg_pid); - advance = 1; - break; - case '?': - b_adduint(dest, last_return_code); - advance = 1; - break; - case '#': - b_adduint(dest, global_argc ? global_argc-1 : 0); - advance = 1; - break; + case '$': /* pid */ + case '!': /* last bg pid */ + case '?': /* last exit code */ + case '#': /* number of args */ + case '*': /* args */ + case '@': /* args */ + goto make_one_char_var; case '{': b_addchr(dest, SPECIAL_VAR_SYMBOL); ctx->child->sp++; @@ -3042,15 +3069,6 @@ static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *i b_getch(input); process_command_subs(dest, ctx, input, ")"); break; - case '*': - sep[0] = ifs[0]; - for (i = 1; i < global_argc; i++) { - parse_string(dest, ctx, global_argv[i]); - if (i+1 < global_argc) - parse_string(dest, ctx, sep); - } - break; - case '@': case '-': case '_': /* still unhandled, but should be eventually */ @@ -3058,25 +3076,19 @@ static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *i return 1; break; default: - b_addqchr(dest,'$', dest->quote); + b_addqchr(dest, '$', dest->quote); } - /* Eat the character if the flag was set. If the compiler - * is smart enough, we could substitute "b_getch(input);" - * for all the "advance = 1;" above, and also end up with - * a nice size-optimized program. Hah! That'll be the day. - */ - if (advance) b_getch(input); debug_printf_parse("handle_dollar return 0\n"); return 0; } -static int parse_string(o_string *dest, struct p_context *ctx, const char *src) -{ - struct in_str foo; - setup_string_in_str(&foo, src); - return parse_stream(dest, ctx, &foo, NULL); -} - +//static int parse_string(o_string *dest, struct p_context *ctx, const char *src) +//{ +// struct in_str foo; +// setup_string_in_str(&foo, src); +// return parse_stream(dest, ctx, &foo, NULL); +//} +// /* return code is 0 for normal exit, 1 for syntax error */ static int parse_stream(o_string *dest, struct p_context *ctx, struct in_str *input, const char *end_trigger) diff --git a/shell/hush_test/hush-bugs/argv0.right b/shell/hush_test/hush-bugs/argv0.right new file mode 100644 index 000000000..d86bac9de --- /dev/null +++ b/shell/hush_test/hush-bugs/argv0.right @@ -0,0 +1 @@ +OK diff --git a/shell/hush_test/hush-bugs/argv0.tests b/shell/hush_test/hush-bugs/argv0.tests new file mode 100755 index 000000000..f5c40f6fe --- /dev/null +++ b/shell/hush_test/hush-bugs/argv0.tests @@ -0,0 +1,4 @@ +if test $# = 0; then + exec "$THIS_SH" "$0" arg +fi +echo OK diff --git a/shell/hush_test/hush-bugs/quote1.right b/shell/hush_test/hush-bugs/quote1.right new file mode 100644 index 000000000..cb382054c --- /dev/null +++ b/shell/hush_test/hush-bugs/quote1.right @@ -0,0 +1 @@ +'1' diff --git a/shell/hush_test/hush-bugs/quote1.tests b/shell/hush_test/hush-bugs/quote1.tests new file mode 100755 index 000000000..f55895466 --- /dev/null +++ b/shell/hush_test/hush-bugs/quote1.tests @@ -0,0 +1,2 @@ +a=1 +echo "'$a'" diff --git a/shell/hush_test/hush-bugs/quote2.right b/shell/hush_test/hush-bugs/quote2.right new file mode 100644 index 000000000..3bc9edcd6 --- /dev/null +++ b/shell/hush_test/hush-bugs/quote2.right @@ -0,0 +1 @@ +>1 diff --git a/shell/hush_test/hush-bugs/quote2.tests b/shell/hush_test/hush-bugs/quote2.tests new file mode 100755 index 000000000..bd966f30b --- /dev/null +++ b/shell/hush_test/hush-bugs/quote2.tests @@ -0,0 +1,2 @@ +a=1 +echo ">$a" diff --git a/shell/hush_test/hush-bugs/starquoted.right b/shell/hush_test/hush-bugs/starquoted.right new file mode 100644 index 000000000..fedaf4805 --- /dev/null +++ b/shell/hush_test/hush-bugs/starquoted.right @@ -0,0 +1 @@ +.1 abc d e f. diff --git a/shell/hush_test/hush-bugs/starquoted.tests b/shell/hush_test/hush-bugs/starquoted.tests new file mode 100755 index 000000000..3be2026b7 --- /dev/null +++ b/shell/hush_test/hush-bugs/starquoted.tests @@ -0,0 +1,4 @@ +if test $# = 0; then + exec "$THIS_SH" starquoted.tests 1 abc 'd e f' +fi +for a in "$*"; do echo ".$a."; done diff --git a/shell/hush_test/hush-vars/star.right b/shell/hush_test/hush-vars/star.right new file mode 100644 index 000000000..b33b176b2 --- /dev/null +++ b/shell/hush_test/hush-vars/star.right @@ -0,0 +1,5 @@ +.1. +.abc. +.d. +.e. +.f. diff --git a/shell/hush_test/hush-vars/star.tests b/shell/hush_test/hush-vars/star.tests new file mode 100755 index 000000000..1914dde44 --- /dev/null +++ b/shell/hush_test/hush-vars/star.tests @@ -0,0 +1,8 @@ +if test $# = 0; then + exec "$THIS_SH" star.tests 1 abc 'd e f' +fi +# 'd e f' should be split into 3 separate args: +for a in $*; do echo ".$a."; done + +# must produce .1 abc d e f. Currently does not +#for a in "$*"; do echo ".$a."; done diff --git a/shell/hush_test/hush-vars/var.right b/shell/hush_test/hush-vars/var.right index c13b98e38..14b2314d9 100644 --- a/shell/hush_test/hush-vars/var.right +++ b/shell/hush_test/hush-vars/var.right @@ -1,4 +1,4 @@ http://busybox.net http://busybox.net_abc 1 -0 +1 diff --git a/shell/hush_test/hush-vars/var.tests b/shell/hush_test/hush-vars/var.tests index b0637ea6b..0a63696c9 100755 --- a/shell/hush_test/hush-vars/var.tests +++ b/shell/hush_test/hush-vars/var.tests @@ -6,5 +6,4 @@ echo ${URL}_abc true false; echo $? true -# BUG: prints 0, must be 1 { false; echo $?; } -- 2.25.1