aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2009-06-09 18:40:52 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2009-06-09 18:40:52 +0200
commit9d617c44d2b1135d14b7dafd01a1d3992293f4d9 (patch)
treea763d31365f18cb2bb984d252bc849f03c45625a
parent2634bf366b298a827d043566656f8696f4dc153c (diff)
downloadbusybox-w32-9d617c44d2b1135d14b7dafd01a1d3992293f4d9.tar.gz
busybox-w32-9d617c44d2b1135d14b7dafd01a1d3992293f4d9.tar.bz2
busybox-w32-9d617c44d2b1135d14b7dafd01a1d3992293f4d9.zip
hush: specially handle [[ - suppress globbing & multiword expansion
It's a bashism, but is surprisingly easy to do and costs very little code. Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--shell/hush.c132
-rw-r--r--shell/hush_test/hush-bugs/export_exp.right7
-rwxr-xr-xshell/hush_test/hush-bugs/export_exp.tests19
3 files changed, 138 insertions, 20 deletions
diff --git a/shell/hush.c b/shell/hush.c
index 9ecb60293..c9962e33b 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -296,12 +296,40 @@ struct command {
296 pid_t pid; /* 0 if exited */ 296 pid_t pid; /* 0 if exited */
297 int assignment_cnt; /* how many argv[i] are assignments? */ 297 int assignment_cnt; /* how many argv[i] are assignments? */
298 smallint is_stopped; /* is the command currently running? */ 298 smallint is_stopped; /* is the command currently running? */
299 smallint grp_type; /* GRP_xxx */ 299 smallint cmd_type; /* CMD_xxx */
300#define GRP_NORMAL 0 300#define CMD_NORMAL 0
301#define GRP_SUBSHELL 1 301#define CMD_SUBSHELL 1
302/* used for "[[ EXPR ]]" */
303#define CMD_SINGLEWORD_NOGLOB 2
304/* used for "export noglob=* glob* a=`echo a b`" */
305/* #define CMD_SINGLEWORD_NOGLOB_COND 3 */
306// It is hard to implement correctly, adds significant amounts of tricky code,
307// and all this only useful for really obscure export statements
308// almost nobody would use anyway. #ifdef CMD_SINGLEWORD_NOGLOB_COND
309// guard the code which implement it, but I have doubts it works
310// in all cases (especially with mixed globbed/non-globbed arguments)
302#if ENABLE_HUSH_FUNCTIONS 311#if ENABLE_HUSH_FUNCTIONS
303# define GRP_FUNCTION 2 312# define CMD_FUNCDEF 4
304#endif 313#endif
314//TODO
315//#define CMD_ARITH - let EXPR, ((EXPR))
316//let EXPR [EXPR...]
317// Each EXPR is an arithmetic expression to be evaluated (ARITHMETIC EVALUATION)
318// If the last arg evaluates to 0, let returns 1; 0 is returned otherwise.
319// NB: let `echo 'a=a + 1'` - error (IOW: multi-word expansion is used)
320//((EXPR))
321// The EXPR is evaluated according to ARITHMETIC EVALUATION.
322// This is exactly equivalent to let "expression".
323//[[ EXPR ]]
324// Basically, word splitting and pathname expansion should NOT be performed
325// Examples:
326// no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
327// no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
328// Additional operators:
329// || and && should work as -o and -a
330// =~ regexp match
331// Apart from the above, [[ expr ]] should work as [ expr ]
332
305 /* if non-NULL, this "command" is { list }, ( list ), or a compound statement */ 333 /* if non-NULL, this "command" is { list }, ( list ), or a compound statement */
306 struct pipe *group; 334 struct pipe *group;
307#if !BB_MMU 335#if !BB_MMU
@@ -310,11 +338,11 @@ struct command {
310#if ENABLE_HUSH_FUNCTIONS 338#if ENABLE_HUSH_FUNCTIONS
311 struct function *child_func; 339 struct function *child_func;
312/* This field is used to prevent a bug here: 340/* This field is used to prevent a bug here:
313 * while...do f1() {a;}; f1; f1 {b;}; f1; done 341 * while...do f1() {a;}; f1; f1() {b;}; f1; done
314 * When we execute "f1() {a;}" cmd, we create new function and clear 342 * When we execute "f1() {a;}" cmd, we create new function and clear
315 * cmd->group, cmd->group_as_string, cmd->argv[0]. 343 * cmd->group, cmd->group_as_string, cmd->argv[0].
316 * when we execute "f1 {b;}", we notice that f1 exists, 344 * When we execute "f1() {b;}", we notice that f1 exists,
317 * and that it's "parent cmd" struct is still "alive", 345 * and that its "parent cmd" struct is still "alive",
318 * we put those fields back into cmd->xxx 346 * we put those fields back into cmd->xxx
319 * (struct function has ->parent_cmd ptr to facilitate that). 347 * (struct function has ->parent_cmd ptr to facilitate that).
320 * When we loop back, we can execute "f1() {a;}" again and set f1 correctly. 348 * When we loop back, we can execute "f1() {a;}" again and set f1 correctly.
@@ -2440,7 +2468,7 @@ static char **expand_variables(char **argv, int or_mask)
2440 n = 0; 2468 n = 0;
2441 v = argv; 2469 v = argv;
2442 while (*v) { 2470 while (*v) {
2443 n = expand_vars_to_list(&output, n, *v, (char)or_mask); 2471 n = expand_vars_to_list(&output, n, *v, (unsigned char)or_mask);
2444 v++; 2472 v++;
2445 } 2473 }
2446 debug_print_list("expand_variables", &output, n); 2474 debug_print_list("expand_variables", &output, n);
@@ -2456,6 +2484,46 @@ static char **expand_strvec_to_strvec(char **argv)
2456 return expand_variables(argv, 0x100); 2484 return expand_variables(argv, 0x100);
2457} 2485}
2458 2486
2487static char **expand_strvec_to_strvec_singleword_noglob(char **argv)
2488{
2489 return expand_variables(argv, 0x80);
2490}
2491
2492#ifdef CMD_SINGLEWORD_NOGLOB_COND
2493static char **expand_strvec_to_strvec_singleword_noglob_cond(char **argv)
2494{
2495 int n;
2496 char **list;
2497 char **v;
2498 o_string output = NULL_O_STRING;
2499
2500 n = 0;
2501 v = argv;
2502 while (*v) {
2503 int is_var = is_well_formed_var_name(*v, '=');
2504 /* is_var * 0x80: singleword expansion for vars */
2505 n = expand_vars_to_list(&output, n, *v, is_var * 0x80);
2506
2507 /* Subtle! expand_vars_to_list did not glob last word yet.
2508 * It does this only when fed with further data.
2509 * Therefore we set globbing flags AFTER it, not before:
2510 */
2511
2512 /* if it is not recognizably abc=...; then: */
2513 output.o_escape = !is_var; /* protect against globbing for "$var" */
2514 /* (unquoted $var will temporarily switch it off) */
2515 output.o_glob = !is_var; /* and indeed do globbing */
2516 v++;
2517 }
2518 debug_print_list("expand_cond", &output, n);
2519
2520 /* output.data (malloced in one block) gets returned in "list" */
2521 list = o_finalize_list(&output, n);
2522 debug_print_strings("expand_cond[1]", list);
2523 return list;
2524}
2525#endif
2526
2459/* Used for expansion of right hand of assignments */ 2527/* Used for expansion of right hand of assignments */
2460/* NB: should NOT do globbing! "export v=/bin/c*; env | grep ^v=" outputs 2528/* NB: should NOT do globbing! "export v=/bin/c*; env | grep ^v=" outputs
2461 * "v=/bin/c*" */ 2529 * "v=/bin/c*" */
@@ -2465,7 +2533,7 @@ static char *expand_string_to_string(const char *str)
2465 2533
2466 argv[0] = (char*)str; 2534 argv[0] = (char*)str;
2467 argv[1] = NULL; 2535 argv[1] = NULL;
2468 list = expand_variables(argv, 0x80); /* 0x80: make one-element expansion */ 2536 list = expand_variables(argv, 0x80); /* 0x80: singleword expansion */
2469 if (HUSH_DEBUG) 2537 if (HUSH_DEBUG)
2470 if (!list[0] || list[1]) 2538 if (!list[0] || list[1])
2471 bb_error_msg_and_die("BUG in varexp2"); 2539 bb_error_msg_and_die("BUG in varexp2");
@@ -2875,8 +2943,8 @@ static void free_pipe(struct pipe *pi)
2875 } 2943 }
2876 /* not "else if": on syntax error, we may have both! */ 2944 /* not "else if": on syntax error, we may have both! */
2877 if (command->group) { 2945 if (command->group) {
2878 debug_printf_clean(" begin group (grp_type:%d)\n", 2946 debug_printf_clean(" begin group (cmd_type:%d)\n",
2879 command->grp_type); 2947 command->cmd_type);
2880 free_pipe_list(command->group); 2948 free_pipe_list(command->group);
2881 debug_printf_clean(" end group\n"); 2949 debug_printf_clean(" end group\n");
2882 command->group = NULL; 2950 command->group = NULL;
@@ -3688,7 +3756,7 @@ static int run_pipe(struct pipe *pi)
3688 3756
3689 if (pi->num_cmds != 1 3757 if (pi->num_cmds != 1
3690 || pi->followup == PIPE_BG 3758 || pi->followup == PIPE_BG
3691 || command->grp_type == GRP_SUBSHELL 3759 || command->cmd_type == CMD_SUBSHELL
3692 ) { 3760 ) {
3693 goto must_fork; 3761 goto must_fork;
3694 } 3762 }
@@ -3700,7 +3768,7 @@ static int run_pipe(struct pipe *pi)
3700 3768
3701 if (command->group) { 3769 if (command->group) {
3702#if ENABLE_HUSH_FUNCTIONS 3770#if ENABLE_HUSH_FUNCTIONS
3703 if (command->grp_type == GRP_FUNCTION) { 3771 if (command->cmd_type == CMD_FUNCDEF) {
3704 /* "executing" func () { list } */ 3772 /* "executing" func () { list } */
3705 struct function *funcp; 3773 struct function *funcp;
3706 3774
@@ -3770,7 +3838,18 @@ static int run_pipe(struct pipe *pi)
3770 } 3838 }
3771 3839
3772 /* Expand the rest into (possibly) many strings each */ 3840 /* Expand the rest into (possibly) many strings each */
3773 argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt); 3841 if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) {
3842 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt);
3843 }
3844#ifdef CMD_SINGLEWORD_NOGLOB_COND
3845 else if (command->cmd_type == CMD_SINGLEWORD_NOGLOB_COND) {
3846 argv_expanded = expand_strvec_to_strvec_singleword_noglob_cond(argv + command->assignment_cnt);
3847
3848 }
3849#endif
3850 else {
3851 argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt);
3852 }
3774 3853
3775 x = find_builtin(argv_expanded[0]); 3854 x = find_builtin(argv_expanded[0]);
3776#if ENABLE_HUSH_FUNCTIONS 3855#if ENABLE_HUSH_FUNCTIONS
@@ -4012,7 +4091,7 @@ static void debug_print_tree(struct pipe *pi, int lvl)
4012 [RES_XXXX ] = "XXXX" , 4091 [RES_XXXX ] = "XXXX" ,
4013 [RES_SNTX ] = "SNTX" , 4092 [RES_SNTX ] = "SNTX" ,
4014 }; 4093 };
4015 static const char *const GRPTYPE[] = { 4094 static const char *const CMDTYPE[] = {
4016 "{}", 4095 "{}",
4017 "()", 4096 "()",
4018#if ENABLE_HUSH_FUNCTIONS 4097#if ENABLE_HUSH_FUNCTIONS
@@ -4036,7 +4115,7 @@ static void debug_print_tree(struct pipe *pi, int lvl)
4036 command->assignment_cnt); 4115 command->assignment_cnt);
4037 if (command->group) { 4116 if (command->group) {
4038 fprintf(stderr, " group %s: (argv=%p)\n", 4117 fprintf(stderr, " group %s: (argv=%p)\n",
4039 GRPTYPE[command->grp_type], 4118 CMDTYPE[command->cmd_type],
4040 argv); 4119 argv);
4041 debug_print_tree(command->group, lvl+1); 4120 debug_print_tree(command->group, lvl+1);
4042 prn++; 4121 prn++;
@@ -4649,7 +4728,7 @@ static int reserved_word(o_string *word, struct parse_context *ctx)
4649 debug_printf_parse("pop stack %p\n", ctx->stack); 4728 debug_printf_parse("pop stack %p\n", ctx->stack);
4650 old = ctx->stack; 4729 old = ctx->stack;
4651 old->command->group = ctx->list_head; 4730 old->command->group = ctx->list_head;
4652 old->command->grp_type = GRP_NORMAL; 4731 old->command->cmd_type = CMD_NORMAL;
4653#if !BB_MMU 4732#if !BB_MMU
4654 o_addstr(&old->as_string, ctx->as_string.data); 4733 o_addstr(&old->as_string, ctx->as_string.data);
4655 o_free_unsafe(&ctx->as_string); 4734 o_free_unsafe(&ctx->as_string);
@@ -4745,6 +4824,19 @@ static int done_word(o_string *word, struct parse_context *ctx)
4745 (ctx->ctx_res_w == RES_SNTX)); 4824 (ctx->ctx_res_w == RES_SNTX));
4746 return (ctx->ctx_res_w == RES_SNTX); 4825 return (ctx->ctx_res_w == RES_SNTX);
4747 } 4826 }
4827# ifdef CMD_SINGLEWORD_NOGLOB_COND
4828 if (strcmp(word->data, "export") == 0
4829# if ENABLE_HUSH_LOCAL
4830 || strcmp(word->data, "local") == 0
4831# endif
4832 ) {
4833 command->cmd_type = CMD_SINGLEWORD_NOGLOB_COND;
4834 } else
4835# endif
4836 if (strcmp(word->data, "[[") == 0) {
4837 command->cmd_type = CMD_SINGLEWORD_NOGLOB;
4838 }
4839 /* fall through */
4748 } 4840 }
4749#endif 4841#endif
4750 if (command->group) { 4842 if (command->group) {
@@ -5189,7 +5281,7 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
5189 return 1; 5281 return 1;
5190 } 5282 }
5191 nommu_addchr(&ctx->as_string, ch); 5283 nommu_addchr(&ctx->as_string, ch);
5192 command->grp_type = GRP_FUNCTION; 5284 command->cmd_type = CMD_FUNCDEF;
5193 goto skip; 5285 goto skip;
5194 } 5286 }
5195#endif 5287#endif
@@ -5209,7 +5301,7 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
5209 endch = '}'; 5301 endch = '}';
5210 if (ch == '(') { 5302 if (ch == '(') {
5211 endch = ')'; 5303 endch = ')';
5212 command->grp_type = GRP_SUBSHELL; 5304 command->cmd_type = CMD_SUBSHELL;
5213 } else { 5305 } else {
5214 /* bash does not allow "{echo...", requires whitespace */ 5306 /* bash does not allow "{echo...", requires whitespace */
5215 ch = i_getch(input); 5307 ch = i_getch(input);
diff --git a/shell/hush_test/hush-bugs/export_exp.right b/shell/hush_test/hush-bugs/export_exp.right
new file mode 100644
index 000000000..17a2e93f7
--- /dev/null
+++ b/shell/hush_test/hush-bugs/export_exp.right
@@ -0,0 +1,7 @@
1aa0 bb0
2a=aa0 b=bb0
3aa1 bb1
4a=aa1 b=bb1
5zzz=zzz
6zz=*
7Done
diff --git a/shell/hush_test/hush-bugs/export_exp.tests b/shell/hush_test/hush-bugs/export_exp.tests
new file mode 100755
index 000000000..91f57aa2c
--- /dev/null
+++ b/shell/hush_test/hush-bugs/export_exp.tests
@@ -0,0 +1,19 @@
1v="a=aa0 b=bb0"
2# only 1st arg should be expanded in multiple words
3export $v c=$v
4echo $a $b
5echo $c
6
7# only 1st arg should be expanded in multiple words
8export `echo a=aa1 b=bb1` c=`echo a=aa1 b=bb1`
9echo $a $b
10echo $c
11
12>zz=zz
13>zzz=zzz
14# only 1st arg should be globbed
15export zzz* zz=*
16env | grep ^zz | sort
17rm -rf zz=zz zzz=zzz
18
19echo Done