summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--shell/hush.c173
1 files changed, 85 insertions, 88 deletions
diff --git a/shell/hush.c b/shell/hush.c
index 808adf7b1..2155dda8e 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -5119,7 +5119,6 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
# if BB_MMU
#define parse_dollar_squote(as_string, dest, input) \
parse_dollar_squote(dest, input)
-#define as_string NULL
# endif
static int parse_dollar_squote(o_string *as_string, o_string *dest, struct in_str *input)
{
@@ -5204,7 +5203,6 @@ static int parse_dollar_squote(o_string *as_string, o_string *dest, struct in_st
}
return 1;
-# undef as_string
}
#else
# define parse_dollar_squote(as_string, dest, input) 0
@@ -5482,7 +5480,6 @@ static int parse_dollar(o_string *as_string,
#if BB_MMU
#define encode_string(as_string, dest, input, dquote_end) \
encode_string(dest, input, dquote_end)
-#define as_string NULL
#endif
static int encode_string(o_string *as_string,
o_string *dest,
@@ -5564,7 +5561,6 @@ static int encode_string(o_string *as_string,
o_addchr(dest, SPECIAL_VAR_SYMBOL);
}
goto again;
-#undef as_string
}
/*
@@ -5605,8 +5601,7 @@ static struct pipe *parse_stream(char **pstring,
heredoc_cnt = 0;
while (1) {
- const char *is_blank;
- const char *is_special;
+ int is_blank;
int ch;
int next;
int redir_fd;
@@ -5623,11 +5618,8 @@ static struct pipe *parse_stream(char **pstring,
continue;
}
#endif
- if (ch == EOF)
- break;
-
/* Handle "'" and "\" first, as they won't play nice with
- * i_peek_and_eat_bkslash_nl() anyway:
+ * i_peek_and_eat_bkslash_nl():
* echo z\\
* and
* echo '\
@@ -5660,13 +5652,14 @@ static struct pipe *parse_stream(char **pstring,
o_addchr(&ctx.word, ch);
continue; /* get next char */
}
+ if (ch == EOF)
+ break;
nommu_addchr(&ctx.as_string, ch);
if (ch == '\'') {
ctx.word.has_quoted_part = 1;
next = i_getch(input);
if (next == '\'' && !ctx.pending_redirect/*why?*/)
goto insert_empty_quoted_str_marker;
-
ch = next;
while (1) {
if (ch == EOF) {
@@ -5688,73 +5681,38 @@ static struct pipe *parse_stream(char **pstring,
}
next = '\0';
- if (ch != '\n')
- next = i_peek_and_eat_bkslash_nl(input);
-
- is_special = "{}<>&|();#" /* special outside of "str" */
- "$\"" IF_HUSH_TICK("`") /* always special */
- SPECIAL_VAR_SYMBOL_STR;
-#if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
- if (ctx.command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB) {
- /* In [[ ]], {}<>&|() are not special */
- is_special += 8;
- } else
-#endif
- /* Are { and } special here? */
- if (ctx.command->argv /* word [word]{... - non-special */
- || !IS_NULL_WORD(ctx.word) /* word{... ""{... - non-special */
- || (next != ';' /* }; - special */
- && next != ')' /* }) - special */
- && next != '(' /* {( - special */
- && next != '&' /* }& and }&& ... - special */
- && next != '|' /* }|| ... - special */
- && !strchr(defifs, next) /* {word - non-special */
- )
- ) {
- /* They are not special, skip "{}" */
- is_special += 2;
- }
- is_special = strchr(is_special, ch);
- is_blank = strchr(defifs, ch);
-
- if (!is_special && !is_blank) { /* ordinary char */
- ordinary_char:
- o_addQchr(&ctx.word, ch);
- if ((ctx.is_assignment == MAYBE_ASSIGNMENT
- || ctx.is_assignment == WORD_IS_KEYWORD)
- && ch == '='
- && endofname(ctx.word.data)[0] == '='
- ) {
- ctx.is_assignment = DEFINITELY_ASSIGNMENT;
- debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
- }
- continue;
- }
-
+ is_blank = 1;
+ /* If '\n', must not peek (peeking past '\n' provokes line editing) */
+ if (ch == '\n')
+ /* had to test for '\n' anyway, can also jump directly to newline handling */
+ goto ch_is_newline;
+ next = i_peek_and_eat_bkslash_nl(input);
+
+ is_blank = (ch == ' ' || ch == '\t');
if (is_blank) {
#if ENABLE_HUSH_LINENO_VAR
-/* Case:
- * "while ...; do<whitespace><newline>
- * cmd ..."
- * would think that "cmd" starts in <whitespace> -
+/* "while ...; do<whitespace><newline>
+ * CMD"
+ * would think that CMD starts in <whitespace> -
* i.e., at the previous line.
- * We need to skip all whitespace before newlines.
+ * Need to skip whitespace up to next newline (and eat it)
+ * or not-whitespace (and do not eat it).
*/
- while (ch != '\n') {
+ do {
next = i_peek(input);
if (next != ' ' && next != '\t' && next != '\n')
break; /* next char is not ws */
ch = i_getch(input);
- }
- /* ch == last eaten whitespace char */
+ } while (ch != '\n');
#endif
+ ch_is_newline:
if (done_word(&ctx))
goto parse_error_exitcode1;
if (ch == '\n') {
/* Is this a case when newline is simply ignored?
* Some examples:
- * "cmd | <newline> cmd ..."
- * "case ... in <newline> word) ..."
+ * "CMD | <newline> CMD ..."
+ * "case ... in <newline> PATTERN) ..."
*/
if (IS_NULL_CMD(ctx.command)
&& IS_NULL_WORD(ctx.word)
@@ -5764,18 +5722,18 @@ static struct pipe *parse_stream(char **pstring,
* Without check #1, interactive shell
* ignores even bare <newline>,
* and shows the continuation prompt:
- * ps1_prompt$ <enter>
+ * ps1$ <enter>
* ps2> _ <=== wrong, should be ps1
- * Without check #2, "cmd & <newline>"
+ * Without check #2, "CMD & <newline>"
* is similarly mistreated.
- * (BTW, this makes "cmd & cmd"
- * and "cmd && cmd" non-orthogonal.
+ * (BTW, this makes "CMD & CMD"
+ * and "CMD && CMD" non-orthogonal.
* Really, ask yourself, why
- * "cmd && <newline>" doesn't start
- * cmd but waits for more input?
+ * "CMD && <newline>" doesn't start
+ * CMD but waits for more input?
* The only reason is that it might be
- * a "cmd1 && <nl> cmd2 &" construct,
- * cmd1 may need to run in BG).
+ * a "CMD1 && <nl> CMD2 &" construct:
+ * CMD1 may need to run in BG).
*/
pi = ctx.list_head;
if (pi->num_cmds != 0 /* check #1 */
@@ -5797,11 +5755,51 @@ static struct pipe *parse_stream(char **pstring,
ch = ';';
/* note: if (is_blank) continue;
* will still trigger for us */
+ } /* if ch == '\n */
+ } else {
+ const char *is_special;
+
+ is_special = "{}<>&|();#" /* special outside of "str" */
+ "$\"" IF_HUSH_TICK("`") /* always special */
+ SPECIAL_VAR_SYMBOL_STR;
+#if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
+ if (ctx.command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB) {
+ /* In [[ ]], {}<>&|() are not special */
+ is_special += 8;
+ } else
+#endif
+ /* Are { and } special here? */
+ if (ctx.command->argv /* WORD [WORD]{... - non-special */
+ || !IS_NULL_WORD(ctx.word) /* WORD{... ""{... - non-special */
+ || (next != ';' /* }; - special */
+ && next != ')' /* }) - special */
+ && next != '(' /* {( - special */
+ && next != '&' /* }& and }&& ... - special */
+ && next != '|' /* }|| ... - special */
+ && !strchr(defifs, next) /* {WORD - non-special */
+ )
+ ) {
+ /* They are not special, skip "{}" */
+ is_special += 2;
+ }
+ is_special = strchr(is_special, ch);
+ if (!is_special) { /* ordinary char */
+ ordinary_char:
+ o_addQchr(&ctx.word, ch);
+ if ((ctx.is_assignment == MAYBE_ASSIGNMENT
+ || ctx.is_assignment == WORD_IS_KEYWORD)
+ && ch == '='
+ && endofname(ctx.word.data)[0] == '='
+ ) {
+ ctx.is_assignment = DEFINITELY_ASSIGNMENT;
+ debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
+ }
+ continue;
}
}
- /* "cmd}" or "cmd }..." without semicolon or &:
- * } is an ordinary char in this case, even inside { cmd; }
+ /* "CMD}" or "CMD }..." without semicolon or &:
+ * } is an ordinary char in this case, even inside { CMD; }
* Pathological example: { ""}; } should run "}" command.
*/
if (ch == '}') {
@@ -5809,9 +5807,9 @@ static struct pipe *parse_stream(char **pstring,
/* word} or ""} */
goto ordinary_char;
}
- if (!IS_NULL_CMD(ctx.command)) { /* cmd } */
- /* Generally, there should be semicolon: "cmd; }"
- * However, bash allows to omit it if "cmd" is
+ if (!IS_NULL_CMD(ctx.command)) { /* CMD } */
+ /* Generally, there should be semicolon: "CMD; }"
+ * However, bash allows to omit it if "CMD" is
* a group. Examples:
* { { echo 1; } }
* {(echo 1)}
@@ -5823,8 +5821,8 @@ static struct pipe *parse_stream(char **pstring,
goto term_group;
goto ordinary_char;
}
- if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */
- /* Can't be an end of {cmd}, skip the check */
+ if (!IS_NULL_PIPE(ctx.pipe)) /* CMD | } */
+ /* Can't be an end of {CMD}, skip the check */
goto skip_end_trigger;
/* else: } does terminate a group */
}
@@ -5876,7 +5874,7 @@ static struct pipe *parse_stream(char **pstring,
}
}
- if (is_blank)
+ if (is_blank) /* space, tab or newline? */
continue;
/* Catch <, > before deciding whether this word is
@@ -5928,7 +5926,7 @@ static struct pipe *parse_stream(char **pstring,
continue; /* get next char */
case '#':
if (IS_NULL_WORD(ctx.word)) {
- /* skip "#comment" */
+ /* skip "#COMMENT" */
/* note: we do not add it to &ctx.as_string */
/* TODO: in bash:
* comment inside $() goes to the next \n, even inside quoted string (!):
@@ -6068,7 +6066,7 @@ static struct pipe *parse_stream(char **pstring,
goto parse_error_exitcode1;
#if ENABLE_HUSH_CASE
if (ctx.ctx_res_w == RES_MATCH)
- break; /* we are in case's "word | word)" */
+ break; /* we are in case's "WORD | WORD)" */
#endif
if (next == '|') { /* || */
ch = i_getch(input);
@@ -6095,10 +6093,10 @@ static struct pipe *parse_stream(char **pstring,
goto new_cmd;
case '(':
#if ENABLE_HUSH_CASE
- /* "case... in [(]word)..." - skip '(' */
+ /* "case... in [(]PATTERN)..." - skip '(' */
if (ctx.ctx_res_w == RES_MATCH
- && ctx.command->argv == NULL /* not (word|(... */
- && IS_NULL_WORD(ctx.word) /* not word(... or ""(... */
+ && ctx.command->argv == NULL /* not (PATTERN|(... */
+ && IS_NULL_WORD(ctx.word) /* not PATTERN(... or ""(... */
) {
continue; /* get next char */
}
@@ -6106,9 +6104,8 @@ static struct pipe *parse_stream(char **pstring,
/* fall through */
case '{': {
int n = parse_group(&ctx, input, ch);
- if (n < 0) {
+ if (n < 0)
goto parse_error_exitcode1;
- }
debug_printf_heredoc("parse_group done, needs heredocs:%d\n", n);
heredoc_cnt += n;
goto new_cmd;
@@ -6123,7 +6120,7 @@ static struct pipe *parse_stream(char **pstring,
}
#endif
case '}':
- /* proper use of this character is caught by end_trigger:
+ /* Proper use of this character is caught by end_trigger:
* if we see {, we call parse_group(..., end_trigger='}')
* and it will match } earlier (not here). */
G.last_exitcode = 2;