hush: fix '{ false; echo $?; }' bug.
hush: expand testsuite. variable expansion is still very broken
This commit is contained in:
204
shell/hush.c
204
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 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)
|
||||||
|
1
shell/hush_test/hush-bugs/argv0.right
Normal file
1
shell/hush_test/hush-bugs/argv0.right
Normal file
@@ -0,0 +1 @@
|
|||||||
|
OK
|
4
shell/hush_test/hush-bugs/argv0.tests
Executable file
4
shell/hush_test/hush-bugs/argv0.tests
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
if test $# = 0; then
|
||||||
|
exec "$THIS_SH" "$0" arg
|
||||||
|
fi
|
||||||
|
echo OK
|
1
shell/hush_test/hush-bugs/quote1.right
Normal file
1
shell/hush_test/hush-bugs/quote1.right
Normal file
@@ -0,0 +1 @@
|
|||||||
|
'1'
|
2
shell/hush_test/hush-bugs/quote1.tests
Executable file
2
shell/hush_test/hush-bugs/quote1.tests
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
a=1
|
||||||
|
echo "'$a'"
|
1
shell/hush_test/hush-bugs/quote2.right
Normal file
1
shell/hush_test/hush-bugs/quote2.right
Normal file
@@ -0,0 +1 @@
|
|||||||
|
>1
|
2
shell/hush_test/hush-bugs/quote2.tests
Executable file
2
shell/hush_test/hush-bugs/quote2.tests
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
a=1
|
||||||
|
echo ">$a"
|
1
shell/hush_test/hush-bugs/starquoted.right
Normal file
1
shell/hush_test/hush-bugs/starquoted.right
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.1 abc d e f.
|
4
shell/hush_test/hush-bugs/starquoted.tests
Executable file
4
shell/hush_test/hush-bugs/starquoted.tests
Executable 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
|
5
shell/hush_test/hush-vars/star.right
Normal file
5
shell/hush_test/hush-vars/star.right
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.1.
|
||||||
|
.abc.
|
||||||
|
.d.
|
||||||
|
.e.
|
||||||
|
.f.
|
8
shell/hush_test/hush-vars/star.tests
Executable file
8
shell/hush_test/hush-vars/star.tests
Executable 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
|
@@ -1,4 +1,4 @@
|
|||||||
http://busybox.net
|
http://busybox.net
|
||||||
http://busybox.net_abc
|
http://busybox.net_abc
|
||||||
1
|
1
|
||||||
0
|
1
|
||||||
|
@@ -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 $?; }
|
||||||
|
Reference in New Issue
Block a user