hush: delete unused field in struct child.
reinstate needed check for invalid syntax. document command parsing in hush_doc.txt.
This commit is contained in:
parent
17f02e79f4
commit
395ae45216
34
shell/hush.c
34
shell/hush.c
@ -322,7 +322,6 @@ struct child_prog {
|
|||||||
char **argv; /* program name and arguments */
|
char **argv; /* program name and arguments */
|
||||||
struct pipe *group; /* if non-NULL, first in group or subshell */
|
struct pipe *group; /* if non-NULL, first in group or subshell */
|
||||||
struct redir_struct *redirects; /* I/O redirections */
|
struct redir_struct *redirects; /* I/O redirections */
|
||||||
struct pipe *family; /* pointer back to the child's parent pipe */
|
|
||||||
};
|
};
|
||||||
/* argv vector may contain variable references (^Cvar^C, ^C0^C etc)
|
/* argv vector may contain variable references (^Cvar^C, ^C0^C etc)
|
||||||
* and on execution these are substituted with their values.
|
* and on execution these are substituted with their values.
|
||||||
@ -2830,7 +2829,6 @@ static void initialize_context(struct p_context *ctx)
|
|||||||
ctx->pipe = ctx->list_head = new_pipe();
|
ctx->pipe = ctx->list_head = new_pipe();
|
||||||
/* Create the memory for child, roughly:
|
/* Create the memory for child, roughly:
|
||||||
* ctx->pipe->progs = new struct child_prog;
|
* ctx->pipe->progs = new struct child_prog;
|
||||||
* ctx->pipe->progs[0].family = ctx->pipe;
|
|
||||||
* ctx->child = &ctx->pipe->progs[0];
|
* ctx->child = &ctx->pipe->progs[0];
|
||||||
*/
|
*/
|
||||||
done_command(ctx);
|
done_command(ctx);
|
||||||
@ -2960,6 +2958,7 @@ static int done_word(o_string *word, struct p_context *ctx)
|
|||||||
{
|
{
|
||||||
struct child_prog *child = ctx->child;
|
struct child_prog *child = ctx->child;
|
||||||
|
|
||||||
|
debug_printf_parse("done_word entered: '%s' %p\n", word->data, child);
|
||||||
/* If this word wasn't an assignment, next ones definitely
|
/* If this word wasn't an assignment, next ones definitely
|
||||||
* can't be assignments. Even if they look like ones. */
|
* can't be assignments. Even if they look like ones. */
|
||||||
if (word->o_assignment != DEFINITELY_ASSIGNMENT) {
|
if (word->o_assignment != DEFINITELY_ASSIGNMENT) {
|
||||||
@ -2967,8 +2966,6 @@ static int done_word(o_string *word, struct p_context *ctx)
|
|||||||
} else {
|
} else {
|
||||||
word->o_assignment = MAYBE_ASSIGNMENT;
|
word->o_assignment = MAYBE_ASSIGNMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_printf_parse("done_word entered: '%s' %p\n", word->data, child);
|
|
||||||
if (word->length == 0 && word->nonnull == 0) {
|
if (word->length == 0 && word->nonnull == 0) {
|
||||||
debug_printf_parse("done_word return 0: true null, ignored\n");
|
debug_printf_parse("done_word return 0: true null, ignored\n");
|
||||||
return 0;
|
return 0;
|
||||||
@ -2980,15 +2977,17 @@ static int done_word(o_string *word, struct p_context *ctx)
|
|||||||
word->o_assignment = NOT_ASSIGNMENT;
|
word->o_assignment = NOT_ASSIGNMENT;
|
||||||
debug_printf("word stored in rd_filename: '%s'\n", word->data);
|
debug_printf("word stored in rd_filename: '%s'\n", word->data);
|
||||||
} else {
|
} else {
|
||||||
// if (child->group) { /* TODO: example how to trigger? */
|
/* "{ echo foo; } echo bar" - bad */
|
||||||
// syntax(NULL);
|
/* NB: bash allows e.g. "if true; then { echo foo; } fi". TODO? */
|
||||||
// debug_printf_parse("done_word return 1: syntax error, groups and arglists don't mix\n");
|
if (child->group) {
|
||||||
// return 1;
|
syntax(NULL);
|
||||||
// }
|
debug_printf_parse("done_word return 1: syntax error, groups and arglists don't mix\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
#if HAS_KEYWORDS
|
#if HAS_KEYWORDS
|
||||||
#if ENABLE_HUSH_CASE
|
#if ENABLE_HUSH_CASE
|
||||||
if (ctx->ctx_dsemicolon) {
|
if (ctx->ctx_dsemicolon) {
|
||||||
/* already done when ctx_dsemicolon was set to 1 */
|
/* already done when ctx_dsemicolon was set to 1: */
|
||||||
/* ctx->ctx_res_w = RES_MATCH; */
|
/* ctx->ctx_res_w = RES_MATCH; */
|
||||||
ctx->ctx_dsemicolon = 0;
|
ctx->ctx_dsemicolon = 0;
|
||||||
} else
|
} else
|
||||||
@ -3085,9 +3084,7 @@ static int done_command(struct p_context *ctx)
|
|||||||
* child structure is not counted in pi->num_progs. */
|
* child structure is not counted in pi->num_progs. */
|
||||||
pi->progs = xrealloc(pi->progs, sizeof(*pi->progs) * (pi->num_progs+1));
|
pi->progs = xrealloc(pi->progs, sizeof(*pi->progs) * (pi->num_progs+1));
|
||||||
child = &pi->progs[pi->num_progs];
|
child = &pi->progs[pi->num_progs];
|
||||||
|
|
||||||
memset(child, 0, sizeof(*child));
|
memset(child, 0, sizeof(*child));
|
||||||
child->family = pi;
|
|
||||||
|
|
||||||
ctx->child = child;
|
ctx->child = child;
|
||||||
/* but ctx->pipe and ctx->list_head remain unchanged */
|
/* but ctx->pipe and ctx->list_head remain unchanged */
|
||||||
@ -3100,22 +3097,28 @@ static void done_pipe(struct p_context *ctx, pipe_style type)
|
|||||||
int not_null;
|
int not_null;
|
||||||
|
|
||||||
debug_printf_parse("done_pipe entered, followup %d\n", type);
|
debug_printf_parse("done_pipe entered, followup %d\n", type);
|
||||||
not_null = done_command(ctx); /* implicit closure of previous command */
|
/* Close previous command */
|
||||||
|
not_null = done_command(ctx);
|
||||||
ctx->pipe->followup = type;
|
ctx->pipe->followup = type;
|
||||||
IF_HAS_KEYWORDS(ctx->pipe->pi_inverted = ctx->ctx_inverted;)
|
IF_HAS_KEYWORDS(ctx->pipe->pi_inverted = ctx->ctx_inverted;)
|
||||||
IF_HAS_KEYWORDS(ctx->ctx_inverted = 0;)
|
IF_HAS_KEYWORDS(ctx->ctx_inverted = 0;)
|
||||||
IF_HAS_KEYWORDS(ctx->pipe->res_word = ctx->ctx_res_w;)
|
IF_HAS_KEYWORDS(ctx->pipe->res_word = ctx->ctx_res_w;)
|
||||||
|
|
||||||
/* Without this check, even just <enter> on command line generates
|
/* Without this check, even just <enter> on command line generates
|
||||||
* tree of three NOPs (!). Which is harmless but annoying.
|
* tree of three NOPs (!). Which is harmless but annoying.
|
||||||
* IOW: it is safe to do it unconditionally.
|
* IOW: it is safe to do it unconditionally.
|
||||||
* RES_NONE case is for "for a in; do ..." (empty IN set)
|
* RES_NONE case is for "for a in; do ..." (empty IN set)
|
||||||
* to work, possibly other cases too. */
|
* to work, possibly other cases too. */
|
||||||
if (not_null IF_HAS_KEYWORDS(|| ctx->ctx_res_w != RES_NONE)) {
|
if (not_null IF_HAS_KEYWORDS(|| ctx->ctx_res_w != RES_NONE)) {
|
||||||
struct pipe *new_p = new_pipe();
|
struct pipe *new_p;
|
||||||
|
debug_printf_parse("done_pipe: adding new pipe: "
|
||||||
|
" not_null:%d ctx->ctx_res_w:%d\n",
|
||||||
|
not_null, ctx->ctx_res_w);
|
||||||
|
new_p = new_pipe();
|
||||||
ctx->pipe->next = new_p;
|
ctx->pipe->next = new_p;
|
||||||
ctx->pipe = new_p;
|
ctx->pipe = new_p;
|
||||||
ctx->child = NULL; /* needed! */
|
ctx->child = NULL; /* needed! */
|
||||||
/* RES_IF, RES_WHILE etc are "sticky" -
|
/* RES_THEN, RES_DO etc are "sticky" -
|
||||||
* they remain set for commands inside if/while.
|
* they remain set for commands inside if/while.
|
||||||
* This is used to control execution.
|
* This is used to control execution.
|
||||||
* RES_FOR and RES_IN are NOT sticky (needed to support
|
* RES_FOR and RES_IN are NOT sticky (needed to support
|
||||||
@ -3132,7 +3135,6 @@ static void done_pipe(struct p_context *ctx, pipe_style type)
|
|||||||
#endif
|
#endif
|
||||||
/* Create the memory for child, roughly:
|
/* Create the memory for child, roughly:
|
||||||
* ctx->pipe->progs = new struct child_prog;
|
* ctx->pipe->progs = new struct child_prog;
|
||||||
* ctx->pipe->progs[0].family = ctx->pipe;
|
|
||||||
* ctx->child = &ctx->pipe->progs[0];
|
* ctx->child = &ctx->pipe->progs[0];
|
||||||
*/
|
*/
|
||||||
done_command(ctx);
|
done_command(ctx);
|
||||||
|
@ -1,3 +1,80 @@
|
|||||||
|
2008-07-14
|
||||||
|
|
||||||
|
Command parsing
|
||||||
|
|
||||||
|
Command parsing results in "pipe" structures. "Pipe" structure
|
||||||
|
does not always correspond to what sh language calls "pipe",
|
||||||
|
it also controls execution of if, while, etc statements.
|
||||||
|
|
||||||
|
struct pipe fields:
|
||||||
|
smallint res_word - "none" for normal commands,
|
||||||
|
"if" for if condition etc
|
||||||
|
struct child_prog progs[] - array of commands in pipe
|
||||||
|
smallint followup - how this pipe is related to next: is it
|
||||||
|
"pipe; pipe", "pipe & pipe" "pipe && pipe",
|
||||||
|
"pipe || pipe"?
|
||||||
|
|
||||||
|
Blocks of commands { pipe; pipe; } and (pipe; pipe) are represented
|
||||||
|
as one pipe struct with one progs[0] element which is a "group" -
|
||||||
|
struct child_prog can contain a list of pipes. Sometimes these
|
||||||
|
"groups" are created implicitly, e.g. every control
|
||||||
|
statement (if, while, etc) sits inside its own "pipe" struct).
|
||||||
|
|
||||||
|
res_word controls statement execution. Examples:
|
||||||
|
|
||||||
|
"echo Hello" -
|
||||||
|
pipe 0 res_word=NONE followup=SEQ prog[0] 'echo' 'Hello'
|
||||||
|
pipe 1 res_word=NONE followup=1 SEQ
|
||||||
|
|
||||||
|
"echo foo || echo bar" -
|
||||||
|
pipe 0 res_word=NONE followup=OR prog[0] 'echo' 'foo'
|
||||||
|
pipe 1 res_word=NONE followup=SEQ prog[0] 'echo' 'bar'
|
||||||
|
pipe 2 res_word=NONE followup=SEQ
|
||||||
|
|
||||||
|
"if true; then echo Hello; true; fi" -
|
||||||
|
res_word=NONE followup=SEQ
|
||||||
|
prog 0 group {}:
|
||||||
|
pipe 0 res_word=IF followup=SEQ prog[0] 'true'
|
||||||
|
pipe 1 res_word=THEN followup=SEQ prog[0] 'echo' 'Hello'
|
||||||
|
pipe 2 res_word=THEN followup=SEQ prog[0] 'true'
|
||||||
|
pipe 3 res_word=FI followup=SEQ
|
||||||
|
pipe 4 res_word=NONE followup=(null)
|
||||||
|
pipe 1 res_word=NONE followup=SEQ
|
||||||
|
|
||||||
|
"if true; then { echo Hello; true; }; fi" -
|
||||||
|
pipe 0 res_word=NONE followup=SEQ
|
||||||
|
prog 0 group {}:
|
||||||
|
pipe 0 res_word=IF followup=SEQ prog[0] 'true'
|
||||||
|
pipe 1 res_word=THEN followup=SEQ
|
||||||
|
prog 0 group {}:
|
||||||
|
pipe 0 res_word=NONE followup=SEQ prog[0] 'echo' 'Hello'
|
||||||
|
pipe 1 res_word=NONE followup=SEQ prog[0] 'true'
|
||||||
|
pipe 2 res_word=NONE followup=SEQ
|
||||||
|
pipe 2 res_word=NONE followup=(null)
|
||||||
|
pipe 1 res_word=NONE followup=1 SEQ
|
||||||
|
|
||||||
|
"for v in a b; do echo $v; true; done" -
|
||||||
|
pipe 0 res_word=NONE followup=SEQ
|
||||||
|
prog 0 group {}:
|
||||||
|
pipe 0 res_word=FOR followup=SEQ prog[0] 'v'
|
||||||
|
pipe 1 res_word=IN followup=SEQ prog[0] 'a' 'b'
|
||||||
|
pipe 2 res_word=DO followup=SEQ prog[0] 'echo' '$v'
|
||||||
|
pipe 3 res_word=DO followup=SEQ prog[0] 'true'
|
||||||
|
pipe 4 res_word=DONE followup=SEQ
|
||||||
|
pipe 5 res_word=NONE followup=(null)
|
||||||
|
pipe 1 res_word=NONE followup=SEQ
|
||||||
|
|
||||||
|
Note how "THEN" and "DO" does not just mark the first pipe,
|
||||||
|
it "sticks" to all pipes in the body. This is used when
|
||||||
|
hush executes parsed pipes.
|
||||||
|
|
||||||
|
Dummy trailing pipes with no commands are artifacts of imperfect
|
||||||
|
parsing algorithm - done_pipe() appends new pipe struct beforehand
|
||||||
|
and last one ends up empty and unused.
|
||||||
|
|
||||||
|
|
||||||
|
2008-01
|
||||||
|
|
||||||
This is how hush runs commands:
|
This is how hush runs commands:
|
||||||
|
|
||||||
/* callsite: process_command_subs */
|
/* callsite: process_command_subs */
|
||||||
|
Loading…
Reference in New Issue
Block a user