diff options
-rw-r--r-- | shell/hush.c | 592 |
1 files changed, 295 insertions, 297 deletions
diff --git a/shell/hush.c b/shell/hush.c index c0325cf5e..7cce89183 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -8832,6 +8832,30 @@ static int FAST_FUNC builtin_printf(char **argv) } #endif +#if ENABLE_HUSH_HELP +static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM) +{ + const struct built_in_command *x; + + printf( + "Built-in commands:\n" + "------------------\n"); + for (x = bltins1; x != &bltins1[ARRAY_SIZE(bltins1)]; x++) { + if (x->b_descr) + printf("%-10s%s\n", x->b_cmd, x->b_descr); + } + return EXIT_SUCCESS; +} +#endif + +#if MAX_HISTORY && ENABLE_FEATURE_EDITING +static int FAST_FUNC builtin_history(char **argv UNUSED_PARAM) +{ + show_history(G.line_input_state); + return EXIT_SUCCESS; +} +#endif + static char **skip_dash_dash(char **argv) { argv++; @@ -8840,24 +8864,6 @@ static char **skip_dash_dash(char **argv) return argv; } -static int FAST_FUNC builtin_eval(char **argv) -{ - int rcode = EXIT_SUCCESS; - - argv = skip_dash_dash(argv); - if (*argv) { - char *str = expand_strvec_to_string(argv); - /* bash: - * eval "echo Hi; done" ("done" is syntax error): - * "echo Hi" will not execute too. - */ - parse_and_run_string(str); - free(str); - rcode = G.last_exitcode; - } - return rcode; -} - static int FAST_FUNC builtin_cd(char **argv) { const char *newdir; @@ -8885,6 +8891,30 @@ static int FAST_FUNC builtin_cd(char **argv) return EXIT_SUCCESS; } +static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM) +{ + puts(get_cwd(0)); + return EXIT_SUCCESS; +} + +static int FAST_FUNC builtin_eval(char **argv) +{ + int rcode = EXIT_SUCCESS; + + argv = skip_dash_dash(argv); + if (*argv) { + char *str = expand_strvec_to_string(argv); + /* bash: + * eval "echo Hi; done" ("done" is syntax error): + * "echo Hi" will not execute too. + */ + parse_and_run_string(str); + free(str); + rcode = G.last_exitcode; + } + return rcode; +} + static int FAST_FUNC builtin_exec(char **argv) { argv = skip_dash_dash(argv); @@ -8930,6 +8960,147 @@ static int FAST_FUNC builtin_exit(char **argv) hush_exit(xatoi(argv[0]) & 0xff); } +#if ENABLE_HUSH_TYPE +/* http://www.opengroup.org/onlinepubs/9699919799/utilities/type.html */ +static int FAST_FUNC builtin_type(char **argv) +{ + int ret = EXIT_SUCCESS; + + while (*++argv) { + const char *type; + char *path = NULL; + + if (0) {} /* make conditional compile easier below */ + /*else if (find_alias(*argv)) + type = "an alias";*/ +#if ENABLE_HUSH_FUNCTIONS + else if (find_function(*argv)) + type = "a function"; +#endif + else if (find_builtin(*argv)) + type = "a shell builtin"; + else if ((path = find_in_path(*argv)) != NULL) + type = path; + else { + bb_error_msg("type: %s: not found", *argv); + ret = EXIT_FAILURE; + continue; + } + + printf("%s is %s\n", *argv, type); + free(path); + } + + return ret; +} +#endif + +#if ENABLE_HUSH_READ +/* Interruptibility of read builtin in bash + * (tested on bash-4.2.8 by sending signals (not by ^C)): + * + * Empty trap makes read ignore corresponding signal, for any signal. + * + * SIGINT: + * - terminates non-interactive shell; + * - interrupts read in interactive shell; + * if it has non-empty trap: + * - executes trap and returns to command prompt in interactive shell; + * - executes trap and returns to read in non-interactive shell; + * SIGTERM: + * - is ignored (does not interrupt) read in interactive shell; + * - terminates non-interactive shell; + * if it has non-empty trap: + * - executes trap and returns to read; + * SIGHUP: + * - terminates shell (regardless of interactivity); + * if it has non-empty trap: + * - executes trap and returns to read; + */ +static int FAST_FUNC builtin_read(char **argv) +{ + const char *r; + char *opt_n = NULL; + char *opt_p = NULL; + char *opt_t = NULL; + char *opt_u = NULL; + const char *ifs; + int read_flags; + + /* "!": do not abort on errors. + * Option string must start with "sr" to match BUILTIN_READ_xxx + */ + read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u); + if (read_flags == (uint32_t)-1) + return EXIT_FAILURE; + argv += optind; + ifs = get_local_var_value("IFS"); /* can be NULL */ + + again: + r = shell_builtin_read(set_local_var_from_halves, + argv, + ifs, + read_flags, + opt_n, + opt_p, + opt_t, + opt_u + ); + + if ((uintptr_t)r == 1 && errno == EINTR) { + unsigned sig = check_and_run_traps(); + if (sig && sig != SIGINT) + goto again; + } + + if ((uintptr_t)r > 1) { + bb_error_msg("%s", r); + r = (char*)(uintptr_t)1; + } + + return (uintptr_t)r; +} +#endif + +#if ENABLE_HUSH_UMASK +static int FAST_FUNC builtin_umask(char **argv) +{ + int rc; + mode_t mask; + + rc = 1; + mask = umask(0); + argv = skip_dash_dash(argv); + if (argv[0]) { + mode_t old_mask = mask; + + /* numeric umasks are taken as-is */ + /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */ + if (!isdigit(argv[0][0])) + mask ^= 0777; + mask = bb_parse_mode(argv[0], mask); + if (!isdigit(argv[0][0])) + mask ^= 0777; + if ((unsigned)mask > 0777) { + mask = old_mask; + /* bash messages: + * bash: umask: 'q': invalid symbolic mode operator + * bash: umask: 999: octal number out of range + */ + bb_error_msg("%s: invalid mode '%s'", "umask", argv[0]); + rc = 0; + } + } else { + /* Mimic bash */ + printf("%04o\n", (unsigned) mask); + /* fall through and restore mask which we set to 0 */ + } + umask(mask); + + return !rc; /* rc != 0 - success */ +} +#endif + #if ENABLE_HUSH_EXPORT || ENABLE_HUSH_TRAP static void print_escaped(const char *s) { @@ -9218,72 +9389,60 @@ static int FAST_FUNC builtin_shift(char **argv) return EXIT_FAILURE; } -#if ENABLE_HUSH_READ -/* Interruptibility of read builtin in bash - * (tested on bash-4.2.8 by sending signals (not by ^C)): - * - * Empty trap makes read ignore corresponding signal, for any signal. - * - * SIGINT: - * - terminates non-interactive shell; - * - interrupts read in interactive shell; - * if it has non-empty trap: - * - executes trap and returns to command prompt in interactive shell; - * - executes trap and returns to read in non-interactive shell; - * SIGTERM: - * - is ignored (does not interrupt) read in interactive shell; - * - terminates non-interactive shell; - * if it has non-empty trap: - * - executes trap and returns to read; - * SIGHUP: - * - terminates shell (regardless of interactivity); - * if it has non-empty trap: - * - executes trap and returns to read; - */ -static int FAST_FUNC builtin_read(char **argv) +static int FAST_FUNC builtin_source(char **argv) { - const char *r; - char *opt_n = NULL; - char *opt_p = NULL; - char *opt_t = NULL; - char *opt_u = NULL; - const char *ifs; - int read_flags; + char *arg_path, *filename; + FILE *input; + save_arg_t sv; + char *args_need_save; +#if ENABLE_HUSH_FUNCTIONS + smallint sv_flg; +#endif - /* "!": do not abort on errors. - * Option string must start with "sr" to match BUILTIN_READ_xxx - */ - read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u); - if (read_flags == (uint32_t)-1) + argv = skip_dash_dash(argv); + filename = argv[0]; + if (!filename) { + /* bash says: "bash: .: filename argument required" */ + return 2; /* bash compat */ + } + arg_path = NULL; + if (!strchr(filename, '/')) { + arg_path = find_in_path(filename); + if (arg_path) + filename = arg_path; + } + input = remember_FILE(fopen_or_warn(filename, "r")); + free(arg_path); + if (!input) { + /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */ + /* POSIX: non-interactive shell should abort here, + * not merely fail. So far no one complained :) + */ return EXIT_FAILURE; - argv += optind; - ifs = get_local_var_value("IFS"); /* can be NULL */ + } - again: - r = shell_builtin_read(set_local_var_from_halves, - argv, - ifs, - read_flags, - opt_n, - opt_p, - opt_t, - opt_u - ); +#if ENABLE_HUSH_FUNCTIONS + sv_flg = G_flag_return_in_progress; + /* "we are inside sourced file, ok to use return" */ + G_flag_return_in_progress = -1; +#endif + args_need_save = argv[1]; /* used as a boolean variable */ + if (args_need_save) + save_and_replace_G_args(&sv, argv); - if ((uintptr_t)r == 1 && errno == EINTR) { - unsigned sig = check_and_run_traps(); - if (sig && sig != SIGINT) - goto again; - } + /* "false; . ./empty_line; echo Zero:$?" should print 0 */ + G.last_exitcode = 0; + parse_and_run_file(input); + fclose_and_forget(input); - if ((uintptr_t)r > 1) { - bb_error_msg("%s", r); - r = (char*)(uintptr_t)1; - } + if (args_need_save) /* can't use argv[1] instead: "shift" can mangle it */ + restore_G_args(&sv, argv); +#if ENABLE_HUSH_FUNCTIONS + G_flag_return_in_progress = sv_flg; +#endif - return (uintptr_t)r; + return G.last_exitcode; } -#endif #if ENABLE_HUSH_TRAP static int FAST_FUNC builtin_trap(char **argv) @@ -9377,41 +9536,6 @@ static int FAST_FUNC builtin_trap(char **argv) } #endif -#if ENABLE_HUSH_TYPE -/* http://www.opengroup.org/onlinepubs/9699919799/utilities/type.html */ -static int FAST_FUNC builtin_type(char **argv) -{ - int ret = EXIT_SUCCESS; - - while (*++argv) { - const char *type; - char *path = NULL; - - if (0) {} /* make conditional compile easier below */ - /*else if (find_alias(*argv)) - type = "an alias";*/ -#if ENABLE_HUSH_FUNCTIONS - else if (find_function(*argv)) - type = "a function"; -#endif - else if (find_builtin(*argv)) - type = "a shell builtin"; - else if ((path = find_in_path(*argv)) != NULL) - type = path; - else { - bb_error_msg("type: %s: not found", *argv); - ret = EXIT_FAILURE; - continue; - } - - printf("%s is %s\n", *argv, type); - free(path); - } - - return ret; -} -#endif - #if ENABLE_HUSH_JOB static struct pipe *parse_jobspec(const char *str) { @@ -9441,6 +9565,23 @@ static struct pipe *parse_jobspec(const char *str) return NULL; } +static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM) +{ + struct pipe *job; + const char *status_string; + + checkjobs(NULL, 0 /*(no pid to wait for)*/); + for (job = G.job_list; job; job = job->next) { + if (job->alive_cmds == job->stopped_cmds) + status_string = "Stopped"; + else + status_string = "Running"; + + printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext); + } + return EXIT_SUCCESS; +} + /* built-in 'fg' and 'bg' handler */ static int FAST_FUNC builtin_fg_bg(char **argv) { @@ -9496,192 +9637,6 @@ static int FAST_FUNC builtin_fg_bg(char **argv) } #endif -#if ENABLE_HUSH_HELP -static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM) -{ - const struct built_in_command *x; - - printf( - "Built-in commands:\n" - "------------------\n"); - for (x = bltins1; x != &bltins1[ARRAY_SIZE(bltins1)]; x++) { - if (x->b_descr) - printf("%-10s%s\n", x->b_cmd, x->b_descr); - } - return EXIT_SUCCESS; -} -#endif - -#if MAX_HISTORY && ENABLE_FEATURE_EDITING -static int FAST_FUNC builtin_history(char **argv UNUSED_PARAM) -{ - show_history(G.line_input_state); - return EXIT_SUCCESS; -} -#endif - -#if ENABLE_HUSH_JOB -static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM) -{ - struct pipe *job; - const char *status_string; - - checkjobs(NULL, 0 /*(no pid to wait for)*/); - for (job = G.job_list; job; job = job->next) { - if (job->alive_cmds == job->stopped_cmds) - status_string = "Stopped"; - else - status_string = "Running"; - - printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext); - } - return EXIT_SUCCESS; -} -#endif - -#if ENABLE_HUSH_MEMLEAK -static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM) -{ - void *p; - unsigned long l; - -# ifdef M_TRIM_THRESHOLD - /* Optional. Reduces probability of false positives */ - malloc_trim(0); -# endif - /* Crude attempt to find where "free memory" starts, - * sans fragmentation. */ - p = malloc(240); - l = (unsigned long)p; - free(p); - p = malloc(3400); - if (l < (unsigned long)p) l = (unsigned long)p; - free(p); - - -# if 0 /* debug */ - { - struct mallinfo mi = mallinfo(); - printf("top alloc:0x%lx malloced:%d+%d=%d\n", l, - mi.arena, mi.hblkhd, mi.arena + mi.hblkhd); - } -# endif - - if (!G.memleak_value) - G.memleak_value = l; - - l -= G.memleak_value; - if ((long)l < 0) - l = 0; - l /= 1024; - if (l > 127) - l = 127; - - /* Exitcode is "how many kilobytes we leaked since 1st call" */ - return l; -} -#endif - -static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM) -{ - puts(get_cwd(0)); - return EXIT_SUCCESS; -} - -static int FAST_FUNC builtin_source(char **argv) -{ - char *arg_path, *filename; - FILE *input; - save_arg_t sv; - char *args_need_save; -#if ENABLE_HUSH_FUNCTIONS - smallint sv_flg; -#endif - - argv = skip_dash_dash(argv); - filename = argv[0]; - if (!filename) { - /* bash says: "bash: .: filename argument required" */ - return 2; /* bash compat */ - } - arg_path = NULL; - if (!strchr(filename, '/')) { - arg_path = find_in_path(filename); - if (arg_path) - filename = arg_path; - } - input = remember_FILE(fopen_or_warn(filename, "r")); - free(arg_path); - if (!input) { - /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */ - /* POSIX: non-interactive shell should abort here, - * not merely fail. So far no one complained :) - */ - return EXIT_FAILURE; - } - -#if ENABLE_HUSH_FUNCTIONS - sv_flg = G_flag_return_in_progress; - /* "we are inside sourced file, ok to use return" */ - G_flag_return_in_progress = -1; -#endif - args_need_save = argv[1]; /* used as a boolean variable */ - if (args_need_save) - save_and_replace_G_args(&sv, argv); - - /* "false; . ./empty_line; echo Zero:$?" should print 0 */ - G.last_exitcode = 0; - parse_and_run_file(input); - fclose_and_forget(input); - - if (args_need_save) /* can't use argv[1] instead: "shift" can mangle it */ - restore_G_args(&sv, argv); -#if ENABLE_HUSH_FUNCTIONS - G_flag_return_in_progress = sv_flg; -#endif - - return G.last_exitcode; -} - -#if ENABLE_HUSH_UMASK -static int FAST_FUNC builtin_umask(char **argv) -{ - int rc; - mode_t mask; - - rc = 1; - mask = umask(0); - argv = skip_dash_dash(argv); - if (argv[0]) { - mode_t old_mask = mask; - - /* numeric umasks are taken as-is */ - /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */ - if (!isdigit(argv[0][0])) - mask ^= 0777; - mask = bb_parse_mode(argv[0], mask); - if (!isdigit(argv[0][0])) - mask ^= 0777; - if ((unsigned)mask > 0777) { - mask = old_mask; - /* bash messages: - * bash: umask: 'q': invalid symbolic mode operator - * bash: umask: 999: octal number out of range - */ - bb_error_msg("%s: invalid mode '%s'", "umask", argv[0]); - rc = 0; - } - } else { - /* Mimic bash */ - printf("%04o\n", (unsigned) mask); - /* fall through and restore mask which we set to 0 */ - } - umask(mask); - - return !rc; /* rc != 0 - success */ -} -#endif - #if ENABLE_HUSH_KILL static int FAST_FUNC builtin_kill(char **argv) { @@ -9984,3 +9939,46 @@ static int FAST_FUNC builtin_return(char **argv) return rc; } #endif + +#if ENABLE_HUSH_MEMLEAK +static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM) +{ + void *p; + unsigned long l; + +# ifdef M_TRIM_THRESHOLD + /* Optional. Reduces probability of false positives */ + malloc_trim(0); +# endif + /* Crude attempt to find where "free memory" starts, + * sans fragmentation. */ + p = malloc(240); + l = (unsigned long)p; + free(p); + p = malloc(3400); + if (l < (unsigned long)p) l = (unsigned long)p; + free(p); + + +# if 0 /* debug */ + { + struct mallinfo mi = mallinfo(); + printf("top alloc:0x%lx malloced:%d+%d=%d\n", l, + mi.arena, mi.hblkhd, mi.arena + mi.hblkhd); + } +# endif + + if (!G.memleak_value) + G.memleak_value = l; + + l -= G.memleak_value; + if ((long)l < 0) + l = 0; + l /= 1024; + if (l > 127) + l = 127; + + /* Exitcode is "how many kilobytes we leaked since 1st call" */ + return l; +} +#endif |