aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
Diffstat (limited to 'shell')
-rw-r--r--shell/hush.c592
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