forgot about avn add... :(
[oweals/busybox.git] / shell / lash.c
index 27eb8ec290eb0c8e8baed55bdb1505964f6c2b9c..4067bc6bc853f2bb6622a4658477286bc3101df3 100644 (file)
 
 
 #include "busybox.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/wait.h>
-#include <unistd.h>
 #include <getopt.h>
-#include <termios.h>
 #include "cmdedit.h"
 
 #ifdef CONFIG_LOCALE_SUPPORT
@@ -65,6 +54,9 @@ enum {
        ELSE_EXP_CONTEXT = 0x10
 };
 
+#define LASH_OPT_DONE (1)
+#define LASH_OPT_SAW_QUOTE (2)
+
 #ifdef CONFIG_LASH_PIPE_N_REDIRECTS
 struct redir_struct {
        enum redir_type type;   /* type of redirection */
@@ -165,7 +157,7 @@ static int shell_context;  /* Type prompt trigger (PS1 or PS2) */
 
 /* Globals that are static to this file */
 static const char *cwd;
-static char *local_pending_command = NULL;
+static char *local_pending_command;
 static struct jobset job_list = { NULL, NULL };
 static int argc;
 static char **argv;
@@ -187,7 +179,7 @@ static inline void debug_printf(const char *format, ...)
        va_end(args);
 }
 #else
-static inline void debug_printf(const char *format, ...) { }
+static inline void debug_printf(const char ATTRIBUTE_UNUSED *format, ...) { }
 #endif
 
 /*
@@ -228,7 +220,7 @@ static int builtin_cd(struct child_prog *child)
        else
                newdir = child->argv[1];
        if (chdir(newdir)) {
-               printf("cd: %s: %m\n", newdir);
+               bb_perror_msg("cd: %s", newdir);
                return EXIT_FAILURE;
        }
        cwd = xgetcwd((char *)cwd);
@@ -315,12 +307,12 @@ static int builtin_fg_bg(struct child_prog *child)
 }
 
 /* built-in 'help' handler */
-static int builtin_help(struct child_prog *dummy)
+static int builtin_help(struct child_prog ATTRIBUTE_UNUSED *dummy)
 {
        struct built_in_command *x;
 
-       printf("\nBuilt-in commands:\n");
-       printf("-------------------\n");
+       printf("\nBuilt-in commands:\n"
+                  "-------------------\n");
        for (x = bltins; x->cmd; x++) {
                if (x->descr==NULL)
                        continue;
@@ -331,7 +323,7 @@ static int builtin_help(struct child_prog *dummy)
                        continue;
                printf("%s\t%s\n", x->cmd, x->descr);
        }
-       printf("\n\n");
+       putchar('\n');
        return EXIT_SUCCESS;
 }
 
@@ -354,7 +346,7 @@ static int builtin_jobs(struct child_prog *child)
 
 
 /* built-in 'pwd' handler */
-static int builtin_pwd(struct child_prog *dummy)
+static int builtin_pwd(struct child_prog ATTRIBUTE_UNUSED *dummy)
 {
        cwd = xgetcwd((char *)cwd);
        if (!cwd)
@@ -378,7 +370,7 @@ static int builtin_export(struct child_prog *child)
        }
        res = putenv(v);
        if (res)
-               fprintf(stderr, "export: %m\n");
+               bb_perror_msg("export");
 #ifdef CONFIG_FEATURE_SH_FANCY_PROMPT
        if (strncmp(v, "PS1=", 4)==0)
                PS1 = getenv("PS1");
@@ -397,7 +389,7 @@ static int builtin_export(struct child_prog *child)
 /* built-in 'read VAR' handler */
 static int builtin_read(struct child_prog *child)
 {
-       int res = 0, len, newlen;
+       int res = 0, len;
        char *s;
        char string[MAX_READ];
 
@@ -408,19 +400,19 @@ static int builtin_read(struct child_prog *child)
                string[len++] = '=';
                string[len]   = '\0';
                fgets(&string[len], sizeof(string) - len, stdin);       /* read string */
-               newlen = strlen(string);
-               if(newlen > len)
-                       string[--newlen] = '\0';        /* chomp trailing newline */
+               res = strlen(string);
+               if (res > len)
+                       string[--res] = '\0';   /* chomp trailing newline */
                /*
                ** string should now contain "VAR=<value>"
                ** copy it (putenv() won't do that, so we must make sure
                ** the string resides in a static buffer!)
                */
                res = -1;
-               if((s = strdup(string)))
+               if ((s = strdup(string)))
                        res = putenv(s);
                if (res)
-                       fprintf(stderr, "read: %m\n");
+                       bb_perror_msg("read");
        }
        else
                fgets(string, sizeof(string), stdin);
@@ -434,12 +426,8 @@ static int builtin_source(struct child_prog *child)
        FILE *input;
        int status;
 
-       if (child->argv[1] == NULL)
-               return EXIT_FAILURE;
-
-       input = fopen(child->argv[1], "r");
+       input = fopen_or_warn(child->argv[1], "r");
        if (!input) {
-               printf( "Couldn't open file '%s'\n", child->argv[1]);
                return EXIT_FAILURE;
        }
 
@@ -543,16 +531,6 @@ static void checkjobs(struct jobset *j_list)
                        /* child stopped */
                        job->stopped_progs++;
                        job->progs[prognum].is_stopped = 1;
-
-#if 0
-                       /* Printing this stuff is a pain, since it tends to
-                        * overwrite the prompt an inconveinient moments.  So
-                        * don't do that.  */
-                       if (job->stopped_progs == job->num_progs) {
-                               printf(JOB_STATUS_FORMAT, job->jobid, "Stopped",
-                                          job->text);
-                       }
-#endif
                }
        }
 
@@ -656,7 +634,7 @@ static inline void setup_prompt_string(char **prompt_str)
        if (shell_context == 0) {
                free(PS1);
                PS1=xmalloc(strlen(cwd)+4);
-               sprintf(PS1, "%s %s", cwd, ( geteuid() != 0 ) ?  "$ ":"# ");
+               sprintf(PS1, "%s %c ", cwd, ( geteuid() != 0 ) ? '$': '#');
                *prompt_str = PS1;
        } else {
                *prompt_str = PS2;
@@ -700,49 +678,27 @@ static int get_command(FILE * source, char *command)
 
        if (!fgets(command, BUFSIZ - 2, source)) {
                if (source == stdin)
-                       printf("\n");
+                       puts("");
                return 1;
        }
 
        return 0;
 }
 
-static char* itoa(register int i)
-{
-       static char a[7]; /* Max 7 ints */
-       register char *b = a + sizeof(a) - 1;
-       int   sign = (i < 0);
-
-       if (sign)
-               i = -i;
-       *b = 0;
-       do
-       {
-               *--b = '0' + (i % 10);
-               i /= 10;
-       }
-       while (i);
-       if (sign)
-               *--b = '-';
-       return b;
-}
-
 static char * strsep_space( char *string, int * ix)
 {
-       char *token;
-
        /* Short circuit the trivial case */
        if ( !string || ! string[*ix])
                return NULL;
 
        /* Find the end of the token. */
-       whilestring[*ix] && !isspace(string[*ix]) ) {
+       while (string[*ix] && !isspace(string[*ix]) ) {
                (*ix)++;
        }
 
        /* Find the end of any whitespace trailing behind
         * the token and let that be part of the token */
-       while( string[*ix] && isspace(string[*ix]) ) {
+       while (string[*ix] && (isspace)(string[*ix]) ) {
                (*ix)++;
        }
 
@@ -751,9 +707,7 @@ static char * strsep_space( char *string, int * ix)
                return NULL;
        }
 
-       token = bb_xstrndup(string, *ix);
-
-       return token;
+       return xstrndup(string, *ix);
 }
 
 static int expand_arguments(char *command)
@@ -762,7 +716,7 @@ static int expand_arguments(char *command)
        expand_t expand_result;
        char *tmpcmd, *cmd, *cmd_copy;
        char *src, *dst, *var;
-       const char *out_of_space = "out of space during expansion";
+       const char * const out_of_space = "out of space during expansion";
        int flags = GLOB_NOCHECK
 #ifdef GLOB_BRACE
                | GLOB_BRACE
@@ -792,7 +746,7 @@ static int expand_arguments(char *command)
 
        /* We need a clean copy, so strsep can mess up the copy while
         * we write stuff into the original (in a minute) */
-       cmd = cmd_copy = bb_xstrdup(command);
+       cmd = cmd_copy = xstrdup(command);
        *command = '\0';
        for (ix = 0, tmpcmd = cmd;
                        (tmpcmd = strsep_space(cmd, &ix)) != NULL; cmd += ix, ix=0) {
@@ -887,7 +841,7 @@ static int expand_arguments(char *command)
                                num_skip_chars=1;
                        } else {
                                src=dst+1;
-                               while(isalnum(*src) || *src=='_') src++;
+                               while((isalnum)(*src) || *src=='_') src++;
                        }
                        if (src == NULL) {
                                src = dst+dstlen;
@@ -931,10 +885,9 @@ static int parse_command(char **command_ptr, struct job *job, int *inbg)
        char *command;
        char *return_command = NULL;
        char *src, *buf;
-       int argc_l = 0;
-       int done = 0;
+       int argc_l;
+       int flag;
        int argv_alloced;
-       int saw_quote = 0;
        char quote = '\0';
        struct child_prog *prog;
 #ifdef CONFIG_LASH_PIPE_N_REDIRECTS
@@ -943,8 +896,7 @@ static int parse_command(char **command_ptr, struct job *job, int *inbg)
 #endif
 
        /* skip leading white space */
-       while (**command_ptr && isspace(**command_ptr))
-               (*command_ptr)++;
+       *command_ptr = skip_whitespace(*command_ptr);
 
        /* this handles empty lines or leading '#' characters */
        if (!**command_ptr || (**command_ptr == '#')) {
@@ -978,9 +930,10 @@ static int parse_command(char **command_ptr, struct job *job, int *inbg)
        prog->argv = xmalloc(sizeof(*prog->argv) * argv_alloced);
        prog->argv[0] = job->cmdbuf;
 
+       flag = argc_l = 0;
        buf = command;
        src = *command_ptr;
-       while (*src && !done) {
+       while (*src && !(flag & LASH_OPT_DONE)) {
                if (quote == *src) {
                        quote = '\0';
                } else if (quote) {
@@ -1001,7 +954,7 @@ static int parse_command(char **command_ptr, struct job *job, int *inbg)
                                           *src == ']') *buf++ = '\\';
                        *buf++ = *src;
                } else if (isspace(*src)) {
-                       if (*prog->argv[argc_l] || saw_quote) {
+                       if (*prog->argv[argc_l] || flag & LASH_OPT_SAW_QUOTE) {
                                buf++, argc_l++;
                                /* +1 here leaves room for the NULL which ends argv */
                                if ((argc_l + 1) == argv_alloced) {
@@ -1011,21 +964,21 @@ static int parse_command(char **command_ptr, struct job *job, int *inbg)
                                                                                  argv_alloced);
                                }
                                prog->argv[argc_l] = buf;
-                               saw_quote = 0;
+                               flag ^= LASH_OPT_SAW_QUOTE;
                        }
                } else
                        switch (*src) {
                        case '"':
                        case '\'':
                                quote = *src;
-                               saw_quote = 1;
+                               flag |= LASH_OPT_SAW_QUOTE;
                                break;
 
                        case '#':                       /* comment */
                                if (*(src-1)== '$')
                                        *buf++ = *src;
                                else
-                                       done = 1;
+                                       flag |= LASH_OPT_DONE;
                                break;
 
 #ifdef CONFIG_LASH_PIPE_N_REDIRECTS
@@ -1068,8 +1021,7 @@ static int parse_command(char **command_ptr, struct job *job, int *inbg)
 
                                /* This isn't POSIX sh compliant. Oh well. */
                                chptr = src;
-                               while (isspace(*chptr))
-                                       chptr++;
+                               chptr = skip_whitespace(chptr);
 
                                if (!*chptr) {
                                        bb_error_msg("file name expected after %c", *(src-1));
@@ -1088,13 +1040,10 @@ static int parse_command(char **command_ptr, struct job *job, int *inbg)
 
                        case '|':                       /* pipe */
                                /* finish this command */
-                               if (*prog->argv[argc_l] || saw_quote)
+                               if (*prog->argv[argc_l] || flag & LASH_OPT_SAW_QUOTE)
                                        argc_l++;
                                if (!argc_l) {
-                                       bb_error_msg("empty command in pipe");
-                                       free_job(job);
-                                       job->num_progs=0;
-                                       return 1;
+                                       goto empty_command_in_pipe;
                                }
                                prog->argv[argc_l] = NULL;
 
@@ -1114,10 +1063,10 @@ static int parse_command(char **command_ptr, struct job *job, int *inbg)
                                prog->argv[0] = ++buf;
 
                                src++;
-                               while (*src && isspace(*src))
-                                       src++;
+                               src = skip_whitespace(src);
 
                                if (!*src) {
+empty_command_in_pipe:
                                        bb_error_msg("empty command in pipe");
                                        free_job(job);
                                        job->num_progs=0;
@@ -1131,9 +1080,10 @@ static int parse_command(char **command_ptr, struct job *job, int *inbg)
 #ifdef CONFIG_LASH_JOB_CONTROL
                        case '&':                       /* background */
                                *inbg = 1;
+                               /* fallthrough */
 #endif
                        case ';':                       /* multiple commands */
-                               done = 1;
+                               flag |= LASH_OPT_DONE;
                                return_command = *command_ptr + (src - *command_ptr) + 1;
                                break;
 
@@ -1154,7 +1104,7 @@ static int parse_command(char **command_ptr, struct job *job, int *inbg)
                src++;
        }
 
-       if (*prog->argv[argc_l] || saw_quote) {
+       if (*prog->argv[argc_l] || flag & LASH_OPT_SAW_QUOTE) {
                argc_l++;
        }
        if (!argc_l) {
@@ -1164,10 +1114,10 @@ static int parse_command(char **command_ptr, struct job *job, int *inbg)
        prog->argv[argc_l] = NULL;
 
        if (!return_command) {
-               job->text = bb_xstrdup(*command_ptr);
+               job->text = xstrdup(*command_ptr);
        } else {
                /* This leaves any trailing spaces, which is a bit sloppy */
-               job->text = bb_xstrndup(*command_ptr, return_command - *command_ptr);
+               job->text = xstrndup(*command_ptr, return_command - *command_ptr);
        }
 
        *command_ptr = return_command;
@@ -1181,12 +1131,6 @@ static int pseudo_exec(struct child_prog *child)
 {
        struct built_in_command *x;
 
-       /* Check if the command sets an environment variable. */
-       if( strchr(child->argv[0], '=') != NULL ) {
-               child->argv[1] = child->argv[0];
-               _exit(builtin_export(child));
-       }
-
        /* Check if the command matches any of the non-forking builtins.
         * Depending on context, this might be redundant.  But it's
         * easier to waste a few CPU cycles than it is to figure out
@@ -1201,7 +1145,7 @@ static int pseudo_exec(struct child_prog *child)
        /* Check if the command matches any of the forking builtins. */
        for (x = bltins_forking; x->cmd; x++) {
                if (strcmp(child->argv[0], x->cmd) == 0) {
-                       bb_applet_name=x->cmd;
+                       applet_name=x->cmd;
                        _exit (x->function(child));
                }
        }
@@ -1231,7 +1175,7 @@ static int pseudo_exec(struct child_prog *child)
 
        /* Do not use bb_perror_msg_and_die() here, since we must not
         * call exit() but should call _exit() instead */
-       fprintf(stderr, "%s: %m\n", child->argv[0]);
+       bb_perror_msg("%s", child->argv[0]);
        _exit(EXIT_FAILURE);
 }
 
@@ -1310,6 +1254,12 @@ static int run_command(struct job *newjob, int inbg, int outpipe[2])
                 * is doomed to failure, and doesn't work on bash, either.
                 */
                if (newjob->num_progs == 1) {
+                       /* Check if the command sets an environment variable. */
+                       if (strchr(child->argv[0], '=') != NULL) {
+                               child->argv[1] = child->argv[0];
+                               return builtin_export(child);
+                       }
+
                        for (x = bltins; x->cmd; x++) {
                                if (strcmp(child->argv[0], x->cmd) == 0 ) {
                                        int rcode;
@@ -1336,7 +1286,7 @@ static int run_command(struct job *newjob, int inbg, int outpipe[2])
                        signal(SIGTTOU, SIG_DFL);
                        signal(SIGCHLD, SIG_DFL);
 
-                       // Close all open filehandles.
+                       /* Close all open filehandles. */
                        while(close_me_list) close((long)llist_pop(&close_me_list));
 
                        if (outpipe[1]!=-1) {
@@ -1508,6 +1458,8 @@ static void free_memory(void)
                remove_job(&job_list, job_list.fg);
        }
 }
+#else
+void free_memory(void);
 #endif
 
 #ifdef CONFIG_LASH_JOB_CONTROL
@@ -1538,7 +1490,7 @@ static void setup_job_control(void)
        /* Put ourselves in our own process group.  */
        setsid();
        shell_pgrp = getpid ();
-       setpgid (shell_pgrp, shell_pgrp);
+       setpgid(shell_pgrp, shell_pgrp);
 
        /* Grab control of the terminal.  */
        tcsetpgrp(shell_terminal, shell_pgrp);
@@ -1551,14 +1503,13 @@ static inline void setup_job_control(void)
 
 int lash_main(int argc_l, char **argv_l)
 {
-       int opt, interactive=FALSE;
+       unsigned opt;
        FILE *input = stdin;
        argc = argc_l;
        argv = argv_l;
 
        /* These variables need re-initializing when recursing */
        last_jobid = 0;
-       local_pending_command = NULL;
        close_me_list = NULL;
        job_list.head = NULL;
        job_list.fg = NULL;
@@ -1571,27 +1522,18 @@ int lash_main(int argc_l, char **argv_l)
                        llist_add_to(&close_me_list, (void *)(long)fileno(prof_input));
                        /* Now run the file */
                        busy_loop(prof_input);
-                       fclose(prof_input);
+                       fclose_if_not_stdin(prof_input);
                        llist_pop(&close_me_list);
                }
        }
 
-       while ((opt = getopt(argc_l, argv_l, "cxi")) > 0) {
-               switch (opt) {
-                       case 'c':
-                               input = NULL;
-                               if (local_pending_command != 0)
-                                       bb_error_msg_and_die("multiple -c arguments");
-                               local_pending_command = bb_xstrdup(argv[optind]);
-                               optind++;
-                               argv = argv+optind;
-                               break;
-                       case 'i':
-                               interactive = TRUE;
-                               break;
-                       default:
-                               bb_show_usage();
-               }
+       opt = getopt32(argc_l, argv_l, "+ic:", &local_pending_command);
+#define LASH_OPT_i (1<<0)
+#define LASH_OPT_c (1<<2)
+       if (opt & LASH_OPT_c) {
+               input = NULL;
+               optind++;
+               argv += optind;
        }
        /* A shell is interactive if the `-i' flag was given, or if all of
         * the following conditions are met:
@@ -1601,20 +1543,21 @@ int lash_main(int argc_l, char **argv_l)
         *    standard output is a terminal
         *    Refer to Posix.2, the description of the `sh' utility. */
        if (argv[optind]==NULL && input==stdin &&
-                       isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
-               interactive=TRUE;
+                       isatty(STDIN_FILENO) && isatty(STDOUT_FILENO))
+       {
+               opt |= LASH_OPT_i;
        }
        setup_job_control();
-       if (interactive==TRUE) {
-               //printf( "optind=%d  argv[optind]='%s'\n", optind, argv[optind]);
+       if (opt & LASH_OPT_i) {
                /* Looks like they want an interactive shell */
-#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
-               printf( "\n\n%s Built-in shell (lash)\n", BB_BANNER);
-               printf( "Enter 'help' for a list of built-in commands.\n\n");
-#endif
-       } else if (local_pending_command==NULL) {
+               if (!ENABLE_FEATURE_SH_EXTRA_QUIET) {
+                       printf("\n\n%s Built-in shell (lash)\n"
+                                       "Enter 'help' for a list of built-in commands.\n\n",
+                                       BB_BANNER);
+               }
+       } else if (!local_pending_command && argv[optind]) {
                //printf( "optind=%d  argv[optind]='%s'\n", optind, argv[optind]);
-               input = bb_xfopen(argv[optind], "r");
+               input = xfopen(argv[optind], "r");
                /* be lazy, never mark this closed */
                llist_add_to(&close_me_list, (void *)(long)fileno(input));
        }
@@ -1624,15 +1567,10 @@ int lash_main(int argc_l, char **argv_l)
        if (!cwd)
                cwd = bb_msg_unknown;
 
-#ifdef CONFIG_FEATURE_CLEAN_UP
-       atexit(free_memory);
-#endif
+       if (ENABLE_FEATURE_CLEAN_UP) atexit(free_memory);
 
-#ifdef CONFIG_FEATURE_COMMAND_EDITING
-       cmdedit_set_initial_prompt();
-#else
-       PS1 = NULL;
-#endif
+       if (ENABLE_FEATURE_COMMAND_EDITING) cmdedit_set_initial_prompt();
+       else PS1 = NULL;
 
        return (busy_loop(input));
 }