diff options
author | Denys Vlasenko <dvlasenk@redhat.com> | 2010-11-22 17:58:14 +0100 |
---|---|---|
committer | Denys Vlasenko <dvlasenk@redhat.com> | 2010-11-22 17:58:14 +0100 |
commit | 7b4c0fd5f4df4541a1659de04823326cd22c254e (patch) | |
tree | b0e14ceeb84af1155ba5dd25016569b20ff1e700 | |
parent | 1e23f32453d8aa84c66c8f0bb9f753d68614d05a (diff) | |
download | busybox-w32-7b4c0fd5f4df4541a1659de04823326cd22c254e.tar.gz busybox-w32-7b4c0fd5f4df4541a1659de04823326cd22c254e.tar.bz2 busybox-w32-7b4c0fd5f4df4541a1659de04823326cd22c254e.zip |
hush: fix improper handling of newline and hash chars in few corner cases
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
-rw-r--r-- | shell/hush.c | 253 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/assignment3.right | 2 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/assignment3.tests | 5 | ||||
-rw-r--r-- | shell/hush_test/hush-parsing/comment1.right | 2 | ||||
-rwxr-xr-x | shell/hush_test/hush-parsing/comment1.tests | 2 | ||||
-rw-r--r-- | shell/hush_test/hush-parsing/eol1.right | 1 | ||||
-rwxr-xr-x | shell/hush_test/hush-parsing/eol1.tests | 18 |
7 files changed, 160 insertions, 123 deletions
diff --git a/shell/hush.c b/shell/hush.c index 50e9ce333..da32c2435 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -543,7 +543,6 @@ struct command { | |||
543 | #define IS_NULL_CMD(cmd) \ | 543 | #define IS_NULL_CMD(cmd) \ |
544 | (!(cmd)->group && !(cmd)->argv && !(cmd)->redirects) | 544 | (!(cmd)->group && !(cmd)->argv && !(cmd)->redirects) |
545 | 545 | ||
546 | |||
547 | struct pipe { | 546 | struct pipe { |
548 | struct pipe *next; | 547 | struct pipe *next; |
549 | int num_cmds; /* total number of commands in pipe */ | 548 | int num_cmds; /* total number of commands in pipe */ |
@@ -2622,6 +2621,94 @@ static void free_pipe_list(struct pipe *pi) | |||
2622 | 2621 | ||
2623 | /*** Parsing routines ***/ | 2622 | /*** Parsing routines ***/ |
2624 | 2623 | ||
2624 | #ifndef debug_print_tree | ||
2625 | static void debug_print_tree(struct pipe *pi, int lvl) | ||
2626 | { | ||
2627 | static const char *const PIPE[] = { | ||
2628 | [PIPE_SEQ] = "SEQ", | ||
2629 | [PIPE_AND] = "AND", | ||
2630 | [PIPE_OR ] = "OR" , | ||
2631 | [PIPE_BG ] = "BG" , | ||
2632 | }; | ||
2633 | static const char *RES[] = { | ||
2634 | [RES_NONE ] = "NONE" , | ||
2635 | # if ENABLE_HUSH_IF | ||
2636 | [RES_IF ] = "IF" , | ||
2637 | [RES_THEN ] = "THEN" , | ||
2638 | [RES_ELIF ] = "ELIF" , | ||
2639 | [RES_ELSE ] = "ELSE" , | ||
2640 | [RES_FI ] = "FI" , | ||
2641 | # endif | ||
2642 | # if ENABLE_HUSH_LOOPS | ||
2643 | [RES_FOR ] = "FOR" , | ||
2644 | [RES_WHILE] = "WHILE", | ||
2645 | [RES_UNTIL] = "UNTIL", | ||
2646 | [RES_DO ] = "DO" , | ||
2647 | [RES_DONE ] = "DONE" , | ||
2648 | # endif | ||
2649 | # if ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE | ||
2650 | [RES_IN ] = "IN" , | ||
2651 | # endif | ||
2652 | # if ENABLE_HUSH_CASE | ||
2653 | [RES_CASE ] = "CASE" , | ||
2654 | [RES_CASE_IN ] = "CASE_IN" , | ||
2655 | [RES_MATCH] = "MATCH", | ||
2656 | [RES_CASE_BODY] = "CASE_BODY", | ||
2657 | [RES_ESAC ] = "ESAC" , | ||
2658 | # endif | ||
2659 | [RES_XXXX ] = "XXXX" , | ||
2660 | [RES_SNTX ] = "SNTX" , | ||
2661 | }; | ||
2662 | static const char *const CMDTYPE[] = { | ||
2663 | "{}", | ||
2664 | "()", | ||
2665 | "[noglob]", | ||
2666 | # if ENABLE_HUSH_FUNCTIONS | ||
2667 | "func()", | ||
2668 | # endif | ||
2669 | }; | ||
2670 | |||
2671 | int pin, prn; | ||
2672 | |||
2673 | pin = 0; | ||
2674 | while (pi) { | ||
2675 | fprintf(stderr, "%*spipe %d res_word=%s followup=%d %s\n", lvl*2, "", | ||
2676 | pin, RES[pi->res_word], pi->followup, PIPE[pi->followup]); | ||
2677 | prn = 0; | ||
2678 | while (prn < pi->num_cmds) { | ||
2679 | struct command *command = &pi->cmds[prn]; | ||
2680 | char **argv = command->argv; | ||
2681 | |||
2682 | fprintf(stderr, "%*s cmd %d assignment_cnt:%d", | ||
2683 | lvl*2, "", prn, | ||
2684 | command->assignment_cnt); | ||
2685 | if (command->group) { | ||
2686 | fprintf(stderr, " group %s: (argv=%p)%s%s\n", | ||
2687 | CMDTYPE[command->cmd_type], | ||
2688 | argv | ||
2689 | # if !BB_MMU | ||
2690 | , " group_as_string:", command->group_as_string | ||
2691 | # else | ||
2692 | , "", "" | ||
2693 | # endif | ||
2694 | ); | ||
2695 | debug_print_tree(command->group, lvl+1); | ||
2696 | prn++; | ||
2697 | continue; | ||
2698 | } | ||
2699 | if (argv) while (*argv) { | ||
2700 | fprintf(stderr, " '%s'", *argv); | ||
2701 | argv++; | ||
2702 | } | ||
2703 | fprintf(stderr, "\n"); | ||
2704 | prn++; | ||
2705 | } | ||
2706 | pi = pi->next; | ||
2707 | pin++; | ||
2708 | } | ||
2709 | } | ||
2710 | #endif /* debug_print_tree */ | ||
2711 | |||
2625 | static struct pipe *new_pipe(void) | 2712 | static struct pipe *new_pipe(void) |
2626 | { | 2713 | { |
2627 | struct pipe *pi; | 2714 | struct pipe *pi; |
@@ -4011,15 +4098,16 @@ static struct pipe *parse_stream(char **pstring, | |||
4011 | goto parse_error; | 4098 | goto parse_error; |
4012 | } | 4099 | } |
4013 | if (ch == '\n') { | 4100 | if (ch == '\n') { |
4014 | #if ENABLE_HUSH_CASE | 4101 | /* Is this a case when newline is simply ignored? |
4015 | /* "case ... in <newline> word) ..." - | 4102 | * Some examples: |
4016 | * newlines are ignored (but ';' wouldn't be) */ | 4103 | * "cmd | <newline> cmd ..." |
4017 | if (ctx.command->argv == NULL | 4104 | * "case ... in <newline> word) ..." |
4018 | && ctx.ctx_res_w == RES_MATCH | 4105 | */ |
4106 | if (IS_NULL_CMD(ctx.command) | ||
4107 | && dest.length == 0 && !dest.has_quoted_part | ||
4019 | ) { | 4108 | ) { |
4020 | continue; | 4109 | continue; |
4021 | } | 4110 | } |
4022 | #endif | ||
4023 | /* Treat newline as a command separator. */ | 4111 | /* Treat newline as a command separator. */ |
4024 | done_pipe(&ctx, PIPE_SEQ); | 4112 | done_pipe(&ctx, PIPE_SEQ); |
4025 | debug_printf_parse("heredoc_cnt:%d\n", heredoc_cnt); | 4113 | debug_printf_parse("heredoc_cnt:%d\n", heredoc_cnt); |
@@ -4151,6 +4239,31 @@ static struct pipe *parse_stream(char **pstring, | |||
4151 | if (parse_redirect(&ctx, redir_fd, redir_style, input)) | 4239 | if (parse_redirect(&ctx, redir_fd, redir_style, input)) |
4152 | goto parse_error; | 4240 | goto parse_error; |
4153 | continue; /* back to top of while (1) */ | 4241 | continue; /* back to top of while (1) */ |
4242 | case '#': | ||
4243 | if (dest.length == 0 && !dest.has_quoted_part) { | ||
4244 | /* skip "#comment" */ | ||
4245 | while (1) { | ||
4246 | ch = i_peek(input); | ||
4247 | if (ch == EOF || ch == '\n') | ||
4248 | break; | ||
4249 | i_getch(input); | ||
4250 | /* note: we do not add it to &ctx.as_string */ | ||
4251 | } | ||
4252 | nommu_addchr(&ctx.as_string, '\n'); | ||
4253 | continue; /* back to top of while (1) */ | ||
4254 | } | ||
4255 | break; | ||
4256 | case '\\': | ||
4257 | if (next == '\n') { | ||
4258 | /* It's "\<newline>" */ | ||
4259 | #if !BB_MMU | ||
4260 | /* Remove trailing '\' from ctx.as_string */ | ||
4261 | ctx.as_string.data[--ctx.as_string.length] = '\0'; | ||
4262 | #endif | ||
4263 | ch = i_getch(input); /* eat it */ | ||
4264 | continue; /* back to top of while (1) */ | ||
4265 | } | ||
4266 | break; | ||
4154 | } | 4267 | } |
4155 | 4268 | ||
4156 | if (dest.o_assignment == MAYBE_ASSIGNMENT | 4269 | if (dest.o_assignment == MAYBE_ASSIGNMENT |
@@ -4165,19 +4278,8 @@ static struct pipe *parse_stream(char **pstring, | |||
4165 | /* Note: nommu_addchr(&ctx.as_string, ch) is already done */ | 4278 | /* Note: nommu_addchr(&ctx.as_string, ch) is already done */ |
4166 | 4279 | ||
4167 | switch (ch) { | 4280 | switch (ch) { |
4168 | case '#': | 4281 | case '#': /* non-comment #: "echo a#b" etc */ |
4169 | if (dest.length == 0) { | 4282 | o_addQchr(&dest, ch); |
4170 | while (1) { | ||
4171 | ch = i_peek(input); | ||
4172 | if (ch == EOF || ch == '\n') | ||
4173 | break; | ||
4174 | i_getch(input); | ||
4175 | /* note: we do not add it to &ctx.as_string */ | ||
4176 | } | ||
4177 | nommu_addchr(&ctx.as_string, '\n'); | ||
4178 | } else { | ||
4179 | o_addQchr(&dest, ch); | ||
4180 | } | ||
4181 | break; | 4283 | break; |
4182 | case '\\': | 4284 | case '\\': |
4183 | if (next == EOF) { | 4285 | if (next == EOF) { |
@@ -4185,21 +4287,14 @@ static struct pipe *parse_stream(char **pstring, | |||
4185 | xfunc_die(); | 4287 | xfunc_die(); |
4186 | } | 4288 | } |
4187 | ch = i_getch(input); | 4289 | ch = i_getch(input); |
4188 | if (ch != '\n') { | 4290 | /* note: ch != '\n' (that case does not reach this place) */ |
4189 | o_addchr(&dest, '\\'); | 4291 | o_addchr(&dest, '\\'); |
4190 | /*nommu_addchr(&ctx.as_string, '\\'); - already done */ | 4292 | /*nommu_addchr(&ctx.as_string, '\\'); - already done */ |
4191 | o_addchr(&dest, ch); | 4293 | o_addchr(&dest, ch); |
4192 | nommu_addchr(&ctx.as_string, ch); | 4294 | nommu_addchr(&ctx.as_string, ch); |
4193 | /* Example: echo Hello \2>file | 4295 | /* Example: echo Hello \2>file |
4194 | * we need to know that word 2 is quoted */ | 4296 | * we need to know that word 2 is quoted */ |
4195 | dest.has_quoted_part = 1; | 4297 | dest.has_quoted_part = 1; |
4196 | } | ||
4197 | #if !BB_MMU | ||
4198 | else { | ||
4199 | /* It's "\<newline>". Remove trailing '\' from ctx.as_string */ | ||
4200 | ctx.as_string.data[--ctx.as_string.length] = '\0'; | ||
4201 | } | ||
4202 | #endif | ||
4203 | break; | 4298 | break; |
4204 | case '$': | 4299 | case '$': |
4205 | if (parse_dollar(&ctx.as_string, &dest, input, /*quote_mask:*/ 0) != 0) { | 4300 | if (parse_dollar(&ctx.as_string, &dest, input, /*quote_mask:*/ 0) != 0) { |
@@ -6869,94 +6964,6 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
6869 | return -1; | 6964 | return -1; |
6870 | } | 6965 | } |
6871 | 6966 | ||
6872 | #ifndef debug_print_tree | ||
6873 | static void debug_print_tree(struct pipe *pi, int lvl) | ||
6874 | { | ||
6875 | static const char *const PIPE[] = { | ||
6876 | [PIPE_SEQ] = "SEQ", | ||
6877 | [PIPE_AND] = "AND", | ||
6878 | [PIPE_OR ] = "OR" , | ||
6879 | [PIPE_BG ] = "BG" , | ||
6880 | }; | ||
6881 | static const char *RES[] = { | ||
6882 | [RES_NONE ] = "NONE" , | ||
6883 | # if ENABLE_HUSH_IF | ||
6884 | [RES_IF ] = "IF" , | ||
6885 | [RES_THEN ] = "THEN" , | ||
6886 | [RES_ELIF ] = "ELIF" , | ||
6887 | [RES_ELSE ] = "ELSE" , | ||
6888 | [RES_FI ] = "FI" , | ||
6889 | # endif | ||
6890 | # if ENABLE_HUSH_LOOPS | ||
6891 | [RES_FOR ] = "FOR" , | ||
6892 | [RES_WHILE] = "WHILE", | ||
6893 | [RES_UNTIL] = "UNTIL", | ||
6894 | [RES_DO ] = "DO" , | ||
6895 | [RES_DONE ] = "DONE" , | ||
6896 | # endif | ||
6897 | # if ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE | ||
6898 | [RES_IN ] = "IN" , | ||
6899 | # endif | ||
6900 | # if ENABLE_HUSH_CASE | ||
6901 | [RES_CASE ] = "CASE" , | ||
6902 | [RES_CASE_IN ] = "CASE_IN" , | ||
6903 | [RES_MATCH] = "MATCH", | ||
6904 | [RES_CASE_BODY] = "CASE_BODY", | ||
6905 | [RES_ESAC ] = "ESAC" , | ||
6906 | # endif | ||
6907 | [RES_XXXX ] = "XXXX" , | ||
6908 | [RES_SNTX ] = "SNTX" , | ||
6909 | }; | ||
6910 | static const char *const CMDTYPE[] = { | ||
6911 | "{}", | ||
6912 | "()", | ||
6913 | "[noglob]", | ||
6914 | # if ENABLE_HUSH_FUNCTIONS | ||
6915 | "func()", | ||
6916 | # endif | ||
6917 | }; | ||
6918 | |||
6919 | int pin, prn; | ||
6920 | |||
6921 | pin = 0; | ||
6922 | while (pi) { | ||
6923 | fprintf(stderr, "%*spipe %d res_word=%s followup=%d %s\n", lvl*2, "", | ||
6924 | pin, RES[pi->res_word], pi->followup, PIPE[pi->followup]); | ||
6925 | prn = 0; | ||
6926 | while (prn < pi->num_cmds) { | ||
6927 | struct command *command = &pi->cmds[prn]; | ||
6928 | char **argv = command->argv; | ||
6929 | |||
6930 | fprintf(stderr, "%*s cmd %d assignment_cnt:%d", | ||
6931 | lvl*2, "", prn, | ||
6932 | command->assignment_cnt); | ||
6933 | if (command->group) { | ||
6934 | fprintf(stderr, " group %s: (argv=%p)%s%s\n", | ||
6935 | CMDTYPE[command->cmd_type], | ||
6936 | argv | ||
6937 | # if !BB_MMU | ||
6938 | , " group_as_string:", command->group_as_string | ||
6939 | # else | ||
6940 | , "", "" | ||
6941 | # endif | ||
6942 | ); | ||
6943 | debug_print_tree(command->group, lvl+1); | ||
6944 | prn++; | ||
6945 | continue; | ||
6946 | } | ||
6947 | if (argv) while (*argv) { | ||
6948 | fprintf(stderr, " '%s'", *argv); | ||
6949 | argv++; | ||
6950 | } | ||
6951 | fprintf(stderr, "\n"); | ||
6952 | prn++; | ||
6953 | } | ||
6954 | pi = pi->next; | ||
6955 | pin++; | ||
6956 | } | ||
6957 | } | ||
6958 | #endif /* debug_print_tree */ | ||
6959 | |||
6960 | /* NB: called by pseudo_exec, and therefore must not modify any | 6967 | /* NB: called by pseudo_exec, and therefore must not modify any |
6961 | * global data until exec/_exit (we can be a child after vfork!) */ | 6968 | * global data until exec/_exit (we can be a child after vfork!) */ |
6962 | static int run_list(struct pipe *pi) | 6969 | static int run_list(struct pipe *pi) |
diff --git a/shell/hush_test/hush-misc/assignment3.right b/shell/hush_test/hush-misc/assignment3.right new file mode 100644 index 000000000..0f02d7cbc --- /dev/null +++ b/shell/hush_test/hush-misc/assignment3.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Done:0 | ||
2 | abc=123 | ||
diff --git a/shell/hush_test/hush-misc/assignment3.tests b/shell/hush_test/hush-misc/assignment3.tests new file mode 100755 index 000000000..790129be1 --- /dev/null +++ b/shell/hush_test/hush-misc/assignment3.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | # This must be interpreted as assignments | ||
2 | a=1 b\ | ||
3 | =2 c=3 | ||
4 | echo Done:$? | ||
5 | echo abc=$a$b$c | ||
diff --git a/shell/hush_test/hush-parsing/comment1.right b/shell/hush_test/hush-parsing/comment1.right new file mode 100644 index 000000000..a102b1d4e --- /dev/null +++ b/shell/hush_test/hush-parsing/comment1.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Nothing: | ||
2 | String: #should-be-echoed | ||
diff --git a/shell/hush_test/hush-parsing/comment1.tests b/shell/hush_test/hush-parsing/comment1.tests new file mode 100755 index 000000000..d268860ff --- /dev/null +++ b/shell/hush_test/hush-parsing/comment1.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | echo Nothing: #should-not-be-echoed | ||
2 | echo String: ""#should-be-echoed | ||
diff --git a/shell/hush_test/hush-parsing/eol1.right b/shell/hush_test/hush-parsing/eol1.right new file mode 100644 index 000000000..31c896f62 --- /dev/null +++ b/shell/hush_test/hush-parsing/eol1.right | |||
@@ -0,0 +1 @@ | |||
Done:0 | |||
diff --git a/shell/hush_test/hush-parsing/eol1.tests b/shell/hush_test/hush-parsing/eol1.tests new file mode 100755 index 000000000..f1b55e8b8 --- /dev/null +++ b/shell/hush_test/hush-parsing/eol1.tests | |||
@@ -0,0 +1,18 @@ | |||
1 | # bug was that we treated <newline> as ';' in this line: | ||
2 | true || echo foo | | ||
3 | echo BAD1 | cat | ||
4 | |||
5 | # variation on the same theme | ||
6 | true || echo foo | | ||
7 | # comment | ||
8 | echo BAD2 | cat | ||
9 | |||
10 | # variation on the same theme | ||
11 | true || echo foo | | ||
12 | |||
13 | echo BAD3 | cat | ||
14 | |||
15 | # this should error out, but currently works in hush: | ||
16 | #true || echo foo |; | ||
17 | |||
18 | echo Done:$? | ||