diff options
author | Ron Yorston <rmy@pobox.com> | 2019-05-27 11:56:52 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2019-05-27 11:56:52 +0100 |
commit | a61949401890cbb33a9d6c4571b51c53460ad438 (patch) | |
tree | 64dedaddb89896d5b1670a421af123670ca2120b /shell | |
parent | 03a7b173605a890e1db5177ecd5b8dd591081c41 (diff) | |
parent | bcb1fc3e6ca6fe902610f507eaf9b0b58a5c583a (diff) | |
download | busybox-w32-a61949401890cbb33a9d6c4571b51c53460ad438.tar.gz busybox-w32-a61949401890cbb33a9d6c4571b51c53460ad438.tar.bz2 busybox-w32-a61949401890cbb33a9d6c4571b51c53460ad438.zip |
Merge branch 'busybox' into merge
Diffstat (limited to 'shell')
-rw-r--r-- | shell/Config.src | 5 | ||||
-rw-r--r-- | shell/ash_test/ash-misc/export1.right | 1 | ||||
-rwxr-xr-x | shell/ash_test/ash-misc/export1.tests | 2 | ||||
-rw-r--r-- | shell/ash_test/ash-vars/param_expand_default.right | 2 | ||||
-rwxr-xr-x | shell/ash_test/ash-vars/param_expand_default.tests | 6 | ||||
-rw-r--r-- | shell/hush.c | 241 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/export1.right | 1 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/export1.tests | 2 | ||||
-rw-r--r-- | shell/hush_test/hush-vars/param_expand_default.right | 3 | ||||
-rwxr-xr-x | shell/hush_test/hush-vars/param_expand_default.tests | 7 | ||||
-rw-r--r-- | shell/math.c | 42 | ||||
-rw-r--r-- | shell/math.h | 4 | ||||
-rw-r--r-- | shell/shell_common.c | 343 | ||||
-rw-r--r-- | shell/shell_common.h | 2 |
14 files changed, 404 insertions, 257 deletions
diff --git a/shell/Config.src b/shell/Config.src index bc7218fe5..d7623f774 100644 --- a/shell/Config.src +++ b/shell/Config.src | |||
@@ -99,6 +99,11 @@ config FEATURE_SH_MATH_64 | |||
99 | slightly larger, but will allow computation with very large numbers. | 99 | slightly larger, but will allow computation with very large numbers. |
100 | This is not in POSIX, so do not rely on this in portable code. | 100 | This is not in POSIX, so do not rely on this in portable code. |
101 | 101 | ||
102 | config FEATURE_SH_MATH_BASE | ||
103 | bool "Support BASE#nnnn literals" | ||
104 | default y | ||
105 | depends on FEATURE_SH_MATH | ||
106 | |||
102 | config FEATURE_SH_EXTRA_QUIET | 107 | config FEATURE_SH_EXTRA_QUIET |
103 | bool "Hide message on interactive shell startup" | 108 | bool "Hide message on interactive shell startup" |
104 | default y | 109 | default y |
diff --git a/shell/ash_test/ash-misc/export1.right b/shell/ash_test/ash-misc/export1.right new file mode 100644 index 000000000..56647af8e --- /dev/null +++ b/shell/ash_test/ash-misc/export1.right | |||
@@ -0,0 +1 @@ | |||
|\w \\ \ \| | |||
diff --git a/shell/ash_test/ash-misc/export1.tests b/shell/ash_test/ash-misc/export1.tests new file mode 100755 index 000000000..4ffb40bdc --- /dev/null +++ b/shell/ash_test/ash-misc/export1.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | export Z='\w \\ \ \' | ||
2 | echo "|$Z|" | ||
diff --git a/shell/ash_test/ash-vars/param_expand_default.right b/shell/ash_test/ash-vars/param_expand_default.right index 3eecd1375..7a42f67e8 100644 --- a/shell/ash_test/ash-vars/param_expand_default.right +++ b/shell/ash_test/ash-vars/param_expand_default.right | |||
@@ -5,3 +5,5 @@ _aaaa _aaaa _aaaa _aaaa _aaaa | |||
5 | _ _ _ _word _word | 5 | _ _ _ _word _word |
6 | _ _ _ _ _word | 6 | _ _ _ _ _word |
7 | _fff _fff _fff _fff _fff | 7 | _fff _fff _fff _fff _fff |
8 | 1:1 | ||
9 | 0:0 | ||
diff --git a/shell/ash_test/ash-vars/param_expand_default.tests b/shell/ash_test/ash-vars/param_expand_default.tests index 5e42d30e3..dbd3e2218 100755 --- a/shell/ash_test/ash-vars/param_expand_default.tests +++ b/shell/ash_test/ash-vars/param_expand_default.tests | |||
@@ -1,6 +1,5 @@ | |||
1 | # first try some invalid patterns (do in subshell due to parsing error) | 1 | # first try some invalid patterns (do in subshell due to parsing error) |
2 | # (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages) | 2 | # (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages) |
3 | # valid in bash and ash (same as $-): "$THIS_SH" -c 'echo ${-}' SHELL | ||
4 | "$THIS_SH" -c 'echo ${:-}' SHELL | 3 | "$THIS_SH" -c 'echo ${:-}' SHELL |
5 | 4 | ||
6 | # now some funky ones | 5 | # now some funky ones |
@@ -21,3 +20,8 @@ echo _$f _${f-} _${f:-} _${f-word} _${f:-word} | |||
21 | 20 | ||
22 | f=fff | 21 | f=fff |
23 | echo _$f _${f-} _${f:-} _${f-word} _${f:-word} | 22 | echo _$f _${f-} _${f:-} _${f-word} _${f:-word} |
23 | |||
24 | set -- | ||
25 | set -- "${1-}"; echo 1:$# | ||
26 | set -- | ||
27 | set -- ${1-}; echo 0:$# | ||
diff --git a/shell/hush.c b/shell/hush.c index b3ae73b9b..4b08232a4 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -466,9 +466,9 @@ | |||
466 | 466 | ||
467 | #define JOB_STATUS_FORMAT "[%u] %-22s %.40s\n" | 467 | #define JOB_STATUS_FORMAT "[%u] %-22s %.40s\n" |
468 | 468 | ||
469 | #define _SPECIAL_VARS_STR "_*@$!?#" | 469 | #define _SPECIAL_VARS_STR "_*@$!?#-" |
470 | #define SPECIAL_VARS_STR ("_*@$!?#" + 1) | 470 | #define SPECIAL_VARS_STR ("_*@$!?#-" + 1) |
471 | #define NUMERIC_SPECVARS_STR ("_*@$!?#" + 3) | 471 | #define NUMERIC_SPECVARS_STR ("_*@$!?#-" + 3) |
472 | #if BASH_PATTERN_SUBST | 472 | #if BASH_PATTERN_SUBST |
473 | /* Support / and // replace ops */ | 473 | /* Support / and // replace ops */ |
474 | /* Note that // is stored as \ in "encoded" string representation */ | 474 | /* Note that // is stored as \ in "encoded" string representation */ |
@@ -854,8 +854,7 @@ struct globals { | |||
854 | /* 'interactive_fd' is a fd# open to ctty, if we have one | 854 | /* 'interactive_fd' is a fd# open to ctty, if we have one |
855 | * _AND_ if we decided to act interactively */ | 855 | * _AND_ if we decided to act interactively */ |
856 | int interactive_fd; | 856 | int interactive_fd; |
857 | const char *PS1; | 857 | IF_NOT_FEATURE_EDITING_FANCY_PROMPT(char *PS1;) |
858 | IF_FEATURE_EDITING_FANCY_PROMPT(const char *PS2;) | ||
859 | # define G_interactive_fd (G.interactive_fd) | 858 | # define G_interactive_fd (G.interactive_fd) |
860 | #else | 859 | #else |
861 | # define G_interactive_fd 0 | 860 | # define G_interactive_fd 0 |
@@ -903,6 +902,7 @@ struct globals { | |||
903 | #else | 902 | #else |
904 | # define G_x_mode 0 | 903 | # define G_x_mode 0 |
905 | #endif | 904 | #endif |
905 | char opt_s; | ||
906 | #if ENABLE_HUSH_INTERACTIVE | 906 | #if ENABLE_HUSH_INTERACTIVE |
907 | smallint promptmode; /* 0: PS1, 1: PS2 */ | 907 | smallint promptmode; /* 0: PS1, 1: PS2 */ |
908 | #endif | 908 | #endif |
@@ -968,8 +968,8 @@ struct globals { | |||
968 | smallint we_have_children; | 968 | smallint we_have_children; |
969 | #endif | 969 | #endif |
970 | #if ENABLE_HUSH_LINENO_VAR | 970 | #if ENABLE_HUSH_LINENO_VAR |
971 | unsigned lineno; | 971 | unsigned parse_lineno; |
972 | char *lineno_var; | 972 | unsigned execute_lineno; |
973 | #endif | 973 | #endif |
974 | HFILE *HFILE_list; | 974 | HFILE *HFILE_list; |
975 | /* Which signals have non-DFL handler (even with no traps set)? | 975 | /* Which signals have non-DFL handler (even with no traps set)? |
@@ -1009,6 +1009,7 @@ struct globals { | |||
1009 | int debug_indent; | 1009 | int debug_indent; |
1010 | #endif | 1010 | #endif |
1011 | struct sigaction sa; | 1011 | struct sigaction sa; |
1012 | char optstring_buf[sizeof("eixs")]; | ||
1012 | #if BASH_EPOCH_VARS | 1013 | #if BASH_EPOCH_VARS |
1013 | char epoch_buf[sizeof("%lu.nnnnnn") + sizeof(long)*3]; | 1014 | char epoch_buf[sizeof("%lu.nnnnnn") + sizeof(long)*3]; |
1014 | #endif | 1015 | #endif |
@@ -1448,13 +1449,6 @@ static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch) | |||
1448 | #endif | 1449 | #endif |
1449 | 1450 | ||
1450 | 1451 | ||
1451 | #if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT | ||
1452 | static void cmdedit_update_prompt(void); | ||
1453 | #else | ||
1454 | # define cmdedit_update_prompt() ((void)0) | ||
1455 | #endif | ||
1456 | |||
1457 | |||
1458 | /* Utility functions | 1452 | /* Utility functions |
1459 | */ | 1453 | */ |
1460 | /* Replace each \x with x in place, return ptr past NUL. */ | 1454 | /* Replace each \x with x in place, return ptr past NUL. */ |
@@ -2039,7 +2033,8 @@ static sighandler_t pick_sighandler(unsigned sig) | |||
2039 | static void hush_exit(int exitcode) | 2033 | static void hush_exit(int exitcode) |
2040 | { | 2034 | { |
2041 | #if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT | 2035 | #if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT |
2042 | save_history(G.line_input_state); | 2036 | if (G.line_input_state) |
2037 | save_history(G.line_input_state); | ||
2043 | #endif | 2038 | #endif |
2044 | 2039 | ||
2045 | fflush_all(); | 2040 | fflush_all(); |
@@ -2229,6 +2224,10 @@ static const char* FAST_FUNC get_local_var_value(const char *name) | |||
2229 | if (strcmp(name, "RANDOM") == 0) | 2224 | if (strcmp(name, "RANDOM") == 0) |
2230 | return utoa(next_random(&G.random_gen)); | 2225 | return utoa(next_random(&G.random_gen)); |
2231 | #endif | 2226 | #endif |
2227 | #if ENABLE_HUSH_LINENO_VAR | ||
2228 | if (strcmp(name, "LINENO") == 0) | ||
2229 | return utoa(G.execute_lineno); | ||
2230 | #endif | ||
2232 | #if BASH_EPOCH_VARS | 2231 | #if BASH_EPOCH_VARS |
2233 | { | 2232 | { |
2234 | const char *fmt = NULL; | 2233 | const char *fmt = NULL; |
@@ -2248,32 +2247,20 @@ static const char* FAST_FUNC get_local_var_value(const char *name) | |||
2248 | return NULL; | 2247 | return NULL; |
2249 | } | 2248 | } |
2250 | 2249 | ||
2250 | #if ENABLE_HUSH_GETOPTS | ||
2251 | static void handle_changed_special_names(const char *name, unsigned name_len) | 2251 | static void handle_changed_special_names(const char *name, unsigned name_len) |
2252 | { | 2252 | { |
2253 | if (ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT | 2253 | if (name_len == 6) { |
2254 | && name_len == 3 && name[0] == 'P' && name[1] == 'S' | ||
2255 | ) { | ||
2256 | cmdedit_update_prompt(); | ||
2257 | return; | ||
2258 | } | ||
2259 | |||
2260 | if ((ENABLE_HUSH_LINENO_VAR || ENABLE_HUSH_GETOPTS) | ||
2261 | && name_len == 6 | ||
2262 | ) { | ||
2263 | #if ENABLE_HUSH_LINENO_VAR | ||
2264 | if (strncmp(name, "LINENO", 6) == 0) { | ||
2265 | G.lineno_var = NULL; | ||
2266 | return; | ||
2267 | } | ||
2268 | #endif | ||
2269 | #if ENABLE_HUSH_GETOPTS | ||
2270 | if (strncmp(name, "OPTIND", 6) == 0) { | 2254 | if (strncmp(name, "OPTIND", 6) == 0) { |
2271 | G.getopt_count = 0; | 2255 | G.getopt_count = 0; |
2272 | return; | 2256 | return; |
2273 | } | 2257 | } |
2274 | #endif | ||
2275 | } | 2258 | } |
2276 | } | 2259 | } |
2260 | #else | ||
2261 | /* Do not even bother evaluating arguments */ | ||
2262 | # define handle_changed_special_names(...) ((void)0) | ||
2263 | #endif | ||
2277 | 2264 | ||
2278 | /* str holds "NAME=VAL" and is expected to be malloced. | 2265 | /* str holds "NAME=VAL" and is expected to be malloced. |
2279 | * We take ownership of it. | 2266 | * We take ownership of it. |
@@ -2289,6 +2276,7 @@ static int set_local_var(char *str, unsigned flags) | |||
2289 | char *free_me = NULL; | 2276 | char *free_me = NULL; |
2290 | char *eq_sign; | 2277 | char *eq_sign; |
2291 | int name_len; | 2278 | int name_len; |
2279 | int retval; | ||
2292 | unsigned local_lvl = (flags >> SETFLAG_VARLVL_SHIFT); | 2280 | unsigned local_lvl = (flags >> SETFLAG_VARLVL_SHIFT); |
2293 | 2281 | ||
2294 | eq_sign = strchr(str, '='); | 2282 | eq_sign = strchr(str, '='); |
@@ -2402,24 +2390,24 @@ static int set_local_var(char *str, unsigned flags) | |||
2402 | #endif | 2390 | #endif |
2403 | if (flags & SETFLAG_EXPORT) | 2391 | if (flags & SETFLAG_EXPORT) |
2404 | cur->flg_export = 1; | 2392 | cur->flg_export = 1; |
2393 | retval = 0; | ||
2405 | if (cur->flg_export) { | 2394 | if (cur->flg_export) { |
2406 | if (flags & SETFLAG_UNEXPORT) { | 2395 | if (flags & SETFLAG_UNEXPORT) { |
2407 | cur->flg_export = 0; | 2396 | cur->flg_export = 0; |
2408 | /* unsetenv was already done */ | 2397 | /* unsetenv was already done */ |
2409 | } else { | 2398 | } else { |
2410 | int i; | ||
2411 | debug_printf_env("%s: putenv '%s'/%u\n", __func__, cur->varstr, cur->var_nest_level); | 2399 | debug_printf_env("%s: putenv '%s'/%u\n", __func__, cur->varstr, cur->var_nest_level); |
2412 | i = putenv(cur->varstr); | 2400 | retval = putenv(cur->varstr); |
2413 | /* only now we can free old exported malloced string */ | 2401 | /* fall through to "free(free_me)" - |
2414 | free(free_me); | 2402 | * only now we can free old exported malloced string |
2415 | return i; | 2403 | */ |
2416 | } | 2404 | } |
2417 | } | 2405 | } |
2418 | free(free_me); | 2406 | free(free_me); |
2419 | 2407 | ||
2420 | handle_changed_special_names(cur->varstr, name_len - 1); | 2408 | handle_changed_special_names(cur->varstr, name_len - 1); |
2421 | 2409 | ||
2422 | return 0; | 2410 | return retval; |
2423 | } | 2411 | } |
2424 | 2412 | ||
2425 | static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) | 2413 | static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) |
@@ -2462,7 +2450,7 @@ static int unset_local_var_len(const char *name, int name_len) | |||
2462 | cur_pp = &cur->next; | 2450 | cur_pp = &cur->next; |
2463 | } | 2451 | } |
2464 | 2452 | ||
2465 | /* Handle "unset PS1" et al even if did not find the variable to unset */ | 2453 | /* Handle "unset LINENO" et al even if did not find the variable to unset */ |
2466 | handle_changed_special_names(name, name_len); | 2454 | handle_changed_special_names(name, name_len); |
2467 | 2455 | ||
2468 | return EXIT_SUCCESS; | 2456 | return EXIT_SUCCESS; |
@@ -2581,36 +2569,27 @@ static void reinit_unicode_for_hush(void) | |||
2581 | * \ | 2569 | * \ |
2582 | * It exercises a lot of corner cases. | 2570 | * It exercises a lot of corner cases. |
2583 | */ | 2571 | */ |
2584 | # if ENABLE_FEATURE_EDITING_FANCY_PROMPT | ||
2585 | static void cmdedit_update_prompt(void) | ||
2586 | { | ||
2587 | G.PS1 = get_local_var_value("PS1"); | ||
2588 | if (G.PS1 == NULL) | ||
2589 | G.PS1 = ""; | ||
2590 | G.PS2 = get_local_var_value("PS2"); | ||
2591 | if (G.PS2 == NULL) | ||
2592 | G.PS2 = ""; | ||
2593 | } | ||
2594 | # endif | ||
2595 | static const char *setup_prompt_string(void) | 2572 | static const char *setup_prompt_string(void) |
2596 | { | 2573 | { |
2597 | const char *prompt_str; | 2574 | const char *prompt_str; |
2598 | 2575 | ||
2599 | debug_printf_prompt("%s promptmode:%d\n", __func__, G.promptmode); | 2576 | debug_printf_prompt("%s promptmode:%d\n", __func__, G.promptmode); |
2600 | 2577 | ||
2601 | IF_FEATURE_EDITING_FANCY_PROMPT( prompt_str = G.PS2;) | 2578 | # if ENABLE_FEATURE_EDITING_FANCY_PROMPT |
2602 | IF_NOT_FEATURE_EDITING_FANCY_PROMPT(prompt_str = "> ";) | 2579 | prompt_str = get_local_var_value(G.promptmode == 0 ? "PS1" : "PS2"); |
2580 | if (!prompt_str) | ||
2581 | prompt_str = ""; | ||
2582 | # else | ||
2583 | prompt_str = "> "; /* if PS2, else... */ | ||
2603 | if (G.promptmode == 0) { /* PS1 */ | 2584 | if (G.promptmode == 0) { /* PS1 */ |
2604 | if (!ENABLE_FEATURE_EDITING_FANCY_PROMPT) { | 2585 | /* No fancy prompts supported, (re)generate "CURDIR $ " by hand */ |
2605 | /* No fancy prompts supported, (re)generate "CURDIR $ " by hand */ | 2586 | free(G.PS1); |
2606 | free((char*)G.PS1); | 2587 | /* bash uses $PWD value, even if it is set by user. |
2607 | /* bash uses $PWD value, even if it is set by user. | 2588 | * It uses current dir only if PWD is unset. |
2608 | * It uses current dir only if PWD is unset. | 2589 | * We always use current dir. */ |
2609 | * We always use current dir. */ | 2590 | G.PS1 = xasprintf("%s %c ", get_cwd(0), (geteuid() != 0) ? '$' : '#'); |
2610 | G.PS1 = xasprintf("%s %c ", get_cwd(0), (geteuid() != 0) ? '$' : '#'); | ||
2611 | } | ||
2612 | prompt_str = G.PS1; | ||
2613 | } | 2591 | } |
2592 | # endif | ||
2614 | debug_printf("prompt_str '%s'\n", prompt_str); | 2593 | debug_printf("prompt_str '%s'\n", prompt_str); |
2615 | return prompt_str; | 2594 | return prompt_str; |
2616 | } | 2595 | } |
@@ -2747,8 +2726,8 @@ static int i_getch(struct in_str *i) | |||
2747 | i->last_char = ch; | 2726 | i->last_char = ch; |
2748 | #if ENABLE_HUSH_LINENO_VAR | 2727 | #if ENABLE_HUSH_LINENO_VAR |
2749 | if (ch == '\n') { | 2728 | if (ch == '\n') { |
2750 | G.lineno++; | 2729 | G.parse_lineno++; |
2751 | debug_printf_parse("G.lineno++ = %u\n", G.lineno); | 2730 | debug_printf_parse("G.parse_lineno++ = %u\n", G.parse_lineno); |
2752 | } | 2731 | } |
2753 | #endif | 2732 | #endif |
2754 | return ch; | 2733 | return ch; |
@@ -3750,8 +3729,8 @@ static int done_command(struct parse_context *ctx) | |||
3750 | clear_and_ret: | 3729 | clear_and_ret: |
3751 | memset(command, 0, sizeof(*command)); | 3730 | memset(command, 0, sizeof(*command)); |
3752 | #if ENABLE_HUSH_LINENO_VAR | 3731 | #if ENABLE_HUSH_LINENO_VAR |
3753 | command->lineno = G.lineno; | 3732 | command->lineno = G.parse_lineno; |
3754 | debug_printf_parse("command->lineno = G.lineno (%u)\n", G.lineno); | 3733 | debug_printf_parse("command->lineno = G.parse_lineno (%u)\n", G.parse_lineno); |
3755 | #endif | 3734 | #endif |
3756 | return pi->num_cmds; /* used only for 0/nonzero check */ | 3735 | return pi->num_cmds; /* used only for 0/nonzero check */ |
3757 | } | 3736 | } |
@@ -4198,7 +4177,7 @@ static int done_word(struct parse_context *ctx) | |||
4198 | #if ENABLE_HUSH_LOOPS | 4177 | #if ENABLE_HUSH_LOOPS |
4199 | if (ctx->ctx_res_w == RES_FOR) { | 4178 | if (ctx->ctx_res_w == RES_FOR) { |
4200 | if (ctx->word.has_quoted_part | 4179 | if (ctx->word.has_quoted_part |
4201 | || !is_well_formed_var_name(command->argv[0], '\0') | 4180 | || endofname(command->argv[0])[0] != '\0' |
4202 | ) { | 4181 | ) { |
4203 | /* bash says just "not a valid identifier" */ | 4182 | /* bash says just "not a valid identifier" */ |
4204 | syntax_error("not a valid identifier in for"); | 4183 | syntax_error("not a valid identifier in for"); |
@@ -4912,6 +4891,7 @@ static int parse_dollar(o_string *as_string, | |||
4912 | case '#': /* number of args */ | 4891 | case '#': /* number of args */ |
4913 | case '*': /* args */ | 4892 | case '*': /* args */ |
4914 | case '@': /* args */ | 4893 | case '@': /* args */ |
4894 | case '-': /* $- option flags set by set builtin or shell options (-i etc) */ | ||
4915 | goto make_one_char_var; | 4895 | goto make_one_char_var; |
4916 | case '{': { | 4896 | case '{': { |
4917 | char len_single_ch; | 4897 | char len_single_ch; |
@@ -5086,11 +5066,10 @@ static int parse_dollar(o_string *as_string, | |||
5086 | case '_': | 5066 | case '_': |
5087 | goto make_var; | 5067 | goto make_var; |
5088 | #if 0 | 5068 | #if 0 |
5089 | /* TODO: $_ and $-: */ | 5069 | /* TODO: $_: */ |
5090 | /* $_ Shell or shell script name; or last argument of last command | 5070 | /* $_ Shell or shell script name; or last argument of last command |
5091 | * (if last command wasn't a pipe; if it was, bash sets $_ to ""); | 5071 | * (if last command wasn't a pipe; if it was, bash sets $_ to ""); |
5092 | * but in command's env, set to full pathname used to invoke it */ | 5072 | * but in command's env, set to full pathname used to invoke it */ |
5093 | /* $- Option flags set by set builtin or shell options (-i etc) */ | ||
5094 | ch = i_getch(input); | 5073 | ch = i_getch(input); |
5095 | nommu_addchr(as_string, ch); | 5074 | nommu_addchr(as_string, ch); |
5096 | ch = i_peek_and_eat_bkslash_nl(input); | 5075 | ch = i_peek_and_eat_bkslash_nl(input); |
@@ -5372,7 +5351,7 @@ static struct pipe *parse_stream(char **pstring, | |||
5372 | if ((ctx.is_assignment == MAYBE_ASSIGNMENT | 5351 | if ((ctx.is_assignment == MAYBE_ASSIGNMENT |
5373 | || ctx.is_assignment == WORD_IS_KEYWORD) | 5352 | || ctx.is_assignment == WORD_IS_KEYWORD) |
5374 | && ch == '=' | 5353 | && ch == '=' |
5375 | && is_well_formed_var_name(ctx.word.data, '=') | 5354 | && endofname(ctx.word.data)[0] == '=' |
5376 | ) { | 5355 | ) { |
5377 | ctx.is_assignment = DEFINITELY_ASSIGNMENT; | 5356 | ctx.is_assignment = DEFINITELY_ASSIGNMENT; |
5378 | debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); | 5357 | debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); |
@@ -6119,6 +6098,12 @@ static int encode_then_append_var_plusminus(o_string *output, int n, | |||
6119 | /* string has no special chars | 6098 | /* string has no special chars |
6120 | * && string has no $IFS chars | 6099 | * && string has no $IFS chars |
6121 | */ | 6100 | */ |
6101 | if (dquoted) { | ||
6102 | /* Prints 1 (quoted expansion is a "" word, not nothing): | ||
6103 | * set -- "${notexist-}"; echo $# | ||
6104 | */ | ||
6105 | output->has_quoted_part = 1; | ||
6106 | } | ||
6122 | return expand_vars_to_list(output, n, str); | 6107 | return expand_vars_to_list(output, n, str); |
6123 | } | 6108 | } |
6124 | 6109 | ||
@@ -6415,6 +6400,26 @@ static NOINLINE int expand_one_var(o_string *output, int n, | |||
6415 | case '#': /* argc */ | 6400 | case '#': /* argc */ |
6416 | val = utoa(G.global_argc ? G.global_argc-1 : 0); | 6401 | val = utoa(G.global_argc ? G.global_argc-1 : 0); |
6417 | break; | 6402 | break; |
6403 | case '-': { /* active options */ | ||
6404 | /* Check set_mode() to see what option chars we support */ | ||
6405 | char *cp; | ||
6406 | val = cp = G.optstring_buf; | ||
6407 | if (G.o_opt[OPT_O_ERREXIT]) | ||
6408 | *cp++ = 'e'; | ||
6409 | if (G_interactive_fd) | ||
6410 | *cp++ = 'i'; | ||
6411 | if (G_x_mode) | ||
6412 | *cp++ = 'x'; | ||
6413 | /* If G.o_opt[OPT_O_NOEXEC] is true, | ||
6414 | * commands read but are not executed, | ||
6415 | * so $- can not execute too, 'n' is never seen in $-. | ||
6416 | */ | ||
6417 | if (G.opt_s) | ||
6418 | *cp++ = 's'; | ||
6419 | //TODO: show 'c' if executed via "hush -c 'CMDS'" (bash only, not ash) | ||
6420 | *cp = '\0'; | ||
6421 | break; | ||
6422 | } | ||
6418 | default: | 6423 | default: |
6419 | val = get_local_var_value(var); | 6424 | val = get_local_var_value(var); |
6420 | } | 6425 | } |
@@ -7275,22 +7280,22 @@ static void parse_and_run_stream(struct in_str *inp, int end_trigger) | |||
7275 | static void parse_and_run_string(const char *s) | 7280 | static void parse_and_run_string(const char *s) |
7276 | { | 7281 | { |
7277 | struct in_str input; | 7282 | struct in_str input; |
7278 | //IF_HUSH_LINENO_VAR(unsigned sv = G.lineno;) | 7283 | //IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;) |
7279 | 7284 | ||
7280 | setup_string_in_str(&input, s); | 7285 | setup_string_in_str(&input, s); |
7281 | parse_and_run_stream(&input, '\0'); | 7286 | parse_and_run_stream(&input, '\0'); |
7282 | //IF_HUSH_LINENO_VAR(G.lineno = sv;) | 7287 | //IF_HUSH_LINENO_VAR(G.parse_lineno = sv;) |
7283 | } | 7288 | } |
7284 | 7289 | ||
7285 | static void parse_and_run_file(HFILE *fp) | 7290 | static void parse_and_run_file(HFILE *fp) |
7286 | { | 7291 | { |
7287 | struct in_str input; | 7292 | struct in_str input; |
7288 | IF_HUSH_LINENO_VAR(unsigned sv = G.lineno;) | 7293 | IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;) |
7289 | 7294 | ||
7290 | IF_HUSH_LINENO_VAR(G.lineno = 1;) | 7295 | IF_HUSH_LINENO_VAR(G.parse_lineno = 1;) |
7291 | setup_file_in_str(&input, fp); | 7296 | setup_file_in_str(&input, fp); |
7292 | parse_and_run_stream(&input, ';'); | 7297 | parse_and_run_stream(&input, ';'); |
7293 | IF_HUSH_LINENO_VAR(G.lineno = sv;) | 7298 | IF_HUSH_LINENO_VAR(G.parse_lineno = sv;) |
7294 | } | 7299 | } |
7295 | 7300 | ||
7296 | #if ENABLE_HUSH_TICK | 7301 | #if ENABLE_HUSH_TICK |
@@ -7598,10 +7603,10 @@ static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp) | |||
7598 | avoid_fd = 9; | 7603 | avoid_fd = 9; |
7599 | 7604 | ||
7600 | #if ENABLE_HUSH_INTERACTIVE | 7605 | #if ENABLE_HUSH_INTERACTIVE |
7601 | if (fd == G.interactive_fd) { | 7606 | if (fd == G_interactive_fd) { |
7602 | /* Testcase: "ls -l /proc/$$/fd 255>&-" should work */ | 7607 | /* Testcase: "ls -l /proc/$$/fd 255>&-" should work */ |
7603 | G.interactive_fd = xdup_CLOEXEC_and_close(G.interactive_fd, avoid_fd); | 7608 | G_interactive_fd = xdup_CLOEXEC_and_close(G_interactive_fd, avoid_fd); |
7604 | debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G.interactive_fd); | 7609 | debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G_interactive_fd); |
7605 | return 1; /* "we closed fd" */ | 7610 | return 1; /* "we closed fd" */ |
7606 | } | 7611 | } |
7607 | #endif | 7612 | #endif |
@@ -7677,7 +7682,7 @@ static void restore_redirects(struct squirrel *sq) | |||
7677 | free(sq); | 7682 | free(sq); |
7678 | } | 7683 | } |
7679 | 7684 | ||
7680 | /* If moved, G.interactive_fd stays on new fd, not restoring it */ | 7685 | /* If moved, G_interactive_fd stays on new fd, not restoring it */ |
7681 | } | 7686 | } |
7682 | 7687 | ||
7683 | #if ENABLE_FEATURE_SH_STANDALONE && BB_MMU | 7688 | #if ENABLE_FEATURE_SH_STANDALONE && BB_MMU |
@@ -7694,7 +7699,7 @@ static int internally_opened_fd(int fd, struct squirrel *sq) | |||
7694 | int i; | 7699 | int i; |
7695 | 7700 | ||
7696 | #if ENABLE_HUSH_INTERACTIVE | 7701 | #if ENABLE_HUSH_INTERACTIVE |
7697 | if (fd == G.interactive_fd) | 7702 | if (fd == G_interactive_fd) |
7698 | return 1; | 7703 | return 1; |
7699 | #endif | 7704 | #endif |
7700 | /* If this one of script's fds? */ | 7705 | /* If this one of script's fds? */ |
@@ -8989,8 +8994,7 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
8989 | struct variable *old_vars; | 8994 | struct variable *old_vars; |
8990 | 8995 | ||
8991 | #if ENABLE_HUSH_LINENO_VAR | 8996 | #if ENABLE_HUSH_LINENO_VAR |
8992 | if (G.lineno_var) | 8997 | G.execute_lineno = command->lineno; |
8993 | strcpy(G.lineno_var + sizeof("LINENO=")-1, utoa(command->lineno)); | ||
8994 | #endif | 8998 | #endif |
8995 | 8999 | ||
8996 | if (argv[command->assignment_cnt] == NULL) { | 9000 | if (argv[command->assignment_cnt] == NULL) { |
@@ -9223,8 +9227,7 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
9223 | xpiped_pair(pipefds); | 9227 | xpiped_pair(pipefds); |
9224 | 9228 | ||
9225 | #if ENABLE_HUSH_LINENO_VAR | 9229 | #if ENABLE_HUSH_LINENO_VAR |
9226 | if (G.lineno_var) | 9230 | G.execute_lineno = command->lineno; |
9227 | strcpy(G.lineno_var + sizeof("LINENO=")-1, utoa(command->lineno)); | ||
9228 | #endif | 9231 | #endif |
9229 | 9232 | ||
9230 | command->pid = BB_MMU ? fork() : vfork(); | 9233 | command->pid = BB_MMU ? fork() : vfork(); |
@@ -9908,14 +9911,6 @@ int hush_main(int argc, char **argv) | |||
9908 | /* Export PWD */ | 9911 | /* Export PWD */ |
9909 | set_pwd_var(SETFLAG_EXPORT); | 9912 | set_pwd_var(SETFLAG_EXPORT); |
9910 | 9913 | ||
9911 | #if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT | ||
9912 | /* Set (but not export) PS1/2 unless already set */ | ||
9913 | if (!get_local_var_value("PS1")) | ||
9914 | set_local_var_from_halves("PS1", "\\w \\$ "); | ||
9915 | if (!get_local_var_value("PS2")) | ||
9916 | set_local_var_from_halves("PS2", "> "); | ||
9917 | #endif | ||
9918 | |||
9919 | #if BASH_HOSTNAME_VAR | 9914 | #if BASH_HOSTNAME_VAR |
9920 | /* Set (but not export) HOSTNAME unless already set */ | 9915 | /* Set (but not export) HOSTNAME unless already set */ |
9921 | if (!get_local_var_value("HOSTNAME")) { | 9916 | if (!get_local_var_value("HOSTNAME")) { |
@@ -9927,6 +9922,11 @@ int hush_main(int argc, char **argv) | |||
9927 | /* IFS is not inherited from the parent environment */ | 9922 | /* IFS is not inherited from the parent environment */ |
9928 | set_local_var_from_halves("IFS", defifs); | 9923 | set_local_var_from_halves("IFS", defifs); |
9929 | 9924 | ||
9925 | if (!get_local_var_value("PATH")) | ||
9926 | set_local_var_from_halves("PATH", bb_default_root_path); | ||
9927 | |||
9928 | /* PS1/PS2 are set later, if we determine that we are interactive */ | ||
9929 | |||
9930 | /* bash also exports SHLVL and _, | 9930 | /* bash also exports SHLVL and _, |
9931 | * and sets (but doesn't export) the following variables: | 9931 | * and sets (but doesn't export) the following variables: |
9932 | * BASH=/bin/bash | 9932 | * BASH=/bin/bash |
@@ -9960,21 +9960,7 @@ int hush_main(int argc, char **argv) | |||
9960 | * PS4='+ ' | 9960 | * PS4='+ ' |
9961 | */ | 9961 | */ |
9962 | 9962 | ||
9963 | #if ENABLE_HUSH_LINENO_VAR | ||
9964 | if (ENABLE_HUSH_LINENO_VAR) { | ||
9965 | char *p = xasprintf("LINENO=%*s", (int)(sizeof(int)*3), ""); | ||
9966 | set_local_var(p, /*flags*/ 0); | ||
9967 | G.lineno_var = p; /* can't assign before set_local_var("LINENO=...") */ | ||
9968 | } | ||
9969 | #endif | ||
9970 | |||
9971 | #if ENABLE_FEATURE_EDITING | ||
9972 | G.line_input_state = new_line_input_t(FOR_SHELL); | ||
9973 | #endif | ||
9974 | |||
9975 | /* Initialize some more globals to non-zero values */ | 9963 | /* Initialize some more globals to non-zero values */ |
9976 | cmdedit_update_prompt(); | ||
9977 | |||
9978 | die_func = restore_ttypgrp_and__exit; | 9964 | die_func = restore_ttypgrp_and__exit; |
9979 | 9965 | ||
9980 | /* Shell is non-interactive at first. We need to call | 9966 | /* Shell is non-interactive at first. We need to call |
@@ -10192,6 +10178,7 @@ int hush_main(int argc, char **argv) | |||
10192 | #endif | 10178 | #endif |
10193 | goto final_return; | 10179 | goto final_return; |
10194 | } | 10180 | } |
10181 | G.opt_s = 1; | ||
10195 | 10182 | ||
10196 | /* Up to here, shell was non-interactive. Now it may become one. | 10183 | /* Up to here, shell was non-interactive. Now it may become one. |
10197 | * NB: don't forget to (re)run install_special_sighandlers() as needed. | 10184 | * NB: don't forget to (re)run install_special_sighandlers() as needed. |
@@ -10260,6 +10247,9 @@ int hush_main(int argc, char **argv) | |||
10260 | } | 10247 | } |
10261 | enable_restore_tty_pgrp_on_exit(); | 10248 | enable_restore_tty_pgrp_on_exit(); |
10262 | 10249 | ||
10250 | # if ENABLE_FEATURE_EDITING | ||
10251 | G.line_input_state = new_line_input_t(FOR_SHELL); | ||
10252 | # endif | ||
10263 | # if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0 | 10253 | # if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0 |
10264 | { | 10254 | { |
10265 | const char *hp = get_local_var_value("HISTFILE"); | 10255 | const char *hp = get_local_var_value("HISTFILE"); |
@@ -10308,14 +10298,23 @@ int hush_main(int argc, char **argv) | |||
10308 | * (--norc turns this off, --rcfile <file> overrides) | 10298 | * (--norc turns this off, --rcfile <file> overrides) |
10309 | */ | 10299 | */ |
10310 | 10300 | ||
10311 | if (!ENABLE_FEATURE_SH_EXTRA_QUIET && G_interactive_fd) { | 10301 | if (G_interactive_fd) { |
10312 | /* note: ash and hush share this string */ | 10302 | #if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT |
10313 | printf("\n\n%s %s\n" | 10303 | /* Set (but not export) PS1/2 unless already set */ |
10314 | IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n") | 10304 | if (!get_local_var_value("PS1")) |
10315 | "\n", | 10305 | set_local_var_from_halves("PS1", "\\w \\$ "); |
10316 | bb_banner, | 10306 | if (!get_local_var_value("PS2")) |
10317 | "hush - the humble shell" | 10307 | set_local_var_from_halves("PS2", "> "); |
10318 | ); | 10308 | #endif |
10309 | if (!ENABLE_FEATURE_SH_EXTRA_QUIET) { | ||
10310 | /* note: ash and hush share this string */ | ||
10311 | printf("\n\n%s %s\n" | ||
10312 | IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n") | ||
10313 | "\n", | ||
10314 | bb_banner, | ||
10315 | "hush - the humble shell" | ||
10316 | ); | ||
10317 | } | ||
10319 | } | 10318 | } |
10320 | 10319 | ||
10321 | parse_and_run_file(hfopen(NULL)); /* stdin */ | 10320 | parse_and_run_file(hfopen(NULL)); /* stdin */ |
@@ -10378,7 +10377,8 @@ static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM) | |||
10378 | #if MAX_HISTORY && ENABLE_FEATURE_EDITING | 10377 | #if MAX_HISTORY && ENABLE_FEATURE_EDITING |
10379 | static int FAST_FUNC builtin_history(char **argv UNUSED_PARAM) | 10378 | static int FAST_FUNC builtin_history(char **argv UNUSED_PARAM) |
10380 | { | 10379 | { |
10381 | show_history(G.line_input_state); | 10380 | if (G.line_input_state) |
10381 | show_history(G.line_input_state); | ||
10382 | return EXIT_SUCCESS; | 10382 | return EXIT_SUCCESS; |
10383 | } | 10383 | } |
10384 | #endif | 10384 | #endif |
@@ -10687,9 +10687,7 @@ static int helper_export_local(char **argv, unsigned flags) | |||
10687 | { | 10687 | { |
10688 | do { | 10688 | do { |
10689 | char *name = *argv; | 10689 | char *name = *argv; |
10690 | char *name_end = strchrnul(name, '='); | 10690 | const char *name_end = endofname(name); |
10691 | |||
10692 | /* So far we do not check that name is valid (TODO?) */ | ||
10693 | 10691 | ||
10694 | if (*name_end == '\0') { | 10692 | if (*name_end == '\0') { |
10695 | struct variable *var, **vpp; | 10693 | struct variable *var, **vpp; |
@@ -10747,8 +10745,15 @@ static int helper_export_local(char **argv, unsigned flags) | |||
10747 | */ | 10745 | */ |
10748 | name = xasprintf("%s=", name); | 10746 | name = xasprintf("%s=", name); |
10749 | } else { | 10747 | } else { |
10748 | if (*name_end != '=') { | ||
10749 | bb_error_msg("'%s': bad variable name", name); | ||
10750 | /* do not parse following argv[]s: */ | ||
10751 | return 1; | ||
10752 | } | ||
10750 | /* (Un)exporting/making local NAME=VALUE */ | 10753 | /* (Un)exporting/making local NAME=VALUE */ |
10751 | name = xstrdup(name); | 10754 | name = xstrdup(name); |
10755 | /* Testcase: export PS1='\w \$ ' */ | ||
10756 | unbackslash(name); | ||
10752 | } | 10757 | } |
10753 | debug_printf_env("%s: set_local_var('%s')\n", __func__, name); | 10758 | debug_printf_env("%s: set_local_var('%s')\n", __func__, name); |
10754 | if (set_local_var(name, flags)) | 10759 | if (set_local_var(name, flags)) |
diff --git a/shell/hush_test/hush-misc/export1.right b/shell/hush_test/hush-misc/export1.right new file mode 100644 index 000000000..56647af8e --- /dev/null +++ b/shell/hush_test/hush-misc/export1.right | |||
@@ -0,0 +1 @@ | |||
|\w \\ \ \| | |||
diff --git a/shell/hush_test/hush-misc/export1.tests b/shell/hush_test/hush-misc/export1.tests new file mode 100755 index 000000000..4ffb40bdc --- /dev/null +++ b/shell/hush_test/hush-misc/export1.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | export Z='\w \\ \ \' | ||
2 | echo "|$Z|" | ||
diff --git a/shell/hush_test/hush-vars/param_expand_default.right b/shell/hush_test/hush-vars/param_expand_default.right index acc717205..8bd0814b0 100644 --- a/shell/hush_test/hush-vars/param_expand_default.right +++ b/shell/hush_test/hush-vars/param_expand_default.right | |||
@@ -1,8 +1,9 @@ | |||
1 | hush: syntax error: unterminated ${name} | 1 | hush: syntax error: unterminated ${name} |
2 | hush: syntax error: unterminated ${name} | ||
3 | _0 _0 | 2 | _0 _0 |
4 | _ _ _ _word _word | 3 | _ _ _ _word _word |
5 | _aaaa _aaaa _aaaa _aaaa _aaaa | 4 | _aaaa _aaaa _aaaa _aaaa _aaaa |
6 | _ _ _ _word _word | 5 | _ _ _ _word _word |
7 | _ _ _ _ _word | 6 | _ _ _ _ _word |
8 | _fff _fff _fff _fff _fff | 7 | _fff _fff _fff _fff _fff |
8 | 1:1 | ||
9 | 0:0 | ||
diff --git a/shell/hush_test/hush-vars/param_expand_default.tests b/shell/hush_test/hush-vars/param_expand_default.tests index 16e5f8efe..dbd3e2218 100755 --- a/shell/hush_test/hush-vars/param_expand_default.tests +++ b/shell/hush_test/hush-vars/param_expand_default.tests | |||
@@ -1,7 +1,5 @@ | |||
1 | # first try some invalid patterns (do in subshell due to parsing error) | 1 | # first try some invalid patterns (do in subshell due to parsing error) |
2 | # (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages) | 2 | # (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages) |
3 | # valid in bash and ash (same as $-), not supported in hush (yet?): | ||
4 | "$THIS_SH" -c 'echo ${-}' SHELL | ||
5 | "$THIS_SH" -c 'echo ${:-}' SHELL | 3 | "$THIS_SH" -c 'echo ${:-}' SHELL |
6 | 4 | ||
7 | # now some funky ones | 5 | # now some funky ones |
@@ -22,3 +20,8 @@ echo _$f _${f-} _${f:-} _${f-word} _${f:-word} | |||
22 | 20 | ||
23 | f=fff | 21 | f=fff |
24 | echo _$f _${f-} _${f:-} _${f-word} _${f:-word} | 22 | echo _$f _${f-} _${f:-} _${f-word} _${f:-word} |
23 | |||
24 | set -- | ||
25 | set -- "${1-}"; echo 1:$# | ||
26 | set -- | ||
27 | set -- ${1-}; echo 0:$# | ||
diff --git a/shell/math.c b/shell/math.c index 611b3beab..eaf4f2453 100644 --- a/shell/math.c +++ b/shell/math.c | |||
@@ -513,6 +513,46 @@ static const char op_tokens[] ALIGN1 = { | |||
513 | }; | 513 | }; |
514 | #define ptr_to_rparen (&op_tokens[sizeof(op_tokens)-7]) | 514 | #define ptr_to_rparen (&op_tokens[sizeof(op_tokens)-7]) |
515 | 515 | ||
516 | #if ENABLE_FEATURE_SH_MATH_BASE | ||
517 | static arith_t strto_arith_t(const char *nptr, char **endptr) | ||
518 | { | ||
519 | unsigned base; | ||
520 | arith_t n; | ||
521 | |||
522 | # if ENABLE_FEATURE_SH_MATH_64 | ||
523 | n = strtoull(nptr, endptr, 0); | ||
524 | # else | ||
525 | n = strtoul(nptr, endptr, 0); | ||
526 | # endif | ||
527 | if (**endptr != '#' | ||
528 | || (*nptr < '1' || *nptr > '9') | ||
529 | || (n < 2 || n > 64) | ||
530 | ) { | ||
531 | return n; | ||
532 | } | ||
533 | |||
534 | /* It's "N#nnnn" or "NN#nnnn" syntax, NN can't start with 0, | ||
535 | * NN is in 2..64 range. | ||
536 | */ | ||
537 | base = (unsigned)n; | ||
538 | n = 0; | ||
539 | nptr = *endptr + 1; | ||
540 | /* bash allows "N#" (empty "nnnn" part) */ | ||
541 | while (isdigit(*nptr)) { | ||
542 | /* bash does not check for overflows */ | ||
543 | n = n * base + (*nptr++ - '0'); | ||
544 | } | ||
545 | *endptr = (char*)nptr; | ||
546 | return n; | ||
547 | } | ||
548 | #else /* !ENABLE_FEATURE_SH_MATH_BASE */ | ||
549 | # if ENABLE_FEATURE_SH_MATH_64 | ||
550 | # define strto_arith_t(nptr, endptr) strtoull(nptr, endptr, 0) | ||
551 | # else | ||
552 | # define strto_arith_t(nptr, endptr) strtoul(nptr, endptr, 0) | ||
553 | # endif | ||
554 | #endif | ||
555 | |||
516 | static arith_t FAST_FUNC | 556 | static arith_t FAST_FUNC |
517 | evaluate_string(arith_state_t *math_state, const char *expr) | 557 | evaluate_string(arith_state_t *math_state, const char *expr) |
518 | { | 558 | { |
@@ -591,7 +631,7 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
591 | /* Number */ | 631 | /* Number */ |
592 | numstackptr->var = NULL; | 632 | numstackptr->var = NULL; |
593 | errno = 0; | 633 | errno = 0; |
594 | numstackptr->val = strto_arith_t(expr, (char**) &expr, 0); | 634 | numstackptr->val = strto_arith_t(expr, (char**) &expr); |
595 | if (errno) | 635 | if (errno) |
596 | numstackptr->val = 0; /* bash compat */ | 636 | numstackptr->val = 0; /* bash compat */ |
597 | goto num; | 637 | goto num; |
diff --git a/shell/math.h b/shell/math.h index 90eb49144..a3fe51b6b 100644 --- a/shell/math.h +++ b/shell/math.h | |||
@@ -66,11 +66,9 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN | |||
66 | #if ENABLE_FEATURE_SH_MATH_64 | 66 | #if ENABLE_FEATURE_SH_MATH_64 |
67 | typedef long long arith_t; | 67 | typedef long long arith_t; |
68 | #define ARITH_FMT "%"LL_FMT"d" | 68 | #define ARITH_FMT "%"LL_FMT"d" |
69 | #define strto_arith_t strtoull | ||
70 | #else | 69 | #else |
71 | typedef long arith_t; | 70 | typedef long arith_t; |
72 | #define ARITH_FMT "%ld" | 71 | # define ARITH_FMT "%ld" |
73 | #define strto_arith_t strtoul | ||
74 | #endif | 72 | #endif |
75 | 73 | ||
76 | typedef const char* FAST_FUNC (*arith_var_lookup_t)(const char *name); | 74 | typedef const char* FAST_FUNC (*arith_var_lookup_t)(const char *name); |
diff --git a/shell/shell_common.c b/shell/shell_common.c index 70ecf2095..d1df5888c 100644 --- a/shell/shell_common.c +++ b/shell/shell_common.c | |||
@@ -26,19 +26,6 @@ const char defifsvar[] ALIGN1 = "IFS= \t\n\r"; | |||
26 | #endif | 26 | #endif |
27 | const char defoptindvar[] ALIGN1 = "OPTIND=1"; | 27 | const char defoptindvar[] ALIGN1 = "OPTIND=1"; |
28 | 28 | ||
29 | |||
30 | int FAST_FUNC is_well_formed_var_name(const char *s, char terminator) | ||
31 | { | ||
32 | if (!s || !(isalpha(*s) || *s == '_')) | ||
33 | return 0; | ||
34 | |||
35 | do | ||
36 | s++; | ||
37 | while (isalnum(*s) || *s == '_'); | ||
38 | |||
39 | return *s == terminator; | ||
40 | } | ||
41 | |||
42 | /* read builtin */ | 29 | /* read builtin */ |
43 | 30 | ||
44 | /* Needs to be interruptible: shell must handle traps and shell-special signals | 31 | /* Needs to be interruptible: shell must handle traps and shell-special signals |
@@ -76,7 +63,7 @@ shell_builtin_read(struct builtin_read_params *params) | |||
76 | argv = params->argv; | 63 | argv = params->argv; |
77 | pp = argv; | 64 | pp = argv; |
78 | while (*pp) { | 65 | while (*pp) { |
79 | if (!is_well_formed_var_name(*pp, '\0')) { | 66 | if (endofname(*pp)[0] != '\0') { |
80 | /* Mimic bash message */ | 67 | /* Mimic bash message */ |
81 | bb_error_msg("read: '%s': not a valid identifier", *pp); | 68 | bb_error_msg("read: '%s': not a valid identifier", *pp); |
82 | return (const char *)(uintptr_t)1; | 69 | return (const char *)(uintptr_t)1; |
@@ -384,99 +371,138 @@ shell_builtin_read(struct builtin_read_params *params) | |||
384 | struct limits { | 371 | struct limits { |
385 | uint8_t cmd; /* RLIMIT_xxx fit into it */ | 372 | uint8_t cmd; /* RLIMIT_xxx fit into it */ |
386 | uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ | 373 | uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ |
387 | char option; | ||
388 | const char *name; | 374 | const char *name; |
389 | }; | 375 | }; |
390 | 376 | ||
391 | static const struct limits limits_tbl[] = { | 377 | static const struct limits limits_tbl[] = { |
392 | #ifdef RLIMIT_FSIZE | 378 | { RLIMIT_CORE, 9, "core file size (blocks)" }, // -c |
393 | { RLIMIT_FSIZE, 9, 'f', "file size (blocks)" }, | 379 | { RLIMIT_DATA, 10, "data seg size (kb)" }, // -d |
380 | { RLIMIT_NICE, 0, "scheduling priority" }, // -e | ||
381 | { RLIMIT_FSIZE, 9, "file size (blocks)" }, // -f | ||
382 | #define LIMIT_F_IDX 3 | ||
383 | #ifdef RLIMIT_SIGPENDING | ||
384 | { RLIMIT_SIGPENDING, 0, "pending signals" }, // -i | ||
394 | #endif | 385 | #endif |
395 | #ifdef RLIMIT_CPU | 386 | #ifdef RLIMIT_MEMLOCK |
396 | { RLIMIT_CPU, 0, 't', "cpu time (seconds)" }, | 387 | { RLIMIT_MEMLOCK, 10, "max locked memory (kb)" }, // -l |
397 | #endif | 388 | #endif |
398 | #ifdef RLIMIT_DATA | 389 | #ifdef RLIMIT_RSS |
399 | { RLIMIT_DATA, 10, 'd', "data seg size (kb)" }, | 390 | { RLIMIT_RSS, 10, "max memory size (kb)" }, // -m |
400 | #endif | 391 | #endif |
401 | #ifdef RLIMIT_STACK | 392 | #ifdef RLIMIT_NOFILE |
402 | { RLIMIT_STACK, 10, 's', "stack size (kb)" }, | 393 | { RLIMIT_NOFILE, 0, "open files" }, // -n |
403 | #endif | 394 | #endif |
404 | #ifdef RLIMIT_CORE | 395 | #ifdef RLIMIT_MSGQUEUE |
405 | { RLIMIT_CORE, 9, 'c', "core file size (blocks)" }, | 396 | { RLIMIT_MSGQUEUE, 0, "POSIX message queues (bytes)" }, // -q |
406 | #endif | 397 | #endif |
407 | #ifdef RLIMIT_RSS | 398 | #ifdef RLIMIT_RTPRIO |
408 | { RLIMIT_RSS, 10, 'm', "resident set size (kb)" }, | 399 | { RLIMIT_RTPRIO, 0, "real-time priority" }, // -r |
409 | #endif | 400 | #endif |
410 | #ifdef RLIMIT_MEMLOCK | 401 | #ifdef RLIMIT_STACK |
411 | { RLIMIT_MEMLOCK, 10, 'l', "locked memory (kb)" }, | 402 | { RLIMIT_STACK, 10, "stack size (kb)" }, // -s |
412 | #endif | 403 | #endif |
413 | #ifdef RLIMIT_NPROC | 404 | #ifdef RLIMIT_CPU |
414 | { RLIMIT_NPROC, 0, 'p', "processes" }, | 405 | { RLIMIT_CPU, 0, "cpu time (seconds)" }, // -t |
415 | #endif | 406 | #endif |
416 | #ifdef RLIMIT_NOFILE | 407 | #ifdef RLIMIT_NPROC |
417 | { RLIMIT_NOFILE, 0, 'n', "file descriptors" }, | 408 | { RLIMIT_NPROC, 0, "max user processes" }, // -u |
418 | #endif | 409 | #endif |
419 | #ifdef RLIMIT_AS | 410 | #ifdef RLIMIT_AS |
420 | { RLIMIT_AS, 10, 'v', "address space (kb)" }, | 411 | { RLIMIT_AS, 10, "virtual memory (kb)" }, // -v |
421 | #endif | 412 | #endif |
422 | #ifdef RLIMIT_LOCKS | 413 | #ifdef RLIMIT_LOCKS |
423 | { RLIMIT_LOCKS, 0, 'w', "locks" }, | 414 | { RLIMIT_LOCKS, 0, "file locks" }, // -x |
415 | #endif | ||
416 | }; | ||
417 | // bash also shows: | ||
418 | //pipe size (512 bytes, -p) 8 | ||
419 | |||
420 | static const char limit_chars[] ALIGN1 = | ||
421 | "c" | ||
422 | "d" | ||
423 | "e" | ||
424 | "f" | ||
425 | #ifdef RLIMIT_SIGPENDING | ||
426 | "i" | ||
427 | #endif | ||
428 | #ifdef RLIMIT_MEMLOCK | ||
429 | "l" | ||
430 | #endif | ||
431 | #ifdef RLIMIT_RSS | ||
432 | "m" | ||
433 | #endif | ||
434 | #ifdef RLIMIT_NOFILE | ||
435 | "n" | ||
424 | #endif | 436 | #endif |
425 | #ifdef RLIMIT_NICE | 437 | #ifdef RLIMIT_MSGQUEUE |
426 | { RLIMIT_NICE, 0, 'e', "scheduling priority" }, | 438 | "q" |
427 | #endif | 439 | #endif |
428 | #ifdef RLIMIT_RTPRIO | 440 | #ifdef RLIMIT_RTPRIO |
429 | { RLIMIT_RTPRIO, 0, 'r', "real-time priority" }, | 441 | "r" |
430 | #endif | 442 | #endif |
431 | }; | 443 | #ifdef RLIMIT_STACK |
432 | 444 | "s" | |
433 | enum { | ||
434 | OPT_hard = (1 << 0), | ||
435 | OPT_soft = (1 << 1), | ||
436 | }; | ||
437 | |||
438 | /* "-": treat args as parameters of option with ASCII code 1 */ | ||
439 | static const char ulimit_opt_string[] ALIGN1 = "-HSa" | ||
440 | #ifdef RLIMIT_FSIZE | ||
441 | "f::" | ||
442 | #endif | 445 | #endif |
443 | #ifdef RLIMIT_CPU | 446 | #ifdef RLIMIT_CPU |
444 | "t::" | 447 | "t" |
445 | #endif | 448 | #endif |
446 | #ifdef RLIMIT_DATA | 449 | #ifdef RLIMIT_NPROC |
447 | "d::" | 450 | "u" |
448 | #endif | 451 | #endif |
449 | #ifdef RLIMIT_STACK | 452 | #ifdef RLIMIT_AS |
450 | "s::" | 453 | "v" |
451 | #endif | 454 | #endif |
452 | #ifdef RLIMIT_CORE | 455 | #ifdef RLIMIT_LOCKS |
453 | "c::" | 456 | "x" |
454 | #endif | 457 | #endif |
455 | #ifdef RLIMIT_RSS | 458 | ; |
456 | "m::" | 459 | |
460 | /* "-": treat args as parameters of option with ASCII code 1 */ | ||
461 | static const char ulimit_opt_string[] ALIGN1 = "-HSa" | ||
462 | "c::" | ||
463 | "d::" | ||
464 | "e::" | ||
465 | "f::" | ||
466 | #ifdef RLIMIT_SIGPENDING | ||
467 | "i::" | ||
457 | #endif | 468 | #endif |
458 | #ifdef RLIMIT_MEMLOCK | 469 | #ifdef RLIMIT_MEMLOCK |
459 | "l::" | 470 | "l::" |
460 | #endif | 471 | #endif |
461 | #ifdef RLIMIT_NPROC | 472 | #ifdef RLIMIT_RSS |
462 | "p::" | 473 | "m::" |
463 | #endif | 474 | #endif |
464 | #ifdef RLIMIT_NOFILE | 475 | #ifdef RLIMIT_NOFILE |
465 | "n::" | 476 | "n::" |
466 | #endif | 477 | #endif |
478 | #ifdef RLIMIT_MSGQUEUE | ||
479 | "q::" | ||
480 | #endif | ||
481 | #ifdef RLIMIT_RTPRIO | ||
482 | "r::" | ||
483 | #endif | ||
484 | #ifdef RLIMIT_STACK | ||
485 | "s::" | ||
486 | #endif | ||
487 | #ifdef RLIMIT_CPU | ||
488 | "t::" | ||
489 | #endif | ||
490 | #ifdef RLIMIT_NPROC | ||
491 | "u::" | ||
492 | #endif | ||
467 | #ifdef RLIMIT_AS | 493 | #ifdef RLIMIT_AS |
468 | "v::" | 494 | "v::" |
469 | #endif | 495 | #endif |
470 | #ifdef RLIMIT_LOCKS | 496 | #ifdef RLIMIT_LOCKS |
471 | "w::" | 497 | "x::" |
472 | #endif | ||
473 | #ifdef RLIMIT_NICE | ||
474 | "e::" | ||
475 | #endif | ||
476 | #ifdef RLIMIT_RTPRIO | ||
477 | "r::" | ||
478 | #endif | 498 | #endif |
479 | ; | 499 | ; |
500 | |||
501 | enum { | ||
502 | OPT_hard = (1 << 0), | ||
503 | OPT_soft = (1 << 1), | ||
504 | OPT_all = (1 << 2), | ||
505 | }; | ||
480 | 506 | ||
481 | static void printlim(unsigned opts, const struct rlimit *limit, | 507 | static void printlim(unsigned opts, const struct rlimit *limit, |
482 | const struct limits *l) | 508 | const struct limits *l) |
@@ -484,7 +510,7 @@ static void printlim(unsigned opts, const struct rlimit *limit, | |||
484 | rlim_t val; | 510 | rlim_t val; |
485 | 511 | ||
486 | val = limit->rlim_max; | 512 | val = limit->rlim_max; |
487 | if (!(opts & OPT_hard)) | 513 | if (opts & OPT_soft) |
488 | val = limit->rlim_cur; | 514 | val = limit->rlim_cur; |
489 | 515 | ||
490 | if (val == RLIM_INFINITY) | 516 | if (val == RLIM_INFINITY) |
@@ -498,8 +524,11 @@ static void printlim(unsigned opts, const struct rlimit *limit, | |||
498 | int FAST_FUNC | 524 | int FAST_FUNC |
499 | shell_builtin_ulimit(char **argv) | 525 | shell_builtin_ulimit(char **argv) |
500 | { | 526 | { |
527 | struct rlimit limit; | ||
528 | unsigned opt_cnt; | ||
501 | unsigned opts; | 529 | unsigned opts; |
502 | unsigned argc; | 530 | unsigned argc; |
531 | unsigned i; | ||
503 | 532 | ||
504 | /* We can't use getopt32: need to handle commands like | 533 | /* We can't use getopt32: need to handle commands like |
505 | * ulimit 123 -c2 -l 456 | 534 | * ulimit 123 -c2 -l 456 |
@@ -510,12 +539,48 @@ shell_builtin_ulimit(char **argv) | |||
510 | */ | 539 | */ |
511 | GETOPT_RESET(); | 540 | GETOPT_RESET(); |
512 | 541 | ||
542 | // bash 4.4.23: | ||
543 | // | ||
544 | // -H and/or -S change meaning even of options *before* them: ulimit -f 2000 -H | ||
545 | // sets hard limit, ulimit -a -H prints hard limits. | ||
546 | // | ||
547 | // -a is equivalent for requesting all limits to be shown. | ||
548 | // | ||
549 | // If -a is specified, attempts to set limits are ignored: | ||
550 | // ulimit -m 1000; ulimit -m 2000 -a | ||
551 | // shows 1000, not 2000. HOWEVER, *implicit* -f form "ulimit 2000 -a" | ||
552 | // DOES set -f limit [we don't implement this quirk], "ulimit -a 2000" does not. | ||
553 | // Options are still parsed: ulimit -az complains about unknown -z opt. | ||
554 | // | ||
555 | // -a is not cumulative: "ulimit -a -a" = "ulimit -a -f -m" = "ulimit -a" | ||
556 | // | ||
557 | // -HSa can be combined in one argument and with one other option (example: -Sm), | ||
558 | // but other options can't: limit value is an optional argument, | ||
559 | // thus "-mf" means "-m f", f is the parameter of -m. | ||
560 | // | ||
561 | // Limit can be set and then printed: ulimit -m 2000 -m | ||
562 | // If set more than once, they are set and printed in order: | ||
563 | // try ulimit -m -m 1000 -m -m 2000 -m -m 3000 -m | ||
564 | // | ||
565 | // Limits are shown in the order of options given: | ||
566 | // ulimit -m -f is not the same as ulimit -f -m. | ||
567 | // | ||
568 | // If both -S and -H are given, show soft limit. | ||
569 | // | ||
570 | // Short printout (limit value only) is printed only if just one option | ||
571 | // is given: ulimit -m. ulimit -f -m prints verbose lines. | ||
572 | // ulimit -f -f prints same verbose line twice. | ||
573 | // ulimit -m 10000 -f prints verbose line for -f. | ||
574 | |||
513 | argc = string_array_len(argv); | 575 | argc = string_array_len(argv); |
514 | 576 | ||
577 | /* First pass over options: detect -H/-S/-a status, | ||
578 | * and "bare ulimit" and "only one option" cases | ||
579 | * by counting other opts. | ||
580 | */ | ||
581 | opt_cnt = 0; | ||
515 | opts = 0; | 582 | opts = 0; |
516 | while (1) { | 583 | while (1) { |
517 | struct rlimit limit; | ||
518 | const struct limits *l; | ||
519 | int opt_char = getopt(argc, argv, ulimit_opt_string); | 584 | int opt_char = getopt(argc, argv, ulimit_opt_string); |
520 | 585 | ||
521 | if (opt_char == -1) | 586 | if (opt_char == -1) |
@@ -528,74 +593,94 @@ shell_builtin_ulimit(char **argv) | |||
528 | opts |= OPT_soft; | 593 | opts |= OPT_soft; |
529 | continue; | 594 | continue; |
530 | } | 595 | } |
531 | |||
532 | if (opt_char == 'a') { | 596 | if (opt_char == 'a') { |
533 | for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) { | 597 | opts |= OPT_all; |
534 | getrlimit(l->cmd, &limit); | ||
535 | printf("-%c: %-30s ", l->option, l->name); | ||
536 | printlim(opts, &limit, l); | ||
537 | } | ||
538 | continue; | 598 | continue; |
539 | } | 599 | } |
600 | if (opt_char == '?') { | ||
601 | /* bad option. getopt already complained. */ | ||
602 | return EXIT_FAILURE; | ||
603 | } | ||
604 | opt_cnt++; | ||
605 | } /* while (there are options) */ | ||
606 | |||
607 | if (!(opts & (OPT_hard | OPT_soft))) | ||
608 | opts |= (OPT_hard | OPT_soft); | ||
609 | if (opts & OPT_all) { | ||
610 | for (i = 0; i < ARRAY_SIZE(limits_tbl); i++) { | ||
611 | getrlimit(limits_tbl[i].cmd, &limit); | ||
612 | printf("%-32s(-%c) ", limits_tbl[i].name, limit_chars[i]); | ||
613 | printlim(opts, &limit, &limits_tbl[i]); | ||
614 | } | ||
615 | return EXIT_SUCCESS; | ||
616 | } | ||
540 | 617 | ||
541 | if (opt_char == 1) | 618 | /* Second pass: set or print limits, in order */ |
619 | GETOPT_RESET(); | ||
620 | while (1) { | ||
621 | char *val_str; | ||
622 | int opt_char = getopt(argc, argv, ulimit_opt_string); | ||
623 | |||
624 | if (opt_char == -1) | ||
625 | break; | ||
626 | if (opt_char == 'H') | ||
627 | continue; | ||
628 | if (opt_char == 'S') | ||
629 | continue; | ||
630 | //if (opt_char == 'a') - impossible | ||
631 | |||
632 | if (opt_char == 1) /* if "ulimit NNN", -f is assumed */ | ||
542 | opt_char = 'f'; | 633 | opt_char = 'f'; |
543 | for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) { | 634 | i = strchrnul(limit_chars, opt_char) - limit_chars; |
544 | if (opt_char == l->option) { | 635 | //if (i >= ARRAY_SIZE(limits_tbl)) - bad option, impossible |
545 | char *val_str; | 636 | |
546 | 637 | val_str = optarg; | |
547 | getrlimit(l->cmd, &limit); | 638 | if (!val_str && argv[optind] && argv[optind][0] != '-') |
548 | 639 | val_str = argv[optind++]; /* ++ skips NN in "-c NN" case */ | |
549 | val_str = optarg; | 640 | |
550 | if (!val_str && argv[optind] && argv[optind][0] != '-') | 641 | getrlimit(limits_tbl[i].cmd, &limit); |
551 | val_str = argv[optind++]; /* ++ skips NN in "-c NN" case */ | 642 | if (!val_str) { |
552 | if (val_str) { | 643 | if (opt_cnt > 1) |
553 | rlim_t val; | 644 | printf("%-32s(-%c) ", limits_tbl[i].name, limit_chars[i]); |
554 | 645 | printlim(opts, &limit, &limits_tbl[i]); | |
555 | if (strcmp(val_str, "unlimited") == 0) | 646 | } else { |
556 | val = RLIM_INFINITY; | 647 | rlim_t val = RLIM_INFINITY; |
557 | else { | 648 | if (strcmp(val_str, "unlimited") != 0) { |
558 | if (sizeof(val) == sizeof(int)) | 649 | if (sizeof(val) == sizeof(int)) |
559 | val = bb_strtou(val_str, NULL, 10); | 650 | val = bb_strtou(val_str, NULL, 10); |
560 | else if (sizeof(val) == sizeof(long)) | 651 | else if (sizeof(val) == sizeof(long)) |
561 | val = bb_strtoul(val_str, NULL, 10); | 652 | val = bb_strtoul(val_str, NULL, 10); |
562 | else | 653 | else |
563 | val = bb_strtoull(val_str, NULL, 10); | 654 | val = bb_strtoull(val_str, NULL, 10); |
564 | if (errno) { | 655 | if (errno) { |
565 | bb_error_msg("invalid number '%s'", val_str); | 656 | bb_error_msg("invalid number '%s'", val_str); |
566 | return EXIT_FAILURE; | 657 | return EXIT_FAILURE; |
567 | } | ||
568 | val <<= l->factor_shift; | ||
569 | } | ||
570 | //bb_error_msg("opt %c val_str:'%s' val:%lld", opt_char, val_str, (long long)val); | ||
571 | /* from man bash: "If neither -H nor -S | ||
572 | * is specified, both the soft and hard | ||
573 | * limits are set. */ | ||
574 | if (!opts) | ||
575 | opts = OPT_hard + OPT_soft; | ||
576 | if (opts & OPT_hard) | ||
577 | limit.rlim_max = val; | ||
578 | if (opts & OPT_soft) | ||
579 | limit.rlim_cur = val; | ||
580 | //bb_error_msg("setrlimit(%d, %lld, %lld)", l->cmd, (long long)limit.rlim_cur, (long long)limit.rlim_max); | ||
581 | if (setrlimit(l->cmd, &limit) < 0) { | ||
582 | bb_perror_msg("error setting limit"); | ||
583 | return EXIT_FAILURE; | ||
584 | } | ||
585 | } else { | ||
586 | printlim(opts, &limit, l); | ||
587 | } | 658 | } |
588 | break; | 659 | val <<= limits_tbl[i].factor_shift; |
660 | } | ||
661 | //bb_error_msg("opt %c val_str:'%s' val:%lld", opt_char, val_str, (long long)val); | ||
662 | /* from man bash: "If neither -H nor -S | ||
663 | * is specified, both the soft and hard | ||
664 | * limits are set. */ | ||
665 | if (opts & OPT_hard) | ||
666 | limit.rlim_max = val; | ||
667 | if (opts & OPT_soft) | ||
668 | limit.rlim_cur = val; | ||
669 | //bb_error_msg("setrlimit(%d, %lld, %lld)", limits_tbl[i].cmd, (long long)limit.rlim_cur, (long long)limit.rlim_max); | ||
670 | if (setrlimit(limits_tbl[i].cmd, &limit) < 0) { | ||
671 | bb_perror_msg("error setting limit"); | ||
672 | return EXIT_FAILURE; | ||
589 | } | 673 | } |
590 | } /* for (every possible opt) */ | ||
591 | |||
592 | if (l == &limits_tbl[ARRAY_SIZE(limits_tbl)]) { | ||
593 | /* bad option. getopt already complained. */ | ||
594 | break; | ||
595 | } | 674 | } |
596 | } /* while (there are options) */ | 675 | } /* while (there are options) */ |
597 | 676 | ||
598 | return 0; | 677 | if (opt_cnt == 0) { |
678 | /* "bare ulimit": treat it as if it was -f */ | ||
679 | getrlimit(limits_tbl[LIMIT_F_IDX].cmd, &limit); | ||
680 | printlim(opts, &limit, &limits_tbl[LIMIT_F_IDX]); | ||
681 | } | ||
682 | |||
683 | return EXIT_SUCCESS; | ||
599 | } | 684 | } |
600 | #else | 685 | #else |
601 | int FAST_FUNC shell_builtin_ulimit(char **argv UNUSED_PARAM) | 686 | int FAST_FUNC shell_builtin_ulimit(char **argv UNUSED_PARAM) |
diff --git a/shell/shell_common.h b/shell/shell_common.h index a1323021d..7b478f1df 100644 --- a/shell/shell_common.h +++ b/shell/shell_common.h | |||
@@ -26,8 +26,6 @@ extern const char defifsvar[] ALIGN1; /* "IFS= \t\n" */ | |||
26 | 26 | ||
27 | extern const char defoptindvar[] ALIGN1; /* "OPTIND=1" */ | 27 | extern const char defoptindvar[] ALIGN1; /* "OPTIND=1" */ |
28 | 28 | ||
29 | int FAST_FUNC is_well_formed_var_name(const char *s, char terminator); | ||
30 | |||
31 | /* Builtins */ | 29 | /* Builtins */ |
32 | 30 | ||
33 | struct builtin_read_params { | 31 | struct builtin_read_params { |