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
    	 ** 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 {
    	        if (console_buffer[0] != '\n') {
    	                if (strlen(the_command) + strlen(console_buffer)
    			    < CFG_CBSIZE) {
    			        n = strlen(the_command);
    			        the_command[n-1] = ' ';
    			        strcpy(&the_command[n],console_buffer);
    			}
    			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
    		                        to exit, and therefore don't need to keep data
    		                        structures consistent for free() use. */
    		/* 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
    1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000
    
    	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;
    			}
    		   	/* Look up command in command table */
    			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)
    		                /* avoid "bootd" recursion */
    				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 */
    		                /* found - check max args */
    				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 */
    				rcode = (cmdtp->cmd)
    					(cmdtp, flag,child->argc-i,&child->argv[i]);
    				if ( !cmdtp->repeatable )
    					flag_repeat = 0;
    #endif
    				child->argv-=i;  /* XXX restore hack so free() can work right */
    #ifndef __U_BOOT__
    				restore_redirects(squirrel);
    #endif
    				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) continue;
    		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
    	                for (a = child->argc;a >= 0;a--) {
    	                        free(child->argv[a]);
    	                }
    					free(child->argv);
    	                child->argc = 0;
    #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);
    					globfree(&r->word);
    				}
    			} else {
    				final_printf("&%d\n", r->dup);
    			}
    			rnext=r->next;
    			free(r);
    		}
    		child->redirects=NULL;
    #endif