jobs.c revision 103223
198943Sluigi/*-
2117328Sluigi * Copyright (c) 1991, 1993
398943Sluigi *	The Regents of the University of California.  All rights reserved.
498943Sluigi *
598943Sluigi * This code is derived from software contributed to Berkeley by
698943Sluigi * Kenneth Almquist.
798943Sluigi *
898943Sluigi * Redistribution and use in source and binary forms, with or without
998943Sluigi * modification, are permitted provided that the following conditions
1098943Sluigi * are met:
1198943Sluigi * 1. Redistributions of source code must retain the above copyright
1298943Sluigi *    notice, this list of conditions and the following disclaimer.
1398943Sluigi * 2. Redistributions in binary form must reproduce the above copyright
1498943Sluigi *    notice, this list of conditions and the following disclaimer in the
1598943Sluigi *    documentation and/or other materials provided with the distribution.
1698943Sluigi * 3. All advertising materials mentioning features or use of this software
1798943Sluigi *    must display the following acknowledgement:
1898943Sluigi *	This product includes software developed by the University of
1998943Sluigi *	California, Berkeley and its contributors.
2098943Sluigi * 4. Neither the name of the University nor the names of its contributors
2198943Sluigi *    may be used to endorse or promote products derived from this software
2298943Sluigi *    without specific prior written permission.
2398943Sluigi *
2498943Sluigi * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2598943Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2698943Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2798943Sluigi * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2898943Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2998943Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30136071Sgreen * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3198943Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3298943Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3398943Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3498943Sluigi * SUCH DAMAGE.
3598943Sluigi */
3698943Sluigi
3798943Sluigi#ifndef lint
3898943Sluigi#if 0
3998943Sluigistatic char sccsid[] = "@(#)jobs.c	8.5 (Berkeley) 5/4/95";
4098943Sluigi#endif
4198943Sluigi#endif /* not lint */
4298943Sluigi#include <sys/cdefs.h>
4398943Sluigi__FBSDID("$FreeBSD: head/bin/sh/jobs.c 103223 2002-09-11 16:38:33Z nectar $");
44117469Sluigi
4598943Sluigi#include <fcntl.h>
4698943Sluigi#include <signal.h>
47136071Sgreen#include <errno.h>
48136071Sgreen#include <paths.h>
4998943Sluigi#include <unistd.h>
50175659Srwatson#include <stdlib.h>
51175659Srwatson#include <sys/param.h>
52169424Smaxim#include <sys/wait.h>
5398943Sluigi#include <sys/time.h>
54165648Spiso#include <sys/resource.h>
55136071Sgreen#include <paths.h>
56145246Sbrooks#include <sys/ioctl.h>
5798943Sluigi
5898943Sluigi#include "shell.h"
5998943Sluigi#if JOBS
6098943Sluigi#include <termios.h>
61145246Sbrooks#undef CEOF			/* syntax.h redefines this */
6298943Sluigi#endif
6398943Sluigi#include "redir.h"
6498943Sluigi#include "show.h"
6598943Sluigi#include "main.h"
66165648Spiso#include "parser.h"
6798943Sluigi#include "nodes.h"
68117328Sluigi#include "jobs.h"
69176391Sjulian#include "options.h"
7098943Sluigi#include "trap.h"
7198943Sluigi#include "syntax.h"
7298943Sluigi#include "input.h"
7398943Sluigi#include "output.h"
74165648Spiso#include "memalloc.h"
7598943Sluigi#include "error.h"
7698943Sluigi#include "mystring.h"
7798943Sluigi
78102098Sluigi
79123804Smaximstruct job *jobtab;		/* array of jobs */
80170923Smaximint njobs;			/* size of array */
81101628SluigiMKINIT pid_t backgndpid = -1;	/* pid of last background process */
82117328Sluigi#if JOBS
83123495Sluigistruct job *jobmru;		/* most recently used job list */
8498943Sluigipid_t initialpgrp;		/* pgrp of shell on invocation */
8598943Sluigi#endif
8698943Sluigiint in_waitcmd = 0;		/* are we in waitcmd()? */
87130013Scsjpint in_dowait = 0;		/* are we in dowait()? */
88130013Scsjpvolatile sig_atomic_t breakwaitcmd = 0;	/* should wait be terminated? */
89130013Scsjpstatic int ttyfd = -1;
90130013Scsjp
91130013Scsjp#if JOBS
9298943SluigiSTATIC void restartjob(struct job *);
93159636Soleg#endif
94159636SolegSTATIC void freejob(struct job *);
95159636SolegSTATIC struct job *getjob(char *);
96159636SolegSTATIC pid_t dowait(int, struct job *);
97159636SolegSTATIC pid_t waitproc(int, int *);
98159636SolegSTATIC void cmdtxt(union node *);
99159636SolegSTATIC void cmdputs(char *);
100159636Soleg#if JOBS
101159636SolegSTATIC void setcurjob(struct job *);
102159636SolegSTATIC void deljob(struct job *);
103159636SolegSTATIC struct job *getcurjob(struct job *);
104159636Soleg#endif
105159636SolegSTATIC void showjob(struct job *, pid_t, int, int);
106159636Soleg
107159636Soleg
108159636Soleg/*
109159636Soleg * Turn job control on and off.
110159636Soleg */
111159636Soleg
112159636SolegMKINIT int jobctl;
113159636Soleg
114159636Soleg#if JOBS
115159636Solegvoid
116159636Solegsetjobctl(int on)
117159636Soleg{
118159636Soleg	int i;
119159636Soleg
120158879Soleg	if (on == jobctl || rootshell == 0)
121158879Soleg		return;
122159636Soleg	if (on) {
123159636Soleg		if (ttyfd != -1)
124159636Soleg			close(ttyfd);
125159636Soleg		if ((ttyfd = open(_PATH_TTY, O_RDWR)) < 0) {
126159636Soleg			i = 0;
127159636Soleg			while (i <= 2 && !isatty(i))
128159636Soleg				i++;
129159636Soleg			if (i > 2 || (ttyfd = dup(i)) < 0)
130159636Soleg				goto out;
13198943Sluigi		}
132117328Sluigi		if (fcntl(ttyfd, F_SETFD, FD_CLOEXEC) < 0) {
133117328Sluigi			close(ttyfd);
134117328Sluigi			ttyfd = -1;
135117328Sluigi			goto out;
136117328Sluigi		}
13798943Sluigi		do { /* while we are in the background */
13898943Sluigi			initialpgrp = tcgetpgrp(ttyfd);
13998943Sluigi			if (initialpgrp < 0) {
140117469Sluigiout:				out2str("sh: can't access tty; job control turned off\n");
14198943Sluigi				mflag = 0;
14298943Sluigi				return;
14398943Sluigi			}
14498943Sluigi			if (initialpgrp == -1)
14598943Sluigi				initialpgrp = getpgrp();
14698943Sluigi			else if (initialpgrp != getpgrp()) {
14798943Sluigi				killpg(0, SIGTTIN);
14898943Sluigi				continue;
14998943Sluigi			}
15098943Sluigi		} while (0);
15198943Sluigi		setsignal(SIGTSTP);
15298943Sluigi		setsignal(SIGTTOU);
15398943Sluigi		setsignal(SIGTTIN);
15498943Sluigi		setpgid(0, rootpid);
15598943Sluigi		tcsetpgrp(ttyfd, rootpid);
15698943Sluigi	} else { /* turning job control off */
15798943Sluigi		setpgid(0, initialpgrp);
15898943Sluigi		tcsetpgrp(ttyfd, initialpgrp);
15998943Sluigi		close(ttyfd);
16098943Sluigi		ttyfd = -1;
16198943Sluigi		setsignal(SIGTSTP);
16298943Sluigi		setsignal(SIGTTOU);
16398943Sluigi		setsignal(SIGTTIN);
16498943Sluigi	}
16598943Sluigi	jobctl = on;
16698943Sluigi}
16798943Sluigi#endif
16898943Sluigi
16998943Sluigi
17098943Sluigi#ifdef mkinit
17198943SluigiINCLUDE <sys/types.h>
17298943SluigiINCLUDE <stdlib.h>
17398943Sluigi
17498943SluigiSHELLPROC {
17598943Sluigi	backgndpid = -1;
17698943Sluigi#if JOBS
17798943Sluigi	jobctl = 0;
17898943Sluigi#endif
17998943Sluigi}
18098943Sluigi
18198943Sluigi#endif
18298943Sluigi
18398943Sluigi
18498943Sluigi
185172801Srpaulo#if JOBS
186172801Srpauloint
18798943Sluigifgcmd(int argc __unused, char **argv)
18898943Sluigi{
18998943Sluigi	struct job *jp;
19098943Sluigi	pid_t pgrp;
19198943Sluigi	int status;
19298943Sluigi
19398943Sluigi	jp = getjob(argv[1]);
19498943Sluigi	if (jp->jobctl == 0)
19598943Sluigi		error("job not created under job control");
19698943Sluigi	out1str(jp->ps[0].cmd);
19798943Sluigi	out1c('\n');
19898943Sluigi	flushout(&output);
19998943Sluigi	pgrp = jp->ps[0].pid;
20098943Sluigi	tcsetpgrp(ttyfd, pgrp);
20198943Sluigi	restartjob(jp);
20298943Sluigi	jp->foreground = 1;
20398943Sluigi	INTOFF;
20498943Sluigi	status = waitforjob(jp, (int *)NULL);
20598943Sluigi	INTON;
20698943Sluigi	return status;
20798943Sluigi}
20898943Sluigi
20998943Sluigi
21098943Sluigiint
21198943Sluigibgcmd(int argc, char **argv)
21298943Sluigi{
21398943Sluigi	char s[64];
21498943Sluigi	struct job *jp;
21598943Sluigi
21698943Sluigi	do {
21798943Sluigi		jp = getjob(*++argv);
21898943Sluigi		if (jp->jobctl == 0)
21998943Sluigi			error("job not created under job control");
22098943Sluigi		if (jp->state == JOBDONE)
22198943Sluigi			continue;
22298943Sluigi		restartjob(jp);
22398943Sluigi		jp->foreground = 0;
22498943Sluigi		fmtstr(s, 64, "[%d] ", jp - jobtab + 1);
22598943Sluigi		out1str(s);
22698943Sluigi		out1str(jp->ps[0].cmd);
22798943Sluigi		out1c('\n');
22898943Sluigi	} while (--argc > 1);
22998943Sluigi	return 0;
23098943Sluigi}
23198943Sluigi
23298943Sluigi
23398943SluigiSTATIC void
23498943Sluigirestartjob(struct job *jp)
23598943Sluigi{
23698943Sluigi	struct procstat *ps;
23798943Sluigi	int i;
23898943Sluigi
23998943Sluigi	if (jp->state == JOBDONE)
240101641Sluigi		return;
241101641Sluigi	setcurjob(jp);
24298943Sluigi	INTOFF;
24398943Sluigi	killpg(jp->ps[0].pid, SIGCONT);
24498943Sluigi	for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) {
24598943Sluigi		if (WIFSTOPPED(ps->status)) {
24698943Sluigi			ps->status = -1;
24798943Sluigi			jp->state = 0;
24898943Sluigi		}
249141351Sglebius	}
250141351Sglebius	INTON;
25198943Sluigi}
25298943Sluigi#endif
25398943Sluigi
25498943Sluigi
25598943Sluigiint
25698943Sluigijobscmd(int argc, char *argv[])
25798943Sluigi{
258165648Spiso	char *id;
25998943Sluigi	int ch, sformat, lformat;
260136071Sgreen
261136071Sgreen	optind = optreset = 1;
262158879Soleg	opterr = 0;
263158879Soleg	sformat = lformat = 0;
264136071Sgreen	while ((ch = getopt(argc, argv, "ls")) != -1) {
265158879Soleg		switch (ch) {
26698943Sluigi		case 'l':
26798943Sluigi			lformat = 1;
268133600Scsjp			break;
26998943Sluigi		case 's':
27098943Sluigi			sformat = 1;
27198943Sluigi			break;
27298943Sluigi		case '?':
27398943Sluigi		default:
274136073Sgreen			error("unknown option: -%c", optopt);
275136073Sgreen		}
276136073Sgreen	}
27798943Sluigi	argc -= optind;
27898943Sluigi	argv += optind;
27998943Sluigi
28098943Sluigi	if (argc == 0)
28198943Sluigi		showjobs(0, sformat, lformat);
28298943Sluigi	else
28398943Sluigi		while ((id = *argv++) != NULL)
28498943Sluigi			showjob(getjob(id), 0, sformat, lformat);
28598943Sluigi
28698943Sluigi	return (0);
28798943Sluigi}
28898943Sluigi
28998943SluigiSTATIC void
290136075Sgreenshowjob(struct job *jp, pid_t pid, int sformat, int lformat)
29198943Sluigi{
29298943Sluigi	char s[64];
29398943Sluigi	struct procstat *ps;
29498943Sluigi	struct job *j;
29598943Sluigi	int col, curr, i, jobno, prev, procno;
29698943Sluigi	char c;
297102087Sluigi
298102087Sluigi	procno = jp->nprocs;
299112250Scjc	jobno = jp - jobtab + 1;
300128575Sandre	curr = prev = 0;
301133387Sandre#if JOBS
302117241Sluigi	if ((j = getcurjob(NULL)) != NULL) {
303117469Sluigi		curr = j - jobtab + 1;
30498943Sluigi		if ((j = getcurjob(j)) != NULL)
30598943Sluigi			prev = j - jobtab + 1;
306101978Sluigi	}
30798943Sluigi#endif
30898943Sluigi	for (ps = jp->ps ; ; ps++) {	/* for each process */
30998943Sluigi		if (sformat) {
31098943Sluigi			out1fmt("%d\n", (int)ps->pid);
31198943Sluigi			goto skip;
31298943Sluigi		}
31398943Sluigi		if (!lformat && ps != jp->ps && pid == 0)
31498943Sluigi			goto skip;
31598943Sluigi		if (pid != 0 && pid != ps->pid)
31698943Sluigi			goto skip;
31798943Sluigi		if (jobno == curr && ps == jp->ps)
31898943Sluigi			c = '+';
31998943Sluigi		else if (jobno == prev && ps == jp->ps)
32098943Sluigi			c = '-';
321165648Spiso		else
322165648Spiso			c = ' ';
323165648Spiso		if (ps == jp->ps)
324165648Spiso			fmtstr(s, 64, "[%d] %c ", jobno, c);
325165648Spiso		else
326165648Spiso			fmtstr(s, 64, "    %c ", c);
327165648Spiso		out1str(s);
328165648Spiso		col = strlen(s);
329165648Spiso		if (lformat) {
330165648Spiso			fmtstr(s, 64, "%d ", (int)ps->pid);
331165648Spiso			out1str(s);
332165648Spiso			col += strlen(s);
333145246Sbrooks		}
334145246Sbrooks		s[0] = '\0';
335145246Sbrooks		if (ps != jp->ps) {
336145246Sbrooks			*s = '\0';
337145246Sbrooks		} else if (ps->status == -1) {
338145246Sbrooks			strcpy(s, "Running");
339145246Sbrooks		} else if (WIFEXITED(ps->status)) {
340146894Smlaier			if (WEXITSTATUS(ps->status) == 0)
341146894Smlaier				strcpy(s, "Done");
342149020Sbz			else
343149020Sbz				fmtstr(s, 64, "Done (%d)",
344178888Sjulian				    WEXITSTATUS(ps->status));
345178888Sjulian		} else {
346178888Sjulian#if JOBS
34798943Sluigi			if (WIFSTOPPED(ps->status))
34898943Sluigi				i = WSTOPSIG(ps->status);
34998943Sluigi			else
35098943Sluigi#endif
351101978Sluigi				i = WTERMSIG(ps->status);
35298943Sluigi			if ((i & 0x7F) < NSIG && sys_siglist[i & 0x7F])
35398943Sluigi				scopy(sys_siglist[i & 0x7F], s);
35498943Sluigi			else
35598943Sluigi				fmtstr(s, 64, "Signal %d", i & 0x7F);
35698943Sluigi			if (WCOREDUMP(ps->status))
35798943Sluigi				strcat(s, " (core dumped)");
35898943Sluigi		}
35998943Sluigi		out1str(s);
36098943Sluigi		col += strlen(s);
36198943Sluigi		do {
36298943Sluigi			out1c(' ');
36398943Sluigi			col++;
36498943Sluigi		} while (col < 30);
36598943Sluigi		out1str(ps->cmd);
36698943Sluigi		out1c('\n');
36799475Sluigiskip:		if (--procno <= 0)
36898943Sluigi			break;
369145246Sbrooks	}
370145246Sbrooks}
371145246Sbrooks
372145246Sbrooks/*
373145246Sbrooks * Print a list of jobs.  If "change" is nonzero, only print jobs whose
37498943Sluigi * statuses have changed since the last call to showjobs.
375117328Sluigi *
37698943Sluigi * If the shell is interrupted in the process of creating a job, the
37798943Sluigi * result may be a job structure containing zero processes.  Such structures
378165648Spiso * will be freed here.
379165648Spiso */
380165648Spiso
381165648Spisovoid
382165648Spisoshowjobs(int change, int sformat, int lformat)
383165648Spiso{
384165648Spiso	int jobno;
385165648Spiso	struct job *jp;
386165648Spiso
387165648Spiso	TRACE(("showjobs(%d) called\n", change));
388165648Spiso	while (dowait(0, (struct job *)NULL) > 0);
389165648Spiso	for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) {
390165648Spiso		if (! jp->used)
391165648Spiso			continue;
392165648Spiso		if (jp->nprocs == 0) {
393165648Spiso			freejob(jp);
39498943Sluigi			continue;
39598943Sluigi		}
39698943Sluigi		if (change && ! jp->changed)
39798943Sluigi			continue;
39898943Sluigi		showjob(jp, 0, sformat, lformat);
39998943Sluigi		jp->changed = 0;
40098943Sluigi		if (jp->state == JOBDONE) {
40198943Sluigi			freejob(jp);
40298943Sluigi		}
40398943Sluigi	}
404141351Sglebius}
405141351Sglebius
40698943Sluigi
40798943Sluigi/*
40898943Sluigi * Mark a job structure as unused.
40998943Sluigi */
41098943Sluigi
41198943SluigiSTATIC void
412149020Sbzfreejob(struct job *jp)
41398943Sluigi{
414149020Sbz	struct procstat *ps;
41599475Sluigi	int i;
41698943Sluigi
417117469Sluigi	INTOFF;
418165648Spiso	for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) {
419178888Sjulian		if (ps->cmd != nullstr)
420117328Sluigi			ckfree(ps->cmd);
42198943Sluigi	}
42298943Sluigi	if (jp->ps != &jp->ps0)
423136071Sgreen		ckfree(jp->ps);
424136071Sgreen	jp->used = 0;
425136071Sgreen#if JOBS
426158879Soleg	deljob(jp);
427158879Soleg#endif
428136071Sgreen	INTON;
429136071Sgreen}
430136071Sgreen
43198943Sluigi
432158879Soleg
43398943Sluigiint
43498943Sluigiwaitcmd(int argc, char **argv)
435133600Scsjp{
43698943Sluigi	struct job *job;
43798943Sluigi	int status, retval;
43898943Sluigi	struct job *jp;
43998943Sluigi
44098943Sluigi	if (argc > 1) {
44198943Sluigi		job = getjob(argv[1]);
442136073Sgreen	} else {
443136073Sgreen		job = NULL;
444136073Sgreen	}
44598943Sluigi
44698943Sluigi	/*
44798943Sluigi	 * Loop until a process is terminated or stopped, or a SIGINT is
44898943Sluigi	 * received.
44998943Sluigi	 */
450178888Sjulian
45198943Sluigi	in_waitcmd++;
45298943Sluigi	do {
45398943Sluigi		if (job != NULL) {
45498943Sluigi			if (job->state) {
45598943Sluigi				status = job->ps[job->nprocs - 1].status;
45698943Sluigi				if (WIFEXITED(status))
45798943Sluigi					retval = WEXITSTATUS(status);
45898943Sluigi#if JOBS
45998943Sluigi				else if (WIFSTOPPED(status))
46098943Sluigi					retval = WSTOPSIG(status) + 128;
46198943Sluigi#endif
46298943Sluigi				else
463136075Sgreen					retval = WTERMSIG(status) + 128;
46498943Sluigi				if (! iflag)
46598943Sluigi					freejob(job);
46698943Sluigi				in_waitcmd--;
46798943Sluigi				return retval;
46898943Sluigi			}
46998943Sluigi		} else {
47098943Sluigi			for (jp = jobtab ; ; jp++) {
47199909Sluigi				if (jp >= jobtab + njobs) {	/* no running procs */
47298943Sluigi					in_waitcmd--;
473102087Sluigi					return 0;
474102087Sluigi				}
475102087Sluigi				if (jp->used && jp->state == 0)
476102087Sluigi					break;
477102087Sluigi			}
478102087Sluigi		}
479102087Sluigi	} while (dowait(1, (struct job *)NULL) != -1);
480102087Sluigi	in_waitcmd--;
481112250Scjc
482128575Sandre	return 0;
483133387Sandre}
484117241Sluigi
485145246Sbrooks
486145246Sbrooks
487145246Sbrooksint
488145246Sbrooksjobidcmd(int argc __unused, char **argv)
489145246Sbrooks{
490145246Sbrooks	struct job *jp;
491146894Smlaier	int i;
492146894Smlaier
493145246Sbrooks	jp = getjob(argv[1]);
494145246Sbrooks	for (i = 0 ; i < jp->nprocs ; ) {
495145246Sbrooks		out1fmt("%d", (int)jp->ps[i].pid);
496145246Sbrooks		out1c(++i < jp->nprocs? ' ' : '\n');
497117469Sluigi	}
49898943Sluigi	return 0;
49998943Sluigi}
50098943Sluigi
50198943Sluigi
50298943Sluigi
503101641Sluigi/*
504101641Sluigi * Convert a job name to a job structure.
505101641Sluigi */
506101641Sluigi
507117328SluigiSTATIC struct job *
50898943Sluigigetjob(char *name)
50998943Sluigi{
510153374Sglebius	int jobno;
511153374Sglebius	struct job *found, *jp;
512117328Sluigi	pid_t pid;
513117328Sluigi	int i;
514117328Sluigi
515115793Sticso	if (name == NULL) {
516115793Sticso#if JOBS
517115793Sticsocurrentjob:	if ((jp = getcurjob(NULL)) == NULL)
518129389Sstefanf			error("No current job");
519115793Sticso		return (jp);
520117328Sluigi#else
521117328Sluigi		error("No current job");
522117328Sluigi#endif
523117469Sluigi	} else if (name[0] == '%') {
524119740Stmm		if (is_digit(name[1])) {
525117328Sluigi			jobno = number(name + 1);
526117328Sluigi			if (jobno > 0 && jobno <= njobs
527117328Sluigi			 && jobtab[jobno - 1].used != 0)
528117577Sluigi				return &jobtab[jobno - 1];
529117328Sluigi#if JOBS
530117328Sluigi		} else if (name[1] == '%' && name[2] == '\0') {
531117328Sluigi			goto currentjob;
532117328Sluigi		} else if (name[1] == '+' && name[2] == '\0') {
533117328Sluigi			goto currentjob;
534117328Sluigi		} else if (name[1] == '-' && name[2] == '\0') {
535117328Sluigi			if ((jp = getcurjob(NULL)) == NULL ||
536117328Sluigi			    (jp = getcurjob(jp)) == NULL)
537117328Sluigi				error("No previous job");
538130281Sru			return (jp);
539165648Spiso#endif
540165648Spiso		} else if (name[1] == '?') {
541165648Spiso			found = NULL;
542117328Sluigi			for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
543117328Sluigi				if (jp->used && jp->nprocs > 0
544117328Sluigi				 && strstr(jp->ps[0].cmd, name + 2) != NULL) {
545117328Sluigi					if (found)
546117328Sluigi						error("%s: ambiguous", name);
547117328Sluigi					found = jp;
548117328Sluigi				}
54998943Sluigi			}
55098943Sluigi			if (found != NULL)
551117328Sluigi				return (found);
55298943Sluigi		} else {
55398943Sluigi			found = NULL;
55498943Sluigi			for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
55598943Sluigi				if (jp->used && jp->nprocs > 0
55698943Sluigi				 && prefix(name + 1, jp->ps[0].cmd)) {
557117469Sluigi					if (found)
55898943Sluigi						error("%s: ambiguous", name);
55998943Sluigi					found = jp;
56098943Sluigi				}
56198943Sluigi			}
56298943Sluigi			if (found)
563129389Sstefanf				return found;
56498943Sluigi		}
565117328Sluigi	} else if (is_number(name)) {
566117328Sluigi		pid = (pid_t)number(name);
567117328Sluigi		for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
568117328Sluigi			if (jp->used && jp->nprocs > 0
569117469Sluigi			 && jp->ps[jp->nprocs - 1].pid == pid)
570117469Sluigi				return jp;
57198943Sluigi		}
57298943Sluigi	}
57398943Sluigi	error("No such job: %s", name);
57498943Sluigi	/*NOTREACHED*/
57598943Sluigi	return NULL;
57698943Sluigi}
57798943Sluigi
57898943Sluigi
579140271Sbrooks
580140271Sbrooks/*
581140271Sbrooks * Return a new job structure,
582140271Sbrooks */
583140271Sbrooks
584140271Sbrooksstruct job *
585140271Sbrooksmakejob(union node *node __unused, int nprocs)
586140271Sbrooks{
587140271Sbrooks	int i;
588140271Sbrooks	struct job *jp;
589140271Sbrooks
590140271Sbrooks	for (i = njobs, jp = jobtab ; ; jp++) {
591140271Sbrooks		if (--i < 0) {
592140271Sbrooks			INTOFF;
593140271Sbrooks			if (njobs == 0) {
594140271Sbrooks				jobtab = ckmalloc(4 * sizeof jobtab[0]);
595140271Sbrooks#if JOBS
596140271Sbrooks				jobmru = NULL;
597140271Sbrooks#endif
598140271Sbrooks			} else {
599140271Sbrooks				jp = ckmalloc((njobs + 4) * sizeof jobtab[0]);
600140271Sbrooks				memcpy(jp, jobtab, njobs * sizeof jp[0]);
601140271Sbrooks#if JOBS
602140271Sbrooks				/* Relocate `next' pointers and list head */
603140271Sbrooks				if (jobmru != NULL)
604140271Sbrooks					jobmru = &jp[jobmru - jobtab];
605140271Sbrooks				for (i = 0; i < njobs; i++)
606140271Sbrooks					if (jp[i].next != NULL)
607140271Sbrooks						jp[i].next = &jp[jp[i].next -
608140271Sbrooks						    jobtab];
609140271Sbrooks#endif
610140271Sbrooks				/* Relocate `ps' pointers */
611140271Sbrooks				for (i = 0; i < njobs; i++)
612140271Sbrooks					if (jp[i].ps == &jobtab[i].ps0)
613140271Sbrooks						jp[i].ps = &jp[i].ps0;
614140271Sbrooks				ckfree(jobtab);
615140271Sbrooks				jobtab = jp;
616140271Sbrooks			}
617140271Sbrooks			jp = jobtab + njobs;
618140271Sbrooks			for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0);
619140271Sbrooks			INTON;
620140271Sbrooks			break;
621140271Sbrooks		}
622140271Sbrooks		if (jp->used == 0)
623140271Sbrooks			break;
624140271Sbrooks	}
625140271Sbrooks	INTOFF;
626140271Sbrooks	jp->state = 0;
627140271Sbrooks	jp->used = 1;
628140271Sbrooks	jp->changed = 0;
62998943Sluigi	jp->nprocs = 0;
63098943Sluigi	jp->foreground = 0;
63198943Sluigi#if JOBS
632117328Sluigi	jp->jobctl = jobctl;
63398943Sluigi	jp->next = NULL;
63498943Sluigi#endif
63598943Sluigi	if (nprocs > 1) {
636117469Sluigi		jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
63798943Sluigi	} else {
63898943Sluigi		jp->ps = &jp->ps0;
63998943Sluigi	}
64098943Sluigi	INTON;
64198943Sluigi	TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
64298943Sluigi	    jp - jobtab + 1));
64398943Sluigi	return jp;
64498943Sluigi}
64598943Sluigi
64698943Sluigi#if JOBS
64798943SluigiSTATIC void
64898943Sluigisetcurjob(struct job *cj)
64998943Sluigi{
65098943Sluigi	struct job *jp, *prev;
65198943Sluigi
65298943Sluigi	for (prev = NULL, jp = jobmru; jp != NULL; prev = jp, jp = jp->next) {
65398943Sluigi		if (jp == cj) {
65498943Sluigi			if (prev != NULL)
65598943Sluigi				prev->next = jp->next;
656117328Sluigi			else
657117328Sluigi				jobmru = jp->next;
658117328Sluigi			jp->next = jobmru;
659117328Sluigi			jobmru = cj;
660117328Sluigi			return;
661117328Sluigi		}
662117328Sluigi	}
663136075Sgreen	cj->next = jobmru;
664158879Soleg	jobmru = cj;
665117328Sluigi}
666117328Sluigi
667117328SluigiSTATIC void
66898943Sluigideljob(struct job *j)
669117328Sluigi{
67098943Sluigi	struct job *jp, *prev;
67198943Sluigi
67298943Sluigi	for (prev = NULL, jp = jobmru; jp != NULL; prev = jp, jp = jp->next) {
673102087Sluigi		if (jp == j) {
67498943Sluigi			if (prev != NULL)
675117328Sluigi				prev->next = jp->next;
67698943Sluigi			else
677117469Sluigi				jobmru = jp->next;
67898943Sluigi			return;
679116690Sluigi		}
680117328Sluigi	}
681117328Sluigi}
682116690Sluigi
683116690Sluigi/*
684116690Sluigi * Return the most recently used job that isn't `nj', and preferably one
685116690Sluigi * that is stopped.
68698943Sluigi */
68798943SluigiSTATIC struct job *
68898943Sluigigetcurjob(struct job *nj)
68998943Sluigi{
69098943Sluigi	struct job *jp;
69198943Sluigi
69298943Sluigi	/* Try to find a stopped one.. */
69398943Sluigi	for (jp = jobmru; jp != NULL; jp = jp->next)
69498943Sluigi		if (jp->used && jp != nj && jp->state == JOBSTOPPED)
69598943Sluigi			return (jp);
69698943Sluigi	/* Otherwise the most recently used job that isn't `nj' */
69798943Sluigi	for (jp = jobmru; jp != NULL; jp = jp->next)
69898943Sluigi		if (jp->used && jp != nj)
69998943Sluigi			return (jp);
70098943Sluigi
70198943Sluigi	return (NULL);
70298943Sluigi}
70398943Sluigi
704101628Sluigi#endif
70598943Sluigi
70698943Sluigi/*
70798943Sluigi * Fork of a subshell.  If we are doing job control, give the subshell its
70898943Sluigi * own process group.  Jp is a job structure that the job is to be added to.
709101628Sluigi * N is the command that will be evaluated by the child.  Both jp and n may
710101628Sluigi * be NULL.  The mode parameter can be one of the following:
71198943Sluigi *	FORK_FG - Fork off a foreground process.
71298943Sluigi *	FORK_BG - Fork off a background process.
713101628Sluigi *	FORK_NOJOB - Like FORK_FG, but don't give the process its own
714117577Sluigi *		     process group even if job control is on.
715101628Sluigi *
716106505Smaxim * When job control is turned off, background processes have their standard
71798943Sluigi * input redirected to /dev/null (except for the second and later processes
71898943Sluigi * in a pipeline).
71998943Sluigi */
72098943Sluigi
721101628Sluigipid_t
72298943Sluigiforkshell(struct job *jp, union node *n, int mode)
723101628Sluigi{
724101628Sluigi	pid_t pid;
725101628Sluigi	pid_t pgrp;
72698943Sluigi
727101628Sluigi	TRACE(("forkshell(%%%d, 0x%lx, %d) called\n", jp - jobtab, (long)n,
728101628Sluigi	    mode));
729101628Sluigi	INTOFF;
730101628Sluigi	pid = fork();
731101628Sluigi	if (pid == -1) {
732101628Sluigi		TRACE(("Fork failed, errno=%d\n", errno));
733101628Sluigi		INTON;
734101628Sluigi		error("Cannot fork: %s", strerror(errno));
735117577Sluigi	}
736101628Sluigi	if (pid == 0) {
737101628Sluigi		struct job *p;
738101628Sluigi		int wasroot;
73998943Sluigi		int i;
740101628Sluigi
741101628Sluigi		TRACE(("Child shell %d\n", (int)getpid()));
742101628Sluigi		wasroot = rootshell;
74398943Sluigi		rootshell = 0;
74498943Sluigi		closescript();
74598943Sluigi		INTON;
74698943Sluigi		clear_traps();
74798943Sluigi#if JOBS
74898943Sluigi		jobctl = 0;		/* do job control only in root shell */
74998943Sluigi		if (wasroot && mode != FORK_NOJOB && mflag) {
75098943Sluigi			if (jp == NULL || jp->nprocs == 0)
75198943Sluigi				pgrp = getpid();
75298943Sluigi			else
753101628Sluigi				pgrp = jp->ps[0].pid;
754101628Sluigi			if (setpgid(0, pgrp) == 0 && mode == FORK_FG) {
75598943Sluigi				/*** this causes superfluous TIOCSPGRPS ***/
75698943Sluigi				if (tcsetpgrp(ttyfd, pgrp) < 0)
75798943Sluigi					error("tcsetpgrp failed, errno=%d", errno);
75898943Sluigi			}
75998943Sluigi			setsignal(SIGTSTP);
760101628Sluigi			setsignal(SIGTTOU);
76198943Sluigi		} else if (mode == FORK_BG) {
76298943Sluigi			ignoresig(SIGINT);
76398943Sluigi			ignoresig(SIGQUIT);
764136071Sgreen			if ((jp == NULL || jp->nprocs == 0) &&
765136071Sgreen			    ! fd0_redirected_p ()) {
766136071Sgreen				close(0);
767136071Sgreen				if (open(_PATH_DEVNULL, O_RDONLY) != 0)
768136071Sgreen					error("Can't open %s: %s",
769136071Sgreen					    _PATH_DEVNULL, strerror(errno));
770136071Sgreen			}
771136071Sgreen		}
772136071Sgreen#else
773136071Sgreen		if (mode == FORK_BG) {
774136071Sgreen			ignoresig(SIGINT);
775136071Sgreen			ignoresig(SIGQUIT);
776136071Sgreen			if ((jp == NULL || jp->nprocs == 0) &&
777136071Sgreen			    ! fd0_redirected_p ()) {
778136071Sgreen				close(0);
779136071Sgreen				if (open(_PATH_DEVNULL, O_RDONLY) != 0)
780136071Sgreen					error("Can't open %s: %s",
781136071Sgreen					    _PATH_DEVNULL, strerror(errno));
782136071Sgreen			}
783136071Sgreen		}
784136071Sgreen#endif
785136071Sgreen		INTOFF;
786136071Sgreen		for (i = njobs, p = jobtab ; --i >= 0 ; p++)
787136071Sgreen			if (p->used)
788136071Sgreen				freejob(p);
789136071Sgreen		INTON;
790136071Sgreen		if (wasroot && iflag) {
791136071Sgreen			setsignal(SIGINT);
792136071Sgreen			setsignal(SIGQUIT);
793136071Sgreen			setsignal(SIGTERM);
794136071Sgreen		}
795136071Sgreen		return pid;
796136071Sgreen	}
797136071Sgreen	if (rootshell && mode != FORK_NOJOB && mflag) {
798136071Sgreen		if (jp == NULL || jp->nprocs == 0)
799136071Sgreen			pgrp = pid;
800136071Sgreen		else
801136071Sgreen			pgrp = jp->ps[0].pid;
802136071Sgreen		setpgid(pid, pgrp);
803136071Sgreen	}
804136071Sgreen	if (mode == FORK_BG)
805136071Sgreen		backgndpid = pid;		/* set $! */
806136071Sgreen	if (jp) {
807136071Sgreen		struct procstat *ps = &jp->ps[jp->nprocs++];
808136071Sgreen		ps->pid = pid;
809136071Sgreen		ps->status = -1;
810136071Sgreen		ps->cmd = nullstr;
811136071Sgreen		if (iflag && rootshell && n)
812136071Sgreen			ps->cmd = commandtext(n);
813136071Sgreen		jp->foreground = mode == FORK_FG;
814136071Sgreen#if JOBS
815136071Sgreen		setcurjob(jp);
816136071Sgreen#endif
817136071Sgreen	}
818136071Sgreen	INTON;
819136071Sgreen	TRACE(("In parent shell:  child = %d\n", (int)pid));
820136071Sgreen	return pid;
821136071Sgreen}
822136071Sgreen
823136071Sgreen
824136071Sgreen
825136071Sgreen/*
826136071Sgreen * Wait for job to finish.
827136071Sgreen *
828136071Sgreen * Under job control we have the problem that while a child process is
829136071Sgreen * running interrupts generated by the user are sent to the child but not
830136071Sgreen * to the shell.  This means that an infinite loop started by an inter-
831136071Sgreen * active user may be hard to kill.  With job control turned off, an
832136071Sgreen * interactive user may place an interactive program inside a loop.  If
833136071Sgreen * the interactive program catches interrupts, the user doesn't want
834136071Sgreen * these interrupts to also abort the loop.  The approach we take here
835136071Sgreen * is to have the shell ignore interrupt signals while waiting for a
836136071Sgreen * foreground process to terminate, and then send itself an interrupt
837136071Sgreen * signal if the child process was terminated by an interrupt signal.
838136071Sgreen * Unfortunately, some programs want to do a bit of cleanup and then
839136071Sgreen * exit on interrupt; unless these processes terminate themselves by
840136071Sgreen * sending a signal to themselves (instead of calling exit) they will
841136071Sgreen * confuse this approach.
842136071Sgreen */
843136071Sgreen
844136071Sgreenint
845136071Sgreenwaitforjob(struct job *jp, int *origstatus)
846136071Sgreen{
847136071Sgreen#if JOBS
848136071Sgreen	pid_t mypgrp = getpgrp();
849136071Sgreen#endif
850136071Sgreen	int status;
851136071Sgreen	int st;
852136071Sgreen
853136071Sgreen	INTOFF;
854136071Sgreen	TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1));
855136071Sgreen	while (jp->state == 0)
856136071Sgreen		if (dowait(1, jp) == -1)
857136071Sgreen			dotrap();
858136071Sgreen#if JOBS
859136071Sgreen	if (jp->jobctl) {
860136071Sgreen		if (tcsetpgrp(ttyfd, mypgrp) < 0)
861136071Sgreen			error("tcsetpgrp failed, errno=%d\n", errno);
862136071Sgreen	}
863136071Sgreen	if (jp->state == JOBSTOPPED)
864136071Sgreen		setcurjob(jp);
865117328Sluigi#endif
86698943Sluigi	status = jp->ps[jp->nprocs - 1].status;
86798943Sluigi	if (origstatus != NULL)
86898943Sluigi		*origstatus = status;
86998943Sluigi	/* convert to 8 bits */
870117328Sluigi	if (WIFEXITED(status))
87198943Sluigi		st = WEXITSTATUS(status);
872102087Sluigi#if JOBS
87398943Sluigi	else if (WIFSTOPPED(status))
874102087Sluigi		st = WSTOPSIG(status) + 128;
87598943Sluigi#endif
876159636Soleg	else
877159636Soleg		st = WTERMSIG(status) + 128;
878159636Soleg	if (! JOBS || jp->state == JOBDONE)
879159636Soleg		freejob(jp);
880159636Soleg	if (int_pending()) {
881159636Soleg		if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT)
88298943Sluigi			kill(getpid(), SIGINT);
883159636Soleg		else
884159636Soleg			CLEAR_PENDING_INT;
885159636Soleg	}
88698943Sluigi	INTON;
88798943Sluigi	return st;
888159636Soleg}
889159636Soleg
890159636Soleg
89198943Sluigi
892159636Soleg/*
893159636Soleg * Wait for a process to terminate.
894159636Soleg */
895101978Sluigi
896159636SolegSTATIC pid_t
897159636Solegdowait(int block, struct job *job)
898159636Soleg{
899102087Sluigi	pid_t pid;
900102087Sluigi	int status;
901159636Soleg	struct procstat *sp;
90298943Sluigi	struct job *jp;
90398943Sluigi	struct job *thisjob;
904159636Soleg	int done;
905102087Sluigi	int stopped;
906159636Soleg	int sig;
90798943Sluigi	int i;
908159636Soleg
90998943Sluigi	in_dowait++;
91098943Sluigi	TRACE(("dowait(%d) called\n", block));
91198943Sluigi	do {
91298943Sluigi		pid = waitproc(block, &status);
91398943Sluigi		TRACE(("wait returns %d, status=%d\n", (int)pid, status));
91498943Sluigi	} while ((pid == -1 && errno == EINTR && breakwaitcmd == 0) ||
91598943Sluigi	    (WIFSTOPPED(status) && !iflag));
91698943Sluigi	in_dowait--;
91798943Sluigi	if (breakwaitcmd != 0) {
91898943Sluigi		breakwaitcmd = 0;
91998943Sluigi		return -1;
92098943Sluigi	}
92198943Sluigi	if (pid <= 0)
92298943Sluigi		return pid;
92398943Sluigi	INTOFF;
92498943Sluigi	thisjob = NULL;
92598943Sluigi	for (jp = jobtab ; jp < jobtab + njobs ; jp++) {
92698943Sluigi		if (jp->used) {
92798943Sluigi			done = 1;
92898943Sluigi			stopped = 1;
92998943Sluigi			for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) {
93098943Sluigi				if (sp->pid == -1)
93198943Sluigi					continue;
93298943Sluigi				if (sp->pid == pid) {
93398943Sluigi					TRACE(("Changing status of proc %d from 0x%x to 0x%x\n",
93498943Sluigi						   (int)pid, sp->status,
93598943Sluigi						   status));
93698943Sluigi					sp->status = status;
93798943Sluigi					thisjob = jp;
93898943Sluigi				}
93998943Sluigi				if (sp->status == -1)
940102087Sluigi					stopped = 0;
94198943Sluigi				else if (WIFSTOPPED(sp->status))
94298943Sluigi					done = 0;
94398943Sluigi			}
94498943Sluigi			if (stopped) {		/* stopped or done */
94598943Sluigi				int state = done? JOBDONE : JOBSTOPPED;
94698943Sluigi				if (jp->state != state) {
947117328Sluigi					TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state));
94898943Sluigi					jp->state = state;
949117469Sluigi#if JOBS
95098943Sluigi					if (done)
95198943Sluigi						deljob(jp);
95299475Sluigi#endif
95398943Sluigi				}
95499475Sluigi			}
95598943Sluigi		}
95698943Sluigi	}
957149020Sbz	INTON;
958149020Sbz	if (! rootshell || ! iflag || (job && thisjob == job)) {
959149020Sbz#if JOBS
960149020Sbz		if (WIFSTOPPED(status))
961149020Sbz			sig = WSTOPSIG(status);
962149020Sbz		else
963149020Sbz#endif
964149020Sbz		{
965149020Sbz			if (WIFEXITED(status))
966149020Sbz				sig = 0;
967149020Sbz			else
968149020Sbz				sig = WTERMSIG(status);
969149020Sbz		}
970149020Sbz		if (sig != 0 && sig != SIGINT && sig != SIGPIPE) {
971149020Sbz			if (thisjob->foreground && !WIFSTOPPED(status)) {
972149020Sbz				i = WTERMSIG(status);
973149020Sbz				if ((i & 0x7F) < NSIG && sys_siglist[i & 0x7F])
974149020Sbz					out1str(sys_siglist[i & 0x7F]);
975149020Sbz				else
976149020Sbz					out1fmt("Signal %d", i & 0x7F);
977149020Sbz				if (WCOREDUMP(status))
978149020Sbz					out1str(" (core dumped)");
979149020Sbz				out1c('\n');
980149020Sbz			} else
981149020Sbz				showjob(thisjob, pid, 0, 0);
982149020Sbz		}
983149020Sbz	} else {
984149020Sbz		TRACE(("Not printing status, rootshell=%d, job=0x%x\n", rootshell, job));
985149020Sbz		if (thisjob)
986149020Sbz			thisjob->changed = 1;
987149020Sbz	}
988149020Sbz	return pid;
989149020Sbz}
990149020Sbz
99198943Sluigi
99298943Sluigi
99398943Sluigi/*
99498943Sluigi * Do a wait system call.  If job control is compiled in, we accept
99598943Sluigi * stopped processes.  If block is zero, we return a value of zero
99698943Sluigi * rather than blocking.
99798943Sluigi */
99898943SluigiSTATIC pid_t
99998943Sluigiwaitproc(int block, int *status)
100098943Sluigi{
100198943Sluigi	int flags;
100298943Sluigi
1003117577Sluigi#if JOBS
100498943Sluigi	flags = WUNTRACED;
100598943Sluigi#else
1006117577Sluigi	flags = 0;
100798943Sluigi#endif
100898943Sluigi	if (block == 0)
100998943Sluigi		flags |= WNOHANG;
101098943Sluigi	return wait3(status, flags, (struct rusage *)NULL);
101198943Sluigi}
101298943Sluigi
101398943Sluigi/*
101498943Sluigi * return 1 if there are stopped jobs, otherwise 0
101598943Sluigi */
101698943Sluigiint job_warning = 0;
101798943Sluigiint
101898943Sluigistoppedjobs(void)
101998943Sluigi{
102098943Sluigi	int jobno;
1021117469Sluigi	struct job *jp;
102298943Sluigi
1023117469Sluigi	if (job_warning)
102498943Sluigi		return (0);
1025117577Sluigi	for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) {
1026117577Sluigi		if (jp->used == 0)
102798943Sluigi			continue;
102898943Sluigi		if (jp->state == JOBSTOPPED) {
102998943Sluigi			out2str("You have stopped jobs.\n");
103098943Sluigi			job_warning = 2;
103198943Sluigi			return (1);
103298943Sluigi		}
103398943Sluigi	}
103498943Sluigi
103598943Sluigi	return (0);
103698943Sluigi}
103798943Sluigi
103898943Sluigi/*
103998943Sluigi * Return a string identifying a command (to be printed by the
104098943Sluigi * jobs command.
104198943Sluigi */
104298943Sluigi
104398943SluigiSTATIC char *cmdnextc;
104498943SluigiSTATIC int cmdnleft;
104598943Sluigi#define MAXCMDTEXT	200
104698943Sluigi
104798943Sluigichar *
104898943Sluigicommandtext(union node *n)
104998943Sluigi{
105098943Sluigi	char *name;
105198943Sluigi
1052117469Sluigi	cmdnextc = name = ckmalloc(MAXCMDTEXT);
105398943Sluigi	cmdnleft = MAXCMDTEXT - 4;
105498943Sluigi	cmdtxt(n);
1055117328Sluigi	*cmdnextc = '\0';
1056117328Sluigi	return name;
105798943Sluigi}
1058102087Sluigi
105998943Sluigi
106098943SluigiSTATIC void
106198943Sluigicmdtxt(union node *n)
106298943Sluigi{
106398943Sluigi	union node *np;
1064130281Sru	struct nodelist *lp;
1065130281Sru	char *p;
1066130281Sru	int i;
1067130281Sru	char s[2];
1068130281Sru
1069130281Sru	if (n == NULL)
1070130281Sru		return;
1071130281Sru	switch (n->type) {
107298943Sluigi	case NSEMI:
1073117328Sluigi		cmdtxt(n->nbinary.ch1);
1074116716Sluigi		cmdputs("; ");
107598943Sluigi		cmdtxt(n->nbinary.ch2);
107698943Sluigi		break;
107798943Sluigi	case NAND:
107898943Sluigi		cmdtxt(n->nbinary.ch1);
107998943Sluigi		cmdputs(" && ");
108098943Sluigi		cmdtxt(n->nbinary.ch2);
1081117577Sluigi		break;
108298943Sluigi	case NOR:
108398943Sluigi		cmdtxt(n->nbinary.ch1);
1084116716Sluigi		cmdputs(" || ");
1085116716Sluigi		cmdtxt(n->nbinary.ch2);
1086116716Sluigi		break;
1087116716Sluigi	case NPIPE:
1088116716Sluigi		for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
1089116716Sluigi			cmdtxt(lp->n);
109098943Sluigi			if (lp->next)
1091117328Sluigi				cmdputs(" | ");
1092116716Sluigi		}
1093117328Sluigi		break;
1094116716Sluigi	case NSUBSHELL:
109598943Sluigi		cmdputs("(");
1096116716Sluigi		cmdtxt(n->nredir.n);
1097116716Sluigi		cmdputs(")");
1098116716Sluigi		break;
1099116716Sluigi	case NREDIR:
110098943Sluigi	case NBACKGND:
110198943Sluigi		cmdtxt(n->nredir.n);
110298943Sluigi		break;
110398943Sluigi	case NIF:
110498943Sluigi		cmdputs("if ");
1105117328Sluigi		cmdtxt(n->nif.test);
1106117328Sluigi		cmdputs("; then ");
1107117328Sluigi		cmdtxt(n->nif.ifpart);
1108117328Sluigi		cmdputs("...");
1109117328Sluigi		break;
1110117328Sluigi	case NWHILE:
1111117328Sluigi		cmdputs("while ");
1112117328Sluigi		goto until;
1113117577Sluigi	case NUNTIL:
111498943Sluigi		cmdputs("until ");
1115117328Sluigiuntil:
111698943Sluigi		cmdtxt(n->nbinary.ch1);
111798943Sluigi		cmdputs("; do ");
111898943Sluigi		cmdtxt(n->nbinary.ch2);
111998943Sluigi		cmdputs("; done");
112098943Sluigi		break;
1121117328Sluigi	case NFOR:
112298943Sluigi		cmdputs("for ");
1123117328Sluigi		cmdputs(n->nfor.var);
112498943Sluigi		cmdputs(" in ...");
112598943Sluigi		break;
112698943Sluigi	case NCASE:
1127117328Sluigi		cmdputs("case ");
1128117328Sluigi		cmdputs(n->ncase.expr->narg.text);
1129117328Sluigi		cmdputs(" in ...");
113098943Sluigi		break;
113198943Sluigi	case NDEFUN:
113298943Sluigi		cmdputs(n->narg.text);
113398943Sluigi		cmdputs("() ...");
113498943Sluigi		break;
113598943Sluigi	case NCMD:
1136117577Sluigi		for (np = n->ncmd.args ; np ; np = np->narg.next) {
113798943Sluigi			cmdtxt(np);
113898943Sluigi			if (np->narg.next)
113998943Sluigi				cmdputs(" ");
114098943Sluigi		}
114198943Sluigi		for (np = n->ncmd.redirect ; np ; np = np->nfile.next) {
114298943Sluigi			cmdputs(" ");
114398943Sluigi			cmdtxt(np);
114498943Sluigi		}
114598943Sluigi		break;
114698943Sluigi	case NARG:
114798943Sluigi		cmdputs(n->narg.text);
114898943Sluigi		break;
114998943Sluigi	case NTO:
115098943Sluigi		p = ">";  i = 1;  goto redir;
115198943Sluigi	case NAPPEND:
115298943Sluigi		p = ">>";  i = 1;  goto redir;
115398943Sluigi	case NTOFD:
115499475Sluigi		p = ">&";  i = 1;  goto redir;
115599475Sluigi	case NCLOBBER:
115699475Sluigi		p = ">|"; i = 1; goto redir;
1157117328Sluigi	case NFROM:
115898943Sluigi		p = "<";  i = 0;  goto redir;
115999475Sluigi	case NFROMTO:
116099475Sluigi		p = "<>";  i = 0;  goto redir;
116199475Sluigi	case NFROMFD:
116299475Sluigi		p = "<&";  i = 0;  goto redir;
116399475Sluigiredir:
116499475Sluigi		if (n->nfile.fd != i) {
116599475Sluigi			s[0] = n->nfile.fd + '0';
116699475Sluigi			s[1] = '\0';
116799475Sluigi			cmdputs(s);
116899475Sluigi		}
116999475Sluigi		cmdputs(p);
117099475Sluigi		if (n->type == NTOFD || n->type == NFROMFD) {
117199475Sluigi			if (n->ndup.dupfd >= 0)
117299475Sluigi				s[0] = n->ndup.dupfd + '0';
117399475Sluigi			else
117499475Sluigi				s[0] = '-';
117599475Sluigi			s[1] = '\0';
117699475Sluigi			cmdputs(s);
117799475Sluigi		} else {
117899475Sluigi			cmdtxt(n->nfile.fname);
117999475Sluigi		}
118099475Sluigi		break;
118199475Sluigi	case NHERE:
118299475Sluigi	case NXHERE:
118399475Sluigi		cmdputs("<<...");
118499475Sluigi		break;
118599475Sluigi	default:
118699475Sluigi		cmdputs("???");
118799475Sluigi		break;
118899475Sluigi	}
118999475Sluigi}
119099475Sluigi
119199475Sluigi
119299475Sluigi
1193145246SbrooksSTATIC void
1194145246Sbrookscmdputs(char *s)
1195145246Sbrooks{
1196145246Sbrooks	char *p, *q;
1197145246Sbrooks	char c;
1198145246Sbrooks	int subtype = 0;
1199145246Sbrooks
1200145246Sbrooks	if (cmdnleft <= 0)
1201145246Sbrooks		return;
1202145246Sbrooks	p = s;
1203145246Sbrooks	q = cmdnextc;
1204145246Sbrooks	while ((c = *p++) != '\0') {
1205145246Sbrooks		if (c == CTLESC)
1206145246Sbrooks			*q++ = *p++;
1207145246Sbrooks		else if (c == CTLVAR) {
1208145246Sbrooks			*q++ = '$';
1209145246Sbrooks			if (--cmdnleft > 0)
1210145246Sbrooks				*q++ = '{';
1211152923Sume			subtype = *p++;
1212145246Sbrooks		} else if (c == '=' && subtype != 0) {
1213145246Sbrooks			*q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL];
1214145246Sbrooks			subtype = 0;
1215145246Sbrooks		} else if (c == CTLENDVAR) {
1216145246Sbrooks			*q++ = '}';
1217145246Sbrooks		} else if (c == CTLBACKQ || c == CTLBACKQ+CTLQUOTE)
1218145246Sbrooks			cmdnleft++;		/* ignore it */
1219145246Sbrooks		else
1220145246Sbrooks			*q++ = c;
1221145246Sbrooks		if (--cmdnleft <= 0) {
1222145246Sbrooks			*q++ = '.';
1223145246Sbrooks			*q++ = '.';
1224145246Sbrooks			*q++ = '.';
1225145246Sbrooks			break;
1226145246Sbrooks		}
1227145246Sbrooks	}
1228145246Sbrooks	cmdnextc = q;
1229145246Sbrooks}
1230145246Sbrooks