//#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>
/* 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);
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},
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}
};
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 */
/* Now run the file */
status = busy_loop(input);
+ fclose(input);
return (status);
}
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;
}
#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;
}
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;
/* +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;
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]) {
/* 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;
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 */
/* 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)
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);
/* 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
}
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;
}
}
*commandPtr = returnCommand;
-
+
return 0;
}
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) {
}
}
#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]);
/* 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;
}
/* 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 */
}
else {
free(command);
- command = (char *) calloc(BUFSIZ, sizeof(char));
+ command = (char *) xcalloc(BUFSIZ, sizeof(char));
nextCommand = NULL;
}
} else {
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...*/