summaryrefslogtreecommitdiffhomepage
path: root/shell/lash.c
diff options
context:
space:
mode:
Diffstat (limited to 'shell/lash.c')
-rw-r--r--shell/lash.c1255
1 files changed, 649 insertions, 606 deletions
diff --git a/shell/lash.c b/shell/lash.c
index 9e467dc54..498f43779 100644
--- a/shell/lash.c
+++ b/shell/lash.c
@@ -39,293 +39,306 @@
#include <unistd.h>
-#define MAX_COMMAND_LEN 250 /* max length of a single command
- string */
+#ifdef BB_FEATURE_SH_COMMAND_EDITING
+#include "cmdedit.h"
+#endif
+
#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
-enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE, REDIRECT_APPEND };
+
+enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
+ 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 */
-static int shell_cd(struct job* cmd, struct jobSet* junk);
-static int shell_env(struct job* dummy, struct jobSet* junk);
-static int shell_exit(struct job* cmd, struct jobSet* junk);
-static int shell_fg_bg(struct job* cmd, struct jobSet* jobList);
-static int shell_help(struct job* cmd, struct jobSet* junk);
-static int shell_jobs(struct job* dummy, struct jobSet* jobList);
-static int shell_pwd(struct job* dummy, struct jobSet* junk);
-static int shell_set(struct job* cmd, struct jobSet* junk);
-static int shell_source(struct job* cmd, struct jobSet* jobList);
-static int shell_unset(struct job* cmd, struct jobSet* junk);
-
-static void checkJobs(struct jobSet * jobList);
-static int getCommand(FILE * source, char * command);
-static int parseCommand(char ** commandPtr, struct job * job, int * isBg);
-static int setupRedirections(struct childProgram * prog);
-static int runCommand(struct job newJob, struct jobSet * jobList, int inBg);
+static int shell_cd(struct job *cmd, struct jobSet *junk);
+static int shell_env(struct job *dummy, struct jobSet *junk);
+static int shell_exit(struct job *cmd, struct jobSet *junk);
+static int shell_fg_bg(struct job *cmd, struct jobSet *jobList);
+static int shell_help(struct job *cmd, struct jobSet *junk);
+static int shell_jobs(struct job *dummy, struct jobSet *jobList);
+static int shell_pwd(struct job *dummy, struct jobSet *junk);
+static int shell_set(struct job *cmd, struct jobSet *junk);
+static int shell_source(struct job *cmd, struct jobSet *jobList);
+static int shell_unset(struct job *cmd, struct jobSet *junk);
+
+static void checkJobs(struct jobSet *jobList);
+static int getCommand(FILE * source, char *command);
+static int parseCommand(char **commandPtr, struct job *job, int *isBg);
+static int setupRedirections(struct childProgram *prog);
+static int runCommand(struct job newJob, struct jobSet *jobList, int inBg);
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},
- {"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},
- //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo},
- {".", "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];
static char *prompt = "# ";
+
/* built-in 'cd <path>' handler */
-static int shell_cd(struct job* cmd, struct jobSet* junk)
+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));
+ 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;
+ return TRUE;
}
/* built-in 'env' handler */
-static int shell_env(struct job* dummy, struct jobSet* junk)
+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)
+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)
+static int shell_fg_bg(struct job *cmd, struct jobSet *jobList)
{
- int i, jobNum;
- struct job* job;
-
- 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;
-
- 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 (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;
-
- kill(-job->pgrp, SIGCONT);
-
- job->stoppedProgs = 0;
-
- return TRUE;
+ 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;
+ }
+ } 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 (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;
+
+ kill(-job->pgrp, SIGCONT);
+
+ job->stoppedProgs = 0;
+
+ return TRUE;
}
/* built-in 'help' handler */
-static int shell_help(struct job* cmd, struct jobSet* junk)
+static int shell_help(struct job *cmd, struct jobSet *junk)
{
- struct builtInCommand *x;
+ 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;
+ 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)
+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";
-
- printf(JOB_STATUS_FORMAT, job->jobId, statusString,
- job->text);
- }
- return TRUE;
+ 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;
}
/* built-in 'pwd' handler */
-static int shell_pwd(struct job* dummy, struct jobSet* junk)
+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)
+static int shell_set(struct job *cmd, struct jobSet *junk)
{
- int 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);
+ 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)
+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;
- }
+ FILE *input;
+ int status;
- /* Now run the file */
- status = busy_loop(input);
- return (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)
+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)
+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[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);
+ if (cmd->text)
+ free(cmd->text);
free(cmd->cmdBuf);
}
/* remove a job from the jobList */
-static void removeJob(struct jobSet * jobList, struct job * job)
+static void removeJob(struct jobSet *jobList, struct job *job)
{
- struct job * prevJob;
+ struct job *prevJob;
- freeJob(job);
+ freeJob(job);
if (job == jobList->head) {
- jobList->head = job->next;
+ jobList->head = job->next;
} else {
- prevJob = jobList->head;
- while (prevJob->next != job) prevJob = prevJob->next;
- prevJob->next = job->next;
+ prevJob = jobList->head;
+ while (prevJob->next != job)
+ prevJob = prevJob->next;
+ prevJob->next = job->next;
}
free(job);
@@ -333,56 +346,62 @@ static void removeJob(struct jobSet * jobList, struct job * 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)
+static void checkJobs(struct jobSet *jobList)
{
- struct job * job;
+ struct job *job;
pid_t childpid;
int status;
- int progNum=0;
-
+ 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;
- }
-
- 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->stoppedProgs == job->numProgs) {
- printf(JOB_STATUS_FORMAT, job->jobId, "Stopped", job->text);
- }
- }
+ 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 (!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 (childpid == -1 && errno != ECHILD)
- perror("waitpid");
+ perror("waitpid");
}
-static int getCommand(FILE * source, char * command)
+static int getCommand(FILE * source, char *command)
{
if (source == stdin) {
- fprintf(stdout, "%s %s", cwd, prompt);
- fflush(stdout);
+ 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;
+#endif
}
- if (!fgets(command, MAX_COMMAND_LEN, source)) {
- if (source == stdin) printf("\n");
- return 1;
+ if (!fgets(command, BUFSIZ - 2, source)) {
+ if (source == stdin)
+ printf("\n");
+ return 1;
}
/* remove trailing newline */
@@ -391,46 +410,48 @@ static int getCommand(FILE * source, char * command)
return 0;
}
-static void globLastArgument(struct childProgram * prog, int * argcPtr,
- int * argcAllocedPtr)
+static void globLastArgument(struct childProgram *prog, int *argcPtr,
+ int *argcAllocedPtr)
{
int argc = *argcPtr;
int argcAlloced = *argcAllocedPtr;
int rc;
int flags;
int i;
- char * src, * dst;
+ char *src, *dst;
- if (argc > 1) { /* cmd->globResult is already initialized */
- flags = GLOB_APPEND;
- i = prog->globResult.gl_pathc;
+ if (argc > 1) { /* cmd->globResult is already initialized */
+ flags = GLOB_APPEND;
+ i = prog->globResult.gl_pathc;
} else {
- prog->freeGlob = 1;
- flags = 0;
- i = 0;
+ 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';
+ 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);
+ 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;
@@ -442,27 +463,28 @@ static void globLastArgument(struct childProgram * prog, int * argcPtr,
the beginning of the next command (if the original command had more
then one job associated with it) or NULL if no more commands are
present. */
-static int parseCommand(char ** commandPtr, struct job * job, int * isBg)
+static int parseCommand(char **commandPtr, struct job *job, int *isBg)
{
- char * command;
- char * returnCommand = NULL;
- char * src, * buf, * chptr;
+ char *command;
+ char *returnCommand = NULL;
+ char *src, *buf, *chptr;
int argc = 0;
int done = 0;
int argvAlloced;
int i;
- char quote = '\0';
+ char quote = '\0';
int count;
- struct childProgram * prog;
+ 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;
+ while (**commandPtr && isspace(**commandPtr))
+ (*commandPtr)++;
+
+ /* this handles empty lines or leading '#' characters */
+ if (!**commandPtr || (**commandPtr == '#')) {
+ job->numProgs = 0;
+ *commandPtr = NULL;
+ return 0;
}
*isBg = 0;
@@ -491,177 +513,185 @@ static int parseCommand(char ** commandPtr, struct job * job, int * isBg)
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 (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);
+ argc++;
+ globLastArgument(prog, &argc, &argvAlloced);
}
if (!argc) {
- freeJob(job);
- return 0;
+ freeJob(job);
+ return 0;
}
prog->argv[argc] = NULL;
if (!returnCommand) {
- job->text = malloc(strlen(*commandPtr) + 1);
- strcpy(job->text, *commandPtr);
+ job->text = malloc(strlen(*commandPtr) + 1);
+ strcpy(job->text, *commandPtr);
} else {
- /* This leaves any trailing spaces, which is a bit sloppy */
+ /* This leaves any trailing spaces, which is a bit sloppy */
- count = returnCommand - *commandPtr;
- job->text = malloc(count + 1);
- strncpy(job->text, *commandPtr, count);
- job->text[count] = '\0';
+ count = returnCommand - *commandPtr;
+ job->text = malloc(count + 1);
+ strncpy(job->text, *commandPtr, count);
+ job->text[count] = '\0';
}
*commandPtr = returnCommand;
@@ -669,63 +699,64 @@ static int parseCommand(char ** commandPtr, struct job * job, int * isBg)
return 0;
}
-static int runCommand(struct job newJob, struct jobSet * jobList,
- int inBg)
+static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
{
- struct job * job;
+ struct job *job;
int i;
int nextin, nextout;
- int pipefds[2]; /* pipefd[0] is for reading */
+ 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));
- }
+ for (x = bltins; x->cmd; x++) {
+ if (!strcmp(newJob.progs[0].argv[0], x->cmd)) {
+ return (x->function(&newJob, jobList));
+ }
}
nextin = 0, nextout = 1;
for (i = 0; i < newJob.numProgs; i++) {
- if ((i + 1) < newJob.numProgs) {
- pipe(pipefds);
- nextout = pipefds[1];
- } else {
- nextout = 1;
- }
-
- if (!(newJob.progs[i].pid = fork())) {
- signal(SIGTTOU, SIG_DFL);
-
- if (nextin != 0) {
- dup2(nextin, 0);
- close(nextin);
- }
-
- if (nextout != 1) {
- dup2(nextout, 1);
- close(nextout);
- }
-
- /* explicit redirections override pipes */
- setupRedirections(newJob.progs + i);
-
- execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
- fatalError( "sh: %s: %s\n", newJob.progs[i].argv[0],
- strerror(errno));
- }
-
- /* 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];
+ if ((i + 1) < newJob.numProgs) {
+ pipe(pipefds);
+ nextout = pipefds[1];
+ } else {
+ nextout = 1;
+ }
+
+ if (!(newJob.progs[i].pid = fork())) {
+ signal(SIGTTOU, SIG_DFL);
+
+ if (nextin != 0) {
+ dup2(nextin, 0);
+ close(nextin);
+ }
+
+ if (nextout != 1) {
+ dup2(nextout, 1);
+ close(nextout);
+ }
+
+ /* explicit redirections override pipes */
+ setupRedirections(newJob.progs + i);
+
+ execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
+ fatalError("sh: %s: %s\n", newJob.progs[i].argv[0],
+ strerror(errno));
+ }
+
+ /* 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;
@@ -733,16 +764,16 @@ static int runCommand(struct job newJob, struct jobSet * 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;
+ 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));
+ job = jobList->head = malloc(sizeof(*job));
} else {
- for (job = jobList->head; job->next; job = job->next);
- job->next = malloc(sizeof(*job));
- job = job->next;
+ for (job = jobList->head; job->next; job = job->next);
+ job->next = malloc(sizeof(*job));
+ job = job->next;
}
*job = newJob;
@@ -751,165 +782,177 @@ static int runCommand(struct job newJob, struct jobSet * jobList,
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 */
+ /* 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);
+ printf("[%d] %d\n", job->jobId,
+ newJob.progs[newJob.numProgs - 1].pid);
} else {
- jobList->fg = job;
+ jobList->fg = job;
+
+ /* move the new process group into the foreground */
- /* move the new process group into the foreground */
-
- if (tcsetpgrp(0, newJob.pgrp))
- perror("tcsetpgrp");
+ if (tcsetpgrp(0, newJob.pgrp))
+ perror("tcsetpgrp");
}
return 0;
}
-static int setupRedirections(struct childProgram * prog)
+static int setupRedirections(struct childProgram *prog)
{
int i;
int openfd;
- int mode=O_RDONLY;
- struct redirectionSpecifier * redir = prog->redirections;
+ 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;
- }
-
- if (openfd != redir->fd) {
- dup2(openfd, redir->fd);
- close(openfd);
- }
+ 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;
+ }
+
+ if (openfd != redir->fd) {
+ dup2(openfd, redir->fd);
+ close(openfd);
+ }
}
return 0;
}
-static int busy_loop(FILE * input)
+static int busy_loop(FILE * input)
{
- char command[MAX_COMMAND_LEN + 1];
- char * nextCommand = NULL;
+ 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));
+
/* 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) {
- if (!jobList.fg) {
- /* no job is in the foreground */
-
- /* see if any background processes have exited */
- checkJobs(&jobList);
-
- if (!nextCommand) {
- if (getCommand(input, command)) break;
- nextCommand = command;
- }
-
- if (!parseCommand(&nextCommand, &newJob, &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++;
-
- 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;
-
- if (!jobList.fg->runningProgs) {
- /* child exited */
-
- removeJob(&jobList, jobList.fg);
- jobList.fg = NULL;
-
- /* 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;
- }
- }
-
- if (!jobList.fg) {
- /* move the shell to the foreground */
- if (tcsetpgrp(0, getpid()))
- perror("tcsetpgrp");
- }
- }
- }
+ if (!jobList.fg) {
+ /* no job is in the foreground */
+
+ /* see if any background processes have exited */
+ checkJobs(&jobList);
+
+ if (!nextCommand) {
+ if (getCommand(input, command))
+ break;
+ nextCommand = command;
+ }
+
+ if (!parseCommand(&nextCommand, &newJob, &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++;
+
+ 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;
+
+ if (!jobList.fg->runningProgs) {
+ /* child exited */
+
+ removeJob(&jobList, jobList.fg);
+ jobList.fg = NULL;
+
+ /* 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;
+ }
+ }
+
+ if (!jobList.fg) {
+ /* move the shell to the foreground */
+ if (tcsetpgrp(0, getpid()))
+ perror("tcsetpgrp");
+ }
+ }
+ }
+ free( command);
return 0;
}
-int shell_main(int argc, char ** argv)
+int shell_main(int argc, char **argv)
{
- FILE * input = stdin;
+ 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]));
- }
-
- return (busy_loop( input));
+ 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]));
+
+ /* Set terminal IO to canonical mode, and save old term settings. */
+#ifdef BB_FEATURE_SH_COMMAND_EDITING
+ cmdedit_init();
+#endif
+ }
+
+ return (busy_loop(input));
}