hush: fix '{ false; echo $?; }' bug.

hush: expand testsuite. variable expansion is still very broken
This commit is contained in:
Denis Vlasenko
2007-05-13 18:34:53 +00:00
parent 119b995437
commit 602d13cba5
13 changed files with 138 additions and 98 deletions

View File

@@ -371,7 +371,7 @@ static int b_check_space(o_string *o, int len);
static int b_addchr(o_string *o, int ch); static int b_addchr(o_string *o, int ch);
static void b_reset(o_string *o); static void b_reset(o_string *o);
static int b_addqchr(o_string *o, int ch, int quote); 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: */ /* in_str manipulations: */
static int static_get(struct in_str *i); static int static_get(struct in_str *i);
static int static_peek(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 const char *lookup_param(const char *src);
static char *make_string(char **inp); static char *make_string(char **inp);
static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input); 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); static int parse_stream(o_string *dest, struct p_context *ctx, struct in_str *input0, const char *end_trigger);
/* setup: */ /* setup: */
static int parse_stream_outer(struct in_str *inp, int parse_flag); 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); return b_addchr(o, ch);
} }
static int b_adduint(o_string *o, unsigned i) //static int b_adduint(o_string *o, unsigned i)
{ //{
int r; // int r;
char buf[sizeof(unsigned)*3 + 1]; // char buf[sizeof(unsigned)*3 + 1];
char *p = buf; // char *p = buf;
*(utoa_to_buf(i, buf, sizeof(buf))) = '\0'; // *(utoa_to_buf(i, buf, sizeof(buf))) = '\0';
/* no escape checking necessary */ // /* no escape checking necessary */
do r = b_addchr(o, *p++); while (r == 0 && *p); // do r = b_addchr(o, *p++); while (r == 0 && *p);
return r; // return r;
} //}
//
static int static_get(struct in_str *i) static int static_get(struct in_str *i)
{ {
int ch = *i->p++; int ch = *i->p++;
@@ -1288,9 +1288,7 @@ static void pseudo_exec_argv(char **argv)
getpid(), argv[i]); getpid(), argv[i]);
// FIXME: vfork case?? // FIXME: vfork case??
p = insert_var_value(argv[i]); p = insert_var_value(argv[i]);
putenv(strdup(p)); putenv(p == argv[i] ? xstrdup(p) : p);
if (p != argv[i])
free(p);
} }
argv += i; argv += i;
/* If a variable is assigned in a forest, and nobody listens, /* 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++) { for (i = 0; is_assignment(argv[i]); i++) {
p = insert_var_value(argv[i]); p = insert_var_value(argv[i]);
putenv(strdup(p));
if (p != argv[i]) { if (p != argv[i]) {
child->sp--; child->sp--;
free(p); putenv(p);
} else {
putenv(xstrdup(p));
} }
} }
if (child->sp) { 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) static char **make_list_in(char **inp, char *name)
{ {
int len, i; int len, i;
#if 0
int name_len = strlen(name); int name_len = strlen(name);
int n = 0; #endif
int n;
char **list; char **list;
char *p1, *p2, *p3; char *p1, *p2, *p3;
/* create list of variable values */ /* create list of variable values */
list = xmalloc(sizeof(*list)); list = xmalloc(sizeof(*list));
n = 0;
for (i = 0; inp[i]; i++) { for (i = 0; inp[i]; i++) {
p3 = insert_var_value(inp[i]); p3 = insert_var_value(inp[i]);
p1 = p3; p1 = p3;
while (*p1) { while (*p1) {
if ((*p1 == ' ')) { if (*p1 == ' ') {
p1++; p1++;
continue; continue;
} }
p2 = strchr(p1, ' '); p2 = strchrnul(p1, ' ');
if (p2) { len = p2 - p1;
len = p2 - p1;
} else {
len = strlen(p1);
p2 = p1 + len;
}
/* we use n + 2 in realloc for list, because we add /* we use n + 2 in realloc for list, because we add
* new element and then we will add NULL element */ * new element and then we will add NULL element */
list = xrealloc(list, sizeof(*list) * (n + 2)); 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); list[n] = xmalloc(2 + name_len + len);
strcpy(list[n], name); strcpy(list[n], name);
strcat(list[n], "="); list[n][name_len] = '=';
strncat(list[n], p1, len); strncat(&(list[n][name_len + 1]), p1, len);
list[n++][name_len + len + 1] = '\0'; list[n][name_len + len + 1] = '\0';
#endif
n++;
p1 = p2; p1 = p2;
} }
if (p3 != inp[i]) free(p3); if (p3 != inp[i])
free(p3);
} }
list[n] = NULL; list[n] = NULL;
return list; return list;
@@ -2335,8 +2337,10 @@ static char *insert_var_value(char *inp)
int res_str_len = 0; int res_str_len = 0;
int len; int len;
int done = 0; int done = 0;
char *p, *res_str = NULL; int i;
const char *p1; const char *p1;
char *p, *p2;
char *res_str = NULL;
while ((p = strchr(inp, SPECIAL_VAR_SYMBOL))) { while ((p = strchr(inp, SPECIAL_VAR_SYMBOL))) {
if (p != inp) { if (p != inp) {
@@ -2348,11 +2352,42 @@ static char *insert_var_value(char *inp)
inp = ++p; inp = ++p;
p = strchr(inp, SPECIAL_VAR_SYMBOL); p = strchr(inp, SPECIAL_VAR_SYMBOL);
*p = '\0'; *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) { if (p1) {
len = res_str_len + strlen(p1); len = res_str_len + strlen(p1);
res_str = xrealloc(res_str, (1 + len)); res_str = xrealloc(res_str, 1 + len);
strcpy((res_str + res_str_len), p1); strcpy(res_str + res_str_len, p1);
res_str_len = len; res_str_len = len;
} }
*p = SPECIAL_VAR_SYMBOL; *p = SPECIAL_VAR_SYMBOL;
@@ -2956,21 +2991,20 @@ static char* make_string(char ** inp)
char *p; char *p;
char *str = NULL; char *str = NULL;
int n; int n;
int len = 2; int val_len;
int len = 0;
for (n = 0; inp[n]; n++) { for (n = 0; inp[n]; n++) {
p = insert_var_value(inp[n]); p = insert_var_value(inp[n]);
str = xrealloc(str, (len + strlen(p))); val_len = strlen(p);
if (n) { str = xrealloc(str, len + val_len + 3); /* +3: space, '\n', <nul>*/
strcat(str, " "); str[len++] = ' ';
} else { strcpy(str + len, p);
*str = '\0'; len += val_len;
}
strcat(str, p);
len = strlen(str) + 3;
if (p != inp[n]) free(p); 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] = '\n';
str[len+1] = '\0'; str[len+1] = '\0';
return str; return str;
@@ -2979,46 +3013,39 @@ static char* make_string(char ** inp)
/* return code: 0 for OK, 1 for syntax error */ /* return code: 0 for OK, 1 for syntax error */
static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input) static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input)
{ {
int i, advance = 0; // int i;
char sep[] = " "; // char sep[] = " ";
int ch = input->peek(input); /* first character after the $ */ int ch = b_peek(input); /* first character after the $ */
debug_printf_parse("handle_dollar entered: ch='%c'\n", ch); debug_printf_parse("handle_dollar entered: ch='%c'\n", ch);
if (isalpha(ch)) { if (isalpha(ch) || ch == '?') {
b_addchr(dest, SPECIAL_VAR_SYMBOL); b_addchr(dest, SPECIAL_VAR_SYMBOL);
ctx->child->sp++; ctx->child->sp++;
while (1) { while (1) {
ch = b_peek(input);
if (!isalnum(ch) && ch != '_')
break;
debug_printf_parse(": '%c'\n", ch); debug_printf_parse(": '%c'\n", ch);
b_getch(input); b_getch(input);
b_addchr(dest, ch); b_addchr(dest, ch);
ch = b_peek(input);
if (!isalnum(ch) && ch != '_')
break;
} }
b_addchr(dest, SPECIAL_VAR_SYMBOL); b_addchr(dest, SPECIAL_VAR_SYMBOL);
} else if (isdigit(ch)) { } else if (isdigit(ch)) {
i = ch - '0'; /* XXX is $0 special? */ make_one_char_var:
if (i < global_argc) { b_addchr(dest, SPECIAL_VAR_SYMBOL);
parse_string(dest, ctx, global_argv[i]); /* recursion */ ctx->child->sp++;
} debug_printf_parse(": '%c'\n", ch);
advance = 1; b_getch(input);
b_addchr(dest, ch);
b_addchr(dest, SPECIAL_VAR_SYMBOL);
} else switch (ch) { } else switch (ch) {
case '$': case '$': /* pid */
b_adduint(dest, getpid()); case '!': /* last bg pid */
advance = 1; case '?': /* last exit code */
break; case '#': /* number of args */
case '!': case '*': /* args */
if (last_bg_pid > 0) b_adduint(dest, last_bg_pid); case '@': /* args */
advance = 1; goto make_one_char_var;
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 '{': case '{':
b_addchr(dest, SPECIAL_VAR_SYMBOL); b_addchr(dest, SPECIAL_VAR_SYMBOL);
ctx->child->sp++; 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); b_getch(input);
process_command_subs(dest, ctx, input, ")"); process_command_subs(dest, ctx, input, ")");
break; 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 '-':
case '_': case '_':
/* still unhandled, but should be eventually */ /* 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; return 1;
break; break;
default: 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"); debug_printf_parse("handle_dollar return 0\n");
return 0; return 0;
} }
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)
{ //{
struct in_str foo; // struct in_str foo;
setup_string_in_str(&foo, src); // setup_string_in_str(&foo, src);
return parse_stream(dest, ctx, &foo, NULL); // return parse_stream(dest, ctx, &foo, NULL);
} //}
//
/* return code is 0 for normal exit, 1 for syntax error */ /* return code is 0 for normal exit, 1 for syntax error */
static int parse_stream(o_string *dest, struct p_context *ctx, static int parse_stream(o_string *dest, struct p_context *ctx,
struct in_str *input, const char *end_trigger) struct in_str *input, const char *end_trigger)

View File

@@ -0,0 +1 @@
OK

View File

@@ -0,0 +1,4 @@
if test $# = 0; then
exec "$THIS_SH" "$0" arg
fi
echo OK

View File

@@ -0,0 +1 @@
'1'

View File

@@ -0,0 +1,2 @@
a=1
echo "'$a'"

View File

@@ -0,0 +1 @@
>1

View File

@@ -0,0 +1,2 @@
a=1
echo ">$a"

View File

@@ -0,0 +1 @@
.1 abc d e f.

View File

@@ -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

View File

@@ -0,0 +1,5 @@
.1.
.abc.
.d.
.e.
.f.

View File

@@ -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

View File

@@ -1,4 +1,4 @@
http://busybox.net http://busybox.net
http://busybox.net_abc http://busybox.net_abc
1 1
0 1

View File

@@ -6,5 +6,4 @@ echo ${URL}_abc
true true
false; echo $? false; echo $?
true true
# BUG: prints 0, must be 1
{ false; echo $?; } { false; echo $?; }