hush: support "for if in do done then; do echo $if; done" case
function old new delta done_pipe 83 95 +12 parse_stream 1758 1764 +6 done_word 674 647 -27 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/1 up/down: 18/-27) Total: -9 bytes
This commit is contained in:
parent
d48e81f0cd
commit
733e3fbc2f
59
shell/hush.c
59
shell/hush.c
@ -1344,7 +1344,7 @@ static int setup_redirects(struct child_prog *prog, int squirrel[])
|
|||||||
if (redir->dup == -1) {
|
if (redir->dup == -1) {
|
||||||
char *p;
|
char *p;
|
||||||
mode = redir_table[redir->rd_type].mode;
|
mode = redir_table[redir->rd_type].mode;
|
||||||
//TODO: check redir to names like '\\'
|
//TODO: check redir for names like '\\'
|
||||||
p = expand_string_to_string(redir->rd_filename);
|
p = expand_string_to_string(redir->rd_filename);
|
||||||
openfd = open_or_warn(p, mode);
|
openfd = open_or_warn(p, mode);
|
||||||
free(p);
|
free(p);
|
||||||
@ -2854,17 +2854,7 @@ static int reserved_word(const o_string *word, struct p_context *ctx)
|
|||||||
continue;
|
continue;
|
||||||
debug_printf("found reserved word %s, res %d\n", r->literal, r->res);
|
debug_printf("found reserved word %s, res %d\n", r->literal, r->res);
|
||||||
if (r->flag == 0) { /* '!' */
|
if (r->flag == 0) { /* '!' */
|
||||||
#if ENABLE_HUSH_LOOPS
|
if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */
|
||||||
if (ctx->ctx_res_w == RES_IN) {
|
|
||||||
/* 'for a in ! a b c; ...' - ! isn't a keyword here */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (ctx->ctx_inverted /* bash doesn't accept '! ! true' */
|
|
||||||
#if ENABLE_HUSH_LOOPS
|
|
||||||
|| ctx->ctx_res_w == RES_FOR /* example: 'for ! a' */
|
|
||||||
#endif
|
|
||||||
) {
|
|
||||||
syntax(NULL);
|
syntax(NULL);
|
||||||
IF_HAS_KEYWORDS(ctx->ctx_res_w = RES_SNTX;)
|
IF_HAS_KEYWORDS(ctx->ctx_res_w = RES_SNTX;)
|
||||||
}
|
}
|
||||||
@ -2885,7 +2875,7 @@ static int reserved_word(const o_string *word, struct p_context *ctx)
|
|||||||
*new = *ctx; /* physical copy */
|
*new = *ctx; /* physical copy */
|
||||||
initialize_context(ctx);
|
initialize_context(ctx);
|
||||||
ctx->stack = new;
|
ctx->stack = new;
|
||||||
} else if (ctx->ctx_res_w == RES_NONE || !(ctx->old_flag & (1 << r->res))) {
|
} else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) {
|
||||||
syntax(NULL);
|
syntax(NULL);
|
||||||
ctx->ctx_res_w = RES_SNTX;
|
ctx->ctx_res_w = RES_SNTX;
|
||||||
return 1;
|
return 1;
|
||||||
@ -2940,7 +2930,10 @@ static int done_word(o_string *word, struct p_context *ctx)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
#if HAS_KEYWORDS
|
#if HAS_KEYWORDS
|
||||||
if (!child->argv) { /* if it's the first word... */
|
if (!child->argv /* if it's the first word... */
|
||||||
|
&& ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */
|
||||||
|
&& ctx->ctx_res_w != RES_IN
|
||||||
|
) {
|
||||||
debug_printf_parse(": checking '%s' for reserved-ness\n", word->data);
|
debug_printf_parse(": checking '%s' for reserved-ness\n", word->data);
|
||||||
if (reserved_word(word, ctx)) {
|
if (reserved_word(word, ctx)) {
|
||||||
o_reset(word);
|
o_reset(word);
|
||||||
@ -2953,9 +2946,9 @@ static int done_word(o_string *word, struct p_context *ctx)
|
|||||||
if (word->nonnull /* word had "xx" or 'xx' at least as part of it. */
|
if (word->nonnull /* word had "xx" or 'xx' at least as part of it. */
|
||||||
/* optimization: and if it's ("" or '') or ($v... or `cmd`...): */
|
/* optimization: and if it's ("" or '') or ($v... or `cmd`...): */
|
||||||
&& (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL)
|
&& (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL)
|
||||||
/* (otherwise it's "abc".... and is already safe) */
|
/* (otherwise it's known to be not empty and is already safe) */
|
||||||
) {
|
) {
|
||||||
/* but exclude "$@" - it expands to no word despite "" */
|
/* exclude "$@" - it can expand to no word despite "" */
|
||||||
char *p = word->data;
|
char *p = word->data;
|
||||||
while (p[0] == SPECIAL_VAR_SYMBOL
|
while (p[0] == SPECIAL_VAR_SYMBOL
|
||||||
&& (p[1] & 0x7f) == '@'
|
&& (p[1] & 0x7f) == '@'
|
||||||
@ -2964,7 +2957,9 @@ static int done_word(o_string *word, struct p_context *ctx)
|
|||||||
p += 3;
|
p += 3;
|
||||||
}
|
}
|
||||||
if (p == word->data || p[0] != '\0') {
|
if (p == word->data || p[0] != '\0') {
|
||||||
/* Insert "empty variable" reference, this makes
|
/* saw no "$@", or not only "$@" but some
|
||||||
|
* real text is there too */
|
||||||
|
/* insert "empty variable" reference, this makes
|
||||||
* e.g. "", $empty"" etc to not disappear */
|
* e.g. "", $empty"" etc to not disappear */
|
||||||
o_addchr(word, SPECIAL_VAR_SYMBOL);
|
o_addchr(word, SPECIAL_VAR_SYMBOL);
|
||||||
o_addchr(word, SPECIAL_VAR_SYMBOL);
|
o_addchr(word, SPECIAL_VAR_SYMBOL);
|
||||||
@ -2979,8 +2974,13 @@ static int done_word(o_string *word, struct p_context *ctx)
|
|||||||
|
|
||||||
#if ENABLE_HUSH_LOOPS
|
#if ENABLE_HUSH_LOOPS
|
||||||
/* Force FOR to have just one word (variable name) */
|
/* Force FOR to have just one word (variable name) */
|
||||||
if (ctx->ctx_res_w == RES_FOR)
|
/* NB: basically, this makes hush see "for v in ..." syntax as if
|
||||||
|
* as it is "for v; in ...". FOR and IN become two pipe structs
|
||||||
|
* in parse tree. */
|
||||||
|
if (ctx->ctx_res_w == RES_FOR) {
|
||||||
|
//TODO: check that child->argv[0] is a valid variable name!
|
||||||
done_pipe(ctx, PIPE_SEQ);
|
done_pipe(ctx, PIPE_SEQ);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
debug_printf_parse("done_word return 0\n");
|
debug_printf_parse("done_word return 0\n");
|
||||||
return 0;
|
return 0;
|
||||||
@ -3030,19 +3030,28 @@ static void done_pipe(struct p_context *ctx, pipe_style type)
|
|||||||
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 */
|
not_null = done_command(ctx); /* implicit closure of previous command */
|
||||||
ctx->pipe->followup = type;
|
ctx->pipe->followup = type;
|
||||||
IF_HAS_KEYWORDS(ctx->pipe->res_word = ctx->ctx_res_w;)
|
|
||||||
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;)
|
||||||
/* 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_IN case is for "for a in; do ..." (empty IN set)
|
* RES_NONE case is for "for a in; do ..." (empty IN set)
|
||||||
* to work. */
|
* to work, possibly other cases too. */
|
||||||
if (not_null USE_HUSH_LOOPS(|| ctx->pipe->res_word == RES_IN)) {
|
if (not_null IF_HAS_KEYWORDS(|| ctx->ctx_res_w != RES_NONE)) {
|
||||||
struct pipe *new_p = new_pipe();
|
struct pipe *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" -
|
||||||
|
* they remain set for commands inside if/while.
|
||||||
|
* This is used to control execution.
|
||||||
|
* RES_FOR and RES_IN are NOT sticky (needed to support
|
||||||
|
* cases where variable or value happens to match a keyword):
|
||||||
|
*/
|
||||||
|
if (ctx->ctx_res_w == RES_FOR
|
||||||
|
|| ctx->ctx_res_w == RES_IN)
|
||||||
|
ctx->ctx_res_w = RES_NONE;
|
||||||
/* 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->pipe->progs[0].family = ctx->pipe;
|
||||||
@ -3444,7 +3453,7 @@ static int handle_dollar(o_string *dest, struct in_str *input)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scan input, call done_word() whenever full IFS delemited word was seen.
|
/* Scan input, call done_word() whenever full IFS delimited word was seen.
|
||||||
* call done_pipe if '\n' was seen (and end_trigger != NULL)
|
* call done_pipe if '\n' was seen (and end_trigger != NULL)
|
||||||
* Return if (non-quoted) char in end_trigger was seen; or on parse error. */
|
* Return if (non-quoted) char in end_trigger was seen; or on parse error. */
|
||||||
/* Return code is 0 if end_trigger char is met,
|
/* Return code is 0 if end_trigger char is met,
|
||||||
@ -3517,7 +3526,9 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
|
|||||||
//err chk?
|
//err chk?
|
||||||
done_pipe(ctx, PIPE_SEQ);
|
done_pipe(ctx, PIPE_SEQ);
|
||||||
}
|
}
|
||||||
if (!HAS_KEYWORDS IF_HAS_KEYWORDS(|| ctx->ctx_res_w == RES_NONE)) {
|
if (!HAS_KEYWORDS
|
||||||
|
IF_HAS_KEYWORDS(|| (ctx->ctx_res_w == RES_NONE && ctx->old_flag == 0))
|
||||||
|
) {
|
||||||
debug_printf_parse("parse_stream return 0: end_trigger char found\n");
|
debug_printf_parse("parse_stream return 0: end_trigger char found\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
4
shell/hush_test/hush-misc/for_with_keywords.right
Normal file
4
shell/hush_test/hush-misc/for_with_keywords.right
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
do
|
||||||
|
done
|
||||||
|
then
|
||||||
|
OK: 0
|
2
shell/hush_test/hush-misc/for_with_keywords.tests
Executable file
2
shell/hush_test/hush-misc/for_with_keywords.tests
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
for if in do done then; do echo $if; done
|
||||||
|
echo OK: $?
|
Loading…
Reference in New Issue
Block a user