-static void checkJobs(struct jobSet * jobList)
-{
- 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;
- }
-
- 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");
-}
-
-static int getCommand(FILE * source, char * command)
-{
- if (source == stdin) {
- fprintf(stdout, "%s %s", cwd, prompt);
- fflush(stdout);
- }
-
- if (!fgets(command, MAX_COMMAND_LEN, source)) {
- if (source == stdin) printf("\n");
- return 1;
- }
-
- /* remove trailing newline */
- command[strlen(command) - 1] = '\0';
-
- return 0;
-}
-
-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;
-
- 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);
- }
-
- *argcAllocedPtr = argcAlloced;
- *argcPtr = argc;
-}
-
-/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
- line). If a valid command is found, commandPtr is set to point to
- 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
+static void checkjobs(struct jobset *j_list)
+{
+ struct job *job;
+ pid_t childpid;
+ int status;
+ int prognum = 0;
+
+ while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
+ for (job = j_list->head; job; job = job->next) {
+ prognum = 0;
+ while (prognum < job->num_progs &&
+ job->progs[prognum].pid != childpid) prognum++;
+ if (prognum < job->num_progs)
+ break;
+ }
+
+ /* This happens on backticked commands */
+ if(job==NULL)
+ return;
+
+ if (WIFEXITED(status) || WIFSIGNALED(status)) {
+ /* child exited */
+ job->running_progs--;
+ job->progs[prognum].pid = 0;
+
+ if (!job->running_progs) {
+ printf(JOB_STATUS_FORMAT, job->jobid, "Done", job->text);
+ last_jobid=0;
+ remove_job(j_list, job);
+ }
+ } else {
+ /* child stopped */
+ job->stopped_progs++;
+ job->progs[prognum].is_stopped = 1;
+
+#if 0
+ /* Printing this stuff is a pain, since it tends to
+ * overwrite the prompt an inconveinient moments. So
+ * don't do that. */
+ if (job->stopped_progs == job->num_progs) {
+ printf(JOB_STATUS_FORMAT, job->jobid, "Stopped",
+ job->text);
+ }
+#endif
+ }
+ }
+
+ if (childpid == -1 && errno != ECHILD)
+ bb_perror_msg("waitpid");
+}
+#else
+static void checkjobs(struct jobset *j_list)
+{
+}
+static void free_job(struct job *cmd)
+{
+}
+static void remove_job(struct jobset *j_list, struct job *job)
+{
+}
+#endif
+
+#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
+/* squirrel != NULL means we squirrel away copies of stdin, stdout,
+ * and stderr if they are redirected. */
+static int setup_redirects(struct child_prog *prog, int squirrel[])
+{
+ int i;
+ int openfd;
+ int mode = O_RDONLY;
+ struct redir_struct *redir = prog->redirects;
+
+ for (i = 0; i < prog->num_redirects; i++, redir++) {
+ switch (redir->type) {
+ case REDIRECT_INPUT:
+ mode = O_RDONLY;
+ break;
+ case REDIRECT_OVERWRITE:
+ mode = O_WRONLY | O_CREAT | O_TRUNC;
+ break;
+ case REDIRECT_APPEND:
+ mode = O_WRONLY | 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!) */
+ bb_perror_msg("error opening %s", redir->filename);
+ return 1;
+ }
+
+ if (openfd != redir->fd) {
+ if (squirrel && redir->fd < 3) {
+ squirrel[redir->fd] = dup(redir->fd);
+ }
+ dup2(openfd, redir->fd);
+ close(openfd);
+ }
+ }
+
+ return 0;
+}
+
+static void restore_redirects(int squirrel[])
+{
+ int i, fd;
+ for (i=0; i<3; i++) {
+ fd = squirrel[i];
+ if (fd != -1) {
+ /* No error checking. I sure wouldn't know what
+ * to do with an error if I found one! */
+ dup2(fd, i);
+ close(fd);
+ }
+ }
+}
+#else
+static inline int setup_redirects(struct child_prog *prog, int squirrel[])
+{
+ return 0;
+}
+static inline void restore_redirects(int squirrel[])
+{
+}
+#endif
+
+static inline void cmdedit_set_initial_prompt(void)
+{
+#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
+ PS1 = NULL;
+#else
+ PS1 = getenv("PS1");
+ if(PS1==0)
+ PS1 = "\\w \\$ ";
+#endif
+}
+
+static inline void setup_prompt_string(char **prompt_str)
+{
+#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
+ /* Set up the prompt */
+ if (shell_context == 0) {
+ free(PS1);
+ PS1=xmalloc(strlen(cwd)+4);
+ sprintf(PS1, "%s %s", cwd, ( geteuid() != 0 ) ? "$ ":"# ");
+ *prompt_str = PS1;
+ } else {
+ *prompt_str = PS2;
+ }
+#else
+ *prompt_str = (shell_context==0)? PS1 : PS2;
+#endif
+}
+
+static int get_command(FILE * source, char *command)
+{
+ char *prompt_str;
+
+ if (source == NULL) {
+ if (local_pending_command) {
+ /* a command specified (-c option): return it & mark it done */
+ strcpy(command, local_pending_command);
+ free(local_pending_command);
+ local_pending_command = NULL;
+ return 0;
+ }
+ return 1;
+ }
+
+ if (source == stdin) {
+ setup_prompt_string(&prompt_str);
+
+#ifdef CONFIG_FEATURE_COMMAND_EDITING
+ /*
+ ** enable command line editing only while a command line
+ ** is actually being read; otherwise, we'll end up bequeathing
+ ** atexit() handlers and other unwanted stuff to our
+ ** child processes (rob@sysgo.de)
+ */
+ cmdedit_read_input(prompt_str, command);
+ return 0;
+#else
+ fputs(prompt_str, stdout);
+#endif
+ }
+
+ if (!fgets(command, BUFSIZ - 2, source)) {
+ if (source == stdin)
+ printf("\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+static char* itoa(register int i)
+{
+ static char a[7]; /* Max 7 ints */
+ register char *b = a + sizeof(a) - 1;
+ int sign = (i < 0);
+
+ if (sign)
+ i = -i;
+ *b = 0;
+ do
+ {
+ *--b = '0' + (i % 10);
+ i /= 10;
+ }
+ while (i);
+ if (sign)
+ *--b = '-';
+ return b;
+}
+
+char * strsep_space( char *string, int * ix)
+{
+ char *token, *begin;
+
+ begin = string;
+
+ /* Short circuit the trivial case */
+ if ( !string || ! string[*ix])
+ return NULL;
+
+ /* Find the end of the token. */
+ while( string && string[*ix] && !isspace(string[*ix]) ) {
+ (*ix)++;
+ }
+
+ /* Find the end of any whitespace trailing behind
+ * the token and let that be part of the token */
+ while( string && string[*ix] && isspace(string[*ix]) ) {
+ (*ix)++;
+ }
+
+ if (! string && *ix==0) {
+ /* Nothing useful was found */
+ return NULL;
+ }
+
+ token = xmalloc(*ix+1);
+ token[*ix] = '\0';
+ strncpy(token, string, *ix);
+
+ return token;
+}
+
+static int expand_arguments(char *command)
+{
+ int total_length=0, length, i, retval, ix = 0;
+ expand_t expand_result;
+ char *tmpcmd, *cmd, *cmd_copy;
+ char *src, *dst, *var;
+ const char *out_of_space = "out of space during expansion";
+ int flags = GLOB_NOCHECK
+#ifdef GLOB_BRACE
+ | GLOB_BRACE
+#endif
+#ifdef GLOB_TILDE
+ | GLOB_TILDE
+#endif
+ ;
+
+ /* get rid of the terminating \n */
+ chomp(command);
+
+ /* Fix up escape sequences to be the Real Thing(tm) */
+ while( command && command[ix]) {
+ if (command[ix] == '\\') {
+ const char *tmp = command+ix+1;
+ command[ix] = bb_process_escape_sequence( &tmp );
+ memmove(command+ix + 1, tmp, strlen(tmp)+1);
+ }
+ ix++;
+ }
+ /* Use glob and then fixup environment variables and such */
+
+ /* It turns out that glob is very stupid. We have to feed it one word at a
+ * time since it can't cope with a full string. Here we convert command
+ * (char*) into cmd (char**, one word per string) */
+
+ /* We need a clean copy, so strsep can mess up the copy while
+ * we write stuff into the original (in a minute) */
+ cmd = cmd_copy = bb_xstrdup(command);
+ *command = '\0';
+ for (ix = 0, tmpcmd = cmd;
+ (tmpcmd = strsep_space(cmd, &ix)) != NULL; cmd += ix, ix=0) {
+ if (*tmpcmd == '\0')
+ break;
+ /* we need to trim() the result for glob! */
+ trim(tmpcmd);
+ retval = glob(tmpcmd, flags, NULL, &expand_result);
+ free(tmpcmd); /* Free mem allocated by strsep_space */
+ if (retval == GLOB_NOSPACE) {
+ /* Mem may have been allocated... */
+ globfree (&expand_result);
+ bb_error_msg(out_of_space);
+ return FALSE;
+ } else if (retval != 0) {
+ /* Some other error. GLOB_NOMATCH shouldn't
+ * happen because of the GLOB_NOCHECK flag in
+ * the glob call. */
+ bb_error_msg("syntax error");
+ return FALSE;
+ } else {
+ /* Convert from char** (one word per string) to a simple char*,
+ * but don't overflow command which is BUFSIZ in length */
+ for (i=0; i < expand_result.gl_pathc; i++) {
+ length=strlen(expand_result.gl_pathv[i]);
+ if (total_length+length+1 >= BUFSIZ) {
+ bb_error_msg(out_of_space);
+ return FALSE;
+ }
+ strcat(command+total_length, " ");
+ total_length+=1;
+ strcat(command+total_length, expand_result.gl_pathv[i]);
+ total_length+=length;
+ }
+ globfree (&expand_result);
+ }
+ }
+ free(cmd_copy);
+ trim(command);
+
+ /* Now do the shell variable substitutions which
+ * wordexp can't do for us, namely $? and $! */
+ src = command;
+ while((dst = strchr(src,'$')) != NULL){
+ var = NULL;
+ switch(*(dst+1)) {
+ case '?':
+ var = itoa(last_return_code);
+ break;
+ case '!':
+ if (last_bg_pid==-1)
+ *(var)='\0';
+ else
+ var = itoa(last_bg_pid);
+ break;
+ /* Everything else like $$, $#, $[0-9], etc. should all be
+ * expanded by wordexp(), so we can in theory skip that stuff
+ * here, but just to be on the safe side (i.e., since uClibc
+ * wordexp doesn't do this stuff yet), lets leave it in for
+ * now. */
+ case '$':
+ var = itoa(getpid());
+ break;
+ case '#':
+ var = itoa(argc-1);
+ break;
+ case '0':case '1':case '2':case '3':case '4':
+ case '5':case '6':case '7':case '8':case '9':
+ {
+ int ixx=*(dst+1)-48+1;
+ if (ixx >= argc) {
+ var='\0';
+ } else {
+ var = argv[ixx];
+ }
+ }
+ break;
+
+ }
+ if (var) {
+ /* a single character construction was found, and
+ * already handled in the case statement */
+ src=dst+2;
+ } else {
+ /* Looks like an environment variable */
+ char delim_hold;
+ int num_skip_chars=0;
+ int dstlen = strlen(dst);
+ /* Is this a ${foo} type variable? */
+ if (dstlen >=2 && *(dst+1) == '{') {
+ src=strchr(dst+1, '}');
+ num_skip_chars=1;
+ } else {
+ src=dst+1;
+ while(isalnum(*src) || *src=='_') src++;
+ }
+ if (src == NULL) {
+ src = dst+dstlen;
+ }
+ delim_hold=*src;
+ *src='\0'; /* temporary */
+ var = getenv(dst + 1 + num_skip_chars);
+ *src=delim_hold;
+ src += num_skip_chars;
+ }
+ if (var == NULL) {
+ /* Seems we got an un-expandable variable. So delete it. */
+ var = "";
+ }
+ {
+ int subst_len = strlen(var);
+ int trail_len = strlen(src);
+ if (dst+subst_len+trail_len >= command+BUFSIZ) {
+ bb_error_msg(out_of_space);
+ return FALSE;
+ }
+ /* Move stuff to the end of the string to accommodate
+ * filling the created gap with the new stuff */
+ memmove(dst+subst_len, src, trail_len+1);
+ /* Now copy in the new stuff */
+ memcpy(dst, var, subst_len);
+ src = dst+subst_len;
+ }
+ }
+
+ return TRUE;
+}
+
+/* Return cmd->num_progs as 0 if no command is present (e.g. an empty
+ line). If a valid command is found, command_ptr is set to point to
+ 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