Skip to content
Snippets Groups Projects
hush.c 89.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • Wolfgang Denk's avatar
    Wolfgang Denk committed
    	 ** 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, the_command);
    #else
    	fputs(prompt_str, stdout);
    	fflush(stdout);
    	the_command[0]=fgetc(i->file);
    	the_command[1]='\0';
    #endif
    	fflush(stdout);
    	i->p = the_command;
    #else
    	extern char console_buffer[CFG_CBSIZE];
    	int n;
    	static char the_command[CFG_CBSIZE];
    
    	i->__promptme = 1;
    	if (i->promptmode == 1) {
    		n = readline(CFG_PROMPT);
    	} else {
    		n = readline(CFG_PROMPT_HUSH_PS2);
    	}
    	if (n == -1 ) {
    		flag_repeat = 0;
    		i->__promptme = 0;
    	}
    	n = strlen(console_buffer);
    	console_buffer[n] = '\n';
    	console_buffer[n+1]= '\0';
    	if (had_ctrlc()) flag_repeat = 0;
    	clear_ctrlc();
    	do_repeat = 0;
    	if (i->promptmode == 1) {
    		if (console_buffer[0] == '\n'&& flag_repeat == 0) {
    			strcpy(the_command,console_buffer);
    		}
    		else {
    			if (console_buffer[0] != '\n') {
    				strcpy(the_command,console_buffer);
    				flag_repeat = 1;
    			}
    			else {
    				do_repeat = 1;
    			}
    		}
    		i->p = the_command;
    	}
    	else {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		if (console_buffer[0] != '\n') {
    			if (strlen(the_command) + strlen(console_buffer)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			    < CFG_CBSIZE) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				n = strlen(the_command);
    				the_command[n-1] = ' ';
    				strcpy(&the_command[n],console_buffer);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			}
    			else {
    				the_command[0] = '\n';
    				the_command[1] = '\0';
    				flag_repeat = 0;
    			}
    		}
    		if (i->__promptme == 0) {
    			the_command[0] = '\n';
    			the_command[1] = '\0';
    		}
    		i->p = console_buffer;
    	}
    #endif
    }
    
    /* This is the magic location that prints prompts
     * and gets data back from the user */
    static int file_get(struct in_str *i)
    {
    	int ch;
    
    	ch = 0;
    	/* If there is data waiting, eat it up */
    	if (i->p && *i->p) {
    		ch=*i->p++;
    	} else {
    		/* need to double check i->file because we might be doing something
    		 * more complicated by now, like sourcing or substituting. */
    #ifndef __U_BOOT__
    		if (i->__promptme && interactive && i->file == stdin) {
    			while(! i->p || (interactive && strlen(i->p)==0) ) {
    #else
    			while(! i->p  || strlen(i->p)==0 ) {
    #endif
    				get_user_input(i);
    			}
    			i->promptmode=2;
    #ifndef __U_BOOT__
    			i->__promptme = 0;
    #endif
    			if (i->p && *i->p) {
    				ch=*i->p++;
    			}
    #ifndef __U_BOOT__
    		} else {
    			ch = fgetc(i->file);
    		}
    
    #endif
    		debug_printf("b_getch: got a %d\n", ch);
    	}
    #ifndef __U_BOOT__
    	if (ch == '\n') i->__promptme=1;
    #endif
    	return ch;
    }
    
    /* All the callers guarantee this routine will never be
     * used right after a newline, so prompting is not needed.
     */
    static int file_peek(struct in_str *i)
    {
    #ifndef __U_BOOT__
    	if (i->p && *i->p) {
    #endif
    		return *i->p;
    #ifndef __U_BOOT__
    	} else {
    		i->peek_buf[0] = fgetc(i->file);
    		i->peek_buf[1] = '\0';
    		i->p = i->peek_buf;
    		debug_printf("b_peek: got a %d\n", *i->p);
    		return *i->p;
    	}
    #endif
    }
    
    #ifndef __U_BOOT__
    static void setup_file_in_str(struct in_str *i, FILE *f)
    #else
    static void setup_file_in_str(struct in_str *i)
    #endif
    {
    	i->peek = file_peek;
    	i->get = file_get;
    	i->__promptme=1;
    	i->promptmode=1;
    #ifndef __U_BOOT__
    	i->file = f;
    #endif
    	i->p = NULL;
    }
    
    static void setup_string_in_str(struct in_str *i, const char *s)
    {
    	i->peek = static_peek;
    	i->get = static_get;
    	i->__promptme=1;
    	i->promptmode=1;
    	i->p = s;
    }
    
    #ifndef __U_BOOT__
    static void mark_open(int fd)
    {
    	struct close_me *new = xmalloc(sizeof(struct close_me));
    	new->fd = fd;
    	new->next = close_me_head;
    	close_me_head = new;
    }
    
    static void mark_closed(int fd)
    {
    	struct close_me *tmp;
    	if (close_me_head == NULL || close_me_head->fd != fd)
    		error_msg_and_die("corrupt close_me");
    	tmp = close_me_head;
    	close_me_head = close_me_head->next;
    	free(tmp);
    }
    
    
    static void close_all(void)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    	struct close_me *c;
    	for (c=close_me_head; c; c=c->next) {
    		close(c->fd);
    	}
    	close_me_head = NULL;
    }
    
    /* 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 openfd, mode;
    	struct redir_struct *redir;
    
    	for (redir=prog->redirects; redir; redir=redir->next) {
    		if (redir->dup == -1 && redir->word.gl_pathv == NULL) {
    			/* something went wrong in the parse.  Pretend it didn't happen */
    			continue;
    		}
    		if (redir->dup == -1) {
    			mode=redir_table[redir->type].mode;
    			openfd = open(redir->word.gl_pathv[0], 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!) */
    				perror_msg("error opening %s", redir->word.gl_pathv[0]);
    				return 1;
    			}
    		} else {
    			openfd = redir->dup;
    		}
    
    		if (openfd != redir->fd) {
    			if (squirrel && redir->fd < 3) {
    				squirrel[redir->fd] = dup(redir->fd);
    			}
    			if (openfd == -3) {
    				close(openfd);
    			} else {
    				dup2(openfd, redir->fd);
    				if (redir->dup == -1)
    					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);
    		}
    	}
    }
    
    /* never returns */
    /* XXX no exit() here.  If you don't exec, use _exit instead.
     * The at_exit handlers apparently confuse the calling process,
     * in particular stdin handling.  Not sure why? */
    static void pseudo_exec(struct child_prog *child)
    {
    	int i, rcode;
    	char *p;
    	struct built_in_command *x;
    	if (child->argv) {
    		for (i=0; is_assignment(child->argv[i]); i++) {
    			debug_printf("pid %d environment modification: %s\n",getpid(),child->argv[i]);
    			p = insert_var_value(child->argv[i]);
    			putenv(strdup(p));
    			if (p != child->argv[i]) free(p);
    		}
    		child->argv+=i;  /* XXX this hack isn't so horrible, since we are about
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    					to exit, and therefore don't need to keep data
    					structures consistent for free() use. */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		/* If a variable is assigned in a forest, and nobody listens,
    		 * was it ever really set?
    		 */
    		if (child->argv[0] == NULL) {
    			_exit(EXIT_SUCCESS);
    		}
    
    		/*
    		 * Check if the command matches any of the builtins.
    		 * Depending on context, this might be redundant.  But it's
    		 * easier to waste a few CPU cycles than it is to figure out
    		 * if this is one of those cases.
    		 */
    		for (x = bltins; x->cmd; x++) {
    			if (strcmp(child->argv[0], x->cmd) == 0 ) {
    				debug_printf("builtin exec %s\n", child->argv[0]);
    				rcode = x->function(child);
    				fflush(stdout);
    				_exit(rcode);
    			}
    		}
    
    		/* Check if the command matches any busybox internal commands
    		 * ("applets") here.
    		 * FIXME: This feature is not 100% safe, since
    		 * BusyBox is not fully reentrant, so we have no guarantee the things
    		 * from the .bss are still zeroed, or that things from .data are still
    		 * at their defaults.  We could exec ourself from /proc/self/exe, but I
    		 * really dislike relying on /proc for things.  We could exec ourself
    		 * from global_argv[0], but if we are in a chroot, we may not be able
    		 * to find ourself... */
    
    #ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		{
    			int argc_l;
    			char** argv_l=child->argv;
    			char *name = child->argv[0];
    
    
    #ifdef CONFIG_FEATURE_SH_APPLETS_ALWAYS_WIN
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			/* Following discussions from November 2000 on the busybox mailing
    			 * list, the default configuration, (without
    			 * get_last_path_component()) lets the user force use of an
    			 * external command by specifying the full (with slashes) filename.
    
    			 * If you enable CONFIG_FEATURE_SH_APPLETS_ALWAYS_WIN then applets
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			 * _aways_ override external commands, so if you want to run
    			 * /bin/cat, it will use BusyBox cat even if /bin/cat exists on the
    			 * filesystem and is _not_ busybox.  Some systems may want this,
    			 * most do not.  */
    			name = get_last_path_component(name);
    #endif
    			/* Count argc for use in a second... */
    			for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++);
    			optind = 1;
    			debug_printf("running applet %s\n", name);
    			run_applet_by_name(name, argc_l, child->argv);
    		}
    #endif
    		debug_printf("exec of %s\n",child->argv[0]);
    		execvp(child->argv[0],child->argv);
    		perror_msg("couldn't exec: %s",child->argv[0]);
    		_exit(1);
    	} else if (child->group) {
    		debug_printf("runtime nesting to group\n");
    		interactive=0;    /* crucial!!!! */
    		rcode = run_list_real(child->group);
    		/* OK to leak memory by not calling free_pipe_list,
    		 * since this process is about to exit */
    		_exit(rcode);
    	} else {
    		/* Can happen.  See what bash does with ">foo" by itself. */
    		debug_printf("trying to pseudo_exec null command\n");
    		_exit(EXIT_SUCCESS);
    	}
    }
    
    static void insert_bg_job(struct pipe *pi)
    {
    	struct pipe *thejob;
    
    	/* Linear search for the ID of the job to use */
    	pi->jobid = 1;
    	for (thejob = job_list; thejob; thejob = thejob->next)
    		if (thejob->jobid >= pi->jobid)
    			pi->jobid = thejob->jobid + 1;
    
    	/* add thejob to the list of running jobs */
    	if (!job_list) {
    		thejob = job_list = xmalloc(sizeof(*thejob));
    	} else {
    		for (thejob = job_list; thejob->next; thejob = thejob->next) /* nothing */;
    		thejob->next = xmalloc(sizeof(*thejob));
    		thejob = thejob->next;
    	}
    
    	/* physically copy the struct job */
    	memcpy(thejob, pi, sizeof(struct pipe));
    	thejob->next = NULL;
    	thejob->running_progs = thejob->num_progs;
    	thejob->stopped_progs = 0;
    	thejob->text = xmalloc(BUFSIZ); /* cmdedit buffer size */
    
    	/*if (pi->progs[0] && pi->progs[0].argv && pi->progs[0].argv[0]) */
    	{
    		char *bar=thejob->text;
    		char **foo=pi->progs[0].argv;
    		while(foo && *foo) {
    			bar += sprintf(bar, "%s ", *foo++);
    		}
    	}
    
    	/* we don't wait for background thejobs to return -- append it
    	   to the list of backgrounded thejobs and leave it alone */
    	printf("[%d] %d\n", thejob->jobid, thejob->progs[0].pid);
    	last_bg_pid = thejob->progs[0].pid;
    	last_jobid = thejob->jobid;
    }
    
    /* remove a backgrounded job */
    static void remove_bg_job(struct pipe *pi)
    {
    	struct pipe *prev_pipe;
    
    	if (pi == job_list) {
    		job_list = pi->next;
    	} else {
    		prev_pipe = job_list;
    		while (prev_pipe->next != pi)
    			prev_pipe = prev_pipe->next;
    		prev_pipe->next = pi->next;
    	}
    	if (job_list)
    		last_jobid = job_list->jobid;
    	else
    		last_jobid = 0;
    
    	pi->stopped_progs = 0;
    	free_pipe(pi, 0);
    	free(pi);
    }
    
    /* Checks to see if any processes have exited -- if they
       have, figure out why and see if a job has completed */
    static int checkjobs(struct pipe* fg_pipe)
    {
    	int attributes;
    	int status;
    	int prognum = 0;
    	struct pipe *pi;
    	pid_t childpid;
    
    	attributes = WUNTRACED;
    	if (fg_pipe==NULL) {
    		attributes |= WNOHANG;
    	}
    
    	while ((childpid = waitpid(-1, &status, attributes)) > 0) {
    		if (fg_pipe) {
    			int i, rcode = 0;
    			for (i=0; i < fg_pipe->num_progs; i++) {
    				if (fg_pipe->progs[i].pid == childpid) {
    					if (i==fg_pipe->num_progs-1)
    						rcode=WEXITSTATUS(status);
    					(fg_pipe->num_progs)--;
    					return(rcode);
    				}
    			}
    		}
    
    		for (pi = job_list; pi; pi = pi->next) {
    			prognum = 0;
    			while (prognum < pi->num_progs && pi->progs[prognum].pid != childpid) {
    				prognum++;
    			}
    			if (prognum < pi->num_progs)
    				break;
    		}
    
    		if(pi==NULL) {
    			debug_printf("checkjobs: pid %d was not in our list!\n", childpid);
    			continue;
    		}
    
    		if (WIFEXITED(status) || WIFSIGNALED(status)) {
    			/* child exited */
    			pi->running_progs--;
    			pi->progs[prognum].pid = 0;
    
    			if (!pi->running_progs) {
    				printf(JOB_STATUS_FORMAT, pi->jobid, "Done", pi->text);
    				remove_bg_job(pi);
    			}
    		} else {
    			/* child stopped */
    			pi->stopped_progs++;
    			pi->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 (pi->stopped_progs == pi->num_progs) {
    				printf("\n"JOB_STATUS_FORMAT, pi->jobid, "Stopped", pi->text);
    			}
    #endif
    		}
    	}
    
    	if (childpid == -1 && errno != ECHILD)
    		perror_msg("waitpid");
    
    	/* move the shell to the foreground */
    	/*if (interactive && tcsetpgrp(shell_terminal, getpgid(0))) */
    	/*	perror_msg("tcsetpgrp-2"); */
    	return -1;
    }
    
    /* Figure out our controlling tty, checking in order stderr,
     * stdin, and stdout.  If check_pgrp is set, also check that
     * we belong to the foreground process group associated with
     * that tty.  The value of shell_terminal is needed in order to call
     * tcsetpgrp(shell_terminal, ...); */
    void controlling_tty(int check_pgrp)
    {
    	pid_t curpgrp;
    
    	if ((curpgrp = tcgetpgrp(shell_terminal = 2)) < 0
    			&& (curpgrp = tcgetpgrp(shell_terminal = 0)) < 0
    			&& (curpgrp = tcgetpgrp(shell_terminal = 1)) < 0)
    		goto shell_terminal_error;
    
    	if (check_pgrp && curpgrp != getpgid(0))
    		goto shell_terminal_error;
    
    	return;
    
    shell_terminal_error:
    		shell_terminal = -1;
    		return;
    }
    #endif
    
    /* run_pipe_real() starts all the jobs, but doesn't wait for anything
     * to finish.  See checkjobs().
     *
     * return code is normally -1, when the caller has to wait for children
     * to finish to determine the exit status of the pipe.  If the pipe
     * is a simple builtin command, however, the action is done by the
     * time run_pipe_real returns, and the exit code is provided as the
     * return value.
     *
     * The input of the pipe is always stdin, the output is always
     * stdout.  The outpipe[] mechanism in BusyBox-0.48 lash is bogus,
     * because it tries to avoid running the command substitution in
     * subshell, when that is in fact necessary.  The subshell process
     * now has its stdout directed to the input of the appropriate pipe,
     * so this routine is noticeably simpler.
     */
    static int run_pipe_real(struct pipe *pi)
    {
    	int i;
    #ifndef __U_BOOT__
    	int nextin, nextout;
    	int pipefds[2];				/* pipefds[0] is for reading */
    	struct child_prog *child;
    	struct built_in_command *x;
    	char *p;
    
    # if __GNUC__
    	/* Avoid longjmp clobbering */
    	(void) &i;
    	(void) &nextin;
    	(void) &nextout;
    	(void) &child;
    # endif
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #else
    	int nextin;
    	int flag = do_repeat ? CMD_FLAG_REPEAT : 0;
    	struct child_prog *child;
    	cmd_tbl_t *cmdtp;
    	char *p;
    
    # if __GNUC__
    	/* Avoid longjmp clobbering */
    	(void) &i;
    	(void) &nextin;
    	(void) &child;
    # endif
    #endif	/* __U_BOOT__ */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	nextin = 0;
    #ifndef __U_BOOT__
    	pi->pgrp = -1;
    #endif
    
    	/* Check if this is a simple builtin (not part of a pipe).
    	 * Builtins within pipes have to fork anyway, and are handled in
    	 * pseudo_exec.  "echo foo | read bar" doesn't work on bash, either.
    	 */
    	if (pi->num_progs == 1) child = & (pi->progs[0]);
    #ifndef __U_BOOT__
    	if (pi->num_progs == 1 && child->group && child->subshell == 0) {
    		int squirrel[] = {-1, -1, -1};
    		int rcode;
    		debug_printf("non-subshell grouping\n");
    		setup_redirects(child, squirrel);
    		/* XXX could we merge code with following builtin case,
    		 * by creating a pseudo builtin that calls run_list_real? */
    		rcode = run_list_real(child->group);
    		restore_redirects(squirrel);
    #else
    		if (pi->num_progs == 1 && child->group) {
    		int rcode;
    		debug_printf("non-subshell grouping\n");
    		rcode = run_list_real(child->group);
    #endif
    		return rcode;
    	} else if (pi->num_progs == 1 && pi->progs[0].argv != NULL) {
    		for (i=0; is_assignment(child->argv[i]); i++) { /* nothing */ }
    		if (i!=0 && child->argv[i]==NULL) {
    			/* assignments, but no command: set the local environment */
    			for (i=0; child->argv[i]!=NULL; i++) {
    
    				/* Ok, this case is tricky.  We have to decide if this is a
    				 * local variable, or an already exported variable.  If it is
    				 * already exported, we have to export the new value.  If it is
    				 * not exported, we need only set this as a local variable.
    				 * This junk is all to decide whether or not to export this
    				 * variable. */
    				int export_me=0;
    				char *name, *value;
    				name = xstrdup(child->argv[i]);
    				debug_printf("Local environment set: %s\n", name);
    				value = strchr(name, '=');
    				if (value)
    					*value=0;
    #ifndef __U_BOOT__
    				if ( get_local_var(name)) {
    					export_me=1;
    				}
    #endif
    				free(name);
    				p = insert_var_value(child->argv[i]);
    				set_local_var(p, export_me);
    				if (p != child->argv[i]) free(p);
    			}
    			return EXIT_SUCCESS;   /* don't worry about errors in set_local_var() yet */
    		}
    		for (i = 0; is_assignment(child->argv[i]); i++) {
    			p = insert_var_value(child->argv[i]);
    #ifndef __U_BOOT__
    			putenv(strdup(p));
    #else
    			set_local_var(p, 0);
    #endif
    			if (p != child->argv[i]) {
    				child->sp--;
    				free(p);
    			}
    		}
    		if (child->sp) {
    			char * str = NULL;
    
    			str = make_string((child->argv + i));
    			parse_string_outer(str, FLAG_EXIT_FROM_LOOP | FLAG_REPARSING);
    			free(str);
    			return last_return_code;
    		}
    #ifndef __U_BOOT__
    		for (x = bltins; x->cmd; x++) {
    			if (strcmp(child->argv[i], x->cmd) == 0 ) {
    				int squirrel[] = {-1, -1, -1};
    				int rcode;
    				if (x->function == builtin_exec && child->argv[i+1]==NULL) {
    					debug_printf("magic exec\n");
    					setup_redirects(child,NULL);
    					return EXIT_SUCCESS;
    				}
    				debug_printf("builtin inline %s\n", child->argv[0]);
    				/* XXX setup_redirects acts on file descriptors, not FILEs.
    				 * This is perfect for work that comes after exec().
    				 * Is it really safe for inline use?  Experimentally,
    				 * things seem to work with glibc. */
    				setup_redirects(child, squirrel);
    #else
    			/* check ";", because ,example , argv consist from
    			 * "help;flinfo" must not execute
    			 */
    			if (strchr(child->argv[i], ';')) {
    				printf ("Unknown command '%s' - try 'help' or use 'run' command\n",
    					child->argv[i]);
    				return -1;
    			}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			/* Look up command in command table */
    
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			if ((cmdtp = find_cmd(child->argv[i])) == NULL) {
    				printf ("Unknown command '%s' - try 'help'\n", child->argv[i]);
    				return -1;	/* give up after bad command */
    			} else {
    				int rcode;
    #if (CONFIG_COMMANDS & CFG_CMD_BOOTD)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	    extern int do_bootd (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
    
    				/* avoid "bootd" recursion */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				if (cmdtp->cmd == do_bootd) {
    					if (flag & CMD_FLAG_BOOTD) {
    						printf ("'bootd' recursion detected\n");
    						return -1;
    					}
    				else
    					flag |= CMD_FLAG_BOOTD;
    				}
    #endif	/* CFG_CMD_BOOTD */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				/* found - check max args */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				if ((child->argc - i) > cmdtp->maxargs) {
    					printf ("Usage:\n%s\n", cmdtp->usage);
    					return -1;
    				}
    #endif
    				child->argv+=i;  /* XXX horrible hack */
    #ifndef __U_BOOT__
    				rcode = x->function(child);
    #else
    				/* OK - call function to do the command */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				rcode = (cmdtp->cmd)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    (cmdtp, flag,child->argc-i,&child->argv[i]);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				if ( !cmdtp->repeatable )
    					flag_repeat = 0;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #endif
    				child->argv-=i;  /* XXX restore hack so free() can work right */
    #ifndef __U_BOOT__
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				restore_redirects(squirrel);
    #endif
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				return rcode;
    			}
    		}
    #ifndef __U_BOOT__
    	}
    
    	for (i = 0; i < pi->num_progs; i++) {
    		child = & (pi->progs[i]);
    
    		/* pipes are inserted between pairs of commands */
    		if ((i + 1) < pi->num_progs) {
    			if (pipe(pipefds)<0) perror_msg_and_die("pipe");
    			nextout = pipefds[1];
    		} else {
    			nextout=1;
    			pipefds[0] = -1;
    		}
    
    		/* XXX test for failed fork()? */
    		if (!(child->pid = fork())) {
    			/* Set the handling for job control signals back to the default.  */
    			signal(SIGINT, SIG_DFL);
    			signal(SIGQUIT, SIG_DFL);
    			signal(SIGTERM, SIG_DFL);
    			signal(SIGTSTP, SIG_DFL);
    			signal(SIGTTIN, SIG_DFL);
    			signal(SIGTTOU, SIG_DFL);
    			signal(SIGCHLD, SIG_DFL);
    
    			close_all();
    
    			if (nextin != 0) {
    				dup2(nextin, 0);
    				close(nextin);
    			}
    			if (nextout != 1) {
    				dup2(nextout, 1);
    				close(nextout);
    			}
    			if (pipefds[0]!=-1) {
    				close(pipefds[0]);  /* opposite end of our output pipe */
    			}
    
    			/* Like bash, explicit redirects override pipes,
    			 * and the pipe fd is available for dup'ing. */
    			setup_redirects(child,NULL);
    
    			if (interactive && pi->followup!=PIPE_BG) {
    				/* If we (the child) win the race, put ourselves in the process
    				 * group whose leader is the first process in this pipe. */
    				if (pi->pgrp < 0) {
    					pi->pgrp = getpid();
    				}
    				if (setpgid(0, pi->pgrp) == 0) {
    					tcsetpgrp(2, pi->pgrp);
    				}
    			}
    
    			pseudo_exec(child);
    		}
    
    
    		/* put our child in the process group whose leader is the
    		   first process in this pipe */
    		if (pi->pgrp < 0) {
    			pi->pgrp = child->pid;
    		}
    		/* Don't check for errors.  The child may be dead already,
    		 * in which case setpgid returns error code EACCES. */
    		setpgid(child->pid, pi->pgrp);
    
    		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];
    	}
    #endif
    	return -1;
    }
    
    static int run_list_real(struct pipe *pi)
    {
    	char *save_name = NULL;
    	char **list = NULL;
    	char **save_list = NULL;
    	struct pipe *rpipe;
    	int flag_rep = 0;
    #ifndef __U_BOOT__
    	int save_num_progs;
    #endif
    	int rcode=0, flag_skip=1;
    	int flag_restore = 0;
    	int if_code=0, next_if_code=0;  /* need double-buffer to handle elif */
    	reserved_style rmode, skip_more_in_this_rmode=RES_XXXX;
    	/* check syntax for "for" */
    	for (rpipe = pi; rpipe; rpipe = rpipe->next) {
    		if ((rpipe->r_mode == RES_IN ||
    		    rpipe->r_mode == RES_FOR) &&
    		    (rpipe->next == NULL)) {
    				syntax();
    #ifdef __U_BOOT__
    				flag_repeat = 0;
    #endif
    				return 1;
    		}
    		if ((rpipe->r_mode == RES_IN &&
    			(rpipe->next->r_mode == RES_IN &&
    			rpipe->next->progs->argv != NULL))||
    			(rpipe->r_mode == RES_FOR &&
    			rpipe->next->r_mode != RES_IN)) {
    				syntax();
    #ifdef __U_BOOT__
    				flag_repeat = 0;
    #endif
    				return 1;
    		}
    	}
    	for (; pi; pi = (flag_restore != 0) ? rpipe : pi->next) {
    		if (pi->r_mode == RES_WHILE || pi->r_mode == RES_UNTIL ||
    			pi->r_mode == RES_FOR) {
    #ifdef __U_BOOT__
    				/* check Ctrl-C */
    				ctrlc();
    				if ((had_ctrlc())) {
    					return 1;
    				}
    #endif
    				flag_restore = 0;
    				if (!rpipe) {
    					flag_rep = 0;
    					rpipe = pi;
    				}
    		}
    		rmode = pi->r_mode;
    		debug_printf("rmode=%d  if_code=%d  next_if_code=%d skip_more=%d\n", rmode, if_code, next_if_code, skip_more_in_this_rmode);
    		if (rmode == skip_more_in_this_rmode && flag_skip) {
    			if (pi->followup == PIPE_SEQ) flag_skip=0;
    			continue;
    		}
    		flag_skip = 1;
    		skip_more_in_this_rmode = RES_XXXX;
    		if (rmode == RES_THEN || rmode == RES_ELSE) if_code = next_if_code;
    		if (rmode == RES_THEN &&  if_code) continue;
    		if (rmode == RES_ELSE && !if_code) continue;
    
    		if (rmode == RES_ELIF && !if_code) break;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		if (rmode == RES_FOR && pi->num_progs) {
    			if (!list) {
    				/* if no variable values after "in" we skip "for" */
    				if (!pi->next->progs->argv) continue;
    				/* create list of variable values */
    				list = make_list_in(pi->next->progs->argv,
    					pi->progs->argv[0]);
    				save_list = list;
    				save_name = pi->progs->argv[0];
    				pi->progs->argv[0] = NULL;
    				flag_rep = 1;
    			}
    			if (!(*list)) {
    				free(pi->progs->argv[0]);
    				free(save_list);
    				list = NULL;
    				flag_rep = 0;
    				pi->progs->argv[0] = save_name;
    #ifndef __U_BOOT__
    				pi->progs->glob_result.gl_pathv[0] =
    					pi->progs->argv[0];
    #endif
    				continue;
    			} else {
    				/* insert new value from list for variable */
    				if (pi->progs->argv[0])
    					free(pi->progs->argv[0]);
    				pi->progs->argv[0] = *list++;
    #ifndef __U_BOOT__
    				pi->progs->glob_result.gl_pathv[0] =
    					pi->progs->argv[0];
    #endif
    			}
    		}
    		if (rmode == RES_IN) continue;
    		if (rmode == RES_DO) {
    			if (!flag_rep) continue;
    		}
    		if ((rmode == RES_DONE)) {
    			if (flag_rep) {
    				flag_restore = 1;
    			} else {
    				rpipe = NULL;
    			}
    		}
    		if (pi->num_progs == 0) continue;
    #ifndef __U_BOOT__
    		save_num_progs = pi->num_progs; /* save number of programs */
    #endif
    		rcode = run_pipe_real(pi);
    		debug_printf("run_pipe_real returned %d\n",rcode);
    #ifndef __U_BOOT__
    		if (rcode!=-1) {
    			/* We only ran a builtin: rcode was set by the return value
    			 * of run_pipe_real(), and we don't need to wait for anything. */
    		} else if (pi->followup==PIPE_BG) {
    			/* XXX check bash's behavior with nontrivial pipes */
    			/* XXX compute jobid */
    			/* XXX what does bash do with attempts to background builtins? */
    			insert_bg_job(pi);
    			rcode = EXIT_SUCCESS;
    		} else {
    			if (interactive) {
    				/* move the new process group into the foreground */
    				if (tcsetpgrp(shell_terminal, pi->pgrp) && errno != ENOTTY)
    					perror_msg("tcsetpgrp-3");
    				rcode = checkjobs(pi);
    				/* move the shell to the foreground */
    				if (tcsetpgrp(shell_terminal, getpgid(0)) && errno != ENOTTY)
    					perror_msg("tcsetpgrp-4");
    			} else {
    				rcode = checkjobs(pi);
    			}
    			debug_printf("checkjobs returned %d\n",rcode);
    		}
    		last_return_code=rcode;
    #else
    		last_return_code=(rcode == 0) ? 0 : 1;
    #endif
    #ifndef __U_BOOT__
    		pi->num_progs = save_num_progs; /* restore number of programs */
    #endif
    		if ( rmode == RES_IF || rmode == RES_ELIF )
    			next_if_code=rcode;  /* can be overwritten a number of times */
    		if (rmode == RES_WHILE)
    			flag_rep = !last_return_code;
    		if (rmode == RES_UNTIL)
    			flag_rep = last_return_code;
    		if ( (rcode==EXIT_SUCCESS && pi->followup==PIPE_OR) ||
    		     (rcode!=EXIT_SUCCESS && pi->followup==PIPE_AND) )
    			skip_more_in_this_rmode=rmode;
    #ifndef __U_BOOT__
    		checkjobs(NULL);
    #endif
    	}
    	return rcode;
    }
    
    /* broken, of course, but OK for testing */
    static char *indenter(int i)
    {
    	static char blanks[]="                                    ";
    	return &blanks[sizeof(blanks)-i-1];
    }
    
    /* return code is the exit status of the pipe */
    static int free_pipe(struct pipe *pi, int indent)
    {
    	char **p;
    	struct child_prog *child;
    #ifndef __U_BOOT__
    	struct redir_struct *r, *rnext;
    #endif
    	int a, i, ret_code=0;
    	char *ind = indenter(indent);
    
    #ifndef __U_BOOT__
    	if (pi->stopped_progs > 0)
    		return ret_code;
    	final_printf("%s run pipe: (pid %d)\n",ind,getpid());
    #endif
    	for (i=0; i<pi->num_progs; i++) {
    		child = &pi->progs[i];
    		final_printf("%s  command %d:\n",ind,i);
    		if (child->argv) {
    			for (a=0,p=child->argv; *p; a++,p++) {
    				final_printf("%s   argv[%d] = %s\n",ind,a,*p);
    			}
    #ifndef __U_BOOT__
    			globfree(&child->glob_result);
    #else
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			for (a = child->argc;a >= 0;a--) {
    				free(child->argv[a]);
    			}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    					free(child->argv);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			child->argc = 0;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #endif
    			child->argv=NULL;
    		} else if (child->group) {
    #ifndef __U_BOOT__
    			final_printf("%s   begin group (subshell:%d)\n",ind, child->subshell);
    #endif
    			ret_code = free_pipe_list(child->group,indent+3);
    			final_printf("%s   end group\n",ind);
    		} else {
    			final_printf("%s   (nil)\n",ind);
    		}
    #ifndef __U_BOOT__
    		for (r=child->redirects; r; r=rnext) {
    			final_printf("%s   redirect %d%s", ind, r->fd, redir_table[r->type].descrip);
    			if (r->dup == -1) {
    				/* guard against the case >$FOO, where foo is unset or blank */
    				if (r->word.gl_pathv) {
    					final_printf(" %s\n", *r->word.gl_pathv);