summaryrefslogtreecommitdiffhomepage
path: root/sh.c
diff options
context:
space:
mode:
Diffstat (limited to 'sh.c')
-rw-r--r--sh.c1346
1 files changed, 676 insertions, 670 deletions
diff --git a/sh.c b/sh.c
index 498f43779..e143cfe74 100644
--- a/sh.c
+++ b/sh.c
@@ -47,46 +47,47 @@
enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
- REDIRECT_APPEND };
+ REDIRECT_APPEND
+};
struct jobSet {
- struct job *head; /* head of list of running jobs */
- struct job *fg; /* current foreground job */
+ struct job *head; /* head of list of running jobs */
+ struct job *fg; /* current foreground job */
};
struct redirectionSpecifier {
- enum redirectionType type; /* type of redirection */
- int fd; /* file descriptor being redirected */
- char *filename; /* file to redirect fd to */
+ enum redirectionType type; /* type of redirection */
+ int fd; /* file descriptor being redirected */
+ char *filename; /* file to redirect fd to */
};
struct childProgram {
- pid_t pid; /* 0 if exited */
- char **argv; /* program name and arguments */
- int numRedirections; /* elements in redirection array */
- struct redirectionSpecifier *redirections; /* I/O redirections */
- glob_t globResult; /* result of parameter globbing */
- int freeGlob; /* should we globfree(&globResult)? */
- int isStopped; /* is the program currently running? */
+ pid_t pid; /* 0 if exited */
+ char **argv; /* program name and arguments */
+ int numRedirections; /* elements in redirection array */
+ struct redirectionSpecifier *redirections; /* I/O redirections */
+ glob_t globResult; /* result of parameter globbing */
+ int freeGlob; /* should we globfree(&globResult)? */
+ int isStopped; /* is the program currently running? */
};
struct job {
- int jobId; /* job number */
- int numProgs; /* total number of programs in job */
- int runningProgs; /* number of programs running */
- char *text; /* name of job */
- char *cmdBuf; /* buffer various argv's point into */
- pid_t pgrp; /* process group ID for the job */
- struct childProgram *progs; /* array of programs in job */
- struct job *next; /* to track background commands */
- int stoppedProgs; /* number of programs alive, but stopped */
+ int jobId; /* job number */
+ int numProgs; /* total number of programs in job */
+ int runningProgs; /* number of programs running */
+ char *text; /* name of job */
+ char *cmdBuf; /* buffer various argv's point into */
+ pid_t pgrp; /* process group ID for the job */
+ struct childProgram *progs; /* array of programs in job */
+ struct job *next; /* to track background commands */
+ int stoppedProgs; /* number of programs alive, but stopped */
};
struct builtInCommand {
- char *cmd; /* name */
- char *descr; /* description */
- char *usage; /* usage */
- int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
+ char *cmd; /* name */
+ char *descr; /* description */
+ char *usage; /* usage */
+ int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
};
/* Some function prototypes */
@@ -111,24 +112,26 @@ static int busy_loop(FILE * input);
/* Table of built-in functions */
static struct builtInCommand bltins[] = {
- {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg},
- {"cd", "Change working directory", "cd [dir]", shell_cd},
- //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo},
- {"env", "Print all environment variables", "env", shell_env},
- {"exit", "Exit from shell()", "exit", shell_exit},
- {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg},
- {"jobs", "Lists the active jobs", "jobs", shell_jobs},
- {"pwd", "Print current directory", "pwd", shell_pwd},
- {"set", "Set environment variable", "set [VAR=value]", shell_set},
- {"unset", "Unset environment variable", "unset VAR", shell_unset},
- {".", "Source-in and run commands in a file", ". filename",
- shell_source},
- {"help", "List shell built-in commands", "help", shell_help},
- {NULL, NULL, NULL, NULL}
+ {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg},
+ {"cd", "Change working directory", "cd [dir]", shell_cd},
+ //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo},
+ {"env", "Print all environment variables", "env", shell_env},
+ {"exit", "Exit from shell()", "exit", shell_exit},
+ {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg},
+ {"jobs", "Lists the active jobs", "jobs", shell_jobs},
+ {"pwd", "Print current directory", "pwd", shell_pwd},
+ {"set", "Set environment variable", "set [VAR=value]", shell_set},
+ {"unset", "Unset environment variable", "unset VAR", shell_unset},
+
+ {".", "Source-in and run commands in a file", ". filename",
+ shell_source},
+ {"help", "List shell built-in commands", "help", shell_help},
+ {NULL, NULL, NULL, NULL}
};
static const char shell_usage[] =
- "sh [FILE]...\n\n" "The BusyBox command interpreter (shell).\n\n";
+
+ "sh [FILE]...\n\n" "The BusyBox command interpreter (shell).\n\n";
static char cwd[1024];
@@ -139,323 +142,326 @@ static char *prompt = "# ";
/* built-in 'cd <path>' handler */
static int shell_cd(struct job *cmd, struct jobSet *junk)
{
- char *newdir;
- if (!cmd->progs[0].argv[1] == 1)
- newdir = getenv("HOME");
- else
- newdir = cmd->progs[0].argv[1];
- if (chdir(newdir)) {
- printf("cd: %s: %s\n", newdir, strerror(errno));
- return FALSE;
- }
- getcwd(cwd, sizeof(cwd));
-
- return TRUE;
+ char *newdir;
+
+ if (!cmd->progs[0].argv[1] == 1)
+ newdir = getenv("HOME");
+ else
+ newdir = cmd->progs[0].argv[1];
+ if (chdir(newdir)) {
+ printf("cd: %s: %s\n", newdir, strerror(errno));
+ return FALSE;
+ }
+ getcwd(cwd, sizeof(cwd));
+
+ return TRUE;
}
/* built-in 'env' handler */
static int shell_env(struct job *dummy, struct jobSet *junk)
{
- char **e;
+ char **e;
- for (e = environ; *e; e++) {
- fprintf(stdout, "%s\n", *e);
- }
- return (0);
+ for (e = environ; *e; e++) {
+ fprintf(stdout, "%s\n", *e);
+ }
+ return (0);
}
/* built-in 'exit' handler */
static int shell_exit(struct job *cmd, struct jobSet *junk)
{
- if (!cmd->progs[0].argv[1] == 1)
- exit TRUE;
- else
- exit(atoi(cmd->progs[0].argv[1]));
+ if (!cmd->progs[0].argv[1] == 1)
+ exit TRUE;
+
+ else
+ exit(atoi(cmd->progs[0].argv[1]));
}
/* built-in 'fg' and 'bg' handler */
static int shell_fg_bg(struct job *cmd, struct jobSet *jobList)
{
- int i, jobNum;
- struct job *job;
-
- if (!jobList->head) {
- if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
- fprintf(stderr, "%s: exactly one argument is expected\n",
- cmd->progs[0].argv[0]);
- return FALSE;
- }
- if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
- fprintf(stderr, "%s: bad argument '%s'\n",
- cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
- return FALSE;
+ int i, jobNum;
+ struct job *job;
+
+ if (!jobList->head) {
+ if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
+ fprintf(stderr, "%s: exactly one argument is expected\n",
+ cmd->progs[0].argv[0]);
+ return FALSE;
+ }
+ if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
+ fprintf(stderr, "%s: bad argument '%s'\n",
+ cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
+ return FALSE;
+ for (job = jobList->head; job; job = job->next) {
+ if (job->jobId == jobNum) {
+ break;
+ }
+ }
+ }
+ } else {
+ job = jobList->head;
}
- } else {
- job = jobList->head;
- }
-
- for (job = jobList->head; job; job = job->next)
- if (job->jobId == jobNum)
- break;
-
- if (!job) {
- fprintf(stderr, "%s: unknown job %d\n",
- cmd->progs[0].argv[0], jobNum);
- return FALSE;
- }
- if (*cmd->progs[0].argv[0] == 'f') {
- /* Make this job the foreground job */
+ if (!job) {
+ fprintf(stderr, "%s: unknown job %d\n",
+ cmd->progs[0].argv[0], jobNum);
+ return FALSE;
+ }
- if (tcsetpgrp(0, job->pgrp))
- perror("tcsetpgrp");
- jobList->fg = job;
- }
+ if (*cmd->progs[0].argv[0] == 'f') {
+ /* Make this job the foreground job */
+ if (tcsetpgrp(0, job->pgrp))
+ perror("tcsetpgrp");
+ jobList->fg = job;
+ }
- /* Restart the processes in the job */
- for (i = 0; i < job->numProgs; i++)
- job->progs[i].isStopped = 0;
+ /* Restart the processes in the job */
+ for (i = 0; i < job->numProgs; i++)
+ job->progs[i].isStopped = 0;
- kill(-job->pgrp, SIGCONT);
+ kill(-job->pgrp, SIGCONT);
- job->stoppedProgs = 0;
+ job->stoppedProgs = 0;
- return TRUE;
+ return TRUE;
}
/* built-in 'help' handler */
static int shell_help(struct job *cmd, struct jobSet *junk)
{
- struct builtInCommand *x;
-
- fprintf(stdout, "\nBuilt-in commands:\n");
- fprintf(stdout, "-------------------\n");
- for (x = bltins; x->cmd; x++) {
- fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
- }
- fprintf(stdout, "\n\n");
- return TRUE;
+ struct builtInCommand *x;
+
+ fprintf(stdout, "\nBuilt-in commands:\n");
+ fprintf(stdout, "-------------------\n");
+ for (x = bltins; x->cmd; x++) {
+ fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
+ }
+ fprintf(stdout, "\n\n");
+ return TRUE;
}
/* built-in 'jobs' handler */
static int shell_jobs(struct job *dummy, struct jobSet *jobList)
{
- struct job *job;
- char *statusString;
- for (job = jobList->head; job; job = job->next) {
- if (job->runningProgs == job->stoppedProgs)
- statusString = "Stopped";
- else
- statusString = "Running";
+ struct job *job;
+ char *statusString;
+
+ for (job = jobList->head; job; job = job->next) {
+ if (job->runningProgs == job->stoppedProgs)
+ statusString = "Stopped";
+ else
+ statusString = "Running";
- printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
- }
- return TRUE;
+ printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
+ }
+ return TRUE;
}
/* built-in 'pwd' handler */
static int shell_pwd(struct job *dummy, struct jobSet *junk)
{
- getcwd(cwd, sizeof(cwd));
- fprintf(stdout, "%s\n", cwd);
- return TRUE;
+ getcwd(cwd, sizeof(cwd));
+ fprintf(stdout, "%s\n", cwd);
+ return TRUE;
}
/* built-in 'set VAR=value' handler */
static int shell_set(struct job *cmd, struct jobSet *junk)
{
- int res;
-
- if (!cmd->progs[0].argv[1] == 1) {
- return (shell_env(cmd, junk));
- }
- res = putenv(cmd->progs[0].argv[1]);
- if (res)
- fprintf(stdout, "set: %s\n", strerror(errno));
- return (res);
+ int res;
+
+ if (!cmd->progs[0].argv[1] == 1) {
+ return (shell_env(cmd, junk));
+ }
+ res = putenv(cmd->progs[0].argv[1]);
+ if (res)
+ fprintf(stdout, "set: %s\n", strerror(errno));
+ return (res);
}
/* Built-in '.' handler (read-in and execute commands from file) */
static int shell_source(struct job *cmd, struct jobSet *junk)
{
- FILE *input;
- int status;
-
- if (!cmd->progs[0].argv[1] == 1)
- return FALSE;
-
- input = fopen(cmd->progs[0].argv[1], "r");
- if (!input) {
- fprintf(stdout, "Couldn't open file '%s'\n",
- cmd->progs[0].argv[1]);
- return FALSE;
- }
-
- /* Now run the file */
- status = busy_loop(input);
- return (status);
+ FILE *input;
+ int status;
+
+ if (!cmd->progs[0].argv[1] == 1)
+ return FALSE;
+
+ input = fopen(cmd->progs[0].argv[1], "r");
+ if (!input) {
+ fprintf(stdout, "Couldn't open file '%s'\n",
+ cmd->progs[0].argv[1]);
+ return FALSE;
+ }
+
+ /* Now run the file */
+ status = busy_loop(input);
+ return (status);
}
/* built-in 'unset VAR' handler */
static int shell_unset(struct job *cmd, struct jobSet *junk)
{
- if (!cmd->progs[0].argv[1] == 1) {
- fprintf(stdout, "unset: parameter required.\n");
- return FALSE;
- }
- unsetenv(cmd->progs[0].argv[1]);
- return TRUE;
+ if (!cmd->progs[0].argv[1] == 1) {
+ fprintf(stdout, "unset: parameter required.\n");
+ return FALSE;
+ }
+ unsetenv(cmd->progs[0].argv[1]);
+ return TRUE;
}
/* free up all memory from a job */
static void freeJob(struct job *cmd)
{
- int i;
-
- for (i = 0; i < cmd->numProgs; i++) {
- free(cmd->progs[i].argv);
- if (cmd->progs[i].redirections)
- free(cmd->progs[i].redirections);
- if (cmd->progs[i].freeGlob)
- globfree(&cmd->progs[i].globResult);
- }
- free(cmd->progs);
- if (cmd->text)
- free(cmd->text);
- free(cmd->cmdBuf);
+ int i;
+
+ for (i = 0; i < cmd->numProgs; i++) {
+ free(cmd->progs[i].argv);
+ if (cmd->progs[i].redirections)
+ free(cmd->progs[i].redirections);
+ if (cmd->progs[i].freeGlob)
+ globfree(&cmd->progs[i].globResult);
+ }
+ free(cmd->progs);
+ if (cmd->text)
+ free(cmd->text);
+ free(cmd->cmdBuf);
}
/* remove a job from the jobList */
static void removeJob(struct jobSet *jobList, struct job *job)
{
- struct job *prevJob;
-
- freeJob(job);
- if (job == jobList->head) {
- jobList->head = job->next;
- } else {
- prevJob = jobList->head;
- while (prevJob->next != job)
- prevJob = prevJob->next;
- prevJob->next = job->next;
- }
-
- free(job);
+ struct job *prevJob;
+
+ freeJob(job);
+ if (job == jobList->head) {
+ jobList->head = job->next;
+ } else {
+ prevJob = jobList->head;
+ while (prevJob->next != job)
+ prevJob = prevJob->next;
+ prevJob->next = job->next;
+ }
+
+ free(job);
}
/* Checks to see if any background processes have exited -- if they
have, figure out why and see if a job has completed */
static void checkJobs(struct jobSet *jobList)
{
- struct job *job;
- pid_t childpid;
- int status;
- int progNum = 0;
+ struct job *job;
+ pid_t childpid;
+ int status;
+ int progNum = 0;
+
+ while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
+ for (job = jobList->head; job; job = job->next) {
+ progNum = 0;
+ while (progNum < job->numProgs &&
+ job->progs[progNum].pid != childpid) progNum++;
+ if (progNum < job->numProgs)
+ break;
+ }
- while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
- for (job = jobList->head; job; job = job->next) {
- progNum = 0;
- while (progNum < job->numProgs &&
- job->progs[progNum].pid != childpid) progNum++;
- if (progNum < job->numProgs)
- break;
- }
+ if (WIFEXITED(status) || WIFSIGNALED(status)) {
+ /* child exited */
+ job->runningProgs--;
+ job->progs[progNum].pid = 0;
- if (WIFEXITED(status) || WIFSIGNALED(status)) {
- /* child exited */
- job->runningProgs--;
- job->progs[progNum].pid = 0;
+ if (!job->runningProgs) {
+ printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
+ removeJob(jobList, job);
+ }
+ } else {
+ /* child stopped */
+ job->stoppedProgs++;
+ job->progs[progNum].isStopped = 1;
- if (!job->runningProgs) {
- printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
- removeJob(jobList, job);
- }
- } else {
- /* child stopped */
- job->stoppedProgs++;
- job->progs[progNum].isStopped = 1;
-
- if (job->stoppedProgs == job->numProgs) {
- printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
- job->text);
- }
+ if (job->stoppedProgs == job->numProgs) {
+ printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
+ job->text);
+ }
+ }
}
- }
- if (childpid == -1 && errno != ECHILD)
- perror("waitpid");
+ if (childpid == -1 && errno != ECHILD)
+ perror("waitpid");
}
static int getCommand(FILE * source, char *command)
{
- if (source == stdin) {
- fprintf(stdout, "BBSHELL %s %s", cwd, prompt);
- fflush(stdout);
+ if (source == stdin) {
+ fprintf(stdout, "BBSHELL %s %s", cwd, prompt);
+ fflush(stdout);
#ifdef BB_FEATURE_SH_COMMAND_EDITING
- cmdedit_read_input(fileno(stdin), fileno(stdout), command);
- return 0;
+ cmdedit_read_input(fileno(stdin), fileno(stdout), command);
+ return 0;
#endif
- }
+ }
- if (!fgets(command, BUFSIZ - 2, source)) {
- if (source == stdin)
- printf("\n");
- return 1;
- }
+ if (!fgets(command, BUFSIZ - 2, source)) {
+ if (source == stdin)
+ printf("\n");
+ return 1;
+ }
- /* remove trailing newline */
- command[strlen(command) - 1] = '\0';
+ /* remove trailing newline */
+ command[strlen(command) - 1] = '\0';
- return 0;
+ return 0;
}
static void globLastArgument(struct childProgram *prog, int *argcPtr,
- int *argcAllocedPtr)
+ int *argcAllocedPtr)
{
- int argc = *argcPtr;
- int argcAlloced = *argcAllocedPtr;
- int rc;
- int flags;
- int i;
- char *src, *dst;
-
- if (argc > 1) { /* cmd->globResult is already initialized */
- flags = GLOB_APPEND;
- i = prog->globResult.gl_pathc;
- } else {
- prog->freeGlob = 1;
- flags = 0;
- i = 0;
- }
-
- rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
- if (rc == GLOB_NOSPACE) {
- fprintf(stderr, "out of space during glob operation\n");
- return;
- } else if (rc == GLOB_NOMATCH ||
- (!rc && (prog->globResult.gl_pathc - i) == 1 &&
- !strcmp(prog->argv[argc - 1],
- prog->globResult.gl_pathv[i]))) {
- /* we need to remove whatever \ quoting is still present */
- src = dst = prog->argv[argc - 1];
- while (*src) {
- if (*src != '\\')
- *dst++ = *src;
- src++;
+ int argc = *argcPtr;
+ int argcAlloced = *argcAllocedPtr;
+ int rc;
+ int flags;
+ int i;
+ char *src, *dst;
+
+ if (argc > 1) { /* cmd->globResult is already initialized */
+ flags = GLOB_APPEND;
+ i = prog->globResult.gl_pathc;
+ } else {
+ prog->freeGlob = 1;
+ flags = 0;
+ i = 0;
+ }
+
+ rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
+ if (rc == GLOB_NOSPACE) {
+ fprintf(stderr, "out of space during glob operation\n");
+ return;
+ } else if (rc == GLOB_NOMATCH ||
+ (!rc && (prog->globResult.gl_pathc - i) == 1 &&
+ !strcmp(prog->argv[argc - 1],
+ prog->globResult.gl_pathv[i]))) {
+ /* we need to remove whatever \ quoting is still present */
+ src = dst = prog->argv[argc - 1];
+ while (*src) {
+ if (*src != '\\')
+ *dst++ = *src;
+ src++;
+ }
+ *dst = '\0';
+ } else if (!rc) {
+ argcAlloced += (prog->globResult.gl_pathc - i);
+ prog->argv =
+ realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
+ memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
+ sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
+ argc += (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 - 1), prog->globResult.gl_pathv + i,
- sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
- argc += (prog->globResult.gl_pathc - i - 1);
- }
-
- *argcAllocedPtr = argcAlloced;
- *argcPtr = argc;
+
+ *argcAllocedPtr = argcAlloced;
+ *argcPtr = argc;
}
/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
@@ -465,396 +471,396 @@ static void globLastArgument(struct childProgram *prog, int *argcPtr,
present. */
static int parseCommand(char **commandPtr, struct job *job, int *isBg)
{
- char *command;
- char *returnCommand = NULL;
- char *src, *buf, *chptr;
- int argc = 0;
- int done = 0;
- int argvAlloced;
- int i;
- char quote = '\0';
- int count;
- struct childProgram *prog;
-
- /* skip leading white space */
- while (**commandPtr && isspace(**commandPtr))
- (*commandPtr)++;
-
- /* this handles empty lines or leading '#' characters */
- if (!**commandPtr || (**commandPtr == '#')) {
- job->numProgs = 0;
- *commandPtr = NULL;
- return 0;
- }
-
- *isBg = 0;
- job->numProgs = 1;
- job->progs = malloc(sizeof(*job->progs));
-
- /* We set the argv elements to point inside of this string. The
- memory is freed by freeJob().
-
- 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(1, strlen(*commandPtr) + 1);
- job->text = NULL;
-
- prog = job->progs;
- prog->numRedirections = 0;
- prog->redirections = NULL;
- prog->freeGlob = 0;
- prog->isStopped = 0;
-
- argvAlloced = 5;
- prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
- prog->argv[0] = job->cmdBuf;
-
- buf = command;
- src = *commandPtr;
- while (*src && !done) {
- if (quote == *src) {
- quote = '\0';
- } else if (quote) {
- if (*src == '\\') {
- src++;
- if (!*src) {
- fprintf(stderr, "character expected after \\\n");
- freeJob(job);
- return 1;
- }
+ char *command;
+ char *returnCommand = NULL;
+ char *src, *buf, *chptr;
+ int argc = 0;
+ int done = 0;
+ int argvAlloced;
+ int i;
+ char quote = '\0';
+ int count;
+ struct childProgram *prog;
+
+ /* skip leading white space */
+ while (**commandPtr && isspace(**commandPtr))
+ (*commandPtr)++;
+
+ /* this handles empty lines or leading '#' characters */
+ if (!**commandPtr || (**commandPtr == '#')) {
+ job->numProgs = 0;
+ *commandPtr = NULL;
+ return 0;
+ }
- /* in shell, "\'" should yield \' */
- if (*src != quote)
- *buf++ = '\\';
- } else if (*src == '*' || *src == '?' || *src == '[' ||
- *src == ']') *buf++ = '\\';
- *buf++ = *src;
- } else if (isspace(*src)) {
- if (*prog->argv[argc]) {
- buf++, argc++;
- /* +1 here leaves room for the NULL which ends argv */
- if ((argc + 1) == argvAlloced) {
- argvAlloced += 5;
- prog->argv = realloc(prog->argv,
- sizeof(*prog->argv) *
- argvAlloced);
- }
- prog->argv[argc] = buf;
+ *isBg = 0;
+ job->numProgs = 1;
+ job->progs = malloc(sizeof(*job->progs));
+
+ /* We set the argv elements to point inside of this string. The
+ memory is freed by freeJob().
+
+ 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(1, strlen(*commandPtr) + 1);
+ job->text = NULL;
+
+ prog = job->progs;
+ prog->numRedirections = 0;
+ prog->redirections = NULL;
+ prog->freeGlob = 0;
+ prog->isStopped = 0;
+
+ argvAlloced = 5;
+ prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
+ prog->argv[0] = job->cmdBuf;
+
+ buf = command;
+ src = *commandPtr;
+ while (*src && !done) {
+ if (quote == *src) {
+ quote = '\0';
+ } else if (quote) {
+ if (*src == '\\') {
+ src++;
+ if (!*src) {
+ fprintf(stderr, "character expected after \\\n");
+ freeJob(job);
+ return 1;
+ }
+
+ /* in shell, "\'" should yield \' */
+ if (*src != quote)
+ *buf++ = '\\';
+ } else if (*src == '*' || *src == '?' || *src == '[' ||
+ *src == ']') *buf++ = '\\';
+ *buf++ = *src;
+ } else if (isspace(*src)) {
+ if (*prog->argv[argc]) {
+ buf++, argc++;
+ /* +1 here leaves room for the NULL which ends argv */
+ if ((argc + 1) == argvAlloced) {
+ argvAlloced += 5;
+ prog->argv = realloc(prog->argv,
+ sizeof(*prog->argv) *
+ argvAlloced);
+ }
+ prog->argv[argc] = buf;
+
+ globLastArgument(prog, &argc, &argvAlloced);
+ }
+ } else
+ switch (*src) {
+ case '"':
+ case '\'':
+ quote = *src;
+ break;
+
+ case '#': /* comment */
+ done = 1;
+ break;
+
+ case '>': /* redirections */
+ case '<':
+ i = prog->numRedirections++;
+ prog->redirections = realloc(prog->redirections,
+ sizeof(*prog->redirections) *
+ (i + 1));
+
+ prog->redirections[i].fd = -1;
+ if (buf != prog->argv[argc]) {
+ /* the stuff before this character may be the file number
+ being redirected */
+ prog->redirections[i].fd =
+ strtol(prog->argv[argc], &chptr, 10);
+
+ if (*chptr && *prog->argv[argc]) {
+ buf++, argc++;
+ globLastArgument(prog, &argc, &argvAlloced);
+ }
+ }
+
+ if (prog->redirections[i].fd == -1) {
+ if (*src == '>')
+ prog->redirections[i].fd = 1;
+ else
+ prog->redirections[i].fd = 0;
+ }
+
+ if (*src++ == '>') {
+ if (*src == '>')
+ prog->redirections[i].type =
+ REDIRECT_APPEND, src++;
+ else
+ prog->redirections[i].type = REDIRECT_OVERWRITE;
+ } else {
+ prog->redirections[i].type = REDIRECT_INPUT;
+ }
+
+ /* This isn't POSIX sh compliant. Oh well. */
+ chptr = src;
+ while (isspace(*chptr))
+ chptr++;
+
+ if (!*chptr) {
+ fprintf(stderr, "file name expected after %c\n", *src);
+ freeJob(job);
+ return 1;
+ }
+
+ prog->redirections[i].filename = buf;
+ while (*chptr && !isspace(*chptr))
+ *buf++ = *chptr++;
+
+ src = chptr - 1; /* we src++ later */
+ prog->argv[argc] = ++buf;
+ break;
+
+ case '|': /* pipe */
+ /* finish this command */
+ if (*prog->argv[argc])
+ argc++;
+ if (!argc) {
+ fprintf(stderr, "empty command in pipe\n");
+ freeJob(job);
+ return 1;
+ }
+ prog->argv[argc] = NULL;
+
+ /* and start the next */
+ job->numProgs++;
+ job->progs = realloc(job->progs,
+ sizeof(*job->progs) * job->numProgs);
+ prog = job->progs + (job->numProgs - 1);
+ prog->numRedirections = 0;
+ prog->redirections = NULL;
+ prog->freeGlob = 0;
+ argc = 0;
+
+ argvAlloced = 5;
+ prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
+ prog->argv[0] = ++buf;
+
+ src++;
+ while (*src && isspace(*src))
+ src++;
+
+ if (!*src) {
+ fprintf(stderr, "empty command in pipe\n");
+ return 1;
+ }
+ src--; /* we'll ++ it at the end of the loop */
+
+ break;
+
+ case '&': /* background */
+ *isBg = 1;
+ case ';': /* multiple commands */
+ done = 1;
+ returnCommand = *commandPtr + (src - *commandPtr) + 1;
+ break;
+ case '\\':
+ src++;
+ if (!*src) {
+ freeJob(job);
+ fprintf(stderr, "character expected after \\\n");
+ return 1;
+ }
+ if (*src == '*' || *src == '[' || *src == ']'
+ || *src == '?') *buf++ = '\\';
+ /* fallthrough */
+ default:
+ *buf++ = *src;
+ }
+
+ src++;
+ }
+
+ if (*prog->argv[argc]) {
+ argc++;
globLastArgument(prog, &argc, &argvAlloced);
- }
- } else
- switch (*src) {
- case '"':
- case '\'':
- quote = *src;
- break;
-
- case '#': /* comment */
- done = 1;
- break;
-
- case '>': /* redirections */
- case '<':
- i = prog->numRedirections++;
- prog->redirections = realloc(prog->redirections,
- sizeof(*prog->redirections) *
- (i + 1));
-
- prog->redirections[i].fd = -1;
- if (buf != prog->argv[argc]) {
- /* the stuff before this character may be the file number
- being redirected */
- prog->redirections[i].fd =
- strtol(prog->argv[argc], &chptr, 10);
-
- if (*chptr && *prog->argv[argc]) {
- buf++, argc++;
- globLastArgument(prog, &argc, &argvAlloced);
- }
- }
+ }
+ if (!argc) {
+ freeJob(job);
+ return 0;
+ }
+ prog->argv[argc] = NULL;
- if (prog->redirections[i].fd == -1) {
- if (*src == '>')
- prog->redirections[i].fd = 1;
- else
- prog->redirections[i].fd = 0;
- }
+ if (!returnCommand) {
+ job->text = malloc(strlen(*commandPtr) + 1);
+ strcpy(job->text, *commandPtr);
+ } else {
+ /* This leaves any trailing spaces, which is a bit sloppy */
- if (*src++ == '>') {
- if (*src == '>')
- prog->redirections[i].type =
- REDIRECT_APPEND, src++;
- else
- prog->redirections[i].type = REDIRECT_OVERWRITE;
- } else {
- prog->redirections[i].type = REDIRECT_INPUT;
- }
+ count = returnCommand - *commandPtr;
+ job->text = malloc(count + 1);
+ strncpy(job->text, *commandPtr, count);
+ job->text[count] = '\0';
+ }
+
+ *commandPtr = returnCommand;
- /* This isn't POSIX sh compliant. Oh well. */
- chptr = src;
- while (isspace(*chptr))
- chptr++;
+ return 0;
+}
- if (!*chptr) {
- fprintf(stderr, "file name expected after %c\n", *src);
- freeJob(job);
- return 1;
+static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
+{
+ struct job *job;
+ int i;
+ int nextin, nextout;
+ int pipefds[2]; /* pipefd[0] is for reading */
+ struct builtInCommand *x;
+
+ /* handle built-ins here -- we don't fork() so we can't background
+ these very easily */
+ for (x = bltins; x->cmd; x++) {
+ if (!strcmp(newJob.progs[0].argv[0], x->cmd)) {
+ return (x->function(&newJob, jobList));
}
+ }
- prog->redirections[i].filename = buf;
- while (*chptr && !isspace(*chptr))
- *buf++ = *chptr++;
-
- src = chptr - 1; /* we src++ later */
- prog->argv[argc] = ++buf;
- break;
-
- case '|': /* pipe */
- /* finish this command */
- if (*prog->argv[argc])
- argc++;
- if (!argc) {
- fprintf(stderr, "empty command in pipe\n");
- freeJob(job);
- return 1;
+ nextin = 0, nextout = 1;
+ for (i = 0; i < newJob.numProgs; i++) {
+ if ((i + 1) < newJob.numProgs) {
+ pipe(pipefds);
+ nextout = pipefds[1];
+ } else {
+ nextout = 1;
}
- prog->argv[argc] = NULL;
-
- /* and start the next */
- job->numProgs++;
- job->progs = realloc(job->progs,
- sizeof(*job->progs) * job->numProgs);
- prog = job->progs + (job->numProgs - 1);
- prog->numRedirections = 0;
- prog->redirections = NULL;
- prog->freeGlob = 0;
- argc = 0;
-
- argvAlloced = 5;
- prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
- prog->argv[0] = ++buf;
- src++;
- while (*src && isspace(*src))
- src++;
+ if (!(newJob.progs[i].pid = fork())) {
+ signal(SIGTTOU, SIG_DFL);
- if (!*src) {
- fprintf(stderr, "empty command in pipe\n");
- return 1;
- }
- src--; /* we'll ++ it at the end of the loop */
+ if (nextin != 0) {
+ dup2(nextin, 0);
+ close(nextin);
+ }
- break;
+ if (nextout != 1) {
+ dup2(nextout, 1);
+ close(nextout);
+ }
- case '&': /* background */
- *isBg = 1;
- case ';': /* multiple commands */
- done = 1;
- returnCommand = *commandPtr + (src - *commandPtr) + 1;
- break;
+ /* explicit redirections override pipes */
+ setupRedirections(newJob.progs + i);
- case '\\':
- src++;
- if (!*src) {
- freeJob(job);
- fprintf(stderr, "character expected after \\\n");
- return 1;
+ execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
+ fatalError("sh: %s: %s\n", newJob.progs[i].argv[0],
+ strerror(errno));
}
- if (*src == '*' || *src == '[' || *src == ']'
- || *src == '?') *buf++ = '\\';
- /* fallthrough */
- default:
- *buf++ = *src;
- }
-
- src++;
- }
-
- if (*prog->argv[argc]) {
- argc++;
- globLastArgument(prog, &argc, &argvAlloced);
- }
- if (!argc) {
- freeJob(job);
- return 0;
- }
- prog->argv[argc] = NULL;
- if (!returnCommand) {
- job->text = malloc(strlen(*commandPtr) + 1);
- strcpy(job->text, *commandPtr);
- } else {
- /* This leaves any trailing spaces, which is a bit sloppy */
+ /* put our child in the process group whose leader is the
+ first process in this pipe */
+ setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
- count = returnCommand - *commandPtr;
- job->text = malloc(count + 1);
- strncpy(job->text, *commandPtr, count);
- job->text[count] = '\0';
- }
+ if (nextin != 0)
+ close(nextin);
+ if (nextout != 1)
+ close(nextout);
- *commandPtr = returnCommand;
+ /* If there isn't another process, nextin is garbage
+ but it doesn't matter */
+ nextin = pipefds[0];
+ }
- return 0;
-}
+ newJob.pgrp = newJob.progs[0].pid;
-static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
-{
- struct job *job;
- int i;
- int nextin, nextout;
- int pipefds[2]; /* pipefd[0] is for reading */
- struct builtInCommand *x;
-
- /* handle built-ins here -- we don't fork() so we can't background
- these very easily */
- for (x = bltins; x->cmd; x++) {
- if (!strcmp(newJob.progs[0].argv[0], x->cmd)) {
- return (x->function(&newJob, jobList));
- }
- }
+ /* find the ID for the job to use */
+ newJob.jobId = 1;
+ for (job = jobList->head; job; job = job->next)
+ if (job->jobId >= newJob.jobId)
+ newJob.jobId = job->jobId + 1;
- nextin = 0, nextout = 1;
- for (i = 0; i < newJob.numProgs; i++) {
- if ((i + 1) < newJob.numProgs) {
- pipe(pipefds);
- nextout = pipefds[1];
+ /* add the job to the list of running jobs */
+ if (!jobList->head) {
+ job = jobList->head = malloc(sizeof(*job));
} else {
- nextout = 1;
+ for (job = jobList->head; job->next; job = job->next);
+ job->next = malloc(sizeof(*job));
+ job = job->next;
}
- if (!(newJob.progs[i].pid = fork())) {
- signal(SIGTTOU, SIG_DFL);
+ *job = newJob;
+ job->next = NULL;
+ job->runningProgs = job->numProgs;
+ job->stoppedProgs = 0;
- if (nextin != 0) {
- dup2(nextin, 0);
- close(nextin);
- }
+ if (inBg) {
+ /* we don't wait for background jobs to return -- append it
+ to the list of backgrounded jobs and leave it alone */
- if (nextout != 1) {
- dup2(nextout, 1);
- close(nextout);
- }
+ printf("[%d] %d\n", job->jobId,
+ newJob.progs[newJob.numProgs - 1].pid);
+ } else {
+ jobList->fg = job;
- /* explicit redirections override pipes */
- setupRedirections(newJob.progs + i);
+ /* move the new process group into the foreground */
- execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
- fatalError("sh: %s: %s\n", newJob.progs[i].argv[0],
- strerror(errno));
+ if (tcsetpgrp(0, newJob.pgrp))
+ perror("tcsetpgrp");
}
- /* put our child in the process group whose leader is the
- first process in this pipe */
- setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
-
- if (nextin != 0)
- close(nextin);
- if (nextout != 1)
- close(nextout);
-
- /* If there isn't another process, nextin is garbage
- but it doesn't matter */
- nextin = pipefds[0];
- }
-
- newJob.pgrp = newJob.progs[0].pid;
-
- /* find the ID for the job to use */
- newJob.jobId = 1;
- for (job = jobList->head; job; job = job->next)
- if (job->jobId >= newJob.jobId)
- newJob.jobId = job->jobId + 1;
-
- /* add the job to the list of running jobs */
- if (!jobList->head) {
- job = jobList->head = malloc(sizeof(*job));
- } else {
- for (job = jobList->head; job->next; job = job->next);
- job->next = malloc(sizeof(*job));
- job = job->next;
- }
-
- *job = newJob;
- job->next = NULL;
- job->runningProgs = job->numProgs;
- job->stoppedProgs = 0;
-
- if (inBg) {
- /* we don't wait for background jobs to return -- append it
- to the list of backgrounded jobs and leave it alone */
-
- printf("[%d] %d\n", job->jobId,
- newJob.progs[newJob.numProgs - 1].pid);
- } else {
- jobList->fg = job;
-
- /* move the new process group into the foreground */
-
- if (tcsetpgrp(0, newJob.pgrp))
- perror("tcsetpgrp");
- }
-
- return 0;
+ return 0;
}
static int setupRedirections(struct childProgram *prog)
{
- int i;
- int openfd;
- int mode = O_RDONLY;
- struct redirectionSpecifier *redir = prog->redirections;
-
- for (i = 0; i < prog->numRedirections; i++, redir++) {
- switch (redir->type) {
- case REDIRECT_INPUT:
- mode = O_RDONLY;
- break;
- case REDIRECT_OVERWRITE:
- mode = O_RDWR | O_CREAT | O_TRUNC;
- break;
- case REDIRECT_APPEND:
- mode = O_RDWR | O_CREAT | O_APPEND;
- break;
- }
+ int i;
+ int openfd;
+ int mode = O_RDONLY;
+ struct redirectionSpecifier *redir = prog->redirections;
+
+ for (i = 0; i < prog->numRedirections; i++, redir++) {
+ switch (redir->type) {
+ case REDIRECT_INPUT:
+ mode = O_RDONLY;
+ break;
+ case REDIRECT_OVERWRITE:
+ mode = O_RDWR | O_CREAT | O_TRUNC;
+ break;
+ case REDIRECT_APPEND:
+ mode = O_RDWR | O_CREAT | O_APPEND;
+ break;
+ }
- openfd = open(redir->filename, mode, 0666);
- if (openfd < 0) {
- /* this could get lost if stderr has been redirected, but
- bash and ash both lose it as well (though zsh doesn't!) */
- fprintf(stderr, "error opening %s: %s\n", redir->filename,
- strerror(errno));
- return 1;
- }
+ openfd = open(redir->filename, mode, 0666);
+ if (openfd < 0) {
+ /* this could get lost if stderr has been redirected, but
+ bash and ash both lose it as well (though zsh doesn't!) */
+ fprintf(stderr, "error opening %s: %s\n", redir->filename,
+ strerror(errno));
+ return 1;
+ }
- if (openfd != redir->fd) {
- dup2(openfd, redir->fd);
- close(openfd);
+ if (openfd != redir->fd) {
+ dup2(openfd, redir->fd);
+ close(openfd);
+ }
}
- }
- return 0;
+ return 0;
}
static int busy_loop(FILE * input)
{
- char *command;
- char *nextCommand = NULL;
- struct jobSet jobList = { NULL, NULL };
- struct job newJob;
- int i;
- int status;
- int inBg;
+ char *command;
+ char *nextCommand = NULL;
+ struct jobSet jobList = { NULL, NULL };
+ struct job newJob;
+ int i;
+ int status;
+ int inBg;
- command = (char*) calloc(BUFSIZ, sizeof(char));
+ command = (char *) calloc(BUFSIZ, sizeof(char));
- /* don't pay any attention to this signal; it just confuses
- things and isn't really meant for shells anyway */
- signal(SIGTTOU, SIG_IGN);
+ /* don't pay any attention to this signal; it just confuses
+ things and isn't really meant for shells anyway */
+ signal(SIGTTOU, SIG_IGN);
- while (1) {
+ while (1) {
if (!jobList.fg) {
/* no job is in the foreground */
@@ -862,97 +868,97 @@ static int busy_loop(FILE * input)
checkJobs(&jobList);
if (!nextCommand) {
- if (getCommand(input, command))
- break;
- nextCommand = command;
+ if (getCommand(input, command))
+ break;
+ nextCommand = command;
}
if (!parseCommand(&nextCommand, &newJob, &inBg) &&
- newJob.numProgs) {
- runCommand(newJob, &jobList, inBg);
+ newJob.numProgs) {
+ runCommand(newJob, &jobList, inBg);
}
} else {
/* a job is running in the foreground; wait for it */
i = 0;
while (!jobList.fg->progs[i].pid ||
- jobList.fg->progs[i].isStopped) i++;
+ jobList.fg->progs[i].isStopped) i++;
waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
if (WIFEXITED(status) || WIFSIGNALED(status)) {
- /* the child exited */
- jobList.fg->runningProgs--;
- jobList.fg->progs[i].pid = 0;
+ /* the child exited */
+ jobList.fg->runningProgs--;
+ jobList.fg->progs[i].pid = 0;
- if (!jobList.fg->runningProgs) {
- /* child exited */
+ if (!jobList.fg->runningProgs) {
+ /* child exited */
- removeJob(&jobList, jobList.fg);
- jobList.fg = NULL;
+ removeJob(&jobList, jobList.fg);
+ jobList.fg = NULL;
- /* move the shell to the foreground */
- if (tcsetpgrp(0, getpid()))
- perror("tcsetpgrp");
- }
+ /* move the shell to the foreground */
+ if (tcsetpgrp(0, getpid()))
+ perror("tcsetpgrp");
+ }
} else {
- /* the child was stopped */
- jobList.fg->stoppedProgs++;
- jobList.fg->progs[i].isStopped = 1;
-
- if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
- printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
- "Stopped", jobList.fg->text);
- jobList.fg = NULL;
- }
+ /* the child was stopped */
+ jobList.fg->stoppedProgs++;
+ jobList.fg->progs[i].isStopped = 1;
+
+ if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
+ printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
+ "Stopped", jobList.fg->text);
+ jobList.fg = NULL;
+ }
}
if (!jobList.fg) {
- /* move the shell to the foreground */
- if (tcsetpgrp(0, getpid()))
- perror("tcsetpgrp");
+ /* move the shell to the foreground */
+ if (tcsetpgrp(0, getpid()))
+ perror("tcsetpgrp");
}
}
}
- free( command);
+ free(command);
- return 0;
+ return 0;
}
int shell_main(int argc, char **argv)
{
- FILE *input = stdin;
-
- if (argc > 2) {
- usage(shell_usage);
- }
- /* initialize the cwd */
- getcwd(cwd, sizeof(cwd));
-
-
- //if (argv[0] && argv[0][0] == '-') {
- // shell_source("/etc/profile");
- //}
-
- if (argc < 2) {
- fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER,
- BB_BT);
- fprintf(stdout,
- "Enter 'help' for a list of built-in commands.\n\n");
- } else {
- input = fopen(argv[1], "r");
- if (!input)
- fatalError("A: Couldn't open file '%s': %s\n", argv[1],
- strerror(errno));
+ FILE *input = stdin;
+
+ if (argc > 2) {
+ usage(shell_usage);
+ }
+ /* initialize the cwd */
+ getcwd(cwd, sizeof(cwd));
+
+
+ //if (argv[0] && argv[0][0] == '-') {
+ // shell_source("/etc/profile");
+ //}
+
+ if (argc < 2) {
+ fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER,
+ BB_BT);
+ fprintf(stdout,
+ "Enter 'help' for a list of built-in commands.\n\n");
+ } else {
+ input = fopen(argv[1], "r");
+ if (!input)
+ fatalError("A: Couldn't open file '%s': %s\n", argv[1],
+ strerror(errno));
// else
// fatalError("Got it.\n");
- //exit(shell_source(argv[1]));
+ //exit(shell_source(argv[1]));
- /* Set terminal IO to canonical mode, and save old term settings. */
+ /* Set terminal IO to canonical mode, and save old term settings. */
#ifdef BB_FEATURE_SH_COMMAND_EDITING
- cmdedit_init();
+ cmdedit_init();
#endif
- }
+ }
- return (busy_loop(input));
+ return (busy_loop(input));
}