aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2019-05-27 11:56:52 +0100
committerRon Yorston <rmy@pobox.com>2019-05-27 11:56:52 +0100
commita61949401890cbb33a9d6c4571b51c53460ad438 (patch)
tree64dedaddb89896d5b1670a421af123670ca2120b /shell
parent03a7b173605a890e1db5177ecd5b8dd591081c41 (diff)
parentbcb1fc3e6ca6fe902610f507eaf9b0b58a5c583a (diff)
downloadbusybox-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.src5
-rw-r--r--shell/ash_test/ash-misc/export1.right1
-rwxr-xr-xshell/ash_test/ash-misc/export1.tests2
-rw-r--r--shell/ash_test/ash-vars/param_expand_default.right2
-rwxr-xr-xshell/ash_test/ash-vars/param_expand_default.tests6
-rw-r--r--shell/hush.c241
-rw-r--r--shell/hush_test/hush-misc/export1.right1
-rwxr-xr-xshell/hush_test/hush-misc/export1.tests2
-rw-r--r--shell/hush_test/hush-vars/param_expand_default.right3
-rwxr-xr-xshell/hush_test/hush-vars/param_expand_default.tests7
-rw-r--r--shell/math.c42
-rw-r--r--shell/math.h4
-rw-r--r--shell/shell_common.c343
-rw-r--r--shell/shell_common.h2
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
102config FEATURE_SH_MATH_BASE
103 bool "Support BASE#nnnn literals"
104 default y
105 depends on FEATURE_SH_MATH
106
102config FEATURE_SH_EXTRA_QUIET 107config 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 @@
1export Z='\w \\ \ \'
2echo "|$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
81:1
90: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
22f=fff 21f=fff
23echo _$f _${f-} _${f:-} _${f-word} _${f:-word} 22echo _$f _${f-} _${f:-} _${f-word} _${f:-word}
23
24set --
25set -- "${1-}"; echo 1:$#
26set --
27set -- ${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
1452static 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)
2039static void hush_exit(int exitcode) 2033static 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
2251static void handle_changed_special_names(const char *name, unsigned name_len) 2251static 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
2425static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) 2413static 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
2585static 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
2595static const char *setup_prompt_string(void) 2572static 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)
7275static void parse_and_run_string(const char *s) 7280static 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
7285static void parse_and_run_file(HFILE *fp) 7290static 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
10379static int FAST_FUNC builtin_history(char **argv UNUSED_PARAM) 10378static 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 @@
1export Z='\w \\ \ \'
2echo "|$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 @@
1hush: syntax error: unterminated ${name} 1hush: syntax error: unterminated ${name}
2hush: 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
81:1
90: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
23f=fff 21f=fff
24echo _$f _${f-} _${f:-} _${f-word} _${f:-word} 22echo _$f _${f-} _${f:-} _${f-word} _${f:-word}
23
24set --
25set -- "${1-}"; echo 1:$#
26set --
27set -- ${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
517static 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
516static arith_t FAST_FUNC 556static arith_t FAST_FUNC
517evaluate_string(arith_state_t *math_state, const char *expr) 557evaluate_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
67typedef long long arith_t; 67typedef 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
71typedef long arith_t; 70typedef 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
76typedef const char* FAST_FUNC (*arith_var_lookup_t)(const char *name); 74typedef 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
27const char defoptindvar[] ALIGN1 = "OPTIND=1"; 27const char defoptindvar[] ALIGN1 = "OPTIND=1";
28 28
29
30int 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)
384struct limits { 371struct 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
391static const struct limits limits_tbl[] = { 377static 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
420static 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"
433enum {
434 OPT_hard = (1 << 0),
435 OPT_soft = (1 << 1),
436};
437
438/* "-": treat args as parameters of option with ASCII code 1 */
439static 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 */
461static 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
501enum {
502 OPT_hard = (1 << 0),
503 OPT_soft = (1 << 1),
504 OPT_all = (1 << 2),
505};
480 506
481static void printlim(unsigned opts, const struct rlimit *limit, 507static 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,
498int FAST_FUNC 524int FAST_FUNC
499shell_builtin_ulimit(char **argv) 525shell_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
601int FAST_FUNC shell_builtin_ulimit(char **argv UNUSED_PARAM) 686int 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
27extern const char defoptindvar[] ALIGN1; /* "OPTIND=1" */ 27extern const char defoptindvar[] ALIGN1; /* "OPTIND=1" */
28 28
29int FAST_FUNC is_well_formed_var_name(const char *s, char terminator);
30
31/* Builtins */ 29/* Builtins */
32 30
33struct builtin_read_params { 31struct builtin_read_params {