home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume19 / ash / part04 < prev    next >
Text File  |  1989-05-29  |  52KB  |  2,144 lines

  1. Subject:  v19i004:  A reimplementation of the System V shell, Part04/08
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: ka@june.cs.washington.edu (Kenneth Almquist)
  7. Posting-number: Volume 19, Issue 4
  8. Archive-name: ash/part04
  9.  
  10. # This is part 4 of ash.  To unpack, feed it into the shell (not csh).
  11. # The ash distribution consists of eight pieces.  Be sure you get them all.
  12. # After you unpack everything, read the file README.
  13.  
  14. echo extracting jobs.h
  15. cat > jobs.h <<\EOF
  16. /*
  17.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  18.  * This file is part of ash, which is distributed under the terms specified
  19.  * by the Ash General Public License.  See the file named LICENSE.
  20.  */
  21.  
  22. /* Mode argument to forkshell.  Don't change FORK_FG or FORK_BG. */
  23. #define FORK_FG 0
  24. #define FORK_BG 1
  25. #define FORK_NOJOB 2
  26.  
  27.  
  28. /*
  29.  * A job structure contains information about a job.  A job is either a
  30.  * single process or a set of processes contained in a pipeline.  In the
  31.  * latter case, pidlist will be non-NULL, and will point to a -1 terminated
  32.  * array of pids.
  33.  */
  34.  
  35. struct procstat {
  36.       short pid;        /* process id */
  37.       short status;        /* status flags (defined above) */
  38.       char *cmd;        /* text of command being run */
  39. };
  40.  
  41.  
  42. /* states */
  43. #define JOBSTOPPED 1        /* all procs are stopped */
  44. #define JOBDONE 2        /* all procs are completed */
  45.  
  46.  
  47. struct job {
  48.       struct procstat ps0;    /* status of process */
  49.       struct procstat *ps;    /* status or processes when more than one */
  50.       short nprocs;        /* number of processes */
  51.       short pgrp;        /* process group of this job */
  52.       char state;        /* true if job is finished */
  53.       char used;        /* true if this entry is in used */
  54.       char changed;        /* true if status has changed */
  55. #if JOBS
  56.       char jobctl;        /* job running under job control */
  57. #endif
  58. };
  59.  
  60. extern short backgndpid;    /* pid of last background process */
  61.  
  62.  
  63. #ifdef __STDC__
  64. void setjobctl(int);
  65. void showjobs(int);
  66. struct job *makejob(union node *, int);
  67. int forkshell(struct job *, union node *, int);
  68. int waitforjob(struct job *);
  69. #else
  70. void setjobctl();
  71. void showjobs();
  72. struct job *makejob();
  73. int forkshell();
  74. int waitforjob();
  75. #endif
  76.  
  77. #if ! JOBS
  78. #define setjobctl(on)    /* do nothing */
  79. #endif
  80. EOF
  81. if test `wc -c < jobs.h` -ne 1738
  82. then    echo 'jobs.h is the wrong size'
  83. fi
  84. echo extracting jobs.c
  85. cat > jobs.c <<\EOF
  86. /*
  87.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  88.  * This file is part of ash, which is distributed under the terms specified
  89.  * by the Ash General Public License.  See the file named LICENSE.
  90.  */
  91.  
  92. #include "shell.h"
  93. #if JOBS
  94. #include "sgtty.h"
  95. #undef CEOF            /* syntax.h redefines this */
  96. #endif
  97. #include "main.h"
  98. #include "parser.h"
  99. #include "nodes.h"
  100. #include "jobs.h"
  101. #include "options.h"
  102. #include "trap.h"
  103. #include "signames.h"
  104. #include "syntax.h"
  105. #include "input.h"
  106. #include "output.h"
  107. #include "memalloc.h"
  108. #include "error.h"
  109. #include "mystring.h"
  110. #include <fcntl.h>
  111. #include <signal.h>
  112. #include "myerrno.h"
  113. #ifdef BSD
  114. #include <sys/wait.h>
  115. #include <sys/time.h>
  116. #include <sys/resource.h>
  117. #endif
  118.  
  119.  
  120.  
  121. struct job *jobtab;        /* array of jobs */
  122. int njobs;            /* size of array */
  123. MKINIT short backgndpid = -1;    /* pid of last background process */
  124. #if JOBS
  125. int initialpgrp;        /* pgrp of shell on invocation */
  126. short curjob;            /* current job */
  127. #endif
  128.  
  129. #ifdef __STDC__
  130. STATIC void restartjob(struct job *);
  131. STATIC struct job *getjob(char *);
  132. STATIC void freejob(struct job *);
  133. STATIC int procrunning(int);
  134. STATIC int dowait(int, struct job *);
  135. STATIC int waitproc(int, int *);
  136. STATIC char *commandtext(union node *);
  137. #else
  138. STATIC void restartjob();
  139. STATIC struct job *getjob();
  140. STATIC void freejob();
  141. STATIC int procrunning();
  142. STATIC int dowait();
  143. STATIC int waitproc();
  144. STATIC char *commandtext();
  145. #endif
  146.  
  147.  
  148.  
  149. #if JOBS
  150. /*
  151.  * Turn job control on and off.
  152.  *
  153.  * Note:  This code assumes that the third arg to ioctl is a character
  154.  * pointer, which is true on Berkeley systems but not System V.  Since
  155.  * System V doesn't have job control yet, this isn't a problem now.
  156.  */
  157.  
  158. MKINIT int jobctl;
  159.  
  160. void
  161. setjobctl(on) {
  162.       int ldisc;
  163.  
  164.       if (on == jobctl || rootshell == 0)
  165.             return;
  166.       if (on) {
  167.             do { /* while we are in the background */
  168.                   if (ioctl(2, TIOCGPGRP, (char *)&initialpgrp) < 0) {
  169.                         out2str("ash: can't access tty; job control turned off\n");
  170.                         jflag = 0;
  171.                         return;
  172.                   }
  173.                   if (initialpgrp == -1)
  174.                         initialpgrp = getpgrp(0);
  175.                   else if (initialpgrp != getpgrp(0)) {
  176.                         killpg(initialpgrp, SIGTTIN);
  177.                         continue;
  178.                   }
  179.             } while (0);
  180.             if (ioctl(2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) {
  181.                   out2str("ash: need new tty driver to run job control; job control turned off\n");
  182.                   jflag = 0;
  183.                   return;
  184.             }
  185.             setsignal(SIGTSTP);
  186.             setsignal(SIGTTOU);
  187.             ioctl(2, TIOCSPGRP, (char *)&rootpid);
  188.             setpgrp(0, rootpid);
  189.       } else { /* turning job control off */
  190.             ioctl(2, TIOCSPGRP, (char *)&initialpgrp);
  191.             setpgrp(0, initialpgrp);
  192.             setsignal(SIGTSTP);
  193.             setsignal(SIGTTOU);
  194.       }
  195.       jobctl = on;
  196. }
  197. #endif
  198.  
  199.  
  200. #ifdef mkinit
  201.  
  202. SHELLPROC {
  203.       backgndpid = -1;
  204. #if JOBS
  205.       jobctl = 0;
  206. #endif
  207. }
  208.  
  209. #endif
  210.  
  211.  
  212.  
  213. #if JOBS
  214. fgcmd(argc, argv)  char **argv; {
  215.       struct job *jp;
  216.       int pgrp;
  217.       int status;
  218.  
  219.       jp = getjob(argv[1]);
  220.       if (jp->jobctl == 0)
  221.             error("job not created under job control");
  222.       pgrp = jp->ps[0].pid;
  223.       ioctl(2, TIOCSPGRP, (char *)&pgrp);
  224.       restartjob(jp);
  225.       INTOFF;
  226.       status = waitforjob(jp);
  227.       INTON;
  228.       return status;
  229. }
  230.  
  231.  
  232. bgcmd(argc, argv)  char **argv; {
  233.       struct job *jp;
  234.  
  235.       do {
  236.         jp = getjob(*++argv);
  237.         if (jp->jobctl == 0)
  238.           error("job not created under job control");
  239.         restartjob(jp);
  240.       } while (--argc > 1);
  241.       return 0;
  242. }
  243.  
  244.  
  245. STATIC void
  246. restartjob(jp)
  247.       struct job *jp;
  248.       {
  249.       struct procstat *ps;
  250.       int i;
  251.  
  252.       if (jp->state == JOBDONE)
  253.             return;
  254.       INTOFF;
  255.       killpg(jp->ps[0].pid, SIGCONT);
  256.       for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) {
  257.             if ((ps->status & 0377) == 0177) {
  258.                   ps->status = -1;
  259.                   jp->state = 0;
  260.             }
  261.       }
  262.       INTON;
  263. }
  264. #endif
  265.  
  266.  
  267. int
  268. jobscmd(argc, argv)  char **argv; {
  269.       showjobs(0);
  270.       return 0;
  271. }
  272.  
  273.  
  274. /*
  275.  * Print a list of jobs.  If "change" is nonzero, only print jobs whose
  276.  * statuses have changed since the last call to showjobs.
  277.  *
  278.  * If the shell is interrupted in the process of creating a job, the
  279.  * result may be a job structure containing zero processes.  Such structures
  280.  * will be freed here.
  281.  */
  282.  
  283. void
  284. showjobs(change) {
  285.       int jobno;
  286.       int procno;
  287.       int i;
  288.       struct job *jp;
  289.       struct procstat *ps;
  290.       int col;
  291.       char s[64];
  292.  
  293.       TRACE(("showjobs(%d) called\n", change));
  294.       while (dowait(0, (struct job *)NULL) > 0);
  295.       for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) {
  296.         if (! jp->used)
  297.           continue;
  298.         if (jp->nprocs == 0) {
  299.                   freejob(jp);
  300.           continue;
  301.         }
  302.         if (change && ! jp->changed)
  303.           continue;
  304.         procno = jp->nprocs;
  305.         for (ps = jp->ps ; ; ps++) {    /* for each process */
  306.           if (ps == jp->ps)
  307.             fmtstr(s, 64, "[%d] %d ", jobno, ps->pid);
  308.           else
  309.             fmtstr(s, 64, "    %d ", ps->pid);
  310.           out1str(s);
  311.           col = strlen(s);
  312.           s[0] = '\0';
  313.           if (ps->status == -1) {
  314.             /* don't print anything */
  315.           } else if ((ps->status & 0xFF) == 0) {
  316.             fmtstr(s, 64, "Exit %d", ps->status >> 8);
  317.           } else {
  318.             i = ps->status;
  319. #if JOBS
  320.             if ((i & 0xFF) == 0177)
  321.                   i >>= 8;
  322. #endif
  323.             if ((i & 0x7F) <= MAXSIG && sigmesg[i & 0x7F])
  324.                   scopy(sigmesg[i & 0x7F], s);
  325.             else
  326.                   fmtstr(s, 64, "Signal %d", i & 0x7F);
  327.             if (i & 0x80)
  328.                   strcat(s, " (core dumped)");
  329.           }
  330.           out1str(s);
  331.           col += strlen(s);
  332.           do {
  333.             out1c(' ');
  334.             col++;
  335.           } while (col < 30);
  336.           out1str(ps->cmd);
  337.           out1c('\n');
  338.           if (--procno <= 0)
  339.             break;
  340.         }
  341.         jp->changed = 0;
  342.         if (jp->state == JOBDONE) {
  343.           freejob(jp);
  344.         }
  345.       }
  346. }
  347.  
  348.  
  349. /*
  350.  * Mark a job structure as unused.
  351.  */
  352.  
  353. STATIC void
  354. freejob(jp)
  355.       struct job *jp;
  356.       {
  357.       struct procstat *ps;
  358.       int i;
  359.  
  360.       INTOFF;
  361.       for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) {
  362.         if (ps->cmd != nullstr)
  363.           ckfree(ps->cmd);
  364.       }
  365.       if (jp->ps != &jp->ps0)
  366.         ckfree(jp->ps);
  367.       jp->used = 0;
  368. #if JOBS
  369.       if (curjob == jp - jobtab + 1)
  370.         curjob = 0;
  371. #endif
  372.       INTON;
  373. }
  374.  
  375.  
  376.  
  377. int
  378. waitcmd(argc, argv)  char **argv; {
  379.       struct job *job;
  380.       int status;
  381.       struct job *jp;
  382.  
  383.       if (argc > 1) {
  384.             job = getjob(argv[1]);
  385.       } else {
  386.         job = NULL;
  387.       }
  388.       for (;;) {    /* loop until process terminated or stopped */
  389.             if (job != NULL) {
  390.                   if (job->state) {
  391.             status = job->ps[job->nprocs - 1].status;
  392.             if ((status & 0xFF) == 0)
  393.                   status = status >> 8 & 0xFF;
  394. #if JOBS
  395.             else if ((status & 0xFF) == 0177)
  396.                   status = (status >> 8 & 0x7F) + 128;
  397. #endif
  398.             else
  399.                   status = (status & 0x7F) + 128;
  400.             if (! iflag)
  401.                   freejob(job);
  402.                         return status;
  403.           }
  404.         } else {
  405.           for (jp = jobtab ; ; jp++) {
  406.             if (jp >= jobtab + njobs) {    /* no running procs */
  407.                   return 0;
  408.             }
  409.             if (jp->used && jp->state == 0)
  410.                   break;
  411.           }
  412.             }
  413.             dowait(1, (struct job *)NULL);
  414.       }
  415. }
  416.  
  417.  
  418.  
  419. jobidcmd(argc, argv)  char **argv; {
  420.       struct job *jp;
  421.       int i;
  422.  
  423.       jp = getjob(argv[1]);
  424.       for (i = 0 ; i < jp->nprocs ; ) {
  425.         out1fmt("%d", jp->ps[i].pid);
  426.         out1c(++i < jp->nprocs? ' ' : '\n');
  427.       }
  428.       return 0;
  429. }
  430.  
  431.  
  432.  
  433. /*
  434.  * Convert a job name to a job structure.
  435.  */
  436.  
  437. STATIC struct job *
  438. getjob(name)
  439.       char *name;
  440.       {
  441.       int jobno;
  442.       register struct job *jp;
  443.       int pid;
  444.       int i;
  445.  
  446.       if (name == NULL) {
  447. #if JOBS
  448. currentjob:
  449.         if ((jobno = curjob) == 0 || jobtab[jobno - 1].used == 0)
  450.           error("No current job");
  451.         return &jobtab[jobno - 1];
  452. #else
  453.         error("No current job");
  454. #endif
  455.       } else if (name[0] == '%') {
  456.         if (is_digit(name[1])) {
  457.           jobno = number(name + 1);
  458.           if (jobno > 0 && jobno <= njobs
  459.            && jobtab[jobno - 1].used != 0)
  460.             return &jobtab[jobno - 1];
  461. #if JOBS
  462.         } else if (name[1] == '%' && name[2] == '\0') {
  463.           goto currentjob;
  464. #endif
  465.         } else {
  466.           register struct job *found = NULL;
  467.           for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
  468.             if (jp->used && jp->nprocs > 0
  469.              && prefix(name + 1, jp->ps[0].cmd)) {
  470.                   if (found)
  471.                     error("%s: ambiguous", name);
  472.                   found = jp;
  473.             }
  474.           }
  475.           if (found)
  476.             return found;
  477.         }
  478.       } else if (is_number(name)) {
  479.         pid = number(name);
  480.         for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
  481.           if (jp->used && jp->nprocs > 0
  482.            && jp->ps[jp->nprocs - 1].pid == pid)
  483.             return jp;
  484.         }
  485.       }
  486.       error("No such job: %s", name);
  487. }
  488.  
  489.  
  490.  
  491. /*
  492.  * Return a new job structure,
  493.  */
  494.  
  495. struct job *
  496. makejob(node, nprocs)
  497.       union node *node;
  498.       {
  499.       int i;
  500.       struct job *jp;
  501.  
  502.       for (i = njobs, jp = jobtab ; ; jp++) {
  503.             if (--i < 0) {
  504.           INTOFF;
  505.                   if (njobs == 0) {
  506.                         jobtab = ckmalloc(4 * sizeof jobtab[0]);
  507.                   } else {
  508.                         jp = ckmalloc((njobs + 4) * sizeof jobtab[0]);
  509.                         bcopy(jobtab, jp, njobs * sizeof jp[0]);
  510.                         ckfree(jobtab);
  511.                         jobtab = jp;
  512.                   }
  513.                   jp = jobtab + njobs;
  514.                   for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0);
  515.           INTON;
  516.                   break;
  517.             }
  518.             if (jp->used == 0)
  519.                   break;
  520.       }
  521.       INTOFF;
  522.       jp->state = 0;
  523.       jp->used = 1;
  524.       jp->changed = 0;
  525.       jp->nprocs = 0;
  526. #if JOBS
  527.       jp->jobctl = jobctl;
  528. #endif
  529.       if (nprocs > 1) {
  530.             jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
  531.       } else {
  532.             jp->ps = &jp->ps0;
  533.       }
  534.       INTON;
  535.       TRACE(("makejob(0x%x, %d) returns %%%d\n", (int)node, nprocs, jp - jobtab + 1));
  536.       return jp;
  537. }    
  538.  
  539.  
  540. /*
  541.  * Fork of a subshell.  If we are doing job control, give the subshell its
  542.  * own process group.  Jp is a job structure that the job is to be added to.
  543.  * N is the command that will be evaluated by the child.  Both jp and n may
  544.  * be NULL.  The mode parameter can be one of the following:
  545.  *    FORK_FG - Fork off a foreground process.
  546.  *    FORK_BG - Fork off a background process.
  547.  *    FORK_NOJOB - Like FORK_FG, but don't give the process its own
  548.  *             process group even if job control is on.
  549.  *
  550.  * When job control is turned off, background processes have their standard
  551.  * input redirected to /dev/null (except for the second and later processes
  552.  * in a pipeline).
  553.  */
  554.  
  555. int
  556. forkshell(jp, n, mode)
  557.       union node *n;
  558.       struct job *jp;
  559.       {
  560.       int pid;
  561.       int pgrp;
  562.  
  563.       TRACE(("forkshell(%%%d, 0x%x, %d) called\n", jp - jobtab, (int)n, mode));
  564.       INTOFF;
  565.       pid = fork();
  566.       if (pid == -1) {
  567.             TRACE(("Fork failed, errno=%d\n", errno));
  568.             INTON;
  569.             error("Cannot fork");
  570.       }
  571.       if (pid == 0) {
  572.         struct job *p;
  573.             int wasroot;
  574.         int i;
  575.  
  576.             TRACE(("Child shell %d\n", getpid()));
  577.         wasroot = rootshell;
  578.             rootshell = 0;
  579.         for (i = njobs, p = jobtab ; --i >= 0 ; p++)
  580.           if (p->used)
  581.             freejob(p);
  582.         closescript();
  583.             INTON;
  584.         clear_traps();
  585. #if JOBS
  586.         jobctl = 0;        /* do job control only in root shell */
  587.             if (wasroot && mode != FORK_NOJOB && jflag) {
  588.                   if (jp == NULL || jp->nprocs == 0)
  589.                         pgrp = getpid();
  590.                   else
  591.                         pgrp = jp->ps[0].pid;
  592.                   if (mode == FORK_FG) {
  593.                         if (ioctl(2, TIOCSPGRP, (char *)&pgrp) < 0)
  594.                               error("TIOCSPGRP failed, errno=%d\n", errno);
  595.                   }
  596.                   setpgrp(0, pgrp);
  597.                   setsignal(SIGTSTP);
  598.                   setsignal(SIGTTOU);
  599.             } else if (mode == FORK_BG) {
  600.           ignoresig(SIGINT);
  601.           ignoresig(SIGQUIT);
  602.           if (jp == NULL || jp->nprocs == 0) {
  603.             close(0);
  604.             if (open("/dev/null", O_RDONLY) != 0)
  605.                   error("Can't open /dev/null");
  606.           }
  607.         }
  608. #else
  609.         if (mode == FORK_BG) {
  610.           ignoresig(SIGINT);
  611.           ignoresig(SIGQUIT);
  612.           if (jp == NULL || jp->nprocs == 0) {
  613.             close(0);
  614.             if (open("/dev/null", O_RDONLY) != 0)
  615.                   error("Can't open /dev/null");
  616.           }
  617.         }
  618. #endif
  619.             if (wasroot && iflag) {
  620.                   setsignal(SIGINT);
  621.                   setsignal(SIGQUIT);
  622.                   setsignal(SIGTERM);
  623.             }
  624.             return pid;
  625.       }
  626.       if (mode == FORK_BG)
  627.             backgndpid = pid;        /* set $! */
  628.       if (jp) {
  629.         struct procstat *ps = &jp->ps[jp->nprocs++];
  630.             ps->pid = pid;
  631.         ps->status = -1;
  632.         ps->cmd = nullstr;
  633.         if (iflag && rootshell && n)
  634.           ps->cmd = commandtext(n);
  635.       }
  636.       INTON;
  637.       TRACE(("In parent shell:  child = %d\n", pid));
  638.       return pid;
  639. }
  640.  
  641.  
  642.  
  643. /*
  644.  * Wait for job to finish.
  645.  *
  646.  * Under job control we have the problem that while a child process is
  647.  * running interrupts generated by the user are sent to the child but not
  648.  * to the shell.  This means that an infinite loop started by an inter-
  649.  * active user may be hard to kill.  With job control turned off, an
  650.  * interactive user may place an interactive program inside a loop.  If
  651.  * the interactive program catches interrupts, the user doesn't want
  652.  * these interrupts to also abort the loop.  The approach we take here
  653.  * is to have the shell ignore interrupt signals while waiting for a
  654.  * forground process to terminate, and then send itself an interrupt
  655.  * signal if the child process was terminated by an interrupt signal.
  656.  * Unfortunately, some programs want to do a bit of cleanup and then
  657.  * exit on interrupt; unless these processes terminate themselves by
  658.  * sending a signal to themselves (instead of calling exit) they will
  659.  * confuse this approach.
  660.  */
  661.  
  662. int
  663. waitforjob(jp)
  664.       register struct job *jp;
  665.       {
  666. #if JOBS
  667.       int mypgrp = getpgrp(0);
  668. #endif
  669.       int status;
  670.       int st;
  671.  
  672.       INTOFF;
  673.       TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1));
  674.       while (jp->state == 0) {
  675.             dowait(1, jp);
  676.       }
  677. #if JOBS
  678.       if (jp->jobctl) {
  679.             if (ioctl(2, TIOCSPGRP, (char *)&mypgrp) < 0)
  680.                   error("TIOCSPGRP failed, errno=%d\n", errno);
  681.       }
  682.       if (jp->state == JOBSTOPPED)
  683.         curjob = jp - jobtab + 1;
  684. #endif
  685.       status = jp->ps[jp->nprocs - 1].status;
  686.       /* convert to 8 bits */
  687.       if ((status & 0xFF) == 0)
  688.             st = status >> 8 & 0xFF;
  689. #if JOBS
  690.       else if ((status & 0xFF) == 0177)
  691.             st = (status >> 8 & 0x7F) + 128;
  692. #endif
  693.       else
  694.             st = (status & 0x7F) + 128;
  695.       if (! JOBS || jp->state == JOBDONE)
  696.         freejob(jp);
  697.       CLEAR_PENDING_INT;
  698.       if ((status & 0x7F) == SIGINT)
  699.         kill(getpid(), SIGINT);
  700.       INTON;
  701.       return st;
  702. }
  703.  
  704.  
  705.  
  706. /*
  707.  * Wait for a process to terminate.
  708.  */
  709.  
  710. STATIC int
  711. dowait(block, job)
  712.       struct job *job;
  713.       {
  714.       int pid;
  715.       int status;
  716.       struct procstat *sp;
  717.       struct job *jp;
  718.       struct job *thisjob;
  719.       int done;
  720.       int stopped;
  721.       int core;
  722.  
  723.       TRACE(("dowait(%d) called\n", block));
  724.       do {
  725.             pid = waitproc(block, &status);
  726.             TRACE(("wait returns %d, status=%d\n", pid, status));
  727.       } while (pid == -1 && errno == EINTR);
  728.       if (pid <= 0)
  729.             return pid;
  730.       INTOFF;
  731.       thisjob = NULL;
  732.       for (jp = jobtab ; jp < jobtab + njobs ; jp++) {
  733.             if (jp->used) {
  734.                   done = 1;
  735.                   stopped = 1;
  736.                   for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) {
  737.                         if (sp->pid == -1)
  738.                               continue;
  739.                         if (sp->pid == pid) {
  740.                               TRACE(("Changin status of proc %d from 0x%x to 0x%x\n", pid, sp->status, status));
  741.                               sp->status = status;
  742.                               thisjob = jp;
  743.                         }
  744.                         if (sp->status == -1)
  745.                               stopped = 0;
  746.                         else if ((sp->status & 0377) == 0177)
  747.                               done = 0;
  748.                   }
  749.                   if (stopped) {        /* stopped or done */
  750.                         int state = done? JOBDONE : JOBSTOPPED;
  751.                         if (jp->state != state) {
  752.                               TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state));
  753.                               jp->state = state;
  754. #if JOBS
  755.                   if (done && curjob == jp - jobtab + 1)
  756.                     curjob = 0;        /* no current job */
  757. #endif
  758.                         }
  759.                   }
  760.             }
  761.       }
  762.       INTON;
  763.       if (! rootshell || ! iflag || (job && thisjob == job)) {
  764. #if JOBS
  765.         if ((status & 0xFF) == 0177)
  766.           status >>= 8;
  767. #endif
  768.             core = status & 0x80;
  769.             status &= 0x7F;
  770.             if (status != 0 && status != SIGINT && status != SIGPIPE) {
  771.           if (thisjob != job)
  772.             outfmt(out2, "%d: ", pid);
  773. #if JOBS
  774.           if (status == SIGTSTP && rootshell && iflag)
  775.             outfmt(out2, "%%%d ", job - jobtab + 1);
  776. #endif
  777.           if (status <= MAXSIG && sigmesg[status])
  778.             out2str(sigmesg[status]);
  779.           else
  780.             outfmt(out2, "Signal %d", status);
  781.           if (core)
  782.             out2str(" - core dumped");
  783.                   out2c('\n');
  784.                   flushout(&errout);
  785.             } else {
  786.                   TRACE(("Not printing status: status=%d\n", status));
  787.             }
  788.       } else {
  789.         TRACE(("Not printing status, rootshell=%d, job=0x%x\n", rootshell, job));
  790.         if (thisjob)
  791.           thisjob->changed = 1;
  792.       }
  793.       return pid;
  794. }
  795.  
  796.  
  797.  
  798. /*
  799.  * Do a wait system call.  If job control is compiled in, we accept
  800.  * stopped processes.  If block is zero, we return a value of zero
  801.  * rather than blocking.
  802.  *
  803.  * System V doesn't have a non-blocking wait system call.  It does
  804.  * have a SIGCLD signal that is sent to a process when one of it's
  805.  * children dies.  The obvious way to use SIGCLD would be to install
  806.  * a handler for SIGCLD which simply bumped a counter when a SIGCLD
  807.  * was received, and have waitproc bump another counter when it got
  808.  * the status of a process.  Waitproc would then know that a wait
  809.  * system call would not block if the two counters were different.
  810.  * This approach doesn't work because if a process has children that
  811.  * have not been waited for, System V will send it a SIGCLD when it
  812.  * installs a signal handler for SIGCLD.  What this means is that when
  813.  * a child exits, the shell will be sent SIGCLD signals continuously
  814.  * until is runs out of stack space, unless it does a wait call before
  815.  * restoring the signal handler.  The code below takes advantage of
  816.  * this (mis)feature by installing a signal handler for SIGCLD and
  817.  * then checking to see whether it was called.  If there are any
  818.  * children to be waited for, it will be.
  819.  *
  820.  * If neither SYSV nor BSD is defined, we don't implement nonblocking
  821.  * waits at all.  In this case, the user will not be informed when
  822.  * a background process until the next time she runs a real program
  823.  * (as opposed to running a builtin command or just typing return),
  824.  * and the jobs command may give out of date information.
  825.  */
  826.  
  827. #ifdef SYSV
  828. STATIC int gotsigchild;
  829.  
  830. STATIC int onsigchild() {
  831.       gotsigchild = 1;
  832. }
  833. #endif
  834.  
  835.  
  836. STATIC int
  837. waitproc(block, status)
  838.       int *status;
  839.       {
  840. #ifdef BSD
  841.       int flags;
  842.  
  843. #if JOBS
  844.       flags = WUNTRACED;
  845. #else
  846.       flags = 0;
  847. #endif
  848.       if (block == 0)
  849.             flags |= WNOHANG;
  850.       return wait3((union wait *)status, flags, (struct rusage *)NULL);
  851. #else
  852. #ifdef SYSV
  853.       int (*save)();
  854.  
  855.       if (block == 0) {
  856.             gotsigchild = 0;
  857.             save = signal(SIGCLD, onsigchild);
  858.             signal(SIGCLD, save);
  859.             if (gotsigchild == 0)
  860.                   return 0;
  861.       }
  862.       return wait(status);
  863. #else
  864.       if (block == 0)
  865.             return 0;
  866.       return wait(status);
  867. #endif
  868. #endif
  869. }
  870.  
  871.  
  872.  
  873. /*
  874.  * Return a string identifying a command (to be printed by the
  875.  * jobs command.
  876.  */
  877.  
  878. STATIC char *cmdnextc;
  879. STATIC int cmdnleft;
  880. STATIC void cmdtxt(), cmdputs();
  881.  
  882. STATIC char *
  883. commandtext(n)
  884.       union node *n;
  885.       {
  886.       char *name;
  887.  
  888.       cmdnextc = name = ckmalloc(50);
  889.       cmdnleft = 50 - 4;
  890.       cmdtxt(n);
  891.       *cmdnextc = '\0';
  892.       return name;
  893. }
  894.  
  895.  
  896. STATIC void
  897. cmdtxt(n)
  898.       union node *n;
  899.       {
  900.       union node *np;
  901.       struct nodelist *lp;
  902.       char *p;
  903.       int i;
  904.       char s[2];
  905.  
  906.       switch (n->type) {
  907.       case NSEMI:
  908.             cmdtxt(n->nbinary.ch1);
  909.             cmdputs("; ");
  910.             cmdtxt(n->nbinary.ch2);
  911.             break;
  912.       case NAND:
  913.             cmdtxt(n->nbinary.ch1);
  914.             cmdputs(" && ");
  915.             cmdtxt(n->nbinary.ch2);
  916.             break;
  917.       case NOR:
  918.             cmdtxt(n->nbinary.ch1);
  919.             cmdputs(" || ");
  920.             cmdtxt(n->nbinary.ch2);
  921.             break;
  922.       case NPIPE:
  923.             for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
  924.                   cmdtxt(lp->n);
  925.                   if (lp->next)
  926.                         cmdputs(" | ");
  927.             }
  928.             break;
  929.       case NSUBSHELL:
  930.         cmdputs("(");
  931.         cmdtxt(n->nredir.n);
  932.         cmdputs(")");
  933.         break;
  934.       case NREDIR:
  935.       case NBACKGND:
  936.         cmdtxt(n->nredir.n);
  937.         break;
  938.       case NIF:
  939.             cmdputs("if ");
  940.             cmdtxt(n->nif.test);
  941.             cmdputs("; then ");
  942.             cmdtxt(n->nif.ifpart);
  943.             cmdputs("...");
  944.             break;
  945.       case NWHILE:
  946.             cmdputs("while ");
  947.             goto until;
  948.       case NUNTIL:
  949.             cmdputs("until ");
  950. until:
  951.             cmdtxt(n->nbinary.ch1);
  952.             cmdputs("; do ");
  953.             cmdtxt(n->nbinary.ch2);
  954.             cmdputs("; done");
  955.             break;
  956.       case NFOR:
  957.             cmdputs("for ");
  958.             cmdputs(n->nfor.var);
  959.             cmdputs(" in ...");
  960.             break;
  961.       case NCASE:
  962.             cmdputs("case ");
  963.             cmdputs(n->ncase.expr->narg.text);
  964.             cmdputs(" in ...");
  965.             break;
  966.       case NDEFUN:
  967.             cmdputs(n->narg.text);
  968.             cmdputs("() ...");
  969.             break;
  970.       case NCMD:
  971.             for (np = n->ncmd.args ; np ; np = np->narg.next) {
  972.                   cmdtxt(np);
  973.                   if (np->narg.next)
  974.                         cmdputs(" ");
  975.             }
  976.             for (np = n->ncmd.redirect ; np ; np = np->nfile.next) {
  977.                   cmdputs(" ");
  978.                   cmdtxt(np);
  979.             }
  980.             break;
  981.       case NARG:
  982.             cmdputs(n->narg.text);
  983.             break;
  984.       case NTO:
  985.             p = ">";  i = 1;  goto redir;
  986.       case NAPPEND:
  987.             p = ">>";  i = 1;  goto redir;
  988.       case NTOFD:
  989.             p = ">&";  i = 1;  goto redir;
  990.       case NFROM:
  991.             p = "<";  i = 0;  goto redir;
  992.       case NFROMFD:
  993.             p = "<&";  i = 0;  goto redir;
  994. redir:
  995.             if (n->nfile.fd != i) {
  996.                   s[0] = n->nfile.fd + '0';
  997.                   s[1] = '\0';
  998.                   cmdputs(s);
  999.             }
  1000.             cmdputs(p);
  1001.             if (n->type == NTOFD || n->type == NFROMFD) {
  1002.                   s[0] = n->ndup.dupfd + '0';
  1003.                   s[1] = '\0';
  1004.                   cmdputs(s);
  1005.             } else {
  1006.                   cmdtxt(n->nfile.fname);
  1007.             }
  1008.             break;
  1009.       case NHERE:
  1010.       case NXHERE:
  1011.             cmdputs("<<...");
  1012.         break;
  1013.       default:
  1014.             cmdputs("???");
  1015.             break;
  1016.       }
  1017. }
  1018.  
  1019.  
  1020.  
  1021. STATIC void
  1022. cmdputs(s)
  1023.       char *s;
  1024.       {
  1025.       register char *p, *q;
  1026.       register char c;
  1027.       int subtype = 0;
  1028.  
  1029.       if (cmdnleft <= 0)
  1030.             return;
  1031.       p = s;
  1032.       q = cmdnextc;
  1033.       while ((c = *p++) != '\0') {
  1034.             if (c == CTLESC)
  1035.                   *q++ = *p++;
  1036.             else if (c == CTLVAR) {
  1037.                   *q++ = '$';
  1038.           if (--cmdnleft > 0)
  1039.             *q++ = '{';
  1040.           subtype = *p++;
  1041.         } else if (c == '=' && subtype != 0) {
  1042.           *q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL];
  1043.           subtype = 0;
  1044.         } else if (c == CTLENDVAR) {
  1045.           *q++ = '}';
  1046.             } else if (c == CTLBACKQ | c == CTLBACKQ+CTLQUOTE)
  1047.                   cmdnleft++;        /* ignore it */
  1048.             else
  1049.                   *q++ = c;
  1050.             if (--cmdnleft <= 0) {
  1051.                   *q++ = '.';
  1052.                   *q++ = '.';
  1053.                   *q++ = '.';
  1054.                   break;
  1055.             }
  1056.       }
  1057.       cmdnextc = q;
  1058. }
  1059. EOF
  1060. if test `wc -c < jobs.c` -ne 24668
  1061. then    echo 'jobs.c is the wrong size'
  1062. fi
  1063. echo extracting machdep.h
  1064. cat > machdep.h <<\EOF
  1065. /*
  1066.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1067.  * This file is part of ash, which is distributed under the terms specified
  1068.  * by the Ash General Public License.  See the file named LICENSE.
  1069.  */
  1070.  
  1071. /*
  1072.  * Most machines require the value returned from malloc to be aligned
  1073.  * in some way.  The following macro will get this right on many machines.
  1074.  */
  1075.  
  1076. #ifndef ALIGN
  1077. union align {
  1078.       int i;
  1079.       char *cp;
  1080. };
  1081.  
  1082. #define ALIGN(nbytes)    ((nbytes) + sizeof(union align) - 1 &~ (sizeof(union align) - 1))
  1083. #endif
  1084. EOF
  1085. if test `wc -c < machdep.h` -ne 525
  1086. then    echo 'machdep.h is the wrong size'
  1087. fi
  1088. echo extracting mail.h
  1089. cat > mail.h <<\EOF
  1090. #ifdef __STDC__
  1091. void chkmail(int);
  1092. #else
  1093. void chkmail();
  1094. #endif
  1095. EOF
  1096. if test `wc -c < mail.h` -ne 64
  1097. then    echo 'mail.h is the wrong size'
  1098. fi
  1099. echo extracting mail.c
  1100. cat > mail.c <<\EOF
  1101. /*
  1102.  * Routines to check for mail.  (Perhaps make part of main.c?)
  1103.  */
  1104.  
  1105. #include "shell.h"
  1106. #include "exec.h"    /* defines padvance() */
  1107. #include "var.h"
  1108. #include "output.h"
  1109. #include "memalloc.h"
  1110. #include "error.h"
  1111. #include <sys/types.h>
  1112. #include <sys/stat.h>
  1113.  
  1114.  
  1115. #define MAXMBOXES 10
  1116.  
  1117.  
  1118. STATIC int nmboxes;            /* number of mailboxes */
  1119. STATIC time_t mailtime[MAXMBOXES];    /* times of mailboxes */
  1120.  
  1121.  
  1122.  
  1123. /*
  1124.  * Print appropriate message(s) if mail has arrived.  If the argument is
  1125.  * nozero, then the value of MAIL has changed, so we just update the
  1126.  * values.
  1127.  */
  1128.  
  1129. void
  1130. chkmail(silent) {
  1131.       register int i;
  1132.       char *mpath;
  1133.       char *p;
  1134.       register char *q;
  1135.       struct stackmark smark;
  1136.       struct stat statb;
  1137.  
  1138.       if (silent)
  1139.         nmboxes = 10;
  1140.       if (nmboxes == 0)
  1141.         return;
  1142.       setstackmark(&smark);
  1143.       mpath = mpathset()? mpathval() : mailval();
  1144.       for (i = 0 ; i < nmboxes ; i++) {
  1145.         p = padvance(&mpath, nullstr);
  1146.         if (p == NULL)
  1147.           break;
  1148.         if (*p == '\0')
  1149.           continue;
  1150.         for (q = p ; *q ; q++);
  1151.         if (q[-1] != '/')
  1152.           abort();
  1153.         q[-1] = '\0';            /* delete trailing '/' */
  1154. #ifdef notdef /* this is what the System V shell claims to do (it lies) */
  1155.         if (stat(p, &statb) < 0)
  1156.           statb.st_mtime = 0;
  1157.         if (statb.st_mtime > mailtime[i] && ! silent) {
  1158.           out2str(pathopt? pathopt : "you have mail");
  1159.           out2c('\n');
  1160.         }
  1161.         mailtime[i] = statb.st_mtime;
  1162. #else /* this is what it should do */
  1163.         if (stat(p, &statb) < 0)
  1164.           statb.st_size = 0;
  1165.         if (statb.st_size > mailtime[i] && ! silent) {
  1166.           out2str(pathopt? pathopt : "you have mail");
  1167.           out2c('\n');
  1168.         }
  1169.         mailtime[i] = statb.st_size;
  1170. #endif
  1171.       }
  1172.       nmboxes = i;
  1173.       popstackmark(&smark);
  1174. }
  1175. EOF
  1176. if test `wc -c < mail.c` -ne 1722
  1177. then    echo 'mail.c is the wrong size'
  1178. fi
  1179. echo extracting main.h
  1180. cat > main.h <<\EOF
  1181. extern int rootpid;        /* pid of main shell */
  1182. extern int rootshell;        /* true if we aren't a child of the main shell */
  1183.  
  1184. #ifdef __STDC__
  1185. void readcmdfile(char *);
  1186. void cmdloop(int);
  1187. #else
  1188. void readcmdfile();
  1189. void cmdloop();
  1190. #endif
  1191. EOF
  1192. if test `wc -c < main.h` -ne 229
  1193. then    echo 'main.h is the wrong size'
  1194. fi
  1195. echo extracting main.c
  1196. cat > main.c <<\EOF
  1197. /*
  1198.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1199.  * This file is part of ash, which is distributed under the terms specified
  1200.  * by the Ash General Public License.  See the file named LICENSE.
  1201.  */
  1202.  
  1203.  
  1204. #include <signal.h>
  1205. #include <fcntl.h>
  1206. #include "shell.h"
  1207. #include "main.h"
  1208. #include "mail.h"
  1209. #include "options.h"
  1210. #include "output.h"
  1211. #include "parser.h"
  1212. #include "nodes.h"
  1213. #include "eval.h"
  1214. #include "jobs.h"
  1215. #include "input.h"
  1216. #include "trap.h"
  1217. #if ATTY
  1218. #include "var.h"
  1219. #endif
  1220. #include "memalloc.h"
  1221. #include "error.h"
  1222. #include "init.h"
  1223. #include "mystring.h"
  1224.  
  1225. #define PROFILE 0
  1226.  
  1227. const char copyright[] = "@(#)Copyright 1989 by Kenneth Almquist";
  1228. int rootpid;
  1229. int rootshell;
  1230. STATIC union node *curcmd;
  1231. STATIC union node *prevcmd;
  1232. extern int errno;
  1233. #if PROFILE
  1234. short profile_buf[16384];
  1235. extern int etext();
  1236. #endif
  1237.  
  1238. #ifdef __STDC__
  1239. STATIC void read_profile(char *);
  1240. char *getenv(char *);
  1241. #else
  1242. STATIC void read_profile();
  1243. char *getenv();
  1244. #endif
  1245.  
  1246.  
  1247. /*
  1248.  * Main routine.  We initialize things, parse the arguments, execute
  1249.  * profiles if we're a login shell, and then call cmdloop to execute
  1250.  * commands.  The setjmp call sets up the location to jump to when an
  1251.  * exception occurs.  When an exception occurs the variable "state"
  1252.  * is used to figure out how far we had gotten.
  1253.  */
  1254.  
  1255. main(argc, argv)  char **argv; {
  1256.       struct jmploc jmploc;
  1257.       struct stackmark smark;
  1258.       volatile int state;
  1259.       char *shinit;
  1260.  
  1261. #if PROFILE
  1262.       monitor(4, etext, profile_buf, sizeof profile_buf, 50);
  1263. #endif
  1264.       state = 0;
  1265.       if (setjmp(jmploc.loc)) {
  1266.         /*
  1267.          * When a shell procedure is executed, we raise the
  1268.          * exception EXSHELLPROC to clean up before executing
  1269.          * the shell procedure.
  1270.          */
  1271.         if (exception == EXSHELLPROC) {
  1272.           rootpid = getpid();
  1273.           rootshell = 1;
  1274.           minusc = NULL;
  1275.           state = 3;
  1276.         } else if (state == 0 || iflag == 0 || ! rootshell)
  1277.           exitshell(2);
  1278.         reset();
  1279. #if ATTY
  1280.         if (exception == EXINT
  1281.          && (! attyset() || equal(termval(), "emacs"))) {
  1282. #else
  1283.         if (exception == EXINT) {
  1284. #endif
  1285.           out2c('\n');
  1286.           flushout(&errout);
  1287.         }
  1288.         popstackmark(&smark);
  1289.         FORCEINTON;                /* enable interrupts */
  1290.         if (state == 1)
  1291.           goto state1;
  1292.         else if (state == 2)
  1293.           goto state2;
  1294.         else
  1295.           goto state3;
  1296.       }
  1297.       handler = &jmploc;
  1298. #ifdef DEBUG
  1299.       opentrace();
  1300.       trputs("Shell args:  ");  trargs(argv);
  1301. #endif
  1302.       rootpid = getpid();
  1303.       rootshell = 1;
  1304.       init();
  1305.       setstackmark(&smark);
  1306.       procargs(argc, argv);
  1307.       if (argv[0] && argv[0][0] == '-') {
  1308.         state = 1;
  1309.         read_profile("/etc/profile");
  1310. state1:
  1311.         state = 2;
  1312.         read_profile(".profile");
  1313.       } else if ((sflag || minusc) && (shinit = getenv("SHINIT")) != NULL) {
  1314.         state = 2;
  1315.         evalstring(shinit);
  1316.       }
  1317. state2:
  1318.       state = 3;
  1319.       if (minusc) {
  1320.         evalstring(minusc);
  1321.       }
  1322.       if (sflag || minusc == NULL) {
  1323. state3:
  1324.         cmdloop(1);
  1325.       }
  1326. #if PROFILE
  1327.       monitor(0);
  1328. #endif
  1329.       exitshell(exitstatus);
  1330. }
  1331.  
  1332.  
  1333. /*
  1334.  * Read and execute commands.  "Top" is nonzero for the top level command
  1335.  * loop; it turns on prompting if the shell is interactive.
  1336.  */
  1337.  
  1338. void
  1339. cmdloop(top) {
  1340.       union node *n;
  1341.       struct stackmark smark;
  1342.       int inter;
  1343.       int numeof;
  1344.  
  1345.       TRACE(("cmdloop(%d) called\n", top));
  1346.       setstackmark(&smark);
  1347.       numeof = 0;
  1348.       for (;;) {
  1349.         if (sigpending)
  1350.           dotrap();
  1351.         inter = 0;
  1352.         if (iflag && top) {
  1353.           inter++;
  1354.           showjobs(1);
  1355.           chkmail(0);
  1356.           flushout(&output);
  1357.         }
  1358.         n = parsecmd(inter);
  1359.         if (n == NEOF) {
  1360.           if (Iflag == 0 || numeof >= 50)
  1361.             break;
  1362.           out2str("\nUse \"exit\" to leave ash.\n");
  1363.           numeof++;
  1364.         } else if (n != NULL && nflag == 0) {
  1365.           if (inter) {
  1366.             INTOFF;
  1367.             if (prevcmd)
  1368.                   freefunc(prevcmd);
  1369.             prevcmd = curcmd;
  1370.             curcmd = copyfunc(n);
  1371.             INTON;
  1372.           }
  1373.           evaltree(n, 0);
  1374. #ifdef notdef
  1375.           if (exitstatus)                      /*DEBUG*/
  1376.             outfmt(&errout, "Exit status 0x%X\n", exitstatus);
  1377. #endif
  1378.         }
  1379.         popstackmark(&smark);
  1380.       }
  1381.       popstackmark(&smark);        /* unnecessary */
  1382. }
  1383.  
  1384.  
  1385.  
  1386. /*
  1387.  * Read /etc/profile or .profile.  Return on error.
  1388.  */
  1389.  
  1390. STATIC void
  1391. read_profile(name)
  1392.       char *name;
  1393.       {
  1394.       int fd;
  1395.  
  1396.       INTOFF;
  1397.       if ((fd = open(name, O_RDONLY)) >= 0)
  1398.         setinputfd(fd, 1);
  1399.       INTON;
  1400.       if (fd < 0)
  1401.         return;
  1402.       cmdloop(0);
  1403.       popfile();
  1404. }
  1405.  
  1406.  
  1407.  
  1408. /*
  1409.  * Read a file containing shell functions.
  1410.  */
  1411.  
  1412. void
  1413. readcmdfile(name)
  1414.       char *name;
  1415.       {
  1416.       int fd;
  1417.  
  1418.       INTOFF;
  1419.       if ((fd = open(name, O_RDONLY)) >= 0)
  1420.         setinputfd(fd, 1);
  1421.       else
  1422.         error("Can't open %s", name);
  1423.       INTON;
  1424.       cmdloop(0);
  1425.       popfile();
  1426. }
  1427.  
  1428.  
  1429.  
  1430. /*
  1431.  * Take commands from a file.  To be compatable we should do a path
  1432.  * search for the file, but a path search doesn't make any sense.
  1433.  */
  1434.  
  1435. dotcmd(argc, argv)  char **argv; {
  1436.       exitstatus = 0;
  1437.       if (argc >= 2) {        /* That's what SVR2 does */
  1438.         setinputfile(argv[1], 1);
  1439.         commandname = argv[1];
  1440.         cmdloop(0);
  1441.         popfile();
  1442.       }
  1443.       return exitstatus;
  1444. }
  1445.  
  1446.  
  1447. exitcmd(argc, argv)  char **argv; {
  1448.       if (argc > 1)
  1449.         exitstatus = number(argv[1]);
  1450.       exitshell(exitstatus);
  1451. }
  1452.  
  1453.  
  1454. lccmd(argc, argv)  char **argv; {
  1455.       if (argc > 1) {
  1456.         defun(argv[1], prevcmd);
  1457.         return 0;
  1458.       } else {
  1459.         INTOFF;
  1460.         freefunc(curcmd);
  1461.         curcmd = prevcmd;
  1462.         prevcmd = NULL;
  1463.         INTON;
  1464.         evaltree(curcmd, 0);
  1465.         return exitstatus;
  1466.       }
  1467. }
  1468.  
  1469.  
  1470.  
  1471. #ifdef notdef
  1472. /*
  1473.  * Should never be called.
  1474.  */
  1475.  
  1476. void
  1477. exit(exitstatus) {
  1478.       _exit(exitstatus);
  1479. }
  1480. #endif
  1481. EOF
  1482. if test `wc -c < main.c` -ne 5561
  1483. then    echo 'main.c is the wrong size'
  1484. fi
  1485. echo extracting makefile
  1486. cat > makefile <<\EOF
  1487. # Makefile for ash.
  1488. #
  1489. # Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1490. # This file is part of ash, which is distributed under the terms specified
  1491. # by the Ash General Public License.  See the file named LICENSE.
  1492.  
  1493. FILES=main.o options.o parser.o eval.o expand.o jobs.o redir.o exec.o\
  1494.  builtins.o cd.o miscbltin.o mail.o var.o input.o output.o nodes.o syntax.o\
  1495.  signames.o memalloc.o error.o trap.o show.o dirent.o mystring.o\
  1496.  init.o
  1497.  
  1498. CFILES=main.c options.c parser.c eval.c expand.c jobs.c redir.c exec.c\
  1499.  builtins.c cd.c miscbltin.c mail.c var.c input.c output.c nodes.c syntax.c\
  1500.  signames.c memalloc.c error.c trap.c show.c dirent.c mystring.c
  1501.  
  1502. GENERATEDFILES=syntax.h syntax.c signames.h signames.c nodes.c nodes.c\
  1503.  builtin.h builtin.c init.c token.def
  1504.  
  1505. #MALLOC=mymalloc.o
  1506.  
  1507.  
  1508. #CC=gcc
  1509. DEBUG=-g
  1510. CFLAGS=$(DEBUG)
  1511. LDFLAGS=
  1512. BLTIN=bltin
  1513.  
  1514.  
  1515. all: make_bltin ash
  1516.  
  1517. make_bltin:
  1518.     cd bltin; make 'CC=$(CC)' 'DEBUG=$(DEBUG)'
  1519.  
  1520.  
  1521. clean:
  1522.     rm -f $(FILES)
  1523.     rm -f $(GENERATEDFILES) mksyntax mksignames mknodes mkinit
  1524.     rm -f bltin/bltinlib.a bltin/*.o bltin/operators.h bltin/operators.c
  1525.  
  1526. clobber: clean
  1527.     rm -f ash bltin/catf bltin/expr bltin/test 'bltin/[' bltin/echo bltin/line bltin/nlecho bltin/true bltin/: bltin/umask
  1528.  
  1529.  
  1530. ash:$P $(FILES) $(BLTIN)/bltinlib.a $(MALLOC)
  1531.     $(CC) -o temp $(LDFLAGS) $(DEBUG) $(FILES) $(BLTIN)/bltinlib.a $(MALLOC)
  1532. #    ld -o temp crt0.o $(FILES) $(BLTIN)/bltinlib.a $(MALLOC) -lc
  1533.     mv -f temp $@
  1534.  
  1535. lint:
  1536.     lint $(CFILES) init.c
  1537.  
  1538. syntax.c syntax.h: mksyntax
  1539.     ./mksyntax
  1540.  
  1541. mksyntax: mksyntax.c parser.h
  1542.     $(CC) -o mksyntax mksyntax.c
  1543.  
  1544. signames.c signames.h: mksignames
  1545.     ./mksignames
  1546.  
  1547. mksignames: mksignames.c
  1548.     $(CC) -o mksignames mksignames.c
  1549.  
  1550. nodes.c nodes.h: mknodes nodetypes nodes.c.pat
  1551.     ./mknodes
  1552.  
  1553. mknodes: mknodes.c
  1554.     $(CC) -o mknodes -g mknodes.c
  1555.  
  1556. token.def: mktokens
  1557.     sh mktokens
  1558.  
  1559. builtins.h builtins.c: mkbuiltins builtins
  1560.     sh mkbuiltins
  1561.     rm -f builtins.o
  1562.  
  1563. .c:
  1564.     echo make is confused, it but should recover
  1565.  
  1566. init.o: mkinit $(CFILES)
  1567.     ./mkinit '$(CC) -c $(CFLAGS) init.c' $(CFILES)
  1568.  
  1569. mkinit: mkinit.c
  1570.     $(CC) -o mkinit mkinit.c
  1571.  
  1572.  
  1573. cd.o: shell.h var.h nodes.h jobs.h options.h output.h memalloc.h error.h\
  1574.     mystring.h
  1575. dirent.o: shell.h mydirent.h
  1576. eval.o: shell.h nodes.h syntax.h expand.h parser.h jobs.h eval.h builtins.h\
  1577.     options.h exec.h redir.h input.h output.h trap.h var.h memalloc.h\
  1578.     error.h mystring.h
  1579. error.o: shell.h main.h options.h output.h error.h
  1580. exec.o: shell.h main.h nodes.h parser.h redir.h eval.h exec.h builtins.h var.h\
  1581.     options.h input.h output.h memalloc.h error.h init.h\
  1582.     mystring.h
  1583. expand.o: shell.h main.h nodes.h eval.h expand.h syntax.h parser.h jobs.h\
  1584.     options.h var.h input.h output.h memalloc.h error.h\
  1585.     mystring.h mydirent.h
  1586. input.c: shell.h syntax.h input.h output.h memalloc.h error.h
  1587. jobs.o: shell.h main.h parser.h nodes.h jobs.h options.h trap.h signames.h\
  1588.     syntax.h input.h output.h memalloc.h error.h mystring.h
  1589. mail.o: shell.h exec.h var.h output.h memalloc.h error.h
  1590. main.o: shell.h mail.h options.h var.h output.h parser.h nodes.h eval.h jobs.h\
  1591.     input.h trap.h error.h memalloc.h init.h
  1592. memalloc.o: shell.h output.h memalloc.h error.h machdep.h mystring.h
  1593. miscbltin.o: shell.h options.h var.h output.h memalloc.h error.h mystring.h
  1594. mystring.o: shell.h syntax.h error.h mystring.h
  1595. nodes.o: shell.h nodes.h memalloc.h machdep.h mystring.h
  1596. options.o: shell.h options.h nodes.h eval.h jobs.h input.h output.h trap.h\
  1597.     var.h memalloc.h error.h mystring.h
  1598. output.o: shell.h syntax.h output.h memalloc.h error.h
  1599. parser.o: shell.h parser.h nodes.h expand.h redir.h syntax.h options.h input.h\
  1600.     output.h var.h error.h memalloc.h mystring.h token.def
  1601. redir.o: shell.h nodes.h jobs.h expand.h redir.h output.h memalloc.h error.h
  1602. show.o: shell.h parser.h nodes.h mystring.h
  1603. syntax.o: shell.h syntax.h
  1604. trap.o: shell.h main.h nodes.h eval.h jobs.h options.h syntax.h signames.h\
  1605.     output.h memalloc.h error.h trap.h
  1606. var.o: shell.h output.h expand.h nodes.h eval.h exec.h syntax.h mail.h\
  1607.     options.h var.h memalloc.h error.h mystring.h
  1608. EOF
  1609. if test `wc -c < makefile` -ne 3992
  1610. then    echo 'makefile is the wrong size'
  1611. fi
  1612. echo extracting memalloc.h
  1613. cat > memalloc.h <<\EOF
  1614. /*
  1615.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1616.  * This file is part of ash, which is distributed under the terms specified
  1617.  * by the Ash General Public License.  See the file named LICENSE.
  1618.  */
  1619.  
  1620. struct stackmark {
  1621.       struct stack_block *stackp;
  1622.       char *stacknxt;
  1623.       int stacknleft;
  1624. };
  1625.  
  1626.  
  1627. extern char *stacknxt;
  1628. extern int stacknleft;
  1629. extern int sstrnleft;
  1630. extern int herefd;
  1631.  
  1632. #ifdef __STDC__
  1633. pointer ckmalloc(int);
  1634. pointer ckrealloc(pointer, int);
  1635. void free(pointer);        /* defined in C library */
  1636. char *savestr(char *);
  1637. pointer stalloc(int);
  1638. void stunalloc(pointer);
  1639. void setstackmark(struct stackmark *);
  1640. void popstackmark(struct stackmark *);
  1641. void growstackblock(void);
  1642. void grabstackblock(int);
  1643. char *growstackstr(void);
  1644. char *makestrspace(void);
  1645. void ungrabstackstr(char *, char *);
  1646. #else
  1647. pointer ckmalloc();
  1648. pointer ckrealloc();
  1649. void free();        /* defined in C library */
  1650. char *savestr();
  1651. pointer stalloc();
  1652. void stunalloc();
  1653. void setstackmark();
  1654. void popstackmark();
  1655. void growstackblock();
  1656. void grabstackblock();
  1657. char *growstackstr();
  1658. char *makestrspace();
  1659. void ungrabstackstr();
  1660. #endif
  1661.  
  1662.  
  1663.  
  1664. #define stackblock() stacknxt
  1665. #define stackblocksize() stacknleft
  1666. #define STARTSTACKSTR(p)    p = stackblock(), sstrnleft = stackblocksize()
  1667. #define STPUTC(c, p)    (--sstrnleft >= 0? (*p++ = (c)) : (p = growstackstr(), *p++ = (c)))
  1668. #define CHECKSTRSPACE(n, p)    if (sstrnleft < n) p = makestrspace(); else
  1669. #define USTPUTC(c, p)    (--sstrnleft, *p++ = (c))
  1670. #define STACKSTRNUL(p)    (sstrnleft == 0? (p = growstackstr(), *p = '\0') : (*p = '\0'))
  1671. #define STUNPUTC(p)    (++sstrnleft, --p)
  1672. #define STTOPC(p)    p[-1]
  1673. #define STADJUST(amount, p)    (p += (amount), sstrnleft -= (amount))
  1674. #define grabstackstr(p)    stalloc(stackblocksize() - sstrnleft)
  1675.  
  1676. #define ckfree(p)    free((pointer)(p))
  1677. EOF
  1678. if test `wc -c < memalloc.h` -ne 1787
  1679. then    echo 'memalloc.h is the wrong size'
  1680. fi
  1681. echo extracting memalloc.c
  1682. cat > memalloc.c <<\EOF
  1683. /*
  1684.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1685.  * This file is part of ash, which is distributed under the terms specified
  1686.  * by the Ash General Public License.  See the file named LICENSE.
  1687.  */
  1688.  
  1689. #include "shell.h"
  1690. #include "output.h"
  1691. #include "memalloc.h"
  1692. #include "error.h"
  1693. #include "machdep.h"
  1694. #include "mystring.h"
  1695.  
  1696.  
  1697.  
  1698. /*
  1699.  * Like malloc, but returns an error when out of space.
  1700.  */
  1701.  
  1702. pointer
  1703. ckmalloc(nbytes) {
  1704.       register pointer p;
  1705.       pointer malloc();
  1706.  
  1707.       if ((p = malloc(nbytes)) == NULL)
  1708.         error("Out of space");
  1709.       return p;
  1710. }
  1711.  
  1712.  
  1713. /*
  1714.  * Same for realloc.
  1715.  */
  1716.  
  1717. pointer
  1718. ckrealloc(p, nbytes)
  1719.       register pointer p;
  1720.       {
  1721.       pointer realloc();
  1722.  
  1723.       if ((p = realloc(p, nbytes)) == NULL)
  1724.         error("Out of space");
  1725.       return p;
  1726. }
  1727.  
  1728.  
  1729. /*
  1730.  * Make a copy of a string in safe storage.
  1731.  */
  1732.  
  1733. char *
  1734. savestr(s)
  1735.       char *s;
  1736.       {
  1737.       register char *p;
  1738.  
  1739.       p = ckmalloc(strlen(s) + 1);
  1740.       scopy(s, p);
  1741.       return p;
  1742. }
  1743.  
  1744.  
  1745. /*
  1746.  * Parse trees for commands are allocated in lifo order, so we use a stack
  1747.  * to make this more efficient, and also to avoid all sorts of exception
  1748.  * handling code to handle interrupts in the middle of a parse.
  1749.  *
  1750.  * The size 504 was chosen because the Ultrix malloc handles that size
  1751.  * well.
  1752.  */
  1753.  
  1754. #define MINSIZE 504        /* minimum size of a block */
  1755.  
  1756.  
  1757. struct stack_block {
  1758.       struct stack_block *prev;
  1759.       char space[MINSIZE];
  1760. };
  1761.  
  1762. struct stack_block stackbase;
  1763. struct stack_block *stackp = &stackbase;
  1764. char *stacknxt = stackbase.space;
  1765. int stacknleft = MINSIZE;
  1766. int sstrnleft;
  1767. int herefd = -1;
  1768.  
  1769.  
  1770.  
  1771. pointer
  1772. stalloc(nbytes) {
  1773.       register char *p;
  1774.  
  1775.       nbytes = ALIGN(nbytes);
  1776.       if (nbytes > stacknleft) {
  1777.         int blocksize;
  1778.         struct stack_block *sp;
  1779.  
  1780.         blocksize = nbytes;
  1781.         if (blocksize < MINSIZE)
  1782.           blocksize = MINSIZE;
  1783.         INTOFF;
  1784.         sp = ckmalloc(sizeof(struct stack_block) - MINSIZE + blocksize);
  1785.         sp->prev = stackp;
  1786.         stacknxt = sp->space;
  1787.         stacknleft = blocksize;
  1788.         stackp = sp;
  1789.         INTON;
  1790.       }
  1791.       p = stacknxt;
  1792.       stacknxt += nbytes;
  1793.       stacknleft -= nbytes;
  1794.       return p;
  1795. }
  1796.  
  1797.  
  1798. void
  1799. stunalloc(p)
  1800.       pointer p;
  1801.       {
  1802.       if (p == NULL) {        /*DEBUG */
  1803.         write(2, "stunalloc\n", 10);
  1804.         abort();
  1805.       }
  1806.       stacknleft += stacknxt - (char *)p;
  1807.       stacknxt = p;
  1808. }
  1809.  
  1810.  
  1811.  
  1812. void
  1813. setstackmark(mark)
  1814.       struct stackmark *mark;
  1815.       {
  1816.       mark->stackp = stackp;
  1817.       mark->stacknxt = stacknxt;
  1818.       mark->stacknleft = stacknleft;
  1819. }
  1820.  
  1821.  
  1822. void
  1823. popstackmark(mark)
  1824.       struct stackmark *mark;
  1825.       {
  1826.       struct stack_block *sp;
  1827.  
  1828.       INTOFF;
  1829.       while (stackp != mark->stackp) {
  1830.         sp = stackp;
  1831.         stackp = sp->prev;
  1832.         ckfree(sp);
  1833.       }
  1834.       stacknxt = mark->stacknxt;
  1835.       stacknleft = mark->stacknleft;
  1836.       INTON;
  1837. }
  1838.  
  1839.  
  1840. /*
  1841.  * When the parser reads in a string, it wants to stick the string on the
  1842.  * stack and only adjust the stack pointer when it knows how big the
  1843.  * string is.  Stackblock (defined in stack.h) returns a pointer to a block
  1844.  * of space on top of the stack and stackblocklen returns the length of
  1845.  * this block.  Growstackblock will grow this space by at least one byte,
  1846.  * possibly moving it (like realloc).  Grabstackblock actually allocates the
  1847.  * part of the block that has been used.
  1848.  */
  1849.  
  1850. void
  1851. growstackblock() {
  1852.       char *p;
  1853.       int newlen = stacknleft * 2 + 100;
  1854.       char *oldspace = stacknxt;
  1855.       int oldlen = stacknleft;
  1856.       struct stack_block *sp;
  1857.  
  1858.       if (stacknxt == stackp->space && stackp != &stackbase) {
  1859.         INTOFF;
  1860.         sp = stackp;
  1861.         stackp = sp->prev;
  1862.         sp = ckrealloc((pointer)sp, sizeof(struct stack_block) - MINSIZE + newlen);
  1863.         sp->prev = stackp;
  1864.         stackp = sp;
  1865.         stacknxt = sp->space;
  1866.         stacknleft = newlen;
  1867.         INTON;
  1868.       } else {
  1869.         p = stalloc(newlen);
  1870.         bcopy(oldspace, p, oldlen);
  1871.         stacknxt = p;            /* free the space */
  1872.         stacknleft += newlen;        /* we just allocated */
  1873.       }
  1874. }
  1875.  
  1876.  
  1877.  
  1878. void
  1879. grabstackblock(len) {
  1880.       len = ALIGN(len);
  1881.       stacknxt += len;
  1882.       stacknleft -= len;
  1883. }
  1884.  
  1885.  
  1886.  
  1887. /*
  1888.  * The following routines are somewhat easier to use that the above.
  1889.  * The user declares a variable of type STACKSTR, which may be declared
  1890.  * to be a register.  The macro STARTSTACKSTR initializes things.  Then
  1891.  * the user uses the macro STPUTC to add characters to the string.  In
  1892.  * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
  1893.  * grown as necessary.  When the user is done, she can just leave the
  1894.  * string there and refer to it using stackblock().  Or she can allocate
  1895.  * the space for it using grabstackstr().  If it is necessary to allow
  1896.  * someone else to use the stack temporarily and then continue to grow
  1897.  * the string, the user should use grabstack to allocate the space, and
  1898.  * then call ungrabstr(p) to return to the previous mode of operation.
  1899.  *
  1900.  * USTPUTC is like STPUTC except that it doesn't check for overflow.
  1901.  * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
  1902.  * is space for at least one character.
  1903.  */
  1904.  
  1905.  
  1906. char *
  1907. growstackstr() {
  1908.       int len = stackblocksize();
  1909.       if (herefd >= 0 && len >= 1024) {
  1910.         xwrite(herefd, stackblock(), len);
  1911.         sstrnleft = len - 1;
  1912.         return stackblock();
  1913.       }
  1914.       growstackblock();
  1915.       sstrnleft = stackblocksize() - len - 1;
  1916.       return stackblock() + len;
  1917. }
  1918.  
  1919.  
  1920. /*
  1921.  * Called from CHECKSTRSPACE.
  1922.  */
  1923.  
  1924. char *
  1925. makestrspace() {
  1926.       int len = stackblocksize() - sstrnleft;
  1927.       growstackblock();
  1928.       sstrnleft = stackblocksize() - len;
  1929.       return stackblock() + len;
  1930. }
  1931.  
  1932.  
  1933.  
  1934. void
  1935. ungrabstackstr(s, p)
  1936.       char *s;
  1937.       char *p;
  1938.       {
  1939.       stacknleft += stacknxt - s;
  1940.       stacknxt = s;
  1941.       sstrnleft = stacknleft - (p - s);
  1942. }
  1943. EOF
  1944. if test `wc -c < memalloc.c` -ne 5694
  1945. then    echo 'memalloc.c is the wrong size'
  1946. fi
  1947. echo extracting miscbltin.c
  1948. cat > miscbltin.c <<\EOF
  1949. /*
  1950.  * Miscelaneous builtins.
  1951.  *
  1952.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1953.  * This file is part of ash, which is distributed under the terms specified
  1954.  * by the Ash General Public License.  See the file named LICENSE.
  1955.  */
  1956.  
  1957. #include "shell.h"
  1958. #include "options.h"
  1959. #include "var.h"
  1960. #include "output.h"
  1961. #include "memalloc.h"
  1962. #include "error.h"
  1963. #include "mystring.h"
  1964.  
  1965. #undef eflag
  1966.  
  1967. extern char **argptr;        /* argument list for builtin command */
  1968.  
  1969.  
  1970. /*
  1971.  * The read builtin.  The -e option causes backslashes to escape the
  1972.  * following character.
  1973.  *
  1974.  * This uses unbuffered input, which may be avoidable in some cases.
  1975.  */
  1976.  
  1977. readcmd(argc, argv)  char **argv; {
  1978.       char **ap;
  1979.       int backslash;
  1980.       char c;
  1981.       int eflag;
  1982.       char *prompt;
  1983.       char *ifs;
  1984.       char *p;
  1985.       int startword;
  1986.       int status;
  1987.       int i;
  1988.  
  1989.       eflag = 0;
  1990.       prompt = NULL;
  1991.       while ((i = nextopt("ep:")) != '\0') {
  1992.         if (i == 'p')
  1993.           prompt = optarg;
  1994.         else
  1995.           eflag = 1;
  1996.       }
  1997.       if (prompt && isatty(0)) {
  1998.         out2str(prompt);
  1999.         flushall();
  2000.       }
  2001.       if ((ap = argptr) == NULL)
  2002.         error("arg count");
  2003.       if ((ifs = bltinlookup("IFS", 1)) == NULL)
  2004.         ifs = nullstr;
  2005.       status = 0;
  2006.       startword = 1;
  2007.       backslash = 0;
  2008.       STARTSTACKSTR(p);
  2009.       for (;;) {
  2010.         if (read(0, &c, 1) != 1) {
  2011.           status = 1;
  2012.           break;
  2013.         }
  2014.         if (c == '\0')
  2015.           continue;
  2016.         if (backslash) {
  2017.           backslash = 0;
  2018.           if (c != '\n')
  2019.             STPUTC(c, p);
  2020.           continue;
  2021.         }
  2022.         if (eflag && c == '\\') {
  2023.           backslash++;
  2024.           continue;
  2025.         }
  2026.         if (c == '\n')
  2027.           break;
  2028.         if (startword && *ifs == ' ' && strchr(ifs, c)) {
  2029.           continue;
  2030.         }
  2031.         startword = 0;
  2032.         if (backslash && c == '\\') {
  2033.           if (read(0, &c, 1) != 1) {
  2034.             status = 1;
  2035.             break;
  2036.           }
  2037.           STPUTC(c, p);
  2038.         } else if (ap[1] != NULL && strchr(ifs, c) != NULL) {
  2039.           STACKSTRNUL(p);
  2040.           setvar(*ap, stackblock(), 0);
  2041.           ap++;
  2042.           startword = 1;
  2043.           STARTSTACKSTR(p);
  2044.         } else {
  2045.           STPUTC(c, p);
  2046.         }
  2047.       }
  2048.       STACKSTRNUL(p);
  2049.       setvar(*ap, stackblock(), 0);
  2050.       while (*++ap != NULL)
  2051.         setvar(*ap, nullstr, 0);
  2052.       return status;
  2053. }
  2054.  
  2055.  
  2056.  
  2057. umaskcmd(argc, argv)  char **argv; {
  2058.       int mask;
  2059.       char *p;
  2060.       int i;
  2061.  
  2062.       if ((p = argv[1]) == NULL) {
  2063.         INTOFF;
  2064.         mask = umask(0);
  2065.         umask(mask);
  2066.         INTON;
  2067.         out1fmt("%.4o\n", mask);    /* %#o might be better */
  2068.       } else {
  2069.         mask = 0;
  2070.         do {
  2071.           if ((unsigned)(i = *p - '0') >= 8)
  2072.             error("Illegal number: %s", argv[1]);
  2073.           mask = (mask << 3) + i;
  2074.         } while (*++p != '\0');
  2075.         umask(mask);
  2076.       }
  2077.       return 0;
  2078. }
  2079. EOF
  2080. if test `wc -c < miscbltin.c` -ne 2633
  2081. then    echo 'miscbltin.c is the wrong size'
  2082. fi
  2083. echo extracting mkbuiltins
  2084. cat > mkbuiltins <<\EOF
  2085. # Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  2086. # This file is part of ash, which is distributed under the terms specified
  2087. # by the Ash General Public License.  See the file named LICENSE.
  2088.  
  2089. temp=/tmp/ka$$
  2090. havejobs=0
  2091. if grep '^#define JOBS[     ]*1' shell.h > /dev/null
  2092. then    havejobs=1
  2093. fi
  2094. exec > builtins.c
  2095. cat <<\!
  2096. /*
  2097.  * This file was generated by the mkbuiltins program.
  2098.  */
  2099.  
  2100. #include "shell.h"
  2101. #include "builtins.h"
  2102.  
  2103. !
  2104. awk '/^[^#]/ {if('$havejobs' || $2 != "-j") print $0}' builtins |
  2105.     sed 's/-j//' > $temp
  2106. awk '{    printf "int %s();\n", $1}' $temp
  2107. echo '
  2108. int (*const builtinfunc[])() = {'
  2109. awk '/^[^#]/ {    printf "\t%s,\n", $1}' $temp
  2110. echo '};
  2111.  
  2112. const struct builtincmd builtincmd[] = {'
  2113. awk '{    for (i = 2 ; i <= NF ; i++) {
  2114.         printf "\t\"%s\", %d,\n",  $i, NR-1
  2115.     }}' $temp
  2116. echo '    NULL, 0
  2117. };'
  2118.  
  2119. exec > builtins.h
  2120. cat <<\!
  2121. /*
  2122.  * This file was generated by the mkbuiltins program.
  2123.  */
  2124.  
  2125. !
  2126. tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ < $temp |
  2127.     awk '{    printf "#define %s %d\n", $1, NR-1}'
  2128. echo '
  2129. struct builtincmd {
  2130.       char *name;
  2131.       int code;
  2132. };
  2133.  
  2134. extern int (*const builtinfunc[])();
  2135. extern const struct builtincmd builtincmd[];'
  2136. rm -f $temp
  2137. EOF
  2138. if test `wc -c < mkbuiltins` -ne 1168
  2139. then    echo 'mkbuiltins is the wrong size'
  2140. fi
  2141. echo Archive 4 unpacked
  2142. exit
  2143.  
  2144.