- 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;
- }
-
- /* 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);
- }
- 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 */
-
- count = returnCommand - *commandPtr;
- job->text = malloc(count + 1);
- strncpy(job->text, *commandPtr, count);
- job->text[count] = '\0';
- }
-
- *commandPtr = returnCommand;
-
- return 0;
-}
-
-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));
- }
- }
-
- 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];
- }
-
- 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;
-}
-
-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;
- }
-
- 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)
-{
- char command[MAX_COMMAND_LEN + 1];
- char * nextCommand = NULL;
- struct jobSet jobList = { NULL, NULL };
- struct job newJob;
- int i;
- int status;
- int inBg;
-
- /* 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");
- }
- }
- }
-
- 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 (!**command_ptr || (**command_ptr == '#')) {
+ job->num_progs=0;
+ return 0;
+ }
+
+ *inbg = 0;
+ job->num_progs = 1;
+ job->progs = xmalloc(sizeof(*job->progs));
+
+ /* We set the argv elements to point inside of this string. The
+ memory is freed by free_job(). Allocate twice the original
+ length in case we need to quote every single character.
+
+ 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 = xcalloc(2*strlen(*command_ptr) + 1, sizeof(char));
+ job->text = NULL;
+
+ prog = job->progs;
+ prog->num_redirects = 0;
+ prog->redirects = NULL;
+ prog->free_glob = 0;
+ prog->is_stopped = 0;
+ prog->family = job;
+
+ argv_alloced = 5;
+ prog->argv = xmalloc(sizeof(*prog->argv) * argv_alloced);
+ prog->argv[0] = job->cmdbuf;
+
+ buf = command;
+ src = *command_ptr;
+ while (*src && !done) {
+ if (quote == *src) {
+ quote = '\0';
+ } else if (quote) {
+ if (*src == '\\') {
+ src++;
+ if (!*src) {
+ error_msg("character expected after \\");
+ free_job(job);
+ return 1;
+ }
+
+ /* in shell, "\'" should yield \' */
+ if (*src != quote) {
+ *buf++ = '\\';
+ *buf++ = '\\';
+ }
+ } else if (*src == '*' || *src == '?' || *src == '[' ||
+ *src == ']') *buf++ = '\\';
+ *buf++ = *src;
+ } else if (isspace(*src)) {
+ if (*prog->argv[argc_l]) {
+ buf++, argc_l++;
+ /* +1 here leaves room for the NULL which ends argv */
+ if ((argc_l + 1) == argv_alloced) {
+ argv_alloced += 5;
+ prog->argv = xrealloc(prog->argv,
+ sizeof(*prog->argv) *
+ argv_alloced);
+ }
+ expand_argument(prog, &argc_l, &argv_alloced);
+ prog->argv[argc_l] = buf;
+ }
+ } else
+ switch (*src) {
+ case '"':
+ case '\'':
+ quote = *src;
+ break;
+
+ case '#': /* comment */
+ if (*(src-1)== '$')
+ *buf++ = *src;
+ else
+ done = 1;
+ break;
+
+ case '>': /* redirects */
+ case '<':
+ i = prog->num_redirects++;
+ prog->redirects = xrealloc(prog->redirects,
+ sizeof(*prog->redirects) *
+ (i + 1));
+
+ prog->redirects[i].fd = -1;
+ if (buf != prog->argv[argc_l]) {
+ /* the stuff before this character may be the file number
+ being redirected */
+ prog->redirects[i].fd =
+ strtol(prog->argv[argc_l], &chptr, 10);
+
+ if (*chptr && *prog->argv[argc_l]) {
+ buf++, argc_l++;
+ expand_argument(prog, &argc_l, &argv_alloced);
+ prog->argv[argc_l] = buf;
+ }
+ }
+
+ if (prog->redirects[i].fd == -1) {
+ if (*src == '>')
+ prog->redirects[i].fd = 1;
+ else
+ prog->redirects[i].fd = 0;
+ }
+
+ if (*src++ == '>') {
+ if (*src == '>')
+ prog->redirects[i].type =
+ REDIRECT_APPEND, src++;
+ else
+ prog->redirects[i].type = REDIRECT_OVERWRITE;
+ } else {
+ prog->redirects[i].type = REDIRECT_INPUT;
+ }
+
+ /* This isn't POSIX sh compliant. Oh well. */
+ chptr = src;
+ while (isspace(*chptr))
+ chptr++;
+
+ if (!*chptr) {
+ error_msg("file name expected after %c", *src);
+ free_job(job);
+ job->num_progs=0;
+ return 1;
+ }
+
+ prog->redirects[i].filename = buf;
+ while (*chptr && !isspace(*chptr))
+ *buf++ = *chptr++;
+
+ src = chptr - 1; /* we src++ later */
+ prog->argv[argc_l] = ++buf;
+ break;
+
+ case '|': /* pipe */
+ /* finish this command */
+ if (*prog->argv[argc_l])
+ argc_l++;
+ if (!argc_l) {
+ error_msg("empty command in pipe");
+ free_job(job);
+ job->num_progs=0;
+ return 1;
+ }
+ prog->argv[argc_l] = NULL;
+
+ /* and start the next */
+ job->num_progs++;
+ job->progs = xrealloc(job->progs,
+ sizeof(*job->progs) * job->num_progs);
+ prog = job->progs + (job->num_progs - 1);
+ prog->num_redirects = 0;
+ prog->redirects = NULL;
+ prog->free_glob = 0;
+ prog->is_stopped = 0;
+ prog->family = job;
+ argc_l = 0;
+
+ argv_alloced = 5;
+ prog->argv = xmalloc(sizeof(*prog->argv) * argv_alloced);
+ prog->argv[0] = ++buf;
+
+ src++;
+ while (*src && isspace(*src))
+ src++;
+
+ if (!*src) {
+ error_msg("empty command in pipe");
+ free_job(job);
+ job->num_progs=0;
+ return 1;
+ }
+ src--; /* we'll ++ it at the end of the loop */
+
+ break;
+
+ case '&': /* background */
+ *inbg = 1;
+ case ';': /* multiple commands */
+ done = 1;
+ return_command = *command_ptr + (src - *command_ptr) + 1;
+ break;
+
+#ifdef BB_FEATURE_SH_BACKTICKS
+ case '`':
+ /* Exec a backtick-ed command */
+ /* Besides any previous brokenness, I have not
+ * updated backtick handling for close_me support.
+ * I don't know if it needs it or not. -- LRD */
+ {
+ char* charptr1=NULL, *charptr2;
+ char* ptr=NULL;
+ struct job *newjob;
+ struct jobset njob_list = { NULL, NULL };
+ int pipefd[2];
+ int size;
+
+ ptr=strchr(++src, '`');
+ if (ptr==NULL) {
+ fprintf(stderr, "Unmatched '`' in command\n");
+ free_job(job);
+ return 1;
+ }
+
+ /* Make some space to hold just the backticked command */
+ charptr1 = charptr2 = xmalloc(1+ptr-src);
+ memcpy(charptr1, src, ptr-src);
+ charptr1[ptr-src] = '\0';
+ newjob = xmalloc(sizeof(struct job));
+ newjob->job_list = &njob_list;
+ /* Now parse and run the backticked command */
+ if (!parse_command(&charptr1, newjob, inbg)
+ && newjob->num_progs) {
+ pipe(pipefd);
+ run_command(newjob, 0, pipefd);
+ }
+ checkjobs(job->job_list);
+ free_job(newjob); /* doesn't actually free newjob,
+ looks like a memory leak */
+ free(charptr2);
+
+ /* Make a copy of any stuff left over in the command
+ * line after the second backtick */
+ charptr2 = xmalloc(strlen(ptr)+1);
+ memcpy(charptr2, ptr+1, strlen(ptr));
+
+
+ /* Copy the output from the backtick-ed command into the
+ * command line, making extra room as needed */
+ --src;
+ charptr1 = xmalloc(BUFSIZ);
+ while ( (size=full_read(pipefd[0], charptr1, BUFSIZ-1)) >0) {
+ int newsize=src - *command_ptr + size + 1 + strlen(charptr2);
+ if (newsize > BUFSIZ) {
+ *command_ptr=xrealloc(*command_ptr, newsize);
+ }
+ memcpy(src, charptr1, size);
+ src+=size;
+ }
+ free(charptr1);
+ close(pipefd[0]);
+ if (*(src-1)=='\n')
+ --src;
+
+ /* Now paste into the *command_ptr all the stuff
+ * leftover after the second backtick */
+ memcpy(src, charptr2, strlen(charptr2)+1);
+ free(charptr2);
+
+ /* Now recursively call parse_command to deal with the new
+ * and improved version of the command line with the backtick
+ * results expanded in place... */
+ {
+ struct jobset *jl=job->job_list;
+ free_job(job);
+ job->job_list = jl;
+ }
+ return(parse_command(command_ptr, job, inbg));
+ }
+ break;
+#endif // BB_FEATURE_SH_BACKTICKS
+
+ case '\\':
+ src++;
+ if (!*src) {
+/* This is currently a little broken... */
+#ifdef HANDLE_CONTINUATION_CHARS
+ /* They fed us a continuation char, so continue reading stuff
+ * on the next line, then tack that onto the end of the current
+ * command */
+ char *command;
+ int newsize;
+ printf("erik: found a continue char at EOL...\n");
+ command = (char *) xcalloc(BUFSIZ, sizeof(char));
+ if (get_command(input, command)) {
+ error_msg("character expected after \\");
+ free(command);
+ free_job(job);
+ return 1;
+ }
+ newsize = strlen(*command_ptr) + strlen(command) + 2;
+ if (newsize > BUFSIZ) {
+ printf("erik: doing realloc\n");
+ *command_ptr=xrealloc(*command_ptr, newsize);
+ }
+ printf("erik: A: *command_ptr='%s'\n", *command_ptr);
+ memcpy(--src, command, strlen(command));
+ printf("erik: B: *command_ptr='%s'\n", *command_ptr);
+ free(command);
+ break;
+#else
+ error_msg("character expected after \\");
+ free(command);
+ free_job(job);
+ return 1;
+#endif
+ }
+ if (*src == '*' || *src == '[' || *src == ']'
+ || *src == '?') *buf++ = '\\';
+ /* fallthrough */
+ default:
+ *buf++ = *src;
+ }
+
+ src++;
+ }
+
+ if (*prog->argv[argc_l]) {
+ argc_l++;
+ expand_argument(prog, &argc_l, &argv_alloced);
+ }
+ if (!argc_l) {
+ free_job(job);
+ return 0;
+ }
+ prog->argv[argc_l] = NULL;
+
+ if (!return_command) {
+ job->text = xmalloc(strlen(*command_ptr) + 1);
+ strcpy(job->text, *command_ptr);
+ } else {
+ /* This leaves any trailing spaces, which is a bit sloppy */
+ count = return_command - *command_ptr;
+ job->text = xmalloc(count + 1);
+ strncpy(job->text, *command_ptr, count);
+ job->text[count] = '\0';
+ }
+
+ *command_ptr = return_command;