summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <dvlasenk@redhat.com>2010-11-22 17:58:14 +0100
committerDenys Vlasenko <dvlasenk@redhat.com>2010-11-22 17:58:14 +0100
commit7b4c0fd5f4df4541a1659de04823326cd22c254e (patch)
treeb0e14ceeb84af1155ba5dd25016569b20ff1e700
parent1e23f32453d8aa84c66c8f0bb9f753d68614d05a (diff)
downloadbusybox-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.c253
-rw-r--r--shell/hush_test/hush-misc/assignment3.right2
-rwxr-xr-xshell/hush_test/hush-misc/assignment3.tests5
-rw-r--r--shell/hush_test/hush-parsing/comment1.right2
-rwxr-xr-xshell/hush_test/hush-parsing/comment1.tests2
-rw-r--r--shell/hush_test/hush-parsing/eol1.right1
-rwxr-xr-xshell/hush_test/hush-parsing/eol1.tests18
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
547struct pipe { 546struct 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
2625static 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
2625static struct pipe *new_pipe(void) 2712static 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
6873static 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!) */
6962static int run_list(struct pipe *pi) 6969static 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 @@
1Done:0
2abc=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
2a=1 b\
3=2 c=3
4echo Done:$?
5echo 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 @@
1Nothing:
2String: #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 @@
1echo Nothing: #should-not-be-echoed
2echo 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:
2true || echo foo |
3echo BAD1 | cat
4
5# variation on the same theme
6true || echo foo |
7# comment
8echo BAD2 | cat
9
10# variation on the same theme
11true || echo foo |
12
13echo BAD3 | cat
14
15# this should error out, but currently works in hush:
16#true || echo foo |;
17
18echo Done:$?