Re-organized some sections and added a whole new section on avoiding the
[oweals/busybox.git] / sh.c
diff --git a/sh.c b/sh.c
index 5a254736858bafb607acd89fdc13fa72adac5dd2..7f5b9060207c30bb20c33de7a2e688c2948577d0 100644 (file)
--- a/sh.c
+++ b/sh.c
 
 //#define BB_FEATURE_SH_BACKTICKS
 //#define BB_FEATURE_SH_IF_EXPRESSIONS
-//#define BB_FEATURE_SH_ENVIRONMENT
+#define BB_FEATURE_SH_ENVIRONMENT
 //#define DEBUG_SHELL
 
 
-#include "internal.h"
+#include "busybox.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
@@ -108,6 +108,7 @@ struct builtInCommand {
 /* function prototypes for builtins */
 static int builtin_cd(struct job *cmd, struct jobSet *junk);
 static int builtin_env(struct job *dummy, struct jobSet *junk);
+static int builtin_exec(struct job *cmd, struct jobSet *junk);
 static int builtin_exit(struct job *cmd, struct jobSet *junk);
 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
 static int builtin_help(struct job *cmd, struct jobSet *junk);
@@ -139,12 +140,14 @@ static int busy_loop(FILE * input);
 static struct builtInCommand bltins[] = {
        {"bg", "Resume a job in the background", builtin_fg_bg},
        {"cd", "Change working directory", builtin_cd},
+       {"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec},
        {"exit", "Exit from shell()", builtin_exit},
        {"fg", "Bring job into the foreground", builtin_fg_bg},
        {"jobs", "Lists the active jobs", builtin_jobs},
        {"export", "Set environment variable", builtin_export},
        {"unset", "Unset environment variable", builtin_unset},
        {"read", "Input environment variable", builtin_read},
+       {".", "Source-in and run commands in a file", builtin_source},
 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
        {"if", NULL, builtin_if},
        {"then", NULL, builtin_then},
@@ -159,7 +162,6 @@ static struct builtInCommand bltins[] = {
 static struct builtInCommand bltins_forking[] = {
        {"env", "Print all environment variables", builtin_env},
        {"pwd", "Print current directory", builtin_pwd},
-       {".", "Source-in and run commands in a file", builtin_source},
        {"help", "List shell built-in commands", builtin_help},
        {NULL, NULL, NULL}
 };
@@ -219,13 +221,26 @@ static int builtin_env(struct job *dummy, struct jobSet *junk)
        return (0);
 }
 
+/* built-in 'exec' handler */
+static int builtin_exec(struct job *cmd, struct jobSet *junk)
+{
+       if (cmd->progs[0].argv[1])
+       {
+               cmd->progs[0].argv++;
+               execvp(cmd->progs[0].argv[0], cmd->progs[0].argv);
+               fatalError("Exec to %s failed: %s\n", cmd->progs[0].argv[0],
+                               strerror(errno));
+       }
+       return TRUE;
+}
+
 /* built-in 'exit' handler */
 static int builtin_exit(struct job *cmd, struct jobSet *junk)
 {
        if (!cmd->progs[0].argv[1] == 1)
                exit TRUE;
 
-       return(atoi(cmd->progs[0].argv[1]));
+       exit (atoi(cmd->progs[0].argv[1]));
 }
 
 /* built-in 'fg' and 'bg' handler */
@@ -500,6 +515,7 @@ static int builtin_source(struct job *cmd, struct jobSet *junk)
 
        /* Now run the file */
        status = busy_loop(input);
+       fclose(input);
        return (status);
 }
 
@@ -611,10 +627,10 @@ static int setupRedirections(struct childProgram *prog)
                        mode = O_RDONLY;
                        break;
                case REDIRECT_OVERWRITE:
-                       mode = O_RDWR | O_CREAT | O_TRUNC;
+                       mode = O_WRONLY | O_CREAT | O_TRUNC;
                        break;
                case REDIRECT_APPEND:
-                       mode = O_RDWR | O_CREAT | O_APPEND;
+                       mode = O_WRONLY | O_CREAT | O_APPEND;
                        break;
                }
 
@@ -769,31 +785,42 @@ static void globLastArgument(struct childProgram *prog, int *argcPtr,
 #endif
        }
 
-       rc = glob(prog->argv[argc_l - 1], flags, NULL, &prog->globResult);
-       if (rc == GLOB_NOSPACE) {
-               errorMsg("out of space during glob operation\n");
-               return;
-       } else if (rc == GLOB_NOMATCH ||
+       if (strpbrk(prog->argv[argc_l - 1],"*[]?")!= NULL){
+               rc = glob(prog->argv[argc_l - 1], flags, NULL, &prog->globResult);
+               if (rc == GLOB_NOSPACE) {
+                       errorMsg("out of space during glob operation\n");
+                       return;
+               } else if (rc == GLOB_NOMATCH ||
                           (!rc && (prog->globResult.gl_pathc - i) == 1 &&
                                strcmp(prog->argv[argc_l - 1],
                                                prog->globResult.gl_pathv[i]) == 0)) {
-               /* we need to remove whatever \ quoting is still present */
-               src = dst = prog->argv[argc_l - 1];
-               while (*src) {
-                       if (*src != '\\')
-                               *dst++ = *src;
-                       src++;
+                       /* we need to remove whatever \ quoting is still present */
+                       src = dst = prog->argv[argc_l - 1];
+                       while (*src) {
+                               if (*src != '\\')
+                                       *dst++ = *src;
+                               src++;
+                       }
+                       *dst = '\0';
+               } else if (!rc) {
+                       argcAlloced += (prog->globResult.gl_pathc - i);
+                       prog->argv = xrealloc(prog->argv, argcAlloced * sizeof(*prog->argv));
+                       memcpy(prog->argv + (argc_l - 1), prog->globResult.gl_pathv + i,
+                                  sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
+                       argc_l += (prog->globResult.gl_pathc - i - 1);
                }
-               *dst = '\0';
-       } else if (!rc) {
-               argcAlloced += (prog->globResult.gl_pathc - i);
-               prog->argv =
-                       realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
-               memcpy(prog->argv + (argc_l - 1), prog->globResult.gl_pathv + i,
-                          sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
-               argc_l += (prog->globResult.gl_pathc - i - 1);
+       }else{
+                       src = dst = prog->argv[argc_l - 1];
+                       while (*src) {
+                               if (*src != '\\')
+                                       *dst++ = *src;
+                               src++;
+                       }
+                       *dst = '\0';
+                       prog->globResult.gl_pathc=0;
+                       if (flags==0)
+                               prog->globResult.gl_pathv=NULL;
        }
-
        *argcAllocedPtr = argcAlloced;
        *argcPtr = argc_l;
 }
@@ -837,7 +864,7 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
           Getting clean memory relieves us of the task of NULL 
           terminating things and makes the rest of this look a bit 
           cleaner (though it is, admittedly, a tad less efficient) */
-       job->cmdBuf = command = calloc(2*strlen(*commandPtr) + 1, sizeof(char));
+       job->cmdBuf = command = xcalloc(2*strlen(*commandPtr) + 1, sizeof(char));
        job->text = NULL;
 
        prog = job->progs;
@@ -876,9 +903,9 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
                                /* +1 here leaves room for the NULL which ends argv */
                                if ((argc_l + 1) == argvAlloced) {
                                        argvAlloced += 5;
-                                       prog->argv = realloc(prog->argv,
-                                                                                sizeof(*prog->argv) *
-                                                                                argvAlloced);
+                                       prog->argv = xrealloc(prog->argv,
+                                                                                 sizeof(*prog->argv) *
+                                                                                 argvAlloced);
                                }
                                globLastArgument(prog, &argc_l, &argvAlloced);
                                prog->argv[argc_l] = buf;
@@ -900,9 +927,9 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
                        case '>':                       /* redirections */
                        case '<':
                                i = prog->numRedirections++;
-                               prog->redirections = realloc(prog->redirections,
-                                                                                        sizeof(*prog->redirections) *
-                                                                                        (i + 1));
+                               prog->redirections = xrealloc(prog->redirections,
+                                                                                         sizeof(*prog->redirections) *
+                                                                                         (i + 1));
 
                                prog->redirections[i].fd = -1;
                                if (buf != prog->argv[argc_l]) {
@@ -969,8 +996,8 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
 
                                /* and start the next */
                                job->numProgs++;
-                               job->progs = realloc(job->progs,
-                                                                        sizeof(*job->progs) * job->numProgs);
+                               job->progs = xrealloc(job->progs,
+                                                                         sizeof(*job->progs) * job->numProgs);
                                prog = job->progs + (job->numProgs - 1);
                                prog->numRedirections = 0;
                                prog->redirections = NULL;
@@ -1003,16 +1030,6 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
                                returnCommand = *commandPtr + (src - *commandPtr) + 1;
                                break;
 
-                       case '\\':
-                               src++;
-                               if (!*src) {
-                                       errorMsg("character expected after \\\n");
-                                       freeJob(job);
-                                       return 1;
-                               }
-                               if (*src == '*' || *src == '[' || *src == ']'
-                                       || *src == '?') *buf++ = '\\';
-                               /* fallthrough */
 #ifdef BB_FEATURE_SH_BACKTICKS
                        case '`':
                                /* Exec a backtick-ed command */
@@ -1033,7 +1050,8 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
 
                                        /* Make some space to hold just the backticked command */
                                        charptr1 = charptr2 = xmalloc(1+ptr-src);
-                                       snprintf(charptr1, 1+ptr-src, src);
+                                       memcpy(charptr1, src, ptr-src);
+                                       charptr1[ptr-src] = '\0';
                                        newJob = xmalloc(sizeof(struct job));
                                        /* Now parse and run the backticked command */
                                        if (!parseCommand(&charptr1, newJob, &njobList, inBg) 
@@ -1058,7 +1076,7 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
                                        while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
                                                int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
                                                if (newSize > BUFSIZ) {
-                                                       *commandPtr=realloc(*commandPtr, src - *commandPtr + 
+                                                       *commandPtr=xrealloc(*commandPtr, src - *commandPtr + 
                                                                        size + 1 + strlen(charptr2));
                                                }
                                                memcpy(src, charptr1, size); 
@@ -1071,7 +1089,7 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
 
                                        /* Now paste into the *commandPtr all the stuff 
                                         * leftover after the second backtick */
-                                       memcpy(src, charptr2, strlen(charptr2));
+                                       memcpy(src, charptr2, strlen(charptr2)+1);
                                        free(charptr2);
 
                                        /* Now recursively call parseCommand to deal with the new
@@ -1082,6 +1100,17 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
                                }
                                break;
 #endif // BB_FEATURE_SH_BACKTICKS
+
+                       case '\\':
+                               src++;
+                               if (!*src) {
+                                       errorMsg("character expected after \\\n");
+                                       freeJob(job);
+                                       return 1;
+                               }
+                               if (*src == '*' || *src == '[' || *src == ']'
+                                       || *src == '?') *buf++ = '\\';
+                               /* fallthrough */
                        default:
                                *buf++ = *src;
                        }
@@ -1111,7 +1140,7 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
        }
 
        *commandPtr = returnCommand;
-
+       
        return 0;
 }
 
@@ -1123,10 +1152,9 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int
        int pipefds[2];                         /* pipefd[0] is for reading */
        struct builtInCommand *x;
 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
-       const struct BB_applet *a = applets;
+       struct BB_applet search_applet, *applet;
 #endif
 
-
        nextin = 0, nextout = 1;
        for (i = 0; i < newJob->numProgs; i++) {
                if ((i + 1) < newJob->numProgs) {
@@ -1186,23 +1214,44 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int
                                }
                        }
 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
-                       /* Check if the command matches any busybox internal commands here */
-                       while (a->name != 0) {
-                               if (strcmp(get_last_path_component(newJob->progs[i].argv[0]), a->name) == 0) {
-                                       int argc_l;
-                                       char** argv=newJob->progs[i].argv;
-                                       for(argc_l=0;*argv!=NULL; argv++, argc_l++);
-                                       applet_name=a->name;
-                                       optind = 1;
-                                       exit((*(a->main)) (argc_l, newJob->progs[i].argv));
-                               }
-                               a++;
+                       /* Check if the command matches any busybox internal
+                        * commands ("applets") here.  Following discussions from
+                        * November 2000 on busybox@opensource.lineo.com, don't use
+                        * get_last_path_component().  This way explicit (with
+                        * slashes) filenames will never be interpreted as an
+                        * applet, just like with builtins.  This way the user can
+                        * override an applet with an explicit filename reference.
+                        * The only downside to this change is that an explicit
+                        * /bin/foo invocation fill fork and exec /bin/foo, even if
+                        * /bin/foo is a symlink to busybox.
+                        */
+                       search_applet.name = newJob->progs[i].argv[0];
+
+#ifdef BB_FEATURE_SH_BUILTINS_ALWAYS_WIN
+                       /* If you enable BB_FEATURE_SH_BUILTINS_ALWAYS_WIN, then
+                        * if you run /bin/cat, it will use BusyBox cat even if 
+                        * /bin/cat exists on the filesystem and is _not_ busybox.
+                        * Some systems want this, others do not.  Choose wisely.  :-)
+                        */
+                       search_applet.name = get_last_path_component(search_applet.name);
+#endif
+
+                       /* Do a binary search to find the applet entry given the name. */
+                       applet = bsearch(&search_applet, applets, NUM_APPLETS,
+                                       sizeof(struct BB_applet), applet_name_compare);
+                       if (applet != NULL) {
+                               int argc_l;
+                               char** argv=newJob->progs[i].argv;
+                               for(argc_l=0;*argv!=NULL; argv++, argc_l++);
+                               applet_name=applet->name;
+                               optind = 1;
+                               exit((*(applet->main)) (argc_l, newJob->progs[i].argv));
                        }
 #endif
 
                        execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
                        fatalError("%s: %s\n", newJob->progs[i].argv[0],
-                                          strerror(errno));
+                                       strerror(errno));
                }
                if (outPipe[1]!=-1) {
                        close(outPipe[1]);
@@ -1231,10 +1280,10 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int
 
        /* add the theJob to the list of running jobs */
        if (!jobList->head) {
-               theJob = jobList->head = malloc(sizeof(*theJob));
+               theJob = jobList->head = xmalloc(sizeof(*theJob));
        } else {
                for (theJob = jobList->head; theJob->next; theJob = theJob->next);
-               theJob->next = malloc(sizeof(*theJob));
+               theJob->next = xmalloc(sizeof(*theJob));
                theJob = theJob->next;
        }
 
@@ -1277,7 +1326,7 @@ static int busy_loop(FILE * input)
        /* save current owner of TTY so we can restore it on exit */
        parent_pgrp = tcgetpgrp(0);
 
-       command = (char *) calloc(BUFSIZ, sizeof(char));
+       command = (char *) xcalloc(BUFSIZ, sizeof(char));
 
        /* don't pay any attention to this signal; it just confuses 
           things and isn't really meant for shells anyway */
@@ -1303,7 +1352,7 @@ static int busy_loop(FILE * input)
                        }
                        else {
                                free(command);
-                               command = (char *) calloc(BUFSIZ, sizeof(char));
+                               command = (char *) xcalloc(BUFSIZ, sizeof(char));
                                nextCommand = NULL;
                        }
                } else {
@@ -1430,10 +1479,7 @@ int shell_main(int argc_l, char **argv_l)
                fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
        } else if (local_pending_command==NULL) {
                //fprintf(stdout, "optind=%d  argv[optind]='%s'\n", optind, argv[optind]);
-               input = fopen(argv[optind], "r");
-               if (!input) {
-                       fatalError("%s: %s\n", argv[optind], strerror(errno));
-               }
+               input = xfopen(argv[optind], "r");
        }
 
        /* initialize the cwd -- this is never freed...*/