hush: fix "true | exit 3; echo $?" bug
This commit is contained in:
parent
f2fffd0014
commit
e725bfe6e0
13
shell/README
13
shell/README
@ -1,5 +1,18 @@
|
|||||||
Various bits of what is known about busybox shells, in no particular order.
|
Various bits of what is known about busybox shells, in no particular order.
|
||||||
|
|
||||||
|
2007-05-03
|
||||||
|
hush: update on "sleep 1 | exit 3; echo $?" bug.
|
||||||
|
parse_stream_outer() repeatedly calls parse_stream().
|
||||||
|
parse_stream() is now fixed to stop on ';' in this example,
|
||||||
|
fixing it (parse_stream_outer() will call parse_stream() 1st time,
|
||||||
|
execute the parse tree, call parse_stream() 2nd time and execute the tree).
|
||||||
|
But it's not the end of story.
|
||||||
|
In more complex situations we _must_ parse way farther before executing.
|
||||||
|
Example #2: "{ sleep 1 | exit 3; echo $?; ...few_lines... } >file".
|
||||||
|
Because of redirection, we cannot execute 1st pipe before we parse it all.
|
||||||
|
We probably need to learn to store $var expressions in parse tree.
|
||||||
|
Debug printing of parse tree would be nice too.
|
||||||
|
|
||||||
2007-04-28
|
2007-04-28
|
||||||
hush: Ctrl-C and Ctrl-Z for single NOFORK commands are working.
|
hush: Ctrl-C and Ctrl-Z for single NOFORK commands are working.
|
||||||
Memory and other resource leaks (opendir) are not addressed
|
Memory and other resource leaks (opendir) are not addressed
|
||||||
|
138
shell/hush.c
138
shell/hush.c
@ -89,6 +89,7 @@
|
|||||||
/* Finer-grained debug switch */
|
/* Finer-grained debug switch */
|
||||||
#define debug_printf_jobs(...) do {} while (0)
|
#define debug_printf_jobs(...) do {} while (0)
|
||||||
#define debug_printf_exec(...) do {} while (0)
|
#define debug_printf_exec(...) do {} while (0)
|
||||||
|
#define debug_printf_parse(...) do {} while (0)
|
||||||
|
|
||||||
|
|
||||||
#ifndef debug_printf
|
#ifndef debug_printf
|
||||||
@ -111,6 +112,10 @@ static const char *indenter(int i)
|
|||||||
#define debug_printf_exec(...) fprintf(stderr, __VA_ARGS__)
|
#define debug_printf_exec(...) fprintf(stderr, __VA_ARGS__)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef debug_printf_parse
|
||||||
|
#define debug_printf_parse(...) fprintf(stderr, __VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#if !ENABLE_HUSH_INTERACTIVE
|
#if !ENABLE_HUSH_INTERACTIVE
|
||||||
#undef ENABLE_FEATURE_EDITING
|
#undef ENABLE_FEATURE_EDITING
|
||||||
@ -259,8 +264,14 @@ static int last_return_code;
|
|||||||
extern char **environ; /* This is in <unistd.h>, but protected with __USE_GNU */
|
extern char **environ; /* This is in <unistd.h>, but protected with __USE_GNU */
|
||||||
|
|
||||||
/* "globals" within this file */
|
/* "globals" within this file */
|
||||||
static const char *ifs;
|
enum {
|
||||||
|
MAP_ORDINARY = 0,
|
||||||
|
MAP_FLOWTROUGH_IF_QUOTED = 1,
|
||||||
|
MAP_IFS_IF_UNQUOTED = 2, /* flow through if quoted too */
|
||||||
|
MAP_NEVER_FLOWTROUGH = 3,
|
||||||
|
};
|
||||||
static unsigned char map[256];
|
static unsigned char map[256];
|
||||||
|
static const char *ifs;
|
||||||
static int fake_mode;
|
static int fake_mode;
|
||||||
static struct close_me *close_me_head;
|
static struct close_me *close_me_head;
|
||||||
static const char *cwd;
|
static const char *cwd;
|
||||||
@ -390,13 +401,13 @@ static int done_pipe(struct p_context *ctx, pipe_style type);
|
|||||||
/* primary string parsing: */
|
/* primary string parsing: */
|
||||||
static int redirect_dup_num(struct in_str *input);
|
static int redirect_dup_num(struct in_str *input);
|
||||||
static int redirect_opt_num(o_string *o);
|
static int redirect_opt_num(o_string *o);
|
||||||
static int process_command_subs(o_string *dest, struct p_context *ctx, struct in_str *input, int subst_end);
|
static int process_command_subs(o_string *dest, struct p_context *ctx, struct in_str *input, const char *subst_end);
|
||||||
static int parse_group(o_string *dest, struct p_context *ctx, struct in_str *input, int ch);
|
static int parse_group(o_string *dest, struct p_context *ctx, struct in_str *input, int ch);
|
||||||
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, int 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 flag);
|
static int parse_stream_outer(struct in_str *inp, int flag);
|
||||||
static int parse_string_outer(const char *s, int flag);
|
static int parse_string_outer(const char *s, int flag);
|
||||||
@ -1977,8 +1988,9 @@ static int run_list_real(struct pipe *pi)
|
|||||||
{
|
{
|
||||||
rcode = checkjobs(pi);
|
rcode = checkjobs(pi);
|
||||||
}
|
}
|
||||||
debug_printf("checkjobs returned %d\n", rcode);
|
debug_printf_exec("checkjobs returned %d\n", rcode);
|
||||||
}
|
}
|
||||||
|
debug_printf_exec("setting last_return_code=%d\n", rcode);
|
||||||
last_return_code = rcode;
|
last_return_code = rcode;
|
||||||
pi->num_progs = save_num_progs; /* restore number of programs */
|
pi->num_progs = save_num_progs; /* restore number of programs */
|
||||||
if (rmode == RES_IF || rmode == RES_ELIF)
|
if (rmode == RES_IF || rmode == RES_ELIF)
|
||||||
@ -2519,7 +2531,7 @@ static int reserved_word(o_string *dest, struct p_context *ctx)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* normal return is 0.
|
/* Normal return is 0.
|
||||||
* Syntax or xglob errors return 1. */
|
* Syntax or xglob errors return 1. */
|
||||||
static int done_word(o_string *dest, struct p_context *ctx)
|
static int done_word(o_string *dest, struct p_context *ctx)
|
||||||
{
|
{
|
||||||
@ -2527,9 +2539,9 @@ static int done_word(o_string *dest, struct p_context *ctx)
|
|||||||
glob_t *glob_target;
|
glob_t *glob_target;
|
||||||
int gr, flags = 0;
|
int gr, flags = 0;
|
||||||
|
|
||||||
debug_printf("done_word: %s %p\n", dest->data, child);
|
debug_printf_parse("done_word: '%s' %p\n", dest->data, child);
|
||||||
if (dest->length == 0 && !dest->nonnull) {
|
if (dest->length == 0 && !dest->nonnull) {
|
||||||
debug_printf(" true null, ignored\n");
|
debug_printf_parse("done_word return 0: true null, ignored\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (ctx->pending_redirect) {
|
if (ctx->pending_redirect) {
|
||||||
@ -2537,24 +2549,31 @@ static int done_word(o_string *dest, struct p_context *ctx)
|
|||||||
} else {
|
} else {
|
||||||
if (child->group) {
|
if (child->group) {
|
||||||
syntax();
|
syntax();
|
||||||
return 1; /* syntax error, groups and arglists don't mix */
|
debug_printf_parse("done_word return 1: syntax error, groups and arglists don't mix\n");
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
if (!child->argv && (ctx->type & FLAG_PARSE_SEMICOLON)) {
|
if (!child->argv && (ctx->type & FLAG_PARSE_SEMICOLON)) {
|
||||||
debug_printf("checking %s for reserved-ness\n", dest->data);
|
debug_printf_parse(": checking %s for reserved-ness\n", dest->data);
|
||||||
if (reserved_word(dest, ctx))
|
if (reserved_word(dest, ctx)) {
|
||||||
|
debug_printf_parse("done_word return %d\n", (ctx->w == RES_SNTX));
|
||||||
return (ctx->w == RES_SNTX);
|
return (ctx->w == RES_SNTX);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
glob_target = &child->glob_result;
|
glob_target = &child->glob_result;
|
||||||
if (child->argv) flags |= GLOB_APPEND;
|
if (child->argv) flags |= GLOB_APPEND;
|
||||||
}
|
}
|
||||||
gr = xglob(dest, flags, glob_target);
|
gr = xglob(dest, flags, glob_target);
|
||||||
if (gr != 0) return 1;
|
if (gr != 0) {
|
||||||
|
debug_printf_parse("done_word return 1: xglob returned %d\n", gr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
b_reset(dest);
|
b_reset(dest);
|
||||||
if (ctx->pending_redirect) {
|
if (ctx->pending_redirect) {
|
||||||
ctx->pending_redirect = NULL;
|
ctx->pending_redirect = NULL;
|
||||||
if (glob_target->gl_pathc != 1) {
|
if (glob_target->gl_pathc != 1) {
|
||||||
bb_error_msg("ambiguous redirect");
|
bb_error_msg("ambiguous redirect");
|
||||||
|
debug_printf_parse("done_word return 1: ambiguous redirect\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -2564,6 +2583,7 @@ static int done_word(o_string *dest, struct p_context *ctx)
|
|||||||
done_word(dest, ctx);
|
done_word(dest, ctx);
|
||||||
done_pipe(ctx, PIPE_SEQ);
|
done_pipe(ctx, PIPE_SEQ);
|
||||||
}
|
}
|
||||||
|
debug_printf_parse("done_word return 0\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2614,7 +2634,7 @@ static int done_pipe(struct p_context *ctx, pipe_style type)
|
|||||||
{
|
{
|
||||||
struct pipe *new_p;
|
struct pipe *new_p;
|
||||||
done_command(ctx); /* implicit closure of previous command */
|
done_command(ctx); /* implicit closure of previous command */
|
||||||
debug_printf("done_pipe, type %d\n", type);
|
debug_printf_parse("done_pipe, type %d\n", type);
|
||||||
ctx->pipe->followup = type;
|
ctx->pipe->followup = type;
|
||||||
ctx->pipe->r_mode = ctx->w;
|
ctx->pipe->r_mode = ctx->w;
|
||||||
new_p = new_pipe();
|
new_p = new_pipe();
|
||||||
@ -2710,7 +2730,8 @@ static FILE *generate_stream_from_list(struct pipe *head)
|
|||||||
|
|
||||||
/* this version hacked for testing purposes */
|
/* this version hacked for testing purposes */
|
||||||
/* return code is exit status of the process that is run. */
|
/* return code is exit status of the process that is run. */
|
||||||
static int process_command_subs(o_string *dest, struct p_context *ctx, struct in_str *input, int subst_end)
|
static int process_command_subs(o_string *dest, struct p_context *ctx,
|
||||||
|
struct in_str *input, const char *subst_end)
|
||||||
{
|
{
|
||||||
int retcode;
|
int retcode;
|
||||||
o_string result = NULL_O_STRING;
|
o_string result = NULL_O_STRING;
|
||||||
@ -2732,7 +2753,7 @@ static int process_command_subs(o_string *dest, struct p_context *ctx, struct in
|
|||||||
setup_file_in_str(&pipe_str, p);
|
setup_file_in_str(&pipe_str, p);
|
||||||
|
|
||||||
/* now send results of command back into original context */
|
/* now send results of command back into original context */
|
||||||
retcode = parse_stream(dest, ctx, &pipe_str, '\0');
|
retcode = parse_stream(dest, ctx, &pipe_str, NULL);
|
||||||
/* XXX In case of a syntax error, should we try to kill the child?
|
/* XXX In case of a syntax error, should we try to kill the child?
|
||||||
* That would be tough to do right, so just read until EOF. */
|
* That would be tough to do right, so just read until EOF. */
|
||||||
if (retcode == 1) {
|
if (retcode == 1) {
|
||||||
@ -2757,21 +2778,25 @@ static int process_command_subs(o_string *dest, struct p_context *ctx, struct in
|
|||||||
static int parse_group(o_string *dest, struct p_context *ctx,
|
static int parse_group(o_string *dest, struct p_context *ctx,
|
||||||
struct in_str *input, int ch)
|
struct in_str *input, int ch)
|
||||||
{
|
{
|
||||||
int rcode, endch = 0;
|
int rcode;
|
||||||
|
const char *endch = NULL;
|
||||||
struct p_context sub;
|
struct p_context sub;
|
||||||
struct child_prog *child = ctx->child;
|
struct child_prog *child = ctx->child;
|
||||||
|
|
||||||
|
debug_printf_parse("parse_group entered\n");
|
||||||
if (child->argv) {
|
if (child->argv) {
|
||||||
syntax();
|
syntax();
|
||||||
return 1; /* syntax error, groups and arglists don't mix */
|
debug_printf_parse("parse_group return 1: syntax error, groups and arglists don't mix\n");
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
initialize_context(&sub);
|
initialize_context(&sub);
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
case '(':
|
case '(':
|
||||||
endch = ')';
|
endch = ")";
|
||||||
child->subshell = 1;
|
child->subshell = 1;
|
||||||
break;
|
break;
|
||||||
case '{':
|
case '{':
|
||||||
endch = '}';
|
endch = "}";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
syntax(); /* really logic error */
|
syntax(); /* really logic error */
|
||||||
@ -2780,6 +2805,8 @@ static int parse_group(o_string *dest, struct p_context *ctx,
|
|||||||
done_word(dest, &sub); /* finish off the final word in the subcontext */
|
done_word(dest, &sub); /* finish off the final word in the subcontext */
|
||||||
done_pipe(&sub, PIPE_SEQ); /* and the final command there, too */
|
done_pipe(&sub, PIPE_SEQ); /* and the final command there, too */
|
||||||
child->group = sub.list_head;
|
child->group = sub.list_head;
|
||||||
|
|
||||||
|
debug_printf_parse("parse_group return %d\n", rcode);
|
||||||
return rcode;
|
return rcode;
|
||||||
/* child remains "open", available for possible redirects */
|
/* child remains "open", available for possible redirects */
|
||||||
}
|
}
|
||||||
@ -2880,7 +2907,7 @@ static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *i
|
|||||||
break;
|
break;
|
||||||
case '(':
|
case '(':
|
||||||
b_getch(input);
|
b_getch(input);
|
||||||
process_command_subs(dest, ctx, input, ')');
|
process_command_subs(dest, ctx, input, ")");
|
||||||
break;
|
break;
|
||||||
case '*':
|
case '*':
|
||||||
sep[0] = ifs[0];
|
sep[0] = ifs[0];
|
||||||
@ -2913,12 +2940,12 @@ 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, '\0');
|
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, int end_trigger)
|
struct in_str *input, const char *end_trigger)
|
||||||
{
|
{
|
||||||
int ch, m;
|
int ch, m;
|
||||||
int redir_fd;
|
int redir_fd;
|
||||||
@ -2929,30 +2956,38 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
|
|||||||
* A single-quote triggers a bypass of the main loop until its mate is
|
* A single-quote triggers a bypass of the main loop until its mate is
|
||||||
* found. When recursing, quote state is passed in via dest->quote. */
|
* found. When recursing, quote state is passed in via dest->quote. */
|
||||||
|
|
||||||
debug_printf("parse_stream, end_trigger=%d\n", end_trigger);
|
debug_printf_parse("parse_stream entered, end_trigger='%s'\n", end_trigger);
|
||||||
|
|
||||||
while ((ch = b_getch(input)) != EOF) {
|
while ((ch = b_getch(input)) != EOF) {
|
||||||
m = map[ch];
|
m = map[ch];
|
||||||
next = (ch == '\n') ? 0 : b_peek(input);
|
next = (ch == '\n') ? 0 : b_peek(input);
|
||||||
debug_printf("parse_stream: ch=%c (%d) m=%d quote=%d\n",
|
debug_printf_parse(": ch=%c (%d) m=%d quote=%d\n",
|
||||||
ch, ch, m, dest->quote);
|
ch, ch, m, dest->quote);
|
||||||
if (m == 0 || ((m == 1 || m == 2) && dest->quote)) {
|
if (m == MAP_ORDINARY
|
||||||
|
|| (m != MAP_NEVER_FLOWTROUGH && dest->quote)
|
||||||
|
) {
|
||||||
b_addqchr(dest, ch, dest->quote);
|
b_addqchr(dest, ch, dest->quote);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (m == 2) { /* unquoted IFS */
|
if (m == MAP_IFS_IF_UNQUOTED) {
|
||||||
if (done_word(dest, ctx)) {
|
if (done_word(dest, ctx)) {
|
||||||
|
debug_printf_parse("parse_stream return 1\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
/* If we aren't performing a substitution, treat a newline as a
|
/* If we aren't performing a substitution, treat
|
||||||
* command separator. */
|
* a newline as a command separator.
|
||||||
if (end_trigger != '\0' && ch == '\n')
|
* [why we don't handle it exactly like ';'? --vda] */
|
||||||
|
if (end_trigger && ch == '\n') {
|
||||||
done_pipe(ctx, PIPE_SEQ);
|
done_pipe(ctx, PIPE_SEQ);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (ch == end_trigger && !dest->quote && ctx->w == RES_NONE) {
|
if ((end_trigger && strchr(end_trigger, ch))
|
||||||
debug_printf("leaving parse_stream (triggered)\n");
|
&& !dest->quote && ctx->w == RES_NONE
|
||||||
|
) {
|
||||||
|
debug_printf_parse("parse_stream return 0\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (m == 2)
|
if (m == MAP_IFS_IF_UNQUOTED)
|
||||||
continue;
|
continue;
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
case '#':
|
case '#':
|
||||||
@ -2970,13 +3005,17 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
|
|||||||
case '\\':
|
case '\\':
|
||||||
if (next == EOF) {
|
if (next == EOF) {
|
||||||
syntax();
|
syntax();
|
||||||
|
debug_printf_parse("parse_stream return 1\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
b_addqchr(dest, '\\', dest->quote);
|
b_addqchr(dest, '\\', dest->quote);
|
||||||
b_addqchr(dest, b_getch(input), dest->quote);
|
b_addqchr(dest, b_getch(input), dest->quote);
|
||||||
break;
|
break;
|
||||||
case '$':
|
case '$':
|
||||||
if (handle_dollar(dest, ctx, input) != 0) return 1;
|
if (handle_dollar(dest, ctx, input) != 0) {
|
||||||
|
debug_printf_parse("parse_stream return 1: handle_dollar returned non-0\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case '\'':
|
case '\'':
|
||||||
dest->nonnull = 1;
|
dest->nonnull = 1;
|
||||||
@ -2988,6 +3027,7 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
|
|||||||
}
|
}
|
||||||
if (ch == EOF) {
|
if (ch == EOF) {
|
||||||
syntax();
|
syntax();
|
||||||
|
debug_printf_parse("parse_stream return 1\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -2996,7 +3036,7 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
|
|||||||
dest->quote = !dest->quote;
|
dest->quote = !dest->quote;
|
||||||
break;
|
break;
|
||||||
case '`':
|
case '`':
|
||||||
process_command_subs(dest, ctx, input, '`');
|
process_command_subs(dest, ctx, input, "`");
|
||||||
break;
|
break;
|
||||||
case '>':
|
case '>':
|
||||||
redir_fd = redirect_opt_num(dest);
|
redir_fd = redirect_opt_num(dest);
|
||||||
@ -3007,6 +3047,7 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
|
|||||||
b_getch(input);
|
b_getch(input);
|
||||||
} else if (next == '(') {
|
} else if (next == '(') {
|
||||||
syntax(); /* until we support >(list) Process Substitution */
|
syntax(); /* until we support >(list) Process Substitution */
|
||||||
|
debug_printf_parse("parse_stream return 1: >(process) not supported\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
setup_redirect(ctx, redir_fd, redir_style, input);
|
setup_redirect(ctx, redir_fd, redir_style, input);
|
||||||
@ -3023,6 +3064,7 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
|
|||||||
b_getch(input);
|
b_getch(input);
|
||||||
} else if (next == '(') {
|
} else if (next == '(') {
|
||||||
syntax(); /* until we support <(list) Process Substitution */
|
syntax(); /* until we support <(list) Process Substitution */
|
||||||
|
debug_printf_parse("parse_stream return 1: <(process) not supported\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
setup_redirect(ctx, redir_fd, redir_style, input);
|
setup_redirect(ctx, redir_fd, redir_style, input);
|
||||||
@ -3054,28 +3096,32 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
|
|||||||
break;
|
break;
|
||||||
case '(':
|
case '(':
|
||||||
case '{':
|
case '{':
|
||||||
if (parse_group(dest, ctx, input, ch) != 0)
|
if (parse_group(dest, ctx, input, ch) != 0) {
|
||||||
|
debug_printf_parse("parse_stream return 1: parse_group returned non-0\n");
|
||||||
return 1;
|
return 1;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ')':
|
case ')':
|
||||||
case '}':
|
case '}':
|
||||||
syntax(); /* Proper use of this character caught by end_trigger */
|
syntax(); /* Proper use of this character is caught by end_trigger */
|
||||||
|
debug_printf_parse("parse_stream return 1: unexpected '}'\n");
|
||||||
return 1;
|
return 1;
|
||||||
default:
|
default:
|
||||||
syntax(); /* this is really an internal logic error */
|
syntax(); /* this is really an internal logic error */
|
||||||
|
debug_printf_parse("parse_stream return 1: internal logic error\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* complain if quote? No, maybe we just finished a command substitution
|
/* Complain if quote? No, maybe we just finished a command substitution
|
||||||
* that was quoted. Example:
|
* that was quoted. Example:
|
||||||
* $ echo "`cat foo` plus more"
|
* $ echo "`cat foo` plus more"
|
||||||
* and we just got the EOF generated by the subshell that ran "cat foo"
|
* and we just got the EOF generated by the subshell that ran "cat foo"
|
||||||
* The only real complaint is if we got an EOF when end_trigger != '\0',
|
* The only real complaint is if we got an EOF when end_trigger != NULL,
|
||||||
* that is, we were really supposed to get end_trigger, and never got
|
* that is, we were really supposed to get end_trigger, and never got
|
||||||
* one before the EOF. Can't use the standard "syntax error" return code,
|
* one before the EOF. Can't use the standard "syntax error" return code,
|
||||||
* so that parse_stream_outer can distinguish the EOF and exit smoothly. */
|
* so that parse_stream_outer can distinguish the EOF and exit smoothly. */
|
||||||
debug_printf("leaving parse_stream (EOF)\n");
|
debug_printf_parse("parse_stream return -%d\n", end_trigger != NULL);
|
||||||
if (end_trigger != '\0')
|
if (end_trigger)
|
||||||
return -1;
|
return -1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -3097,17 +3143,17 @@ static void update_ifs_map(void)
|
|||||||
* The map[] array only really needs two bits each, and on most machines
|
* The map[] array only really needs two bits each, and on most machines
|
||||||
* that would be faster because of the reduced L1 cache footprint.
|
* that would be faster because of the reduced L1 cache footprint.
|
||||||
*/
|
*/
|
||||||
memset(map, 0, sizeof(map)); /* most characters flow through always */
|
memset(map, MAP_ORDINARY, sizeof(map)); /* most chars flow through always */
|
||||||
mapset("\\$'\"`", 3); /* never flow through */
|
mapset("\\$'\"`", MAP_NEVER_FLOWTROUGH);
|
||||||
mapset("<>;&|(){}#", 1); /* flow through if quoted */
|
mapset("<>;&|(){}#", MAP_FLOWTROUGH_IF_QUOTED);
|
||||||
mapset(ifs, 2); /* also flow through if quoted */
|
mapset(ifs, MAP_IFS_IF_UNQUOTED); /* also flow through if quoted */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* most recursion does not come through here, the exception is
|
/* most recursion does not come through here, the exception is
|
||||||
* from builtin_source() */
|
* from builtin_source() */
|
||||||
static int parse_stream_outer(struct in_str *inp, int flag)
|
static int parse_stream_outer(struct in_str *inp, int flag)
|
||||||
{
|
{
|
||||||
// FIXME: 'true | exit 3; echo $?' is parsed as a whole,
|
// FIXME: '{ true | exit 3; echo $? }' is parsed as a whole,
|
||||||
// as a result $? is replaced by 0, not 3!
|
// as a result $? is replaced by 0, not 3!
|
||||||
// Need to stop & execute stuff at ';', not parse till EOL!
|
// Need to stop & execute stuff at ';', not parse till EOL!
|
||||||
|
|
||||||
@ -3119,11 +3165,11 @@ static int parse_stream_outer(struct in_str *inp, int flag)
|
|||||||
initialize_context(&ctx);
|
initialize_context(&ctx);
|
||||||
update_ifs_map();
|
update_ifs_map();
|
||||||
if (!(flag & FLAG_PARSE_SEMICOLON) || (flag & FLAG_REPARSING))
|
if (!(flag & FLAG_PARSE_SEMICOLON) || (flag & FLAG_REPARSING))
|
||||||
mapset(";$&|", 0);
|
mapset(";$&|", MAP_ORDINARY);
|
||||||
#if ENABLE_HUSH_INTERACTIVE
|
#if ENABLE_HUSH_INTERACTIVE
|
||||||
inp->promptmode = 1;
|
inp->promptmode = 1;
|
||||||
#endif
|
#endif
|
||||||
rcode = parse_stream(&temp, &ctx, inp, '\n');
|
rcode = parse_stream(&temp, &ctx, inp, ";\n");
|
||||||
if (rcode != 1 && ctx.old_flag != 0) {
|
if (rcode != 1 && ctx.old_flag != 0) {
|
||||||
syntax();
|
syntax();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user