summary refs log tree commit diff
path: root/bin/dash/src/jobs.c
diff options
context:
space:
mode:
Diffstat (limited to 'bin/dash/src/jobs.c')
-rw-r--r--bin/dash/src/jobs.c1547
1 files changed, 0 insertions, 1547 deletions
diff --git a/bin/dash/src/jobs.c b/bin/dash/src/jobs.c
deleted file mode 100644
index 8fb9d4b2..00000000
--- a/bin/dash/src/jobs.c
+++ /dev/null
@@ -1,1547 +0,0 @@
-/*-
- * Copyright (c) 1991, 1993
- *	The Regents of the University of California.  All rights reserved.
- * Copyright (c) 1997-2005
- *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Kenneth Almquist.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <fcntl.h>
-#include <signal.h>
-#include <unistd.h>
-#include <stdlib.h>
-#ifdef HAVE_PATHS_H
-#include <paths.h>
-#endif
-#include <sys/types.h>
-#include <sys/param.h>
-#ifdef BSD
-#include <sys/wait.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#endif
-#include <sys/ioctl.h>
-
-#include "shell.h"
-#if JOBS
-#include <termios.h>
-#undef CEOF			/* syntax.h redefines this */
-#endif
-#include "exec.h"
-#include "eval.h"
-#include "init.h"
-#include "redir.h"
-#include "show.h"
-#include "main.h"
-#include "parser.h"
-#include "nodes.h"
-#include "jobs.h"
-#include "options.h"
-#include "trap.h"
-#include "syntax.h"
-#include "input.h"
-#include "output.h"
-#include "memalloc.h"
-#include "error.h"
-#include "mystring.h"
-#include "system.h"
-
-/* mode flags for set_curjob */
-#define CUR_DELETE 2
-#define CUR_RUNNING 1
-#define CUR_STOPPED 0
-
-/* mode flags for dowait */
-#define DOWAIT_NONBLOCK 0
-#define DOWAIT_BLOCK 1
-#define DOWAIT_WAITCMD 2
-#define DOWAIT_WAITCMD_ALL 4
-
-/* array of jobs */
-static struct job *jobtab;
-/* size of array */
-static unsigned njobs;
-/* pid of last background process */
-pid_t backgndpid;
-
-#if JOBS
-/* pgrp of shell on invocation */
-static int initialpgrp;
-/* control terminal */
-static int ttyfd = -1;
-#endif
-
-/* current job */
-static struct job *curjob;
-
-/* Set if we are in the vforked child */
-int vforked;
-
-STATIC void set_curjob(struct job *, unsigned);
-STATIC int jobno(const struct job *);
-STATIC int sprint_status(char *, int, int);
-STATIC void freejob(struct job *);
-STATIC struct job *getjob(const char *, int);
-STATIC struct job *growjobtab(void);
-STATIC void forkchild(struct job *, union node *, int);
-STATIC void forkparent(struct job *, union node *, int, pid_t);
-STATIC int dowait(int, struct job *);
-#ifdef SYSV
-STATIC int onsigchild(void);
-#endif
-STATIC int waitproc(int, int *);
-STATIC char *commandtext(union node *);
-STATIC void cmdtxt(union node *);
-STATIC void cmdlist(union node *, int);
-STATIC void cmdputs(const char *);
-STATIC void showpipe(struct job *, struct output *);
-STATIC int getstatus(struct job *);
-
-#if JOBS
-static int restartjob(struct job *, int);
-static void xtcsetpgrp(int, pid_t);
-#endif
-
-STATIC void
-set_curjob(struct job *jp, unsigned mode)
-{
-	struct job *jp1;
-	struct job **jpp, **curp;
-
-	/* first remove from list */
-	jpp = curp = &curjob;
-	do {
-		jp1 = *jpp;
-		if (jp1 == jp)
-			break;
-		jpp = &jp1->prev_job;
-	} while (1);
-	*jpp = jp1->prev_job;
-
-	/* Then re-insert in correct position */
-	jpp = curp;
-	switch (mode) {
-	default:
-#ifdef DEBUG
-		abort();
-#endif
-	case CUR_DELETE:
-		/* job being deleted */
-		break;
-	case CUR_RUNNING:
-		/* newly created job or backgrounded job,
-		   put after all stopped jobs. */
-		do {
-			jp1 = *jpp;
-			if (!JOBS || !jp1 || jp1->state != JOBSTOPPED)
-				break;
-			jpp = &jp1->prev_job;
-		} while (1);
-		/* FALLTHROUGH */
-#if JOBS
-	case CUR_STOPPED:
-#endif
-		/* newly stopped job - becomes curjob */
-		jp->prev_job = *jpp;
-		*jpp = jp;
-		break;
-	}
-}
-
-#if JOBS
-/*
- * Turn job control on and off.
- *
- * Note:  This code assumes that the third arg to ioctl is a character
- * pointer, which is true on Berkeley systems but not System V.  Since
- * System V doesn't have job control yet, this isn't a problem now.
- *
- * Called with interrupts off.
- */
-
-int jobctl;
-
-void
-setjobctl(int on)
-{
-	int fd;
-	int pgrp;
-
-	if (on == jobctl || rootshell == 0)
-		return;
-	if (on) {
-		int ofd;
-		ofd = fd = open64(_PATH_TTY, O_RDWR);
-		if (fd < 0) {
-			fd += 3;
-			while (!isatty(fd))
-				if (--fd < 0)
-					goto out;
-		}
-		fd = savefd(fd, ofd);
-		do { /* while we are in the background */
-			if ((pgrp = tcgetpgrp(fd)) < 0) {
-out:
-				sh_warnx("can't access tty; job control turned off");
-				mflag = on = 0;
-				goto close;
-			}
-			if (pgrp == getpgrp())
-				break;
-			killpg(0, SIGTTIN);
-		} while (1);
-		initialpgrp = pgrp;
-
-		setsignal(SIGTSTP);
-		setsignal(SIGTTOU);
-		setsignal(SIGTTIN);
-		pgrp = rootpid;
-		setpgid(0, pgrp);
-		xtcsetpgrp(fd, pgrp);
-	} else {
-		/* turning job control off */
-		fd = ttyfd;
-		pgrp = initialpgrp;
-		xtcsetpgrp(fd, pgrp);
-		setpgid(0, pgrp);
-		setsignal(SIGTSTP);
-		setsignal(SIGTTOU);
-		setsignal(SIGTTIN);
-close:
-		close(fd);
-		fd = -1;
-	}
-	ttyfd = fd;
-	jobctl = on;
-}
-#endif
-
-
-int
-killcmd(argc, argv)
-	int argc;
-	char **argv;
-{
-	extern char *signal_names[];
-	int signo = -1;
-	int list = 0;
-	int i;
-	pid_t pid;
-	struct job *jp;
-
-	if (argc <= 1) {
-usage:
-		sh_error(
-"Usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n"
-"kill -l [exitstatus]"
-		);
-	}
-
-	if (**++argv == '-') {
-		signo = decode_signal(*argv + 1, 1);
-		if (signo < 0) {
-			int c;
-
-			while ((c = nextopt("ls:")) != '\0')
-				switch (c) {
-				default:
-#ifdef DEBUG
-					abort();
-#endif
-				case 'l':
-					list = 1;
-					break;
-				case 's':
-					signo = decode_signal(optionarg, 1);
-					if (signo < 0) {
-						sh_error(
-							"invalid signal number or name: %s",
-							optionarg
-						);
-					}
-		                        break;
-				}
-			argv = argptr;
-		} else
-			argv++;
-	}
-
-	if (!list && signo < 0)
-		signo = SIGTERM;
-
-	if ((signo < 0 || !*argv) ^ list) {
-		goto usage;
-	}
-
-	if (list) {
-		struct output *out;
-
-		out = out1;
-		if (!*argv) {
-			outstr("0\n", out);
-			for (i = 1; i < NSIG; i++) {
-				outfmt(out, snlfmt, signal_names[i]);
-			}
-			return 0;
-		}
-		signo = number(*argv);
-		if (signo > 128)
-			signo -= 128;
-		if (0 < signo && signo < NSIG)
-			outfmt(out, snlfmt, signal_names[signo]);
-		else
-			sh_error("invalid signal number or exit status: %s",
-				 *argv);
-		return 0;
-	}
-
-	i = 0;
-	do {
-		if (**argv == '%') {
-			jp = getjob(*argv, 0);
-			pid = -jp->ps[0].pid;
-		} else
-			pid = **argv == '-' ?
-				-number(*argv + 1) : number(*argv);
-		if (kill(pid, signo) != 0) {
-			sh_warnx("%s\n", strerror(errno));
-			i = 1;
-		}
-	} while (*++argv);
-
-	return i;
-}
-
-STATIC int
-jobno(const struct job *jp)
-{
-	return jp - jobtab + 1;
-}
-
-#if JOBS
-int
-fgcmd(int argc, char **argv)
-{
-	struct job *jp;
-	struct output *out;
-	int mode;
-	int retval;
-
-	mode = (**argv == 'f') ? FORK_FG : FORK_BG;
-	nextopt(nullstr);
-	argv = argptr;
-	out = out1;
-	do {
-		jp = getjob(*argv, 1);
-		if (mode == FORK_BG) {
-			set_curjob(jp, CUR_RUNNING);
-			outfmt(out, "[%d] ", jobno(jp));
-		}
-		outstr(jp->ps->cmd, out);
-		showpipe(jp, out);
-		retval = restartjob(jp, mode);
-	} while (*argv && *++argv);
-	return retval;
-}
-
-int bgcmd(int argc, char **argv)
-#ifdef HAVE_ALIAS_ATTRIBUTE
-	__attribute__((__alias__("fgcmd")));
-#else
-{
-	return fgcmd(argc, argv);
-}
-#endif
-
-
-STATIC int
-restartjob(struct job *jp, int mode)
-{
-	struct procstat *ps;
-	int i;
-	int status;
-	pid_t pgid;
-
-	INTOFF;
-	if (jp->state == JOBDONE)
-		goto out;
-	jp->state = JOBRUNNING;
-	pgid = jp->ps->pid;
-	if (mode == FORK_FG)
-		xtcsetpgrp(ttyfd, pgid);
-	killpg(pgid, SIGCONT);
-	ps = jp->ps;
-	i = jp->nprocs;
-	do {
-		if (WIFSTOPPED(ps->status)) {
-			ps->status = -1;
-		}
-	} while (ps++, --i);
-out:
-	status = (mode == FORK_FG) ? waitforjob(jp) : 0;
-	INTON;
-	return status;
-}
-#endif
-
-STATIC int
-sprint_status(char *os, int status, int sigonly)
-{
-	char *s = os;
-	int st;
-
-	st = WEXITSTATUS(status);
-	if (!WIFEXITED(status)) {
-#if JOBS
-		st = WSTOPSIG(status);
-		if (!WIFSTOPPED(status))
-#endif
-			st = WTERMSIG(status);
-		if (sigonly) {
-			if (st == SIGINT || st == SIGPIPE)
-				goto out;
-#if JOBS
-			if (WIFSTOPPED(status))
-				goto out;
-#endif
-		}
-		s = stpncpy(s, strsignal(st), 32);
-#ifdef WCOREDUMP
-		if (WCOREDUMP(status)) {
-			s = stpcpy(s, " (core dumped)");
-		}
-#endif
-	} else if (!sigonly) {
-		if (st)
-			s += fmtstr(s, 16, "Done(%d)", st);
-		else
-			s = stpcpy(s, "Done");
-	}
-
-out:
-	return s - os;
-}
-
-static void
-showjob(struct output *out, struct job *jp, int mode)
-{
-	struct procstat *ps;
-	struct procstat *psend;
-	int col;
-	int indent;
-	char s[80];
-
-	ps = jp->ps;
-
-	if (mode & SHOW_PGID) {
-		/* just output process (group) id of pipeline */
-		outfmt(out, "%d\n", ps->pid);
-		return;
-	}
-
-	col = fmtstr(s, 16, "[%d]   ", jobno(jp));
-	indent = col;
-
-	if (jp == curjob)
-		s[col - 2] = '+';
-	else if (curjob && jp == curjob->prev_job)
-		s[col - 2] = '-';
-
-	if (mode & SHOW_PID)
-		col += fmtstr(s + col, 16, "%d ", ps->pid);
-
-	psend = ps + jp->nprocs;
-
-	if (jp->state == JOBRUNNING) {
-		scopy("Running", s + col);
-		col += strlen("Running");
-	} else {
-		int status = psend[-1].status;
-#if JOBS
-		if (jp->state == JOBSTOPPED)
-			status = jp->stopstatus;
-#endif
-		col += sprint_status(s + col, status, 0);
-	}
-
-	goto start;
-
-	do {
-		/* for each process */
-		col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3;
-
-start:
-		outfmt(
-			out, "%s%*c%s",
-			s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
-		);
-		if (!(mode & SHOW_PID)) {
-			showpipe(jp, out);
-			break;
-		}
-		if (++ps == psend) {
-			outcslow('\n', out);
-			break;
-		}
-	} while (1);
-
-	jp->changed = 0;
-
-	if (jp->state == JOBDONE) {
-		TRACE(("showjob: freeing job %d\n", jobno(jp)));
-		freejob(jp);
-	}
-}
-
-
-int
-jobscmd(int argc, char **argv)
-{
-	int mode, m;
-	struct output *out;
-
-	mode = 0;
-	while ((m = nextopt("lp")))
-		if (m == 'l')
-			mode = SHOW_PID;
-		else
-			mode = SHOW_PGID;
-
-	out = out1;
-	argv = argptr;
-	if (*argv)
-		do
-			showjob(out, getjob(*argv,0), mode);
-		while (*++argv);
-	else
-		showjobs(out, mode);
-
-	return 0;
-}
-
-
-/*
- * Print a list of jobs.  If "change" is nonzero, only print jobs whose
- * statuses have changed since the last call to showjobs.
- */
-
-void
-showjobs(struct output *out, int mode)
-{
-	struct job *jp;
-
-	TRACE(("showjobs(%x) called\n", mode));
-
-	/* If not even one job changed, there is nothing to do */
-	dowait(DOWAIT_NONBLOCK, NULL);
-
-	for (jp = curjob; jp; jp = jp->prev_job) {
-		if (!(mode & SHOW_CHANGED) || jp->changed)
-			showjob(out, jp, mode);
-	}
-}
-
-/*
- * Mark a job structure as unused.
- */
-
-STATIC void
-freejob(struct job *jp)
-{
-	struct procstat *ps;
-	int i;
-
-	INTOFF;
-	for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) {
-		if (ps->cmd != nullstr)
-			ckfree(ps->cmd);
-	}
-	if (jp->ps != &jp->ps0)
-		ckfree(jp->ps);
-	jp->used = 0;
-	set_curjob(jp, CUR_DELETE);
-	INTON;
-}
-
-
-
-int
-waitcmd(int argc, char **argv)
-{
-	struct job *job;
-	int retval;
-	struct job *jp;
-
-	nextopt(nullstr);
-	retval = 0;
-
-	argv = argptr;
-	if (!*argv) {
-		/* wait for all jobs */
-		for (;;) {
-			jp = curjob;
-			while (1) {
-				if (!jp) {
-					/* no running procs */
-					goto out;
-				}
-				if (jp->state == JOBRUNNING)
-					break;
-				jp->waited = 1;
-				jp = jp->prev_job;
-			}
-			if (!dowait(DOWAIT_WAITCMD_ALL, 0))
-				goto sigout;
-		}
-	}
-
-	retval = 127;
-	do {
-		if (**argv != '%') {
-			pid_t pid = number(*argv);
-			job = curjob;
-			goto start;
-			do {
-				if (job->ps[job->nprocs - 1].pid == pid)
-					break;
-				job = job->prev_job;
-start:
-				if (!job)
-					goto repeat;
-			} while (1);
-		} else
-			job = getjob(*argv, 0);
-		/* loop until process terminated or stopped */
-		if (!dowait(DOWAIT_WAITCMD, job))
-			goto sigout;
-		job->waited = 1;
-		retval = getstatus(job);
-repeat:
-		;
-	} while (*++argv);
-
-out:
-	return retval;
-
-sigout:
-	retval = 128 + pending_sig;
-	goto out;
-}
-
-
-
-/*
- * Convert a job name to a job structure.
- */
-
-STATIC struct job *
-getjob(const char *name, int getctl)
-{
-	struct job *jp;
-	struct job *found;
-	const char *err_msg = "No such job: %s";
-	unsigned num;
-	int c;
-	const char *p;
-	char *(*match)(const char *, const char *);
-
-	jp = curjob;
-	p = name;
-	if (!p)
-		goto currentjob;
-
-	if (*p != '%')
-		goto err;
-
-	c = *++p;
-	if (!c)
-		goto currentjob;
-
-	if (!p[1]) {
-		if (c == '+' || c == '%') {
-currentjob:
-			err_msg = "No current job";
-			goto check;
-		} else if (c == '-') {
-			if (jp)
-				jp = jp->prev_job;
-			err_msg = "No previous job";
-check:
-			if (!jp)
-				goto err;
-			goto gotit;
-		}
-	}
-
-	if (is_number(p)) {
-		num = atoi(p);
-		if (num > 0 && num <= njobs) {
-			jp = jobtab + num - 1;
-			if (jp->used)
-				goto gotit;
-			goto err;
-		}
-	}
-
-	match = prefix;
-	if (*p == '?') {
-		match = strstr;
-		p++;
-	}
-
-	found = 0;
-	while (jp) {
-		if (match(jp->ps[0].cmd, p)) {
-			if (found)
-				goto err;
-			found = jp;
-			err_msg = "%s: ambiguous";
-		}
-		jp = jp->prev_job;
-	}
-
-	if (!found)
-		goto err;
-	jp = found;
-
-gotit:
-#if JOBS
-	err_msg = "job %s not created under job control";
-	if (getctl && jp->jobctl == 0)
-		goto err;
-#endif
-	return jp;
-err:
-	sh_error(err_msg, name);
-}
-
-
-
-/*
- * Return a new job structure.
- * Called with interrupts off.
- */
-
-struct job *
-makejob(union node *node, int nprocs)
-{
-	int i;
-	struct job *jp;
-
-	for (i = njobs, jp = jobtab ; ; jp++) {
-		if (--i < 0) {
-			jp = growjobtab();
-			break;
-		}
-		if (jp->used == 0)
-			break;
-		if (jp->state != JOBDONE || !jp->waited)
-			continue;
-		if (jobctl)
-			continue;
-		freejob(jp);
-		break;
-	}
-	memset(jp, 0, sizeof(*jp));
-#if JOBS
-	if (jobctl)
-		jp->jobctl = 1;
-#endif
-	jp->prev_job = curjob;
-	curjob = jp;
-	jp->used = 1;
-	jp->ps = &jp->ps0;
-	if (nprocs > 1) {
-		jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
-	}
-	TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
-	    jobno(jp)));
-	return jp;
-}
-
-STATIC struct job *
-growjobtab(void)
-{
-	size_t len;
-	ptrdiff_t offset;
-	struct job *jp, *jq;
-
-	len = njobs * sizeof(*jp);
-	jq = jobtab;
-	jp = ckrealloc(jq, len + 4 * sizeof(*jp));
-
-	offset = (char *)jp - (char *)jq;
-	if (offset) {
-		/* Relocate pointers */
-		size_t l = len;
-
-		jq = (struct job *)((char *)jq + l);
-		while (l) {
-			l -= sizeof(*jp);
-			jq--;
-#define joff(p) ((struct job *)((char *)(p) + l))
-#define jmove(p) (p) = (void *)((char *)(p) + offset)
-			if (likely(joff(jp)->ps == &jq->ps0))
-				jmove(joff(jp)->ps);
-			if (joff(jp)->prev_job)
-				jmove(joff(jp)->prev_job);
-		}
-		if (curjob)
-			jmove(curjob);
-#undef joff
-#undef jmove
-	}
-
-	njobs += 4;
-	jobtab = jp;
-	jp = (struct job *)((char *)jp + len);
-	jq = jp + 3;
-	do {
-		jq->used = 0;
-	} while (--jq >= jp);
-	return jp;
-}
-
-
-/*
- * Fork off a subshell.  If we are doing job control, give the subshell its
- * own process group.  Jp is a job structure that the job is to be added to.
- * N is the command that will be evaluated by the child.  Both jp and n may
- * be NULL.  The mode parameter can be one of the following:
- *	FORK_FG - Fork off a foreground process.
- *	FORK_BG - Fork off a background process.
- *	FORK_NOJOB - Like FORK_FG, but don't give the process its own
- *		     process group even if job control is on.
- *
- * When job control is turned off, background processes have their standard
- * input redirected to /dev/null (except for the second and later processes
- * in a pipeline).
- *
- * Called with interrupts off.
- */
-
-static void forkchild(struct job *jp, union node *n, int mode)
-{
-	int lvforked;
-	int oldlvl;
-
-	TRACE(("Child shell %d\n", getpid()));
-
-	oldlvl = shlvl;
-	lvforked = vforked;
-
-	if (!lvforked) {
-		shlvl++;
-
-		forkreset();
-
-#if JOBS
-		/* do job control only in root shell */
-		jobctl = 0;
-#endif
-	}
-
-#if JOBS
-	if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
-		pid_t pgrp;
-
-		if (jp->nprocs == 0)
-			pgrp = getpid();
-		else
-			pgrp = jp->ps[0].pid;
-		/* This can fail because we are doing it in the parent also */
-		(void)setpgid(0, pgrp);
-		if (mode == FORK_FG)
-			xtcsetpgrp(ttyfd, pgrp);
-		setsignal(SIGTSTP);
-		setsignal(SIGTTOU);
-	} else
-#endif
-	if (mode == FORK_BG) {
-		ignoresig(SIGINT);
-		ignoresig(SIGQUIT);
-		if (jp->nprocs == 0) {
-			close(0);
-			if (open64(_PATH_DEVNULL, O_RDONLY) != 0)
-				sh_error("Can't open %s", _PATH_DEVNULL);
-		}
-	}
-	if (!oldlvl && iflag) {
-		if (mode != FORK_BG) {
-			setsignal(SIGINT);
-			setsignal(SIGQUIT);
-		}
-		setsignal(SIGTERM);
-	}
-
-	if (lvforked)
-		return;
-
-	for (jp = curjob; jp; jp = jp->prev_job)
-		freejob(jp);
-}
-
-static void forkparent(struct job *jp, union node *n, int mode, pid_t pid)
-{
-	if (pid < 0) {
-		TRACE(("Fork failed, errno=%d", errno));
-		if (jp)
-			freejob(jp);
-		sh_error("Cannot fork");
-		/* NOTREACHED */
-	}
-
-	TRACE(("In parent shell:  child = %d\n", pid));
-	if (!jp)
-		return;
-#if JOBS
-	if (mode != FORK_NOJOB && jp->jobctl) {
-		int pgrp;
-
-		if (jp->nprocs == 0)
-			pgrp = pid;
-		else
-			pgrp = jp->ps[0].pid;
-		/* This can fail because we are doing it in the child also */
-		(void)setpgid(pid, pgrp);
-	}
-#endif
-	if (mode == FORK_BG) {
-		backgndpid = pid;		/* set $! */
-		set_curjob(jp, CUR_RUNNING);
-	}
-	if (jp) {
-		struct procstat *ps = &jp->ps[jp->nprocs++];
-		ps->pid = pid;
-		ps->status = -1;
-		ps->cmd = nullstr;
-		if (jobctl && n)
-			ps->cmd = commandtext(n);
-	}
-}
-
-int
-forkshell(struct job *jp, union node *n, int mode)
-{
-	int pid;
-
-	TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
-	pid = fork();
-	if (pid == 0)
-		forkchild(jp, n, mode);
-	else
-		forkparent(jp, n, mode, pid);
-
-	return pid;
-}
-
-struct job *vforkexec(union node *n, char **argv, const char *path, int idx)
-{
-	struct job *jp;
-	int pid;
-
-	jp = makejob(n, 1);
-
-	sigblockall(NULL);
-	vforked++;
-
-	pid = vfork();
-
-	if (!pid) {
-		forkchild(jp, n, FORK_FG);
-		sigclearmask();
-		shellexec(argv, path, idx);
-		/* NOTREACHED */
-	}
-
-	vforked = 0;
-	sigclearmask();
-	forkparent(jp, n, FORK_FG, pid);
-
-	return jp;
-}
-
-/*
- * Wait for job to finish.
- *
- * Under job control we have the problem that while a child process is
- * running interrupts generated by the user are sent to the child but not
- * to the shell.  This means that an infinite loop started by an inter-
- * active user may be hard to kill.  With job control turned off, an
- * interactive user may place an interactive program inside a loop.  If
- * the interactive program catches interrupts, the user doesn't want
- * these interrupts to also abort the loop.  The approach we take here
- * is to have the shell ignore interrupt signals while waiting for a
- * forground process to terminate, and then send itself an interrupt
- * signal if the child process was terminated by an interrupt signal.
- * Unfortunately, some programs want to do a bit of cleanup and then
- * exit on interrupt; unless these processes terminate themselves by
- * sending a signal to themselves (instead of calling exit) they will
- * confuse this approach.
- *
- * Called with interrupts off.
- */
-
-int
-waitforjob(struct job *jp)
-{
-	int st;
-
-	TRACE(("waitforjob(%%%d) called\n", jp ? jobno(jp) : 0));
-	dowait(jp ? DOWAIT_BLOCK : DOWAIT_NONBLOCK, jp);
-	if (!jp)
-		return exitstatus;
-
-	st = getstatus(jp);
-#if JOBS
-	if (jp->jobctl) {
-		xtcsetpgrp(ttyfd, rootpid);
-		/*
-		 * This is truly gross.
-		 * If we're doing job control, then we did a TIOCSPGRP which
-		 * caused us (the shell) to no longer be in the controlling
-		 * session -- so we wouldn't have seen any ^C/SIGINT.  So, we
-		 * intuit from the subprocess exit status whether a SIGINT
-		 * occurred, and if so interrupt ourselves.  Yuck.  - mycroft
-		 */
-		if (jp->sigint)
-			raise(SIGINT);
-	}
-#endif
-	if (! JOBS || jp->state == JOBDONE)
-		freejob(jp);
-	return st;
-}
-
-
-
-/*
- * Wait for a process to terminate.
- */
-
-static int waitone(int block, struct job *job)
-{
-	int pid;
-	int status;
-	struct job *jp;
-	struct job *thisjob = NULL;
-	int state;
-
-	INTOFF;
-	TRACE(("dowait(%d) called\n", block));
-	pid = waitproc(block, &status);
-	TRACE(("wait returns pid %d, status=%d\n", pid, status));
-	if (pid <= 0)
-		goto out;
-
-	for (jp = curjob; jp; jp = jp->prev_job) {
-		struct procstat *sp;
-		struct procstat *spend;
-		if (jp->state == JOBDONE)
-			continue;
-		state = JOBDONE;
-		spend = jp->ps + jp->nprocs;
-		sp = jp->ps;
-		do {
-			if (sp->pid == pid) {
-				TRACE(("Job %d: changing status of proc %d from 0x%x to 0x%x\n", jobno(jp), pid, sp->status, status));
-				sp->status = status;
-				thisjob = jp;
-			}
-			if (sp->status == -1)
-				state = JOBRUNNING;
-#if JOBS
-			if (state == JOBRUNNING)
-				continue;
-			if (WIFSTOPPED(sp->status)) {
-				jp->stopstatus = sp->status;
-				state = JOBSTOPPED;
-			}
-#endif
-		} while (++sp < spend);
-		if (thisjob)
-			goto gotjob;
-	}
-	goto out;
-
-gotjob:
-	if (state != JOBRUNNING) {
-		thisjob->changed = 1;
-
-		if (thisjob->state != state) {
-			TRACE(("Job %d: changing state from %d to %d\n", jobno(thisjob), thisjob->state, state));
-			thisjob->state = state;
-#if JOBS
-			if (state == JOBSTOPPED) {
-				set_curjob(thisjob, CUR_STOPPED);
-			}
-#endif
-		}
-	}
-
-out:
-	INTON;
-
-	if (thisjob && thisjob == job) {
-		char s[48 + 1];
-		int len;
-
-		len = sprint_status(s, status, 1);
-		if (len) {
-			s[len] = '\n';
-			s[len + 1] = 0;
-			outstr(s, out2);
-		}
-	}
-	return pid;
-}
-
-static int dowait(int block, struct job *jp)
-{
-	int gotchld = *(volatile int *)&gotsigchld;
-	int rpid;
-	int pid;
-
-	if (jp && jp->state != JOBRUNNING)
-		block = DOWAIT_NONBLOCK;
-
-	if (block == DOWAIT_NONBLOCK && !gotchld)
-		return 1;
-
-	rpid = 1;
-
-	do {
-		pid = waitone(block, jp);
-		rpid &= !!pid;
-
-		block &= ~DOWAIT_WAITCMD_ALL;
-		if (!pid || (jp && jp->state != JOBRUNNING))
-			block = DOWAIT_NONBLOCK;
-	} while (pid >= 0);
-
-	return rpid;
-}
-
-/*
- * Do a wait system call.  If block is zero, we return -1 rather than
- * blocking.  If block is DOWAIT_WAITCMD, we return 0 when a signal
- * other than SIGCHLD interrupted the wait.
- *
- * We use sigsuspend in conjunction with a non-blocking wait3 in
- * order to ensure that waitcmd exits promptly upon the reception
- * of a signal.
- *
- * For code paths other than waitcmd we either use a blocking wait3
- * or a non-blocking wait3.  For the latter case the caller of dowait
- * must ensure that it is called over and over again until all dead
- * children have been reaped.  Otherwise zombies may linger.
- */
-
-
-STATIC int
-waitproc(int block, int *status)
-{
-	sigset_t oldmask;
-	int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG;
-	int err;
-
-#if JOBS
-	if (jobctl)
-		flags |= WUNTRACED;
-#endif
-
-	do {
-		gotsigchld = 0;
-		do
-			err = wait3(status, flags, NULL);
-		while (err < 0 && errno == EINTR);
-
-		if (err || (err = -!block))
-			break;
-
-		sigblockall(&oldmask);
-
-		while (!gotsigchld && !pending_sig)
-			sigsuspend(&oldmask);
-
-		sigclearmask();
-	} while (gotsigchld);
-
-	return err;
-}
-
-/*
- * return 1 if there are stopped jobs, otherwise 0
- */
-int job_warning;
-int
-stoppedjobs(void)
-{
-	struct job *jp;
-	int retval;
-
-	retval = 0;
-	if (job_warning > 1)
-		goto out;
-	jp = curjob;
-	if (jp && jp->state == JOBSTOPPED) {
-		out2str("You have stopped jobs.\n");
-		job_warning++;
-		retval++;
-	}
-
-out:
-	return retval;
-}
-
-/*
- * Return a string identifying a command (to be printed by the
- * jobs command).
- */
-
-STATIC char *cmdnextc;
-
-STATIC char *
-commandtext(union node *n)
-{
-	char *name;
-
-	STARTSTACKSTR(cmdnextc);
-	cmdtxt(n);
-	name = stackblock();
-	TRACE(("commandtext: name %p, end %p\n", name, cmdnextc));
-	return savestr(name);
-}
-
-
-STATIC void
-cmdtxt(union node *n)
-{
-	union node *np;
-	struct nodelist *lp;
-	const char *p;
-	char s[2];
-
-	if (!n)
-		return;
-	switch (n->type) {
-	default:
-#if DEBUG
-		abort();
-#endif
-	case NPIPE:
-		lp = n->npipe.cmdlist;
-		for (;;) {
-			cmdtxt(lp->n);
-			lp = lp->next;
-			if (!lp)
-				break;
-			cmdputs(" | ");
-		}
-		break;
-	case NSEMI:
-		p = "; ";
-		goto binop;
-	case NAND:
-		p = " && ";
-		goto binop;
-	case NOR:
-		p = " || ";
-binop:
-		cmdtxt(n->nbinary.ch1);
-		cmdputs(p);
-		n = n->nbinary.ch2;
-		goto donode;
-	case NREDIR:
-	case NBACKGND:
-		n = n->nredir.n;
-		goto donode;
-	case NNOT:
-		cmdputs("!");
-		n = n->nnot.com;
-donode:
-		cmdtxt(n);
-		break;
-	case NIF:
-		cmdputs("if ");
-		cmdtxt(n->nif.test);
-		cmdputs("; then ");
-		if (n->nif.elsepart) {
-			cmdtxt(n->nif.ifpart);
-			cmdputs("; else ");
-			n = n->nif.elsepart;
-		} else {
-			n = n->nif.ifpart;
-		}
-		p = "; fi";
-		goto dotail;
-	case NSUBSHELL:
-		cmdputs("(");
-		n = n->nredir.n;
-		p = ")";
-		goto dotail;
-	case NWHILE:
-		p = "while ";
-		goto until;
-	case NUNTIL:
-		p = "until ";
-until:
-		cmdputs(p);
-		cmdtxt(n->nbinary.ch1);
-		n = n->nbinary.ch2;
-		p = "; done";
-dodo:
-		cmdputs("; do ");
-dotail:
-		cmdtxt(n);
-		goto dotail2;
-	case NFOR:
-		cmdputs("for ");
-		cmdputs(n->nfor.var);
-		cmdputs(" in ");
-		cmdlist(n->nfor.args, 1);
-		n = n->nfor.body;
-		p = "; done";
-		goto dodo;
-	case NDEFUN:
-		cmdputs(n->ndefun.text);
-		p = "() { ... }";
-		goto dotail2;
-	case NCMD:
-		cmdlist(n->ncmd.args, 1);
-		cmdlist(n->ncmd.redirect, 0);
-		break;
-	case NARG:
-		p = n->narg.text;
-dotail2:
-		cmdputs(p);
-		break;
-	case NHERE:
-	case NXHERE:
-		p = "<<...";
-		goto dotail2;
-	case NCASE:
-		cmdputs("case ");
-		cmdputs(n->ncase.expr->narg.text);
-		cmdputs(" in ");
-		for (np = n->ncase.cases; np; np = np->nclist.next) {
-			cmdtxt(np->nclist.pattern);
-			cmdputs(") ");
-			cmdtxt(np->nclist.body);
-			cmdputs(";; ");
-		}
-		p = "esac";
-		goto dotail2;
-	case NTO:
-		p = ">";
-		goto redir;
-	case NCLOBBER:
-		p = ">|";
-		goto redir;
-	case NAPPEND:
-		p = ">>";
-		goto redir;
-	case NTOFD:
-		p = ">&";
-		goto redir;
-	case NFROM:
-		p = "<";
-		goto redir;
-	case NFROMFD:
-		p = "<&";
-		goto redir;
-	case NFROMTO:
-		p = "<>";
-redir:
-		s[0] = n->nfile.fd + '0';
-		s[1] = '\0';
-		cmdputs(s);
-		cmdputs(p);
-		if (n->type == NTOFD || n->type == NFROMFD) {
-			s[0] = n->ndup.dupfd + '0';
-			p = s;
-			goto dotail2;
-		} else {
-			n = n->nfile.fname;
-			goto donode;
-		}
-	}
-}
-
-STATIC void
-cmdlist(union node *np, int sep)
-{
-	for (; np; np = np->narg.next) {
-		if (!sep)
-			cmdputs(spcstr);
-		cmdtxt(np);
-		if (sep && np->narg.next)
-			cmdputs(spcstr);
-	}
-}
-
-
-STATIC void
-cmdputs(const char *s)
-{
-	const char *p, *str;
-	char cc[2] = " ";
-	char *nextc;
-	signed char c;
-	int subtype = 0;
-	int quoted = 0;
-	static const char vstype[VSTYPE + 1][4] = {
-		"", "}", "-", "+", "?", "=",
-		"%", "%%", "#", "##",
-	};
-
-	nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
-	p = s;
-	while ((c = *p++) != 0) {
-		str = 0;
-		switch (c) {
-		case CTLESC:
-			c = *p++;
-			break;
-		case CTLVAR:
-			subtype = *p++;
-			if ((subtype & VSTYPE) == VSLENGTH)
-				str = "${#";
-			else
-				str = "${";
-			goto dostr;
-		case CTLENDVAR:
-			str = "\"}";
-			str += !(quoted & 1);
-			quoted >>= 1;
-			subtype = 0;
-			goto dostr;
-		case CTLBACKQ:
-			str = "$(...)";
-			goto dostr;
-		case CTLARI:
-			str = "$((";
-			goto dostr;
-		case CTLENDARI:
-			str = "))";
-			goto dostr;
-		case CTLQUOTEMARK:
-			quoted ^= 1;
-			c = '"';
-			break;
-		case '=':
-			if (subtype == 0)
-				break;
-			if ((subtype & VSTYPE) != VSNORMAL)
-				quoted <<= 1;
-			str = vstype[subtype & VSTYPE];
-			if (subtype & VSNUL)
-				c = ':';
-			else
-				goto checkstr;
-			break;
-		case '\'':
-		case '\\':
-		case '"':
-		case '$':
-			/* These can only happen inside quotes */
-			cc[0] = c;
-			str = cc;
-			c = '\\';
-			break;
-		default:
-			break;
-		}
-		USTPUTC(c, nextc);
-checkstr:
-		if (!str)
-			continue;
-dostr:
-		while ((c = *str++)) {
-			USTPUTC(c, nextc);
-		}
-	}
-	if (quoted & 1) {
-		USTPUTC('"', nextc);
-	}
-	*nextc = 0;
-	cmdnextc = nextc;
-}
-
-
-STATIC void
-showpipe(struct job *jp, struct output *out)
-{
-	struct procstat *sp;
-	struct procstat *spend;
-
-	spend = jp->ps + jp->nprocs;
-	for (sp = jp->ps + 1; sp < spend; sp++)
-		outfmt(out, " | %s", sp->cmd);
-	outcslow('\n', out);
-	flushall();
-}
-
-
-#if JOBS
-STATIC void
-xtcsetpgrp(int fd, pid_t pgrp)
-{
-	if (tcsetpgrp(fd, pgrp))
-		sh_error("Cannot set tty process group (%s)", strerror(errno));
-}
-#endif
-
-
-STATIC int
-getstatus(struct job *job) {
-	int status;
-	int retval;
-
-	status = job->ps[job->nprocs - 1].status;
-	retval = WEXITSTATUS(status);
-	if (!WIFEXITED(status)) {
-#if JOBS
-		retval = WSTOPSIG(status);
-		if (!WIFSTOPPED(status))
-#endif
-		{
-			/* XXX: limits number of signals */
-			retval = WTERMSIG(status);
-#if JOBS
-			if (retval == SIGINT)
-				job->sigint = 1;
-#endif
-		}
-		retval += 128;
-	}
-	TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
-		jobno(job), job->nprocs, status, retval));
-	return retval;
-}