home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume19 / ash / part06 < prev    next >
Encoding:
Internet Message Format  |  1989-05-29  |  48.6 KB

  1. Subject:  v19i006:  A reimplementation of the System V shell, Part06/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 6
  8. Archive-name: ash/part06
  9.  
  10. # This is part 6 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 options.h
  15. cat > options.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. struct shparam {
  23.       int nparam;    /* number of positional parameters (without $0) */
  24.       char malloc;    /* true if parameter list dynamicly allocated */
  25.       char **p;        /* parameter list */
  26.       char **optnext;    /* next parameter to be processed by getopts */
  27.       char *optptr;    /* used by getopts */
  28. };
  29.  
  30.  
  31. extern const char optchar[10];    /* string specifying shell option characters */
  32. extern char optval[10];        /* values of the corresponding options */
  33.  
  34. #define eflag optval[0]
  35. #define fflag optval[1]
  36. #define Iflag optval[2]
  37. #define iflag optval[3]
  38. #define jflag optval[4]
  39. #define nflag optval[5]
  40. #define sflag optval[6]
  41. #define xflag optval[7]
  42. #define zflag optval[8]
  43.  
  44.  
  45. extern char *minusc;        /* argument to -c option */
  46. extern char *arg0;        /* $0 */
  47. extern struct shparam shellparam;  /* $@ */
  48. extern char **argptr;        /* argument list for builtin commands */
  49. extern char *optarg;        /* set by nextopt */
  50. extern char *optptr;        /* used by nextopt */
  51.  
  52.  
  53. #ifdef __STDC__
  54. void procargs(int, char **);
  55. void setparam(char **);
  56. void freeparam(struct shparam *);
  57. int nextopt(char *);
  58. #else
  59. void procargs();
  60. void setparam();
  61. void freeparam();
  62. int nextopt();
  63. #endif
  64. EOF
  65. if test `wc -c < options.h` -ne 1370
  66. then    echo 'options.h is the wrong size'
  67. fi
  68. echo extracting options.c
  69. cat > options.c <<\EOF
  70. /*
  71.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  72.  * This file is part of ash, which is distributed under the terms specified
  73.  * by the Ash General Public License.  See the file named LICENSE.
  74.  */
  75.  
  76. #include "shell.h"
  77. #include "options.h"
  78. #include "nodes.h"    /* for other header files */
  79. #include "eval.h"
  80. #include "jobs.h"
  81. #include "input.h"
  82. #include "output.h"
  83. #include "trap.h"
  84. #include "var.h"
  85. #include "memalloc.h"
  86. #include "error.h"
  87. #include "mystring.h"
  88.  
  89.  
  90. const char optchar[10] = "efIijnsxz";    /* shell flags */
  91. char optval[10];        /* values of option flags */
  92. char *arg0;            /* value of $0 */
  93. struct shparam shellparam;    /* current positional parameters */
  94. char **argptr;            /* argument list for builtin commands */
  95. char *optarg;            /* set by nextopt (like getopt) */
  96. char *optptr;            /* used by nextopt */
  97.  
  98. char *minusc;            /* argument to -c option */
  99.  
  100.  
  101. #ifdef __STDC__
  102. STATIC void options(int);
  103. STATIC void setoption(int, int);
  104. #else
  105. STATIC void options();
  106. STATIC void setoption();
  107. #endif
  108.  
  109.  
  110.  
  111. /*
  112.  * Process the shell command line arguments.
  113.  */
  114.  
  115. void
  116. procargs(argc, argv)
  117.       char **argv;
  118.       {
  119.       char *p;
  120.  
  121.       argptr = argv;
  122.       if (argc > 0)
  123.         argptr++;
  124.       for (p = optval ; p < optval + sizeof optval - 1 ; p++)
  125.         *p = 2;
  126.       options(1);
  127.       if (*argptr == NULL && minusc == NULL)
  128.         sflag = 1;
  129.       if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
  130.         iflag = 1;
  131.       if (jflag == 2)
  132.         jflag = iflag;
  133.       for (p = optval ; p < optval + sizeof optval - 1 ; p++)
  134.         if (*p == 2)
  135.           *p = 0;
  136.       arg0 = argv[0];
  137.       if (sflag == 0 && minusc == NULL) {
  138.         commandname = arg0 = *argptr++;
  139.         setinputfile(commandname, 0);
  140.       }
  141.       shellparam.p = argptr;
  142.       /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
  143.       while (*argptr) {
  144.         shellparam.nparam++;
  145.         argptr++;
  146.       }
  147.       setinteractive(iflag);
  148.       setjobctl(jflag);
  149. }
  150.  
  151.  
  152.  
  153. /*
  154.  * Process shell options.  The global variable argptr contains a pointer
  155.  * to the argument list; we advance it past the options.
  156.  */
  157.  
  158. STATIC void
  159. options(cmdline) {
  160.       register char *p;
  161.       int val;
  162.       int c;
  163.  
  164.       minusc = NULL;
  165.       while ((p = *argptr) != NULL) {
  166.         argptr++;
  167.         if ((c = *p++) == '-') {
  168.           val = 1;
  169.           if (p[0] == '-' && p[1] == '\0')
  170.             break;        /* "--" terminates options */
  171.         } else if (c == '+') {
  172.           val = 0;
  173.         } else {
  174.           argptr--;
  175.           break;
  176.         }
  177.         while ((c = *p++) != '\0') {
  178.           if (c == 'c' && cmdline) {
  179.             if (*p == '\0')
  180.                   p = *argptr++;
  181.             if (p == NULL || minusc != NULL)
  182.                   error("Bad -c option");
  183.             minusc = p;
  184.             break;
  185.           } else {
  186.             setoption(c, val);
  187.           }
  188.         }
  189.         if (! cmdline)
  190.           break;
  191.       }
  192. }
  193.  
  194.  
  195. STATIC void
  196. setoption(flag, val)
  197.       char flag;
  198.       int val;
  199.       {
  200.       register char *p;
  201.  
  202.       if ((p = strchr(optchar, flag)) == NULL)
  203.         error("Illegal option -%c", flag);
  204.       optval[p - optchar] = val;
  205. }
  206.  
  207.  
  208.  
  209. #ifdef mkinit
  210. INCLUDE "options.h"
  211.  
  212. SHELLPROC {
  213.       char *p;
  214.  
  215.       for (p = optval ; p < optval + sizeof optval ; p++)
  216.         *p = 0;
  217. }
  218. #endif
  219.  
  220.  
  221. /*
  222.  * Set the shell parameters.
  223.  */
  224.  
  225. void
  226. setparam(argv)
  227.       char **argv;
  228.       {
  229.       char **newparam;
  230.       char **ap;
  231.       int nparam;
  232.  
  233.       for (nparam = 0 ; argv[nparam] ; nparam++);
  234.       ap = newparam = ckmalloc((nparam + 1) * sizeof *ap);
  235.       while (*argv) {
  236.         *ap++ = savestr(*argv++);
  237.       }
  238.       *ap = NULL;
  239.       freeparam(&shellparam);
  240.       shellparam.malloc = 1;
  241.       shellparam.nparam = nparam;
  242.       shellparam.p = newparam;
  243.       shellparam.optnext = NULL;
  244. }
  245.  
  246.  
  247. /*
  248.  * Free the list of positional parameters.
  249.  */
  250.  
  251. void
  252. freeparam(param)
  253.       struct shparam *param;
  254.       {
  255.       char **ap;
  256.  
  257.       if (param->malloc) {
  258.         for (ap = param->p ; *ap ; ap++)
  259.           ckfree(*ap);
  260.         ckfree(param->p);
  261.       }
  262. }
  263.  
  264.  
  265.  
  266. /*
  267.  * The shift builtin command.
  268.  */
  269.  
  270. shiftcmd(argc, argv)  char **argv; {
  271.       int n;
  272.       char **ap1, **ap2;
  273.  
  274.       n = 1;
  275.       if (argc > 1)
  276.         n = number(argv[1]);
  277.       if (n > shellparam.nparam)
  278.         n = shellparam.nparam;
  279.       INTOFF;
  280.       shellparam.nparam -= n;
  281.       for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
  282.         if (shellparam.malloc)
  283.           ckfree(*ap1);
  284.       }
  285.       ap2 = shellparam.p;
  286.       while ((*ap2++ = *ap1++) != NULL);
  287.       shellparam.optnext = NULL;
  288.       INTON;
  289.       return 0;
  290. }
  291.  
  292.  
  293.  
  294. /*
  295.  * The set command builtin.
  296.  */
  297.  
  298. setcmd(argc, argv)  char **argv; {
  299.       if (argc == 1)
  300.         return showvarscmd(argc, argv);
  301.       INTOFF;
  302.       options(0);
  303.       setinteractive(iflag);
  304.       setjobctl(jflag);
  305.       if (*argptr != NULL) {
  306.         setparam(argptr);
  307.       }
  308.       INTON;
  309.       return 0;
  310. }
  311.  
  312.  
  313. /*
  314.  * The getopts builtin.  Shellparam.optnext points to the next argument
  315.  * to be processed.  Shellparam.optptr points to the next character to
  316.  * be processed in the current argument.  If shellparam.optnext is NULL,
  317.  * then it's the first time getopts has been called.
  318.  */
  319.  
  320. getoptscmd(argc, argv)  char **argv; {
  321.       register char *p, *q;
  322.       char c;
  323.       char s[10];
  324.  
  325.       if (argc != 3)
  326.         error("Usage: getopts optstring var");
  327.       if (shellparam.optnext == NULL) {
  328.         shellparam.optnext = shellparam.p;
  329.         shellparam.optptr = NULL;
  330.       }
  331.       if ((p = shellparam.optptr) == NULL || *p == '\0') {
  332.         p = *shellparam.optnext;
  333.         if (p == NULL || *p != '-' || *++p == '\0') {
  334. atend:
  335.           fmtstr(s, 10, "%d", shellparam.optnext - shellparam.p + 1);
  336.           setvar("OPTIND", s, 0);
  337.           shellparam.optnext = NULL;
  338.           return 1;
  339.         }
  340.         shellparam.optnext++;
  341.         if (p[0] == '-' && p[1] == '\0')    /* check for "--" */
  342.           goto atend;
  343.       }
  344.       c = *p++;
  345.       for (q = argv[1] ; *q != c ; ) {
  346.         if (*q == '\0') {
  347.           out1fmt("Illegal option -%c\n", c);
  348.           c = '?';
  349.           goto out;
  350.         }
  351.         if (*++q == ':')
  352.           q++;
  353.       }
  354.       if (*++q == ':') {
  355.         if (*p == '\0' && (p = *shellparam.optnext) == NULL) {
  356.           out1fmt("No arg for -%c option\n", c);
  357.           c = '?';
  358.           goto out;
  359.         }
  360.         shellparam.optnext++;
  361.         setvar("OPTARG", p, 0);
  362.         p = NULL;
  363.       }
  364. out:
  365.       shellparam.optptr = p;
  366.       s[0] = c;
  367.       s[1] = '\0';
  368.       setvar(argv[2], s, 0);
  369.       return 0;
  370. }
  371.  
  372.  
  373. /*
  374.  * Standard option processing (a la getopt) for builtin routines.  The
  375.  * only argument that is passed to nextopt is the option string; the
  376.  * other arguments are unnecessary.  It return the character, or '\0' on
  377.  * end of input.
  378.  */
  379.  
  380. int
  381. nextopt(optstring)
  382.       char *optstring;
  383.       {
  384.       register char *p, *q;
  385.       char c;
  386.  
  387.       if ((p = optptr) == NULL || *p == '\0') {
  388.         p = *argptr;
  389.         if (p == NULL || *p != '-' || *++p == '\0')
  390.           return '\0';
  391.         argptr++;
  392.         if (p[0] == '-' && p[1] == '\0')    /* check for "--" */
  393.           return '\0';
  394.       }
  395.       c = *p++;
  396.       for (q = optstring ; *q != c ; ) {
  397.         if (*q == '\0')
  398.           error("Illegal option -%c", c);
  399.         if (*++q == ':')
  400.           q++;
  401.       }
  402.       if (*++q == ':') {
  403.         if (*p == '\0' && (p = *argptr++) == NULL)
  404.           error("No arg for -%c option", c);
  405.         optarg = p;
  406.         p = NULL;
  407.       }
  408.       optptr = p;
  409.       return c;
  410. }
  411. EOF
  412. if test `wc -c < options.c` -ne 7075
  413. then    echo 'options.c is the wrong size'
  414. fi
  415. echo extracting output.h
  416. cat > output.h <<\EOF
  417. /*
  418.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  419.  * This file is part of ash, which is distributed under the terms specified
  420.  * by the Ash General Public License.  See the file named LICENSE.
  421.  */
  422.  
  423. #ifndef OUTPUT_INCL
  424.  
  425. struct output {
  426.       char *nextc;
  427.       int nleft;
  428.       char *buf;
  429.       int bufsize;
  430.       short fd;
  431.       short flags;
  432. };
  433.  
  434. extern struct output output;
  435. extern struct output errout;
  436. extern struct output memout;
  437. extern struct output *out1;
  438. extern struct output *out2;
  439.  
  440.  
  441. #ifdef __STDC__
  442. void outstr(char *, struct output *);
  443. void out1str(char *);
  444. void out2str(char *);
  445. void outfmt(struct output *, char *, ...);
  446. void out1fmt(char *, ...);
  447. void fmtstr(char *, int, char *, ...);
  448. /* void doformat(struct output *, char *, va_list); */
  449. void doformat();
  450. void emptyoutbuf(struct output *);
  451. void flushall(void);
  452. void flushout(struct output *);
  453. void freestdout(void);
  454. int xwrite(int, char *, int);
  455. int xioctl(int, int, int);
  456. #else
  457. void outstr();
  458. void out1str();
  459. void out2str();
  460. void outfmt();
  461. void out1fmt();
  462. void fmtstr();
  463. /* void doformat(); */
  464. void doformat();
  465. void emptyoutbuf();
  466. void flushall();
  467. void flushout();
  468. void freestdout();
  469. int xwrite();
  470. int xioctl();
  471. #endif
  472.  
  473. #define outc(c, file)    (--(file)->nleft < 0? (emptyoutbuf(file), *(file)->nextc++ = (c)) : (*(file)->nextc++ = (c)))
  474. #define out1c(c)    outc(c, out1);
  475. #define out2c(c)    outc(c, out2);
  476.  
  477. #define OUTPUT_INCL
  478. #endif
  479. EOF
  480. if test `wc -c < output.h` -ne 1412
  481. then    echo 'output.h is the wrong size'
  482. fi
  483. echo extracting output.c
  484. cat > output.c <<\EOF
  485. /*
  486.  * Shell output routines.  We use our own output routines because:
  487.  *    When a builtin command is interrupted we have to discard
  488.  *        any pending output.
  489.  *    When a builtin command appears in back quotes, we want to
  490.  *        save the output of the command in a region obtained
  491.  *        via malloc, rather than doing a fork and reading the
  492.  *        output of the command via a pipe.
  493.  *    Our output routines may be smaller than the stdio routines.
  494.  *
  495.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  496.  * This file is part of ash, which is distributed under the terms specified
  497.  * by the Ash General Public License.  See the file named LICENSE.
  498.  */
  499.  
  500. #include <stdio.h>    /* defines BUFSIZ */
  501. #include "shell.h"
  502. #include "syntax.h"
  503. #include "output.h"
  504. #include "memalloc.h"
  505. #include "error.h"
  506. #ifdef __STDC__
  507. #include "stdarg.h"
  508. #else
  509. #include <varargs.h>
  510. #endif
  511. #include "myerrno.h"
  512.  
  513.  
  514. #define OUTBUFSIZ BUFSIZ
  515. #define BLOCK_OUT -2        /* output to a fixed block of memory */
  516. #define MEM_OUT -3        /* output to dynamically allocated memory */
  517. #define OUTPUT_ERR 01        /* error occurred on output */
  518.  
  519.  
  520. struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0};
  521. struct output errout = {NULL, 0, NULL, 100, 2, 0};;
  522. struct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0};
  523. struct output *out1 = &output;
  524. struct output *out2 = &errout;
  525.  
  526.  
  527.  
  528. #ifdef mkinit
  529.  
  530. INCLUDE "output.h"
  531. INCLUDE "memalloc.h"
  532.  
  533. RESET {
  534.       out1 = &output;
  535.       out2 = &errout;
  536.       if (memout.buf != NULL) {
  537.         ckfree(memout.buf);
  538.         memout.buf = NULL;
  539.       }
  540. }
  541.  
  542. #endif
  543.  
  544.  
  545. #ifdef notdef    /* no longer used */
  546. /*
  547.  * Set up an output file to write to memory rather than a file.
  548.  */
  549.  
  550. void
  551. open_mem(block, length, file)
  552.       char *block;
  553.       int length;
  554.       struct output *file;
  555.       {
  556.       file->nextc = block;
  557.       file->nleft = --length;
  558.       file->fd = BLOCK_OUT;
  559.       file->flags = 0;
  560. }
  561. #endif
  562.  
  563.  
  564. void
  565. out1str(p)
  566.       char *p;
  567.       {
  568.       outstr(p, out1);
  569. }
  570.  
  571.  
  572. void
  573. out2str(p)
  574.       char *p;
  575.       {
  576.       outstr(p, out2);
  577. }
  578.  
  579.  
  580. void
  581. outstr(p, file)
  582.       register char *p;
  583.       register struct output *file;
  584.       {
  585.       while (*p)
  586.         outc(*p++, file);
  587. }
  588.  
  589.  
  590. char out_junk[16];
  591.  
  592.  
  593. void
  594. emptyoutbuf(dest)
  595.       struct output *dest;
  596.       {
  597.       int offset;
  598.  
  599.       if (dest->fd == BLOCK_OUT) {
  600.         dest->nextc = out_junk;
  601.         dest->nleft = sizeof out_junk;
  602.         dest->flags |= OUTPUT_ERR;
  603.       } else if (dest->buf == NULL) {
  604.         INTOFF;
  605.         dest->buf = ckmalloc(dest->bufsize);
  606.         dest->nextc = dest->buf;
  607.         dest->nleft = dest->bufsize;
  608.         INTON;
  609.       } else if (dest->fd == MEM_OUT) {
  610.         offset = dest->bufsize;
  611.         INTOFF;
  612.         dest->bufsize <<= 1;
  613.         dest->buf = ckrealloc(dest->buf, dest->bufsize);
  614.         dest->nleft = dest->bufsize - offset;
  615.         dest->nextc = dest->buf + offset;
  616.         INTON;
  617.       } else {
  618.         flushout(dest);
  619.       }
  620.       dest->nleft--;
  621. }
  622.  
  623.  
  624. void
  625. flushall() {
  626.       flushout(&output);
  627.       flushout(&errout);
  628. }
  629.  
  630.  
  631. void
  632. flushout(dest)
  633.       struct output *dest;
  634.       {
  635.  
  636.       if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0)
  637.         return;
  638.       if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0)
  639.         dest->flags |= OUTPUT_ERR;
  640.       dest->nextc = dest->buf;
  641.       dest->nleft = dest->bufsize;
  642. }
  643.  
  644.  
  645. void
  646. freestdout() {
  647.       INTOFF;
  648.       if (output.buf) {
  649.         ckfree(output.buf);
  650.         output.buf = NULL;
  651.         output.nleft = 0;
  652.       }
  653.       INTON;
  654. }
  655.  
  656.  
  657. #ifdef __STDC__
  658. void
  659. outfmt(struct output *file, char *fmt, ...) {
  660.       va_list ap;
  661.  
  662.       va_start(ap, fmt);
  663.       doformat(file, fmt, ap);
  664.       va_end(ap);
  665. }
  666.  
  667.  
  668. void
  669. out1fmt(char *fmt, ...) {
  670.       va_list ap;
  671.  
  672.       va_start(ap, fmt);
  673.       doformat(out1, fmt, ap);
  674.       va_end(ap);
  675. }
  676.  
  677.  
  678. void
  679. fmtstr(char *outbuf, int length, char *fmt, ...) {
  680.       va_list ap;
  681.       struct output strout;
  682.  
  683.       va_start(ap, fmt);
  684.       strout.nextc = outbuf;
  685.       strout.nleft = length;
  686.       strout.fd = BLOCK_OUT;
  687.       strout.flags = 0;
  688.       doformat(&strout, fmt, ap);
  689.       outc('\0', &strout);
  690.       if (strout.flags & OUTPUT_ERR)
  691.         outbuf[length - 1] = '\0';
  692. }
  693.  
  694. #else /* not __STDC__ */
  695.  
  696. void
  697. outfmt(va_alist)
  698.       va_dcl
  699.       {
  700.       va_list ap;
  701.       struct output *file;
  702.       char *fmt;
  703.  
  704.       va_start(ap);
  705.       file = va_arg(ap, struct output *);
  706.       fmt = va_arg(ap, char *);
  707.       doformat(file, fmt, ap);
  708.       va_end(ap);
  709. }
  710.  
  711.  
  712. void
  713. out1fmt(va_alist)
  714.       va_dcl
  715.       {
  716.       va_list ap;
  717.       char *fmt;
  718.  
  719.       va_start(ap);
  720.       fmt = va_arg(ap, char *);
  721.       doformat(out1, fmt, ap);
  722.       va_end(ap);
  723. }
  724.  
  725.  
  726. void
  727. fmtstr(va_alist)
  728.       va_dcl
  729.       {
  730.       va_list ap;
  731.       struct output strout;
  732.       char *outbuf;
  733.       int length;
  734.       char *fmt;
  735.  
  736.       va_start(ap);
  737.       outbuf = va_arg(ap, char *);
  738.       length = va_arg(ap, int);
  739.       fmt = va_arg(ap, char *);
  740.       strout.nextc = outbuf;
  741.       strout.nleft = length;
  742.       strout.fd = BLOCK_OUT;
  743.       strout.flags = 0;
  744.       doformat(&strout, fmt, ap);
  745.       outc('\0', &strout);
  746.       if (strout.flags & OUTPUT_ERR)
  747.         outbuf[length - 1] = '\0';
  748. }
  749. #endif /* __STDC__ */
  750.  
  751.  
  752. /*
  753.  * Formatted output.  This routine handles a subset of the printf formats:
  754.  * - Formats supported: d, u, o, X, s, and c.
  755.  * - The x format is also accepted but is treated like X.
  756.  * - The l modifier is accepted.
  757.  * - The - and # flags are accepted; # only works with the o format.
  758.  * - Width and precision may be specified with any format except c.
  759.  * - An * may be given for the width or precision.
  760.  * - The obsolete practice of preceding the width with a zero to get
  761.  *   zero padding is not supported; use the precision field.
  762.  * - A % may be printed by writing %% in the format string.
  763.  */
  764.  
  765. #define TEMPSIZE 24
  766.  
  767. #ifdef __STDC__
  768. static const char digit[16] = "0123456789ABCDEF";
  769. #else
  770. static const char digit[17] = "0123456789ABCDEF";
  771. #endif
  772.  
  773.  
  774. void
  775. doformat(dest, f, ap)
  776.       register struct output *dest;
  777.       register char *f;        /* format string */
  778.       va_list ap;
  779.       {
  780.       register char c;
  781.       char temp[TEMPSIZE];
  782.       int flushleft;
  783.       int sharp;
  784.       int width;
  785.       int prec;
  786.       int islong;
  787.       char *p;
  788.       int sign;
  789.       long l;
  790.       unsigned long num;
  791.       unsigned base;
  792.       int len;
  793.       int size;
  794.       int pad;
  795.  
  796.       while ((c = *f++) != '\0') {
  797.         if (c != '%') {
  798.           outc(c, dest);
  799.           continue;
  800.         }
  801.         flushleft = 0;
  802.         sharp = 0;
  803.         width = 0;
  804.         prec = -1;
  805.         islong = 0;
  806.         for (;;) {
  807.           if (*f == '-')
  808.             flushleft++;
  809.           else if (*f == '#')
  810.             sharp++;
  811.           else
  812.             break;
  813.           f++;
  814.         }
  815.         if (*f == '*') {
  816.           width = va_arg(ap, int);
  817.           f++;
  818.         } else {
  819.           while (is_digit(*f)) {
  820.             width = 10 * width + digit_val(*f++);
  821.           }
  822.         }
  823.         if (*f == '.') {
  824.           if (*++f == '*') {
  825.             prec = va_arg(ap, int);
  826.             f++;
  827.           } else {
  828.             prec = 0;
  829.             while (is_digit(*f)) {
  830.                   prec = 10 * prec + digit_val(*f++);
  831.             }
  832.           }
  833.         }
  834.         if (*f == 'l') {
  835.           islong++;
  836.           f++;
  837.         }
  838.         switch (*f) {
  839.         case 'd':
  840.           if (islong)
  841.             l = va_arg(ap, long);
  842.           else
  843.             l = va_arg(ap, int);
  844.           sign = 0;
  845.           num = l;
  846.           if (l < 0) {
  847.             num = -l;
  848.             sign = 1;
  849.           }
  850.           base = 10;
  851.           goto number;
  852.         case 'u':
  853.           base = 10;
  854.           goto uns_number;
  855.         case 'o':
  856.           base = 8;
  857.           goto uns_number;
  858.         case 'x':
  859.           /* we don't implement 'x'; treat like 'X' */
  860.         case 'X':
  861.           base = 16;
  862. uns_number:      /* an unsigned number */
  863.           sign = 0;
  864.           if (islong)
  865.             num = va_arg(ap, unsigned long);
  866.           else
  867.             num = va_arg(ap, unsigned int);
  868. number:          /* process a number */
  869.           p = temp + TEMPSIZE - 1;
  870.           *p = '\0';
  871.           while (num) {
  872.             *--p = digit[num % base];
  873.             num /= base;
  874.           }
  875.           len = (temp + TEMPSIZE - 1) - p;
  876.           if (prec < 0)
  877.             prec = 1;
  878.           if (sharp && *f == 'o' && prec <= len)
  879.             prec = len + 1;
  880.           pad = 0;
  881.           if (width) {
  882.             size = len;
  883.             if (size < prec)
  884.                   size = prec;
  885.             size += sign;
  886.             pad = width - size;
  887.             if (flushleft == 0) {
  888.                   while (--pad >= 0)
  889.                     outc(' ', dest);
  890.             }
  891.           }
  892.           if (sign)
  893.             outc('-', dest);
  894.           prec -= len;
  895.           while (--prec >= 0)
  896.             outc('0', dest);
  897.           while (*p)
  898.             outc(*p++, dest);
  899.           while (--pad >= 0)
  900.             outc(' ', dest);
  901.           break;
  902.         case 's':
  903.           p = va_arg(ap, char *);
  904.           pad = 0;
  905.           if (width) {
  906.             len = strlen(p);
  907.             if (prec >= 0 && len > prec)
  908.                   len = prec;
  909.             pad = width - len;
  910.             if (flushleft == 0) {
  911.                   while (--pad >= 0)
  912.                     outc(' ', dest);
  913.             }
  914.           }
  915.           prec++;
  916.           while (--prec != 0 && *p)
  917.             outc(*p++, dest);
  918.           while (--pad >= 0)
  919.             outc(' ', dest);
  920.           break;
  921.         case 'c':
  922.           c = va_arg(ap, int);
  923.           outc(c, dest);
  924.           break;
  925.         default:
  926.           outc(*f, dest);
  927.           break;
  928.         }
  929.         f++;
  930.       }
  931. }
  932.  
  933.  
  934.  
  935. /*
  936.  * Version of write which resumes after a signal is caught.
  937.  */
  938.  
  939. int
  940. xwrite(fd, buf, nbytes)
  941.       int fd;
  942.       char *buf;
  943.       int nbytes;
  944.       {
  945.       int ntry;
  946.       int i;
  947.       int n;
  948.  
  949.       n = nbytes;
  950.       ntry = 0;
  951.       for (;;) {
  952.         i = write(fd, buf, n);
  953.         if (i > 0) {
  954.           if ((n -= i) <= 0)
  955.             return nbytes;
  956.           buf += i;
  957.           ntry = 0;
  958.         } else if (i == 0) {
  959.           if (++ntry > 10)
  960.             return nbytes - n;
  961.         } else if (errno != EINTR) {
  962.           return -1;
  963.         }
  964.       }
  965. }
  966.  
  967.  
  968. /*
  969.  * Version of ioctl that retries after a signal is caught.
  970.  */
  971.  
  972. int
  973. xioctl(fd, request, arg) {
  974.       int i;
  975.  
  976.       while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR);
  977.       return i;
  978. }
  979. EOF
  980. if test `wc -c < output.c` -ne 9365
  981. then    echo 'output.c is the wrong size'
  982. fi
  983. echo extracting parser.h
  984. cat > parser.h <<\EOF
  985. /*
  986.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  987.  * This file is part of ash, which is distributed under the terms specified
  988.  * by the Ash General Public License.  See the file named LICENSE.
  989.  */
  990.  
  991. /* control characters in argument strings */
  992. #define CTLESC '\201'
  993. #define CTLVAR '\202'
  994. #define CTLENDVAR '\203'
  995. #define CTLBACKQ '\204'
  996. #define CTLQUOTE 01        /* ored with CTLBACKQ code if in quotes */
  997.  
  998. /* variable substitution byte (follows CTLVAR) */
  999. #define VSTYPE 07        /* type of variable substitution */
  1000. #define VSNUL 040        /* colon--treat the empty string as unset */
  1001. #define VSQUOTE 0100        /* inside double quotes--suppress splitting */
  1002.  
  1003. /* values of VSTYPE field */
  1004. #define VSNORMAL 1        /* normal variable:  $var or ${var} */
  1005. #define VSMINUS 2        /* ${var-text} */
  1006. #define VSPLUS 3        /* ${var+text} */
  1007. #define VSQUESTION 4        /* ${var?message} */
  1008. #define VSASSIGN 5        /* ${var=text} */
  1009.  
  1010.  
  1011. /*
  1012.  * NEOF is returned by parsecmd when it encounters an end of file.  It
  1013.  * must be distinct from NULL, so we use the address of a variable that
  1014.  * happens to be handy.
  1015.  */
  1016. extern int tokpushback;
  1017. #define NEOF ((union node *)&tokpushback)
  1018.  
  1019.  
  1020. #ifdef __STDC__
  1021. union node *parsecmd(int);
  1022. int goodname(char *);
  1023. #else
  1024. union node *parsecmd();
  1025. int goodname();
  1026. #endif
  1027. EOF
  1028. if test `wc -c < parser.h` -ne 1262
  1029. then    echo 'parser.h is the wrong size'
  1030. fi
  1031. echo extracting parser.c
  1032. cat > parser.c <<\EOF
  1033. /*
  1034.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1035.  * This file is part of ash, which is distributed under the terms specified
  1036.  * by the Ash General Public License.  See the file named LICENSE.
  1037.  */
  1038.  
  1039. #include "shell.h"
  1040. #include "parser.h"
  1041. #include "nodes.h"
  1042. #include "expand.h"    /* defines rmescapes() */
  1043. #include "redir.h"    /* defines copyfd() */
  1044. #include "syntax.h"
  1045. #include "options.h"
  1046. #include "input.h"
  1047. #include "output.h"
  1048. #include "var.h"
  1049. #include "error.h"
  1050. #include "memalloc.h"
  1051. #include "mystring.h"
  1052.  
  1053.  
  1054. /*
  1055.  * Shell command parser.
  1056.  */
  1057.  
  1058. #define EOFMARKLEN 79
  1059.  
  1060. /* values returned by readtoken */
  1061. #include "token.def"
  1062.  
  1063.  
  1064.  
  1065. struct heredoc {
  1066.       struct heredoc *next;    /* next here document in list */
  1067.       union node *here;        /* redirection node */
  1068.       char *eofmark;        /* string indicating end of input */
  1069.       int striptabs;        /* if set, strip leading tabs */
  1070. };
  1071.  
  1072.  
  1073.  
  1074. struct heredoc *heredoclist;    /* list of here documents to read */
  1075. int parsebackquote;        /* nonzero if we are inside backquotes */
  1076. int doprompt;            /* if set, prompt the user */
  1077. int needprompt;            /* true if interactive and at start of line */
  1078. int lasttoken;            /* last token read */
  1079. MKINIT int tokpushback;        /* last token pushed back */
  1080. char *wordtext;            /* text of last word returned by readtoken */
  1081. struct nodelist *backquotelist;
  1082. union node *redirnode;
  1083. struct heredoc *heredoc;
  1084. int quoteflag;            /* set if (part of) last token was quoted */
  1085. int startlinno;            /* line # where last token started */
  1086.  
  1087.  
  1088. #define GDB_HACK 1 /* avoid local declarations which gdb can't handle */
  1089. #ifdef GDB_HACK
  1090. static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'};
  1091. static const char types[] = "}-+?=";
  1092. #endif
  1093.  
  1094.  
  1095. #ifdef __STDC__
  1096. STATIC union node *list(int);
  1097. STATIC union node *andor(void);
  1098. STATIC union node *pipeline(void);
  1099. STATIC union node *command(void);
  1100. STATIC union node *simplecmd(void);
  1101. STATIC void parsefname(void);
  1102. STATIC void parseheredoc(void);
  1103. STATIC void checkkwd(void);
  1104. STATIC int readtoken(void);
  1105. STATIC int readtoken1(int, char const *, char *, int);
  1106. STATIC void attyline(void);
  1107. STATIC int noexpand(char *);
  1108. STATIC void synexpect(int);
  1109. STATIC void synerror(char *);
  1110. #else
  1111. STATIC union node *list();
  1112. STATIC union node *andor();
  1113. STATIC union node *pipeline();
  1114. STATIC union node *command();
  1115. STATIC union node *simplecmd();
  1116. STATIC void parsefname();
  1117. STATIC void parseheredoc();
  1118. STATIC void checkkwd();
  1119. STATIC int readtoken();
  1120. STATIC int readtoken1();
  1121. STATIC void attyline();
  1122. STATIC int noexpand();
  1123. STATIC void synexpect();
  1124. STATIC void synerror();
  1125. #endif
  1126.  
  1127. #if ATTY
  1128. #ifdef __STDC__
  1129. STATIC void putprompt(char *);
  1130. #else
  1131. STATIC void putprompt();
  1132. #endif
  1133. #else /* not ATTY */
  1134. #define putprompt(s)    out2str(s)
  1135. #endif
  1136.  
  1137.  
  1138.  
  1139.  
  1140. /*
  1141.  * Read and parse a command.  Returns NEOF on end of file.  (NULL is a
  1142.  * valid parse tree indicating a blank line.)
  1143.  */
  1144.  
  1145. union node *
  1146. parsecmd(interact) {
  1147.       int t;
  1148.  
  1149.       doprompt = interact;
  1150.       if (doprompt)
  1151.         putprompt(ps1val());
  1152.       needprompt = 0;
  1153.       if ((t = readtoken()) == TEOF)
  1154.         return NEOF;
  1155.       if (t == TNL)
  1156.         return NULL;
  1157.       tokpushback++;
  1158.       return list(1);
  1159. }
  1160.  
  1161.  
  1162. STATIC union node *
  1163. list(nlflag) {
  1164.       union node *n1, *n2, *n3;
  1165.  
  1166.       checkkwd();
  1167.       if (nlflag == 0 && tokendlist[lasttoken])
  1168.         return NULL;
  1169.       n1 = andor();
  1170.       for (;;) {
  1171.         switch (readtoken()) {
  1172.         case TBACKGND:
  1173.           if (n1->type == NCMD || n1->type == NPIPE) {
  1174.             n1->ncmd.backgnd = 1;
  1175.           } else if (n1->type == NREDIR) {
  1176.             n1->type = NBACKGND;
  1177.           } else {
  1178.             n3 = (union node *)stalloc(sizeof (struct nredir));
  1179.             n3->type = NBACKGND;
  1180.             n3->nredir.n = n1;
  1181.             n3->nredir.redirect = NULL;
  1182.             n1 = n3;
  1183.           }
  1184.           goto tsemi;
  1185.         case TNL:
  1186.           tokpushback++;
  1187.           /* fall through */
  1188. tsemi:        case TSEMI:
  1189.           if (readtoken() == TNL) {
  1190.             parseheredoc();
  1191.             if (nlflag)
  1192.                   return n1;
  1193.           } else {
  1194.             tokpushback++;
  1195.           }
  1196.           checkkwd();
  1197.           if (tokendlist[lasttoken])
  1198.             return n1;
  1199.           n2 = andor();
  1200.           n3 = (union node *)stalloc(sizeof (struct nbinary));
  1201.           n3->type = NSEMI;
  1202.           n3->nbinary.ch1 = n1;
  1203.           n3->nbinary.ch2 = n2;
  1204.           n1 = n3;
  1205.           break;
  1206.         case TEOF:
  1207.           if (heredoclist)
  1208.             parseheredoc();
  1209.           else
  1210.             pungetc();        /* push back EOF on input */
  1211.           return n1;
  1212.         default:
  1213.           if (nlflag)
  1214.             synexpect(-1);
  1215.           tokpushback++;
  1216.           return n1;
  1217.         }
  1218.       }
  1219. }
  1220.  
  1221.  
  1222.  
  1223. STATIC union node *
  1224. andor() {
  1225.       union node *n1, *n2, *n3;
  1226.       int t;
  1227.  
  1228.       n1 = pipeline();
  1229.       for (;;) {
  1230.         if ((t = readtoken()) == TAND) {
  1231.           t = NAND;
  1232.         } else if (t == TOR) {
  1233.           t = NOR;
  1234.         } else {
  1235.           tokpushback++;
  1236.           return n1;
  1237.         }
  1238.         n2 = pipeline();
  1239.         n3 = (union node *)stalloc(sizeof (struct nbinary));
  1240.         n3->type = t;
  1241.         n3->nbinary.ch1 = n1;
  1242.         n3->nbinary.ch2 = n2;
  1243.         n1 = n3;
  1244.       }
  1245. }
  1246.  
  1247.  
  1248.  
  1249. STATIC union node *
  1250. pipeline() {
  1251.       union node *n1, *pipenode;
  1252.       struct nodelist *lp, *prev;
  1253.  
  1254.       n1 = command();
  1255.       if (readtoken() == TPIPE) {
  1256.         pipenode = (union node *)stalloc(sizeof (struct npipe));
  1257.         pipenode->type = NPIPE;
  1258.         pipenode->npipe.backgnd = 0;
  1259.         lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
  1260.         pipenode->npipe.cmdlist = lp;
  1261.         lp->n = n1;
  1262.         do {
  1263.           prev = lp;
  1264.           lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
  1265.           lp->n = command();
  1266.           prev->next = lp;
  1267.         } while (readtoken() == TPIPE);
  1268.         lp->next = NULL;
  1269.         n1 = pipenode;
  1270.       }
  1271.       tokpushback++;
  1272.       return n1;
  1273. }
  1274.  
  1275.  
  1276.  
  1277. STATIC union node *
  1278. command() {
  1279.       union node *n1, *n2;
  1280.       union node *ap, **app;
  1281.       union node *cp, **cpp;
  1282.       union node *redir, **rpp;
  1283.       int t;
  1284.  
  1285.       checkkwd();
  1286.       switch (readtoken()) {
  1287.       case TIF:
  1288.         n1 = (union node *)stalloc(sizeof (struct nif));
  1289.         n1->type = NIF;
  1290.         n1->nif.test = list(0);
  1291.         if (readtoken() != TTHEN)
  1292.           synexpect(TTHEN);
  1293.         n1->nif.ifpart = list(0);
  1294.         n2 = n1;
  1295.         while (readtoken() == TELIF) {
  1296.           n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif));
  1297.           n2 = n2->nif.elsepart;
  1298.           n2->type = NIF;
  1299.           n2->nif.test = list(0);
  1300.           if (readtoken() != TTHEN)
  1301.             synexpect(TTHEN);
  1302.           n2->nif.ifpart = list(0);
  1303.         }
  1304.         if (lasttoken == TELSE)
  1305.           n2->nif.elsepart = list(0);
  1306.         else {
  1307.           n2->nif.elsepart = NULL;
  1308.           tokpushback++;
  1309.         }
  1310.         if (readtoken() != TFI)
  1311.           synexpect(TFI);
  1312.         break;
  1313.       case TWHILE:
  1314.       case TUNTIL:
  1315.         n1 = (union node *)stalloc(sizeof (struct nbinary));
  1316.         n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL;
  1317.         n1->nbinary.ch1 = list(0);
  1318.         if (readtoken() != TDO)
  1319.           synexpect(TDO);
  1320.         n1->nbinary.ch2 = list(0);
  1321.         if (readtoken() != TDONE)
  1322.           synexpect(TDONE);
  1323.         break;
  1324.       case TFOR:
  1325.         if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
  1326.           synerror("Bad for loop variable");
  1327.         n1 = (union node *)stalloc(sizeof (struct nfor));
  1328.         n1->type = NFOR;
  1329.         n1->nfor.var = wordtext;
  1330.         if (readtoken() == TWORD && ! quoteflag && equal(wordtext, "in")) {
  1331.           app = ≈
  1332.           while (readtoken() == TWORD) {
  1333.             n2 = (union node *)stalloc(sizeof (struct narg));
  1334.             n2->type = NARG;
  1335.             n2->narg.text = wordtext;
  1336.             n2->narg.backquote = backquotelist;
  1337.             *app = n2;
  1338.             app = &n2->narg.next;
  1339.           }
  1340.           *app = NULL;
  1341.           n1->nfor.args = ap;
  1342.         } else {
  1343. #ifndef GDB_HACK
  1344.           static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE,
  1345.                           '@', '=', '\0'};
  1346. #endif
  1347.           n2 = (union node *)stalloc(sizeof (struct narg));
  1348.           n2->type = NARG;
  1349.           n2->narg.text = (char *)argvars;
  1350.           n2->narg.backquote = NULL;
  1351.           n2->narg.next = NULL;
  1352.           n1->nfor.args = n2;
  1353.         }
  1354.         if (lasttoken != TNL && lasttoken != TSEMI)
  1355.           synexpect(-1);
  1356.         checkkwd();
  1357.         if ((t = readtoken()) == TDO)
  1358.           t = TDONE;
  1359.         else if (t == TBEGIN)
  1360.           t = TEND;
  1361.         else
  1362.           synexpect(-1);
  1363.         n1->nfor.body = list(0);
  1364.         if (readtoken() != t)
  1365.           synexpect(t);
  1366.         break;
  1367.       case TCASE:
  1368.         n1 = (union node *)stalloc(sizeof (struct ncase));
  1369.         n1->type = NCASE;
  1370.         if (readtoken() != TWORD)
  1371.           synexpect(TWORD);
  1372.         n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg));
  1373.         n2->type = NARG;
  1374.         n2->narg.text = wordtext;
  1375.         n2->narg.backquote = backquotelist;
  1376.         n2->narg.next = NULL;
  1377.         while (readtoken() == TNL);
  1378.         if (lasttoken != TWORD || ! equal(wordtext, "in"))
  1379.           synerror("expecting \"in\"");
  1380.         cpp = &n1->ncase.cases;
  1381.         while (checkkwd(), readtoken() == TWORD) {
  1382.           *cpp = cp = (union node *)stalloc(sizeof (struct nclist));
  1383.           cp->type = NCLIST;
  1384.           app = &cp->nclist.pattern;
  1385.           for (;;) {
  1386.             *app = ap = (union node *)stalloc(sizeof (struct narg));
  1387.             ap->type = NARG;
  1388.             ap->narg.text = wordtext;
  1389.             ap->narg.backquote = backquotelist;
  1390.             if (readtoken() != TPIPE)
  1391.                   break;
  1392.             app = &ap->narg.next;
  1393.             if (readtoken() != TWORD)
  1394.                   synexpect(TWORD);
  1395.           }
  1396.           ap->narg.next = NULL;
  1397.           if (lasttoken != TRP)
  1398.             synexpect(TRP);
  1399.           cp->nclist.body = list(0);
  1400.           if ((t = readtoken()) == TESAC)
  1401.             tokpushback++;
  1402.           else if (t != TENDCASE)
  1403.             synexpect(TENDCASE);
  1404.           cpp = &cp->nclist.next;
  1405.         }
  1406.         *cpp = NULL;
  1407.         if (lasttoken != TESAC)
  1408.           synexpect(TESAC);
  1409.         break;
  1410.       case TLP:
  1411.         n1 = (union node *)stalloc(sizeof (struct nredir));
  1412.         n1->type = NSUBSHELL;
  1413.         n1->nredir.n = list(0);
  1414.         n1->nredir.redirect = NULL;
  1415.         if (readtoken() != TRP)
  1416.           synexpect(TRP);
  1417.         break;
  1418.       case TBEGIN:
  1419.         n1 = list(0);
  1420.         if (readtoken() != TEND)
  1421.           synexpect(TEND);
  1422.         break;
  1423.       case TWORD:
  1424.       case TREDIR:
  1425.         tokpushback++;
  1426.         return simplecmd();
  1427.       default:
  1428.         synexpect(-1);
  1429.       }
  1430.  
  1431.       /* Now check for redirection which may follow command */
  1432.       rpp = &redir;
  1433.       while (readtoken() == TREDIR) {
  1434.         *rpp = n2 = redirnode;
  1435.         rpp = &n2->nfile.next;
  1436.         parsefname();
  1437.       }
  1438.       tokpushback++;
  1439.       *rpp = NULL;
  1440.       if (redir) {
  1441.         if (n1->type != NSUBSHELL) {
  1442.           n2 = (union node *)stalloc(sizeof (struct nredir));
  1443.           n2->type = NREDIR;
  1444.           n2->nredir.n = n1;
  1445.           n1 = n2;
  1446.         }
  1447.         n1->nredir.redirect = redir;
  1448.       }
  1449.       return n1;
  1450. }
  1451.  
  1452.  
  1453. STATIC union node *
  1454. simplecmd() {
  1455.       union node *args, **app;
  1456.       union node *redir, **rpp;
  1457.       union node *n;
  1458.  
  1459.       args = NULL;
  1460.       app = &args;
  1461.       rpp = &redir;
  1462.       for (;;) {
  1463.         if (readtoken() == TWORD) {
  1464.           n = (union node *)stalloc(sizeof (struct narg));
  1465.           n->type = NARG;
  1466.           n->narg.text = wordtext;
  1467.           n->narg.backquote = backquotelist;
  1468.           *app = n;
  1469.           app = &n->narg.next;
  1470.         } else if (lasttoken == TREDIR) {
  1471.           *rpp = n = redirnode;
  1472.           rpp = &n->nfile.next;
  1473.           parsefname();    /* read name of redirection file */
  1474.         } else if (lasttoken == TLP && app == &args->narg.next
  1475.                  && rpp == &redir) {
  1476.           /* We have a function */
  1477.           if (readtoken() != TRP)
  1478.             synexpect(TRP);
  1479.           if (! goodname(n->narg.text))
  1480.             synerror("Bad function name");
  1481.           n->type = NDEFUN;
  1482.           n->narg.next = command();
  1483.           return n;
  1484.         } else {
  1485.           tokpushback++;
  1486.           break;
  1487.         }
  1488.       }
  1489.       *app = NULL;
  1490.       *rpp = NULL;
  1491.       n = (union node *)stalloc(sizeof (struct ncmd));
  1492.       n->type = NCMD;
  1493.       n->ncmd.backgnd = 0;
  1494.       n->ncmd.args = args;
  1495.       n->ncmd.redirect = redir;
  1496.       return n;
  1497. }
  1498.  
  1499.  
  1500. STATIC void
  1501. parsefname() {
  1502.       union node *n = redirnode;
  1503.  
  1504.       if (readtoken() != TWORD)
  1505.         synexpect(-1);
  1506.       if (n->type == NHERE) {
  1507.         struct heredoc *here = heredoc;
  1508.         struct heredoc *p;
  1509.         int i;
  1510.  
  1511.         if (quoteflag == 0)
  1512.           n->type = NXHERE;
  1513.         TRACE(("Here document %d\n", n->type));
  1514.         if (here->striptabs) {
  1515.           while (*wordtext == '\t')
  1516.             wordtext++;
  1517.         }
  1518.         if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
  1519.           synerror("Illegal eof marker for << redirection");
  1520.         rmescapes(wordtext);
  1521.         here->eofmark = wordtext;
  1522.         here->next = NULL;
  1523.         if (heredoclist == NULL)
  1524.           heredoclist = here;
  1525.         else {
  1526.           for (p = heredoclist ; p->next ; p = p->next);
  1527.           p->next = here;
  1528.         }
  1529.       } else if (n->type == NTOFD || n->type == NFROMFD) {
  1530.         if (is_digit(wordtext[0]))
  1531.           n->ndup.dupfd = digit_val(wordtext[0]);
  1532.         else if (wordtext[0] == '-')
  1533.           n->ndup.dupfd = -1;
  1534.         else
  1535.           goto bad;
  1536.         if (wordtext[1] != '\0') {
  1537. bad:
  1538.           synerror("Bad fd number");
  1539.         }
  1540.       } else {
  1541.         n->nfile.fname = (union node *)stalloc(sizeof (struct narg));
  1542.         n = n->nfile.fname;
  1543.         n->type = NARG;
  1544.         n->narg.next = NULL;
  1545.         n->narg.text = wordtext;
  1546.         n->narg.backquote = backquotelist;
  1547.       }
  1548. }
  1549.  
  1550.  
  1551. /*
  1552.  * Input any here documents.
  1553.  */
  1554.  
  1555. STATIC void
  1556. parseheredoc() {
  1557.       struct heredoc *here;
  1558.       union node *n;
  1559.  
  1560.       while (heredoclist) {
  1561.         here = heredoclist;
  1562.         heredoclist = here->next;
  1563.         if (needprompt) {
  1564.           putprompt(ps2val());
  1565.           needprompt = 0;
  1566.         }
  1567.         readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
  1568.             here->eofmark, here->striptabs);
  1569.         n = (union node *)stalloc(sizeof (struct narg));
  1570.         n->narg.type = NARG;
  1571.         n->narg.next = NULL;
  1572.         n->narg.text = wordtext;
  1573.         n->narg.backquote = backquotelist;
  1574.         here->here->nhere.doc = n;
  1575.       }
  1576. }
  1577.  
  1578.  
  1579.  
  1580. /*
  1581.  * This routine is called to tell readtoken that we are at the beginning
  1582.  * of a command, so newlines should be ignored and keywords should be
  1583.  * checked for.  We munge things here rather than setting a flag for
  1584.  * readtoken.
  1585.  */
  1586.  
  1587. STATIC void
  1588. checkkwd() {
  1589.       register char *const *pp;
  1590.       int t;
  1591.  
  1592.       while ((t = readtoken()) == TNL)
  1593.         parseheredoc();
  1594.       if (t == TWORD && quoteflag == 0) {
  1595.         for (pp = parsekwd ; *pp ; pp++) {
  1596.           if (**pp == *wordtext && equal(*pp, wordtext)) {
  1597.             lasttoken = pp - parsekwd + KWDOFFSET;
  1598.             break;
  1599.           }
  1600.         }
  1601.       }
  1602.       tokpushback++;
  1603. }
  1604.  
  1605.  
  1606.  
  1607. STATIC int xxreadtoken();
  1608.  
  1609. STATIC int
  1610. readtoken() {
  1611.       int t;
  1612.  
  1613.       if (tokpushback) {
  1614.         return xxreadtoken();
  1615.       } else {
  1616.         t = xxreadtoken();
  1617.         /* TRACE(("token %s\n", tokname[t])); */
  1618.         return t;
  1619.       }
  1620. }
  1621.  
  1622.  
  1623. /*
  1624.  * Read the next input token.
  1625.  * If the token is a word, we set backquotelist to the list of cmds in
  1626.  *    backquotes.  We set quoteflag to true if any part of the word was
  1627.  *    quoted.
  1628.  * If the token is TREDIR, then we set redirnode to a structure containing
  1629.  *    the redirection.
  1630.  * In all cases, the variable startlinno is set to the number of the line
  1631.  *    on which the token starts.
  1632.  *
  1633.  * [Change comment:  here documents and internal procedures]
  1634.  * [Readtoken shouldn't have any arguments.  Perhaps we should make the
  1635.  *  word parsing code into a separate routine.  In this case, readtoken
  1636.  *  doesn't need to have any internal procedures, but parseword does.
  1637.  *  We could also make parseoperator in essence the main routine, and
  1638.  *  have parseword (readtoken1?) handle both words and redirection.]
  1639.  */
  1640.  
  1641. #define RETURN(token)    return lasttoken = token
  1642.  
  1643. STATIC int
  1644. xxreadtoken() {
  1645.       register c;
  1646.  
  1647.       if (tokpushback) {
  1648.         tokpushback = 0;
  1649.         return lasttoken;
  1650.       }
  1651.       if (needprompt) {
  1652.         putprompt(ps2val());
  1653.         needprompt = 0;
  1654.       }
  1655.       startlinno = plinno;
  1656.       for (;;) {    /* until token or start of word found */
  1657.         c = pgetc_macro();
  1658.         if (c == ' ' || c == '\t')
  1659.           continue;        /* quick check for white space first */
  1660.         switch (c) {
  1661.         case ' ': case '\t':
  1662.           continue;
  1663.         case '#':
  1664.           while ((c = pgetc()) != '\n' && c != PEOF);
  1665.           pungetc();
  1666.           continue;
  1667.         case '\\':
  1668.           if (pgetc() == '\n') {
  1669.             startlinno = ++plinno;
  1670.             if (doprompt)
  1671.                   putprompt(ps2val());
  1672.             continue;
  1673.           }
  1674.           pungetc();
  1675.           goto breakloop;
  1676.         case '\n':
  1677.           plinno++;
  1678.           needprompt = doprompt;
  1679.           RETURN(TNL);
  1680.         case PEOF:
  1681.           RETURN(TEOF);
  1682.         case '&':
  1683.           if (pgetc() == '&')
  1684.             RETURN(TAND);
  1685.           pungetc();
  1686.           RETURN(TBACKGND);
  1687.         case '|':
  1688.           if (pgetc() == '|')
  1689.             RETURN(TOR);
  1690.           pungetc();
  1691.           RETURN(TPIPE);
  1692.         case ';':
  1693.           if (pgetc() == ';')
  1694.             RETURN(TENDCASE);
  1695.           pungetc();
  1696.           RETURN(TSEMI);
  1697.         case '(':
  1698.           RETURN(TLP);
  1699.         case ')':
  1700.           RETURN(TRP);
  1701.         default:
  1702.           goto breakloop;
  1703.         }
  1704.       }
  1705. breakloop:
  1706.       return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
  1707. #undef RETURN
  1708. }
  1709.  
  1710.  
  1711.  
  1712. /*
  1713.  * If eofmark is NULL, read a word or a redirection symbol.  If eofmark
  1714.  * is not NULL, read a here document.  In the latter case, eofmark is the
  1715.  * word which marks the end of the document and striptabs is true if
  1716.  * leading tabs should be stripped from the document.  The argument firstc
  1717.  * is the first character of the input token or document.
  1718.  *
  1719.  * Because C does not have internal subroutines, I have simulated them
  1720.  * using goto's to implement the subroutine linkage.  The following macros
  1721.  * will run code that appears at the end of readtoken1.
  1722.  */
  1723.  
  1724. #define CHECKEND()    {goto checkend; checkend_return:;}
  1725. #define PARSEREDIR()    {goto parseredir; parseredir_return:;}
  1726. #define PARSESUB()    {goto parsesub; parsesub_return:;}
  1727. #define PARSEBACKQOLD()    {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
  1728. #define PARSEBACKQNEW()    {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
  1729.  
  1730. STATIC int
  1731. readtoken1(firstc, syntax, eofmark, striptabs)
  1732.       int firstc;
  1733.       char const *syntax;
  1734.       char *eofmark;
  1735.       int striptabs;
  1736.       {
  1737.       register c = firstc;
  1738.       register char *out;
  1739.       int len;
  1740.       char line[EOFMARKLEN + 1];
  1741.       struct nodelist *bqlist;
  1742.       int quotef;
  1743.       int dblquote;
  1744.       int varnest;
  1745.       int oldstyle;
  1746.  
  1747.       startlinno = plinno;
  1748.       dblquote = 0;
  1749.       if (syntax == DQSYNTAX)
  1750.         dblquote = 1;
  1751.       quotef = 0;
  1752.       bqlist = NULL;
  1753.       varnest = 0;
  1754.       STARTSTACKSTR(out);
  1755.       loop: {    /* for each line, until end of word */
  1756. #if ATTY
  1757.         if (c == '\034' && doprompt
  1758.          && attyset() && ! equal(termval(), "emacs")) {
  1759.           attyline();
  1760.           if (syntax == BASESYNTAX)
  1761.             return readtoken();
  1762.           c = pgetc();
  1763.           goto loop;
  1764.         }
  1765. #endif
  1766.         CHECKEND();    /* set c to PEOF if at end of here document */
  1767.         for (;;) {    /* until end of line or end of word */
  1768.           CHECKSTRSPACE(3, out);    /* permit 3 calls to USTPUTC */
  1769.           switch(syntax[c]) {
  1770.           case CNL:    /* '\n' */
  1771.             if (syntax == BASESYNTAX)
  1772.                   goto endword;    /* exit outer loop */
  1773.             USTPUTC(c, out);
  1774.             plinno++;
  1775.             if (doprompt) {
  1776.                   putprompt(ps2val());
  1777.             }
  1778.             c = pgetc();
  1779.             goto loop;        /* continue outer loop */
  1780.           case CWORD:
  1781.             USTPUTC(c, out);
  1782.             break;
  1783.           case CCTL:
  1784.             if (eofmark == NULL || dblquote)
  1785.                   USTPUTC(CTLESC, out);
  1786.             USTPUTC(c, out);
  1787.             break;
  1788.           case CBACK:    /* backslash */
  1789.             c = pgetc();
  1790.             if (c == PEOF) {
  1791.                   USTPUTC('\\', out);
  1792.                   pungetc();
  1793.             } else if (c == '\n') {
  1794.                   if (doprompt)
  1795.                     putprompt(ps2val());
  1796.             } else {
  1797.                   if (dblquote && c != '\\' && c != '`' && c != '$'
  1798.                        && (c != '"' || eofmark != NULL))
  1799.                     USTPUTC('\\', out);
  1800.                   if (SQSYNTAX[c] == CCTL)
  1801.                     USTPUTC(CTLESC, out);
  1802.                   USTPUTC(c, out);
  1803.                   quotef++;
  1804.             }
  1805.             break;
  1806.           case CSQUOTE:
  1807.             syntax = SQSYNTAX;
  1808.             break;
  1809.           case CDQUOTE:
  1810.             syntax = DQSYNTAX;
  1811.             dblquote = 1;
  1812.             break;
  1813.           case CENDQUOTE:
  1814.             if (eofmark) {
  1815.                   USTPUTC(c, out);
  1816.             } else {
  1817.                   syntax = BASESYNTAX;
  1818.                   quotef++;
  1819.                   dblquote = 0;
  1820.             }
  1821.             break;
  1822.           case CVAR:    /* '$' */
  1823.             PARSESUB();        /* parse substitution */
  1824.             break;
  1825.           case CENDVAR:    /* '}' */
  1826.             if (varnest > 0) {
  1827.                   varnest--;
  1828.                   USTPUTC(CTLENDVAR, out);
  1829.             } else {
  1830.                   USTPUTC(c, out);
  1831.             }
  1832.             break;
  1833.           case CBQUOTE:    /* '`' */
  1834.             if (parsebackquote && syntax == BASESYNTAX) {
  1835.                   if (out == stackblock())
  1836.                     return lasttoken = TENDBQUOTE;
  1837.                   else
  1838.                     goto endword;    /* exit outer loop */
  1839.             }
  1840.             PARSEBACKQOLD();
  1841.             break;
  1842.           case CEOF:
  1843.             goto endword;        /* exit outer loop */
  1844.           default:
  1845.             if (varnest == 0)
  1846.                   goto endword;    /* exit outer loop */
  1847.             USTPUTC(c, out);
  1848.           }
  1849.           c = pgetc_macro();
  1850.         }
  1851.       }
  1852. endword:
  1853.       if (syntax != BASESYNTAX && eofmark == NULL)
  1854.         synerror("Unterminated quoted string");
  1855.       if (varnest != 0) {
  1856.         startlinno = plinno;
  1857.         synerror("Missing '}'");
  1858.       }
  1859.       USTPUTC('\0', out);
  1860.       len = out - stackblock();
  1861.       out = stackblock();
  1862.       if (eofmark == NULL) {
  1863.         if ((c == '>' || c == '<')
  1864.          && quotef == 0
  1865.          && len <= 2
  1866.          && (*out == '\0' || is_digit(*out))) {
  1867.           PARSEREDIR();
  1868.           return lasttoken = TREDIR;
  1869.         } else {
  1870.           pungetc();
  1871.         }
  1872.       }
  1873.       quoteflag = quotef;
  1874.       backquotelist = bqlist;
  1875.       grabstackblock(len);
  1876.       wordtext = out;
  1877.       return lasttoken = TWORD;
  1878. /* end of readtoken routine */
  1879.  
  1880.  
  1881.  
  1882. /*
  1883.  * Check to see whether we are at the end of the here document.  When this
  1884.  * is called, c is set to the first character of the next input line.  If
  1885.  * we are at the end of the here document, this routine sets the c to PEOF.
  1886.  */
  1887.  
  1888. checkend: {
  1889.       if (eofmark) {
  1890.         if (striptabs) {
  1891.           while (c == '\t')
  1892.             c = pgetc();
  1893.         }
  1894.         if (c == *eofmark) {
  1895.           if (pfgets(line, sizeof line) != NULL) {
  1896.             register char *p, *q;
  1897.  
  1898.             p = line;
  1899.             for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
  1900.             if (*p == '\n' && *q == '\0') {
  1901.                   c = PEOF;
  1902.                   plinno++;
  1903.                   needprompt = doprompt;
  1904.             } else {
  1905.                   ppushback(line, strlen(line));
  1906.             }
  1907.           }
  1908.         }
  1909.       }
  1910.       goto checkend_return;
  1911. }
  1912.  
  1913.  
  1914. /*
  1915.  * Parse a redirection operator.  The variable "out" points to a string
  1916.  * specifying the fd to be redirected.  The variable "c" contains the
  1917.  * first character of the redirection operator.
  1918.  */
  1919.  
  1920. parseredir: {
  1921.       char fd = *out;
  1922.       union node *np;
  1923.  
  1924.       np = (union node *)stalloc(sizeof (struct nfile));
  1925.       if (c == '>') {
  1926.         np->nfile.fd = 1;
  1927.         c = pgetc();
  1928.         if (c == '>')
  1929.           np->type = NAPPEND;
  1930.         else if (c == '&')
  1931.           np->type = NTOFD;
  1932.         else {
  1933.           np->type = NTO;
  1934.           pungetc();
  1935.         }
  1936.       } else {    /* c == '<' */
  1937.         np->nfile.fd = 0;
  1938.         c = pgetc();
  1939.         if (c == '<') {
  1940.           if (sizeof (struct nfile) != sizeof (struct nhere)) {
  1941.             np = (union node *)stalloc(sizeof (struct nhere));
  1942.             np->nfile.fd = 0;
  1943.           }
  1944.           np->type = NHERE;
  1945.           heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
  1946.           heredoc->here = np;
  1947.           if ((c = pgetc()) == '-') {
  1948.             heredoc->striptabs = 1;
  1949.           } else {
  1950.             heredoc->striptabs = 0;
  1951.             pungetc();
  1952.           }
  1953.         } else if (c == '&')
  1954.           np->type = NFROMFD;
  1955.         else {
  1956.           np->type = NFROM;
  1957.           pungetc();
  1958.         }
  1959.       }
  1960.       if (fd != '\0')
  1961.         np->nfile.fd = digit_val(fd);
  1962.       redirnode = np;
  1963.       goto parseredir_return;
  1964. }
  1965.  
  1966.  
  1967. /*
  1968.  * Parse a substitution.  At this point, we have read the dollar sign
  1969.  * and nothing else.
  1970.  */
  1971.  
  1972. parsesub: {
  1973.       int subtype;
  1974.       int typeloc;
  1975.       int flags;
  1976.       char *p;
  1977. #ifndef GDB_HACK
  1978.       static const char types[] = "}-+?=";
  1979. #endif
  1980.  
  1981.       c = pgetc();
  1982.       if (c == ' ' || c == '\t' || c == '\n' || c == '"' || c == PEOF) {
  1983.         USTPUTC('$', out);
  1984.         pungetc();
  1985.       } else if (c == '(') {    /* $(command) */
  1986.         PARSEBACKQNEW();
  1987.       } else {
  1988.         USTPUTC(CTLVAR, out);
  1989.         typeloc = out - stackblock();
  1990.         USTPUTC(VSNORMAL, out);
  1991.         subtype = VSNORMAL;
  1992.         if (c == '{') {
  1993.           c = pgetc();
  1994.           subtype = 0;
  1995.         }
  1996.         if (is_name(c)) {
  1997.           do {
  1998.             STPUTC(c, out);
  1999.             c = pgetc();
  2000.           } while (is_in_name(c));
  2001.         } else {
  2002.           if (! is_special(c))
  2003. badsub:            synerror("Bad substitution");
  2004.           USTPUTC(c, out);
  2005.           c = pgetc();
  2006.         }
  2007.         STPUTC('=', out);
  2008.         flags = 0;
  2009.         if (subtype == 0) {
  2010.           if (c == ':') {
  2011.             flags = VSNUL;
  2012.             c = pgetc();
  2013.           }
  2014.           p = strchr(types, c);
  2015.           if (p == NULL)
  2016.             goto badsub;
  2017.           subtype = p - types + VSNORMAL;
  2018.         } else {
  2019.           pungetc();
  2020.         }
  2021.         if (dblquote)
  2022.           flags |= VSQUOTE;
  2023.         *(stackblock() + typeloc) = subtype | flags;
  2024.         if (subtype != VSNORMAL)
  2025.           varnest++;
  2026.       }
  2027.       goto parsesub_return;
  2028. }
  2029.  
  2030.  
  2031. /*
  2032.  * Called to parse command substitutions.  Newstyle is set if the command
  2033.  * is enclosed inside $(...); nlpp is a pointer to the head of the linked
  2034.  * list of commands (passed by reference), and savelen is the number of
  2035.  * characters on the top of the stack which must be preserved.
  2036.  */
  2037.  
  2038. parsebackq: {
  2039.       struct nodelist **nlpp;
  2040.       int savepbq;
  2041.       union node *n;
  2042.       char *volatile str;
  2043.       struct jmploc jmploc;
  2044.       struct jmploc *volatile savehandler;
  2045.       int savelen;
  2046.       int t;
  2047.  
  2048.       savepbq = parsebackquote;
  2049.       if (setjmp(jmploc.loc)) {
  2050.         if (str)
  2051.           ckfree(str);
  2052.         parsebackquote = 0;
  2053.         handler = savehandler;
  2054.         longjmp(handler, 1);
  2055.       }
  2056.       INTOFF;
  2057.       str = NULL;
  2058.       savelen = out - stackblock();
  2059.       if (savelen > 0) {
  2060.         str = ckmalloc(savelen);
  2061.         bcopy(stackblock(), str, savelen);
  2062.       }
  2063.       savehandler = handler;
  2064.       handler = &jmploc;
  2065.       INTON;
  2066.       nlpp = &bqlist;
  2067.       while (*nlpp)
  2068.         nlpp = &(*nlpp)->next;
  2069.       *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist));
  2070.       (*nlpp)->next = NULL;
  2071.       parsebackquote = oldstyle;
  2072.       n = list(0);
  2073.       t = oldstyle? TENDBQUOTE : TRP;
  2074.       if (readtoken() != t)
  2075.         synexpect(t);
  2076.       (*nlpp)->n = n;
  2077.       while (stackblocksize() <= savelen)
  2078.         growstackblock();
  2079.       STARTSTACKSTR(out);
  2080.       if (str) {
  2081.         bcopy(str, out, savelen);
  2082.         STADJUST(savelen, out);
  2083.         INTOFF;
  2084.         ckfree(str);
  2085.         str = NULL;
  2086.         INTON;
  2087.       }
  2088.       parsebackquote = savepbq;
  2089.       handler = savehandler;
  2090.       USTPUTC(CTLBACKQ + dblquote, out);
  2091.       if (oldstyle)
  2092.         goto parsebackq_oldreturn;
  2093.       else
  2094.         goto parsebackq_newreturn;
  2095. }
  2096.  
  2097. } /* end of readtoken */
  2098.  
  2099.  
  2100.  
  2101. #ifdef mkinit
  2102. RESET {
  2103.       tokpushback = 0;
  2104. }
  2105. #endif
  2106.  
  2107.  
  2108. #if ATTY
  2109. /*
  2110.  * Called to process a command generated by atty.  We execute the line,
  2111.  * and catch any errors that occur so they don't propagate outside of
  2112.  * this routine.
  2113.  */
  2114.  
  2115. STATIC void
  2116. attyline() {
  2117.       char line[256];
  2118.       struct stackmark smark;
  2119.       struct jmploc jmploc;
  2120.       struct jmploc *volatile savehandler;
  2121.  
  2122.       if (pfgets(line, sizeof line) == NULL)
  2123.         return;                /* "can't happen" */
  2124.       if (setjmp(jmploc.loc)) {
  2125.         if (exception == EXERROR)
  2126.           out2str("\033]D\n");
  2127.         handler = savehandler;
  2128.         longjmp(handler, 1);
  2129.       }
  2130.       savehandler = handler;
  2131.       handler = &jmploc;
  2132.       setstackmark(&smark);
  2133.       evalstring(line);
  2134.       popstackmark(&smark);
  2135.       handler = savehandler;
  2136.       doprompt = 1;
  2137. }
  2138.  
  2139.  
  2140. /*
  2141.  * Output a prompt for atty.  We output the prompt as part of the
  2142.  * appropriate escape sequence.  
  2143.  */
  2144.  
  2145. STATIC void
  2146. putprompt(s)
  2147.       char *s;
  2148.       {
  2149.       register char *p;
  2150.  
  2151.       if (attyset() && ! equal(termval(), "emacs")) {
  2152.         if (strchr(s, '\7'))
  2153.           out2c('\7');
  2154.         out2str("\033]P1;");
  2155.         for (p = s ; *p ; p++) {
  2156.           if ((unsigned)(*p - ' ') <= '~' - ' ')
  2157.             out2c(*p);
  2158.         }
  2159.         out2c('\n');
  2160.       } else {
  2161.         out2str(s);
  2162.       }
  2163. }
  2164. #endif
  2165.  
  2166.  
  2167.  
  2168. /*
  2169.  * Returns true if the text contains nothing to expand (no dollar signs
  2170.  * or backquotes).
  2171.  */
  2172.  
  2173. STATIC int
  2174. noexpand(text)
  2175.       char *text;
  2176.       {
  2177.       register char *p;
  2178.       register char c;
  2179.  
  2180.       p = text;
  2181.       while ((c = *p++) != '\0') {
  2182.         if (c == CTLESC)
  2183.           p++;
  2184.         else if (BASESYNTAX[c] == CCTL)
  2185.           return 0;
  2186.       }
  2187.       return 1;
  2188. }
  2189.  
  2190.  
  2191. /*
  2192.  * Return true if the argument is a legal variable name (a letter or
  2193.  * underscore followed by zero or more letters, underscores, and digits).
  2194.  */
  2195.  
  2196. int
  2197. goodname(name)
  2198.       char *name;
  2199.       {
  2200.       register char *p;
  2201.  
  2202.       p = name;
  2203.       if (! is_name(*p))
  2204.         return 0;
  2205.       while (*++p) {
  2206.         if (! is_in_name(*p))
  2207.           return 0;
  2208.       }
  2209.       return 1;
  2210. }
  2211.  
  2212.  
  2213. /*
  2214.  * Called when an unexpected token is read during the parse.  The argument
  2215.  * is the token that is expected, or -1 if more than one type of token can
  2216.  * occur at this point.
  2217.  */
  2218.  
  2219. STATIC void
  2220. synexpect(token) {
  2221.       char msg[64];
  2222.  
  2223.       if (token >= 0) {
  2224.         fmtstr(msg, 64, "%s unexpected (expecting %s)",
  2225.           tokname[lasttoken], tokname[token]);
  2226.       } else {
  2227.         fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]);
  2228.       }
  2229.       synerror(msg);
  2230. }
  2231.  
  2232.  
  2233. STATIC void
  2234. synerror(msg)
  2235.       char *msg;
  2236.       {
  2237.       if (commandname)
  2238.         outfmt(&errout, "%s: %d: ", commandname, startlinno);
  2239.       outfmt(&errout, "Syntax error: %s\n", msg);
  2240.       error((char *)NULL);
  2241. }
  2242. EOF
  2243. if test `wc -c < parser.c` -ne 28043
  2244. then    echo 'parser.c is the wrong size'
  2245. fi
  2246. echo Archive 6 unpacked
  2247. exit
  2248.  
  2249.