diff options
Diffstat (limited to 'shell/hush.c')
-rw-r--r-- | shell/hush.c | 385 |
1 files changed, 310 insertions, 75 deletions
diff --git a/shell/hush.c b/shell/hush.c index 708555ac4..8f1017e3c 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -47,17 +47,13 @@ | |||
47 | * follow IFS rules more precisely, including update semantics | 47 | * follow IFS rules more precisely, including update semantics |
48 | * tilde expansion | 48 | * tilde expansion |
49 | * aliases | 49 | * aliases |
50 | * builtins mandated by standards we don't support: | 50 | * "command" missing features: |
51 | * [un]alias, command, fc: | 51 | * command -p CMD: run CMD using default $PATH |
52 | * command -v CMD: print "/path/to/CMD" | 52 | * (can use this to override standalone shell as well?) |
53 | * prints "CMD" for builtins | ||
54 | * prints "alias ALIAS='EXPANSION'" for aliases | ||
55 | * prints nothing and sets $? to 1 if not found | ||
56 | * command -V CMD: print "CMD is /path/CMD|a shell builtin|etc" | ||
57 | * command [-p] CMD: run CMD, even if a function CMD also exists | ||
58 | * (can use this to override standalone shell as well) | ||
59 | * -p: use default $PATH | ||
60 | * command BLTIN: disables special-ness (e.g. errors do not abort) | 53 | * command BLTIN: disables special-ness (e.g. errors do not abort) |
54 | * command -V CMD1 CMD2 CMD3 (multiple args) (not in standard) | ||
55 | * builtins mandated by standards we don't support: | ||
56 | * [un]alias, fc: | ||
61 | * fc -l[nr] [BEG] [END]: list range of commands in history | 57 | * fc -l[nr] [BEG] [END]: list range of commands in history |
62 | * fc [-e EDITOR] [BEG] [END]: edit/rerun range of commands | 58 | * fc [-e EDITOR] [BEG] [END]: edit/rerun range of commands |
63 | * fc -s [PAT=REP] [CMD]: rerun CMD, replacing PAT with REP | 59 | * fc -s [PAT=REP] [CMD]: rerun CMD, replacing PAT with REP |
@@ -124,6 +120,18 @@ | |||
124 | //config: help | 120 | //config: help |
125 | //config: Enable {abc,def} extension. | 121 | //config: Enable {abc,def} extension. |
126 | //config: | 122 | //config: |
123 | //config:config HUSH_LINENO_VAR | ||
124 | //config: bool "$LINENO variable" | ||
125 | //config: default y | ||
126 | //config: depends on HUSH_BASH_COMPAT | ||
127 | //config: | ||
128 | //config:config HUSH_BASH_SOURCE_CURDIR | ||
129 | //config: bool "'source' and '.' builtins search current directory after $PATH" | ||
130 | //config: default n # do not encourage non-standard behavior | ||
131 | //config: depends on HUSH_BASH_COMPAT | ||
132 | //config: help | ||
133 | //config: This is not compliant with standards. Avoid if possible. | ||
134 | //config: | ||
127 | //config:config HUSH_INTERACTIVE | 135 | //config:config HUSH_INTERACTIVE |
128 | //config: bool "Interactive mode" | 136 | //config: bool "Interactive mode" |
129 | //config: default y | 137 | //config: default y |
@@ -253,6 +261,11 @@ | |||
253 | //config: default y | 261 | //config: default y |
254 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH | 262 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH |
255 | //config: | 263 | //config: |
264 | //config:config HUSH_COMMAND | ||
265 | //config: bool "command builtin" | ||
266 | //config: default y | ||
267 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH | ||
268 | //config: | ||
256 | //config:config HUSH_TRAP | 269 | //config:config HUSH_TRAP |
257 | //config: bool "trap builtin" | 270 | //config: bool "trap builtin" |
258 | //config: default y | 271 | //config: default y |
@@ -462,7 +475,10 @@ | |||
462 | # define MINUS_PLUS_EQUAL_QUESTION ("%#:-=+?" + 3) | 475 | # define MINUS_PLUS_EQUAL_QUESTION ("%#:-=+?" + 3) |
463 | #endif | 476 | #endif |
464 | 477 | ||
465 | #define SPECIAL_VAR_SYMBOL 3 | 478 | #define SPECIAL_VAR_SYMBOL_STR "\3" |
479 | #define SPECIAL_VAR_SYMBOL 3 | ||
480 | /* The "variable" with name "\1" emits string "\3". Testcase: "echo ^C" */ | ||
481 | #define SPECIAL_VAR_QUOTED_SVS 1 | ||
466 | 482 | ||
467 | struct variable; | 483 | struct variable; |
468 | 484 | ||
@@ -608,6 +624,9 @@ typedef enum redir_type { | |||
608 | struct command { | 624 | struct command { |
609 | pid_t pid; /* 0 if exited */ | 625 | pid_t pid; /* 0 if exited */ |
610 | int assignment_cnt; /* how many argv[i] are assignments? */ | 626 | int assignment_cnt; /* how many argv[i] are assignments? */ |
627 | #if ENABLE_HUSH_LINENO_VAR | ||
628 | unsigned lineno; | ||
629 | #endif | ||
611 | smallint cmd_type; /* CMD_xxx */ | 630 | smallint cmd_type; /* CMD_xxx */ |
612 | #define CMD_NORMAL 0 | 631 | #define CMD_NORMAL 0 |
613 | #define CMD_SUBSHELL 1 | 632 | #define CMD_SUBSHELL 1 |
@@ -927,6 +946,10 @@ struct globals { | |||
927 | unsigned handled_SIGCHLD; | 946 | unsigned handled_SIGCHLD; |
928 | smallint we_have_children; | 947 | smallint we_have_children; |
929 | #endif | 948 | #endif |
949 | #if ENABLE_HUSH_LINENO_VAR | ||
950 | unsigned lineno; | ||
951 | char *lineno_var; | ||
952 | #endif | ||
930 | struct FILE_list *FILE_list; | 953 | struct FILE_list *FILE_list; |
931 | /* Which signals have non-DFL handler (even with no traps set)? | 954 | /* Which signals have non-DFL handler (even with no traps set)? |
932 | * Set at the start to: | 955 | * Set at the start to: |
@@ -1924,7 +1947,7 @@ static void hush_exit(int exitcode) | |||
1924 | if (G.exiting <= 0 && G_traps && G_traps[0] && G_traps[0][0]) { | 1947 | if (G.exiting <= 0 && G_traps && G_traps[0] && G_traps[0][0]) { |
1925 | char *argv[3]; | 1948 | char *argv[3]; |
1926 | /* argv[0] is unused */ | 1949 | /* argv[0] is unused */ |
1927 | argv[1] = G_traps[0]; | 1950 | argv[1] = xstrdup(G_traps[0]); /* copy, since EXIT trap handler may modify G_traps[0] */ |
1928 | argv[2] = NULL; | 1951 | argv[2] = NULL; |
1929 | G.exiting = 1; /* prevent EXIT trap recursion */ | 1952 | G.exiting = 1; /* prevent EXIT trap recursion */ |
1930 | /* Note: G_traps[0] is not cleared! | 1953 | /* Note: G_traps[0] is not cleared! |
@@ -1985,10 +2008,12 @@ static int check_and_run_traps(void) | |||
1985 | smalluint save_rcode; | 2008 | smalluint save_rcode; |
1986 | char *argv[3]; | 2009 | char *argv[3]; |
1987 | /* argv[0] is unused */ | 2010 | /* argv[0] is unused */ |
1988 | argv[1] = G_traps[sig]; | 2011 | argv[1] = xstrdup(G_traps[sig]); |
2012 | /* why strdup? trap can modify itself: trap 'trap "echo oops" INT' INT */ | ||
1989 | argv[2] = NULL; | 2013 | argv[2] = NULL; |
1990 | save_rcode = G.last_exitcode; | 2014 | save_rcode = G.last_exitcode; |
1991 | builtin_eval(argv); | 2015 | builtin_eval(argv); |
2016 | free(argv[1]); | ||
1992 | //FIXME: shouldn't it be set to 128 + sig instead? | 2017 | //FIXME: shouldn't it be set to 128 + sig instead? |
1993 | G.last_exitcode = save_rcode; | 2018 | G.last_exitcode = save_rcode; |
1994 | last_sig = sig; | 2019 | last_sig = sig; |
@@ -2131,6 +2156,13 @@ static int set_local_var(char *str, unsigned flags) | |||
2131 | } | 2156 | } |
2132 | 2157 | ||
2133 | name_len = eq_sign - str + 1; /* including '=' */ | 2158 | name_len = eq_sign - str + 1; /* including '=' */ |
2159 | #if ENABLE_HUSH_LINENO_VAR | ||
2160 | if (G.lineno_var) { | ||
2161 | if (name_len == 7 && strncmp("LINENO", str, 6) == 0) | ||
2162 | G.lineno_var = NULL; | ||
2163 | } | ||
2164 | #endif | ||
2165 | |||
2134 | var_pp = &G.top_var; | 2166 | var_pp = &G.top_var; |
2135 | while ((cur = *var_pp) != NULL) { | 2167 | while ((cur = *var_pp) != NULL) { |
2136 | if (strncmp(cur->varstr, str, name_len) != 0) { | 2168 | if (strncmp(cur->varstr, str, name_len) != 0) { |
@@ -2252,10 +2284,16 @@ static int unset_local_var_len(const char *name, int name_len) | |||
2252 | 2284 | ||
2253 | if (!name) | 2285 | if (!name) |
2254 | return EXIT_SUCCESS; | 2286 | return EXIT_SUCCESS; |
2287 | |||
2255 | #if ENABLE_HUSH_GETOPTS | 2288 | #if ENABLE_HUSH_GETOPTS |
2256 | if (name_len == 6 && strncmp(name, "OPTIND", 6) == 0) | 2289 | if (name_len == 6 && strncmp(name, "OPTIND", 6) == 0) |
2257 | G.getopt_count = 0; | 2290 | G.getopt_count = 0; |
2258 | #endif | 2291 | #endif |
2292 | #if ENABLE_HUSH_LINENO_VAR | ||
2293 | if (name_len == 6 && G.lineno_var && strncmp(name, "LINENO", 6) == 0) | ||
2294 | G.lineno_var = NULL; | ||
2295 | #endif | ||
2296 | |||
2259 | var_pp = &G.top_var; | 2297 | var_pp = &G.top_var; |
2260 | while ((cur = *var_pp) != NULL) { | 2298 | while ((cur = *var_pp) != NULL) { |
2261 | if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') { | 2299 | if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') { |
@@ -2278,7 +2316,7 @@ static int unset_local_var_len(const char *name, int name_len) | |||
2278 | return EXIT_SUCCESS; | 2316 | return EXIT_SUCCESS; |
2279 | } | 2317 | } |
2280 | 2318 | ||
2281 | #if ENABLE_HUSH_UNSET | 2319 | #if ENABLE_HUSH_UNSET || ENABLE_HUSH_GETOPTS |
2282 | static int unset_local_var(const char *name) | 2320 | static int unset_local_var(const char *name) |
2283 | { | 2321 | { |
2284 | return unset_local_var_len(name, strlen(name)); | 2322 | return unset_local_var_len(name, strlen(name)); |
@@ -2300,7 +2338,7 @@ static void unset_vars(char **strings) | |||
2300 | free(strings); | 2338 | free(strings); |
2301 | } | 2339 | } |
2302 | 2340 | ||
2303 | #if BASH_HOSTNAME_VAR || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_READ | 2341 | #if BASH_HOSTNAME_VAR || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_READ || ENABLE_HUSH_GETOPTS |
2304 | static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) | 2342 | static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) |
2305 | { | 2343 | { |
2306 | char *var = xasprintf("%s=%s", name, val); | 2344 | char *var = xasprintf("%s=%s", name, val); |
@@ -2574,6 +2612,12 @@ static int i_getch(struct in_str *i) | |||
2574 | out: | 2612 | out: |
2575 | debug_printf("file_get: got '%c' %d\n", ch, ch); | 2613 | debug_printf("file_get: got '%c' %d\n", ch, ch); |
2576 | i->last_char = ch; | 2614 | i->last_char = ch; |
2615 | #if ENABLE_HUSH_LINENO_VAR | ||
2616 | if (ch == '\n') { | ||
2617 | G.lineno++; | ||
2618 | debug_printf_parse("G.lineno++ = %u\n", G.lineno); | ||
2619 | } | ||
2620 | #endif | ||
2577 | return ch; | 2621 | return ch; |
2578 | } | 2622 | } |
2579 | 2623 | ||
@@ -3374,8 +3418,13 @@ static void debug_print_tree(struct pipe *pi, int lvl) | |||
3374 | 3418 | ||
3375 | pin = 0; | 3419 | pin = 0; |
3376 | while (pi) { | 3420 | while (pi) { |
3377 | fdprintf(2, "%*spipe %d res_word=%s followup=%d %s\n", lvl*2, "", | 3421 | fdprintf(2, "%*spipe %d %sres_word=%s followup=%d %s\n", |
3378 | pin, RES[pi->res_word], pi->followup, PIPE[pi->followup]); | 3422 | lvl*2, "", |
3423 | pin, | ||
3424 | (IF_HAS_KEYWORDS(pi->pi_inverted ? "! " :) ""), | ||
3425 | RES[pi->res_word], | ||
3426 | pi->followup, PIPE[pi->followup] | ||
3427 | ); | ||
3379 | prn = 0; | 3428 | prn = 0; |
3380 | while (prn < pi->num_cmds) { | 3429 | while (prn < pi->num_cmds) { |
3381 | struct command *command = &pi->cmds[prn]; | 3430 | struct command *command = &pi->cmds[prn]; |
@@ -3384,6 +3433,9 @@ static void debug_print_tree(struct pipe *pi, int lvl) | |||
3384 | fdprintf(2, "%*s cmd %d assignment_cnt:%d", | 3433 | fdprintf(2, "%*s cmd %d assignment_cnt:%d", |
3385 | lvl*2, "", prn, | 3434 | lvl*2, "", prn, |
3386 | command->assignment_cnt); | 3435 | command->assignment_cnt); |
3436 | #if ENABLE_HUSH_LINENO_VAR | ||
3437 | fdprintf(2, " LINENO:%u", command->lineno); | ||
3438 | #endif | ||
3387 | if (command->group) { | 3439 | if (command->group) { |
3388 | fdprintf(2, " group %s: (argv=%p)%s%s\n", | 3440 | fdprintf(2, " group %s: (argv=%p)%s%s\n", |
3389 | CMDTYPE[command->cmd_type], | 3441 | CMDTYPE[command->cmd_type], |
@@ -3456,6 +3508,10 @@ static int done_command(struct parse_context *ctx) | |||
3456 | ctx->command = command = &pi->cmds[pi->num_cmds]; | 3508 | ctx->command = command = &pi->cmds[pi->num_cmds]; |
3457 | clear_and_ret: | 3509 | clear_and_ret: |
3458 | memset(command, 0, sizeof(*command)); | 3510 | memset(command, 0, sizeof(*command)); |
3511 | #if ENABLE_HUSH_LINENO_VAR | ||
3512 | command->lineno = G.lineno; | ||
3513 | debug_printf_parse("command->lineno = G.lineno (%u)\n", G.lineno); | ||
3514 | #endif | ||
3459 | return pi->num_cmds; /* used only for 0/nonzero check */ | 3515 | return pi->num_cmds; /* used only for 0/nonzero check */ |
3460 | } | 3516 | } |
3461 | 3517 | ||
@@ -3643,9 +3699,9 @@ static const struct reserved_combo* match_reserved_word(o_string *word) | |||
3643 | } | 3699 | } |
3644 | return NULL; | 3700 | return NULL; |
3645 | } | 3701 | } |
3646 | /* Return 0: not a keyword, 1: keyword | 3702 | /* Return NULL: not a keyword, else: keyword |
3647 | */ | 3703 | */ |
3648 | static int reserved_word(o_string *word, struct parse_context *ctx) | 3704 | static const struct reserved_combo* reserved_word(o_string *word, struct parse_context *ctx) |
3649 | { | 3705 | { |
3650 | # if ENABLE_HUSH_CASE | 3706 | # if ENABLE_HUSH_CASE |
3651 | static const struct reserved_combo reserved_match = { | 3707 | static const struct reserved_combo reserved_match = { |
@@ -3658,7 +3714,7 @@ static int reserved_word(o_string *word, struct parse_context *ctx) | |||
3658 | return 0; | 3714 | return 0; |
3659 | r = match_reserved_word(word); | 3715 | r = match_reserved_word(word); |
3660 | if (!r) | 3716 | if (!r) |
3661 | return 0; | 3717 | return r; /* NULL */ |
3662 | 3718 | ||
3663 | debug_printf("found reserved word %s, res %d\n", r->literal, r->res); | 3719 | debug_printf("found reserved word %s, res %d\n", r->literal, r->res); |
3664 | # if ENABLE_HUSH_CASE | 3720 | # if ENABLE_HUSH_CASE |
@@ -3673,7 +3729,7 @@ static int reserved_word(o_string *word, struct parse_context *ctx) | |||
3673 | ctx->ctx_res_w = RES_SNTX; | 3729 | ctx->ctx_res_w = RES_SNTX; |
3674 | } | 3730 | } |
3675 | ctx->ctx_inverted = 1; | 3731 | ctx->ctx_inverted = 1; |
3676 | return 1; | 3732 | return r; |
3677 | } | 3733 | } |
3678 | if (r->flag & FLAG_START) { | 3734 | if (r->flag & FLAG_START) { |
3679 | struct parse_context *old; | 3735 | struct parse_context *old; |
@@ -3685,7 +3741,7 @@ static int reserved_word(o_string *word, struct parse_context *ctx) | |||
3685 | } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) { | 3741 | } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) { |
3686 | syntax_error_at(word->data); | 3742 | syntax_error_at(word->data); |
3687 | ctx->ctx_res_w = RES_SNTX; | 3743 | ctx->ctx_res_w = RES_SNTX; |
3688 | return 1; | 3744 | return r; |
3689 | } else { | 3745 | } else { |
3690 | /* "{...} fi" is ok. "{...} if" is not | 3746 | /* "{...} fi" is ok. "{...} if" is not |
3691 | * Example: | 3747 | * Example: |
@@ -3735,7 +3791,7 @@ static int reserved_word(o_string *word, struct parse_context *ctx) | |||
3735 | *ctx = *old; /* physical copy */ | 3791 | *ctx = *old; /* physical copy */ |
3736 | free(old); | 3792 | free(old); |
3737 | } | 3793 | } |
3738 | return 1; | 3794 | return r; |
3739 | } | 3795 | } |
3740 | #endif /* HAS_KEYWORDS */ | 3796 | #endif /* HAS_KEYWORDS */ |
3741 | 3797 | ||
@@ -3801,9 +3857,26 @@ static int done_word(o_string *word, struct parse_context *ctx) | |||
3801 | && ctx->ctx_res_w != RES_CASE | 3857 | && ctx->ctx_res_w != RES_CASE |
3802 | # endif | 3858 | # endif |
3803 | ) { | 3859 | ) { |
3804 | int reserved = reserved_word(word, ctx); | 3860 | const struct reserved_combo *reserved; |
3805 | debug_printf_parse("checking for reserved-ness: %d\n", reserved); | 3861 | reserved = reserved_word(word, ctx); |
3862 | debug_printf_parse("checking for reserved-ness: %d\n", !!reserved); | ||
3806 | if (reserved) { | 3863 | if (reserved) { |
3864 | # if ENABLE_HUSH_LINENO_VAR | ||
3865 | /* Case: | ||
3866 | * "while ...; do | ||
3867 | * cmd ..." | ||
3868 | * If we don't close the pipe _now_, immediately after "do", lineno logic | ||
3869 | * sees "cmd" as starting at "do" - i.e., at the previous line. | ||
3870 | */ | ||
3871 | if (0 | ||
3872 | IF_HUSH_IF(|| reserved->res == RES_THEN) | ||
3873 | IF_HUSH_IF(|| reserved->res == RES_ELIF) | ||
3874 | IF_HUSH_IF(|| reserved->res == RES_ELSE) | ||
3875 | IF_HUSH_LOOPS(|| reserved->res == RES_DO) | ||
3876 | ) { | ||
3877 | done_pipe(ctx, PIPE_SEQ); | ||
3878 | } | ||
3879 | # endif | ||
3807 | o_reset_to_empty_unquoted(word); | 3880 | o_reset_to_empty_unquoted(word); |
3808 | debug_printf_parse("done_word return %d\n", | 3881 | debug_printf_parse("done_word return %d\n", |
3809 | (ctx->ctx_res_w == RES_SNTX)); | 3882 | (ctx->ctx_res_w == RES_SNTX)); |
@@ -3840,21 +3913,6 @@ static int done_word(o_string *word, struct parse_context *ctx) | |||
3840 | word->o_assignment = MAYBE_ASSIGNMENT; | 3913 | word->o_assignment = MAYBE_ASSIGNMENT; |
3841 | } | 3914 | } |
3842 | debug_printf_parse("word->o_assignment='%s'\n", assignment_flag[word->o_assignment]); | 3915 | debug_printf_parse("word->o_assignment='%s'\n", assignment_flag[word->o_assignment]); |
3843 | |||
3844 | if (word->has_quoted_part | ||
3845 | /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */ | ||
3846 | && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL) | ||
3847 | /* (otherwise it's known to be not empty and is already safe) */ | ||
3848 | ) { | ||
3849 | /* exclude "$@" - it can expand to no word despite "" */ | ||
3850 | char *p = word->data; | ||
3851 | while (p[0] == SPECIAL_VAR_SYMBOL | ||
3852 | && (p[1] & 0x7f) == '@' | ||
3853 | && p[2] == SPECIAL_VAR_SYMBOL | ||
3854 | ) { | ||
3855 | p += 3; | ||
3856 | } | ||
3857 | } | ||
3858 | command->argv = add_string_to_strings(command->argv, xstrdup(word->data)); | 3916 | command->argv = add_string_to_strings(command->argv, xstrdup(word->data)); |
3859 | debug_print_strings("word appended to argv", command->argv); | 3917 | debug_print_strings("word appended to argv", command->argv); |
3860 | } | 3918 | } |
@@ -4503,9 +4561,10 @@ static int parse_dollar(o_string *as_string, | |||
4503 | 4561 | ||
4504 | debug_printf_parse("parse_dollar entered: ch='%c'\n", ch); | 4562 | debug_printf_parse("parse_dollar entered: ch='%c'\n", ch); |
4505 | if (isalpha(ch)) { | 4563 | if (isalpha(ch)) { |
4564 | make_var: | ||
4506 | ch = i_getch(input); | 4565 | ch = i_getch(input); |
4507 | nommu_addchr(as_string, ch); | 4566 | nommu_addchr(as_string, ch); |
4508 | make_var: | 4567 | /*make_var1:*/ |
4509 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | 4568 | o_addchr(dest, SPECIAL_VAR_SYMBOL); |
4510 | while (1) { | 4569 | while (1) { |
4511 | debug_printf_parse(": '%c'\n", ch); | 4570 | debug_printf_parse(": '%c'\n", ch); |
@@ -4698,19 +4757,22 @@ static int parse_dollar(o_string *as_string, | |||
4698 | } | 4757 | } |
4699 | #endif | 4758 | #endif |
4700 | case '_': | 4759 | case '_': |
4760 | goto make_var; | ||
4761 | #if 0 | ||
4762 | /* TODO: $_ and $-: */ | ||
4763 | /* $_ Shell or shell script name; or last argument of last command | ||
4764 | * (if last command wasn't a pipe; if it was, bash sets $_ to ""); | ||
4765 | * but in command's env, set to full pathname used to invoke it */ | ||
4766 | /* $- Option flags set by set builtin or shell options (-i etc) */ | ||
4701 | ch = i_getch(input); | 4767 | ch = i_getch(input); |
4702 | nommu_addchr(as_string, ch); | 4768 | nommu_addchr(as_string, ch); |
4703 | ch = i_peek_and_eat_bkslash_nl(input); | 4769 | ch = i_peek_and_eat_bkslash_nl(input); |
4704 | if (isalnum(ch)) { /* it's $_name or $_123 */ | 4770 | if (isalnum(ch)) { /* it's $_name or $_123 */ |
4705 | ch = '_'; | 4771 | ch = '_'; |
4706 | goto make_var; | 4772 | goto make_var1; |
4707 | } | 4773 | } |
4708 | /* else: it's $_ */ | 4774 | /* else: it's $_ */ |
4709 | /* TODO: $_ and $-: */ | 4775 | #endif |
4710 | /* $_ Shell or shell script name; or last argument of last command | ||
4711 | * (if last command wasn't a pipe; if it was, bash sets $_ to ""); | ||
4712 | * but in command's env, set to full pathname used to invoke it */ | ||
4713 | /* $- Option flags set by set builtin or shell options (-i etc) */ | ||
4714 | default: | 4776 | default: |
4715 | o_addQchr(dest, '$'); | 4777 | o_addQchr(dest, '$'); |
4716 | } | 4778 | } |
@@ -4914,7 +4976,8 @@ static struct pipe *parse_stream(char **pstring, | |||
4914 | next = i_peek(input); | 4976 | next = i_peek(input); |
4915 | 4977 | ||
4916 | is_special = "{}<>;&|()#'" /* special outside of "str" */ | 4978 | is_special = "{}<>;&|()#'" /* special outside of "str" */ |
4917 | "\\$\"" IF_HUSH_TICK("`"); /* always special */ | 4979 | "\\$\"" IF_HUSH_TICK("`") /* always special */ |
4980 | SPECIAL_VAR_SYMBOL_STR; | ||
4918 | /* Are { and } special here? */ | 4981 | /* Are { and } special here? */ |
4919 | if (ctx.command->argv /* word [word]{... - non-special */ | 4982 | if (ctx.command->argv /* word [word]{... - non-special */ |
4920 | || dest.length /* word{... - non-special */ | 4983 | || dest.length /* word{... - non-special */ |
@@ -4948,6 +5011,22 @@ static struct pipe *parse_stream(char **pstring, | |||
4948 | } | 5011 | } |
4949 | 5012 | ||
4950 | if (is_blank) { | 5013 | if (is_blank) { |
5014 | #if ENABLE_HUSH_LINENO_VAR | ||
5015 | /* Case: | ||
5016 | * "while ...; do<whitespace><newline> | ||
5017 | * cmd ..." | ||
5018 | * would think that "cmd" starts in <whitespace> - | ||
5019 | * i.e., at the previous line. | ||
5020 | * We need to skip all whitespace before newlines. | ||
5021 | */ | ||
5022 | while (ch != '\n') { | ||
5023 | next = i_peek(input); | ||
5024 | if (next != ' ' && next != '\t' && next != '\n') | ||
5025 | break; /* next char is not ws */ | ||
5026 | ch = i_getch(input); | ||
5027 | } | ||
5028 | /* ch == last eaten whitespace char */ | ||
5029 | #endif | ||
4951 | if (done_word(&dest, &ctx)) { | 5030 | if (done_word(&dest, &ctx)) { |
4952 | goto parse_error; | 5031 | goto parse_error; |
4953 | } | 5032 | } |
@@ -5186,8 +5265,14 @@ static struct pipe *parse_stream(char **pstring, | |||
5186 | /* Note: nommu_addchr(&ctx.as_string, ch) is already done */ | 5265 | /* Note: nommu_addchr(&ctx.as_string, ch) is already done */ |
5187 | 5266 | ||
5188 | switch (ch) { | 5267 | switch (ch) { |
5189 | case '#': /* non-comment #: "echo a#b" etc */ | 5268 | case SPECIAL_VAR_SYMBOL: |
5190 | o_addQchr(&dest, ch); | 5269 | /* Convert raw ^C to corresponding special variable reference */ |
5270 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | ||
5271 | o_addchr(&dest, SPECIAL_VAR_QUOTED_SVS); | ||
5272 | /* fall through */ | ||
5273 | case '#': | ||
5274 | /* non-comment #: "echo a#b" etc */ | ||
5275 | o_addchr(&dest, ch); | ||
5191 | break; | 5276 | break; |
5192 | case '\\': | 5277 | case '\\': |
5193 | if (next == EOF) { | 5278 | if (next == EOF) { |
@@ -5229,6 +5314,11 @@ static struct pipe *parse_stream(char **pstring, | |||
5229 | nommu_addchr(&ctx.as_string, ch); | 5314 | nommu_addchr(&ctx.as_string, ch); |
5230 | if (ch == '\'') | 5315 | if (ch == '\'') |
5231 | break; | 5316 | break; |
5317 | if (ch == SPECIAL_VAR_SYMBOL) { | ||
5318 | /* Convert raw ^C to corresponding special variable reference */ | ||
5319 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | ||
5320 | o_addchr(&dest, SPECIAL_VAR_QUOTED_SVS); | ||
5321 | } | ||
5232 | o_addqchr(&dest, ch); | 5322 | o_addqchr(&dest, ch); |
5233 | } | 5323 | } |
5234 | } | 5324 | } |
@@ -5534,7 +5624,7 @@ static int expand_on_ifs(int *ended_with_ifs, o_string *output, int n, const cha | |||
5534 | static char *encode_then_expand_string(const char *str, int process_bkslash, int do_unbackslash) | 5624 | static char *encode_then_expand_string(const char *str, int process_bkslash, int do_unbackslash) |
5535 | { | 5625 | { |
5536 | #if !BASH_PATTERN_SUBST | 5626 | #if !BASH_PATTERN_SUBST |
5537 | const int do_unbackslash = 1; | 5627 | enum { do_unbackslash = 1 }; |
5538 | #endif | 5628 | #endif |
5539 | char *exp_str; | 5629 | char *exp_str; |
5540 | struct in_str input; | 5630 | struct in_str input; |
@@ -5610,6 +5700,10 @@ static char *replace_pattern(char *val, const char *pattern, const char *repl, c | |||
5610 | unsigned res_len = 0; | 5700 | unsigned res_len = 0; |
5611 | unsigned repl_len = strlen(repl); | 5701 | unsigned repl_len = strlen(repl); |
5612 | 5702 | ||
5703 | /* Null pattern never matches, including if "var" is empty */ | ||
5704 | if (!pattern[0]) | ||
5705 | return result; /* NULL, no replaces happened */ | ||
5706 | |||
5613 | while (1) { | 5707 | while (1) { |
5614 | int size; | 5708 | int size; |
5615 | char *s = strstr_pattern(val, pattern, &size); | 5709 | char *s = strstr_pattern(val, pattern, &size); |
@@ -5640,9 +5734,9 @@ static char *replace_pattern(char *val, const char *pattern, const char *repl, c | |||
5640 | */ | 5734 | */ |
5641 | static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, char **pp) | 5735 | static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, char **pp) |
5642 | { | 5736 | { |
5643 | const char *val = NULL; | 5737 | const char *val; |
5644 | char *to_be_freed = NULL; | 5738 | char *to_be_freed; |
5645 | char *p = *pp; | 5739 | char *p; |
5646 | char *var; | 5740 | char *var; |
5647 | char first_char; | 5741 | char first_char; |
5648 | char exp_op; | 5742 | char exp_op; |
@@ -5651,6 +5745,9 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5651 | char *exp_word = exp_word; /* for compiler */ | 5745 | char *exp_word = exp_word; /* for compiler */ |
5652 | char arg0; | 5746 | char arg0; |
5653 | 5747 | ||
5748 | val = NULL; | ||
5749 | to_be_freed = NULL; | ||
5750 | p = *pp; | ||
5654 | *p = '\0'; /* replace trailing SPECIAL_VAR_SYMBOL */ | 5751 | *p = '\0'; /* replace trailing SPECIAL_VAR_SYMBOL */ |
5655 | var = arg; | 5752 | var = arg; |
5656 | exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL; | 5753 | exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL; |
@@ -5773,8 +5870,6 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5773 | * and if // is used, it is encoded as \: | 5870 | * and if // is used, it is encoded as \: |
5774 | * var\pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL> | 5871 | * var\pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL> |
5775 | */ | 5872 | */ |
5776 | /* Empty variable always gives nothing: */ | ||
5777 | // "v=''; echo ${v/*/w}" prints "", not "w" | ||
5778 | if (val && val[0]) { | 5873 | if (val && val[0]) { |
5779 | /* pattern uses non-standard expansion. | 5874 | /* pattern uses non-standard expansion. |
5780 | * repl should be unbackslashed and globbed | 5875 | * repl should be unbackslashed and globbed |
@@ -5810,6 +5905,13 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5810 | val = to_be_freed; | 5905 | val = to_be_freed; |
5811 | free(pattern); | 5906 | free(pattern); |
5812 | free(repl); | 5907 | free(repl); |
5908 | } else { | ||
5909 | /* Empty variable always gives nothing */ | ||
5910 | // "v=''; echo ${v/*/w}" prints "", not "w" | ||
5911 | /* Just skip "replace" part */ | ||
5912 | *p++ = SPECIAL_VAR_SYMBOL; | ||
5913 | p = strchr(p, SPECIAL_VAR_SYMBOL); | ||
5914 | *p = '\0'; | ||
5813 | } | 5915 | } |
5814 | } | 5916 | } |
5815 | #endif /* BASH_PATTERN_SUBST */ | 5917 | #endif /* BASH_PATTERN_SUBST */ |
@@ -6041,6 +6143,11 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) | |||
6041 | arg++; | 6143 | arg++; |
6042 | cant_be_null = 0x80; | 6144 | cant_be_null = 0x80; |
6043 | break; | 6145 | break; |
6146 | case SPECIAL_VAR_QUOTED_SVS: | ||
6147 | /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_QUOTED_SVS><SPECIAL_VAR_SYMBOL> */ | ||
6148 | arg++; | ||
6149 | val = SPECIAL_VAR_SYMBOL_STR; | ||
6150 | break; | ||
6044 | #if ENABLE_HUSH_TICK | 6151 | #if ENABLE_HUSH_TICK |
6045 | case '`': /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */ | 6152 | case '`': /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */ |
6046 | *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */ | 6153 | *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */ |
@@ -6199,7 +6306,7 @@ static char *expand_string_to_string(const char *str, int do_unbackslash) | |||
6199 | return (char*)list; | 6306 | return (char*)list; |
6200 | } | 6307 | } |
6201 | 6308 | ||
6202 | /* Used for "eval" builtin and case string */ | 6309 | #if ENABLE_HUSH_CASE |
6203 | static char* expand_strvec_to_string(char **argv) | 6310 | static char* expand_strvec_to_string(char **argv) |
6204 | { | 6311 | { |
6205 | char **list; | 6312 | char **list; |
@@ -6221,6 +6328,7 @@ static char* expand_strvec_to_string(char **argv) | |||
6221 | debug_printf_expand("strvec_to_string='%s'\n", (char*)list); | 6328 | debug_printf_expand("strvec_to_string='%s'\n", (char*)list); |
6222 | return (char*)list; | 6329 | return (char*)list; |
6223 | } | 6330 | } |
6331 | #endif | ||
6224 | 6332 | ||
6225 | static char **expand_assignments(char **argv, int count) | 6333 | static char **expand_assignments(char **argv, int count) |
6226 | { | 6334 | { |
@@ -6513,8 +6621,17 @@ static void parse_and_run_string(const char *s) | |||
6513 | static void parse_and_run_file(FILE *f) | 6621 | static void parse_and_run_file(FILE *f) |
6514 | { | 6622 | { |
6515 | struct in_str input; | 6623 | struct in_str input; |
6624 | #if ENABLE_HUSH_LINENO_VAR | ||
6625 | unsigned sv; | ||
6626 | |||
6627 | sv = G.lineno; | ||
6628 | G.lineno = 1; | ||
6629 | #endif | ||
6516 | setup_file_in_str(&input, f); | 6630 | setup_file_in_str(&input, f); |
6517 | parse_and_run_stream(&input, ';'); | 6631 | parse_and_run_stream(&input, ';'); |
6632 | #if ENABLE_HUSH_LINENO_VAR | ||
6633 | G.lineno = sv; | ||
6634 | #endif | ||
6518 | } | 6635 | } |
6519 | 6636 | ||
6520 | #if ENABLE_HUSH_TICK | 6637 | #if ENABLE_HUSH_TICK |
@@ -7330,6 +7447,32 @@ static void dump_cmd_in_x_mode(char **argv) | |||
7330 | # define dump_cmd_in_x_mode(argv) ((void)0) | 7447 | # define dump_cmd_in_x_mode(argv) ((void)0) |
7331 | #endif | 7448 | #endif |
7332 | 7449 | ||
7450 | #if ENABLE_HUSH_COMMAND | ||
7451 | static void if_command_vV_print_and_exit(char opt_vV, char *cmd, const char *explanation) | ||
7452 | { | ||
7453 | char *to_free; | ||
7454 | |||
7455 | if (!opt_vV) | ||
7456 | return; | ||
7457 | |||
7458 | to_free = NULL; | ||
7459 | if (!explanation) { | ||
7460 | char *path = getenv("PATH"); | ||
7461 | explanation = to_free = find_executable(cmd, &path); /* path == NULL is ok */ | ||
7462 | if (!explanation) | ||
7463 | _exit(1); /* PROG was not found */ | ||
7464 | if (opt_vV != 'V') | ||
7465 | cmd = to_free; /* -v PROG prints "/path/to/PROG" */ | ||
7466 | } | ||
7467 | printf((opt_vV == 'V') ? "%s is %s\n" : "%s\n", cmd, explanation); | ||
7468 | free(to_free); | ||
7469 | fflush_all(); | ||
7470 | _exit(0); | ||
7471 | } | ||
7472 | #else | ||
7473 | # define if_command_vV_print_and_exit(a,b,c) ((void)0) | ||
7474 | #endif | ||
7475 | |||
7333 | #if BB_MMU | 7476 | #if BB_MMU |
7334 | #define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \ | 7477 | #define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \ |
7335 | pseudo_exec_argv(argv, assignment_cnt, argv_expanded) | 7478 | pseudo_exec_argv(argv, assignment_cnt, argv_expanded) |
@@ -7350,7 +7493,11 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
7350 | char **argv, int assignment_cnt, | 7493 | char **argv, int assignment_cnt, |
7351 | char **argv_expanded) | 7494 | char **argv_expanded) |
7352 | { | 7495 | { |
7496 | const struct built_in_command *x; | ||
7353 | char **new_env; | 7497 | char **new_env; |
7498 | #if ENABLE_HUSH_COMMAND | ||
7499 | char opt_vV = 0; | ||
7500 | #endif | ||
7354 | 7501 | ||
7355 | new_env = expand_assignments(argv, assignment_cnt); | 7502 | new_env = expand_assignments(argv, assignment_cnt); |
7356 | dump_cmd_in_x_mode(new_env); | 7503 | dump_cmd_in_x_mode(new_env); |
@@ -7399,21 +7546,58 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
7399 | } | 7546 | } |
7400 | #endif | 7547 | #endif |
7401 | 7548 | ||
7549 | #if ENABLE_HUSH_COMMAND | ||
7550 | /* "command BAR": run BAR without looking it up among functions | ||
7551 | * "command -v BAR": print "BAR" or "/path/to/BAR"; or exit 1 | ||
7552 | * "command -V BAR": print "BAR is {a function,a shell builtin,/path/to/BAR}" | ||
7553 | */ | ||
7554 | while (strcmp(argv[0], "command") == 0 && argv[1]) { | ||
7555 | char *p; | ||
7556 | |||
7557 | argv++; | ||
7558 | p = *argv; | ||
7559 | if (p[0] != '-' || !p[1]) | ||
7560 | continue; /* bash allows "command command command [-OPT] BAR" */ | ||
7561 | |||
7562 | for (;;) { | ||
7563 | p++; | ||
7564 | switch (*p) { | ||
7565 | case '\0': | ||
7566 | argv++; | ||
7567 | p = *argv; | ||
7568 | if (p[0] != '-' || !p[1]) | ||
7569 | goto after_opts; | ||
7570 | continue; /* next arg is also -opts, process it too */ | ||
7571 | case 'v': | ||
7572 | case 'V': | ||
7573 | opt_vV = *p; | ||
7574 | continue; | ||
7575 | default: | ||
7576 | bb_error_msg_and_die("%s: %s: invalid option", "command", argv[0]); | ||
7577 | } | ||
7578 | } | ||
7579 | } | ||
7580 | after_opts: | ||
7581 | # if ENABLE_HUSH_FUNCTIONS | ||
7582 | if (opt_vV && find_function(argv[0])) | ||
7583 | if_command_vV_print_and_exit(opt_vV, argv[0], "a function"); | ||
7584 | # endif | ||
7585 | #endif | ||
7586 | |||
7402 | /* Check if the command matches any of the builtins. | 7587 | /* Check if the command matches any of the builtins. |
7403 | * Depending on context, this might be redundant. But it's | 7588 | * Depending on context, this might be redundant. But it's |
7404 | * easier to waste a few CPU cycles than it is to figure out | 7589 | * easier to waste a few CPU cycles than it is to figure out |
7405 | * if this is one of those cases. | 7590 | * if this is one of those cases. |
7406 | */ | 7591 | */ |
7407 | { | 7592 | /* Why "BB_MMU ? :" difference in logic? - |
7408 | /* On NOMMU, it is more expensive to re-execute shell | 7593 | * On NOMMU, it is more expensive to re-execute shell |
7409 | * just in order to run echo or test builtin. | 7594 | * just in order to run echo or test builtin. |
7410 | * It's better to skip it here and run corresponding | 7595 | * It's better to skip it here and run corresponding |
7411 | * non-builtin later. */ | 7596 | * non-builtin later. */ |
7412 | const struct built_in_command *x; | 7597 | x = BB_MMU ? find_builtin(argv[0]) : find_builtin1(argv[0]); |
7413 | x = BB_MMU ? find_builtin(argv[0]) : find_builtin1(argv[0]); | 7598 | if (x) { |
7414 | if (x) { | 7599 | if_command_vV_print_and_exit(opt_vV, argv[0], "a shell builtin"); |
7415 | exec_builtin(&nommu_save->argv_from_re_execing, x, argv); | 7600 | exec_builtin(&nommu_save->argv_from_re_execing, x, argv); |
7416 | } | ||
7417 | } | 7601 | } |
7418 | 7602 | ||
7419 | #if ENABLE_FEATURE_SH_STANDALONE | 7603 | #if ENABLE_FEATURE_SH_STANDALONE |
@@ -7421,6 +7605,7 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
7421 | { | 7605 | { |
7422 | int a = find_applet_by_name(argv[0]); | 7606 | int a = find_applet_by_name(argv[0]); |
7423 | if (a >= 0) { | 7607 | if (a >= 0) { |
7608 | if_command_vV_print_and_exit(opt_vV, argv[0], "an applet"); | ||
7424 | # if BB_MMU /* see above why on NOMMU it is not allowed */ | 7609 | # if BB_MMU /* see above why on NOMMU it is not allowed */ |
7425 | if (APPLET_IS_NOEXEC(a)) { | 7610 | if (APPLET_IS_NOEXEC(a)) { |
7426 | /* Do not leak open fds from opened script files etc. | 7611 | /* Do not leak open fds from opened script files etc. |
@@ -7450,6 +7635,7 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
7450 | #if ENABLE_FEATURE_SH_STANDALONE || BB_MMU | 7635 | #if ENABLE_FEATURE_SH_STANDALONE || BB_MMU |
7451 | skip: | 7636 | skip: |
7452 | #endif | 7637 | #endif |
7638 | if_command_vV_print_and_exit(opt_vV, argv[0], NULL); | ||
7453 | execvp_or_die(argv); | 7639 | execvp_or_die(argv); |
7454 | } | 7640 | } |
7455 | 7641 | ||
@@ -7992,6 +8178,11 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
7992 | char **new_env = NULL; | 8178 | char **new_env = NULL; |
7993 | struct variable *old_vars = NULL; | 8179 | struct variable *old_vars = NULL; |
7994 | 8180 | ||
8181 | #if ENABLE_HUSH_LINENO_VAR | ||
8182 | if (G.lineno_var) | ||
8183 | strcpy(G.lineno_var + sizeof("LINENO=")-1, utoa(command->lineno)); | ||
8184 | #endif | ||
8185 | |||
7995 | if (argv[command->assignment_cnt] == NULL) { | 8186 | if (argv[command->assignment_cnt] == NULL) { |
7996 | /* Assignments, but no command */ | 8187 | /* Assignments, but no command */ |
7997 | /* Ensure redirects take effect (that is, create files). | 8188 | /* Ensure redirects take effect (that is, create files). |
@@ -8139,7 +8330,7 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
8139 | return rcode; | 8330 | return rcode; |
8140 | } | 8331 | } |
8141 | 8332 | ||
8142 | if (ENABLE_FEATURE_SH_NOFORK) { | 8333 | if (ENABLE_FEATURE_SH_NOFORK && NUM_APPLETS > 1) { |
8143 | int n = find_applet_by_name(argv_expanded[0]); | 8334 | int n = find_applet_by_name(argv_expanded[0]); |
8144 | if (n >= 0 && APPLET_IS_NOFORK(n)) { | 8335 | if (n >= 0 && APPLET_IS_NOFORK(n)) { |
8145 | rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, &squirrel, argv_expanded); | 8336 | rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, &squirrel, argv_expanded); |
@@ -8196,6 +8387,11 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
8196 | if (cmd_no < pi->num_cmds) | 8387 | if (cmd_no < pi->num_cmds) |
8197 | xpiped_pair(pipefds); | 8388 | xpiped_pair(pipefds); |
8198 | 8389 | ||
8390 | #if ENABLE_HUSH_LINENO_VAR | ||
8391 | if (G.lineno_var) | ||
8392 | strcpy(G.lineno_var + sizeof("LINENO=")-1, utoa(command->lineno)); | ||
8393 | #endif | ||
8394 | |||
8199 | command->pid = BB_MMU ? fork() : vfork(); | 8395 | command->pid = BB_MMU ? fork() : vfork(); |
8200 | if (!command->pid) { /* child */ | 8396 | if (!command->pid) { /* child */ |
8201 | #if ENABLE_HUSH_JOB | 8397 | #if ENABLE_HUSH_JOB |
@@ -8387,7 +8583,10 @@ static int run_list(struct pipe *pi) | |||
8387 | rword, cond_code, last_rword); | 8583 | rword, cond_code, last_rword); |
8388 | 8584 | ||
8389 | sv_errexit_depth = G.errexit_depth; | 8585 | sv_errexit_depth = G.errexit_depth; |
8390 | if (IF_HAS_KEYWORDS(rword == RES_IF || rword == RES_ELIF ||) | 8586 | if ( |
8587 | #if ENABLE_HUSH_IF | ||
8588 | rword == RES_IF || rword == RES_ELIF || | ||
8589 | #endif | ||
8391 | pi->followup != PIPE_SEQ | 8590 | pi->followup != PIPE_SEQ |
8392 | ) { | 8591 | ) { |
8393 | G.errexit_depth++; | 8592 | G.errexit_depth++; |
@@ -8828,17 +9027,19 @@ int hush_main(int argc, char **argv) | |||
8828 | #if !BB_MMU | 9027 | #if !BB_MMU |
8829 | G.argv0_for_re_execing = argv[0]; | 9028 | G.argv0_for_re_execing = argv[0]; |
8830 | #endif | 9029 | #endif |
9030 | |||
8831 | /* Deal with HUSH_VERSION */ | 9031 | /* Deal with HUSH_VERSION */ |
9032 | debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION"); | ||
9033 | unsetenv("HUSH_VERSION"); /* in case it exists in initial env */ | ||
8832 | shell_ver = xzalloc(sizeof(*shell_ver)); | 9034 | shell_ver = xzalloc(sizeof(*shell_ver)); |
8833 | shell_ver->flg_export = 1; | 9035 | shell_ver->flg_export = 1; |
8834 | shell_ver->flg_read_only = 1; | 9036 | shell_ver->flg_read_only = 1; |
8835 | /* Code which handles ${var<op>...} needs writable values for all variables, | 9037 | /* Code which handles ${var<op>...} needs writable values for all variables, |
8836 | * therefore we xstrdup: */ | 9038 | * therefore we xstrdup: */ |
8837 | shell_ver->varstr = xstrdup(hush_version_str); | 9039 | shell_ver->varstr = xstrdup(hush_version_str); |
9040 | |||
8838 | /* Create shell local variables from the values | 9041 | /* Create shell local variables from the values |
8839 | * currently living in the environment */ | 9042 | * currently living in the environment */ |
8840 | debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION"); | ||
8841 | unsetenv("HUSH_VERSION"); /* in case it exists in initial env */ | ||
8842 | G.top_var = shell_ver; | 9043 | G.top_var = shell_ver; |
8843 | cur_var = G.top_var; | 9044 | cur_var = G.top_var; |
8844 | e = environ; | 9045 | e = environ; |
@@ -8904,6 +9105,14 @@ int hush_main(int argc, char **argv) | |||
8904 | */ | 9105 | */ |
8905 | #endif | 9106 | #endif |
8906 | 9107 | ||
9108 | #if ENABLE_HUSH_LINENO_VAR | ||
9109 | if (ENABLE_HUSH_LINENO_VAR) { | ||
9110 | char *p = xasprintf("LINENO=%*s", (int)(sizeof(int)*3), ""); | ||
9111 | set_local_var(p, /*flags*/ 0); | ||
9112 | G.lineno_var = p; /* can't assign before set_local_var("LINENO=...") */ | ||
9113 | } | ||
9114 | #endif | ||
9115 | |||
8907 | #if ENABLE_FEATURE_EDITING | 9116 | #if ENABLE_FEATURE_EDITING |
8908 | G.line_input_state = new_line_input_t(FOR_SHELL); | 9117 | G.line_input_state = new_line_input_t(FOR_SHELL); |
8909 | #endif | 9118 | #endif |
@@ -9346,13 +9555,34 @@ static int FAST_FUNC builtin_eval(char **argv) | |||
9346 | int rcode = EXIT_SUCCESS; | 9555 | int rcode = EXIT_SUCCESS; |
9347 | 9556 | ||
9348 | argv = skip_dash_dash(argv); | 9557 | argv = skip_dash_dash(argv); |
9349 | if (*argv) { | 9558 | if (argv[0]) { |
9350 | char *str = expand_strvec_to_string(argv); | 9559 | char *str = NULL; |
9560 | |||
9561 | if (argv[1]) { | ||
9562 | /* "The eval utility shall construct a command by | ||
9563 | * concatenating arguments together, separating | ||
9564 | * each with a <space> character." | ||
9565 | */ | ||
9566 | char *p; | ||
9567 | unsigned len = 0; | ||
9568 | char **pp = argv; | ||
9569 | do | ||
9570 | len += strlen(*pp) + 1; | ||
9571 | while (*++pp); | ||
9572 | str = p = xmalloc(len); | ||
9573 | pp = argv; | ||
9574 | do { | ||
9575 | p = stpcpy(p, *pp); | ||
9576 | *p++ = ' '; | ||
9577 | } while (*++pp); | ||
9578 | p[-1] = '\0'; | ||
9579 | } | ||
9580 | |||
9351 | /* bash: | 9581 | /* bash: |
9352 | * eval "echo Hi; done" ("done" is syntax error): | 9582 | * eval "echo Hi; done" ("done" is syntax error): |
9353 | * "echo Hi" will not execute too. | 9583 | * "echo Hi" will not execute too. |
9354 | */ | 9584 | */ |
9355 | parse_and_run_string(str); | 9585 | parse_and_run_string(str ? str : argv[0]); |
9356 | free(str); | 9586 | free(str); |
9357 | rcode = G.last_exitcode; | 9587 | rcode = G.last_exitcode; |
9358 | } | 9588 | } |
@@ -9855,7 +10085,7 @@ static int FAST_FUNC builtin_set(char **argv) | |||
9855 | 10085 | ||
9856 | /* Nothing known, so abort */ | 10086 | /* Nothing known, so abort */ |
9857 | error: | 10087 | error: |
9858 | bb_error_msg("set: %s: invalid option", arg); | 10088 | bb_error_msg("%s: %s: invalid option", "set", arg); |
9859 | return EXIT_FAILURE; | 10089 | return EXIT_FAILURE; |
9860 | } | 10090 | } |
9861 | #endif | 10091 | #endif |
@@ -10038,6 +10268,11 @@ static int FAST_FUNC builtin_source(char **argv) | |||
10038 | arg_path = find_in_path(filename); | 10268 | arg_path = find_in_path(filename); |
10039 | if (arg_path) | 10269 | if (arg_path) |
10040 | filename = arg_path; | 10270 | filename = arg_path; |
10271 | else if (!ENABLE_HUSH_BASH_SOURCE_CURDIR) { | ||
10272 | errno = ENOENT; | ||
10273 | bb_simple_perror_msg(filename); | ||
10274 | return EXIT_FAILURE; | ||
10275 | } | ||
10041 | } | 10276 | } |
10042 | input = remember_FILE(fopen_or_warn(filename, "r")); | 10277 | input = remember_FILE(fopen_or_warn(filename, "r")); |
10043 | free(arg_path); | 10278 | free(arg_path); |