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 cbaf16ef8b6c42bf3ca53c640d3ff17a2a30c6d0..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,6 +140,7 @@ 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},
@@ -219,6 +221,19 @@ 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)
 {
@@ -612,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;
                }
 
@@ -770,30 +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 = 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);
+       }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;
 }
@@ -1113,7 +1140,7 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
        }
 
        *commandPtr = returnCommand;
-
+       
        return 0;
 }
 
@@ -1125,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) {
@@ -1188,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]);
@@ -1432,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...*/