jobs.c revision 100663
1204076Spjd/*-
2204076Spjd * Copyright (c) 1991, 1993
3210886Spjd *	The Regents of the University of California.  All rights reserved.
4204076Spjd *
5204076Spjd * This code is derived from software contributed to Berkeley by
6204076Spjd * Kenneth Almquist.
7204076Spjd *
8204076Spjd * Redistribution and use in source and binary forms, with or without
9204076Spjd * modification, are permitted provided that the following conditions
10204076Spjd * are met:
11204076Spjd * 1. Redistributions of source code must retain the above copyright
12204076Spjd *    notice, this list of conditions and the following disclaimer.
13204076Spjd * 2. Redistributions in binary form must reproduce the above copyright
14204076Spjd *    notice, this list of conditions and the following disclaimer in the
15204076Spjd *    documentation and/or other materials provided with the distribution.
16204076Spjd * 3. All advertising materials mentioning features or use of this software
17204076Spjd *    must display the following acknowledgement:
18204076Spjd *	This product includes software developed by the University of
19204076Spjd *	California, Berkeley and its contributors.
20204076Spjd * 4. Neither the name of the University nor the names of its contributors
21204076Spjd *    may be used to endorse or promote products derived from this software
22204076Spjd *    without specific prior written permission.
23204076Spjd *
24204076Spjd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27204076Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34204076Spjd * SUCH DAMAGE.
35204076Spjd */
36204076Spjd
37204076Spjd#ifndef lint
38204076Spjd#if 0
39204076Spjdstatic char sccsid[] = "@(#)jobs.c	8.5 (Berkeley) 5/4/95";
40204076Spjd#endif
41204076Spjd#endif /* not lint */
42204076Spjd#include <sys/cdefs.h>
43204076Spjd__FBSDID("$FreeBSD: head/bin/sh/jobs.c 100663 2002-07-25 10:47:38Z tjr $");
44204076Spjd
45204076Spjd#include <fcntl.h>
46204076Spjd#include <signal.h>
47204076Spjd#include <errno.h>
48204076Spjd#include <paths.h>
49211982Spjd#include <unistd.h>
50204076Spjd#include <stdlib.h>
51204076Spjd#include <sys/param.h>
52204076Spjd#include <sys/wait.h>
53204076Spjd#include <sys/time.h>
54204076Spjd#include <sys/resource.h>
55204076Spjd#include <paths.h>
56204076Spjd#include <sys/ioctl.h>
57204076Spjd
58204076Spjd#include "shell.h"
59204076Spjd#if JOBS
60204076Spjd#include <termios.h>
61212038Spjd#undef CEOF			/* syntax.h redefines this */
62204076Spjd#endif
63204076Spjd#include "redir.h"
64204076Spjd#include "show.h"
65211886Spjd#include "main.h"
66204076Spjd#include "parser.h"
67204076Spjd#include "nodes.h"
68204076Spjd#include "jobs.h"
69204076Spjd#include "options.h"
70204076Spjd#include "trap.h"
71204076Spjd#include "syntax.h"
72210886Spjd#include "input.h"
73210886Spjd#include "output.h"
74210886Spjd#include "memalloc.h"
75204076Spjd#include "error.h"
76204076Spjd#include "mystring.h"
77204076Spjd
78204076Spjd
79204076Spjdstruct job *jobtab;		/* array of jobs */
80204076Spjdint njobs;			/* size of array */
81204076SpjdMKINIT pid_t backgndpid = -1;	/* pid of last background process */
82204076Spjd#if JOBS
83204076Spjdstruct job *jobmru;		/* most recently used job list */
84204076Spjdpid_t initialpgrp;		/* pgrp of shell on invocation */
85204076Spjd#endif
86204076Spjdint in_waitcmd = 0;		/* are we in waitcmd()? */
87204076Spjdint in_dowait = 0;		/* are we in dowait()? */
88204076Spjdvolatile sig_atomic_t breakwaitcmd = 0;	/* should wait be terminated? */
89204076Spjdstatic int ttyfd = -1;
90204076Spjd
91204076Spjd#if JOBS
92204076SpjdSTATIC void restartjob(struct job *);
93204076Spjd#endif
94204076SpjdSTATIC void freejob(struct job *);
95204076SpjdSTATIC struct job *getjob(char *);
96204076SpjdSTATIC pid_t dowait(int, struct job *);
97204076SpjdSTATIC pid_t waitproc(int, int *);
98204076SpjdSTATIC void cmdtxt(union node *);
99204076SpjdSTATIC void cmdputs(char *);
100204076Spjd#if JOBS
101204076SpjdSTATIC void setcurjob(struct job *);
102204076SpjdSTATIC void deljob(struct job *);
103204076SpjdSTATIC struct job *getcurjob(struct job *);
104204076Spjd#endif
105204076SpjdSTATIC void showjob(struct job *, pid_t, int, int);
106204076Spjd
107204076Spjd
108204076Spjd/*
109204076Spjd * Turn job control on and off.
110204076Spjd */
111204076Spjd
112204076SpjdMKINIT int jobctl;
113204076Spjd
114204076Spjd#if JOBS
115204076Spjdvoid
116204076Spjdsetjobctl(int on)
117204076Spjd{
118204076Spjd	int i;
119204076Spjd
120204076Spjd	if (on == jobctl || rootshell == 0)
121204076Spjd		return;
122204076Spjd	if (on) {
123204076Spjd		if (ttyfd != -1)
124204076Spjd			close(ttyfd);
125204076Spjd		if ((ttyfd = open(_PATH_TTY, O_RDWR)) < 0) {
126204076Spjd			i = 0;
127204076Spjd			while (i <= 2 && !isatty(i))
128204076Spjd				i++;
129204076Spjd			if (i > 2 || (ttyfd = dup(i)) < 0)
130204076Spjd				goto out;
131204076Spjd		}
132204076Spjd		if (fcntl(ttyfd, FD_CLOEXEC, 1) < 0) {
133204076Spjd			close(ttyfd);
134204076Spjd			ttyfd = -1;
135204076Spjd			goto out;
136204076Spjd		}
137204076Spjd		do { /* while we are in the background */
138204076Spjd			initialpgrp = tcgetpgrp(ttyfd);
139204076Spjd			if (initialpgrp < 0) {
140204076Spjdout:				out2str("sh: can't access tty; job control turned off\n");
141204076Spjd				mflag = 0;
142204076Spjd				return;
143204076Spjd			}
144204076Spjd			if (initialpgrp == -1)
145204076Spjd				initialpgrp = getpgrp();
146204076Spjd			else if (initialpgrp != getpgrp()) {
147204076Spjd				killpg(0, SIGTTIN);
148204076Spjd				continue;
149204076Spjd			}
150204076Spjd		} while (0);
151204076Spjd		setsignal(SIGTSTP);
152204076Spjd		setsignal(SIGTTOU);
153204076Spjd		setsignal(SIGTTIN);
154204076Spjd		setpgid(0, rootpid);
155211982Spjd		tcsetpgrp(ttyfd, rootpid);
156204076Spjd	} else { /* turning job control off */
157211982Spjd		setpgid(0, initialpgrp);
158204076Spjd		tcsetpgrp(ttyfd, initialpgrp);
159204076Spjd		close(ttyfd);
160204076Spjd		ttyfd = -1;
161204076Spjd		setsignal(SIGTSTP);
162204076Spjd		setsignal(SIGTTOU);
163204076Spjd		setsignal(SIGTTIN);
164204076Spjd	}
165204076Spjd	jobctl = on;
166204076Spjd}
167204076Spjd#endif
168204076Spjd
169204076Spjd
170204076Spjd#ifdef mkinit
171204076SpjdINCLUDE <sys/types.h>
172204076SpjdINCLUDE <stdlib.h>
173204076Spjd
174204076SpjdSHELLPROC {
175204076Spjd	backgndpid = -1;
176204076Spjd#if JOBS
177204076Spjd	jobctl = 0;
178204076Spjd#endif
179204076Spjd}
180204076Spjd
181204076Spjd#endif
182204076Spjd
183204076Spjd
184204076Spjd
185204076Spjd#if JOBS
186204076Spjdint
187204076Spjdfgcmd(int argc __unused, char **argv)
188204076Spjd{
189204076Spjd	struct job *jp;
190204076Spjd	pid_t pgrp;
191204076Spjd	int status;
192204076Spjd
193204076Spjd	jp = getjob(argv[1]);
194204076Spjd	if (jp->jobctl == 0)
195204076Spjd		error("job not created under job control");
196204076Spjd	out1str(jp->ps[0].cmd);
197204076Spjd	out1c('\n');
198204076Spjd	flushout(&output);
199204076Spjd	pgrp = jp->ps[0].pid;
200204076Spjd	tcsetpgrp(ttyfd, pgrp);
201204076Spjd	restartjob(jp);
202204076Spjd	jp->foreground = 1;
203209183Spjd	INTOFF;
204209183Spjd	status = waitforjob(jp, (int *)NULL);
205209183Spjd	INTON;
206209183Spjd	return status;
207204076Spjd}
208204076Spjd
209204076Spjd
210204076Spjdint
211204076Spjdbgcmd(int argc, char **argv)
212204076Spjd{
213204076Spjd	char s[64];
214204076Spjd	struct job *jp;
215204076Spjd
216204076Spjd	do {
217204076Spjd		jp = getjob(*++argv);
218204076Spjd		if (jp->jobctl == 0)
219204076Spjd			error("job not created under job control");
220204076Spjd		if (jp->state == JOBDONE)
221204076Spjd			continue;
222204076Spjd		restartjob(jp);
223204076Spjd		jp->foreground = 0;
224204076Spjd		fmtstr(s, 64, "[%d] ", jp - jobtab + 1);
225204076Spjd		out1str(s);
226204076Spjd		out1str(jp->ps[0].cmd);
227204076Spjd		out1c('\n');
228204076Spjd	} while (--argc > 1);
229211982Spjd	return 0;
230204076Spjd}
231204076Spjd
232204076Spjd
233204076SpjdSTATIC void
234204076Spjdrestartjob(struct job *jp)
235204076Spjd{
236204076Spjd	struct procstat *ps;
237204076Spjd	int i;
238204076Spjd
239204076Spjd	if (jp->state == JOBDONE)
240204076Spjd		return;
241204076Spjd	setcurjob(jp);
242204076Spjd	INTOFF;
243204076Spjd	killpg(jp->ps[0].pid, SIGCONT);
244204076Spjd	for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) {
245213531Spjd		if (WIFSTOPPED(ps->status)) {
246213531Spjd			ps->status = -1;
247204076Spjd			jp->state = 0;
248204076Spjd		}
249204076Spjd	}
250204076Spjd	INTON;
251204076Spjd}
252204076Spjd#endif
253204076Spjd
254204076Spjd
255204076Spjdint
256212899Spjdjobscmd(int argc, char *argv[])
257204076Spjd{
258204076Spjd	char *id;
259204076Spjd	int ch, sformat, lformat;
260204076Spjd
261204076Spjd	optind = optreset = 1;
262204076Spjd	opterr = 0;
263204076Spjd	sformat = lformat = 0;
264204076Spjd	while ((ch = getopt(argc, argv, "ls")) != -1) {
265204076Spjd		switch (ch) {
266204076Spjd		case 'l':
267204076Spjd			lformat = 1;
268204076Spjd			break;
269212899Spjd		case 's':
270204076Spjd			sformat = 1;
271204076Spjd			break;
272204076Spjd		case '?':
273204076Spjd		default:
274204076Spjd			error("unknown option: -%c", optopt);
275204076Spjd		}
276204076Spjd	}
277204076Spjd	argc -= optind;
278204076Spjd	argv += optind;
279204076Spjd
280204076Spjd	if (argc == 0)
281204076Spjd		showjobs(0, sformat, lformat);
282204076Spjd	else
283204076Spjd		while ((id = *argv++) != NULL)
284204076Spjd			showjob(getjob(id), 0, sformat, lformat);
285204076Spjd
286204076Spjd	return (0);
287204076Spjd}
288204076Spjd
289204076SpjdSTATIC void
290204076Spjdshowjob(struct job *jp, pid_t pid, int sformat, int lformat)
291204076Spjd{
292204076Spjd	char s[64];
293204076Spjd	struct procstat *ps;
294204076Spjd	struct job *j;
295204076Spjd	int col, curr, i, jobno, prev, procno;
296204076Spjd	char c;
297204076Spjd
298204076Spjd	procno = jp->nprocs;
299210881Spjd	jobno = jp - jobtab + 1;
300210881Spjd	curr = prev = 0;
301210881Spjd#if JOBS
302210881Spjd	if ((j = getcurjob(NULL)) != NULL) {
303210881Spjd		curr = j - jobtab + 1;
304210881Spjd		if ((j = getcurjob(j)) != NULL)
305210881Spjd			prev = j - jobtab + 1;
306204076Spjd	}
307204076Spjd#endif
308204076Spjd	for (ps = jp->ps ; ; ps++) {	/* for each process */
309204076Spjd		if (sformat) {
310204076Spjd			out1fmt("%d\n", (int)ps->pid);
311204076Spjd			goto skip;
312204076Spjd		}
313204076Spjd		if (!lformat && ps != jp->ps && pid == 0)
314204076Spjd			goto skip;
315204076Spjd		if (pid != 0 && pid != ps->pid)
316204076Spjd			goto skip;
317204076Spjd		if (jobno == curr && ps == jp->ps)
318204076Spjd			c = '+';
319204076Spjd		else if (jobno == prev && ps == jp->ps)
320204076Spjd			c = '-';
321204076Spjd		else
322204076Spjd			c = ' ';
323204076Spjd		if (ps == jp->ps)
324204076Spjd			fmtstr(s, 64, "[%d] %c ", jobno, c);
325204076Spjd		else
326204076Spjd			fmtstr(s, 64, "    %c ", c);
327204076Spjd		out1str(s);
328204076Spjd		col = strlen(s);
329204076Spjd		if (lformat) {
330204076Spjd			fmtstr(s, 64, "%d ", (int)ps->pid);
331204076Spjd			out1str(s);
332204076Spjd			col += strlen(s);
333204076Spjd		}
334204076Spjd		s[0] = '\0';
335204076Spjd		if (ps != jp->ps) {
336204076Spjd			*s = '\0';
337204076Spjd		} else if (ps->status == -1) {
338204076Spjd			strcpy(s, "Running");
339204076Spjd		} else if (WIFEXITED(ps->status)) {
340204076Spjd			if (WEXITSTATUS(ps->status) == 0)
341204076Spjd				strcpy(s, "Done");
342204076Spjd			else
343204076Spjd				fmtstr(s, 64, "Done (%d)",
344204076Spjd				    WEXITSTATUS(ps->status));
345204076Spjd		} else {
346204076Spjd#if JOBS
347204076Spjd			if (WIFSTOPPED(ps->status))
348204076Spjd				i = WSTOPSIG(ps->status);
349204076Spjd			else
350204076Spjd#endif
351204076Spjd				i = WTERMSIG(ps->status);
352204076Spjd			if ((i & 0x7F) < NSIG && sys_siglist[i & 0x7F])
353204076Spjd				scopy(sys_siglist[i & 0x7F], s);
354204076Spjd			else
355204076Spjd				fmtstr(s, 64, "Signal %d", i & 0x7F);
356204076Spjd			if (WCOREDUMP(ps->status))
357204076Spjd				strcat(s, " (core dumped)");
358204076Spjd		}
359204076Spjd		out1str(s);
360204076Spjd		col += strlen(s);
361204076Spjd		do {
362204076Spjd			out1c(' ');
363204076Spjd			col++;
364204076Spjd		} while (col < 30);
365204076Spjd		out1str(ps->cmd);
366204076Spjd		out1c('\n');
367204076Spjdskip:		if (--procno <= 0)
368204076Spjd			break;
369204076Spjd	}
370204076Spjd}
371204076Spjd
372204076Spjd/*
373204076Spjd * Print a list of jobs.  If "change" is nonzero, only print jobs whose
374204076Spjd * statuses have changed since the last call to showjobs.
375204076Spjd *
376204076Spjd * If the shell is interrupted in the process of creating a job, the
377204076Spjd * result may be a job structure containing zero processes.  Such structures
378204076Spjd * will be freed here.
379204076Spjd */
380204076Spjd
381204076Spjdvoid
382204076Spjdshowjobs(int change, int sformat, int lformat)
383204076Spjd{
384204076Spjd	int jobno;
385204076Spjd	struct job *jp;
386204076Spjd
387204076Spjd	TRACE(("showjobs(%d) called\n", change));
388204076Spjd	while (dowait(0, (struct job *)NULL) > 0);
389204076Spjd	for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) {
390204076Spjd		if (! jp->used)
391204076Spjd			continue;
392204076Spjd		if (jp->nprocs == 0) {
393204076Spjd			freejob(jp);
394204076Spjd			continue;
395204076Spjd		}
396204076Spjd		if (change && ! jp->changed)
397204076Spjd			continue;
398204076Spjd		showjob(jp, 0, sformat, lformat);
399204076Spjd		jp->changed = 0;
400204076Spjd		if (jp->state == JOBDONE) {
401204076Spjd			freejob(jp);
402204076Spjd		}
403204076Spjd	}
404204076Spjd}
405204076Spjd
406204076Spjd
407204076Spjd/*
408204076Spjd * Mark a job structure as unused.
409204076Spjd */
410204076Spjd
411204076SpjdSTATIC void
412204076Spjdfreejob(struct job *jp)
413204076Spjd{
414204076Spjd	struct procstat *ps;
415204076Spjd	int i;
416204076Spjd
417204076Spjd	INTOFF;
418204076Spjd	for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) {
419204076Spjd		if (ps->cmd != nullstr)
420204076Spjd			ckfree(ps->cmd);
421204076Spjd	}
422204076Spjd	if (jp->ps != &jp->ps0)
423204076Spjd		ckfree(jp->ps);
424204076Spjd	jp->used = 0;
425204076Spjd#if JOBS
426204076Spjd	deljob(jp);
427204076Spjd#endif
428204076Spjd	INTON;
429204076Spjd}
430204076Spjd
431204076Spjd
432204076Spjd
433204076Spjdint
434204076Spjdwaitcmd(int argc, char **argv)
435204076Spjd{
436204076Spjd	struct job *job;
437204076Spjd	int status, retval;
438204076Spjd	struct job *jp;
439204076Spjd
440204076Spjd	if (argc > 1) {
441204076Spjd		job = getjob(argv[1]);
442204076Spjd	} else {
443204076Spjd		job = NULL;
444204076Spjd	}
445204076Spjd
446204076Spjd	/*
447204076Spjd	 * Loop until a process is terminated or stopped, or a SIGINT is
448204076Spjd	 * received.
449204076Spjd	 */
450209181Spjd
451204076Spjd	in_waitcmd++;
452204076Spjd	do {
453204076Spjd		if (job != NULL) {
454204076Spjd			if (job->state) {
455204076Spjd				status = job->ps[job->nprocs - 1].status;
456204076Spjd				if (WIFEXITED(status))
457204076Spjd					retval = WEXITSTATUS(status);
458204076Spjd#if JOBS
459204076Spjd				else if (WIFSTOPPED(status))
460204076Spjd					retval = WSTOPSIG(status) + 128;
461204076Spjd#endif
462204076Spjd				else
463204076Spjd					retval = WTERMSIG(status) + 128;
464205738Spjd				if (! iflag)
465205738Spjd					freejob(job);
466205738Spjd				in_waitcmd--;
467204076Spjd				return retval;
468205738Spjd			}
469204076Spjd		} else {
470204076Spjd			for (jp = jobtab ; ; jp++) {
471204076Spjd				if (jp >= jobtab + njobs) {	/* no running procs */
472204076Spjd					in_waitcmd--;
473204076Spjd					return 0;
474204076Spjd				}
475204076Spjd				if (jp->used && jp->state == 0)
476204076Spjd					break;
477204076Spjd			}
478205738Spjd		}
479210881Spjd	} while (dowait(1, (struct job *)NULL) != -1);
480205738Spjd	in_waitcmd--;
481205738Spjd
482211983Spjd	return 0;
483205738Spjd}
484204076Spjd
485205738Spjd
486207347Spjd
487204076Spjdint
488204076Spjdjobidcmd(int argc __unused, char **argv)
489204076Spjd{
490205738Spjd	struct job *jp;
491204076Spjd	int i;
492204076Spjd
493204076Spjd	jp = getjob(argv[1]);
494204076Spjd	for (i = 0 ; i < jp->nprocs ; ) {
495207371Spjd		out1fmt("%d", (int)jp->ps[i].pid);
496207371Spjd		out1c(++i < jp->nprocs? ' ' : '\n');
497207371Spjd	}
498204076Spjd	return 0;
499204076Spjd}
500204076Spjd
501204076Spjd
502204076Spjd
503204076Spjd/*
504204076Spjd * Convert a job name to a job structure.
505204076Spjd */
506204076Spjd
507204076SpjdSTATIC struct job *
508204076Spjdgetjob(char *name)
509204076Spjd{
510204076Spjd	int jobno;
511205738Spjd	struct job *found, *jp;
512204076Spjd	pid_t pid;
513204076Spjd	int i;
514204076Spjd
515204076Spjd	if (name == NULL) {
516204076Spjd#if JOBS
517204076Spjdcurrentjob:	if ((jp = getcurjob(NULL)) == NULL)
518204076Spjd			error("No current job");
519205738Spjd		return (jp);
520204076Spjd#else
521204076Spjd		error("No current job");
522204076Spjd#endif
523204076Spjd	} else if (name[0] == '%') {
524204076Spjd		if (is_digit(name[1])) {
525204076Spjd			jobno = number(name + 1);
526204076Spjd			if (jobno > 0 && jobno <= njobs
527204076Spjd			 && jobtab[jobno - 1].used != 0)
528204076Spjd				return &jobtab[jobno - 1];
529204076Spjd#if JOBS
530204076Spjd		} else if (name[1] == '%' && name[2] == '\0') {
531204076Spjd			goto currentjob;
532204076Spjd		} else if (name[1] == '+' && name[2] == '\0') {
533204076Spjd			goto currentjob;
534204076Spjd		} else if (name[1] == '-' && name[2] == '\0') {
535204076Spjd			if ((jp = getcurjob(NULL)) == NULL ||
536204076Spjd			    (jp = getcurjob(jp)) == NULL)
537204076Spjd				error("No previous job");
538204076Spjd			return (jp);
539204076Spjd#endif
540204076Spjd		} else if (name[1] == '?') {
541204076Spjd			found = NULL;
542204076Spjd			for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
543204076Spjd				if (jp->used && jp->nprocs > 0
544204076Spjd				 && strstr(jp->ps[0].cmd, name + 2) != NULL) {
545204076Spjd					if (found)
546204076Spjd						error("%s: ambiguous", name);
547204076Spjd					found = jp;
548204076Spjd				}
549204076Spjd			}
550204076Spjd			if (found != NULL)
551205738Spjd				return (found);
552204076Spjd		} else {
553204076Spjd			found = NULL;
554204076Spjd			for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
555204076Spjd				if (jp->used && jp->nprocs > 0
556205738Spjd				 && prefix(name + 1, jp->ps[0].cmd)) {
557204076Spjd					if (found)
558204076Spjd						error("%s: ambiguous", name);
559204076Spjd					found = jp;
560204076Spjd				}
561207371Spjd			}
562207371Spjd			if (found)
563207371Spjd				return found;
564204076Spjd		}
565204076Spjd	} else if (is_number(name)) {
566204076Spjd		pid = (pid_t)number(name);
567204076Spjd		for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
568204076Spjd			if (jp->used && jp->nprocs > 0
569204076Spjd			 && jp->ps[jp->nprocs - 1].pid == pid)
570204076Spjd				return jp;
571204076Spjd		}
572204076Spjd	}
573204076Spjd	error("No such job: %s", name);
574204076Spjd	/*NOTREACHED*/
575204076Spjd	return NULL;
576204076Spjd}
577204076Spjd
578205738Spjd
579204076Spjd
580204076Spjd/*
581204076Spjd * Return a new job structure,
582204076Spjd */
583204076Spjd
584204076Spjdstruct job *
585204076Spjdmakejob(union node *node __unused, int nprocs)
586205738Spjd{
587204076Spjd	int i;
588204076Spjd	struct job *jp;
589204076Spjd
590204076Spjd	for (i = njobs, jp = jobtab ; ; jp++) {
591204076Spjd		if (--i < 0) {
592204076Spjd			INTOFF;
593204076Spjd			if (njobs == 0) {
594204076Spjd				jobtab = ckmalloc(4 * sizeof jobtab[0]);
595204076Spjd#if JOBS
596204076Spjd				jobmru = NULL;
597204076Spjd#endif
598204076Spjd			} else {
599204076Spjd				jp = ckmalloc((njobs + 4) * sizeof jobtab[0]);
600204076Spjd				memcpy(jp, jobtab, njobs * sizeof jp[0]);
601204076Spjd#if JOBS
602204076Spjd				/* Relocate `next' pointers and list head */
603204076Spjd				if (jobmru != NULL)
604204076Spjd					jobmru = &jp[jobmru - jobtab];
605204076Spjd				for (i = 0; i < njobs; i++)
606204076Spjd					if (jp[i].next != NULL)
607204076Spjd						jp[i].next = &jp[jp[i].next -
608204076Spjd						    jobtab];
609204076Spjd#endif
610204076Spjd				/* Relocate `ps' pointers */
611204076Spjd				for (i = 0; i < njobs; i++)
612204076Spjd					if (jp[i].ps == &jobtab[i].ps0)
613204076Spjd						jp[i].ps = &jp[i].ps0;
614204076Spjd				ckfree(jobtab);
615204076Spjd				jobtab = jp;
616204076Spjd			}
617204076Spjd			jp = jobtab + njobs;
618204076Spjd			for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0);
619204076Spjd			INTON;
620204076Spjd			break;
621204076Spjd		}
622204076Spjd		if (jp->used == 0)
623204076Spjd			break;
624204076Spjd	}
625204076Spjd	INTOFF;
626204076Spjd	jp->state = 0;
627204076Spjd	jp->used = 1;
628204076Spjd	jp->changed = 0;
629205738Spjd	jp->nprocs = 0;
630204076Spjd	jp->foreground = 0;
631204076Spjd#if JOBS
632204076Spjd	jp->jobctl = jobctl;
633204076Spjd	jp->next = NULL;
634204076Spjd#endif
635204076Spjd	if (nprocs > 1) {
636204076Spjd		jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
637204076Spjd	} else {
638204076Spjd		jp->ps = &jp->ps0;
639204076Spjd	}
640204076Spjd	INTON;
641204076Spjd	TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
642204076Spjd	    jp - jobtab + 1));
643204076Spjd	return jp;
644204076Spjd}
645204076Spjd
646204076Spjd#if JOBS
647204076SpjdSTATIC void
648204076Spjdsetcurjob(struct job *cj)
649205738Spjd{
650205738Spjd	struct job *jp, *prev;
651205738Spjd
652205738Spjd	for (prev = NULL, jp = jobmru; jp != NULL; prev = jp, jp = jp->next) {
653205738Spjd		if (jp == cj) {
654205738Spjd			if (prev != NULL)
655205738Spjd				prev->next = jp->next;
656212038Spjd			else
657205738Spjd				jobmru = jp->next;
658205738Spjd			jp->next = jobmru;
659211983Spjd			jobmru = cj;
660212038Spjd			return;
661205738Spjd		}
662205738Spjd	}
663205738Spjd	cj->next = jobmru;
664205738Spjd	jobmru = cj;
665205738Spjd}
666205738Spjd
667205738SpjdSTATIC void
668205738Spjddeljob(struct job *j)
669205738Spjd{
670205738Spjd	struct job *jp, *prev;
671204076Spjd
672204076Spjd	for (prev = NULL, jp = jobmru; jp != NULL; prev = jp, jp = jp->next) {
673204076Spjd		if (jp == j) {
674204076Spjd			if (prev != NULL)
675204076Spjd				prev->next = jp->next;
676204076Spjd			else
677204076Spjd				jobmru = jp->next;
678211878Spjd			return;
679211878Spjd		}
680211878Spjd	}
681211878Spjd}
682211878Spjd
683211878Spjd/*
684211878Spjd * Return the most recently used job that isn't `nj', and preferably one
685211878Spjd * that is stopped.
686211878Spjd */
687211878SpjdSTATIC struct job *
688204076Spjdgetcurjob(struct job *nj)
689204076Spjd{
690204076Spjd	struct job *jp;
691204076Spjd
692204076Spjd	/* Try to find a stopped one.. */
693204076Spjd	for (jp = jobmru; jp != NULL; jp = jp->next)
694204076Spjd		if (jp->used && jp != nj && jp->state == JOBSTOPPED)
695204076Spjd			return (jp);
696204076Spjd	/* Otherwise the most recently used job that isn't `nj' */
697204076Spjd	for (jp = jobmru; jp != NULL; jp = jp->next)
698204076Spjd		if (jp->used && jp != nj)
699204076Spjd			return (jp);
700204076Spjd
701204076Spjd	return (NULL);
702204076Spjd}
703204076Spjd
704204076Spjd#endif
705204076Spjd
706204076Spjd/*
707206669Spjd * Fork of a subshell.  If we are doing job control, give the subshell its
708204076Spjd * own process group.  Jp is a job structure that the job is to be added to.
709204076Spjd * N is the command that will be evaluated by the child.  Both jp and n may
710204076Spjd * be NULL.  The mode parameter can be one of the following:
711204076Spjd *	FORK_FG - Fork off a foreground process.
712204076Spjd *	FORK_BG - Fork off a background process.
713204076Spjd *	FORK_NOJOB - Like FORK_FG, but don't give the process its own
714204076Spjd *		     process group even if job control is on.
715204076Spjd *
716204076Spjd * When job control is turned off, background processes have their standard
717204076Spjd * input redirected to /dev/null (except for the second and later processes
718204076Spjd * in a pipeline).
719204076Spjd */
720204076Spjd
721204076Spjdpid_t
722204076Spjdforkshell(struct job *jp, union node *n, int mode)
723204076Spjd{
724204076Spjd	pid_t pid;
725204076Spjd	pid_t pgrp;
726204076Spjd
727204076Spjd	TRACE(("forkshell(%%%d, 0x%lx, %d) called\n", jp - jobtab, (long)n,
728204076Spjd	    mode));
729204076Spjd	INTOFF;
730204076Spjd	pid = fork();
731204076Spjd	if (pid == -1) {
732204076Spjd		TRACE(("Fork failed, errno=%d\n", errno));
733204076Spjd		INTON;
734204076Spjd		error("Cannot fork: %s", strerror(errno));
735204076Spjd	}
736204076Spjd	if (pid == 0) {
737204076Spjd		struct job *p;
738204076Spjd		int wasroot;
739204076Spjd		int i;
740204076Spjd
741204076Spjd		TRACE(("Child shell %d\n", (int)getpid()));
742204076Spjd		wasroot = rootshell;
743204076Spjd		rootshell = 0;
744204076Spjd		for (i = njobs, p = jobtab ; --i >= 0 ; p++)
745204076Spjd			if (p->used)
746204076Spjd				freejob(p);
747204076Spjd		closescript();
748204076Spjd		INTON;
749204076Spjd		clear_traps();
750204076Spjd#if JOBS
751204076Spjd		jobctl = 0;		/* do job control only in root shell */
752204076Spjd		if (wasroot && mode != FORK_NOJOB && mflag) {
753204076Spjd			if (jp == NULL || jp->nprocs == 0)
754204076Spjd				pgrp = getpid();
755212034Spjd			else
756204076Spjd				pgrp = jp->ps[0].pid;
757204076Spjd			if (setpgid(0, pgrp) == 0 && mode == FORK_FG) {
758212038Spjd				/*** this causes superfluous TIOCSPGRPS ***/
759212038Spjd				if (tcsetpgrp(ttyfd, pgrp) < 0)
760212038Spjd					error("tcsetpgrp failed, errno=%d", errno);
761212038Spjd			}
762212038Spjd			setsignal(SIGTSTP);
763212038Spjd			setsignal(SIGTTOU);
764212038Spjd		} else if (mode == FORK_BG) {
765212038Spjd			ignoresig(SIGINT);
766204076Spjd			ignoresig(SIGQUIT);
767204076Spjd			if ((jp == NULL || jp->nprocs == 0) &&
768204076Spjd			    ! fd0_redirected_p ()) {
769204076Spjd				close(0);
770212034Spjd				if (open(_PATH_DEVNULL, O_RDONLY) != 0)
771204076Spjd					error("Can't open %s: %s",
772204076Spjd					    _PATH_DEVNULL, strerror(errno));
773204076Spjd			}
774204076Spjd		}
775212038Spjd#else
776212038Spjd		if (mode == FORK_BG) {
777204076Spjd			ignoresig(SIGINT);
778204076Spjd			ignoresig(SIGQUIT);
779204076Spjd			if ((jp == NULL || jp->nprocs == 0) &&
780211977Spjd			    ! fd0_redirected_p ()) {
781211984Spjd				close(0);
782211984Spjd				if (open(_PATH_DEVNULL, O_RDONLY) != 0)
783204076Spjd					error("Can't open %s: %s",
784211977Spjd					    _PATH_DEVNULL, strerror(errno));
785204076Spjd			}
786204076Spjd		}
787204076Spjd#endif
788212038Spjd		if (wasroot && iflag) {
789212038Spjd			setsignal(SIGINT);
790212038Spjd			setsignal(SIGQUIT);
791204076Spjd			setsignal(SIGTERM);
792213007Spjd		}
793213007Spjd		return pid;
794213007Spjd	}
795213530Spjd	if (rootshell && mode != FORK_NOJOB && mflag) {
796213530Spjd		if (jp == NULL || jp->nprocs == 0)
797213530Spjd			pgrp = pid;
798213530Spjd		else
799213530Spjd			pgrp = jp->ps[0].pid;
800213530Spjd		setpgid(pid, pgrp);
801213007Spjd	}
802213007Spjd	if (mode == FORK_BG)
803213007Spjd		backgndpid = pid;		/* set $! */
804213007Spjd	if (jp) {
805213007Spjd		struct procstat *ps = &jp->ps[jp->nprocs++];
806213007Spjd		ps->pid = pid;
807213007Spjd		ps->status = -1;
808213007Spjd		ps->cmd = nullstr;
809213007Spjd		if (iflag && rootshell && n)
810210881Spjd			ps->cmd = commandtext(n);
811205738Spjd		jp->foreground = mode == FORK_FG;
812204076Spjd#if JOBS
813204076Spjd		setcurjob(jp);
814204076Spjd#endif
815204076Spjd	}
816204076Spjd	INTON;
817204076Spjd	TRACE(("In parent shell:  child = %d\n", (int)pid));
818204076Spjd	return pid;
819204076Spjd}
820204076Spjd
821204076Spjd
822213530Spjd
823204076Spjd/*
824204076Spjd * Wait for job to finish.
825204076Spjd *
826204076Spjd * Under job control we have the problem that while a child process is
827204076Spjd * running interrupts generated by the user are sent to the child but not
828204076Spjd * to the shell.  This means that an infinite loop started by an inter-
829204076Spjd * active user may be hard to kill.  With job control turned off, an
830204076Spjd * interactive user may place an interactive program inside a loop.  If
831204076Spjd * the interactive program catches interrupts, the user doesn't want
832204076Spjd * these interrupts to also abort the loop.  The approach we take here
833204076Spjd * is to have the shell ignore interrupt signals while waiting for a
834204076Spjd * foreground process to terminate, and then send itself an interrupt
835204076Spjd * signal if the child process was terminated by an interrupt signal.
836204076Spjd * Unfortunately, some programs want to do a bit of cleanup and then
837204076Spjd * exit on interrupt; unless these processes terminate themselves by
838204076Spjd * sending a signal to themselves (instead of calling exit) they will
839204076Spjd * confuse this approach.
840204076Spjd */
841204076Spjd
842204076Spjdint
843204076Spjdwaitforjob(struct job *jp, int *origstatus)
844204076Spjd{
845204076Spjd#if JOBS
846204076Spjd	pid_t mypgrp = getpgrp();
847204076Spjd#endif
848204076Spjd	int status;
849204076Spjd	int st;
850204076Spjd
851204076Spjd	INTOFF;
852204076Spjd	TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1));
853204076Spjd	while (jp->state == 0)
854204076Spjd		if (dowait(1, jp) == -1)
855204076Spjd			dotrap();
856204076Spjd#if JOBS
857204076Spjd	if (jp->jobctl) {
858204076Spjd		if (tcsetpgrp(ttyfd, mypgrp) < 0)
859204076Spjd			error("tcsetpgrp failed, errno=%d\n", errno);
860204076Spjd	}
861204076Spjd	if (jp->state == JOBSTOPPED)
862204076Spjd		setcurjob(jp);
863204076Spjd#endif
864204076Spjd	status = jp->ps[jp->nprocs - 1].status;
865204076Spjd	if (origstatus != NULL)
866204076Spjd		*origstatus = status;
867204076Spjd	/* convert to 8 bits */
868204076Spjd	if (WIFEXITED(status))
869204076Spjd		st = WEXITSTATUS(status);
870204076Spjd#if JOBS
871204076Spjd	else if (WIFSTOPPED(status))
872204076Spjd		st = WSTOPSIG(status) + 128;
873204076Spjd#endif
874204076Spjd	else
875204076Spjd		st = WTERMSIG(status) + 128;
876204076Spjd	if (! JOBS || jp->state == JOBDONE)
877204076Spjd		freejob(jp);
878204076Spjd	if (int_pending()) {
879204076Spjd		if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT)
880204076Spjd			kill(getpid(), SIGINT);
881204076Spjd		else
882204076Spjd			CLEAR_PENDING_INT;
883211881Spjd	}
884204076Spjd	INTON;
885204076Spjd	return st;
886204076Spjd}
887211881Spjd
888204076Spjd
889204076Spjd
890204076Spjd/*
891204076Spjd * Wait for a process to terminate.
892204076Spjd */
893204076Spjd
894211881SpjdSTATIC pid_t
895211881Spjddowait(int block, struct job *job)
896204076Spjd{
897204076Spjd	pid_t pid;
898204076Spjd	int status;
899211878Spjd	struct procstat *sp;
900211984Spjd	struct job *jp;
901212038Spjd	struct job *thisjob;
902204076Spjd	int done;
903204076Spjd	int stopped;
904204076Spjd	int sig;
905204076Spjd	int i;
906204076Spjd
907204076Spjd	in_dowait++;
908204076Spjd	TRACE(("dowait(%d) called\n", block));
909204076Spjd	do {
910204076Spjd		pid = waitproc(block, &status);
911204076Spjd		TRACE(("wait returns %d, status=%d\n", (int)pid, status));
912204076Spjd	} while ((pid == -1 && errno == EINTR && breakwaitcmd == 0) ||
913204076Spjd	    (WIFSTOPPED(status) && !iflag));
914204076Spjd	in_dowait--;
915204076Spjd	if (breakwaitcmd != 0) {
916204076Spjd		breakwaitcmd = 0;
917204076Spjd		return -1;
918204076Spjd	}
919204076Spjd	if (pid <= 0)
920204076Spjd		return pid;
921204076Spjd	INTOFF;
922204076Spjd	thisjob = NULL;
923204076Spjd	for (jp = jobtab ; jp < jobtab + njobs ; jp++) {
924204076Spjd		if (jp->used) {
925204076Spjd			done = 1;
926204076Spjd			stopped = 1;
927204076Spjd			for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) {
928204076Spjd				if (sp->pid == -1)
929204076Spjd					continue;
930204076Spjd				if (sp->pid == pid) {
931204076Spjd					TRACE(("Changing status of proc %d from 0x%x to 0x%x\n",
932204076Spjd						   (int)pid, sp->status,
933204076Spjd						   status));
934204076Spjd					sp->status = status;
935204076Spjd					thisjob = jp;
936204076Spjd				}
937204076Spjd				if (sp->status == -1)
938204076Spjd					stopped = 0;
939204076Spjd				else if (WIFSTOPPED(sp->status))
940204076Spjd					done = 0;
941204076Spjd			}
942204076Spjd			if (stopped) {		/* stopped or done */
943204076Spjd				int state = done? JOBDONE : JOBSTOPPED;
944204076Spjd				if (jp->state != state) {
945204076Spjd					TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state));
946204076Spjd					jp->state = state;
947204076Spjd#if JOBS
948204076Spjd					if (done)
949204076Spjd						deljob(jp);
950204076Spjd#endif
951204076Spjd				}
952204076Spjd			}
953204076Spjd		}
954204076Spjd	}
955204076Spjd	INTON;
956204076Spjd	if (! rootshell || ! iflag || (job && thisjob == job)) {
957204076Spjd#if JOBS
958204076Spjd		if (WIFSTOPPED(status))
959204076Spjd			sig = WSTOPSIG(status);
960204076Spjd		else
961204076Spjd#endif
962204076Spjd		{
963204076Spjd			if (WIFEXITED(status))
964204076Spjd				sig = 0;
965204076Spjd			else
966204076Spjd				sig = WTERMSIG(status);
967204076Spjd		}
968204076Spjd		if (sig != 0 && sig != SIGINT && sig != SIGPIPE) {
969204076Spjd			if (jp->foreground) {
970204076Spjd#if JOBS
971204076Spjd				if (WIFSTOPPED(status))
972204076Spjd					i = WSTOPSIG(status);
973204076Spjd				else
974204076Spjd#endif
975204076Spjd					i = WTERMSIG(status);
976204076Spjd				if ((i & 0x7F) < NSIG && sys_siglist[i & 0x7F])
977204076Spjd					out1str(sys_siglist[i & 0x7F]);
978204076Spjd				else
979204076Spjd					out1fmt("Signal %d", i & 0x7F);
980204076Spjd				if (WCOREDUMP(status))
981204076Spjd					out1str(" (core dumped)");
982204076Spjd				out1c('\n');
983204076Spjd			} else
984204076Spjd				showjob(thisjob, pid, 0, 1);
985204076Spjd		}
986204076Spjd	} else {
987204076Spjd		TRACE(("Not printing status, rootshell=%d, job=0x%x\n", rootshell, job));
988204076Spjd		if (thisjob)
989204076Spjd			thisjob->changed = 1;
990204076Spjd	}
991204076Spjd	return pid;
992204076Spjd}
993204076Spjd
994204076Spjd
995204076Spjd
996204076Spjd/*
997204076Spjd * Do a wait system call.  If job control is compiled in, we accept
998204076Spjd * stopped processes.  If block is zero, we return a value of zero
999204076Spjd * rather than blocking.
1000204076Spjd */
1001204076SpjdSTATIC pid_t
1002204076Spjdwaitproc(int block, int *status)
1003204076Spjd{
1004204076Spjd	int flags;
1005204076Spjd
1006204076Spjd#if JOBS
1007204076Spjd	flags = WUNTRACED;
1008204076Spjd#else
1009204076Spjd	flags = 0;
1010204076Spjd#endif
1011204076Spjd	if (block == 0)
1012204076Spjd		flags |= WNOHANG;
1013204076Spjd	return wait3(status, flags, (struct rusage *)NULL);
1014204076Spjd}
1015204076Spjd
1016204076Spjd/*
1017204076Spjd * return 1 if there are stopped jobs, otherwise 0
1018204076Spjd */
1019204076Spjdint job_warning = 0;
1020204076Spjdint
1021204076Spjdstoppedjobs(void)
1022204076Spjd{
1023204076Spjd	int jobno;
1024204076Spjd	struct job *jp;
1025204076Spjd
1026204076Spjd	if (job_warning)
1027204076Spjd		return (0);
1028204076Spjd	for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) {
1029204076Spjd		if (jp->used == 0)
1030204076Spjd			continue;
1031204076Spjd		if (jp->state == JOBSTOPPED) {
1032204076Spjd			out2str("You have stopped jobs.\n");
1033204076Spjd			job_warning = 2;
1034204076Spjd			return (1);
1035204076Spjd		}
1036204076Spjd	}
1037204076Spjd
1038204076Spjd	return (0);
1039204076Spjd}
1040204076Spjd
1041204076Spjd/*
1042204076Spjd * Return a string identifying a command (to be printed by the
1043204076Spjd * jobs command.
1044204076Spjd */
1045204076Spjd
1046204076SpjdSTATIC char *cmdnextc;
1047204076SpjdSTATIC int cmdnleft;
1048204076Spjd#define MAXCMDTEXT	200
1049204076Spjd
1050204076Spjdchar *
1051204076Spjdcommandtext(union node *n)
1052204076Spjd{
1053204076Spjd	char *name;
1054204076Spjd
1055204076Spjd	cmdnextc = name = ckmalloc(MAXCMDTEXT);
1056204076Spjd	cmdnleft = MAXCMDTEXT - 4;
1057204076Spjd	cmdtxt(n);
1058204076Spjd	*cmdnextc = '\0';
1059204076Spjd	return name;
1060204076Spjd}
1061204076Spjd
1062204076Spjd
1063204076SpjdSTATIC void
1064204076Spjdcmdtxt(union node *n)
1065204076Spjd{
1066204076Spjd	union node *np;
1067204076Spjd	struct nodelist *lp;
1068204076Spjd	char *p;
1069204076Spjd	int i;
1070204076Spjd	char s[2];
1071204076Spjd
1072204076Spjd	if (n == NULL)
1073204076Spjd		return;
1074204076Spjd	switch (n->type) {
1075204076Spjd	case NSEMI:
1076204076Spjd		cmdtxt(n->nbinary.ch1);
1077204076Spjd		cmdputs("; ");
1078204076Spjd		cmdtxt(n->nbinary.ch2);
1079204076Spjd		break;
1080204076Spjd	case NAND:
1081204076Spjd		cmdtxt(n->nbinary.ch1);
1082204076Spjd		cmdputs(" && ");
1083204076Spjd		cmdtxt(n->nbinary.ch2);
1084204076Spjd		break;
1085204076Spjd	case NOR:
1086204076Spjd		cmdtxt(n->nbinary.ch1);
1087204076Spjd		cmdputs(" || ");
1088204076Spjd		cmdtxt(n->nbinary.ch2);
1089204076Spjd		break;
1090204076Spjd	case NPIPE:
1091204076Spjd		for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
1092204076Spjd			cmdtxt(lp->n);
1093204076Spjd			if (lp->next)
1094204076Spjd				cmdputs(" | ");
1095204076Spjd		}
1096204076Spjd		break;
1097204076Spjd	case NSUBSHELL:
1098204076Spjd		cmdputs("(");
1099204076Spjd		cmdtxt(n->nredir.n);
1100204076Spjd		cmdputs(")");
1101204076Spjd		break;
1102204076Spjd	case NREDIR:
1103204076Spjd	case NBACKGND:
1104204076Spjd		cmdtxt(n->nredir.n);
1105204076Spjd		break;
1106204076Spjd	case NIF:
1107204076Spjd		cmdputs("if ");
1108204076Spjd		cmdtxt(n->nif.test);
1109204076Spjd		cmdputs("; then ");
1110204076Spjd		cmdtxt(n->nif.ifpart);
1111204076Spjd		cmdputs("...");
1112204076Spjd		break;
1113204076Spjd	case NWHILE:
1114204076Spjd		cmdputs("while ");
1115204076Spjd		goto until;
1116204076Spjd	case NUNTIL:
1117204076Spjd		cmdputs("until ");
1118204076Spjduntil:
1119204076Spjd		cmdtxt(n->nbinary.ch1);
1120204076Spjd		cmdputs("; do ");
1121204076Spjd		cmdtxt(n->nbinary.ch2);
1122204076Spjd		cmdputs("; done");
1123204076Spjd		break;
1124204076Spjd	case NFOR:
1125204076Spjd		cmdputs("for ");
1126204076Spjd		cmdputs(n->nfor.var);
1127204076Spjd		cmdputs(" in ...");
1128204076Spjd		break;
1129204076Spjd	case NCASE:
1130204076Spjd		cmdputs("case ");
1131204076Spjd		cmdputs(n->ncase.expr->narg.text);
1132204076Spjd		cmdputs(" in ...");
1133204076Spjd		break;
1134204076Spjd	case NDEFUN:
1135204076Spjd		cmdputs(n->narg.text);
1136204076Spjd		cmdputs("() ...");
1137204076Spjd		break;
1138204076Spjd	case NCMD:
1139204076Spjd		for (np = n->ncmd.args ; np ; np = np->narg.next) {
1140204076Spjd			cmdtxt(np);
1141204076Spjd			if (np->narg.next)
1142204076Spjd				cmdputs(" ");
1143204076Spjd		}
1144204076Spjd		for (np = n->ncmd.redirect ; np ; np = np->nfile.next) {
1145204076Spjd			cmdputs(" ");
1146204076Spjd			cmdtxt(np);
1147204076Spjd		}
1148204076Spjd		break;
1149204076Spjd	case NARG:
1150204076Spjd		cmdputs(n->narg.text);
1151204076Spjd		break;
1152204076Spjd	case NTO:
1153204076Spjd		p = ">";  i = 1;  goto redir;
1154204076Spjd	case NAPPEND:
1155204076Spjd		p = ">>";  i = 1;  goto redir;
1156204076Spjd	case NTOFD:
1157204076Spjd		p = ">&";  i = 1;  goto redir;
1158204076Spjd	case NCLOBBER:
1159204076Spjd		p = ">|"; i = 1; goto redir;
1160204076Spjd	case NFROM:
1161204076Spjd		p = "<";  i = 0;  goto redir;
1162204076Spjd	case NFROMTO:
1163204076Spjd		p = "<>";  i = 0;  goto redir;
1164204076Spjd	case NFROMFD:
1165204076Spjd		p = "<&";  i = 0;  goto redir;
1166204076Spjdredir:
1167204076Spjd		if (n->nfile.fd != i) {
1168204076Spjd			s[0] = n->nfile.fd + '0';
1169204076Spjd			s[1] = '\0';
1170204076Spjd			cmdputs(s);
1171204076Spjd		}
1172204076Spjd		cmdputs(p);
1173204076Spjd		if (n->type == NTOFD || n->type == NFROMFD) {
1174204076Spjd			if (n->ndup.dupfd >= 0)
1175204076Spjd				s[0] = n->ndup.dupfd + '0';
1176204076Spjd			else
1177204076Spjd				s[0] = '-';
1178204076Spjd			s[1] = '\0';
1179204076Spjd			cmdputs(s);
1180204076Spjd		} else {
1181204076Spjd			cmdtxt(n->nfile.fname);
1182204076Spjd		}
1183204076Spjd		break;
1184204076Spjd	case NHERE:
1185204076Spjd	case NXHERE:
1186204076Spjd		cmdputs("<<...");
1187204076Spjd		break;
1188204076Spjd	default:
1189204076Spjd		cmdputs("???");
1190204076Spjd		break;
1191204076Spjd	}
1192204076Spjd}
1193204076Spjd
1194204076Spjd
1195204076Spjd
1196204076SpjdSTATIC void
1197204076Spjdcmdputs(char *s)
1198204076Spjd{
1199204076Spjd	char *p, *q;
1200204076Spjd	char c;
1201204076Spjd	int subtype = 0;
1202204076Spjd
1203204076Spjd	if (cmdnleft <= 0)
1204204076Spjd		return;
1205204076Spjd	p = s;
1206204076Spjd	q = cmdnextc;
1207204076Spjd	while ((c = *p++) != '\0') {
1208204076Spjd		if (c == CTLESC)
1209204076Spjd			*q++ = *p++;
1210204076Spjd		else if (c == CTLVAR) {
1211204076Spjd			*q++ = '$';
1212204076Spjd			if (--cmdnleft > 0)
1213204076Spjd				*q++ = '{';
1214204076Spjd			subtype = *p++;
1215204076Spjd		} else if (c == '=' && subtype != 0) {
1216204076Spjd			*q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL];
1217204076Spjd			subtype = 0;
1218204076Spjd		} else if (c == CTLENDVAR) {
1219204076Spjd			*q++ = '}';
1220204076Spjd		} else if (c == CTLBACKQ || c == CTLBACKQ+CTLQUOTE)
1221204076Spjd			cmdnleft++;		/* ignore it */
1222204076Spjd		else
1223204076Spjd			*q++ = c;
1224204076Spjd		if (--cmdnleft <= 0) {
1225204076Spjd			*q++ = '.';
1226204076Spjd			*q++ = '.';
1227204076Spjd			*q++ = '.';
1228204076Spjd			break;
1229204076Spjd		}
1230204076Spjd	}
1231204076Spjd	cmdnextc = q;
1232204076Spjd}
1233204076Spjd