aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2025-08-14 02:30:32 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2025-08-14 02:30:32 +0200
commit2bb8b9b2a4eb057208ae3a131aa626901d9f905a (patch)
tree0be59353c7973ddba8604ce092e8061fdce44db6 /shell
parent438b92efdd73d0552ecaa938aa33de7f445bb87f (diff)
downloadbusybox-w32-2bb8b9b2a4eb057208ae3a131aa626901d9f905a.tar.gz
busybox-w32-2bb8b9b2a4eb057208ae3a131aa626901d9f905a.tar.bz2
busybox-w32-2bb8b9b2a4eb057208ae3a131aa626901d9f905a.zip
hush: remove two strchr's from the hottest parsing loop
function old new delta parse_stream 2572 2566 -6 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'shell')
-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
5119# if BB_MMU 5119# if BB_MMU
5120#define parse_dollar_squote(as_string, dest, input) \ 5120#define parse_dollar_squote(as_string, dest, input) \
5121 parse_dollar_squote(dest, input) 5121 parse_dollar_squote(dest, input)
5122#define as_string NULL
5123# endif 5122# endif
5124static int parse_dollar_squote(o_string *as_string, o_string *dest, struct in_str *input) 5123static int parse_dollar_squote(o_string *as_string, o_string *dest, struct in_str *input)
5125{ 5124{
@@ -5204,7 +5203,6 @@ static int parse_dollar_squote(o_string *as_string, o_string *dest, struct in_st
5204 } 5203 }
5205 5204
5206 return 1; 5205 return 1;
5207# undef as_string
5208} 5206}
5209#else 5207#else
5210# define parse_dollar_squote(as_string, dest, input) 0 5208# define parse_dollar_squote(as_string, dest, input) 0
@@ -5482,7 +5480,6 @@ static int parse_dollar(o_string *as_string,
5482#if BB_MMU 5480#if BB_MMU
5483#define encode_string(as_string, dest, input, dquote_end) \ 5481#define encode_string(as_string, dest, input, dquote_end) \
5484 encode_string(dest, input, dquote_end) 5482 encode_string(dest, input, dquote_end)
5485#define as_string NULL
5486#endif 5483#endif
5487static int encode_string(o_string *as_string, 5484static int encode_string(o_string *as_string,
5488 o_string *dest, 5485 o_string *dest,
@@ -5564,7 +5561,6 @@ static int encode_string(o_string *as_string,
5564 o_addchr(dest, SPECIAL_VAR_SYMBOL); 5561 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5565 } 5562 }
5566 goto again; 5563 goto again;
5567#undef as_string
5568} 5564}
5569 5565
5570/* 5566/*
@@ -5605,8 +5601,7 @@ static struct pipe *parse_stream(char **pstring,
5605 5601
5606 heredoc_cnt = 0; 5602 heredoc_cnt = 0;
5607 while (1) { 5603 while (1) {
5608 const char *is_blank; 5604 int is_blank;
5609 const char *is_special;
5610 int ch; 5605 int ch;
5611 int next; 5606 int next;
5612 int redir_fd; 5607 int redir_fd;
@@ -5623,11 +5618,8 @@ static struct pipe *parse_stream(char **pstring,
5623 continue; 5618 continue;
5624 } 5619 }
5625#endif 5620#endif
5626 if (ch == EOF)
5627 break;
5628
5629 /* Handle "'" and "\" first, as they won't play nice with 5621 /* Handle "'" and "\" first, as they won't play nice with
5630 * i_peek_and_eat_bkslash_nl() anyway: 5622 * i_peek_and_eat_bkslash_nl():
5631 * echo z\\ 5623 * echo z\\
5632 * and 5624 * and
5633 * echo '\ 5625 * echo '\
@@ -5660,13 +5652,14 @@ static struct pipe *parse_stream(char **pstring,
5660 o_addchr(&ctx.word, ch); 5652 o_addchr(&ctx.word, ch);
5661 continue; /* get next char */ 5653 continue; /* get next char */
5662 } 5654 }
5655 if (ch == EOF)
5656 break;
5663 nommu_addchr(&ctx.as_string, ch); 5657 nommu_addchr(&ctx.as_string, ch);
5664 if (ch == '\'') { 5658 if (ch == '\'') {
5665 ctx.word.has_quoted_part = 1; 5659 ctx.word.has_quoted_part = 1;
5666 next = i_getch(input); 5660 next = i_getch(input);
5667 if (next == '\'' && !ctx.pending_redirect/*why?*/) 5661 if (next == '\'' && !ctx.pending_redirect/*why?*/)
5668 goto insert_empty_quoted_str_marker; 5662 goto insert_empty_quoted_str_marker;
5669
5670 ch = next; 5663 ch = next;
5671 while (1) { 5664 while (1) {
5672 if (ch == EOF) { 5665 if (ch == EOF) {
@@ -5688,73 +5681,38 @@ static struct pipe *parse_stream(char **pstring,
5688 } 5681 }
5689 5682
5690 next = '\0'; 5683 next = '\0';
5691 if (ch != '\n') 5684 is_blank = 1;
5692 next = i_peek_and_eat_bkslash_nl(input); 5685 /* If '\n', must not peek (peeking past '\n' provokes line editing) */
5693 5686 if (ch == '\n')
5694 is_special = "{}<>&|();#" /* special outside of "str" */ 5687 /* had to test for '\n' anyway, can also jump directly to newline handling */
5695 "$\"" IF_HUSH_TICK("`") /* always special */ 5688 goto ch_is_newline;
5696 SPECIAL_VAR_SYMBOL_STR; 5689 next = i_peek_and_eat_bkslash_nl(input);
5697#if defined(CMD_TEST2_SINGLEWORD_NOGLOB) 5690
5698 if (ctx.command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB) { 5691 is_blank = (ch == ' ' || ch == '\t');
5699 /* In [[ ]], {}<>&|() are not special */
5700 is_special += 8;
5701 } else
5702#endif
5703 /* Are { and } special here? */
5704 if (ctx.command->argv /* word [word]{... - non-special */
5705 || !IS_NULL_WORD(ctx.word) /* word{... ""{... - non-special */
5706 || (next != ';' /* }; - special */
5707 && next != ')' /* }) - special */
5708 && next != '(' /* {( - special */
5709 && next != '&' /* }& and }&& ... - special */
5710 && next != '|' /* }|| ... - special */
5711 && !strchr(defifs, next) /* {word - non-special */
5712 )
5713 ) {
5714 /* They are not special, skip "{}" */
5715 is_special += 2;
5716 }
5717 is_special = strchr(is_special, ch);
5718 is_blank = strchr(defifs, ch);
5719
5720 if (!is_special && !is_blank) { /* ordinary char */
5721 ordinary_char:
5722 o_addQchr(&ctx.word, ch);
5723 if ((ctx.is_assignment == MAYBE_ASSIGNMENT
5724 || ctx.is_assignment == WORD_IS_KEYWORD)
5725 && ch == '='
5726 && endofname(ctx.word.data)[0] == '='
5727 ) {
5728 ctx.is_assignment = DEFINITELY_ASSIGNMENT;
5729 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
5730 }
5731 continue;
5732 }
5733
5734 if (is_blank) { 5692 if (is_blank) {
5735#if ENABLE_HUSH_LINENO_VAR 5693#if ENABLE_HUSH_LINENO_VAR
5736/* Case: 5694/* "while ...; do<whitespace><newline>
5737 * "while ...; do<whitespace><newline> 5695 * CMD"
5738 * cmd ..." 5696 * would think that CMD starts in <whitespace> -
5739 * would think that "cmd" starts in <whitespace> -
5740 * i.e., at the previous line. 5697 * i.e., at the previous line.
5741 * We need to skip all whitespace before newlines. 5698 * Need to skip whitespace up to next newline (and eat it)
5699 * or not-whitespace (and do not eat it).
5742 */ 5700 */
5743 while (ch != '\n') { 5701 do {
5744 next = i_peek(input); 5702 next = i_peek(input);
5745 if (next != ' ' && next != '\t' && next != '\n') 5703 if (next != ' ' && next != '\t' && next != '\n')
5746 break; /* next char is not ws */ 5704 break; /* next char is not ws */
5747 ch = i_getch(input); 5705 ch = i_getch(input);
5748 } 5706 } while (ch != '\n');
5749 /* ch == last eaten whitespace char */
5750#endif 5707#endif
5708 ch_is_newline:
5751 if (done_word(&ctx)) 5709 if (done_word(&ctx))
5752 goto parse_error_exitcode1; 5710 goto parse_error_exitcode1;
5753 if (ch == '\n') { 5711 if (ch == '\n') {
5754 /* Is this a case when newline is simply ignored? 5712 /* Is this a case when newline is simply ignored?
5755 * Some examples: 5713 * Some examples:
5756 * "cmd | <newline> cmd ..." 5714 * "CMD | <newline> CMD ..."
5757 * "case ... in <newline> word) ..." 5715 * "case ... in <newline> PATTERN) ..."
5758 */ 5716 */
5759 if (IS_NULL_CMD(ctx.command) 5717 if (IS_NULL_CMD(ctx.command)
5760 && IS_NULL_WORD(ctx.word) 5718 && IS_NULL_WORD(ctx.word)
@@ -5764,18 +5722,18 @@ static struct pipe *parse_stream(char **pstring,
5764 * Without check #1, interactive shell 5722 * Without check #1, interactive shell
5765 * ignores even bare <newline>, 5723 * ignores even bare <newline>,
5766 * and shows the continuation prompt: 5724 * and shows the continuation prompt:
5767 * ps1_prompt$ <enter> 5725 * ps1$ <enter>
5768 * ps2> _ <=== wrong, should be ps1 5726 * ps2> _ <=== wrong, should be ps1
5769 * Without check #2, "cmd & <newline>" 5727 * Without check #2, "CMD & <newline>"
5770 * is similarly mistreated. 5728 * is similarly mistreated.
5771 * (BTW, this makes "cmd & cmd" 5729 * (BTW, this makes "CMD & CMD"
5772 * and "cmd && cmd" non-orthogonal. 5730 * and "CMD && CMD" non-orthogonal.
5773 * Really, ask yourself, why 5731 * Really, ask yourself, why
5774 * "cmd && <newline>" doesn't start 5732 * "CMD && <newline>" doesn't start
5775 * cmd but waits for more input? 5733 * CMD but waits for more input?
5776 * The only reason is that it might be 5734 * The only reason is that it might be
5777 * a "cmd1 && <nl> cmd2 &" construct, 5735 * a "CMD1 && <nl> CMD2 &" construct:
5778 * cmd1 may need to run in BG). 5736 * CMD1 may need to run in BG).
5779 */ 5737 */
5780 pi = ctx.list_head; 5738 pi = ctx.list_head;
5781 if (pi->num_cmds != 0 /* check #1 */ 5739 if (pi->num_cmds != 0 /* check #1 */
@@ -5797,11 +5755,51 @@ static struct pipe *parse_stream(char **pstring,
5797 ch = ';'; 5755 ch = ';';
5798 /* note: if (is_blank) continue; 5756 /* note: if (is_blank) continue;
5799 * will still trigger for us */ 5757 * will still trigger for us */
5758 } /* if ch == '\n */
5759 } else {
5760 const char *is_special;
5761
5762 is_special = "{}<>&|();#" /* special outside of "str" */
5763 "$\"" IF_HUSH_TICK("`") /* always special */
5764 SPECIAL_VAR_SYMBOL_STR;
5765#if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
5766 if (ctx.command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB) {
5767 /* In [[ ]], {}<>&|() are not special */
5768 is_special += 8;
5769 } else
5770#endif
5771 /* Are { and } special here? */
5772 if (ctx.command->argv /* WORD [WORD]{... - non-special */
5773 || !IS_NULL_WORD(ctx.word) /* WORD{... ""{... - non-special */
5774 || (next != ';' /* }; - special */
5775 && next != ')' /* }) - special */
5776 && next != '(' /* {( - special */
5777 && next != '&' /* }& and }&& ... - special */
5778 && next != '|' /* }|| ... - special */
5779 && !strchr(defifs, next) /* {WORD - non-special */
5780 )
5781 ) {
5782 /* They are not special, skip "{}" */
5783 is_special += 2;
5784 }
5785 is_special = strchr(is_special, ch);
5786 if (!is_special) { /* ordinary char */
5787 ordinary_char:
5788 o_addQchr(&ctx.word, ch);
5789 if ((ctx.is_assignment == MAYBE_ASSIGNMENT
5790 || ctx.is_assignment == WORD_IS_KEYWORD)
5791 && ch == '='
5792 && endofname(ctx.word.data)[0] == '='
5793 ) {
5794 ctx.is_assignment = DEFINITELY_ASSIGNMENT;
5795 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
5796 }
5797 continue;
5800 } 5798 }
5801 } 5799 }
5802 5800
5803 /* "cmd}" or "cmd }..." without semicolon or &: 5801 /* "CMD}" or "CMD }..." without semicolon or &:
5804 * } is an ordinary char in this case, even inside { cmd; } 5802 * } is an ordinary char in this case, even inside { CMD; }
5805 * Pathological example: { ""}; } should run "}" command. 5803 * Pathological example: { ""}; } should run "}" command.
5806 */ 5804 */
5807 if (ch == '}') { 5805 if (ch == '}') {
@@ -5809,9 +5807,9 @@ static struct pipe *parse_stream(char **pstring,
5809 /* word} or ""} */ 5807 /* word} or ""} */
5810 goto ordinary_char; 5808 goto ordinary_char;
5811 } 5809 }
5812 if (!IS_NULL_CMD(ctx.command)) { /* cmd } */ 5810 if (!IS_NULL_CMD(ctx.command)) { /* CMD } */
5813 /* Generally, there should be semicolon: "cmd; }" 5811 /* Generally, there should be semicolon: "CMD; }"
5814 * However, bash allows to omit it if "cmd" is 5812 * However, bash allows to omit it if "CMD" is
5815 * a group. Examples: 5813 * a group. Examples:
5816 * { { echo 1; } } 5814 * { { echo 1; } }
5817 * {(echo 1)} 5815 * {(echo 1)}
@@ -5823,8 +5821,8 @@ static struct pipe *parse_stream(char **pstring,
5823 goto term_group; 5821 goto term_group;
5824 goto ordinary_char; 5822 goto ordinary_char;
5825 } 5823 }
5826 if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */ 5824 if (!IS_NULL_PIPE(ctx.pipe)) /* CMD | } */
5827 /* Can't be an end of {cmd}, skip the check */ 5825 /* Can't be an end of {CMD}, skip the check */
5828 goto skip_end_trigger; 5826 goto skip_end_trigger;
5829 /* else: } does terminate a group */ 5827 /* else: } does terminate a group */
5830 } 5828 }
@@ -5876,7 +5874,7 @@ static struct pipe *parse_stream(char **pstring,
5876 } 5874 }
5877 } 5875 }
5878 5876
5879 if (is_blank) 5877 if (is_blank) /* space, tab or newline? */
5880 continue; 5878 continue;
5881 5879
5882 /* Catch <, > before deciding whether this word is 5880 /* Catch <, > before deciding whether this word is
@@ -5928,7 +5926,7 @@ static struct pipe *parse_stream(char **pstring,
5928 continue; /* get next char */ 5926 continue; /* get next char */
5929 case '#': 5927 case '#':
5930 if (IS_NULL_WORD(ctx.word)) { 5928 if (IS_NULL_WORD(ctx.word)) {
5931 /* skip "#comment" */ 5929 /* skip "#COMMENT" */
5932 /* note: we do not add it to &ctx.as_string */ 5930 /* note: we do not add it to &ctx.as_string */
5933/* TODO: in bash: 5931/* TODO: in bash:
5934 * comment inside $() goes to the next \n, even inside quoted string (!): 5932 * comment inside $() goes to the next \n, even inside quoted string (!):
@@ -6068,7 +6066,7 @@ static struct pipe *parse_stream(char **pstring,
6068 goto parse_error_exitcode1; 6066 goto parse_error_exitcode1;
6069#if ENABLE_HUSH_CASE 6067#if ENABLE_HUSH_CASE
6070 if (ctx.ctx_res_w == RES_MATCH) 6068 if (ctx.ctx_res_w == RES_MATCH)
6071 break; /* we are in case's "word | word)" */ 6069 break; /* we are in case's "WORD | WORD)" */
6072#endif 6070#endif
6073 if (next == '|') { /* || */ 6071 if (next == '|') { /* || */
6074 ch = i_getch(input); 6072 ch = i_getch(input);
@@ -6095,10 +6093,10 @@ static struct pipe *parse_stream(char **pstring,
6095 goto new_cmd; 6093 goto new_cmd;
6096 case '(': 6094 case '(':
6097#if ENABLE_HUSH_CASE 6095#if ENABLE_HUSH_CASE
6098 /* "case... in [(]word)..." - skip '(' */ 6096 /* "case... in [(]PATTERN)..." - skip '(' */
6099 if (ctx.ctx_res_w == RES_MATCH 6097 if (ctx.ctx_res_w == RES_MATCH
6100 && ctx.command->argv == NULL /* not (word|(... */ 6098 && ctx.command->argv == NULL /* not (PATTERN|(... */
6101 && IS_NULL_WORD(ctx.word) /* not word(... or ""(... */ 6099 && IS_NULL_WORD(ctx.word) /* not PATTERN(... or ""(... */
6102 ) { 6100 ) {
6103 continue; /* get next char */ 6101 continue; /* get next char */
6104 } 6102 }
@@ -6106,9 +6104,8 @@ static struct pipe *parse_stream(char **pstring,
6106 /* fall through */ 6104 /* fall through */
6107 case '{': { 6105 case '{': {
6108 int n = parse_group(&ctx, input, ch); 6106 int n = parse_group(&ctx, input, ch);
6109 if (n < 0) { 6107 if (n < 0)
6110 goto parse_error_exitcode1; 6108 goto parse_error_exitcode1;
6111 }
6112 debug_printf_heredoc("parse_group done, needs heredocs:%d\n", n); 6109 debug_printf_heredoc("parse_group done, needs heredocs:%d\n", n);
6113 heredoc_cnt += n; 6110 heredoc_cnt += n;
6114 goto new_cmd; 6111 goto new_cmd;
@@ -6123,7 +6120,7 @@ static struct pipe *parse_stream(char **pstring,
6123 } 6120 }
6124#endif 6121#endif
6125 case '}': 6122 case '}':
6126 /* proper use of this character is caught by end_trigger: 6123 /* Proper use of this character is caught by end_trigger:
6127 * if we see {, we call parse_group(..., end_trigger='}') 6124 * if we see {, we call parse_group(..., end_trigger='}')
6128 * and it will match } earlier (not here). */ 6125 * and it will match } earlier (not here). */
6129 G.last_exitcode = 2; 6126 G.last_exitcode = 2;