1: /* $Header: /home/hyperion/mu/christos/src/sys/tcsh-6.00/RCS/sh.proc.c,v 3.1 1991/07/05 02:00:43 christos Exp $ */
   2: /*
   3:  * sh.proc.c: Job manipulations
   4:  */
   5: /*-
   6:  * Copyright (c) 1980, 1991 The Regents of the University of California.
   7:  * All rights reserved.
   8:  *
   9:  * Redistribution and use in source and binary forms, with or without
  10:  * modification, are permitted provided that the following conditions
  11:  * are met:
  12:  * 1. Redistributions of source code must retain the above copyright
  13:  *    notice, this list of conditions and the following disclaimer.
  14:  * 2. Redistributions in binary form must reproduce the above copyright
  15:  *    notice, this list of conditions and the following disclaimer in the
  16:  *    documentation and/or other materials provided with the distribution.
  17:  * 3. All advertising materials mentioning features or use of this software
  18:  *    must display the following acknowledgement:
  19:  *	This product includes software developed by the University of
  20:  *	California, Berkeley and its contributors.
  21:  * 4. Neither the name of the University nor the names of its contributors
  22:  *    may be used to endorse or promote products derived from this software
  23:  *    without specific prior written permission.
  24:  *
  25:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  26:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  27:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  28:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  29:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  30:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  31:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  32:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  33:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  34:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  35:  * SUCH DAMAGE.
  36:  */
  37: #include "config.h"
  38: #if !defined(lint) && !defined(pdp11)
  39: static char *rcsid()
  40:     { return "$Id: sh.proc.c,v 3.1 1991/07/05 02:00:43 christos Exp $"; }
  41: #endif
  42: 
  43: #include "sh.h"
  44: #include "ed.h"
  45: 
  46: /*
  47:  * a little complicated #include <sys/wait.h>! :-(
  48:  */
  49: #if SVID > 0
  50: # ifdef hpux
  51: #  ifndef __hpux
  52: #   include "tc.wait.h" /* 6.5 broke <sys/wait.h> */
  53: #  else
  54: #   ifndef POSIX
  55: #    define _BSD
  56: #   endif
  57: #   ifndef _CLASSIC_POSIX_TYPES
  58: #    define _CLASSIC_POSIX_TYPES
  59: #   endif
  60: #   include <sys/wait.h> /* 7.0 fixed it again */
  61: #  endif /* __hpux */
  62: # else /* hpux */
  63: #  if defined(OREO) || defined(IRIS4D) || defined(POSIX)
  64: #   include <sys/wait.h>
  65: #  else /* OREO || IRIS4D || POSIX */
  66: #   include "tc.wait.h"
  67: #  endif /* OREO || IRIS4D || POSIX */
  68: # endif	/* hpux */
  69: #else /* SVID == 0 */
  70: # include <sys/wait.h>
  71: #endif /* SVID == 0 */
  72: 
  73: #if !defined(NSIG) && defined(SIGMAX)
  74: # define NSIG (SIGMAX+1)
  75: #endif /* !NSIG && SIGMAX */
  76: 
  77: #ifdef aiws
  78: # undef HZ
  79: # define HZ 16
  80: #endif /* aiws */
  81: 
  82: #ifndef HZ
  83: # define HZ 100     /* for division into seconds */
  84: #endif
  85: 
  86: #ifndef WTERMSIG
  87: # define WTERMSIG(w)    (((union wait *) &(w))->w_termsig)
  88: # define BSDWAIT
  89: #endif /* !WTERMSIG */
  90: #ifndef WEXITSTATUS
  91: # define WEXITSTATUS(w) (((union wait *) &(w))->w_retcode)
  92: #endif /* !WEXITSTATUS */
  93: #ifndef WSTOPSIG
  94: # define WSTOPSIG(w)    (((union wait *) &(w))->w_stopsig)
  95: #endif /* WSTOPSIG */
  96: 
  97: #ifndef WCOREDUMP
  98: # ifdef BSDWAIT
  99: #  define WCOREDUMP(w)  (((union wait *) &(w))->w_coredump)
 100: # else /* !BSDWAIT */
 101: #  define WCOREDUMP(w)  ((w) & 0200)
 102: # endif /* !BSDWAIT */
 103: #endif /* !WCOREDUMP */
 104: 
 105: /*
 106:  * C Shell - functions that manage processes, handling hanging, termination
 107:  */
 108: 
 109: #define BIGINDEX    9   /* largest desirable job index */
 110: 
 111: #ifdef BSDTIMES
 112: # if defined(sun) || defined(hp9000)
 113: static struct rusage zru = {{0L, 0L}, {0L, 0L}, 0L, 0L, 0L, 0L,
 114:                 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L};
 115: 
 116: # else /* !sun && !hp9000 */
 117: #  ifdef masscomp
 118: /*
 119:  * Initialization of this structure under RTU 4.1A & RTU 5.0 is problematic
 120:  * because the first two elements are unions of a time_t and a struct timeval.
 121:  * So we'll just have to trust the loader to do the "right thing", DAS DEC-90.
 122:  */
 123: static struct rusage zru;
 124: #  else /* masscomp */
 125: static struct rusage zru = {{0L, 0L}, {0L, 0L}, 0, 0, 0, 0, 0, 0, 0,
 126:                 0, 0, 0, 0, 0, 0};
 127: #  endif /* masscomp */
 128: # endif	/* !sun && !hp9000 */
 129: #else /* ! BSDTIMES */
 130: # ifdef _SEQUENT_
 131: static struct pro_stats zru = {{0L, 0L}, {0L, 0L}, 0, 0, 0, 0, 0, 0, 0,
 132:                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 133: # else /* !_SEQUENT_ */
 134: static struct tms zru = {0L, 0L, 0L, 0L}, lru = {0L, 0L, 0L, 0L};
 135: # endif	/* !_SEQUENT_ */
 136: #endif /* !BSDTIMES */
 137: 
 138: #ifndef RUSAGE_CHILDREN
 139: # define    RUSAGE_CHILDREN -1
 140: #endif
 141: 
 142: static  void         pflushall  __P((void));
 143: static  void         pflush     __P((struct process *));
 144: static  void         pclrcurr   __P((struct process *));
 145: static  void         padd       __P((struct command *));
 146: static  int      pprint     __P((struct process *, int));
 147: static  void         ptprint    __P((struct process *));
 148: static  void         pads       __P((Char *));
 149: static  void         pkill      __P((Char **v, int));
 150: static  struct process  *pgetcurr   __P((struct process *));
 151: static  void         okpcntl    __P((void));
 152: 
 153: /*
 154:  * pchild - called at interrupt level by the SIGCHLD signal
 155:  *	indicating that at least one child has terminated or stopped
 156:  *	thus at least one wait system call will definitely return a
 157:  *	childs status.  Top level routines (like pwait) must be sure
 158:  *	to mask interrupts when playing with the proclist data structures!
 159:  */
 160: sigret_t
 161: /*ARGSUSED*/
 162: pchild(snum)
 163: int snum;
 164: {
 165:     register struct process *pp;
 166:     register struct process *fp;
 167:     register int pid;
 168:     extern int insource;
 169: #ifdef BSDWAIT
 170:     union wait w;
 171: #else /* !BSDWAIT */
 172:     int     w;
 173: #endif /* !BSDWAIT */
 174:     int     jobflags;
 175: #ifdef BSDTIMES
 176:     struct rusage ru;
 177: #else /* !BSDTIMES */
 178: # ifdef _SEQUENT_
 179:     struct pro_stats ru;
 180:     struct pro_stats cpst1, cpst2;
 181:     tmval_t tv;
 182: # else /* !_SEQUENT_ */
 183:     struct tms proctimes;
 184: 
 185:     if (!timesdone) {
 186:     timesdone++;
 187:     (void) times(&shtimes);
 188:     }
 189: # endif	/* _SEQUENT_ */
 190: #endif /* BSDTIMES */
 191: 
 192: #ifdef JOBDEBUG
 193:     xprintf("pchild()\n");
 194: #endif	/* JOBDEBUG */
 195: 
 196: /* Christos on where the signal(SIGCHLD, pchild) shoud be:
 197:  *
 198:  * I think that it should go *after* the wait, unlike most signal handlers.
 199:  *
 200:  * In release two (for which I have manuals), it says that wait will remove
 201:  * the first child from the queue of dead children.
 202:  * All the rest of the children that die while in the signal handler of the
 203:  * SIGC(H)LD, will be placed in the queue. If signal is called to re-establish
 204:  * the signal handler, and there are items in the queue, the process will
 205:  * receive another SIGC(H)LD before signal returns. BTW this is from the
 206:  * manual page on comp-sim... Maybe it is not applicable to the hp's, but
 207:  * I read on the news in comp.unix.wizards or comp.unix.questions yesterday
 208:  * that another person was claiming the the signal() call should be after
 209:  * the wait().
 210:  */
 211: 
 212: loop:
 213:     errno = 0;          /* reset, just in case */
 214: #ifdef JOBDEBUG
 215:     xprintf("Waiting...\n");
 216:     flush();
 217: #endif
 218: #ifdef BSDJOBS
 219: # ifdef BSDTIMES
 220:     /* both a wait3 and rusage */
 221: #  ifndef BSDWAIT
 222:     pid = wait3(&w,
 223:        (setintr && (intty || insource) ? WNOHANG | WUNTRACED : WNOHANG), &ru);
 224: #  else /* BSDWAIT */
 225:     pid = wait3(&w.w_status,
 226:        (setintr && (intty || insource) ? WNOHANG | WUNTRACED : WNOHANG), &ru);
 227: #  endif /* BSDWAIT */
 228: # else /* !BSDTIMES */
 229: #  ifdef _SEQUENT_
 230:     (void) get_pro_stats(&tv, PS_SELF, 0, &cpst1);
 231:     pid = waitpid(-1, &w,
 232:         (setintr && (intty || insource) ? WNOHANG | WUNTRACED : WNOHANG));
 233:     (void) get_pro_stats(&tv, PS_SELF, 0, &cpst2);
 234:     pr_stat_sub(&cpst2, &cpst1, &ru);
 235: #  else /* !_SEQUENT_ */
 236: #   ifndef POSIX
 237:     /* we have a wait3, but no rusage stuff */
 238:     pid = wait3(&w.w_status,
 239:      (setintr && (intty || insource) ? WNOHANG | WUNTRACED : WNOHANG), 0);
 240: #   else /* POSIX */
 241:     pid = waitpid(-1, &w,
 242:         (setintr && (intty || insource) ? WNOHANG | WUNTRACED : WNOHANG));
 243: #   endif /* POSIX */
 244: #  endif /* !_SEQUENT_ */
 245: # endif	/* !BSDTIMES */
 246: #else /* !BSDJOBS */
 247: # ifdef BSDTIMES
 248:     /* both a wait3 and rusage */
 249: #  ifdef hpux
 250:     pid = wait3(&w.w_status, WNOHANG, 0);
 251: #  else /* !hpux */
 252:     pid = wait3(&w.w_status, WNOHANG, &ru);
 253: #  endif /* !hpux */
 254: # else /* !BSDTIMES */
 255: #  if SVID < 3
 256:     /* no wait3, therefore no rusage */
 257:     /* on Sys V, this may hang.  I hope it's not going to be a problem */
 258:     pid = ourwait(&w.w_status);
 259: #  else /* SVID >= 3 */
 260:     /*
 261:      * XXX: for greater than 3 we should use waitpid().
 262:      * but then again, SVR4 falls into the POSIX/BSDJOBS category.
 263:      */
 264:     pid = wait(&w.w_status);
 265: #  endif /* SVID >= 3 */
 266: # endif	/* BSDTIMES */
 267: #endif /* BSDJOBS */
 268: 
 269: #ifdef JOBDEBUG
 270:     {
 271:     char    buffer[100];
 272:     xsprintf(buffer, "pid %d, retval %x termsig %x retcode %x\n",
 273:          pid, w, WTERMSIG(w), WEXITSTATUS(w));
 274:     xprintf(buffer);
 275:     flush();
 276:     }
 277: #endif /* JOBDEBUG */
 278: 
 279:     if (pid <= 0) {
 280: #ifdef JOBDEBUG
 281:     xprintf("errno == %d\n", errno);
 282: #endif
 283:     if (errno == EINTR) {
 284:         errno = 0;
 285:         goto loop;
 286:     }
 287:     pnoprocesses = pid == -1;
 288: #ifndef SIGVOID
 289:     return (0);
 290: #else /* !SIGVOID */
 291:     return;
 292: #endif /* SIGVOID */
 293:     }
 294:     for (pp = proclist.p_next; pp != PNULL; pp = pp->p_next)
 295:     if (pid == pp->p_pid)
 296:         goto found;
 297: #ifndef BSDJOBS
 298:     /* this should never have happened */
 299:     stderror(ERR_SYNC, pid);
 300:     xexit(0);
 301: #else /* BSDJOBS */
 302:     goto loop;
 303: #endif /* BSDJOBS */
 304: found:
 305:     if (pid == atoi(short2str(value(STRchild))))
 306:     unsetv(STRchild);
 307:     pp->p_flags &= ~(PRUNNING | PSTOPPED | PREPORTED);
 308:     if (WIFSTOPPED(w)) {
 309:     pp->p_flags |= PSTOPPED;
 310:     pp->p_reason = WSTOPSIG(w);
 311:     }
 312:     else {
 313:     if (pp->p_flags & (PTIME | PPTIME) || adrof(STRtime))
 314: #ifndef BSDTIMES
 315: # ifdef _SEQUENT_
 316:         (void) get_pro_stats(&pp->p_etime, PS_SELF, NULL, NULL);
 317: # else  /* !_SEQUENT_ */
 318:         pp->p_etime = times(&proctimes);
 319: # endif	/* !_SEQUENT_ */
 320: #else /* BSDTIMES */
 321:         (void) gettimeofday(&pp->p_etime, NULL);
 322: #endif /* BSDTIMES */
 323: 
 324: 
 325: #if defined(BSDTIMES) || defined(_SEQUENT_)
 326:     pp->p_rusage = ru;
 327: #else /* !BSDTIMES && !_SEQUENT_ */
 328:     (void) times(&proctimes);
 329:     pp->p_utime = proctimes.tms_cutime - shtimes.tms_cutime;
 330:     pp->p_stime = proctimes.tms_cstime - shtimes.tms_cstime;
 331:     shtimes = proctimes;
 332: #endif /* !BSDTIMES && !_SEQUENT_ */
 333:     if (WIFSIGNALED(w)) {
 334:         if (WTERMSIG(w) == SIGINT)
 335:         pp->p_flags |= PINTERRUPTED;
 336:         else
 337:         pp->p_flags |= PSIGNALED;
 338:         if (WCOREDUMP(w))
 339:         pp->p_flags |= PDUMPED;
 340:         pp->p_reason = WTERMSIG(w);
 341:     }
 342:     else {
 343:         pp->p_reason = WEXITSTATUS(w);
 344:         if (pp->p_reason != 0)
 345:         pp->p_flags |= PAEXITED;
 346:         else
 347:         pp->p_flags |= PNEXITED;
 348:     }
 349:     }
 350:     jobflags = 0;
 351:     fp = pp;
 352:     do {
 353:     if ((fp->p_flags & (PPTIME | PRUNNING | PSTOPPED)) == 0 &&
 354:         !child && adrof(STRtime) &&
 355: #ifdef BSDTIMES
 356:         fp->p_rusage.ru_utime.tv_sec + fp->p_rusage.ru_stime.tv_sec
 357: #else /* !BSDTIMES */
 358: # ifdef _SEQUENT_
 359:         fp->p_rusage.ps_utime.tv_sec + fp->p_rusage.ps_stime.tv_sec
 360: # else /* !_SEQUENT_ */
 361: #  ifndef POSIX
 362:         (fp->p_utime + fp->p_stime) / HZ
 363: #  else /* POSIX */
 364:         (fp->p_utime + fp->p_stime) / CLK_TCK
 365: #  endif /* POSIX */
 366: # endif /* !_SEQUENT_ */
 367: #endif /* !BSDTIMES */
 368:         >= atoi(short2str(value(STRtime))))
 369:         fp->p_flags |= PTIME;
 370:     jobflags |= fp->p_flags;
 371:     } while ((fp = fp->p_friends) != pp);
 372:     pp->p_flags &= ~PFOREGND;
 373:     if (pp == pp->p_friends && (pp->p_flags & PPTIME)) {
 374:     pp->p_flags &= ~PPTIME;
 375:     pp->p_flags |= PTIME;
 376:     }
 377:     if ((jobflags & (PRUNNING | PREPORTED)) == 0) {
 378:     fp = pp;
 379:     do {
 380:         if (fp->p_flags & PSTOPPED)
 381:         fp->p_flags |= PREPORTED;
 382:     } while ((fp = fp->p_friends) != pp);
 383:     while (fp->p_pid != fp->p_jobid)
 384:         fp = fp->p_friends;
 385:     if (jobflags & PSTOPPED) {
 386:         if (pcurrent && pcurrent != fp)
 387:         pprevious = pcurrent;
 388:         pcurrent = fp;
 389:     }
 390:     else
 391:         pclrcurr(fp);
 392:     if (jobflags & PFOREGND) {
 393:         if (jobflags & (PSIGNALED | PSTOPPED | PPTIME) ||
 394: #ifdef IIASA
 395:         jobflags & PAEXITED ||
 396: #endif /* IIASA */
 397:         !eq(dcwd->di_name, fp->p_cwd->di_name)) {
 398:         ;       /* print in pjwait */
 399:         }
 400:         /* PWP: print a newline after ^C */
 401:         else if (jobflags & PINTERRUPTED)
 402: #ifdef SHORT_STRINGS
 403:         xputchar('\r' | QUOTE), xputchar('\n');
 404: #else /* !SHORT_STRINGS */
 405:         xprintf("\215\n");  /* \215 is a quoted ^M */
 406: #endif /* !SHORT_STRINGS */
 407: #ifdef notdef
 408:         else if ((jobflags & (PTIME|PSTOPPED)) == PTIME)
 409:                 ptprint(fp);
 410: #endif
 411:     }
 412:     else {
 413:         if (jobflags & PNOTIFY || adrof(STRnotify)) {
 414: #ifdef SHORT_STRINGS
 415:         xputchar('\r' | QUOTE), xputchar('\n');
 416: #else /* !SHORT_STRINGS */
 417:         xprintf("\215\n");  /* \215 is a quoted ^M */
 418: #endif /* !SHORT_STRINGS */
 419:         (void) pprint(pp, NUMBER | NAME | REASON);
 420:         if ((jobflags & PSTOPPED) == 0)
 421:             pflush(pp);
 422:         {
 423:             extern Char GettingInput;
 424: 
 425:             if (GettingInput) {
 426:             errno = 0;
 427:             (void) Rawmode();
 428:             ClearLines();
 429:             ClearDisp();
 430:             Refresh();
 431:             }
 432:         }
 433:         }
 434:         else {
 435:         fp->p_flags |= PNEEDNOTE;
 436:         neednote++;
 437:         }
 438:     }
 439:     }
 440: #ifdef BSDJOBS
 441:     goto loop;
 442: #endif /* BSDJOBS */
 443: }
 444: 
 445: void
 446: pnote()
 447: {
 448:     register struct process *pp;
 449:     int     flags;
 450: #ifdef BSDSIGS
 451:     sigmask_t omask;
 452: #endif /* BSDSIGS */
 453: 
 454:     neednote = 0;
 455:     for (pp = proclist.p_next; pp != PNULL; pp = pp->p_next) {
 456:     if (pp->p_flags & PNEEDNOTE) {
 457: #ifdef BSDSIGS
 458:         omask = sigblock(sigmask(SIGCHLD));
 459: #else /* !BSDSIGS */
 460:         (void) sighold(SIGCHLD);
 461: #endif /* !BSDSIGS */
 462:         pp->p_flags &= ~PNEEDNOTE;
 463:         flags = pprint(pp, NUMBER | NAME | REASON);
 464:         if ((flags & (PRUNNING | PSTOPPED)) == 0)
 465:         pflush(pp);
 466: #ifdef BSDSIGS
 467:         (void) sigsetmask(omask);
 468: #else /* !BSDSIGS */
 469:         (void) sigrelse(SIGCHLD);
 470: #endif /* !BSDSIGS */
 471:     }
 472:     }
 473: }
 474: 
 475: /*
 476:  * pwait - wait for current job to terminate, maintaining integrity
 477:  *	of current and previous job indicators.
 478:  */
 479: void
 480: pwait()
 481: {
 482:     register struct process *fp, *pp;
 483: #ifdef BSDSIGS
 484:     sigmask_t omask;
 485: #endif /* BSDSIGS */
 486: 
 487:     /*
 488:      * Here's where dead procs get flushed.
 489:      */
 490: #ifdef BSDSIGS
 491:     omask = sigblock(sigmask(SIGCHLD));
 492: #else /* !BSDSIGS */
 493:     (void) sighold(SIGCHLD);
 494: #endif /* !BSDSIGS */
 495:     for (pp = (fp = &proclist)->p_next; pp != PNULL; pp = (fp = pp)->p_next)
 496:     if (pp->p_pid == 0) {
 497:         fp->p_next = pp->p_next;
 498:         xfree((ptr_t) pp->p_command);
 499:         if (pp->p_cwd && --pp->p_cwd->di_count == 0)
 500:         if (pp->p_cwd->di_next == 0)
 501:             dfree(pp->p_cwd);
 502:         xfree((ptr_t) pp);
 503:         pp = fp;
 504:     }
 505: #ifdef BSDSIGS
 506:     (void) sigsetmask(omask);
 507: #else /* !BSDSIGS */
 508:     (void) sigrelse(SIGCHLD);
 509: # ifdef notdef
 510:     if (setintr)
 511:     sigignore(SIGINT);
 512: # endif
 513: #endif /* !BSDSIGS */
 514:     pjwait(pcurrjob);
 515: }
 516: 
 517: 
 518: /*
 519:  * pjwait - wait for a job to finish or become stopped
 520:  *	It is assumed to be in the foreground state (PFOREGND)
 521:  */
 522: void
 523: pjwait(pp)
 524:     register struct process *pp;
 525: {
 526:     register struct process *fp;
 527:     int     jobflags, reason;
 528: #ifdef BSDSIGS
 529:     sigmask_t omask;
 530: #endif /* BSDSIGS */
 531: 
 532:     while (pp->p_pid != pp->p_jobid)
 533:     pp = pp->p_friends;
 534:     fp = pp;
 535: 
 536:     do {
 537:     if ((fp->p_flags & (PFOREGND | PRUNNING)) == PRUNNING)
 538:         xprintf("BUG: waiting for background job!\n");
 539:     } while ((fp = fp->p_friends) != pp);
 540:     /*
 541:      * Now keep pausing as long as we are not interrupted (SIGINT), and the
 542:      * target process, or any of its friends, are running
 543:      */
 544:     fp = pp;
 545: #ifdef BSDSIGS
 546:     omask = sigblock(sigmask(SIGCHLD));
 547: #endif /* BSDSIGS */
 548:     for (;;) {
 549: #ifndef BSDSIGS
 550:     (void) sighold(SIGCHLD);
 551: #endif /* !BSDSIGS */
 552:     jobflags = 0;
 553:     do
 554:         jobflags |= fp->p_flags;
 555:     while ((fp = (fp->p_friends)) != pp);
 556:     if ((jobflags & PRUNNING) == 0)
 557:         break;
 558: #ifdef JOBDEBUG
 559:     xprintf("starting to sigpause for  SIGCHLD on %d\n", fp->p_pid);
 560: #endif /* JOBDEBUG */
 561: #ifdef BSDSIGS
 562:     /* sigpause(sigblock((sigmask_t) 0) &~ sigmask(SIGCHLD)); */
 563:     (void) sigpause(omask & ~sigmask(SIGCHLD));
 564: #else /* !BSDSIGS */
 565:     (void) sigpause(SIGCHLD);
 566: #endif /* !BSDSIGS */
 567:     }
 568: #ifdef BSDSIGS
 569:     (void) sigsetmask(omask);
 570: #else /* !BSDSIGS */
 571:     (void) sigrelse(SIGCHLD);
 572: #endif /* !BSDSIGS */
 573: #ifdef BSDJOBS
 574:     if (tpgrp > 0)      /* get tty back */
 575:     (void) tcsetpgrp(FSHTTY, tpgrp);
 576: #endif /* BSDJOBS */
 577:     if ((jobflags & (PSIGNALED | PSTOPPED | PTIME)) ||
 578:     !eq(dcwd->di_name, fp->p_cwd->di_name)) {
 579:     if (jobflags & PSTOPPED) {
 580:         xprintf("\n");
 581:         if (adrof(STRl_jobs)) {
 582:         Char   *jobcommand[3];
 583: 
 584:         jobcommand[0] = STRjobs;
 585:         if (eq(value(STRl_jobs), STRlong))
 586:             jobcommand[1] = STRml;
 587:         else
 588:             jobcommand[1] = NULL;
 589:         jobcommand[2] = NULL;
 590: 
 591:         dojobs(jobcommand);
 592:         (void) pprint(pp, SHELLDIR);
 593:         }
 594:         else
 595:         (void) pprint(pp, AREASON | SHELLDIR);
 596:     }
 597:     else
 598:         (void) pprint(pp, AREASON | SHELLDIR);
 599:     }
 600:     if ((jobflags & (PINTERRUPTED | PSTOPPED)) && setintr &&
 601:     (!gointr || !eq(gointr, STRminus))) {
 602:     if ((jobflags & PSTOPPED) == 0)
 603:         pflush(pp);
 604:     pintr1(0);
 605:     /* NOTREACHED */
 606:     }
 607:     reason = 0;
 608:     fp = pp;
 609:     do {
 610:     if (fp->p_reason)
 611:         reason = fp->p_flags & (PSIGNALED | PINTERRUPTED) ?
 612:         fp->p_reason | META : fp->p_reason;
 613:     } while ((fp = fp->p_friends) != pp);
 614:     if ((reason != 0) && (adrof(STRprintexitvalue)))    /* PWP */
 615:     xprintf("Exit %d\n", reason);
 616:     set(STRstatus, putn(reason));
 617:     if (reason && exiterr)
 618:     exitstat();
 619:     pflush(pp);
 620:     /* cwd_cmd(); *//* (PWP) this is what pre_cmd is for! */
 621: }
 622: 
 623: /*
 624:  * dowait - wait for all processes to finish
 625:  */
 626: void
 627: dowait()
 628: {
 629:     register struct process *pp;
 630: #ifdef BSDSIGS
 631:     sigmask_t omask;
 632: #endif /* BSDSIGS */
 633: 
 634:     pjobs++;
 635: #ifdef BSDSIGS
 636:     omask = sigblock(sigmask(SIGCHLD));
 637: loop:
 638: #else /* !BSDSIGS */
 639:     if (setintr)
 640:     (void) sigrelse(SIGINT);
 641: loop:
 642:     (void) sighold(SIGCHLD);
 643: #endif /* !BSDSIGS */
 644:     for (pp = proclist.p_next; pp; pp = pp->p_next)
 645:     if (pp->p_pid &&    /* pp->p_pid == pp->p_jobid && */
 646:         pp->p_flags & PRUNNING) {
 647: #ifdef BSDSIGS
 648:         (void) sigpause((sigmask_t) 0);
 649: #else /* !BSDSIGS */
 650:         (void) sigpause(SIGCHLD);
 651: #endif /* !BSDSIGS */
 652:         goto loop;
 653:     }
 654: #ifdef BSDSIGS
 655:     (void) sigsetmask(omask);
 656: #else /* !BSDSIGS */
 657:     (void) sigrelse(SIGCHLD);
 658: #endif /* !BSDSIGS */
 659:     pjobs = 0;
 660: }
 661: 
 662: /*
 663:  * pflushall - flush all jobs from list (e.g. at fork())
 664:  */
 665: static void
 666: pflushall()
 667: {
 668:     register struct process *pp;
 669: 
 670:     for (pp = proclist.p_next; pp != PNULL; pp = pp->p_next)
 671:     if (pp->p_pid)
 672:         pflush(pp);
 673: }
 674: 
 675: /*
 676:  * pflush - flag all process structures in the same job as the
 677:  *	the argument process for deletion.  The actual free of the
 678:  *	space is not done here since pflush is called at interrupt level.
 679:  */
 680: static void
 681: pflush(pp)
 682:     register struct process *pp;
 683: {
 684:     register struct process *np;
 685:     register int idx;
 686: 
 687:     if (pp->p_pid == 0) {
 688:     xprintf("BUG: process flushed twice");
 689:     return;
 690:     }
 691:     while (pp->p_pid != pp->p_jobid)
 692:     pp = pp->p_friends;
 693:     pclrcurr(pp);
 694:     if (pp == pcurrjob)
 695:     pcurrjob = 0;
 696:     idx = pp->p_index;
 697:     np = pp;
 698:     do {
 699:     np->p_index = np->p_pid = 0;
 700:     np->p_flags &= ~PNEEDNOTE;
 701:     } while ((np = np->p_friends) != pp);
 702:     if (idx == pmaxindex) {
 703:     for (np = proclist.p_next, idx = 0; np; np = np->p_next)
 704:         if (np->p_index > idx)
 705:         idx = np->p_index;
 706:     pmaxindex = idx;
 707:     }
 708: }
 709: 
 710: /*
 711:  * pclrcurr - make sure the given job is not the current or previous job;
 712:  *	pp MUST be the job leader
 713:  */
 714: static void
 715: pclrcurr(pp)
 716:     register struct process *pp;
 717: {
 718:     if (pp == pcurrent)
 719:     if (pprevious != PNULL) {
 720:         pcurrent = pprevious;
 721:         pprevious = pgetcurr(pp);
 722:     }
 723:     else {
 724:         pcurrent = pgetcurr(pp);
 725:         pprevious = pgetcurr(pp);
 726:     }
 727:     else if (pp == pprevious)
 728:     pprevious = pgetcurr(pp);
 729: }
 730: 
 731: /* +4 here is 1 for '\0', 1 ea for << >& >> */
 732: static Char command[PMAXLEN + 4];
 733: static int cmdlen;
 734: static Char *cmdp;
 735: 
 736: /*
 737:  * palloc - allocate a process structure and fill it up.
 738:  *	an important assumption is made that the process is running.
 739:  */
 740: void
 741: palloc(pid, t)
 742:     int     pid;
 743:     register struct command *t;
 744: {
 745:     register struct process *pp;
 746:     int     i;
 747: 
 748:     pp = (struct process *) xcalloc(1, (size_t) sizeof(struct process));
 749:     pp->p_pid = pid;
 750:     pp->p_flags = t->t_dflg & F_AMPERSAND ? PRUNNING : PRUNNING | PFOREGND;
 751:     if (t->t_dflg & F_TIME)
 752:     pp->p_flags |= PPTIME;
 753:     cmdp = command;
 754:     cmdlen = 0;
 755:     padd(t);
 756:     *cmdp++ = 0;
 757:     if (t->t_dflg & F_PIPEOUT) {
 758:     pp->p_flags |= PPOU;
 759:     if (t->t_dflg & F_STDERR)
 760:         pp->p_flags |= PDIAG;
 761:     }
 762:     pp->p_command = Strsave(command);
 763:     if (pcurrjob) {
 764:     struct process *fp;
 765: 
 766:     /* careful here with interrupt level */
 767:     pp->p_cwd = 0;
 768:     pp->p_index = pcurrjob->p_index;
 769:     pp->p_friends = pcurrjob;
 770:     pp->p_jobid = pcurrjob->p_pid;
 771:     for (fp = pcurrjob; fp->p_friends != pcurrjob; fp = fp->p_friends);
 772:     fp->p_friends = pp;
 773:     }
 774:     else {
 775:     pcurrjob = pp;
 776:     pp->p_jobid = pid;
 777:     pp->p_friends = pp;
 778:     pp->p_cwd = dcwd;
 779:     dcwd->di_count++;
 780:     if (pmaxindex < BIGINDEX)
 781:         pp->p_index = ++pmaxindex;
 782:     else {
 783:         struct process *np;
 784: 
 785:         for (i = 1;; i++) {
 786:         for (np = proclist.p_next; np; np = np->p_next)
 787:             if (np->p_index == i)
 788:             goto tryagain;
 789:         pp->p_index = i;
 790:         if (i > pmaxindex)
 791:             pmaxindex = i;
 792:         break;
 793:     tryagain:;
 794:         }
 795:     }
 796:     if (pcurrent == PNULL)
 797:         pcurrent = pp;
 798:     else if (pprevious == PNULL)
 799:         pprevious = pp;
 800:     }
 801:     pp->p_next = proclist.p_next;
 802:     proclist.p_next = pp;
 803: #ifdef BSDTIMES
 804:     (void) gettimeofday(&pp->p_btime, NULL);
 805: #else /* !BSDTIMES */
 806: # ifdef _SEQUENT_
 807:     (void) get_pro_stats(&pp->p_btime, PS_SELF, NULL, NULL);
 808: # else /* !_SEQUENT_ */
 809:     {
 810:     struct tms tmptimes;
 811: 
 812:     pp->p_btime = times(&tmptimes);
 813:     }
 814: # endif /* !_SEQUENT_ */
 815: #endif /* !BSDTIMES */
 816: }
 817: 
 818: static void
 819: padd(t)
 820:     register struct command *t;
 821: {
 822:     Char  **argp;
 823: 
 824:     if (t == 0)
 825:     return;
 826:     switch (t->t_dtyp) {
 827: 
 828:     case NODE_PAREN:
 829:     pads(STRL_parensp);
 830:     padd(t->t_dspr);
 831:     pads(STRspRparen);
 832:     break;
 833: 
 834:     case NODE_COMMAND:
 835:     for (argp = t->t_dcom; *argp; argp++) {
 836:         pads(*argp);
 837:         if (argp[1])
 838:         pads(STRspace);
 839:     }
 840:     break;
 841: 
 842:     case NODE_OR:
 843:     case NODE_AND:
 844:     case NODE_PIPE:
 845:     case NODE_LIST:
 846:     padd(t->t_dcar);
 847:     switch (t->t_dtyp) {
 848:     case NODE_OR:
 849:         pads(STRsP2sp);
 850:         break;
 851:     case NODE_AND:
 852:         pads(STRspand2sp);
 853:         break;
 854:     case NODE_PIPE:
 855:         pads(STRsPsp);
 856:         break;
 857:     case NODE_LIST:
 858:         pads(STRsmsp);
 859:         break;
 860:     }
 861:     padd(t->t_dcdr);
 862:     return;
 863:     }
 864:     if ((t->t_dflg & F_PIPEIN) == 0 && t->t_dlef) {
 865:     pads((t->t_dflg & F_READ) ? STRspL_arrow2sp : STRspLarrowsp);
 866:     pads(t->t_dlef);
 867:     }
 868:     if ((t->t_dflg & F_PIPEOUT) == 0 && t->t_drit) {
 869:     pads((t->t_dflg & F_APPEND) ? STRspR_arrow2 : STRspRarrow);
 870:     if (t->t_dflg & F_STDERR)
 871:         pads(STRand);
 872:     pads(STRspace);
 873:     pads(t->t_drit);
 874:     }
 875: }
 876: 
 877: static void
 878: pads(cp)
 879:     Char   *cp;
 880: {
 881:     register int i;
 882: 
 883:     /*
 884:      * Avoid the Quoted Space alias hack! Reported by:
 885:      * sam@john-bigboote.ICS.UCI.EDU (Sam Horrocks)
 886:      */
 887:     if (cp[0] == STRQNULL[0])
 888:     cp++;
 889: 
 890:     i = Strlen(cp);
 891: 
 892:     if (cmdlen >= PMAXLEN)
 893:     return;
 894:     if (cmdlen + i >= PMAXLEN) {
 895:     (void) Strcpy(cmdp, STRsp3dots);
 896:     cmdlen = PMAXLEN;
 897:     cmdp += 4;
 898:     return;
 899:     }
 900:     (void) Strcpy(cmdp, cp);
 901:     cmdp += i;
 902:     cmdlen += i;
 903: }
 904: 
 905: /*
 906:  * psavejob - temporarily save the current job on a one level stack
 907:  *	so another job can be created.  Used for { } in exp6
 908:  *	and `` in globbing.
 909:  */
 910: void
 911: psavejob()
 912: {
 913:     pholdjob = pcurrjob;
 914:     pcurrjob = PNULL;
 915: }
 916: 
 917: /*
 918:  * prestjob - opposite of psavejob.  This may be missed if we are interrupted
 919:  *	somewhere, but pendjob cleans up anyway.
 920:  */
 921: void
 922: prestjob()
 923: {
 924:     pcurrjob = pholdjob;
 925:     pholdjob = PNULL;
 926: }
 927: 
 928: /*
 929:  * pendjob - indicate that a job (set of commands) has been completed
 930:  *	or is about to begin.
 931:  */
 932: void
 933: pendjob()
 934: {
 935:     register struct process *pp, *tp;
 936: 
 937:     if (pcurrjob && (pcurrjob->p_flags & (PFOREGND | PSTOPPED)) == 0) {
 938:     pp = pcurrjob;
 939:     while (pp->p_pid != pp->p_jobid)
 940:         pp = pp->p_friends;
 941:     xprintf("[%d]", pp->p_index);
 942:     tp = pp;
 943:     do {
 944:         xprintf(" %d", pp->p_pid);
 945:         pp = pp->p_friends;
 946:     } while (pp != tp);
 947:     xprintf("\n");
 948:     }
 949:     pholdjob = pcurrjob = 0;
 950: }
 951: 
 952: /*
 953:  * pprint - print a job
 954:  */
 955: static int
 956: pprint(pp, flag)
 957:     register struct process *pp;
 958:     bool    flag;
 959: {
 960:     register status, reason;
 961:     struct process *tp;
 962:     extern char *linp, linbuf[];
 963:     int     jobflags, pstatus;
 964:     char   *format;
 965: 
 966:     while (pp->p_pid != pp->p_jobid)
 967:     pp = pp->p_friends;
 968:     if (pp == pp->p_friends && (pp->p_flags & PPTIME)) {
 969:     pp->p_flags &= ~PPTIME;
 970:     pp->p_flags |= PTIME;
 971:     }
 972:     tp = pp;
 973:     status = reason = -1;
 974:     jobflags = 0;
 975:     do {
 976:     jobflags |= pp->p_flags;
 977:     pstatus = pp->p_flags & PALLSTATES;
 978:     if (tp != pp && linp != linbuf && !(flag & FANCY) &&
 979:         (pstatus == status && pp->p_reason == reason ||
 980:          !(flag & REASON)))
 981:         xprintf(" ");
 982:     else {
 983:         if (tp != pp && linp != linbuf)
 984:         xprintf("\n");
 985:         if (flag & NUMBER)
 986:         if (pp == tp)
 987:             xprintf("[%d]%s %c ", pp->p_index,
 988:                 pp->p_index < 10 ? " " : "",
 989:                 pp == pcurrent ? '+' :
 990:                 (pp == pprevious ? '-' : ' '));
 991:         else
 992:             xprintf("       ");
 993:         if (flag & FANCY) {
 994: #ifdef TCF
 995:         extern char *sitename();
 996: 
 997: #endif /* TCF */
 998:         xprintf("%5d ", pp->p_pid);
 999: #ifdef TCF
1000:         xprintf("%11s ", sitename(pp->p_pid));
1001: #endif /* TCF */
1002:         }
1003:         if (flag & (REASON | AREASON)) {
1004:         if (flag & NAME)
1005: #ifdef SUSPENDED
1006:             format = "%-23s";
1007: #else /* !SUSPENDED */
1008:             format = "%-21s";
1009: #endif /* !SUSPENDED */
1010:         else
1011:             format = "%s";
1012:         if (pstatus == status)
1013:             if (pp->p_reason == reason) {
1014:             xprintf(format, "");
1015:             goto prcomd;
1016:             }
1017:             else
1018:             reason = pp->p_reason;
1019:         else {
1020:             status = pstatus;
1021:             reason = pp->p_reason;
1022:         }
1023:         switch (status) {
1024: 
1025:         case PRUNNING:
1026:             xprintf(format, "Running ");
1027:             break;
1028: 
1029:         case PINTERRUPTED:
1030:         case PSTOPPED:
1031:         case PSIGNALED:
1032:             /*
1033: 		     * tell what happened to the background job
1034: 		     * From: Michael Schroeder
1035: 		     * <mlschroe@immd4.informatik.uni-erlangen.de>
1036: 		     */
1037:             if ((flag & REASON)
1038:             || ((flag & AREASON)
1039:                 && reason != SIGINT
1040:                 && (reason != SIGPIPE
1041:                 || (pp->p_flags & PPOU) == 0)))
1042:             xprintf(format, mesg[pp->p_reason].pname);
1043:             else
1044:             reason = -1;
1045:             break;
1046: 
1047:         case PNEXITED:
1048:         case PAEXITED:
1049:             if (flag & REASON)
1050:             if (pp->p_reason)
1051: #ifdef SUSPENDED
1052:                 xprintf("Exit %-18d", pp->p_reason);
1053: #else /* SUSPENDED */
1054:                 xprintf("Exit %-16d", pp->p_reason);
1055: #endif /* SUSPENDED */
1056:             else
1057:                 xprintf(format, "Done");
1058:             break;
1059: 
1060:         default:
1061:             xprintf("BUG: status=%-9o", status);
1062:         }
1063:         }
1064:     }
1065: prcomd:
1066:     if (flag & NAME) {
1067:         xprintf("%s", short2str(pp->p_command));
1068:         if (pp->p_flags & PPOU)
1069:         xprintf(" |");
1070:         if (pp->p_flags & PDIAG)
1071:         xprintf("&");
1072:     }
1073:     if (flag & (REASON | AREASON) && pp->p_flags & PDUMPED)
1074:         xprintf(" (core dumped)");
1075:     if (tp == pp->p_friends) {
1076:         if (flag & AMPERSAND)
1077:         xprintf(" &");
1078:         if (flag & JOBDIR &&
1079:         !eq(tp->p_cwd->di_name, dcwd->di_name)) {
1080:         xprintf(" (wd: ");
1081:         dtildepr(value(STRhome), tp->p_cwd->di_name);
1082:         xprintf(")");
1083:         }
1084:     }
1085:     if (pp->p_flags & PPTIME && !(status & (PSTOPPED | PRUNNING))) {
1086:         if (linp != linbuf)
1087:         xprintf("\n\t");
1088: #if defined(BSDTIMES) || defined(_SEQUENT_)
1089:         prusage(&zru, &pp->p_rusage, &pp->p_etime,
1090:             &pp->p_btime);
1091: #else /* !BSDTIMES && !SEQUENT */
1092:         lru.tms_utime = pp->p_utime;
1093:         lru.tms_stime = pp->p_stime;
1094:         lru.tms_cutime = 0;
1095:         lru.tms_cstime = 0;
1096:         prusage(&zru, &lru, pp->p_etime,
1097:             pp->p_btime);
1098: #endif /* !BSDTIMES && !SEQUENT */
1099: 
1100:     }
1101:     if (tp == pp->p_friends) {
1102:         if (linp != linbuf)
1103:         xprintf("\n");
1104:         if (flag & SHELLDIR && !eq(tp->p_cwd->di_name, dcwd->di_name)) {
1105:         xprintf("(wd now: ");
1106:         dtildepr(value(STRhome), dcwd->di_name);
1107:         xprintf(")\n");
1108:         }
1109:     }
1110:     } while ((pp = pp->p_friends) != tp);
1111:     if (jobflags & PTIME && (jobflags & (PSTOPPED | PRUNNING)) == 0) {
1112:     if (jobflags & NUMBER)
1113:         xprintf("       ");
1114:     ptprint(tp);
1115:     }
1116:     return (jobflags);
1117: }
1118: 
1119: static void
1120: ptprint(tp)
1121:     register struct process *tp;
1122: {
1123: #ifdef BSDTIMES
1124:     struct timeval tetime, diff;
1125:     static struct timeval ztime;
1126:     struct rusage ru;
1127:     static struct rusage zru;
1128:     register struct process *pp = tp;
1129: 
1130:     ru = zru;
1131:     tetime = ztime;
1132:     do {
1133:     ruadd(&ru, &pp->p_rusage);
1134:     tvsub(&diff, &pp->p_etime, &pp->p_btime);
1135:     if (timercmp(&diff, &tetime, >))
1136:         tetime = diff;
1137:     } while ((pp = pp->p_friends) != tp);
1138:     prusage(&zru, &ru, &tetime, &ztime);
1139: #else /* !BSDTIMES */
1140: # ifdef _SEQUENT_
1141: #  define timercmp(tvp, uvp, cmp) \
1142:       ((tvp)->tv_sec cmp (uvp)->tv_sec || \
1143:        (tvp)->tv_sec == (uvp)->tv_sec && (tvp)->tv_usec cmp (uvp)->tv_usec)
1144:     tmval_t tetime, diff;
1145:     static tmval_t ztime;
1146:     struct pro_stats ru;
1147:     static struct pro_stats zru;
1148:     register struct process *pp = tp;
1149: 
1150:     ru = zru;
1151:     tetime = ztime;
1152:     do {
1153:     ruadd(&ru, &pp->p_rusage);
1154:     tvsub(&diff, &pp->p_etime, &pp->p_btime);
1155:     if (timercmp(&diff, &tetime, >))
1156:         tetime = diff;
1157:     } while ((pp = pp->p_friends) != tp);
1158:     prusage(&zru, &ru, &tetime, &ztime);
1159: # else /* !_SEQUENT_ */
1160: #  ifndef POSIX
1161:     static time_t ztime = 0;
1162:     static time_t zu_time = 0;
1163:     static time_t zs_time = 0;
1164:     time_t  tetime, diff;
1165:     time_t  u_time, s_time;
1166: 
1167: #  else /* POSIX */
1168:     static clock_t ztime = 0;
1169:     static clock_t zu_time = 0;
1170:     static clock_t zs_time = 0;
1171:     clock_t tetime, diff;
1172:     clock_t u_time, s_time;
1173: 
1174: #  endif /* POSIX */
1175:     struct tms zts, rts;
1176:     register struct process *pp = tp;
1177: 
1178:     u_time = zu_time;
1179:     s_time = zs_time;
1180:     tetime = ztime;
1181:     do {
1182:     u_time += pp->p_utime;
1183:     s_time += pp->p_stime;
1184:     diff = pp->p_etime - pp->p_btime;
1185:     if (diff > tetime)
1186:         tetime = diff;
1187:     } while ((pp = pp->p_friends) != tp);
1188:     zts.tms_utime = zu_time;
1189:     zts.tms_stime = zs_time;
1190:     zts.tms_cutime = 0;
1191:     zts.tms_cstime = 0;
1192:     rts.tms_utime = u_time;
1193:     rts.tms_stime = s_time;
1194:     rts.tms_cutime = 0;
1195:     rts.tms_cstime = 0;
1196:     prusage(&zts, &rts, tetime, ztime);
1197: # endif /* !_SEQUENT_ */
1198: #endif	/* !BSDTIMES */
1199: }
1200: 
1201: /*
1202:  * dojobs - print all jobs
1203:  */
1204: void
1205: dojobs(v)
1206:     Char  **v;
1207: {
1208:     register struct process *pp;
1209:     register int flag = NUMBER | NAME | REASON;
1210:     int     i;
1211: 
1212:     if (chkstop)
1213:     chkstop = 2;
1214:     if (*++v) {
1215:     if (v[1] || !eq(*v, STRml))
1216:         stderror(ERR_JOBS);
1217:     flag |= FANCY | JOBDIR;
1218:     }
1219:     for (i = 1; i <= pmaxindex; i++)
1220:     for (pp = proclist.p_next; pp; pp = pp->p_next)
1221:         if (pp->p_index == i && pp->p_pid == pp->p_jobid) {
1222:         pp->p_flags &= ~PNEEDNOTE;
1223:         if (!(pprint(pp, flag) & (PRUNNING | PSTOPPED)))
1224:             pflush(pp);
1225:         break;
1226:         }
1227: }
1228: 
1229: /*
1230:  * dofg - builtin - put the job into the foreground
1231:  */
1232: void
1233: dofg(v)
1234:     Char  **v;
1235: {
1236:     register struct process *pp;
1237: 
1238:     okpcntl();
1239:     ++v;
1240:     do {
1241:     pp = pfind(*v);
1242:     pstart(pp, 1);
1243: #ifndef BSDSIGS
1244: # ifdef notdef
1245:     if (setintr)
1246:         sigignore(SIGINT);
1247: # endif
1248: #endif /* !BSDSIGS */
1249:     pjwait(pp);
1250:     } while (*v && *++v);
1251: }
1252: 
1253: /*
1254:  * %... - builtin - put the job into the foreground
1255:  */
1256: void
1257: dofg1(v)
1258:     Char  **v;
1259: {
1260:     register struct process *pp;
1261: 
1262:     okpcntl();
1263:     pp = pfind(v[0]);
1264:     pstart(pp, 1);
1265: #ifndef BSDSIGS
1266: # ifdef notdef
1267:     if (setintr)
1268:     sigignore(SIGINT);
1269: # endif
1270: #endif /* !BSDSIGS */
1271:     pjwait(pp);
1272: }
1273: 
1274: /*
1275:  * dobg - builtin - put the job into the background
1276:  */
1277: void
1278: dobg(v)
1279:     Char  **v;
1280: {
1281:     register struct process *pp;
1282: 
1283:     okpcntl();
1284:     ++v;
1285:     do {
1286:     pp = pfind(*v);
1287:     pstart(pp, 0);
1288:     } while (*v && *++v);
1289: }
1290: 
1291: /*
1292:  * %... & - builtin - put the job into the background
1293:  */
1294: void
1295: dobg1(v)
1296:     Char  **v;
1297: {
1298:     register struct process *pp;
1299: 
1300:     pp = pfind(v[0]);
1301:     pstart(pp, 0);
1302: }
1303: 
1304: /*
1305:  * dostop - builtin - stop the job
1306:  */
1307: void
1308: dostop(v)
1309:     Char  **v;
1310: {
1311: #ifdef BSDJOBS
1312:     pkill(++v, SIGSTOP);
1313: #endif /* BSDJOBS */
1314: }
1315: 
1316: /*
1317:  * dokill - builtin - superset of kill (1)
1318:  */
1319: void
1320: dokill(v)
1321:     Char  **v;
1322: {
1323:     register int signum, len = 0;
1324:     register char *name;
1325:     extern int T_Cols;
1326: 
1327:     v++;
1328:     if (v[0] && v[0][0] == '-') {
1329:     if (v[0][1] == 'l') {
1330:         for (signum = 1; signum <= NSIG; signum++) {
1331:         if ((name = mesg[signum].iname) != NULL) {
1332:             len += strlen(name) + 1;
1333:             if (len >= T_Cols - 1) {
1334:             xprintf("\n");
1335:             len = strlen(name) + 1;
1336:             }
1337:             xprintf("%s ", name);
1338:         }
1339:         }
1340:         xprintf("\n");
1341:         return;
1342:     }
1343:     if (Isdigit(v[0][1])) {
1344:         signum = atoi(short2str(v[0] + 1));
1345:         if (signum < 0 || signum > NSIG)
1346:         stderror(ERR_NAME | ERR_BADSIG);
1347:     }
1348:     else {
1349:         for (signum = 1; signum <= NSIG; signum++)
1350:         if (mesg[signum].iname &&
1351:             eq(&v[0][1], str2short(mesg[signum].iname)))
1352:             goto gotsig;
1353:         setname(short2str(&v[0][1]));
1354:         stderror(ERR_NAME | ERR_UNKSIG);
1355:     }
1356: gotsig:
1357:     v++;
1358:     }
1359:     else
1360:     signum = SIGTERM;
1361:     pkill(v, signum);
1362: }
1363: 
1364: static void
1365: pkill(v, signum)
1366:     Char  **v;
1367:     int     signum;
1368: {
1369:     register struct process *pp, *np;
1370:     register int jobflags = 0;
1371:     int     pid, err1 = 0;
1372: #ifdef BSDSIGS
1373:     sigmask_t omask;
1374: #endif /* BSDSIGS */
1375:     Char   *cp;
1376: 
1377: #ifdef BSDSIGS
1378:     omask = sigmask(SIGCHLD);
1379:     if (setintr)
1380:     omask |= sigmask(SIGINT);
1381:     omask = sigblock(omask) & ~omask;
1382: #else /* !BSDSIGS */
1383:     if (setintr)
1384:     (void) sighold(SIGINT);
1385:     (void) sighold(SIGCHLD);
1386: #endif /* !BSDSIGS */
1387:     gflag = 0, tglob(v);
1388:     if (gflag) {
1389:     v = globall(v);
1390:     if (v == 0)
1391:         stderror(ERR_NAME | ERR_NOMATCH);
1392:     }
1393:     else {
1394:     v = gargv = saveblk(v);
1395:     trim(v);
1396:     }
1397: 
1398:     while (v && (cp = *v)) {
1399:     if (*cp == '%') {
1400:         np = pp = pfind(cp);
1401:         do
1402:         jobflags |= np->p_flags;
1403:         while ((np = np->p_friends) != pp);
1404: #ifdef BSDJOBS
1405:         switch (signum) {
1406: 
1407:         case SIGSTOP:
1408:         case SIGTSTP:
1409:         case SIGTTIN:
1410:         case SIGTTOU:
1411:         if ((jobflags & PRUNNING) == 0) {
1412: # ifdef SUSPENDED
1413:             xprintf("%s: Already suspended\n",
1414:                 short2str(cp));
1415: # else /* !SUSPENDED */
1416:             xprintf("%s: Already stopped\n",
1417:                 short2str(cp));
1418: # endif /* !SUSPENDED */
1419:             err1++;
1420:             goto cont;
1421:         }
1422:         break;
1423:         /*
1424: 		 * suspend a process, kill -CONT %, then type jobs; the shell
1425: 		 * says it is suspended, but it is running; thanks jaap..
1426: 		 */
1427:         case SIGCONT:
1428:         pstart(pp, 0);
1429:         goto cont;
1430:         }
1431: #endif /* BSDJOBS */
1432:         if (killpg((pid_t) pp->p_jobid, signum) < 0) {
1433:         xprintf("%s: %s\n", short2str(cp), strerror(errno));
1434:         err1++;
1435:         }
1436: #ifdef BSDJOBS
1437:         if (signum == SIGTERM || signum == SIGHUP)
1438:         (void) killpg((pid_t) pp->p_jobid, SIGCONT);
1439: #endif /* BSDJOBS */
1440:     }
1441:     else if (!(Isdigit(*cp) || *cp == '-'))
1442:         stderror(ERR_NAME | ERR_JOBARGS);
1443:     else {
1444:         pid = atoi(short2str(cp));
1445:         if (kill((pid_t) pid, signum) < 0) {
1446:         xprintf("%d: %s\n", pid, strerror(errno));
1447:         err1++;
1448:         goto cont;
1449:         }
1450: #ifdef BSDJOBS
1451:         if (signum == SIGTERM || signum == SIGHUP)
1452:         (void) kill((pid_t) pid, SIGCONT);
1453: #endif /* BSDJOBS */
1454:     }
1455: cont:
1456:     v++;
1457:     }
1458:     if (gargv)
1459:     blkfree(gargv), gargv = 0;
1460: #ifdef BSDSIGS
1461:     (void) sigsetmask(omask);
1462: #else /* !BSDSIGS */
1463:     (void) sigrelse(SIGCHLD);
1464:     if (setintr)
1465:     (void) sigrelse(SIGINT);
1466: #endif /* !BSDSIGS */
1467:     if (err1)
1468:     stderror(ERR_SILENT);
1469: }
1470: 
1471: /*
1472:  * pstart - start the job in foreground/background
1473:  */
1474: void
1475: pstart(pp, foregnd)
1476:     register struct process *pp;
1477:     int     foregnd;
1478: {
1479:     register struct process *np;
1480: #ifdef BSDSIGS
1481:     sigmask_t omask;
1482: #endif /* BSDSIGS */
1483:     long    jobflags = 0;
1484: 
1485: #ifdef BSDSIGS
1486:     omask = sigblock(sigmask(SIGCHLD));
1487: #else /* !BSDSIGS */
1488:     (void) sighold(SIGCHLD);
1489: #endif
1490:     np = pp;
1491:     do {
1492:     jobflags |= np->p_flags;
1493:     if (np->p_flags & (PRUNNING | PSTOPPED)) {
1494:         np->p_flags |= PRUNNING;
1495:         np->p_flags &= ~PSTOPPED;
1496:         if (foregnd)
1497:         np->p_flags |= PFOREGND;
1498:         else
1499:         np->p_flags &= ~PFOREGND;
1500:     }
1501:     } while ((np = np->p_friends) != pp);
1502:     if (!foregnd)
1503:     pclrcurr(pp);
1504:     (void) pprint(pp, foregnd ? NAME | JOBDIR : NUMBER | NAME | AMPERSAND);
1505: #ifdef BSDJOBS
1506:     if (foregnd)
1507:     (void) tcsetpgrp(FSHTTY, pp->p_jobid);
1508:     if (jobflags & PSTOPPED)
1509:     (void) killpg((pid_t) pp->p_jobid, SIGCONT);
1510: #endif /* BSDJOBS */
1511: #ifdef BSDSIGS
1512:     (void) sigsetmask(omask);
1513: #else /* !BSDSIGS */
1514:     (void) sigrelse(SIGCHLD);
1515: #endif /* !BSDSIGS */
1516: }
1517: 
1518: void
1519: panystop(neednl)
1520:     bool    neednl;
1521: {
1522:     register struct process *pp;
1523: 
1524:     chkstop = 2;
1525:     for (pp = proclist.p_next; pp; pp = pp->p_next)
1526:     if (pp->p_flags & PSTOPPED)
1527:         stderror(ERR_STOPPED, neednl ? "\n" : "");
1528: }
1529: 
1530: struct process *
1531: pfind(cp)
1532:     Char   *cp;
1533: {
1534:     register struct process *pp, *np;
1535: 
1536:     if (cp == 0 || cp[1] == 0 || eq(cp, STRc_2) || eq(cp, STRc_plus)) {
1537:     if (pcurrent == PNULL)
1538:         stderror(ERR_NAME | ERR_JOBCUR);
1539:     return (pcurrent);
1540:     }
1541:     if (eq(cp, STRc_minus) || eq(cp, STRc_hash)) {
1542:     if (pprevious == PNULL)
1543:         stderror(ERR_NAME | ERR_JOBPREV);
1544:     return (pprevious);
1545:     }
1546:     if (Isdigit(cp[1])) {
1547:     int     idx = atoi(short2str(cp + 1));
1548: 
1549:     for (pp = proclist.p_next; pp; pp = pp->p_next)
1550:         if (pp->p_index == idx && pp->p_pid == pp->p_jobid)
1551:         return (pp);
1552:     stderror(ERR_NAME | ERR_NOSUCHJOB);
1553:     }
1554:     np = PNULL;
1555:     for (pp = proclist.p_next; pp; pp = pp->p_next)
1556:     if (pp->p_pid == pp->p_jobid) {
1557:         if (cp[1] == '?') {
1558:         register Char *dp;
1559: 
1560:         for (dp = pp->p_command; *dp; dp++) {
1561:             if (*dp != cp[2])
1562:             continue;
1563:             if (prefix(cp + 2, dp))
1564:             goto match;
1565:         }
1566:         }
1567:         else if (prefix(cp + 1, pp->p_command)) {
1568:     match:
1569:         if (np)
1570:             stderror(ERR_NAME | ERR_AMBIG);
1571:         np = pp;
1572:         }
1573:     }
1574:     if (np)
1575:     return (np);
1576:     stderror(ERR_NAME | cp[1] == '?' ? ERR_JOBPAT : ERR_NOSUCHJOB);
1577:     /* NOTREACHED */
1578:     return (0);
1579: }
1580: 
1581: 
1582: /*
1583:  * pgetcurr - find most recent job that is not pp, preferably stopped
1584:  */
1585: static struct process *
1586: pgetcurr(pp)
1587:     register struct process *pp;
1588: {
1589:     register struct process *np;
1590:     register struct process *xp = PNULL;
1591: 
1592:     for (np = proclist.p_next; np; np = np->p_next)
1593:     if (np != pcurrent && np != pp && np->p_pid &&
1594:         np->p_pid == np->p_jobid) {
1595:         if (np->p_flags & PSTOPPED)
1596:         return (np);
1597:         if (xp == PNULL)
1598:         xp = np;
1599:     }
1600:     return (xp);
1601: }
1602: 
1603: /*
1604:  * donotify - flag the job so as to report termination asynchronously
1605:  */
1606: void
1607: donotify(v)
1608:     Char  **v;
1609: {
1610:     register struct process *pp;
1611: 
1612:     pp = pfind(*++v);
1613:     pp->p_flags |= PNOTIFY;
1614: }
1615: 
1616: /*
1617:  * Do the fork and whatever should be done in the child side that
1618:  * should not be done if we are not forking at all (like for simple builtin's)
1619:  * Also do everything that needs any signals fiddled with in the parent side
1620:  *
1621:  * Wanttty tells whether process and/or tty pgrps are to be manipulated:
1622:  *	-1:	leave tty alone; inherit pgrp from parent
1623:  *	 0:	already have tty; manipulate process pgrps only
1624:  *	 1:	want to claim tty; manipulate process and tty pgrps
1625:  * It is usually just the value of tpgrp.
1626:  */
1627: 
1628: int
1629: pfork(t, wanttty)
1630:     struct command *t;      /* command we are forking for */
1631:     int     wanttty;
1632: {
1633:     register int pid;
1634:     bool    ignint = 0;
1635:     int     pgrp;
1636: #ifdef BSDSIGS
1637:     sigmask_t omask;
1638: #endif /* BSDSIGS */
1639: #if SIGSYNCH
1640:     sigvec_t osv;
1641:     static sigvec_t nsv = {synch_handler, ~0, 0};
1642: #endif /* SIGSYNCH */
1643: 
1644:     /*
1645:      * A child will be uninterruptible only under very special conditions.
1646:      * Remember that the semantics of '&' is implemented by disconnecting the
1647:      * process from the tty so signals do not need to ignored just for '&'.
1648:      * Thus signals are set to default action for children unless: we have had
1649:      * an "onintr -" (then specifically ignored) we are not playing with
1650:      * signals (inherit action)
1651:      */
1652:     if (setintr)
1653:     ignint = (tpgrp == -1 && (t->t_dflg & F_NOINTERRUPT))
1654:         || (gointr && eq(gointr, STRminus));
1655:     /*
1656:      * Check for maximum nesting of 16 processes to avoid Forking loops
1657:      */
1658:     if (child == 16)
1659:     stderror(ERR_NESTING, 16);
1660:     /*
1661:      * Hold SIGCHLD until we have the process installed in our table.
1662:      */
1663: #ifdef SIGSYNCH
1664:     if (mysigvec(SIGSYNCH, &nsv, &osv))
1665:     stderror(ERR_SYSTEM, "pfork: sigvec set", strerror(errno));
1666: #endif /* SIGSYNCH */
1667: #ifdef BSDSIGS
1668:     omask = sigblock(sigmask(SIGCHLD));
1669: #else /* !BSDSIGS */
1670:     (void) sighold(SIGCHLD);
1671: #endif /* !BSDSIGS */
1672:     while ((pid = fork()) < 0)
1673:     if (setintr == 0)
1674:         (void) sleep(FORKSLEEP);
1675:     else {
1676: #ifdef BSDSIGS
1677:         (void) sigsetmask(omask);
1678: #else /* !BSDSIGS */
1679:         (void) sigrelse(SIGINT);
1680:         (void) sigrelse(SIGCHLD);
1681: #endif /* !BSDSIGS */
1682:         stderror(ERR_NOPROC);
1683:     }
1684:     if (pid == 0) {
1685:     settimes();
1686:     pgrp = pcurrjob ? pcurrjob->p_jobid : getpid();
1687:     pflushall();
1688:     pcurrjob = PNULL;
1689: #if !defined(BSDTIMES) && !defined(_SEQUENT_)
1690:     timesdone = 0;
1691: #endif /* !defined(BSDTIMES) && !defined(_SEQUENT_) */
1692:     child++;
1693:     if (setintr) {
1694:         setintr = 0;    /* until I think otherwise */
1695: #ifndef BSDSIGS
1696:         (void) sigrelse(SIGCHLD);
1697: #endif /* !BSDSIGS */
1698:         /*
1699: 	     * Children just get blown away on SIGINT, SIGQUIT unless "onintr
1700: 	     * -" seen.
1701: 	     */
1702:         (void) signal(SIGINT, ignint ? SIG_IGN : SIG_DFL);
1703:         (void) signal(SIGQUIT, ignint ? SIG_IGN : SIG_DFL);
1704: #ifdef BSDJOBS
1705:         if (wanttty >= 0) {
1706:         /* make stoppable */
1707:         (void) signal(SIGTSTP, SIG_DFL);
1708:         (void) signal(SIGTTIN, SIG_DFL);
1709:         (void) signal(SIGTTOU, SIG_DFL);
1710:         }
1711: #endif /* BSDJOBS */
1712:         (void) signal(SIGTERM, parterm);
1713:     }
1714:     else if (tpgrp == -1 && (t->t_dflg & F_NOINTERRUPT)) {
1715:         (void) signal(SIGINT, SIG_IGN);
1716:         (void) signal(SIGQUIT, SIG_IGN);
1717:     }
1718: #ifdef OREO
1719:     sigignore(SIGIO);   /* ignore SIGIO in child too */
1720: #endif /* OREO */
1721: 
1722:     pgetty(wanttty, pgrp);
1723:     /*
1724: 	 * Nohup and nice apply only to NODE_COMMAND's but it would be nice
1725: 	 * (?!?) if you could say "nohup (foo;bar)" Then the parser would have
1726: 	 * to know about nice/nohup/time
1727: 	 */
1728:     if (t->t_dflg & F_NOHUP)
1729:         (void) signal(SIGHUP, SIG_IGN);
1730:     if (t->t_dflg & F_NICE)
1731: #ifdef BSDNICE
1732:         (void) setpriority(PRIO_PROCESS, 0, t->t_nice);
1733: #else /* !BSDNICE */
1734:         (void) nice(t->t_nice);
1735: #endif /* !BSDNICE */
1736: #ifdef F_VER
1737:         if (t->t_dflg & F_VER) {
1738:         Setenv(STRSYSTYPE, t->t_systype ? STRbsd43 : STRsys53);
1739:         dohash();
1740:     }
1741: #endif /* F_VER */
1742: #ifdef SIGSYNCH
1743:     /* rfw 8/89 now parent can continue */
1744:     if (kill(getppid(), SIGSYNCH))
1745:         stderror(ERR_SYSTEM, "pfork child: kill", strerror(errno));
1746: #endif /* SIGSYNCH */
1747: 
1748:     }
1749:     else {
1750: #ifdef POSIXJOBS
1751:     if (wanttty >= 0)
1752:         (void) setpgid(pid, pcurrjob ? pcurrjob->p_jobid : pid);
1753: #endif /* POSIXJOBS */
1754:     palloc(pid, t);
1755: #ifdef SIGSYNCH
1756:     /*
1757: 	 * rfw 8/89 Wait for child to own terminal.  Solves half of ugly
1758: 	 * synchronization problem.  With this change, we know that the only
1759: 	 * reason setpgrp to a previous process in a pipeline can fail is that
1760: 	 * the previous process has already exited. Without this hack, he may
1761: 	 * either have exited or not yet started to run.  Two uglies become
1762: 	 * one.
1763: 	 */
1764:     sigpause(omask & ~SYNCHMASK);
1765:     if (mysigvec(SIGSYNCH, &osv, NULL))
1766:         stderror(ERR_SYSTEM, "pfork parent: sigvec restore",
1767:              strerror(errno));
1768: #endif /* SIGSYNCH */
1769: 
1770: #ifdef BSDSIGS
1771:     (void) sigsetmask(omask);
1772: #else /* !BSDSIGS */
1773:     (void) sigrelse(SIGCHLD);
1774: #endif /* !BSDSIGS */
1775:     }
1776:     return (pid);
1777: }
1778: 
1779: static void
1780: okpcntl()
1781: {
1782:     if (tpgrp == -1)
1783:     stderror(ERR_JOBCONTROL);
1784:     if (tpgrp == 0)
1785:     stderror(ERR_JOBCTRLSUB);
1786: }
1787: 
1788: /*
1789:  * if we don't have vfork(), things can still go in the wrong order
1790:  * resulting in the famous 'Stopped (tty output)'. But some systems
1791:  * don't permit the setpgid() call, (these are more recent secure
1792:  * systems such as ibm's aix), when they do. Then we'd rather print
1793:  * an error message than hang the shell!
1794:  * I am open to suggestions how to fix that.
1795:  */
1796: void
1797: pgetty(wanttty, pgrp)
1798:     int     wanttty, pgrp;
1799: {
1800: #ifdef BSDJOBS
1801: # if defined(BSDSIGS) && defined(POSIXJOBS)
1802:     sigmask_t omask = 0;
1803: # endif /* BSDSIGS && POSIXJOBS */
1804: 
1805: # ifdef JOBDEBUG
1806:     xprintf("wanttty %d\n", wanttty);
1807: # endif
1808: 
1809: # ifdef POSIXJOBS
1810:     /*
1811:      * christos: I am blocking the tty signals till I've set things
1812:      * correctly....
1813:      */
1814:     if (wanttty > 0)
1815: #  ifdef BSDSIGS
1816:     omask = sigblock(sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU));
1817: #  else /* !BSDSIGS */
1818:     {
1819:     (void) sighold(SIGTSTP);
1820:     (void) sighold(SIGTTIN);
1821:     (void) sighold(SIGTTOU);
1822:     }
1823: #  endif /* !BSDSIGS */
1824: 
1825:     /*
1826:      * From: Michael Schroeder <mlschroe@immd4.informatik.uni-erlangen.de>
1827:      * Don't check for tpgrp >= 0 so even non-interactive shells give
1828:      * background jobs process groups Same for the comparison in the other part
1829:      * of the #ifdef
1830:      */
1831:     if (wanttty >= 0)
1832:     if (setpgid(0, pgrp) == -1) {
1833:         xprintf("tcsh: setpgid error.\n");
1834:         xexit(0);
1835:     }
1836: # endif /* POSIXJOBS */
1837: 
1838:     if (wanttty > 0)
1839:     (void) tcsetpgrp(FSHTTY, pgrp);
1840: 
1841: # ifndef POSIXJOBS
1842:     if (wanttty >= 0)
1843:     if (setpgid(0, pgrp) == -1) {
1844:         xprintf("tcsh: setpgid error.\n");
1845:         xexit(0);
1846:     }
1847: # else /* POSIXJOBS */
1848:     if (wanttty > 0)
1849: #  ifdef BSDSIGS
1850:     (void) sigsetmask(omask);
1851: #  else /* BSDSIGS */
1852:     {
1853:     (void) sigrelse(SIGTSTP);
1854:     (void) sigrelse(SIGTTIN);
1855:     (void) sigrelse(SIGTTOU);
1856:     }
1857: #  endif /* !BSDSIGS */
1858: # endif /* POSIXJOBS */
1859: 
1860:     if (tpgrp > 0)
1861:     tpgrp = 0;      /* gave tty away */
1862: #endif /* BSDJOBS */
1863: }

Defined functions

dobg defined in line 1277; used 1 times
dobg1 defined in line 1294; used 1 times
dofg defined in line 1232; used 1 times
dofg1 defined in line 1256; used 1 times
dojobs defined in line 1204; used 3 times
dokill defined in line 1319; used 1 times
donotify defined in line 1606; used 1 times
dostop defined in line 1307; used 1 times
dowait defined in line 626; used 1 times
okpcntl defined in line 1779; used 3 times
padd defined in line 818; used 4 times
pads defined in line 877; used 14 times
palloc defined in line 740; used 3 times
pchild defined in line 160; used 4 times
pclrcurr defined in line 714; used 3 times
pendjob defined in line 932; used 3 times
pfind defined in line 1530; used 13 times
pflush defined in line 680; used 6 times
pflushall defined in line 665; used 1 times
pgetcurr defined in line 1585; used 4 times
pgetty defined in line 1796; used 4 times
pjwait defined in line 522; used 7 times
pkill defined in line 1364; used 2 times
pnote defined in line 445; never used
pprint defined in line 955; used 7 times
pstart defined in line 1474; used 11 times
ptprint defined in line 1119; used 2 times
rcsid defined in line 39; never used

Defined variables

cmdlen defined in line 733; used 5 times
cmdp defined in line 734; used 6 times
command defined in line 732; used 2 times
zru defined in line 134; used 8 times

Defined macros

BIGINDEX defined in line 109; used 1 times
BSDWAIT defined in line 88; used 3 times
HZ defined in line 83; used 3 times
NSIG defined in line 74; used 4 times
RUSAGE_CHILDREN defined in line 139; used 1 times
WCOREDUMP defined in line 101; used 2 times
WEXITSTATUS defined in line 91; used 3 times
WSTOPSIG defined in line 94; used 2 times
WTERMSIG defined in line 87; used 4 times
_BSD defined in line 55; never used
_CLASSIC_POSIX_TYPES defined in line 58; used 1 times
  • in line 57
timercmp defined in line 1141; used 2 times
Last modified: 1991-08-20
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 10944
Valid CSS Valid XHTML 1.0 Strict