jobs.c revision 100305
1153761Swollman/*- 2193785Sedwin * Copyright (c) 1991, 1993 3192886Sedwin * The Regents of the University of California. All rights reserved. 4192886Sedwin * 564499Swollman * This code is derived from software contributed to Berkeley by 62742Swollman * Kenneth Almquist. 72742Swollman * 82742Swollman * Redistribution and use in source and binary forms, with or without 92742Swollman * modification, are permitted provided that the following conditions 10158421Swollman * are met: 112742Swollman * 1. Redistributions of source code must retain the above copyright 122742Swollman * notice, this list of conditions and the following disclaimer. 13158421Swollman * 2. Redistributions in binary form must reproduce the above copyright 14158421Swollman * notice, this list of conditions and the following disclaimer in the 152742Swollman * documentation and/or other materials provided with the distribution. 1686222Swollman * 3. All advertising materials mentioning features or use of this software 1720094Swollman * must display the following acknowledgement: 1820094Swollman * This product includes software developed by the University of 1920094Swollman * California, Berkeley and its contributors. 2020094Swollman * 4. Neither the name of the University nor the names of its contributors 2120094Swollman * may be used to endorse or promote products derived from this software 22158421Swollman * without specific prior written permission. 23158421Swollman * 2420094Swollman * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 252742Swollman * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 262742Swollman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 272742Swollman * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 282742Swollman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 292742Swollman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3058787Sru * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 312742Swollman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 322742Swollman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 332742Swollman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 342742Swollman * SUCH DAMAGE. 35114173Swollman */ 36114173Swollman 37114173Swollman#ifndef lint 38114173Swollman#if 0 39114173Swollmanstatic char sccsid[] = "@(#)jobs.c 8.5 (Berkeley) 5/4/95"; 40114173Swollman#endif 41114173Swollman#endif /* not lint */ 42114173Swollman#include <sys/cdefs.h> 43114173Swollman__FBSDID("$FreeBSD: head/bin/sh/jobs.c 100305 2002-07-18 09:37:51Z tjr $"); 44114173Swollman 45114173Swollman#include <fcntl.h> 46114173Swollman#include <signal.h> 47114173Swollman#include <errno.h> 48114173Swollman#include <paths.h> 49149590Swollman#include <unistd.h> 50149590Swollman#include <stdlib.h> 51114173Swollman#include <sys/param.h> 522742Swollman#ifdef BSD 539908Swollman#include <sys/wait.h> 542742Swollman#include <sys/time.h> 552742Swollman#include <sys/resource.h> 562742Swollman#include <paths.h> 572742Swollman#endif 582742Swollman#include <sys/ioctl.h> 592742Swollman 602742Swollman#include "shell.h" 612742Swollman#if JOBS 622742Swollman#include <termios.h> 6320094Swollman#undef CEOF /* syntax.h redefines this */ 642742Swollman#endif 6520094Swollman#include "redir.h" 66158421Swollman#include "show.h" 6720094Swollman#include "main.h" 6820094Swollman#include "parser.h" 6920094Swollman#include "nodes.h" 7020094Swollman#include "jobs.h" 7120094Swollman#include "options.h" 7220094Swollman#include "trap.h" 7320094Swollman#include "syntax.h" 7420094Swollman#include "input.h" 7520094Swollman#include "output.h" 7620094Swollman#include "memalloc.h" 7720094Swollman#include "error.h" 7820094Swollman#include "mystring.h" 7920094Swollman 802742Swollman 812742Swollmanstruct job *jobtab; /* array of jobs */ 822742Swollmanint njobs; /* size of array */ 832742SwollmanMKINIT pid_t backgndpid = -1; /* pid of last background process */ 8419878Swollman#if JOBS 852742Swollmanstruct job *jobmru; /* most recently used job list */ 862742Swollmanint initialpgrp; /* pgrp of shell on invocation */ 872742Swollman#endif 88158421Swollmanint in_waitcmd = 0; /* are we in waitcmd()? */ 89158421Swollmanint in_dowait = 0; /* are we in dowait()? */ 90158421Swollmanvolatile sig_atomic_t breakwaitcmd = 0; /* should wait be terminated? */ 91158421Swollmanstatic int ttyfd = -1; 92158421Swollman 93153670Swollman#if JOBS 9443014SwollmanSTATIC void restartjob(struct job *); 9543014Swollman#endif 9643014SwollmanSTATIC void freejob(struct job *); 972742SwollmanSTATIC struct job *getjob(char *); 982742SwollmanSTATIC int dowait(int, struct job *); 9919878Swollman#if SYSV 10019878SwollmanSTATIC int onsigchild(void); 10119878Swollman#endif 10258787SruSTATIC int waitproc(int, int *); 10343014SwollmanSTATIC void cmdtxt(union node *); 10475267SwollmanSTATIC void cmdputs(char *); 1052742Swollman#if JOBS 1062742SwollmanSTATIC void setcurjob(struct job *); 107153670SwollmanSTATIC void deljob(struct job *); 108153670SwollmanSTATIC struct job *getcurjob(struct job *); 109153670Swollman#endif 11043014SwollmanSTATIC void showjob(struct job *, pid_t, int, int); 111153670Swollman 112153670Swollman 1132742Swollman/* 1142742Swollman * Turn job control on and off. 11519878Swollman * 11619878Swollman * Note: This code assumes that the third arg to ioctl is a character 11719878Swollman * pointer, which is true on Berkeley systems but not System V. Since 118149514Swollman * System V doesn't have job control yet, this isn't a problem now. 11920094Swollman */ 12043014Swollman 12143014SwollmanMKINIT int jobctl; 1222742Swollman 1232742Swollman#if JOBS 1242742Swollmanvoid 12567578Swollmansetjobctl(int on) 1262742Swollman{ 1272742Swollman int i; 1282742Swollman 1292742Swollman if (on == jobctl || rootshell == 0) 130193785Sedwin return; 131193785Sedwin if (on) { 132193785Sedwin if (ttyfd != -1) 133193785Sedwin close(ttyfd); 134193785Sedwin if ((ttyfd = open(_PATH_TTY, O_RDWR)) < 0) { 135193785Sedwin i = 0; 136193785Sedwin while (i <= 2 && !isatty(i)) 137193785Sedwin i++; 138193785Sedwin if (i > 2 || (ttyfd = dup(i)) < 0) 139193785Sedwin goto out; 140193785Sedwin } 141193785Sedwin if (fcntl(ttyfd, FD_CLOEXEC, 1) < 0) { 142193785Sedwin close(ttyfd); 143193785Sedwin ttyfd = -1; 144193785Sedwin goto out; 145193785Sedwin } 146193785Sedwin do { /* while we are in the background */ 147193785Sedwin initialpgrp = tcgetpgrp(ttyfd); 148193785Sedwin if (initialpgrp < 0) { 149193785Sedwinout: out2str("sh: can't access tty; job control turned off\n"); 150193785Sedwin mflag = 0; 151193785Sedwin return; 152193785Sedwin } 153193785Sedwin if (initialpgrp == -1) 154193785Sedwin initialpgrp = getpgrp(); 155193785Sedwin else if (initialpgrp != getpgrp()) { 156193785Sedwin killpg(0, SIGTTIN); 157193785Sedwin continue; 158193785Sedwin } 159193785Sedwin } while (0); 160193785Sedwin setsignal(SIGTSTP); 161193785Sedwin setsignal(SIGTTOU); 162193785Sedwin setsignal(SIGTTIN); 163193785Sedwin setpgid(0, rootpid); 164193785Sedwin tcsetpgrp(ttyfd, rootpid); 165193785Sedwin } else { /* turning job control off */ 166193785Sedwin setpgid(0, initialpgrp); 167193785Sedwin tcsetpgrp(ttyfd, initialpgrp); 168193785Sedwin close(ttyfd); 169193785Sedwin ttyfd = -1; 170193785Sedwin setsignal(SIGTSTP); 171193785Sedwin setsignal(SIGTTOU); 1722742Swollman setsignal(SIGTTIN); 17375267Swollman } 17419878Swollman jobctl = on; 17519878Swollman} 1762742Swollman#endif 17719878Swollman 17819878Swollman 179193785Sedwin#ifdef mkinit 180193785SedwinINCLUDE <sys/types.h> 1812742SwollmanINCLUDE <stdlib.h> 1822742Swollman 1832742SwollmanSHELLPROC { 18467578Swollman backgndpid = -1; 1852742Swollman#if JOBS 18619878Swollman jobctl = 0; 1872742Swollman#endif 1882742Swollman} 18986222Swollman 19086222Swollman#endif 191149514Swollman 192149514Swollman 193149514Swollman 1942742Swollman#if JOBS 195149514Swollmanint 196149514Swollmanfgcmd(int argc __unused, char **argv) 19786222Swollman{ 1982742Swollman struct job *jp; 1992742Swollman int pgrp; 2002742Swollman int status; 2012742Swollman 2022742Swollman jp = getjob(argv[1]); 2032742Swollman if (jp->jobctl == 0) 2042742Swollman error("job not created under job control"); 20514343Swollman out1str(jp->ps[0].cmd); 2062742Swollman out1c('\n'); 20717200Swollman flushout(&output); 20819878Swollman pgrp = jp->ps[0].pid; 20919878Swollman tcsetpgrp(ttyfd, pgrp); 2102742Swollman restartjob(jp); 21119878Swollman jp->foreground = 1; 2122742Swollman INTOFF; 2132742Swollman status = waitforjob(jp, (int *)NULL); 2142742Swollman INTON; 2152742Swollman return status; 21619878Swollman} 2172742Swollman 2182742Swollman 2192742Swollmanint 2202742Swollmanbgcmd(int argc, char **argv) 22143014Swollman{ 2222742Swollman char s[64]; 2232742Swollman struct job *jp; 2242742Swollman 2252742Swollman do { 22619878Swollman jp = getjob(*++argv); 22719878Swollman if (jp->jobctl == 0) 2282742Swollman error("job not created under job control"); 2292742Swollman if (jp->state == JOBDONE) 2302742Swollman continue; 23193799Swollman restartjob(jp); 2322742Swollman jp->foreground = 0; 2332742Swollman fmtstr(s, 64, "[%d] ", jp - jobtab + 1); 2342742Swollman out1str(s); 2352742Swollman out1str(jp->ps[0].cmd); 2362742Swollman out1c('\n'); 2372742Swollman } while (--argc > 1); 2382742Swollman return 0; 2392742Swollman} 24019878Swollman 2412742Swollman 2422742SwollmanSTATIC void 2432742Swollmanrestartjob(struct job *jp) 244158421Swollman{ 245158421Swollman struct procstat *ps; 246158421Swollman int i; 247158421Swollman 2482742Swollman if (jp->state == JOBDONE) 249158421Swollman return; 250158421Swollman setcurjob(jp); 2512742Swollman INTOFF; 252158421Swollman killpg(jp->ps[0].pid, SIGCONT); 2532742Swollman for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) { 2542742Swollman if (WIFSTOPPED(ps->status)) { 2552742Swollman ps->status = -1; 2562742Swollman jp->state = 0; 2572742Swollman } 25814343Swollman } 25914343Swollman INTON; 260163302Sru} 26193799Swollman#endif 26293799Swollman 26393799Swollman 264163302Sruint 265169811Swollmanjobscmd(int argc, char *argv[]) 266163302Sru{ 267163302Sru char *id; 268163302Sru int ch, sformat, lformat; 269163302Sru 270163302Sru optind = optreset = 1; 271163302Sru sformat = lformat = 0; 272163302Sru while ((ch = getopt(argc, argv, "ls")) != -1) { 273163302Sru switch (ch) { 274163302Sru case 'l': 275163302Sru lformat = 1; 276163302Sru break; 277181421Sedwin case 's': 278181421Sedwin sformat = 1; 279181421Sedwin break; 280181421Sedwin case '?': 281181421Sedwin default: 282181421Sedwin error("unknown option: -%c", optopt); 283181421Sedwin } 284181421Sedwin } 285181421Sedwin argc -= optind; 286181421Sedwin argv += optind; 287181421Sedwin 288181421Sedwin if (argc == 0) 289181421Sedwin showjobs(0, sformat, lformat); 290181421Sedwin else 291181421Sedwin while ((id = *argv++) != NULL) 292181421Sedwin showjob(getjob(id), 0, sformat, lformat); 293181421Sedwin 294181421Sedwin return (0); 295181421Sedwin} 296181421Sedwin 297181421SedwinSTATIC void 298181421Sedwinshowjob(struct job *jp, pid_t pid, int sformat, int lformat) 299163302Sru{ 300163302Sru char s[64]; 30193799Swollman struct procstat *ps; 302163302Sru struct job *j; 30367578Swollman int col, curr, i, jobno, prev, procno; 30493799Swollman char c; 30514343Swollman 30693799Swollman procno = jp->nprocs; 30793799Swollman jobno = jp - jobtab + 1; 30814343Swollman curr = prev = 0; 30993799Swollman#if JOBS 310163302Sru if ((j = getcurjob(NULL)) != NULL) { 3112742Swollman curr = j - jobtab + 1; 3122742Swollman if ((j = getcurjob(j)) != NULL) 3132742Swollman prev = j - jobtab + 1; 31493799Swollman } 315163302Sru#endif 316163302Sru for (ps = jp->ps ; ; ps++) { /* for each process */ 317163302Sru if (sformat) { 318163302Sru out1fmt("%d\n", ps->pid); 31986222Swollman goto skip; 32093799Swollman } 32114343Swollman if (!lformat && ps != jp->ps && pid == 0) 32293799Swollman goto skip; 323163302Sru if (pid != 0 && pid != ps->pid) 324163302Sru goto skip; 325163302Sru if (jobno == curr && ps == jp->ps) 326163302Sru c = '+'; 327163302Sru else if (jobno == prev && ps == jp->ps) 328163302Sru c = '-'; 329163302Sru else 330163302Sru c = ' '; 33167578Swollman if (ps == jp->ps) 33219878Swollman fmtstr(s, 64, "[%d] %c ", jobno, c); 33314343Swollman else 33493799Swollman fmtstr(s, 64, " %c ", c); 335163302Sru out1str(s); 336163302Sru col = strlen(s); 337163302Sru if (lformat) { 338163302Sru fmtstr(s, 64, "%d ", ps->pid); 33967578Swollman out1str(s); 34019878Swollman col += strlen(s); 34119878Swollman } 34214343Swollman s[0] = '\0'; 34393799Swollman if (ps != jp->ps) { 34467578Swollman *s = '\0'; 3452742Swollman } else if (ps->status == -1) { 3462742Swollman strcpy(s, "Running"); 3472742Swollman } else if (WIFEXITED(ps->status)) { 3482742Swollman if (WEXITSTATUS(ps->status) == 0) 3492742Swollman strcpy(s, "Done"); 3502742Swollman else 3512742Swollman fmtstr(s, 64, "Done (%d)", 3522742Swollman WEXITSTATUS(ps->status)); 3532742Swollman } else { 3542742Swollman#if JOBS 3552742Swollman if (WIFSTOPPED(ps->status)) 3562742Swollman i = WSTOPSIG(ps->status); 3572742Swollman else 3582742Swollman#endif 3592742Swollman i = WTERMSIG(ps->status); 3602742Swollman if ((i & 0x7F) < NSIG && sys_siglist[i & 0x7F]) 3612742Swollman scopy(sys_siglist[i & 0x7F], s); 3622742Swollman else 36358787Sru fmtstr(s, 64, "Signal %d", i & 0x7F); 3642742Swollman if (WCOREDUMP(ps->status)) 36530711Swollman strcat(s, " (core dumped)"); 36630711Swollman } 36730711Swollman out1str(s); 36843014Swollman col += strlen(s); 36930711Swollman do { 370158421Swollman out1c(' '); 37143543Swollman col++; 37243543Swollman } while (col < 30); 37343543Swollman out1str(ps->cmd); 37430711Swollman out1c('\n'); 37530711Swollmanskip: if (--procno <= 0) 37630711Swollman break; 37730711Swollman } 37830711Swollman} 37930711Swollman 38030711Swollman/* 38130711Swollman * Print a list of jobs. If "change" is nonzero, only print jobs whose 38230711Swollman * statuses have changed since the last call to showjobs. 38330711Swollman * 38430711Swollman * If the shell is interrupted in the process of creating a job, the 38530711Swollman * result may be a job structure containing zero processes. Such structures 38630711Swollman * will be freed here. 38786222Swollman */ 38830711Swollman 38930711Swollmanvoid 39093799Swollmanshowjobs(int change, int sformat, int lformat) 3912742Swollman{ 39293799Swollman int jobno; 39393799Swollman struct job *jp; 39493799Swollman 39593799Swollman TRACE(("showjobs(%d) called\n", change)); 39693799Swollman while (dowait(0, (struct job *)NULL) > 0); 39793799Swollman for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) { 39893799Swollman if (! jp->used) 39993799Swollman continue; 40093799Swollman if (jp->nprocs == 0) { 40193799Swollman freejob(jp); 40293799Swollman continue; 40393799Swollman } 40493799Swollman if (change && ! jp->changed) 40593799Swollman continue; 4062742Swollman showjob(jp, 0, sformat, lformat); 40793799Swollman jp->changed = 0; 40893799Swollman if (jp->state == JOBDONE) { 40919878Swollman freejob(jp); 4102742Swollman } 4112742Swollman } 4122742Swollman} 4132742Swollman 4142742Swollman 4152742Swollman/* 41619878Swollman * Mark a job structure as unused. 4172742Swollman */ 41819878Swollman 4192742SwollmanSTATIC void 42019878Swollmanfreejob(struct job *jp) 4212742Swollman{ 4222742Swollman struct procstat *ps; 42343543Swollman int i; 42443543Swollman 4252742Swollman INTOFF; 4262742Swollman for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) { 42743543Swollman if (ps->cmd != nullstr) 42858787Sru ckfree(ps->cmd); 42943543Swollman } 4302742Swollman if (jp->ps != &jp->ps0) 43167578Swollman ckfree(jp->ps); 43267578Swollman jp->used = 0; 43367578Swollman#if JOBS 43467578Swollman deljob(jp); 4352742Swollman#endif 436149514Swollman INTON; 4379908Swollman} 4389908Swollman 4399908Swollman 44014343Swollman 44114343Swollmanint 442149514Swollmanwaitcmd(int argc, char **argv) 44320094Swollman{ 44420094Swollman struct job *job; 44520094Swollman int status, retval; 446136638Swollman struct job *jp; 447136638Swollman 448149514Swollman if (argc > 1) { 449136638Swollman job = getjob(argv[1]); 450136638Swollman } else { 451136638Swollman job = NULL; 452136638Swollman } 453136638Swollman 454136638Swollman /* 455136638Swollman * Loop until a process is terminated or stopped, or a SIGINT is 456153670Swollman * received. 457153670Swollman */ 458153670Swollman 459153670Swollman in_waitcmd++; 460153670Swollman do { 461153670Swollman if (job != NULL) { 462153670Swollman if (job->state) { 463153670Swollman status = job->ps[job->nprocs - 1].status; 464153670Swollman if (WIFEXITED(status)) 465153670Swollman retval = WEXITSTATUS(status); 466153670Swollman#if JOBS 4672742Swollman else if (WIFSTOPPED(status)) 4682742Swollman retval = WSTOPSIG(status) + 128; 46919878Swollman#endif 47019878Swollman else 47119878Swollman retval = WTERMSIG(status) + 128; 47219878Swollman if (! iflag) 47320094Swollman freejob(job); 47420094Swollman in_waitcmd--; 47520094Swollman return retval; 47643543Swollman } 477136638Swollman } else { 478153670Swollman for (jp = jobtab ; ; jp++) { 479153670Swollman if (jp >= jobtab + njobs) { /* no running procs */ 4802742Swollman in_waitcmd--; 48158787Sru return 0; 48275267Swollman } 483169811Swollman if (jp->used && jp->state == 0) 484169811Swollman break; 48575267Swollman } 48675267Swollman } 48775267Swollman } while (dowait(1, (struct job *)NULL) != -1); 48875267Swollman in_waitcmd--; 48975267Swollman 49075267Swollman return 0; 49175267Swollman} 49275267Swollman 49375267Swollman 49475267Swollman 49575267Swollmanint 49675267Swollmanjobidcmd(int argc __unused, char **argv) 49775267Swollman{ 49875267Swollman struct job *jp; 49975267Swollman int i; 50075267Swollman 50175267Swollman jp = getjob(argv[1]); 50275267Swollman for (i = 0 ; i < jp->nprocs ; ) { 50375267Swollman out1fmt("%d", jp->ps[i].pid); 50475267Swollman out1c(++i < jp->nprocs? ' ' : '\n'); 50575267Swollman } 50658787Sru return 0; 50758787Sru} 508149514Swollman 509169811Swollman 510149514Swollman 51186222Swollman/* 512149514Swollman * Convert a job name to a job structure. 51358787Sru */ 5142742Swollman 5152742SwollmanSTATIC struct job * 516177591Sedwingetjob(char *name) 51719878Swollman{ 51819878Swollman int jobno; 5192742Swollman struct job *found, *jp; 5202742Swollman int pid; 5212742Swollman int i; 522177591Sedwin 5232742Swollman if (name == NULL) { 5242742Swollman#if JOBS 5252742Swollmancurrentjob: if ((jp = getcurjob(NULL)) == NULL) 5262742Swollman error("No current job"); 5272742Swollman return (jp); 52886222Swollman#else 529158421Swollman error("No current job"); 53086222Swollman#endif 53186222Swollman } else if (name[0] == '%') { 53286222Swollman if (is_digit(name[1])) { 53386222Swollman jobno = number(name + 1); 53486222Swollman if (jobno > 0 && jobno <= njobs 535169811Swollman && jobtab[jobno - 1].used != 0) 536169811Swollman return &jobtab[jobno - 1]; 537169811Swollman#if JOBS 538169811Swollman } else if (name[1] == '%' && name[2] == '\0') { 539169811Swollman goto currentjob; 540169811Swollman } else if (name[1] == '+' && name[2] == '\0') { 541169811Swollman goto currentjob; 542169811Swollman } else if (name[1] == '-' && name[2] == '\0') { 543169811Swollman if ((jp = getcurjob(NULL)) == NULL || 544169811Swollman (jp = getcurjob(jp)) == NULL) 545169811Swollman error("No previous job"); 546169811Swollman return (jp); 547169811Swollman#endif 5482742Swollman } else if (name[1] == '?') { 5492742Swollman found = NULL; 550158421Swollman for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { 55158787Sru if (jp->used && jp->nprocs > 0 55258787Sru && strstr(jp->ps[0].cmd, name + 2) != NULL) { 55319878Swollman if (found) 55486222Swollman error("%s: ambiguous", name); 555169811Swollman found = jp; 55686222Swollman } 55786222Swollman } 55886222Swollman if (found != NULL) 55986222Swollman return (found); 56086222Swollman } else { 56186222Swollman found = NULL; 56286222Swollman for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { 563169811Swollman if (jp->used && jp->nprocs > 0 56486222Swollman && prefix(name + 1, jp->ps[0].cmd)) { 56586222Swollman if (found) 56686222Swollman error("%s: ambiguous", name); 56786222Swollman found = jp; 56886222Swollman } 56993799Swollman } 57019878Swollman if (found) 57186222Swollman return found; 572169811Swollman } 57386222Swollman } else if (is_number(name)) { 5742742Swollman pid = number(name); 575169811Swollman for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { 5762742Swollman if (jp->used && jp->nprocs > 0 57786222Swollman && jp->ps[jp->nprocs - 1].pid == pid) 5782742Swollman return jp; 5792742Swollman } 580114173Swollman } 581114173Swollman error("No such job: %s", name); 582114173Swollman /*NOTREACHED*/ 583114173Swollman return NULL; 58421217Swollman} 585114173Swollman 586114173Swollman 58721217Swollman 588114173Swollman/* 589114173Swollman * Return a new job structure, 590114173Swollman */ 591114173Swollman 592114173Swollmanstruct job * 593114173Swollmanmakejob(union node *node __unused, int nprocs) 594114173Swollman{ 595114173Swollman int i; 596114173Swollman struct job *jp; 597114173Swollman 598114173Swollman for (i = njobs, jp = jobtab ; ; jp++) { 599114173Swollman if (--i < 0) { 600114173Swollman INTOFF; 601114173Swollman if (njobs == 0) { 602114173Swollman jobtab = ckmalloc(4 * sizeof jobtab[0]); 603114173Swollman#if JOBS 604114173Swollman jobmru = NULL; 605114173Swollman#endif 606114173Swollman } else { 607114173Swollman jp = ckmalloc((njobs + 4) * sizeof jobtab[0]); 608114173Swollman memcpy(jp, jobtab, njobs * sizeof jp[0]); 609114173Swollman#if JOBS 610149514Swollman /* Relocate `next' pointers and list head */ 611149514Swollman if (jobmru != NULL) 612149514Swollman jobmru = &jp[jobmru - jobtab]; 613149514Swollman for (i = 0; i < njobs; i++) 614149514Swollman if (jp[i].next != NULL) 615149514Swollman jp[i].next = &jp[jp[i].next - 616149514Swollman jobtab]; 617158421Swollman#endif 618158421Swollman /* Relocate `ps' pointers */ 619149514Swollman for (i = 0; i < njobs; i++) 620149514Swollman if (jp[i].ps == &jobtab[i].ps0) 621149514Swollman jp[i].ps = &jp[i].ps0; 622149514Swollman ckfree(jobtab); 62321217Swollman jobtab = jp; 624149514Swollman } 625149514Swollman jp = jobtab + njobs; 626149514Swollman for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0); 627149514Swollman INTON; 628149514Swollman break; 629149514Swollman } 630149514Swollman if (jp->used == 0) 631149514Swollman break; 632149514Swollman } 633149514Swollman INTOFF; 634149514Swollman jp->state = 0; 635149514Swollman jp->used = 1; 636149514Swollman jp->changed = 0; 637149514Swollman jp->nprocs = 0; 638158421Swollman jp->foreground = 0; 639158421Swollman#if JOBS 640158421Swollman jp->jobctl = jobctl; 641158421Swollman jp->next = NULL; 642172479Sedwin#endif 643172479Sedwin if (nprocs > 1) { 644172479Sedwin jp->ps = ckmalloc(nprocs * sizeof (struct procstat)); 645172479Sedwin } else { 646172479Sedwin jp->ps = &jp->ps0; 647174242Sedwin } 648174242Sedwin INTON; 649174242Sedwin TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs, 650174242Sedwin jp - jobtab + 1)); 651174242Sedwin return jp; 652174242Sedwin} 653174242Sedwin 654174242Sedwin#if JOBS 655174242SedwinSTATIC void 6562742Swollmansetcurjob(struct job *cj) 657114173Swollman{ 658114173Swollman struct job *jp, *prev; 659114173Swollman 660114173Swollman for (prev = NULL, jp = jobmru; jp != NULL; prev = jp, jp = jp->next) { 661114173Swollman if (jp == cj) { 662114173Swollman if (prev != NULL) 663114173Swollman prev->next = jp->next; 664114173Swollman else 665114173Swollman jobmru = jp->next; 666114173Swollman jp->next = jobmru; 667114173Swollman jobmru = cj; 668114173Swollman return; 669114173Swollman } 670114173Swollman } 671114173Swollman cj->next = jobmru; 672114173Swollman jobmru = cj; 673114173Swollman} 674158421Swollman 675158421SwollmanSTATIC void 676172479Sedwindeljob(struct job *j) 677172479Sedwin{ 678172479Sedwin struct job *jp, *prev; 679172479Sedwin 680172479Sedwin for (prev = NULL, jp = jobmru; jp != NULL; prev = jp, jp = jp->next) { 681172479Sedwin if (jp == j) { 682172479Sedwin if (prev != NULL) 683172479Sedwin prev->next = jp->next; 684172479Sedwin else 685172479Sedwin jobmru = jp->next; 686172479Sedwin return; 687172479Sedwin } 688172479Sedwin } 689172479Sedwin} 690172479Sedwin 691172479Sedwin/* 692172479Sedwin * Return the most recently used job that isn't `nj', and preferably one 693172479Sedwin * that is stopped. 694172479Sedwin */ 695172479SedwinSTATIC struct job * 696172479Sedwingetcurjob(struct job *nj) 697172479Sedwin{ 698172479Sedwin struct job *jp; 699172479Sedwin 700172479Sedwin /* Try to find a stopped one.. */ 701172479Sedwin for (jp = jobmru; jp != NULL; jp = jp->next) 702172479Sedwin if (jp->used && jp != nj && jp->state == JOBSTOPPED) 703172479Sedwin return (jp); 704172479Sedwin /* Otherwise the most recently used job that isn't `nj' */ 705172479Sedwin for (jp = jobmru; jp != NULL; jp = jp->next) 7062742Swollman if (jp->used && jp != nj) 7072742Swollman return (jp); 70819878Swollman 709114173Swollman return (NULL); 71019878Swollman} 71119878Swollman 7122742Swollman#endif 71364499Swollman 7142742Swollman/* 71564499Swollman * Fork of a subshell. If we are doing job control, give the subshell its 716149514Swollman * own process group. Jp is a job structure that the job is to be added to. 71764499Swollman * N is the command that will be evaluated by the child. Both jp and n may 71875267Swollman * be NULL. The mode parameter can be one of the following: 71964499Swollman * FORK_FG - Fork off a foreground process. 72064499Swollman * FORK_BG - Fork off a background process. 72164499Swollman * FORK_NOJOB - Like FORK_FG, but don't give the process its own 72264499Swollman * process group even if job control is on. 72364499Swollman * 72464499Swollman * When job control is turned off, background processes have their standard 72564499Swollman * input redirected to /dev/null (except for the second and later processes 72664499Swollman * in a pipeline). 72764499Swollman */ 72864499Swollman 72964499Swollmanint 730177591Sedwinforkshell(struct job *jp, union node *n, int mode) 731177591Sedwin{ 732177591Sedwin int pid; 733177591Sedwin int pgrp; 734177591Sedwin 735177591Sedwin TRACE(("forkshell(%%%d, 0x%lx, %d) called\n", jp - jobtab, (long)n, 736177591Sedwin mode)); 737177591Sedwin INTOFF; 738177591Sedwin pid = fork(); 739177591Sedwin if (pid == -1) { 740177591Sedwin TRACE(("Fork failed, errno=%d\n", errno)); 741177591Sedwin INTON; 742177591Sedwin error("Cannot fork: %s", strerror(errno)); 743177591Sedwin } 744177591Sedwin if (pid == 0) { 7452742Swollman struct job *p; 7462742Swollman int wasroot; 7472742Swollman int i; 7482742Swollman 7492742Swollman TRACE(("Child shell %d\n", getpid())); 75020094Swollman wasroot = rootshell; 75120094Swollman rootshell = 0; 75220094Swollman for (i = njobs, p = jobtab ; --i >= 0 ; p++) 753158421Swollman if (p->used) 754169811Swollman freejob(p); 755177591Sedwin closescript(); 756177591Sedwin INTON; 7572742Swollman clear_traps(); 7582742Swollman#if JOBS 75919878Swollman jobctl = 0; /* do job control only in root shell */ 7602742Swollman if (wasroot && mode != FORK_NOJOB && mflag) { 7612742Swollman if (jp == NULL || jp->nprocs == 0) 7622742Swollman pgrp = getpid(); 7632742Swollman else 7642742Swollman pgrp = jp->ps[0].pid; 7652742Swollman if (setpgid(0, pgrp) == 0 && mode == FORK_FG) { 7662742Swollman /*** this causes superfluous TIOCSPGRPS ***/ 7672742Swollman if (tcsetpgrp(ttyfd, pgrp) < 0) 76875267Swollman error("tcsetpgrp failed, errno=%d", errno); 76975267Swollman } 77075267Swollman setsignal(SIGTSTP); 77175267Swollman setsignal(SIGTTOU); 77275267Swollman } else if (mode == FORK_BG) { 77375267Swollman ignoresig(SIGINT); 77475267Swollman ignoresig(SIGQUIT); 77575267Swollman if ((jp == NULL || jp->nprocs == 0) && 77675267Swollman ! fd0_redirected_p ()) { 77775267Swollman close(0); 77875267Swollman if (open(_PATH_DEVNULL, O_RDONLY) != 0) 77975267Swollman error("Can't open %s: %s", 78075267Swollman _PATH_DEVNULL, strerror(errno)); 78175267Swollman } 78275267Swollman } 78375267Swollman#else 78475267Swollman if (mode == FORK_BG) { 78575267Swollman ignoresig(SIGINT); 78675267Swollman ignoresig(SIGQUIT); 7872742Swollman if ((jp == NULL || jp->nprocs == 0) && 788158421Swollman ! fd0_redirected_p ()) { 7892742Swollman close(0); 7902742Swollman if (open(_PATH_DEVNULL, O_RDONLY) != 0) 7912742Swollman error("Can't open %s: %s", 7922742Swollman _PATH_DEVNULL, strerror(errno)); 7932742Swollman } 7942742Swollman } 7952742Swollman#endif 7962742Swollman if (wasroot && iflag) { 7972742Swollman setsignal(SIGINT); 7982742Swollman setsignal(SIGQUIT); 7992742Swollman setsignal(SIGTERM); 8002742Swollman } 8012742Swollman return pid; 8022742Swollman } 8032742Swollman if (rootshell && mode != FORK_NOJOB && mflag) { 8042742Swollman if (jp == NULL || jp->nprocs == 0) 8052742Swollman pgrp = pid; 8062742Swollman else 8072742Swollman pgrp = jp->ps[0].pid; 8082742Swollman setpgid(pid, pgrp); 8092742Swollman } 8102742Swollman if (mode == FORK_BG) 8112742Swollman backgndpid = pid; /* set $! */ 8122742Swollman if (jp) { 8132742Swollman struct procstat *ps = &jp->ps[jp->nprocs++]; 8142742Swollman ps->pid = pid; 8152742Swollman ps->status = -1; 8162742Swollman ps->cmd = nullstr; 8172742Swollman if (iflag && rootshell && n) 8182742Swollman ps->cmd = commandtext(n); 8192742Swollman jp->foreground = mode == FORK_FG; 8202742Swollman#if JOBS 8212742Swollman setcurjob(jp); 8222742Swollman#endif 8232742Swollman } 8242742Swollman INTON; 8252742Swollman TRACE(("In parent shell: child = %d\n", pid)); 8262742Swollman return pid; 8272742Swollman} 8282742Swollman 8292742Swollman 8302742Swollman 831149514Swollman/* 832149514Swollman * Wait for job to finish. 833149514Swollman * 83430711Swollman * Under job control we have the problem that while a child process is 8352742Swollman * running interrupts generated by the user are sent to the child but not 8362742Swollman * to the shell. This means that an infinite loop started by an inter- 83743543Swollman * active user may be hard to kill. With job control turned off, an 83843543Swollman * interactive user may place an interactive program inside a loop. If 83943543Swollman * the interactive program catches interrupts, the user doesn't want 84043543Swollman * these interrupts to also abort the loop. The approach we take here 84143543Swollman * is to have the shell ignore interrupt signals while waiting for a 84243543Swollman * foreground process to terminate, and then send itself an interrupt 84343543Swollman * signal if the child process was terminated by an interrupt signal. 84464499Swollman * Unfortunately, some programs want to do a bit of cleanup and then 84564499Swollman * exit on interrupt; unless these processes terminate themselves by 84643543Swollman * sending a signal to themselves (instead of calling exit) they will 84764499Swollman * confuse this approach. 84864499Swollman */ 84964499Swollman 85064499Swollmanint 85164499Swollmanwaitforjob(struct job *jp, int *origstatus) 85264499Swollman{ 85364499Swollman#if JOBS 85464499Swollman int mypgrp = getpgrp(); 85564499Swollman#endif 85664499Swollman int status; 8572742Swollman int st; 8582742Swollman 8592742Swollman INTOFF; 86019878Swollman TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1)); 8612742Swollman while (jp->state == 0) 86219878Swollman if (dowait(1, jp) == -1) 8632742Swollman dotrap(); 86419878Swollman#if JOBS 8652742Swollman if (jp->jobctl) { 86619878Swollman if (tcsetpgrp(ttyfd, mypgrp) < 0) 8672742Swollman error("tcsetpgrp failed, errno=%d\n", errno); 86819878Swollman } 8692742Swollman if (jp->state == JOBSTOPPED) 87014343Swollman setcurjob(jp); 87114343Swollman#endif 87230711Swollman status = jp->ps[jp->nprocs - 1].status; 8732742Swollman if (origstatus != NULL) 87475267Swollman *origstatus = status; 87519878Swollman /* convert to 8 bits */ 87619878Swollman if (WIFEXITED(status)) 87719878Swollman st = WEXITSTATUS(status); 87819878Swollman#if JOBS 8792742Swollman else if (WIFSTOPPED(status)) 88030711Swollman st = WSTOPSIG(status) + 128; 88130711Swollman#endif 88243014Swollman else 88314343Swollman st = WTERMSIG(status) + 128; 884149514Swollman if (! JOBS || jp->state == JOBDONE) 88514343Swollman freejob(jp); 88643014Swollman if (int_pending()) { 88730711Swollman if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT) 88858787Sru kill(getpid(), SIGINT); 88943014Swollman else 890149514Swollman CLEAR_PENDING_INT; 89143014Swollman } 89258787Sru INTON; 8939908Swollman return st; 89414343Swollman} 89530711Swollman 89619878Swollman 89730711Swollman 89830711Swollman/* 89930711Swollman * Wait for a process to terminate. 90043014Swollman */ 90143543Swollman 90243543SwollmanSTATIC int 90314343Swollmandowait(int block, struct job *job) 90464499Swollman{ 90564499Swollman int pid; 90664499Swollman int status; 90758787Sru struct procstat *sp; 90864499Swollman struct job *jp; 90958787Sru struct job *thisjob; 910149514Swollman int done; 91164499Swollman int stopped; 91264499Swollman int sig; 91364499Swollman int i; 91464499Swollman 915149514Swollman in_dowait++; 91643014Swollman TRACE(("dowait(%d) called\n", block)); 91758787Sru do { 91858787Sru pid = waitproc(block, &status); 91964499Swollman TRACE(("wait returns %d, status=%d\n", pid, status)); 92064499Swollman } while ((pid == -1 && errno == EINTR && breakwaitcmd == 0) || 92164499Swollman (WIFSTOPPED(status) && !iflag)); 92264499Swollman in_dowait--; 92364499Swollman if (breakwaitcmd != 0) { 92464499Swollman breakwaitcmd = 0; 92564499Swollman return -1; 92664499Swollman } 92764499Swollman if (pid <= 0) 92858787Sru return pid; 929149514Swollman INTOFF; 930149514Swollman thisjob = NULL; 931149514Swollman for (jp = jobtab ; jp < jobtab + njobs ; jp++) { 932149514Swollman if (jp->used) { 933149514Swollman done = 1; 934149514Swollman stopped = 1; 935149514Swollman for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) { 936149514Swollman if (sp->pid == -1) 937149514Swollman continue; 93830711Swollman if (sp->pid == pid) { 939149514Swollman TRACE(("Changing status of proc %d from 0x%x to 0x%x\n", 940149514Swollman pid, sp->status, status)); 941149514Swollman sp->status = status; 942149514Swollman thisjob = jp; 943149514Swollman } 944149514Swollman if (sp->status == -1) 945149514Swollman stopped = 0; 946149514Swollman else if (WIFSTOPPED(sp->status)) 947149514Swollman done = 0; 948149514Swollman } 949149514Swollman if (stopped) { /* stopped or done */ 950149514Swollman int state = done? JOBDONE : JOBSTOPPED; 951149514Swollman if (jp->state != state) { 952149514Swollman TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state)); 953149514Swollman jp->state = state; 954149514Swollman#if JOBS 955149514Swollman if (done) 956149514Swollman deljob(jp); 957149514Swollman#endif 958149514Swollman } 959149514Swollman } 960149514Swollman } 961149514Swollman } 962149514Swollman INTON; 963149514Swollman if (! rootshell || ! iflag || (job && thisjob == job)) { 964149514Swollman#if JOBS 965149514Swollman if (WIFSTOPPED(status)) 966149514Swollman sig = WSTOPSIG(status); 967149514Swollman else 968149514Swollman#endif 969149514Swollman { 970149514Swollman if (WIFEXITED(status)) 971149514Swollman sig = 0; 972149514Swollman else 973149514Swollman sig = WTERMSIG(status); 974149514Swollman } 975149514Swollman if (sig != 0 && sig != SIGINT && sig != SIGPIPE) { 976149514Swollman if (jp->foreground) { 977149514Swollman#if JOBS 978149514Swollman if (WIFSTOPPED(status)) 979149514Swollman i = WSTOPSIG(status); 980149514Swollman else 981149514Swollman#endif 982149514Swollman i = WTERMSIG(status); 983149514Swollman if ((i & 0x7F) < NSIG && sys_siglist[i & 0x7F]) 984149514Swollman out1str(sys_siglist[i & 0x7F]); 985149514Swollman else 986149514Swollman out1fmt("Signal %d", i & 0x7F); 987149514Swollman if (WCOREDUMP(status)) 988149514Swollman out1str(" (core dumped)"); 989149514Swollman out1c('\n'); 990149514Swollman } else 991149514Swollman showjob(thisjob, pid, 0, 1); 992149514Swollman } 993149514Swollman } else { 994149514Swollman TRACE(("Not printing status, rootshell=%d, job=0x%x\n", rootshell, job)); 995149514Swollman if (thisjob) 996149514Swollman thisjob->changed = 1; 997149514Swollman } 9982742Swollman return pid; 99914343Swollman} 100019878Swollman 10012742Swollman 10022742Swollman 10032742Swollman/* 1004105196Swollman * Do a wait system call. If job control is compiled in, we accept 10052742Swollman * stopped processes. If block is zero, we return a value of zero 10062742Swollman * rather than blocking. 10072742Swollman * 10082742Swollman * System V doesn't have a non-blocking wait system call. It does 10092742Swollman * have a SIGCLD signal that is sent to a process when one of it's 10102742Swollman * children dies. The obvious way to use SIGCLD would be to install 1011149514Swollman * a handler for SIGCLD which simply bumped a counter when a SIGCLD 10129908Swollman * was received, and have waitproc bump another counter when it got 10139908Swollman * the status of a process. Waitproc would then know that a wait 10149908Swollman * system call would not block if the two counters were different. 1015149590Swollman * This approach doesn't work because if a process has children that 1016149590Swollman * have not been waited for, System V will send it a SIGCLD when it 1017149590Swollman * installs a signal handler for SIGCLD. What this means is that when 1018149590Swollman * a child exits, the shell will be sent SIGCLD signals continuously 1019149590Swollman * until is runs out of stack space, unless it does a wait call before 1020149590Swollman * restoring the signal handler. The code below takes advantage of 1021149590Swollman * this (mis)feature by installing a signal handler for SIGCLD and 1022149590Swollman * then checking to see whether it was called. If there are any 1023149590Swollman * children to be waited for, it will be. 1024149590Swollman * 1025149590Swollman * If neither SYSV nor BSD is defined, we don't implement nonblocking 1026149590Swollman * waits at all. In this case, the user will not be informed when 1027158421Swollman * a background process until the next time she runs a real program 1028158421Swollman * (as opposed to running a builtin command or just typing return), 10299908Swollman * and the jobs command may give out of date information. 1030149590Swollman */ 1031149590Swollman 1032149590Swollman#ifdef SYSV 1033149590SwollmanSTATIC sig_atomic_t gotsigchild; 1034149590Swollman 1035158421SwollmanSTATIC int onsigchild() { 1036149590Swollman gotsigchild = 1; 1037149590Swollman} 10382742Swollman#endif 103943543Swollman 104043543Swollman 104143543SwollmanSTATIC int 104243543Swollmanwaitproc(int block, int *status) 104343543Swollman{ 104443543Swollman#ifdef BSD 104543543Swollman int flags; 104643543Swollman 104743543Swollman#if JOBS 104843543Swollman flags = WUNTRACED; 104943543Swollman#else 105043543Swollman flags = 0; 105143543Swollman#endif 105243543Swollman if (block == 0) 105343543Swollman flags |= WNOHANG; 105443543Swollman return wait3(status, flags, (struct rusage *)NULL); 105543543Swollman#else 105643543Swollman#ifdef SYSV 105743543Swollman int (*save)(); 105843543Swollman 105943543Swollman if (block == 0) { 1060158421Swollman gotsigchild = 0; 1061158421Swollman save = signal(SIGCLD, onsigchild); 1062158421Swollman signal(SIGCLD, save); 106343543Swollman if (gotsigchild == 0) 10642742Swollman return 0; 106543543Swollman } 106643543Swollman return wait(status); 106743543Swollman#else 1068149590Swollman if (block == 0) 106943543Swollman return 0; 10702742Swollman return wait(status); 10712742Swollman#endif 107258787Sru#endif 107358787Sru} 107458787Sru 107558787Sru/* 107658787Sru * return 1 if there are stopped jobs, otherwise 0 107758787Sru */ 107858787Sruint job_warning = 0; 107958787Sruint 108058787Srustoppedjobs(void) 108158787Sru{ 108258787Sru int jobno; 108358787Sru struct job *jp; 108458787Sru 108558787Sru if (job_warning) 1086153670Swollman return (0); 1087153670Swollman for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) { 1088153670Swollman if (jp->used == 0) 1089158421Swollman continue; 1090158421Swollman if (jp->state == JOBSTOPPED) { 1091158421Swollman out2str("You have stopped jobs.\n"); 1092158421Swollman job_warning = 2; 1093163302Sru return (1); 1094163302Sru } 1095163302Sru } 1096163302Sru 1097190991Sedwin return (0); 1098190991Sedwin} 1099190991Sedwin 1100190991Sedwin/* 1101190991Sedwin * Return a string identifying a command (to be printed by the 1102190991Sedwin * jobs command. 1103190991Sedwin */ 1104190991Sedwin 1105190991SedwinSTATIC char *cmdnextc; 1106190991SedwinSTATIC int cmdnleft; 1107190991Sedwin#define MAXCMDTEXT 200 1108190991Sedwin 1109190991Sedwinchar * 1110190991Sedwincommandtext(union node *n) 1111190991Sedwin{ 1112190991Sedwin char *name; 1113190991Sedwin 1114190991Sedwin cmdnextc = name = ckmalloc(MAXCMDTEXT); 1115190991Sedwin cmdnleft = MAXCMDTEXT - 4; 1116190991Sedwin cmdtxt(n); 1117190991Sedwin *cmdnextc = '\0'; 1118190991Sedwin return name; 1119190991Sedwin} 1120190991Sedwin 1121190991Sedwin 1122190991SedwinSTATIC void 1123190991Sedwincmdtxt(union node *n) 1124190991Sedwin{ 1125190991Sedwin union node *np; 1126190991Sedwin struct nodelist *lp; 1127190991Sedwin char *p; 1128190991Sedwin int i; 1129190991Sedwin char s[2]; 1130190991Sedwin 11312742Swollman if (n == NULL) 113275267Swollman return; 113375267Swollman switch (n->type) { 113475267Swollman case NSEMI: 113575267Swollman cmdtxt(n->nbinary.ch1); 113675267Swollman cmdputs("; "); 113775267Swollman cmdtxt(n->nbinary.ch2); 113875267Swollman break; 113975267Swollman case NAND: 114075267Swollman cmdtxt(n->nbinary.ch1); 114175267Swollman cmdputs(" && "); 114275267Swollman cmdtxt(n->nbinary.ch2); 114375267Swollman break; 114475267Swollman case NOR: 114575267Swollman cmdtxt(n->nbinary.ch1); 114675267Swollman cmdputs(" || "); 114775267Swollman cmdtxt(n->nbinary.ch2); 114875267Swollman break; 114975267Swollman case NPIPE: 115075267Swollman for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { 115175267Swollman cmdtxt(lp->n); 115258787Sru if (lp->next) 1153190991Sedwin cmdputs(" | "); 1154190991Sedwin } 1155192886Sedwin break; 1156153670Swollman case NSUBSHELL: 1157153670Swollman cmdputs("("); 1158163302Sru cmdtxt(n->nredir.n); 1159163302Sru cmdputs(")"); 11602742Swollman break; 11612742Swollman case NREDIR: 116219878Swollman case NBACKGND: 11632742Swollman cmdtxt(n->nredir.n); 1164149514Swollman break; 11652742Swollman case NIF: 1166149514Swollman cmdputs("if "); 116720094Swollman cmdtxt(n->nif.test); 1168149514Swollman cmdputs("; then "); 116920094Swollman cmdtxt(n->nif.ifpart); 117020094Swollman cmdputs("..."); 117120094Swollman break; 117220094Swollman case NWHILE: 1173149514Swollman cmdputs("while "); 1174158421Swollman goto until; 117593799Swollman case NUNTIL: 117693799Swollman cmdputs("until "); 1177158421Swollmanuntil: 1178158421Swollman cmdtxt(n->nbinary.ch1); 117993799Swollman cmdputs("; do "); 118093799Swollman cmdtxt(n->nbinary.ch2); 118193799Swollman cmdputs("; done"); 118293799Swollman break; 1183149514Swollman case NFOR: 1184149514Swollman cmdputs("for "); 1185149514Swollman cmdputs(n->nfor.var); 1186149514Swollman cmdputs(" in ..."); 1187149514Swollman break; 1188149514Swollman case NCASE: 1189149514Swollman cmdputs("case "); 119093799Swollman cmdputs(n->ncase.expr->narg.text); 1191149514Swollman cmdputs(" in ..."); 1192149514Swollman break; 1193149514Swollman case NDEFUN: 1194149514Swollman cmdputs(n->narg.text); 1195149514Swollman cmdputs("() ..."); 1196149514Swollman break; 1197149514Swollman case NCMD: 1198149514Swollman for (np = n->ncmd.args ; np ; np = np->narg.next) { 1199149514Swollman cmdtxt(np); 1200149514Swollman if (np->narg.next) 1201114173Swollman cmdputs(" "); 12022742Swollman } 120393799Swollman for (np = n->ncmd.redirect ; np ; np = np->nfile.next) { 120493799Swollman cmdputs(" "); 120543014Swollman cmdtxt(np); 120693799Swollman } 120793799Swollman break; 120893799Swollman case NARG: 1209149514Swollman cmdputs(n->narg.text); 1210149514Swollman break; 121193799Swollman case NTO: 121293799Swollman p = ">"; i = 1; goto redir; 121393799Swollman case NAPPEND: 121493799Swollman p = ">>"; i = 1; goto redir; 121593799Swollman case NTOFD: 121693799Swollman p = ">&"; i = 1; goto redir; 121793799Swollman case NCLOBBER: 121893799Swollman p = ">|"; i = 1; goto redir; 121993799Swollman case NFROM: 1220149514Swollman p = "<"; i = 0; goto redir; 1221149514Swollman case NFROMTO: 122293799Swollman p = "<>"; i = 0; goto redir; 122320094Swollman case NFROMFD: 122493799Swollman p = "<&"; i = 0; goto redir; 122593799Swollmanredir: 122693799Swollman if (n->nfile.fd != i) { 122793799Swollman s[0] = n->nfile.fd + '0'; 122893799Swollman s[1] = '\0'; 122993799Swollman cmdputs(s); 1230149514Swollman } 1231149514Swollman cmdputs(p); 123293799Swollman if (n->type == NTOFD || n->type == NFROMFD) { 123393799Swollman if (n->ndup.dupfd >= 0) 123493799Swollman s[0] = n->ndup.dupfd + '0'; 123593799Swollman else 123693799Swollman s[0] = '-'; 123793799Swollman s[1] = '\0'; 123893799Swollman cmdputs(s); 123993799Swollman } else { 124093799Swollman cmdtxt(n->nfile.fname); 124193799Swollman } 1242121098Swollman break; 1243149514Swollman case NHERE: 1244149514Swollman case NXHERE: 124593799Swollman cmdputs("<<..."); 124693799Swollman break; 124793799Swollman default: 124893799Swollman cmdputs("???"); 124993799Swollman break; 125093799Swollman } 125193799Swollman} 125293799Swollman 125393799Swollman 1254149514Swollman 1255149514SwollmanSTATIC void 12562742Swollmancmdputs(char *s) 125767578Swollman{ 1258158421Swollman char *p, *q; 1259149514Swollman char c; 1260149514Swollman int subtype = 0; 1261149514Swollman 1262149514Swollman if (cmdnleft <= 0) 1263149514Swollman return; 1264149514Swollman p = s; 1265153670Swollman q = cmdnextc; 1266153670Swollman while ((c = *p++) != '\0') { 1267153670Swollman if (c == CTLESC) 1268149514Swollman *q++ = *p++; 126920094Swollman else if (c == CTLVAR) { 1270153670Swollman *q++ = '$'; 1271153670Swollman if (--cmdnleft > 0) 1272153670Swollman *q++ = '{'; 1273153670Swollman subtype = *p++; 12749908Swollman } else if (c == '=' && subtype != 0) { 12759908Swollman *q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL]; 127643014Swollman subtype = 0; 127719878Swollman } else if (c == CTLENDVAR) { 127858787Sru *q++ = '}'; 1279153670Swollman } else if (c == CTLBACKQ || c == CTLBACKQ+CTLQUOTE) 1280153670Swollman cmdnleft++; /* ignore it */ 12819908Swollman else 12822742Swollman *q++ = c; 12832742Swollman if (--cmdnleft <= 0) { 128443014Swollman *q++ = '.'; 12852742Swollman *q++ = '.'; 1286163302Sru *q++ = '.'; 1287163302Sru break; 1288163302Sru } 1289163302Sru } 1290163302Sru cmdnextc = q; 1291163302Sru} 12922742Swollman