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