diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-10 19:05:43 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-10 19:05:43 +0000 |
commit | b7d8c0dbbd250649d647142edd33226822f3c879 (patch) | |
tree | a315a2c6a2fa6b062fe66aacf0eea66587737a54 | |
parent | 835fcfd33d574d471d9b67a69116281d9ff42040 (diff) | |
download | busybox-w32-b7d8c0dbbd250649d647142edd33226822f3c879.tar.gz busybox-w32-b7d8c0dbbd250649d647142edd33226822f3c879.tar.bz2 busybox-w32-b7d8c0dbbd250649d647142edd33226822f3c879.zip |
hush: first stab at function support. argv passing is not coded yet.
Only very rudimentary testing was done.
With function support off, code growth is zero, with it on:
function old new delta
run_list 2158 2339 +181
parse_stream 1929 2044 +115
find_builtin 24 67 +43
find_function - 36 +36
file_get 244 264 +20
pseudo_exec_argv 145 160 +15
free_strings - 7 +7
free_pipe 183 181 -2
done_word 735 728 -7
expand_variables 2227 2204 -23
------------------------------------------------------------------------------
(add/remove: 2/0 grow/shrink: 5/3 up/down: 417/-32) Total: 385 bytes
-rw-r--r-- | shell/hush.c | 233 |
1 files changed, 169 insertions, 64 deletions
diff --git a/shell/hush.c b/shell/hush.c index 9adf0e127..3728583fc 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -86,7 +86,7 @@ | |||
86 | */ | 86 | */ |
87 | #define HUSH_DEBUG 1 | 87 | #define HUSH_DEBUG 1 |
88 | /* In progress... */ | 88 | /* In progress... */ |
89 | #define ENABLE_HUSH_FUNCTIONS 0 | 89 | #define ENABLE_HUSH_FUNCTIONS 1 |
90 | 90 | ||
91 | 91 | ||
92 | #if BUILD_AS_NOMMU | 92 | #if BUILD_AS_NOMMU |
@@ -374,12 +374,12 @@ struct command { | |||
374 | #define GRP_NORMAL 0 | 374 | #define GRP_NORMAL 0 |
375 | #define GRP_SUBSHELL 1 | 375 | #define GRP_SUBSHELL 1 |
376 | #if ENABLE_HUSH_FUNCTIONS | 376 | #if ENABLE_HUSH_FUNCTIONS |
377 | #define GRP_FUNCTION 2 | 377 | # define GRP_FUNCTION 2 |
378 | #endif | 378 | #endif |
379 | 379 | ||
380 | struct pipe { | 380 | struct pipe { |
381 | struct pipe *next; | 381 | struct pipe *next; |
382 | int num_cmds; /* total number of commands in job */ | 382 | int num_cmds; /* total number of commands in pipe */ |
383 | int alive_cmds; /* number of commands running (not exited) */ | 383 | int alive_cmds; /* number of commands running (not exited) */ |
384 | int stopped_cmds; /* number of commands alive, but stopped */ | 384 | int stopped_cmds; /* number of commands alive, but stopped */ |
385 | #if ENABLE_HUSH_JOB | 385 | #if ENABLE_HUSH_JOB |
@@ -452,6 +452,12 @@ enum { | |||
452 | BC_CONTINUE = 2, | 452 | BC_CONTINUE = 2, |
453 | }; | 453 | }; |
454 | 454 | ||
455 | struct function { | ||
456 | struct function *next; | ||
457 | char *name; | ||
458 | struct pipe *body; | ||
459 | }; | ||
460 | |||
455 | 461 | ||
456 | /* "Globals" within this file */ | 462 | /* "Globals" within this file */ |
457 | /* Sorted roughly by size (smaller offsets == smaller code) */ | 463 | /* Sorted roughly by size (smaller offsets == smaller code) */ |
@@ -504,6 +510,7 @@ struct globals { | |||
504 | const char *cwd; | 510 | const char *cwd; |
505 | struct variable *top_var; /* = &G.shell_ver (set in main()) */ | 511 | struct variable *top_var; /* = &G.shell_ver (set in main()) */ |
506 | struct variable shell_ver; | 512 | struct variable shell_ver; |
513 | struct function *top_func; | ||
507 | /* Signal and trap handling */ | 514 | /* Signal and trap handling */ |
508 | // unsigned count_SIGCHLD; | 515 | // unsigned count_SIGCHLD; |
509 | // unsigned handled_SIGCHLD; | 516 | // unsigned handled_SIGCHLD; |
@@ -2585,6 +2592,35 @@ static void free_pipe_list(struct pipe *head, int indent) | |||
2585 | } | 2592 | } |
2586 | 2593 | ||
2587 | 2594 | ||
2595 | static const struct built_in_command* find_builtin(const char *name) | ||
2596 | { | ||
2597 | const struct built_in_command *x; | ||
2598 | for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) { | ||
2599 | if (strcmp(name, x->cmd) != 0) | ||
2600 | continue; | ||
2601 | debug_printf_exec("found builtin '%s'\n", name); | ||
2602 | return x; | ||
2603 | } | ||
2604 | return NULL; | ||
2605 | } | ||
2606 | |||
2607 | # if ENABLE_HUSH_FUNCTIONS | ||
2608 | static const struct function *find_function(const char *name) | ||
2609 | { | ||
2610 | const struct function *funcp = G.top_func; | ||
2611 | while (funcp) { | ||
2612 | if (strcmp(name, funcp->name) != 0) | ||
2613 | continue; | ||
2614 | return funcp; | ||
2615 | debug_printf_exec("found function '%s'\n", name); | ||
2616 | } | ||
2617 | return NULL; | ||
2618 | } | ||
2619 | #endif | ||
2620 | |||
2621 | |||
2622 | static int run_list(struct pipe *pi); | ||
2623 | |||
2588 | #if BB_MMU | 2624 | #if BB_MMU |
2589 | #define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \ | 2625 | #define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \ |
2590 | pseudo_exec_argv(argv, assignment_cnt, argv_expanded) | 2626 | pseudo_exec_argv(argv, assignment_cnt, argv_expanded) |
@@ -2627,6 +2663,11 @@ static void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
2627 | #endif | 2663 | #endif |
2628 | } | 2664 | } |
2629 | 2665 | ||
2666 | #if ENABLE_FEATURE_SH_STANDALONE || BB_MMU | ||
2667 | if (strchr(argv[0], '/') != NULL) | ||
2668 | goto skip; | ||
2669 | #endif | ||
2670 | |||
2630 | /* On NOMMU, we must never block! | 2671 | /* On NOMMU, we must never block! |
2631 | * Example: { sleep 99999 | read line } & echo Ok | 2672 | * Example: { sleep 99999 | read line } & echo Ok |
2632 | * read builtin will block on read syscall, leaving parent blocked | 2673 | * read builtin will block on read syscall, leaving parent blocked |
@@ -2640,30 +2681,38 @@ static void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
2640 | */ | 2681 | */ |
2641 | { | 2682 | { |
2642 | int rcode; | 2683 | int rcode; |
2643 | const struct built_in_command *x; | 2684 | const struct built_in_command *x = find_builtin(argv[0]); |
2644 | for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) { | 2685 | if (x) { |
2645 | if (strcmp(argv[0], x->cmd) == 0) { | 2686 | rcode = x->function(argv); |
2646 | debug_printf_exec("running builtin '%s'\n", | 2687 | fflush(NULL); |
2647 | argv[0]); | 2688 | _exit(rcode); |
2648 | rcode = x->function(argv); | ||
2649 | fflush(NULL); | ||
2650 | _exit(rcode); | ||
2651 | } | ||
2652 | } | 2689 | } |
2653 | } | 2690 | } |
2691 | # if ENABLE_HUSH_FUNCTIONS | ||
2692 | /* Check if the command matches any functions */ | ||
2693 | { | ||
2694 | int rcode; | ||
2695 | const struct function *funcp = find_function(argv[0]); | ||
2696 | if (funcp) { | ||
2697 | rcode = run_list(funcp->body); | ||
2698 | fflush(NULL); | ||
2699 | _exit(rcode); | ||
2700 | } | ||
2701 | } | ||
2702 | # endif | ||
2654 | #endif | 2703 | #endif |
2655 | 2704 | ||
2656 | #if ENABLE_FEATURE_SH_STANDALONE | 2705 | #if ENABLE_FEATURE_SH_STANDALONE |
2657 | /* Check if the command matches any busybox applets */ | 2706 | /* Check if the command matches any busybox applets */ |
2658 | if (strchr(argv[0], '/') == NULL) { | 2707 | { |
2659 | int a = find_applet_by_name(argv[0]); | 2708 | int a = find_applet_by_name(argv[0]); |
2660 | if (a >= 0) { | 2709 | if (a >= 0) { |
2661 | #if BB_MMU /* see above why on NOMMU it is not allowed */ | 2710 | # if BB_MMU /* see above why on NOMMU it is not allowed */ |
2662 | if (APPLET_IS_NOEXEC(a)) { | 2711 | if (APPLET_IS_NOEXEC(a)) { |
2663 | debug_printf_exec("running applet '%s'\n", argv[0]); | 2712 | debug_printf_exec("running applet '%s'\n", argv[0]); |
2664 | run_applet_no_and_exit(a, argv); | 2713 | run_applet_no_and_exit(a, argv); |
2665 | } | 2714 | } |
2666 | #endif | 2715 | # endif |
2667 | /* Re-exec ourselves */ | 2716 | /* Re-exec ourselves */ |
2668 | debug_printf_exec("re-execing applet '%s'\n", argv[0]); | 2717 | debug_printf_exec("re-execing applet '%s'\n", argv[0]); |
2669 | sigprocmask(SIG_SETMASK, &G.inherited_set, NULL); | 2718 | sigprocmask(SIG_SETMASK, &G.inherited_set, NULL); |
@@ -2674,6 +2723,7 @@ static void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
2674 | } | 2723 | } |
2675 | #endif | 2724 | #endif |
2676 | 2725 | ||
2726 | skip: | ||
2677 | debug_printf_exec("execing '%s'\n", argv[0]); | 2727 | debug_printf_exec("execing '%s'\n", argv[0]); |
2678 | sigprocmask(SIG_SETMASK, &G.inherited_set, NULL); | 2728 | sigprocmask(SIG_SETMASK, &G.inherited_set, NULL); |
2679 | execvp(argv[0], argv); | 2729 | execvp(argv[0], argv); |
@@ -2681,8 +2731,6 @@ static void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
2681 | _exit(EXIT_FAILURE); | 2731 | _exit(EXIT_FAILURE); |
2682 | } | 2732 | } |
2683 | 2733 | ||
2684 | static int run_list(struct pipe *pi); | ||
2685 | |||
2686 | /* Called after [v]fork() in run_pipe | 2734 | /* Called after [v]fork() in run_pipe |
2687 | */ | 2735 | */ |
2688 | static void pseudo_exec(nommu_save_t *nommu_save, | 2736 | static void pseudo_exec(nommu_save_t *nommu_save, |
@@ -3031,8 +3079,35 @@ static int run_pipe(struct pipe *pi) | |||
3031 | if (command->group) { | 3079 | if (command->group) { |
3032 | #if ENABLE_HUSH_FUNCTIONS | 3080 | #if ENABLE_HUSH_FUNCTIONS |
3033 | if (command->grp_type == GRP_FUNCTION) { | 3081 | if (command->grp_type == GRP_FUNCTION) { |
3034 | /* func () { list } */ | 3082 | /* "executing" func () { list } */ |
3035 | bb_error_msg("here we ought to remember function definition, and go on"); | 3083 | struct function *funcp; |
3084 | struct function **funcpp = &G.top_func; | ||
3085 | |||
3086 | while ((funcp = *funcpp) != NULL) { | ||
3087 | if (strcmp(funcp->name, command->argv[0]) == 0) { | ||
3088 | debug_printf_exec("replacing function '%s'", funcp->name); | ||
3089 | free(funcp->name); | ||
3090 | free_pipe_list(funcp->body, /* indent: */ 0); | ||
3091 | goto skip; | ||
3092 | } | ||
3093 | funcpp = &funcp->next; | ||
3094 | } | ||
3095 | debug_printf_exec("remembering new function '%s'", funcp->name); | ||
3096 | funcp = *funcpp = xzalloc(sizeof(*funcp)); | ||
3097 | /*funcp->next = NULL;*/ | ||
3098 | skip: | ||
3099 | funcp->name = command->argv[0]; | ||
3100 | funcp->body = command->group; | ||
3101 | command->group = NULL; | ||
3102 | command->argv[0] = NULL; | ||
3103 | free_strings(command->argv); | ||
3104 | command->argv = NULL; | ||
3105 | /* note: if we are in a loop, future "executions" | ||
3106 | * of func def will see it as null command since | ||
3107 | * command->group == NULL and command->argv == NULL */ | ||
3108 | //this isn't exactly right: while...do f1() {a;}; f1; f1 {b;}; done | ||
3109 | //second loop will execute b! | ||
3110 | |||
3036 | return EXIT_SUCCESS; | 3111 | return EXIT_SUCCESS; |
3037 | } | 3112 | } |
3038 | #endif | 3113 | #endif |
@@ -3052,6 +3127,11 @@ static int run_pipe(struct pipe *pi) | |||
3052 | argv = command->argv ? command->argv : (char **) &null_ptr; | 3127 | argv = command->argv ? command->argv : (char **) &null_ptr; |
3053 | { | 3128 | { |
3054 | const struct built_in_command *x; | 3129 | const struct built_in_command *x; |
3130 | #if ENABLE_HUSH_FUNCTIONS | ||
3131 | const struct function *funcp; | ||
3132 | #else | ||
3133 | enum { funcp = 0 }; | ||
3134 | #endif | ||
3055 | char **new_env = NULL; | 3135 | char **new_env = NULL; |
3056 | char **old_env = NULL; | 3136 | char **old_env = NULL; |
3057 | 3137 | ||
@@ -3078,13 +3158,18 @@ static int run_pipe(struct pipe *pi) | |||
3078 | /* Expand the rest into (possibly) many strings each */ | 3158 | /* Expand the rest into (possibly) many strings each */ |
3079 | argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt); | 3159 | argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt); |
3080 | 3160 | ||
3081 | for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) { | 3161 | x = find_builtin(argv_expanded[0]); |
3082 | if (strcmp(argv_expanded[0], x->cmd) != 0) | 3162 | #if ENABLE_HUSH_FUNCTIONS |
3083 | continue; | 3163 | if (!x) |
3084 | if (x->function == builtin_exec && argv_expanded[1] == NULL) { | 3164 | funcp = find_function(argv_expanded[0]); |
3085 | debug_printf("exec with redirects only\n"); | 3165 | #endif |
3086 | rcode = setup_redirects(command, NULL); | 3166 | if (x || funcp) { |
3087 | goto clean_up_and_ret1; | 3167 | if (!funcp) { |
3168 | if (x->function == builtin_exec && argv_expanded[1] == NULL) { | ||
3169 | debug_printf("exec with redirects only\n"); | ||
3170 | rcode = setup_redirects(command, NULL); | ||
3171 | goto clean_up_and_ret1; | ||
3172 | } | ||
3088 | } | 3173 | } |
3089 | debug_printf("builtin inline %s\n", argv_expanded[0]); | 3174 | debug_printf("builtin inline %s\n", argv_expanded[0]); |
3090 | /* XXX setup_redirects acts on file descriptors, not FILEs. | 3175 | /* XXX setup_redirects acts on file descriptors, not FILEs. |
@@ -3095,9 +3180,18 @@ static int run_pipe(struct pipe *pi) | |||
3095 | if (rcode == 0) { | 3180 | if (rcode == 0) { |
3096 | new_env = expand_assignments(argv, command->assignment_cnt); | 3181 | new_env = expand_assignments(argv, command->assignment_cnt); |
3097 | old_env = putenv_all_and_save_old(new_env); | 3182 | old_env = putenv_all_and_save_old(new_env); |
3098 | debug_printf_exec(": builtin '%s' '%s'...\n", | 3183 | if (!funcp) { |
3184 | debug_printf_exec(": builtin '%s' '%s'...\n", | ||
3099 | x->cmd, argv_expanded[1]); | 3185 | x->cmd, argv_expanded[1]); |
3100 | rcode = x->function(argv_expanded) & 0xff; | 3186 | rcode = x->function(argv_expanded) & 0xff; |
3187 | } | ||
3188 | #if ENABLE_HUSH_FUNCTIONS | ||
3189 | else { | ||
3190 | debug_printf_exec(": function '%s' '%s'...\n", | ||
3191 | funcp->name, argv_expanded[1]); | ||
3192 | rcode = run_list(funcp->body) & 0xff; | ||
3193 | } | ||
3194 | #endif | ||
3101 | } | 3195 | } |
3102 | #if ENABLE_FEATURE_SH_STANDALONE | 3196 | #if ENABLE_FEATURE_SH_STANDALONE |
3103 | clean_up_and_ret: | 3197 | clean_up_and_ret: |
@@ -3114,6 +3208,7 @@ static int run_pipe(struct pipe *pi) | |||
3114 | debug_printf_exec("run_pipe return %d\n", rcode); | 3208 | debug_printf_exec("run_pipe return %d\n", rcode); |
3115 | return rcode; | 3209 | return rcode; |
3116 | } | 3210 | } |
3211 | |||
3117 | #if ENABLE_FEATURE_SH_STANDALONE | 3212 | #if ENABLE_FEATURE_SH_STANDALONE |
3118 | i = find_applet_by_name(argv_expanded[0]); | 3213 | i = find_applet_by_name(argv_expanded[0]); |
3119 | if (i >= 0 && APPLET_IS_NOFORK(i)) { | 3214 | if (i >= 0 && APPLET_IS_NOFORK(i)) { |
@@ -4081,6 +4176,10 @@ static int done_word(o_string *word, struct parse_context *ctx) | |||
4081 | } | 4176 | } |
4082 | } | 4177 | } |
4083 | command->argv = add_string_to_strings(command->argv, xstrdup(word->data)); | 4178 | command->argv = add_string_to_strings(command->argv, xstrdup(word->data)); |
4179 | //SEGV, but good idea. | ||
4180 | // command->argv = add_string_to_strings(command->argv, word->data); | ||
4181 | // word->data = NULL; | ||
4182 | // word->length = 0; | ||
4084 | debug_print_strings("word appended to argv", command->argv); | 4183 | debug_print_strings("word appended to argv", command->argv); |
4085 | } | 4184 | } |
4086 | 4185 | ||
@@ -4089,7 +4188,7 @@ static int done_word(o_string *word, struct parse_context *ctx) | |||
4089 | if (word->o_quoted | 4188 | if (word->o_quoted |
4090 | || !is_well_formed_var_name(command->argv[0], '\0') | 4189 | || !is_well_formed_var_name(command->argv[0], '\0') |
4091 | ) { | 4190 | ) { |
4092 | /* bash says "not a valid identifier" */ | 4191 | /* bash says just "not a valid identifier" */ |
4093 | syntax_error("not a valid identifier in for"); | 4192 | syntax_error("not a valid identifier in for"); |
4094 | return 1; | 4193 | return 1; |
4095 | } | 4194 | } |
@@ -4462,27 +4561,55 @@ static int parse_group(o_string *dest, struct parse_context *ctx, | |||
4462 | 4561 | ||
4463 | debug_printf_parse("parse_group entered\n"); | 4562 | debug_printf_parse("parse_group entered\n"); |
4464 | #if ENABLE_HUSH_FUNCTIONS | 4563 | #if ENABLE_HUSH_FUNCTIONS |
4465 | if (ch == 'F') { /* function definition? */ | 4564 | if (ch == '(') { |
4466 | bb_error_msg("aha '%s' is a function, parsing it...", dest->data); | 4565 | if (!dest->o_quoted) { |
4467 | //command->fname = dest->data; | 4566 | if (dest->length) |
4468 | command->grp_type = GRP_FUNCTION; | 4567 | done_word(dest, ctx); |
4469 | memset(dest, 0, sizeof(*dest)); | 4568 | if (!command->argv) |
4569 | goto skip; /* (... */ | ||
4570 | if (command->argv[1]) { /* word word ... (... */ | ||
4571 | syntax_error("unexpected character ("); | ||
4572 | return 1; | ||
4573 | } | ||
4574 | /* it is "word(..." or "word (..." */ | ||
4575 | do | ||
4576 | ch = i_getch(input); | ||
4577 | while (ch == ' ' || ch == '\t'); | ||
4578 | if (ch != ')') { | ||
4579 | syntax_error("unexpected character X"); | ||
4580 | return 1; | ||
4581 | } | ||
4582 | do | ||
4583 | ch = i_getch(input); | ||
4584 | while (ch == ' ' || ch == '\t' || ch == '\n'); | ||
4585 | if (ch != '{') { | ||
4586 | syntax_error("unexpected character X"); | ||
4587 | return 1; | ||
4588 | } | ||
4589 | command->grp_type = GRP_FUNCTION; | ||
4590 | goto skip; | ||
4591 | } | ||
4470 | } | 4592 | } |
4471 | #endif | 4593 | #endif |
4472 | if (command->argv /* word [word](... */ | 4594 | if (command->argv /* word [word]{... */ |
4473 | || dest->length /* word(... */ | 4595 | || dest->length /* word{... */ |
4474 | || dest->o_quoted /* ""(... */ | 4596 | || dest->o_quoted /* ""{... */ |
4475 | ) { | 4597 | ) { |
4476 | syntax_error(NULL); | 4598 | syntax_error(NULL); |
4477 | debug_printf_parse("parse_group return 1: " | 4599 | debug_printf_parse("parse_group return 1: " |
4478 | "syntax error, groups and arglists don't mix\n"); | 4600 | "syntax error, groups and arglists don't mix\n"); |
4479 | return 1; | 4601 | return 1; |
4480 | } | 4602 | } |
4603 | |||
4604 | #if ENABLE_HUSH_FUNCTIONS | ||
4605 | skip: | ||
4606 | #endif | ||
4481 | endch = '}'; | 4607 | endch = '}'; |
4482 | if (ch == '(') { | 4608 | if (ch == '(') { |
4483 | endch = ')'; | 4609 | endch = ')'; |
4484 | command->grp_type = GRP_SUBSHELL; | 4610 | command->grp_type = GRP_SUBSHELL; |
4485 | } | 4611 | } |
4612 | |||
4486 | { | 4613 | { |
4487 | #if !BB_MMU | 4614 | #if !BB_MMU |
4488 | char *as_string = NULL; | 4615 | char *as_string = NULL; |
@@ -5311,28 +5438,6 @@ static struct pipe *parse_stream(char **pstring, | |||
5311 | continue; | 5438 | continue; |
5312 | } | 5439 | } |
5313 | #endif | 5440 | #endif |
5314 | #if ENABLE_HUSH_FUNCTIONS | ||
5315 | if (dest.length != 0 /* not just () but word() */ | ||
5316 | && dest.o_quoted == 0 /* not a"b"c() */ | ||
5317 | && ctx.command->argv == NULL /* it's the first word */ | ||
5318 | //TODO: "func ( ) {...}" - note spaces - is valid format too in bash | ||
5319 | && i_peek(input) == ')' | ||
5320 | && !match_reserved_word(&dest) | ||
5321 | ) { | ||
5322 | bb_error_msg("seems like a function definition"); | ||
5323 | i_getch(input); | ||
5324 | //if !BB_MMU o_addchr(&ctx.as_string... | ||
5325 | do { | ||
5326 | //TODO: do it properly. | ||
5327 | ch = i_getch(input); | ||
5328 | } while (ch == ' ' || ch == '\n'); | ||
5329 | if (ch != '{') { | ||
5330 | syntax_error("was expecting {"); | ||
5331 | goto parse_error; | ||
5332 | } | ||
5333 | ch = 'F'; /* magic value */ | ||
5334 | } | ||
5335 | #endif | ||
5336 | case '{': | 5441 | case '{': |
5337 | if (parse_group(&dest, &ctx, input, ch) != 0) { | 5442 | if (parse_group(&dest, &ctx, input, ch) != 0) { |
5338 | goto parse_error; | 5443 | goto parse_error; |
@@ -6427,11 +6532,11 @@ static int builtin_unset(char **argv) | |||
6427 | ret = EXIT_FAILURE; | 6532 | ret = EXIT_FAILURE; |
6428 | } | 6533 | } |
6429 | } | 6534 | } |
6430 | #if ENABLE_HUSH_FUNCTIONS | 6535 | //#if ENABLE_HUSH_FUNCTIONS |
6431 | else { | 6536 | // else { |
6432 | unset_local_func(*argv); | 6537 | // unset_local_func(*argv); |
6433 | } | 6538 | // } |
6434 | #endif | 6539 | //#endif |
6435 | argv++; | 6540 | argv++; |
6436 | } | 6541 | } |
6437 | return ret; | 6542 | return ret; |