Big cleanup in config help and description
[oweals/busybox.git] / shell / hush.c
index 153867614aceee0c3728a9770ebc4ed30a9f1f82..9e508fc6ddd12ebb1126c9fbe9d99e04b9f0b173 100644 (file)
@@ -99,8 +99,6 @@
 //config:      bool "bash-compatible extensions"
 //config:      default y
 //config:      depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config:      help
-//config:        Enable bash-compatible extensions.
 //config:
 //config:config HUSH_BRACE_EXPANSION
 //config:      bool "Brace expansion"
 //config:      bool "Save command history to .hush_history"
 //config:      default y
 //config:      depends on HUSH_INTERACTIVE && FEATURE_EDITING_SAVEHISTORY
-//config:      help
-//config:        Enable history saving in hush.
 //config:
 //config:config HUSH_JOB
 //config:      bool "Job control"
 //config:        but no separate process group is formed.
 //config:
 //config:config HUSH_TICK
-//config:      bool "Process substitution"
+//config:      bool "Support process substitution"
 //config:      default y
 //config:      depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
 //config:      help
-//config:        Enable process substitution `command` and $(command) in hush.
+//config:        Enable `command` and $(command).
 //config:
 //config:config HUSH_IF
 //config:      bool "Support if/then/elif/else/fi"
 //config:      default y
 //config:      depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config:      help
-//config:        Enable if/then/elif/else/fi in hush.
 //config:
 //config:config HUSH_LOOPS
 //config:      bool "Support for, while and until loops"
 //config:      default y
 //config:      depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config:      help
-//config:        Enable for, while and until loops in hush.
 //config:
 //config:config HUSH_CASE
 //config:      bool "Support case ... esac statement"
 //config:      default y
 //config:      depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
 //config:      help
-//config:        Enable case ... esac statement in hush. +400 bytes.
+//config:        Enable case ... esac statement. +400 bytes.
 //config:
 //config:config HUSH_FUNCTIONS
 //config:      bool "Support funcname() { commands; } syntax"
 //config:      default y
 //config:      depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
 //config:      help
-//config:        Enable support for shell functions in hush. +800 bytes.
+//config:        Enable support for shell functions. +800 bytes.
 //config:
 //config:config HUSH_LOCAL
 //config:      bool "local builtin"
 //config:        This instructs hush to print commands before execution.
 //config:        Adds ~300 bytes.
 //config:
-//config:config HUSH_EXPORT
-//config:      bool "export builtin"
+//config:config HUSH_ECHO
+//config:      bool "echo builtin"
 //config:      default y
 //config:      depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config:      help
-//config:        Enable export builtin in hush.
 //config:
-//config:config HUSH_EXPORT_N
-//config:      bool "Support 'export -n' option"
+//config:config HUSH_PRINTF
+//config:      bool "printf builtin"
 //config:      default y
-//config:      depends on HUSH_EXPORT
-//config:      help
-//config:        export -n unexports variables. It is a bash extension.
+//config:      depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
 //config:
 //config:config HUSH_HELP
 //config:      bool "help builtin"
 //config:      default y
 //config:      depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config:      help
-//config:        Enable help builtin in hush. Code size + ~1 kbyte.
 //config:
-//config:config HUSH_PRINTF
-//config:      bool "printf builtin"
+//config:config HUSH_EXPORT
+//config:      bool "export builtin"
 //config:      default y
 //config:      depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
+//config:
+//config:config HUSH_EXPORT_N
+//config:      bool "Support 'export -n' option"
+//config:      default y
+//config:      depends on HUSH_EXPORT
 //config:      help
-//config:        Enable printf builtin in hush.
+//config:        export -n unexports variables. It is a bash extension.
 //config:
 //config:config HUSH_KILL
-//config:      bool "kill builtin (for kill %jobspec)"
+//config:      bool "kill builtin (supports kill %jobspec)"
 //config:      default y
 //config:      depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config:      help
-//config:        Enable kill builtin in hush.
 //config:
 //config:config HUSH_WAIT
 //config:      bool "wait builtin"
 //config:      default y
 //config:      depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config:      help
-//config:        Enable wait builtin in hush.
 //config:
 //config:config HUSH_TRAP
 //config:      bool "trap builtin"
 //config:      default y
 //config:      depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config:      help
-//config:        Enable trap builtin in hush.
-//config:
-//config:config HUSH_ULIMIT
-//config:      bool "ulimit builtin"
-//config:      default y
-//config:      depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config:      help
-//config:        Enable ulimit builtin in hush.
 //config:
 //config:config HUSH_TYPE
 //config:      bool "type builtin"
 //config:      default y
 //config:      depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config:      help
-//config:        Enable type builtin in hush.
 //config:
 //config:config HUSH_READ
 //config:      bool "read builtin"
 //config:      default y
 //config:      depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config:      help
-//config:        Enable read builtin in hush.
 //config:
 //config:config HUSH_SET
 //config:      bool "set builtin"
 //config:      default y
 //config:      depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config:      help
-//config:        Enable set builtin in hush.
 //config:
 //config:config HUSH_UNSET
 //config:      bool "unset builtin"
 //config:      default y
 //config:      depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config:      help
-//config:        Enable unset builtin in hush.
+//config:
+//config:config HUSH_ULIMIT
+//config:      bool "ulimit builtin"
+//config:      default y
+//config:      depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
 //config:
 //config:config HUSH_UMASK
 //config:      bool "umask builtin"
 //config:      default y
 //config:      depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config:      help
-//config:        Enable umask builtin in hush.
+//config:
+//config:config HUSH_MEMLEAK
+//config:      bool "memleak builtin (debugging)"
+//config:      default n
+//config:      depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
 //config:
 //config:config MSH
 //config:      bool "msh (deprecated: aliased to hush)"
@@ -846,8 +824,13 @@ struct globals {
        smallint exiting; /* used to prevent EXIT trap recursion */
        /* These four support $?, $#, and $1 */
        smalluint last_exitcode;
+#if ENABLE_HUSH_SET
        /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */
        smalluint global_args_malloced;
+# define G_global_args_malloced (G.global_args_malloced)
+#else
+# define G_global_args_malloced 0
+#endif
        /* how many non-NULL argv's we have. NB: $# + 1 */
        int global_argc;
        char **global_argv;
@@ -886,7 +869,7 @@ struct globals {
        unsigned special_sig_mask;
 #if ENABLE_HUSH_JOB
        unsigned fatal_sig_mask;
-# define G_fatal_sig_mask G.fatal_sig_mask
+# define G_fatal_sig_mask (G.fatal_sig_mask)
 #else
 # define G_fatal_sig_mask 0
 #endif
@@ -897,8 +880,10 @@ struct globals {
 # define G_traps ((char**)NULL)
 #endif
        sigset_t pending_set;
-#if HUSH_DEBUG
+#if ENABLE_HUSH_MEMLEAK
        unsigned long memleak_value;
+#endif
+#if HUSH_DEBUG
        int debug_indent;
 #endif
        struct sigaction sa;
@@ -920,7 +905,9 @@ struct globals {
 
 /* Function prototypes for builtins */
 static int builtin_cd(char **argv) FAST_FUNC;
+#if ENABLE_HUSH_ECHO
 static int builtin_echo(char **argv) FAST_FUNC;
+#endif
 static int builtin_eval(char **argv) FAST_FUNC;
 static int builtin_exec(char **argv) FAST_FUNC;
 static int builtin_exit(char **argv) FAST_FUNC;
@@ -940,7 +927,7 @@ static int builtin_history(char **argv) FAST_FUNC;
 #if ENABLE_HUSH_LOCAL
 static int builtin_local(char **argv) FAST_FUNC;
 #endif
-#if HUSH_DEBUG
+#if ENABLE_HUSH_MEMLEAK
 static int builtin_memleak(char **argv) FAST_FUNC;
 #endif
 #if ENABLE_HUSH_PRINTF
@@ -1037,7 +1024,7 @@ static const struct built_in_command bltins1[] = {
 #if ENABLE_HUSH_LOCAL
        BLTIN("local"    , builtin_local   , "Set local variables"),
 #endif
-#if HUSH_DEBUG
+#if ENABLE_HUSH_MEMLEAK
        BLTIN("memleak"  , builtin_memleak , NULL),
 #endif
 #if ENABLE_HUSH_READ
@@ -1073,11 +1060,11 @@ static const struct built_in_command bltins1[] = {
        BLTIN("wait"     , builtin_wait    , "Wait for process"),
 #endif
 };
-/* For now, echo and test are unconditionally enabled.
- * Maybe make it configurable? */
 static const struct built_in_command bltins2[] = {
        BLTIN("["        , builtin_test    , NULL),
+#if ENABLE_HUSH_ECHO
        BLTIN("echo"     , builtin_echo    , NULL),
+#endif
 #if ENABLE_HUSH_PRINTF
        BLTIN("printf"   , builtin_printf  , NULL),
 #endif
@@ -1467,7 +1454,7 @@ typedef struct save_arg_t {
        char *sv_argv0;
        char **sv_g_argv;
        int sv_g_argc;
-       smallint sv_g_malloced;
+       IF_HUSH_SET(smallint sv_g_malloced;)
 } save_arg_t;
 
 static void save_and_replace_G_args(save_arg_t *sv, char **argv)
@@ -1477,11 +1464,11 @@ static void save_and_replace_G_args(save_arg_t *sv, char **argv)
        sv->sv_argv0 = argv[0];
        sv->sv_g_argv = G.global_argv;
        sv->sv_g_argc = G.global_argc;
-       sv->sv_g_malloced = G.global_args_malloced;
+       IF_HUSH_SET(sv->sv_g_malloced = G.global_args_malloced;)
 
        argv[0] = G.global_argv[0]; /* retain $0 */
        G.global_argv = argv;
-       G.global_args_malloced = 0;
+       IF_HUSH_SET(G.global_args_malloced = 0;)
 
        n = 1;
        while (*++argv)
@@ -1491,19 +1478,19 @@ static void save_and_replace_G_args(save_arg_t *sv, char **argv)
 
 static void restore_G_args(save_arg_t *sv, char **argv)
 {
-       char **pp;
-
+#if ENABLE_HUSH_SET
        if (G.global_args_malloced) {
                /* someone ran "set -- arg1 arg2 ...", undo */
-               pp = G.global_argv;
+               char **pp = G.global_argv;
                while (*++pp) /* note: does not free $0 */
                        free(*pp);
                free(G.global_argv);
        }
+#endif
        argv[0] = sv->sv_argv0;
        G.global_argv = sv->sv_g_argv;
        G.global_argc = sv->sv_g_argc;
-       G.global_args_malloced = sv->sv_g_malloced;
+       IF_HUSH_SET(G.global_args_malloced = sv->sv_g_malloced;)
 }
 
 
@@ -2173,11 +2160,13 @@ static void unset_vars(char **strings)
        free(strings);
 }
 
+#if ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_BASH_COMPAT || ENABLE_HUSH_READ
 static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val)
 {
        char *var = xasprintf("%s=%s", name, val);
        set_local_var(var, /*flags:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
 }
+#endif
 
 
 /*
@@ -8816,12 +8805,12 @@ static int FAST_FUNC builtin_test(char **argv)
 {
        return run_applet_main(argv, test_main);
 }
-
+#if ENABLE_HUSH_ECHO
 static int FAST_FUNC builtin_echo(char **argv)
 {
        return run_applet_main(argv, echo_main);
 }
-
+#endif
 #if ENABLE_HUSH_PRINTF
 static int FAST_FUNC builtin_printf(char **argv)
 {
@@ -8927,6 +8916,7 @@ static int FAST_FUNC builtin_exit(char **argv)
        hush_exit(xatoi(argv[0]) & 0xff);
 }
 
+#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_TRAP
 static void print_escaped(const char *s)
 {
        if (*s == '\'')
@@ -8945,6 +8935,7 @@ static void print_escaped(const char *s)
                putchar('"');
        } while (*s);
 }
+#endif
 
 #if ENABLE_HUSH_EXPORT || ENABLE_HUSH_LOCAL
 # if !ENABLE_HUSH_LOCAL
@@ -9200,7 +9191,7 @@ static int FAST_FUNC builtin_shift(char **argv)
                n = atoi(argv[0]);
        }
        if (n >= 0 && n < G.global_argc) {
-               if (G.global_args_malloced) {
+               if (G_global_args_malloced) {
                        int m = 1;
                        while (m <= n)
                                free(G.global_argv[m++]);
@@ -9432,7 +9423,7 @@ static struct pipe *parse_jobspec(const char *str)
                        return pi;
                }
        }
-       bb_error_msg("%d: no such job", jobnum);
+       bb_error_msg("%u: no such job", jobnum);
        return NULL;
 }
 
@@ -9534,7 +9525,7 @@ static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM)
 }
 #endif
 
-#if HUSH_DEBUG
+#if ENABLE_HUSH_MEMLEAK
 static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM)
 {
        void *p;
@@ -9588,6 +9579,7 @@ 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
@@ -9619,7 +9611,8 @@ static int FAST_FUNC builtin_source(char **argv)
        /* "we are inside sourced file, ok to use return" */
        G_flag_return_in_progress = -1;
 #endif
-       if (argv[1])
+       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 */
@@ -9627,7 +9620,7 @@ static int FAST_FUNC builtin_source(char **argv)
        parse_and_run_file(input);
        fclose_and_forget(input);
 
-       if (argv[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;
@@ -9680,9 +9673,9 @@ static int FAST_FUNC builtin_kill(char **argv)
 {
        int ret = 0;
 
-       argv = skip_dash_dash(argv);
-       if (argv[0] && strcmp(argv[0], "-l") != 0) {
-               int i = 0;
+# if ENABLE_HUSH_JOB
+       if (argv[1] && strcmp(argv[1], "-l") != 0) {
+               int i = 1;
 
                do {
                        struct pipe *pi;
@@ -9719,17 +9712,12 @@ static int FAST_FUNC builtin_kill(char **argv)
                         * sh -c 'true|sleep 2 & sleep 1; kill %1'
                         * sh -c 'true|sleep 1 & sleep 2; kill %1'
                         */
-                       n = pi->num_cmds;
-                       if (ENABLE_HUSH_JOB && G_interactive_fd)
-                               n = 1;
+                       n = G_interactive_fd ? 1 : pi->num_cmds;
                        dst = alloca(n * sizeof(int)*4);
                        argv[i] = dst;
-#if ENABLE_HUSH_JOB
                        if (G_interactive_fd)
                                dst += sprintf(dst, " -%u", (int)pi->pgrp);
-                       else
-#endif
-                       for (j = 0; j < n; j++) {
+                       else for (j = 0; j < n; j++) {
                                struct command *cmd = &pi->cmds[j];
                                /* Skip exited members of the job */
                                if (cmd->pid == 0)
@@ -9744,13 +9732,12 @@ static int FAST_FUNC builtin_kill(char **argv)
                        *dst = '\0';
                } while (argv[++i]);
        }
+# endif
 
-       if (argv[0] || ret == 0) {
-               argv--;
-               argv[0] = (char*)"kill"; /* why? think about "kill -- PID" */
-               /* kill_main also handles "killall" etc, so it does look at argv[0]! */
+       if (argv[1] || ret == 0) {
                ret = run_applet_main(argv, kill_main);
        }
+       /* else: ret = 1, "kill %bad_jobspec" case */
        return ret;
 }
 #endif