aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--shell/ash.c2
-rw-r--r--shell/hush.c274
-rw-r--r--shell/lash_unused.c2
-rw-r--r--shell/msh.c2
4 files changed, 186 insertions, 94 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 1e7429cb1..cc5802c30 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -12233,7 +12233,7 @@ helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
12233 unsigned col; 12233 unsigned col;
12234 unsigned i; 12234 unsigned i;
12235 12235
12236 out1fmt("\n" 12236 out1fmt(
12237 "Built-in commands:\n" 12237 "Built-in commands:\n"
12238 "------------------\n"); 12238 "------------------\n");
12239 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) { 12239 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
diff --git a/shell/hush.c b/shell/hush.c
index f34fdd402..e82766093 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -50,7 +50,7 @@
50 * 50 *
51 * TODOs: 51 * TODOs:
52 * grep for "TODO" and fix (some of them are easy) 52 * grep for "TODO" and fix (some of them are easy)
53 * builtins: ulimit, local 53 * builtins: ulimit
54 * follow IFS rules more precisely, including update semantics 54 * follow IFS rules more precisely, including update semantics
55 * export builtin should be special, its arguments are assignments 55 * export builtin should be special, its arguments are assignments
56 * and therefore expansion of them should be "one-word" expansion: 56 * and therefore expansion of them should be "one-word" expansion:
@@ -593,56 +593,58 @@ struct built_in_command {
593#endif 593#endif
594}; 594};
595 595
596/* For now, echo and test are unconditionally enabled. 596static const struct built_in_command bltins1[] = {
597 * Maybe make it configurable? */ 597 BLTIN("." , builtin_source , "Run commands in a file"),
598static const struct built_in_command bltins[] = { 598 BLTIN(":" , builtin_true , NULL),
599 BLTIN("." , builtin_source , "Run commands in a file"),
600 BLTIN(":" , builtin_true , "No-op"),
601 BLTIN("[" , builtin_test , "Test condition"),
602#if ENABLE_HUSH_JOB 599#if ENABLE_HUSH_JOB
603 BLTIN("bg" , builtin_fg_bg , "Resume a job in the background"), 600 BLTIN("bg" , builtin_fg_bg , "Resume a job in the background"),
604#endif 601#endif
605#if ENABLE_HUSH_LOOPS 602#if ENABLE_HUSH_LOOPS
606 BLTIN("break" , builtin_break , "Exit from a loop"), 603 BLTIN("break" , builtin_break , "Exit from a loop"),
607#endif 604#endif
608 BLTIN("cd" , builtin_cd , "Change directory"), 605 BLTIN("cd" , builtin_cd , "Change directory"),
609#if ENABLE_HUSH_LOOPS 606#if ENABLE_HUSH_LOOPS
610 BLTIN("continue", builtin_continue, "Start new loop iteration"), 607 BLTIN("continue" , builtin_continue, "Start new loop iteration"),
611#endif 608#endif
612 BLTIN("echo" , builtin_echo , "Write to stdout"), 609 BLTIN("eval" , builtin_eval , "Construct and run shell command"),
613 BLTIN("eval" , builtin_eval , "Construct and run shell command"), 610 BLTIN("exec" , builtin_exec , "Execute command, don't return to shell"),
614 BLTIN("exec" , builtin_exec , "Execute command, don't return to shell"), 611 BLTIN("exit" , builtin_exit , "Exit"),
615 BLTIN("exit" , builtin_exit , "Exit"), 612 BLTIN("export" , builtin_export , "Set environment variables"),
616 BLTIN("export" , builtin_export , "Set environment variable"),
617#if ENABLE_HUSH_JOB 613#if ENABLE_HUSH_JOB
618 BLTIN("fg" , builtin_fg_bg , "Bring job into the foreground"), 614 BLTIN("fg" , builtin_fg_bg , "Bring job into the foreground"),
619#endif 615#endif
620#if ENABLE_HUSH_HELP 616#if ENABLE_HUSH_HELP
621 BLTIN("help" , builtin_help , "List shell built-in commands"), 617 BLTIN("help" , builtin_help , NULL),
622#endif 618#endif
623#if ENABLE_HUSH_JOB 619#if ENABLE_HUSH_JOB
624 BLTIN("jobs" , builtin_jobs , "List active jobs"), 620 BLTIN("jobs" , builtin_jobs , "List jobs"),
625#endif 621#endif
626#if ENABLE_HUSH_LOCAL 622#if ENABLE_HUSH_LOCAL
627 BLTIN("local" , builtin_local , "Set local variable"), 623 BLTIN("local" , builtin_local , "Set local variables"),
628#endif 624#endif
629#if HUSH_DEBUG 625#if HUSH_DEBUG
630 BLTIN("memleak" , builtin_memleak , "Debug tool"), 626 BLTIN("memleak" , builtin_memleak , NULL),
631#endif 627#endif
632 BLTIN("pwd" , builtin_pwd , "Print current directory"), 628 BLTIN("read" , builtin_read , "Input into variable"),
633 BLTIN("read" , builtin_read , "Input environment variable"),
634#if ENABLE_HUSH_FUNCTIONS 629#if ENABLE_HUSH_FUNCTIONS
635 BLTIN("return" , builtin_return , "Return from a function"), 630 BLTIN("return" , builtin_return , "Return from a function"),
636#endif 631#endif
637 BLTIN("set" , builtin_set , "Set/unset shell local variables"), 632 BLTIN("set" , builtin_set , "Set/unset positional parameters"),
638 BLTIN("shift" , builtin_shift , "Shift positional parameters"), 633 BLTIN("shift" , builtin_shift , "Shift positional parameters"),
639 BLTIN("test" , builtin_test , "Test condition"), 634 BLTIN("trap" , builtin_trap , "Trap signals"),
640 BLTIN("trap" , builtin_trap , "Trap signals"), 635 BLTIN("type" , builtin_type , "Write a description of command type"),
641 BLTIN("type" , builtin_type , "Write a description of command type"), 636// BLTIN("ulimit" , builtin_ulimit , "Control resource limits"),
642// BLTIN("ulimit" , builtin_ulimit , "Control resource limits"), 637 BLTIN("umask" , builtin_umask , "Set file creation mask"),
643 BLTIN("umask" , builtin_umask , "Set file creation mask"), 638 BLTIN("unset" , builtin_unset , "Unset variables"),
644 BLTIN("unset" , builtin_unset , "Unset environment variable"), 639 BLTIN("wait" , builtin_wait , "Wait for process"),
645 BLTIN("wait" , builtin_wait , "Wait for process"), 640};
641/* For now, echo and test are unconditionally enabled.
642 * Maybe make it configurable? */
643static const struct built_in_command bltins2[] = {
644 BLTIN("[" , builtin_test , NULL),
645 BLTIN("echo" , builtin_echo , NULL),
646 BLTIN("pwd" , builtin_pwd , NULL),
647 BLTIN("test" , builtin_test , NULL),
646}; 648};
647 649
648 650
@@ -1231,15 +1233,17 @@ static int check_and_run_traps(int sig)
1231} 1233}
1232 1234
1233 1235
1234static const char *set_cwd(void) 1236static const char *get_cwd(int force)
1235{ 1237{
1236 /* xrealloc_getcwd_or_warn(arg) calls free(arg), 1238 if (force || G.cwd == NULL) {
1237 * we must not try to free(bb_msg_unknown) */ 1239 /* xrealloc_getcwd_or_warn(arg) calls free(arg),
1238 if (G.cwd == bb_msg_unknown) 1240 * we must not try to free(bb_msg_unknown) */
1239 G.cwd = NULL; 1241 if (G.cwd == bb_msg_unknown)
1240 G.cwd = xrealloc_getcwd_or_warn((char *)G.cwd); 1242 G.cwd = NULL;
1241 if (!G.cwd) 1243 G.cwd = xrealloc_getcwd_or_warn((char *)G.cwd);
1242 G.cwd = bb_msg_unknown; 1244 if (!G.cwd)
1245 G.cwd = bb_msg_unknown;
1246 }
1243 return G.cwd; 1247 return G.cwd;
1244} 1248}
1245 1249
@@ -1579,7 +1583,7 @@ static const char* setup_prompt_string(int promptmode)
1579 /* Set up the prompt */ 1583 /* Set up the prompt */
1580 if (promptmode == 0) { /* PS1 */ 1584 if (promptmode == 0) { /* PS1 */
1581 free((char*)G.PS1); 1585 free((char*)G.PS1);
1582 G.PS1 = xasprintf("%s %c ", G.cwd, (geteuid() != 0) ? '$' : '#'); 1586 G.PS1 = xasprintf("%s %c ", get_cwd(0), (geteuid() != 0) ? '$' : '#');
1583 prompt_str = G.PS1; 1587 prompt_str = G.PS1;
1584 } else 1588 } else
1585 prompt_str = G.PS2; 1589 prompt_str = G.PS2;
@@ -2511,7 +2515,9 @@ static char **expand_assignments(char **argv, int count)
2511 2515
2512#if BB_MMU 2516#if BB_MMU
2513/* never called */ 2517/* never called */
2514void re_execute_shell(char ***to_free, const char *s, char *argv0, char **argv); 2518void re_execute_shell(char ***to_free, const char *s,
2519 char *g_argv0, char **g_argv,
2520 char **builtin_argv) NORETURN;
2515 2521
2516static void reset_traps_to_defaults(void) 2522static void reset_traps_to_defaults(void)
2517{ 2523{
@@ -2563,10 +2569,14 @@ static void reset_traps_to_defaults(void)
2563 2569
2564#else /* !BB_MMU */ 2570#else /* !BB_MMU */
2565 2571
2566static void re_execute_shell(char ***to_free, const char *s, char *g_argv0, char **g_argv) NORETURN; 2572static void re_execute_shell(char ***to_free, const char *s,
2567static void re_execute_shell(char ***to_free, const char *s, char *g_argv0, char **g_argv) 2573 char *g_argv0, char **g_argv,
2574 char **builtin_argv) NORETURN;
2575static void re_execute_shell(char ***to_free, const char *s,
2576 char *g_argv0, char **g_argv,
2577 char **builtin_argv)
2568{ 2578{
2569 char param_buf[sizeof("-$%x:%x:%x:%x") + sizeof(unsigned) * 4]; 2579 char param_buf[sizeof("-$%x:%x:%x:%x:%x") + sizeof(unsigned) * 2];
2570 char *heredoc_argv[4]; 2580 char *heredoc_argv[4];
2571 struct variable *cur; 2581 struct variable *cur;
2572#if ENABLE_HUSH_FUNCTIONS 2582#if ENABLE_HUSH_FUNCTIONS
@@ -2585,16 +2595,22 @@ static void re_execute_shell(char ***to_free, const char *s, char *g_argv0, char
2585 goto do_exec; 2595 goto do_exec;
2586 } 2596 }
2587 2597
2588 sprintf(param_buf, "-$%x:%x:%x" IF_HUSH_LOOPS(":%x") 2598 cnt = 0;
2599 pp = builtin_argv;
2600 if (pp) while (*pp++)
2601 cnt++;
2602
2603 sprintf(param_buf, "-$%x:%x:%x:%x" IF_HUSH_LOOPS(":%x")
2589 , (unsigned) G.root_pid 2604 , (unsigned) G.root_pid
2590 , (unsigned) G.last_bg_pid 2605 , (unsigned) G.last_bg_pid
2591 , (unsigned) G.last_exitcode 2606 , (unsigned) G.last_exitcode
2607 , cnt
2592 IF_HUSH_LOOPS(, G.depth_of_loop) 2608 IF_HUSH_LOOPS(, G.depth_of_loop)
2593 ); 2609 );
2594 /* 1:hush 2:-$<pid>:<pid>:<exitcode>:<depth> <vars...> <funcs...> 2610 /* 1:hush 2:-$<pid>:<pid>:<exitcode>:<depth> <vars...> <funcs...>
2595 * 3:-c 4:<cmd> 5:<arg0> <argN...> 6:NULL 2611 * 3:-c 4:<cmd> 5:<arg0> <argN...> 6:NULL
2596 */ 2612 */
2597 cnt = 6; 2613 cnt += 6;
2598 for (cur = G.top_var; cur; cur = cur->next) { 2614 for (cur = G.top_var; cur; cur = cur->next) {
2599 if (!cur->flg_export || cur->flg_read_only) 2615 if (!cur->flg_export || cur->flg_read_only)
2600 cnt += 2; 2616 cnt += 2;
@@ -2648,6 +2664,11 @@ static void re_execute_shell(char ***to_free, const char *s, char *g_argv0, char
2648 */ 2664 */
2649 *pp++ = (char *) "-c"; 2665 *pp++ = (char *) "-c";
2650 *pp++ = (char *) s; 2666 *pp++ = (char *) s;
2667 if (builtin_argv) {
2668 while (*++builtin_argv)
2669 *pp++ = *builtin_argv;
2670 *pp++ = (char *) "";
2671 }
2651 *pp++ = g_argv0; 2672 *pp++ = g_argv0;
2652 while (*g_argv) 2673 while (*g_argv)
2653 *pp++ = *g_argv++; 2674 *pp++ = *g_argv++;
@@ -2735,7 +2756,7 @@ static void setup_heredoc(struct redir_struct *redir)
2735#else 2756#else
2736 /* Delegate blocking writes to another process */ 2757 /* Delegate blocking writes to another process */
2737 xmove_fd(pair.wr, STDOUT_FILENO); 2758 xmove_fd(pair.wr, STDOUT_FILENO);
2738 re_execute_shell(&to_free, heredoc, NULL, NULL); 2759 re_execute_shell(&to_free, heredoc, NULL, NULL, NULL);
2739#endif 2760#endif
2740 } 2761 }
2741 /* parent */ 2762 /* parent */
@@ -2958,17 +2979,31 @@ static char *find_in_path(const char *arg)
2958 return ret; 2979 return ret;
2959} 2980}
2960 2981
2961static const struct built_in_command* find_builtin(const char *name) 2982static const struct built_in_command* find_builtin_helper(const char *name,
2983 const struct built_in_command *x,
2984 const struct built_in_command *end)
2962{ 2985{
2963 const struct built_in_command *x; 2986 while (x != end) {
2964 for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) { 2987 if (strcmp(name, x->cmd) != 0) {
2965 if (strcmp(name, x->cmd) != 0) 2988 x++;
2966 continue; 2989 continue;
2990 }
2967 debug_printf_exec("found builtin '%s'\n", name); 2991 debug_printf_exec("found builtin '%s'\n", name);
2968 return x; 2992 return x;
2969 } 2993 }
2970 return NULL; 2994 return NULL;
2971} 2995}
2996static const struct built_in_command* find_builtin1(const char *name)
2997{
2998 return find_builtin_helper(name, bltins1, &bltins1[ARRAY_SIZE(bltins1)]);
2999}
3000static const struct built_in_command* find_builtin(const char *name)
3001{
3002 const struct built_in_command *x = find_builtin1(name);
3003 if (x)
3004 return x;
3005 return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]);
3006}
2972 3007
2973#if ENABLE_HUSH_FUNCTIONS 3008#if ENABLE_HUSH_FUNCTIONS
2974static const struct function *find_function(const char *name) 3009static const struct function *find_function(const char *name)
@@ -3084,7 +3119,8 @@ static void exec_function(nommu_save_t *nommu_save,
3084 re_execute_shell(&nommu_save->argv_from_re_execing, 3119 re_execute_shell(&nommu_save->argv_from_re_execing,
3085 funcp->body_as_string, 3120 funcp->body_as_string,
3086 G.global_argv[0], 3121 G.global_argv[0],
3087 argv + 1); 3122 argv + 1,
3123 NULL);
3088# endif 3124# endif
3089} 3125}
3090 3126
@@ -3148,6 +3184,37 @@ static int run_function(const struct function *funcp, char **argv)
3148#endif /* ENABLE_HUSH_FUNCTIONS */ 3184#endif /* ENABLE_HUSH_FUNCTIONS */
3149 3185
3150 3186
3187# if BB_MMU
3188#define exec_builtin(nommu_save, x, argv) \
3189 exec_builtin(x, argv)
3190# else
3191#define exec_builtin(nommu_save, x, argv) \
3192 exec_builtin(nommu_save, argv)
3193# endif
3194static void exec_builtin(nommu_save_t *nommu_save,
3195 const struct built_in_command *x,
3196 char **argv) NORETURN;
3197static void exec_builtin(nommu_save_t *nommu_save,
3198 const struct built_in_command *x,
3199 char **argv)
3200{
3201# if BB_MMU
3202 int rcode = x->function(argv);
3203 fflush(NULL);
3204 _exit(rcode);
3205# else
3206 /* On NOMMU, we must never block!
3207 * Example: { sleep 99 | read line; } & echo Ok
3208 */
3209 re_execute_shell(&nommu_save->argv_from_re_execing,
3210 argv[0],
3211 G.global_argv[0],
3212 G.global_argv + 1,
3213 argv);
3214# endif
3215}
3216
3217
3151#if BB_MMU 3218#if BB_MMU
3152#define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \ 3219#define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \
3153 pseudo_exec_argv(argv, assignment_cnt, argv_expanded) 3220 pseudo_exec_argv(argv, assignment_cnt, argv_expanded)
@@ -3197,27 +3264,22 @@ static void pseudo_exec_argv(nommu_save_t *nommu_save,
3197 goto skip; 3264 goto skip;
3198#endif 3265#endif
3199 3266
3200 /* On NOMMU, we must never block!
3201 * Example: { sleep 99999 | read line } & echo Ok
3202 * read builtin will block on read syscall, leaving parent blocked
3203 * in vfork. Therefore we can't do this:
3204 */
3205#if BB_MMU
3206 /* Check if the command matches any of the builtins. 3267 /* Check if the command matches any of the builtins.
3207 * Depending on context, this might be redundant. But it's 3268 * Depending on context, this might be redundant. But it's
3208 * easier to waste a few CPU cycles than it is to figure out 3269 * easier to waste a few CPU cycles than it is to figure out
3209 * if this is one of those cases. 3270 * if this is one of those cases.
3210 */ 3271 */
3211 { 3272 {
3212 int rcode; 3273 /* On NOMMU, it is more expensive to re-execute shell
3213 const struct built_in_command *x = find_builtin(argv[0]); 3274 * just in order to run echo or test builtin.
3275 * It's better to skip it here and run corresponding
3276 * non-builtin later. */
3277 const struct built_in_command *x;
3278 x = BB_MMU ? find_builtin(argv[0]) : find_builtin1(argv[0]);
3214 if (x) { 3279 if (x) {
3215 rcode = x->function(argv); 3280 exec_builtin(nommu_save, x, argv);
3216 fflush(NULL);
3217 _exit(rcode);
3218 } 3281 }
3219 } 3282 }
3220#endif
3221#if ENABLE_HUSH_FUNCTIONS 3283#if ENABLE_HUSH_FUNCTIONS
3222 /* Check if the command matches any functions */ 3284 /* Check if the command matches any functions */
3223 { 3285 {
@@ -3292,7 +3354,8 @@ static void pseudo_exec(nommu_save_t *nommu_save,
3292 re_execute_shell(&nommu_save->argv_from_re_execing, 3354 re_execute_shell(&nommu_save->argv_from_re_execing,
3293 command->group_as_string, 3355 command->group_as_string,
3294 G.global_argv[0], 3356 G.global_argv[0],
3295 G.global_argv + 1); 3357 G.global_argv + 1,
3358 NULL);
3296#endif 3359#endif
3297 } 3360 }
3298 3361
@@ -5030,7 +5093,8 @@ static FILE *generate_stream_from_string(const char *s)
5030 re_execute_shell(&to_free, 5093 re_execute_shell(&to_free,
5031 s, 5094 s,
5032 G.global_argv[0], 5095 G.global_argv[0],
5033 G.global_argv + 1); 5096 G.global_argv + 1,
5097 NULL);
5034#endif 5098#endif
5035 } 5099 }
5036 5100
@@ -6240,6 +6304,7 @@ int hush_main(int argc, char **argv)
6240 }; 6304 };
6241 int signal_mask_is_inited = 0; 6305 int signal_mask_is_inited = 0;
6242 int opt; 6306 int opt;
6307 unsigned builtin_argc;
6243 char **e; 6308 char **e;
6244 struct variable *cur_var; 6309 struct variable *cur_var;
6245 6310
@@ -6277,7 +6342,6 @@ int hush_main(int argc, char **argv)
6277 G.global_argc = argc; 6342 G.global_argc = argc;
6278 G.global_argv = argv; 6343 G.global_argv = argv;
6279 /* Initialize some more globals to non-zero values */ 6344 /* Initialize some more globals to non-zero values */
6280 set_cwd();
6281 cmdedit_update_prompt(); 6345 cmdedit_update_prompt();
6282 6346
6283 if (setjmp(die_jmp)) { 6347 if (setjmp(die_jmp)) {
@@ -6297,6 +6361,7 @@ int hush_main(int argc, char **argv)
6297 6361
6298 /* Parse options */ 6362 /* Parse options */
6299 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */ 6363 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */
6364 builtin_argc = 0;
6300 while (1) { 6365 while (1) {
6301 opt = getopt(argc, argv, "c:xins" 6366 opt = getopt(argc, argv, "c:xins"
6302#if !BB_MMU 6367#if !BB_MMU
@@ -6310,15 +6375,40 @@ int hush_main(int argc, char **argv)
6310 break; 6375 break;
6311 switch (opt) { 6376 switch (opt) {
6312 case 'c': 6377 case 'c':
6378 /* Possibilities:
6379 * sh ... -c 'script'
6380 * sh ... -c 'script' ARG0 [ARG1...]
6381 * On NOMMU, if builtin_argc != 0,
6382 * sh ... -c 'builtin' [BARGV...] "" ARG0 [ARG1...]
6383 * "" needs to be replaced with NULL
6384 * and BARGV vector fed to builtin function.
6385 * Note: this form never happens:
6386 * sh ... -c 'builtin' [BARGV...] ""
6387 */
6313 if (!G.root_pid) 6388 if (!G.root_pid)
6314 G.root_pid = getpid(); 6389 G.root_pid = getpid();
6315 G.global_argv = argv + optind; 6390 G.global_argv = argv + optind;
6316 if (!argv[optind]) {
6317 /* -c 'script' (no params): prevent empty $0 */
6318 *--G.global_argv = argv[0];
6319 optind--;
6320 } /* else -c 'script' PAR0 PAR1: $0 is PAR0 */
6321 G.global_argc = argc - optind; 6391 G.global_argc = argc - optind;
6392 if (builtin_argc) {
6393 /* -c 'builtin' [BARGV...] "" ARG0 [ARG1...] */
6394 const struct built_in_command *x;
6395
6396 block_signals(0); /* 0: called 1st time */
6397 x = find_builtin(optarg);
6398 if (x) { /* paranoia */
6399 G.global_argc -= builtin_argc; /* skip [BARGV...] "" */
6400 G.global_argv += builtin_argc;
6401 G.global_argv[-1] = NULL; /* replace "" */
6402 G.last_exitcode = x->function(argv + optind - 1);
6403 }
6404 goto final_return;
6405 }
6406 if (!G.global_argv[0]) {
6407 /* -c 'script' (no params): prevent empty $0 */
6408 G.global_argv--; /* points to argv[i] of 'script' */
6409 G.global_argv[0] = argv[0];
6410 G.global_argc--;
6411 } /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */
6322 block_signals(0); /* 0: called 1st time */ 6412 block_signals(0); /* 0: called 1st time */
6323 parse_and_run_string(optarg); 6413 parse_and_run_string(optarg);
6324 goto final_return; 6414 goto final_return;
@@ -6341,6 +6431,8 @@ int hush_main(int argc, char **argv)
6341 G.last_bg_pid = bb_strtou(optarg, &optarg, 16); 6431 G.last_bg_pid = bb_strtou(optarg, &optarg, 16);
6342 optarg++; 6432 optarg++;
6343 G.last_exitcode = bb_strtou(optarg, &optarg, 16); 6433 G.last_exitcode = bb_strtou(optarg, &optarg, 16);
6434 optarg++;
6435 builtin_argc = bb_strtou(optarg, &optarg, 16);
6344# if ENABLE_HUSH_LOOPS 6436# if ENABLE_HUSH_LOOPS
6345 optarg++; 6437 optarg++;
6346 G.depth_of_loop = bb_strtou(optarg, &optarg, 16); 6438 G.depth_of_loop = bb_strtou(optarg, &optarg, 16);
@@ -6617,7 +6709,7 @@ static int FAST_FUNC builtin_cd(char **argv)
6617 bb_perror_msg("cd: %s", newdir); 6709 bb_perror_msg("cd: %s", newdir);
6618 return EXIT_FAILURE; 6710 return EXIT_FAILURE;
6619 } 6711 }
6620 set_cwd(); 6712 get_cwd(1);
6621 return EXIT_SUCCESS; 6713 return EXIT_SUCCESS;
6622} 6714}
6623 6715
@@ -6663,17 +6755,16 @@ static int FAST_FUNC builtin_exit(char **argv)
6663 6755
6664static void print_escaped(const char *s) 6756static void print_escaped(const char *s)
6665{ 6757{
6758 if (*s == '\'')
6759 goto squote;
6666 do { 6760 do {
6667 if (*s != '\'') { 6761 const char *p = strchrnul(s, '\'');
6668 const char *p; 6762 /* print 'xxxx', possibly just '' */
6669 6763 printf("'%.*s'", (int)(p - s), s);
6670 p = strchrnul(s, '\''); 6764 if (*p == '\0')
6671 /* print 'xxxx', possibly just '' */ 6765 break;
6672 printf("'%.*s'", (int)(p - s), s); 6766 s = p;
6673 if (*p == '\0') 6767 squote:
6674 break;
6675 s = p;
6676 }
6677 /* s points to '; print "'''...'''" */ 6768 /* s points to '; print "'''...'''" */
6678 putchar('"'); 6769 putchar('"');
6679 do putchar('\''); while (*++s == '\''); 6770 do putchar('\''); while (*++s == '\'');
@@ -6980,13 +7071,14 @@ static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM)
6980{ 7071{
6981 const struct built_in_command *x; 7072 const struct built_in_command *x;
6982 7073
6983 printf("\n" 7074 printf(
6984 "Built-in commands:\n" 7075 "Built-in commands:\n"
6985 "------------------\n"); 7076 "------------------\n");
6986 for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) { 7077 for (x = bltins1; x != &bltins1[ARRAY_SIZE(bltins1)]; x++) {
6987 printf("%s\t%s\n", x->cmd, x->descr); 7078 if (x->descr)
7079 printf("%s\t%s\n", x->cmd, x->descr);
6988 } 7080 }
6989 printf("\n\n"); 7081 bb_putchar('\n');
6990 return EXIT_SUCCESS; 7082 return EXIT_SUCCESS;
6991} 7083}
6992#endif 7084#endif
@@ -7041,7 +7133,7 @@ static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM)
7041 7133
7042static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM) 7134static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM)
7043{ 7135{
7044 puts(set_cwd()); 7136 puts(get_cwd(0));
7045 return EXIT_SUCCESS; 7137 return EXIT_SUCCESS;
7046} 7138}
7047 7139
diff --git a/shell/lash_unused.c b/shell/lash_unused.c
index 21ea5471b..2574987c5 100644
--- a/shell/lash_unused.c
+++ b/shell/lash_unused.c
@@ -312,7 +312,7 @@ static int builtin_help(struct child_prog UNUSED_PARAM *dummy)
312{ 312{
313 const struct built_in_command *x; 313 const struct built_in_command *x;
314 314
315 printf("\n" 315 printf(
316 "Built-in commands:\n" 316 "Built-in commands:\n"
317 "------------------\n"); 317 "------------------\n");
318 for (x = bltins; x <= &VEC_LAST(bltins); x++) { 318 for (x = bltins; x <= &VEC_LAST(bltins); x++) {
diff --git a/shell/msh.c b/shell/msh.c
index dffacf02a..fe85a8170 100644
--- a/shell/msh.c
+++ b/shell/msh.c
@@ -3174,7 +3174,7 @@ static int dohelp(struct op *t UNUSED_PARAM, char **args UNUSED_PARAM)
3174 int col; 3174 int col;
3175 const struct builtincmd *x; 3175 const struct builtincmd *x;
3176 3176
3177 printf("\n" 3177 printf(
3178 "Built-in commands:\n" 3178 "Built-in commands:\n"
3179 "------------------\n"); 3179 "------------------\n");
3180 3180