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