home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / bin / sh / parser.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-15  |  27.1 KB  |  1,247 lines

  1. /*-
  2.  * Copyright (c) 1991 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * This code is derived from software contributed to Berkeley by
  6.  * Kenneth Almquist.
  7.  *
  8.  * Redistribution and use in source and binary forms, with or without
  9.  * modification, are permitted provided that the following conditions
  10.  * are met:
  11.  * 1. Redistributions of source code must retain the above copyright
  12.  *    notice, this list of conditions and the following disclaimer.
  13.  * 2. Redistributions in binary form must reproduce the above copyright
  14.  *    notice, this list of conditions and the following disclaimer in the
  15.  *    documentation and/or other materials provided with the distribution.
  16.  * 3. All advertising materials mentioning features or use of this software
  17.  *    must display the following acknowledgement:
  18.  *    This product includes software developed by the University of
  19.  *    California, Berkeley and its contributors.
  20.  * 4. Neither the name of the University nor the names of its contributors
  21.  *    may be used to endorse or promote products derived from this software
  22.  *    without specific prior written permission.
  23.  *
  24.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  25.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  26.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  27.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  28.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  29.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  30.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  31.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  32.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  33.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  34.  * SUCH DAMAGE.
  35.  */
  36.  
  37. #ifndef lint
  38. static char sccsid[] = "@(#)parser.c    5.3 (Berkeley) 4/12/91";
  39. #endif /* not lint */
  40.  
  41. #include "shell.h"
  42. #include "parser.h"
  43. #include "nodes.h"
  44. #include "expand.h"    /* defines rmescapes() */
  45. #include "redir.h"    /* defines copyfd() */
  46. #include "syntax.h"
  47. #include "options.h"
  48. #include "input.h"
  49. #include "output.h"
  50. #include "var.h"
  51. #include "error.h"
  52. #include "memalloc.h"
  53. #include "mystring.h"
  54.  
  55.  
  56. /*
  57.  * Shell command parser.
  58.  */
  59.  
  60. #define EOFMARKLEN 79
  61.  
  62. /* values returned by readtoken */
  63. #include "token.def"
  64.  
  65.  
  66.  
  67. struct heredoc {
  68.     struct heredoc *next;    /* next here document in list */
  69.     union node *here;        /* redirection node */
  70.     char *eofmark;        /* string indicating end of input */
  71.     int striptabs;        /* if set, strip leading tabs */
  72. };
  73.  
  74.  
  75.  
  76. struct heredoc *heredoclist;    /* list of here documents to read */
  77. int parsebackquote;        /* nonzero if we are inside backquotes */
  78. int doprompt;            /* if set, prompt the user */
  79. int needprompt;            /* true if interactive and at start of line */
  80. int lasttoken;            /* last token read */
  81. MKINIT int tokpushback;        /* last token pushed back */
  82. char *wordtext;            /* text of last word returned by readtoken */
  83. int checkkwd;               /* 1 == check for kwds, 2 == also eat newlines */
  84. struct nodelist *backquotelist;
  85. union node *redirnode;
  86. struct heredoc *heredoc;
  87. int quoteflag;            /* set if (part of) last token was quoted */
  88. int startlinno;            /* line # where last token started */
  89.  
  90.  
  91. #define GDB_HACK 1 /* avoid local declarations which gdb can't handle */
  92. #ifdef GDB_HACK
  93. static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'};
  94. static const char types[] = "}-+?=";
  95. #endif
  96.  
  97.  
  98. STATIC union node *list __P((int));
  99. STATIC union node *andor __P((void));
  100. STATIC union node *pipeline __P((void));
  101. STATIC union node *command __P((void));
  102. STATIC union node *simplecmd __P((void));
  103. STATIC void parsefname __P((void));
  104. STATIC void parseheredoc __P((void));
  105. STATIC int readtoken __P((void));
  106. STATIC int readtoken1 __P((int, char const *, char *, int));
  107. STATIC void attyline __P((void));
  108. STATIC int noexpand __P((char *));
  109. STATIC void synexpect __P((int));
  110. STATIC void synerror __P((char *));
  111.  
  112. #if ATTY
  113. STATIC void putprompt __P((char *));
  114. #else /* not ATTY */
  115. #define putprompt(s)    out2str(s)
  116. #endif
  117.  
  118.  
  119.  
  120.  
  121. /*
  122.  * Read and parse a command.  Returns NEOF on end of file.  (NULL is a
  123.  * valid parse tree indicating a blank line.)
  124.  */
  125.  
  126. union node *
  127. parsecmd(interact) {
  128.     int t;
  129.  
  130.     doprompt = interact;
  131.     if (doprompt)
  132.         putprompt(ps1val());
  133.     needprompt = 0;
  134.     if ((t = readtoken()) == TEOF)
  135.         return NEOF;
  136.     if (t == TNL)
  137.         return NULL;
  138.     tokpushback++;
  139.     return list(1);
  140. }
  141.  
  142.  
  143. STATIC union node *
  144. list(nlflag) {
  145.     union node *n1, *n2, *n3;
  146.  
  147.     checkkwd = 2;
  148.     if (nlflag == 0 && tokendlist[peektoken()])
  149.         return NULL;
  150.     n1 = andor();
  151.     for (;;) {
  152.         switch (readtoken()) {
  153.         case TBACKGND:
  154.             if (n1->type == NCMD || n1->type == NPIPE) {
  155.                 n1->ncmd.backgnd = 1;
  156.             } else if (n1->type == NREDIR) {
  157.                 n1->type = NBACKGND;
  158.             } else {
  159.                 n3 = (union node *)stalloc(sizeof (struct nredir));
  160.                 n3->type = NBACKGND;
  161.                 n3->nredir.n = n1;
  162.                 n3->nredir.redirect = NULL;
  163.                 n1 = n3;
  164.             }
  165.             goto tsemi;
  166.         case TNL:
  167.             tokpushback++;
  168.             /* fall through */
  169. tsemi:        case TSEMI:
  170.             if (readtoken() == TNL) {
  171.                 parseheredoc();
  172.                 if (nlflag)
  173.                     return n1;
  174.             } else {
  175.                 tokpushback++;
  176.             }
  177.             checkkwd = 2;
  178.             if (tokendlist[peektoken()])
  179.                 return n1;
  180.             n2 = andor();
  181.             n3 = (union node *)stalloc(sizeof (struct nbinary));
  182.             n3->type = NSEMI;
  183.             n3->nbinary.ch1 = n1;
  184.             n3->nbinary.ch2 = n2;
  185.             n1 = n3;
  186.             break;
  187.         case TEOF:
  188.             if (heredoclist)
  189.                 parseheredoc();
  190.             else
  191.                 pungetc();        /* push back EOF on input */
  192.             return n1;
  193.         default:
  194.             if (nlflag)
  195.                 synexpect(-1);
  196.             tokpushback++;
  197.             return n1;
  198.         }
  199.     }
  200. }
  201.  
  202.  
  203.  
  204. STATIC union node *
  205. andor() {
  206.     union node *n1, *n2, *n3;
  207.     int t;
  208.  
  209.     n1 = pipeline();
  210.     for (;;) {
  211.         if ((t = readtoken()) == TAND) {
  212.             t = NAND;
  213.         } else if (t == TOR) {
  214.             t = NOR;
  215.         } else {
  216.             tokpushback++;
  217.             return n1;
  218.         }
  219.         n2 = pipeline();
  220.         n3 = (union node *)stalloc(sizeof (struct nbinary));
  221.         n3->type = t;
  222.         n3->nbinary.ch1 = n1;
  223.         n3->nbinary.ch2 = n2;
  224.         n1 = n3;
  225.     }
  226. }
  227.  
  228.  
  229.  
  230. STATIC union node *
  231. pipeline() {
  232.     union node *n1, *pipenode;
  233.     struct nodelist *lp, *prev;
  234.  
  235.     n1 = command();
  236.     if (readtoken() == TPIPE) {
  237.         pipenode = (union node *)stalloc(sizeof (struct npipe));
  238.         pipenode->type = NPIPE;
  239.         pipenode->npipe.backgnd = 0;
  240.         lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
  241.         pipenode->npipe.cmdlist = lp;
  242.         lp->n = n1;
  243.         do {
  244.             prev = lp;
  245.             lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
  246.             lp->n = command();
  247.             prev->next = lp;
  248.         } while (readtoken() == TPIPE);
  249.         lp->next = NULL;
  250.         n1 = pipenode;
  251.     }
  252.     tokpushback++;
  253.     return n1;
  254. }
  255.  
  256.  
  257.  
  258. STATIC union node *
  259. command() {
  260.     union node *n1, *n2;
  261.     union node *ap, **app;
  262.     union node *cp, **cpp;
  263.     union node *redir, **rpp;
  264.     int t;
  265.  
  266.     checkkwd = 2;
  267.     switch (readtoken()) {
  268.     case TIF:
  269.         n1 = (union node *)stalloc(sizeof (struct nif));
  270.         n1->type = NIF;
  271.         n1->nif.test = list(0);
  272.         if (readtoken() != TTHEN)
  273.             synexpect(TTHEN);
  274.         n1->nif.ifpart = list(0);
  275.         n2 = n1;
  276.         while (readtoken() == TELIF) {
  277.             n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif));
  278.             n2 = n2->nif.elsepart;
  279.             n2->type = NIF;
  280.             n2->nif.test = list(0);
  281.             if (readtoken() != TTHEN)
  282.                 synexpect(TTHEN);
  283.             n2->nif.ifpart = list(0);
  284.         }
  285.         if (lasttoken == TELSE)
  286.             n2->nif.elsepart = list(0);
  287.         else {
  288.             n2->nif.elsepart = NULL;
  289.             tokpushback++;
  290.         }
  291.         if (readtoken() != TFI)
  292.             synexpect(TFI);
  293.         checkkwd = 1;
  294.         break;
  295.     case TWHILE:
  296.     case TUNTIL: {
  297.         int got;
  298.         n1 = (union node *)stalloc(sizeof (struct nbinary));
  299.         n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL;
  300.         n1->nbinary.ch1 = list(0);
  301.         if ((got=readtoken()) != TDO) {
  302. TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : ""));
  303.             synexpect(TDO);
  304.         }
  305.         n1->nbinary.ch2 = list(0);
  306.         if (readtoken() != TDONE)
  307.             synexpect(TDONE);
  308.         checkkwd = 1;
  309.         break;
  310.     }
  311.     case TFOR:
  312.         if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
  313.             synerror("Bad for loop variable");
  314.         n1 = (union node *)stalloc(sizeof (struct nfor));
  315.         n1->type = NFOR;
  316.         n1->nfor.var = wordtext;
  317.         if (readtoken() == TWORD && ! quoteflag && equal(wordtext, "in")) {
  318.             app = ≈
  319.             while (readtoken() == TWORD) {
  320.                 n2 = (union node *)stalloc(sizeof (struct narg));
  321.                 n2->type = NARG;
  322.                 n2->narg.text = wordtext;
  323.                 n2->narg.backquote = backquotelist;
  324.                 *app = n2;
  325.                 app = &n2->narg.next;
  326.             }
  327.             *app = NULL;
  328.             n1->nfor.args = ap;
  329.         } else {
  330. #ifndef GDB_HACK
  331.             static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE,
  332.                                    '@', '=', '\0'};
  333. #endif
  334.             n2 = (union node *)stalloc(sizeof (struct narg));
  335.             n2->type = NARG;
  336.             n2->narg.text = (char *)argvars;
  337.             n2->narg.backquote = NULL;
  338.             n2->narg.next = NULL;
  339.             n1->nfor.args = n2;
  340.         }
  341.         if (lasttoken != TNL && lasttoken != TSEMI)
  342.             synexpect(-1);
  343.         checkkwd = 2;
  344.         if ((t = readtoken()) == TDO)
  345.             t = TDONE;
  346.         else if (t == TBEGIN)
  347.             t = TEND;
  348.         else
  349.             synexpect(-1);
  350.         n1->nfor.body = list(0);
  351.         if (readtoken() != t)
  352.             synexpect(t);
  353.         checkkwd = 1;
  354.         break;
  355.     case TCASE:
  356.         n1 = (union node *)stalloc(sizeof (struct ncase));
  357.         n1->type = NCASE;
  358.         if (readtoken() != TWORD)
  359.             synexpect(TWORD);
  360.         n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg));
  361.         n2->type = NARG;
  362.         n2->narg.text = wordtext;
  363.         n2->narg.backquote = backquotelist;
  364.         n2->narg.next = NULL;
  365.         while (readtoken() == TNL);
  366.         if (lasttoken != TWORD || ! equal(wordtext, "in"))
  367.             synerror("expecting \"in\"");
  368.         cpp = &n1->ncase.cases;
  369.         while (checkkwd = 2, readtoken() == TWORD) {
  370.             *cpp = cp = (union node *)stalloc(sizeof (struct nclist));
  371.             cp->type = NCLIST;
  372.             app = &cp->nclist.pattern;
  373.             for (;;) {
  374.                 *app = ap = (union node *)stalloc(sizeof (struct narg));
  375.                 ap->type = NARG;
  376.                 ap->narg.text = wordtext;
  377.                 ap->narg.backquote = backquotelist;
  378.                 if (readtoken() != TPIPE)
  379.                     break;
  380.                 app = &ap->narg.next;
  381.                 if (readtoken() != TWORD)
  382.                     synexpect(TWORD);
  383.             }
  384.             ap->narg.next = NULL;
  385.             if (lasttoken != TRP)
  386.                 synexpect(TRP);
  387.             cp->nclist.body = list(0);
  388.             if ((t = readtoken()) == TESAC)
  389.                 tokpushback++;
  390.             else if (t != TENDCASE)
  391.                 synexpect(TENDCASE);
  392.             cpp = &cp->nclist.next;
  393.         }
  394.         *cpp = NULL;
  395.         if (lasttoken != TESAC)
  396.             synexpect(TESAC);
  397.         checkkwd = 1;
  398.         break;
  399.     case TLP:
  400.         n1 = (union node *)stalloc(sizeof (struct nredir));
  401.         n1->type = NSUBSHELL;
  402.         n1->nredir.n = list(0);
  403.         n1->nredir.redirect = NULL;
  404.         if (readtoken() != TRP)
  405.             synexpect(TRP);
  406.         checkkwd = 1;
  407.         break;
  408.     case TBEGIN:
  409.         n1 = list(0);
  410.         if (readtoken() != TEND)
  411.             synexpect(TEND);
  412.         checkkwd = 1;
  413.         break;
  414.     case TWORD:
  415.     case TREDIR:
  416.         tokpushback++;
  417.         return simplecmd();
  418.     default:
  419.         synexpect(-1);
  420.     }
  421.  
  422.     /* Now check for redirection which may follow command */
  423.     rpp = &redir;
  424.     while (readtoken() == TREDIR) {
  425.         *rpp = n2 = redirnode;
  426.         rpp = &n2->nfile.next;
  427.         parsefname();
  428.     }
  429.     tokpushback++;
  430.     *rpp = NULL;
  431.     if (redir) {
  432.         if (n1->type != NSUBSHELL) {
  433.             n2 = (union node *)stalloc(sizeof (struct nredir));
  434.             n2->type = NREDIR;
  435.             n2->nredir.n = n1;
  436.             n1 = n2;
  437.         }
  438.         n1->nredir.redirect = redir;
  439.     }
  440.     return n1;
  441. }
  442.  
  443.  
  444. STATIC union node *
  445. simplecmd() {
  446.     union node *args, **app;
  447.     union node *redir, **rpp;
  448.     union node *n;
  449.  
  450.     args = NULL;
  451.     app = &args;
  452.     rpp = &redir;
  453.     for (;;) {
  454.         if (readtoken() == TWORD) {
  455.             n = (union node *)stalloc(sizeof (struct narg));
  456.             n->type = NARG;
  457.             n->narg.text = wordtext;
  458.             n->narg.backquote = backquotelist;
  459.             *app = n;
  460.             app = &n->narg.next;
  461.         } else if (lasttoken == TREDIR) {
  462.             *rpp = n = redirnode;
  463.             rpp = &n->nfile.next;
  464.             parsefname();    /* read name of redirection file */
  465.         } else if (lasttoken == TLP && app == &args->narg.next
  466.                         && rpp == &redir) {
  467.             /* We have a function */
  468.             if (readtoken() != TRP)
  469.                 synexpect(TRP);
  470. #ifdef notdef
  471.             if (! goodname(n->narg.text))
  472.                 synerror("Bad function name");
  473. #endif
  474.             n->type = NDEFUN;
  475.             n->narg.next = command();
  476.             return n;
  477.         } else {
  478.             tokpushback++;
  479.             break;
  480.         }
  481.     }
  482.     *app = NULL;
  483.     *rpp = NULL;
  484.     n = (union node *)stalloc(sizeof (struct ncmd));
  485.     n->type = NCMD;
  486.     n->ncmd.backgnd = 0;
  487.     n->ncmd.args = args;
  488.     n->ncmd.redirect = redir;
  489.     return n;
  490. }
  491.  
  492.  
  493. STATIC void
  494. parsefname() {
  495.     union node *n = redirnode;
  496.  
  497.     if (readtoken() != TWORD)
  498.         synexpect(-1);
  499.     if (n->type == NHERE) {
  500.         struct heredoc *here = heredoc;
  501.         struct heredoc *p;
  502.         int i;
  503.  
  504.         if (quoteflag == 0)
  505.             n->type = NXHERE;
  506.         TRACE(("Here document %d\n", n->type));
  507.         if (here->striptabs) {
  508.             while (*wordtext == '\t')
  509.                 wordtext++;
  510.         }
  511.         if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
  512.             synerror("Illegal eof marker for << redirection");
  513.         rmescapes(wordtext);
  514.         here->eofmark = wordtext;
  515.         here->next = NULL;
  516.         if (heredoclist == NULL)
  517.             heredoclist = here;
  518.         else {
  519.             for (p = heredoclist ; p->next ; p = p->next);
  520.             p->next = here;
  521.         }
  522.     } else if (n->type == NTOFD || n->type == NFROMFD) {
  523.         if (is_digit(wordtext[0]))
  524.             n->ndup.dupfd = digit_val(wordtext[0]);
  525.         else if (wordtext[0] == '-')
  526.             n->ndup.dupfd = -1;
  527.         else
  528.             goto bad;
  529.         if (wordtext[1] != '\0') {
  530. bad:
  531.             synerror("Bad fd number");
  532.         }
  533.     } else {
  534.         n->nfile.fname = (union node *)stalloc(sizeof (struct narg));
  535.         n = n->nfile.fname;
  536.         n->type = NARG;
  537.         n->narg.next = NULL;
  538.         n->narg.text = wordtext;
  539.         n->narg.backquote = backquotelist;
  540.     }
  541. }
  542.  
  543.  
  544. /*
  545.  * Input any here documents.
  546.  */
  547.  
  548. STATIC void
  549. parseheredoc() {
  550.     struct heredoc *here;
  551.     union node *n;
  552.  
  553.     while (heredoclist) {
  554.         here = heredoclist;
  555.         heredoclist = here->next;
  556.         if (needprompt) {
  557.             putprompt(ps2val());
  558.             needprompt = 0;
  559.         }
  560.         readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
  561.                 here->eofmark, here->striptabs);
  562.         n = (union node *)stalloc(sizeof (struct narg));
  563.         n->narg.type = NARG;
  564.         n->narg.next = NULL;
  565.         n->narg.text = wordtext;
  566.         n->narg.backquote = backquotelist;
  567.         here->here->nhere.doc = n;
  568.     }
  569. }
  570.  
  571. STATIC int
  572. peektoken() {
  573.     int t;
  574.  
  575.     t = readtoken();
  576.     tokpushback++;
  577.     return (t);
  578. }
  579.  
  580. STATIC int xxreadtoken();
  581.  
  582. STATIC int
  583. readtoken() {
  584.     int t;
  585. #ifdef DEBUG
  586.     int alreadyseen = tokpushback;
  587. #endif
  588.     
  589.     t = xxreadtoken();
  590.  
  591.     if (checkkwd) {
  592.         /*
  593.          * eat newlines
  594.          */
  595.         if (checkkwd == 2) {
  596.             checkkwd = 0;
  597.             while (t == TNL) {
  598.                 parseheredoc();
  599.                 t = xxreadtoken();
  600.             }
  601.         } else
  602.             checkkwd = 0;
  603.         /*
  604.          * check for keywords
  605.          */
  606.         if (t == TWORD && !quoteflag) {
  607.             register char **pp;
  608.  
  609.             for (pp = parsekwd; *pp; pp++) {
  610.                 if (**pp == *wordtext && equal(*pp, wordtext)) {
  611.                     lasttoken = t = pp - parsekwd + KWDOFFSET;
  612.                     TRACE(("keyword %s recognized\n", tokname[t]));
  613.                     break;
  614.                 }
  615.             }
  616.         }
  617.     }
  618. #ifdef DEBUG
  619.     if (!alreadyseen)
  620.         TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
  621.     else
  622.         TRACE(("reread token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
  623. #endif
  624.     return (t);
  625. }
  626.  
  627.  
  628. /*
  629.  * Read the next input token.
  630.  * If the token is a word, we set backquotelist to the list of cmds in
  631.  *    backquotes.  We set quoteflag to true if any part of the word was
  632.  *    quoted.
  633.  * If the token is TREDIR, then we set redirnode to a structure containing
  634.  *    the redirection.
  635.  * In all cases, the variable startlinno is set to the number of the line
  636.  *    on which the token starts.
  637.  *
  638.  * [Change comment:  here documents and internal procedures]
  639.  * [Readtoken shouldn't have any arguments.  Perhaps we should make the
  640.  *  word parsing code into a separate routine.  In this case, readtoken
  641.  *  doesn't need to have any internal procedures, but parseword does.
  642.  *  We could also make parseoperator in essence the main routine, and
  643.  *  have parseword (readtoken1?) handle both words and redirection.]
  644.  */
  645.  
  646. #define RETURN(token)    return lasttoken = token
  647.  
  648. STATIC int
  649. xxreadtoken() {
  650.     register c;
  651.  
  652.     if (tokpushback) {
  653.         tokpushback = 0;
  654.         return lasttoken;
  655.     }
  656.     if (needprompt) {
  657.         putprompt(ps2val());
  658.         needprompt = 0;
  659.     }
  660.     startlinno = plinno;
  661.     for (;;) {    /* until token or start of word found */
  662.         c = pgetc_macro();
  663.         if (c == ' ' || c == '\t')
  664.             continue;        /* quick check for white space first */
  665.         switch (c) {
  666.         case ' ': case '\t':
  667.             continue;
  668.         case '#':
  669.             while ((c = pgetc()) != '\n' && c != PEOF);
  670.             pungetc();
  671.             continue;
  672.         case '\\':
  673.             if (pgetc() == '\n') {
  674.                 startlinno = ++plinno;
  675.                 if (doprompt)
  676.                     putprompt(ps2val());
  677.                 continue;
  678.             }
  679.             pungetc();
  680.             goto breakloop;
  681.         case '\n':
  682.             plinno++;
  683.             needprompt = doprompt;
  684.             RETURN(TNL);
  685.         case PEOF:
  686.             RETURN(TEOF);
  687.         case '&':
  688.             if (pgetc() == '&')
  689.                 RETURN(TAND);
  690.             pungetc();
  691.             RETURN(TBACKGND);
  692.         case '|':
  693.             if (pgetc() == '|')
  694.                 RETURN(TOR);
  695.             pungetc();
  696.             RETURN(TPIPE);
  697.         case ';':
  698.             if (pgetc() == ';')
  699.                 RETURN(TENDCASE);
  700.             pungetc();
  701.             RETURN(TSEMI);
  702.         case '(':
  703.             RETURN(TLP);
  704.         case ')':
  705.             RETURN(TRP);
  706.         default:
  707.             goto breakloop;
  708.         }
  709.     }
  710. breakloop:
  711.     return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
  712. #undef RETURN
  713. }
  714.  
  715.  
  716.  
  717. /*
  718.  * If eofmark is NULL, read a word or a redirection symbol.  If eofmark
  719.  * is not NULL, read a here document.  In the latter case, eofmark is the
  720.  * word which marks the end of the document and striptabs is true if
  721.  * leading tabs should be stripped from the document.  The argument firstc
  722.  * is the first character of the input token or document.
  723.  *
  724.  * Because C does not have internal subroutines, I have simulated them
  725.  * using goto's to implement the subroutine linkage.  The following macros
  726.  * will run code that appears at the end of readtoken1.
  727.  */
  728.  
  729. #define CHECKEND()    {goto checkend; checkend_return:;}
  730. #define PARSEREDIR()    {goto parseredir; parseredir_return:;}
  731. #define PARSESUB()    {goto parsesub; parsesub_return:;}
  732. #define PARSEBACKQOLD()    {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
  733. #define PARSEBACKQNEW()    {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
  734.  
  735. STATIC int
  736. readtoken1(firstc, syntax, eofmark, striptabs)
  737.     int firstc;
  738.     char const *syntax;
  739.     char *eofmark;
  740.     int striptabs;
  741.     {
  742.     register c = firstc;
  743.     register char *out;
  744.     int len;
  745.     char line[EOFMARKLEN + 1];
  746.     struct nodelist *bqlist;
  747.     int quotef;
  748.     int dblquote;
  749.     int varnest;
  750.     int oldstyle;
  751.  
  752.     startlinno = plinno;
  753.     dblquote = 0;
  754.     if (syntax == DQSYNTAX)
  755.         dblquote = 1;
  756.     quotef = 0;
  757.     bqlist = NULL;
  758.     varnest = 0;
  759.     STARTSTACKSTR(out);
  760.     loop: {    /* for each line, until end of word */
  761. #if ATTY
  762.         if (c == '\034' && doprompt
  763.          && attyset() && ! equal(termval(), "emacs")) {
  764.             attyline();
  765.             if (syntax == BASESYNTAX)
  766.                 return readtoken();
  767.             c = pgetc();
  768.             goto loop;
  769.         }
  770. #endif
  771.         CHECKEND();    /* set c to PEOF if at end of here document */
  772.         for (;;) {    /* until end of line or end of word */
  773.             CHECKSTRSPACE(3, out);    /* permit 3 calls to USTPUTC */
  774.             switch(syntax[c]) {
  775.             case CNL:    /* '\n' */
  776.                 if (syntax == BASESYNTAX)
  777.                     goto endword;    /* exit outer loop */
  778.                 USTPUTC(c, out);
  779.                 plinno++;
  780.                 if (doprompt) {
  781.                     putprompt(ps2val());
  782.                 }
  783.                 c = pgetc();
  784.                 goto loop;        /* continue outer loop */
  785.             case CWORD:
  786.                 USTPUTC(c, out);
  787.                 break;
  788.             case CCTL:
  789.                 if (eofmark == NULL || dblquote)
  790.                     USTPUTC(CTLESC, out);
  791.                 USTPUTC(c, out);
  792.                 break;
  793.             case CBACK:    /* backslash */
  794.                 c = pgetc();
  795.                 if (c == PEOF) {
  796.                     USTPUTC('\\', out);
  797.                     pungetc();
  798.                 } else if (c == '\n') {
  799.                     if (doprompt)
  800.                         putprompt(ps2val());
  801.                 } else {
  802.                     if (dblquote && c != '\\' && c != '`' && c != '$'
  803.                              && (c != '"' || eofmark != NULL))
  804.                         USTPUTC('\\', out);
  805.                     if (SQSYNTAX[c] == CCTL)
  806.                         USTPUTC(CTLESC, out);
  807.                     USTPUTC(c, out);
  808.                     quotef++;
  809.                 }
  810.                 break;
  811.             case CSQUOTE:
  812.                 syntax = SQSYNTAX;
  813.                 break;
  814.             case CDQUOTE:
  815.                 syntax = DQSYNTAX;
  816.                 dblquote = 1;
  817.                 break;
  818.             case CENDQUOTE:
  819.                 if (eofmark) {
  820.                     USTPUTC(c, out);
  821.                 } else {
  822.                     syntax = BASESYNTAX;
  823.                     quotef++;
  824.                     dblquote = 0;
  825.                 }
  826.                 break;
  827.             case CVAR:    /* '$' */
  828.                 PARSESUB();        /* parse substitution */
  829.                 break;
  830.             case CENDVAR:    /* '}' */
  831.                 if (varnest > 0) {
  832.                     varnest--;
  833.                     USTPUTC(CTLENDVAR, out);
  834.                 } else {
  835.                     USTPUTC(c, out);
  836.                 }
  837.                 break;
  838.             case CBQUOTE:    /* '`' */
  839.                 if (parsebackquote && syntax == BASESYNTAX) {
  840.                     if (out == stackblock())
  841.                         return lasttoken = TENDBQUOTE;
  842.                     else
  843.                         goto endword;    /* exit outer loop */
  844.                 }
  845.                 PARSEBACKQOLD();
  846.                 break;
  847.             case CEOF:
  848.                 goto endword;        /* exit outer loop */
  849.             default:
  850.                 if (varnest == 0)
  851.                     goto endword;    /* exit outer loop */
  852.                 USTPUTC(c, out);
  853.             }
  854.             c = pgetc_macro();
  855.         }
  856.     }
  857. endword:
  858.     if (syntax != BASESYNTAX && eofmark == NULL)
  859.         synerror("Unterminated quoted string");
  860.     if (varnest != 0) {
  861.         startlinno = plinno;
  862.         synerror("Missing '}'");
  863.     }
  864.     USTPUTC('\0', out);
  865.     len = out - stackblock();
  866.     out = stackblock();
  867.     if (eofmark == NULL) {
  868.         if ((c == '>' || c == '<')
  869.          && quotef == 0
  870.          && len <= 2
  871.          && (*out == '\0' || is_digit(*out))) {
  872.             PARSEREDIR();
  873.             return lasttoken = TREDIR;
  874.         } else {
  875.             pungetc();
  876.         }
  877.     }
  878.     quoteflag = quotef;
  879.     backquotelist = bqlist;
  880.     grabstackblock(len);
  881.     wordtext = out;
  882.     return lasttoken = TWORD;
  883. /* end of readtoken routine */
  884.  
  885.  
  886.  
  887. /*
  888.  * Check to see whether we are at the end of the here document.  When this
  889.  * is called, c is set to the first character of the next input line.  If
  890.  * we are at the end of the here document, this routine sets the c to PEOF.
  891.  */
  892.  
  893. checkend: {
  894.     if (eofmark) {
  895.         if (striptabs) {
  896.             while (c == '\t')
  897.                 c = pgetc();
  898.         }
  899.         if (c == *eofmark) {
  900.             if (pfgets(line, sizeof line) != NULL) {
  901.                 register char *p, *q;
  902.  
  903.                 p = line;
  904.                 for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
  905.                 if (*p == '\n' && *q == '\0') {
  906.                     c = PEOF;
  907.                     plinno++;
  908.                     needprompt = doprompt;
  909.                 } else {
  910.                     ppushback(line, strlen(line));
  911.                 }
  912.             }
  913.         }
  914.     }
  915.     goto checkend_return;
  916. }
  917.  
  918.  
  919. /*
  920.  * Parse a redirection operator.  The variable "out" points to a string
  921.  * specifying the fd to be redirected.  The variable "c" contains the
  922.  * first character of the redirection operator.
  923.  */
  924.  
  925. parseredir: {
  926.     char fd = *out;
  927.     union node *np;
  928.  
  929.     np = (union node *)stalloc(sizeof (struct nfile));
  930.     if (c == '>') {
  931.         np->nfile.fd = 1;
  932.         c = pgetc();
  933.         if (c == '>')
  934.             np->type = NAPPEND;
  935.         else if (c == '&')
  936.             np->type = NTOFD;
  937.         else {
  938.             np->type = NTO;
  939.             pungetc();
  940.         }
  941.     } else {    /* c == '<' */
  942.         np->nfile.fd = 0;
  943.         c = pgetc();
  944.         if (c == '<') {
  945.             if (sizeof (struct nfile) != sizeof (struct nhere)) {
  946.                 np = (union node *)stalloc(sizeof (struct nhere));
  947.                 np->nfile.fd = 0;
  948.             }
  949.             np->type = NHERE;
  950.             heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
  951.             heredoc->here = np;
  952.             if ((c = pgetc()) == '-') {
  953.                 heredoc->striptabs = 1;
  954.             } else {
  955.                 heredoc->striptabs = 0;
  956.                 pungetc();
  957.             }
  958.         } else if (c == '&')
  959.             np->type = NFROMFD;
  960.         else {
  961.             np->type = NFROM;
  962.             pungetc();
  963.         }
  964.     }
  965.     if (fd != '\0')
  966.         np->nfile.fd = digit_val(fd);
  967.     redirnode = np;
  968.     goto parseredir_return;
  969. }
  970.  
  971.  
  972. /*
  973.  * Parse a substitution.  At this point, we have read the dollar sign
  974.  * and nothing else.
  975.  */
  976.  
  977. parsesub: {
  978.     int subtype;
  979.     int typeloc;
  980.     int flags;
  981.     char *p;
  982. #ifndef GDB_HACK
  983.     static const char types[] = "}-+?=";
  984. #endif
  985.  
  986.     c = pgetc();
  987.     if (c != '(' && c != '{' && !is_name(c) && !is_special(c)) {
  988.         USTPUTC('$', out);
  989.         pungetc();
  990.     } else if (c == '(') {    /* $(command) */
  991.         PARSEBACKQNEW();
  992.     } else {
  993.         USTPUTC(CTLVAR, out);
  994.         typeloc = out - stackblock();
  995.         USTPUTC(VSNORMAL, out);
  996.         subtype = VSNORMAL;
  997.         if (c == '{') {
  998.             c = pgetc();
  999.             subtype = 0;
  1000.         }
  1001.         if (is_name(c)) {
  1002.             do {
  1003.                 STPUTC(c, out);
  1004.                 c = pgetc();
  1005.             } while (is_in_name(c));
  1006.         } else {
  1007.             if (! is_special(c))
  1008. badsub:                synerror("Bad substitution");
  1009.             USTPUTC(c, out);
  1010.             c = pgetc();
  1011.         }
  1012.         STPUTC('=', out);
  1013.         flags = 0;
  1014.         if (subtype == 0) {
  1015.             if (c == ':') {
  1016.                 flags = VSNUL;
  1017.                 c = pgetc();
  1018.             }
  1019.             p = strchr(types, c);
  1020.             if (p == NULL)
  1021.                 goto badsub;
  1022.             subtype = p - types + VSNORMAL;
  1023.         } else {
  1024.             pungetc();
  1025.         }
  1026.         if (dblquote)
  1027.             flags |= VSQUOTE;
  1028.         *(stackblock() + typeloc) = subtype | flags;
  1029.         if (subtype != VSNORMAL)
  1030.             varnest++;
  1031.     }
  1032.     goto parsesub_return;
  1033. }
  1034.  
  1035.  
  1036. /*
  1037.  * Called to parse command substitutions.  Newstyle is set if the command
  1038.  * is enclosed inside $(...); nlpp is a pointer to the head of the linked
  1039.  * list of commands (passed by reference), and savelen is the number of
  1040.  * characters on the top of the stack which must be preserved.
  1041.  */
  1042.  
  1043. parsebackq: {
  1044.     struct nodelist **nlpp;
  1045.     int savepbq;
  1046.     union node *n;
  1047.     char *volatile str;
  1048.     struct jmploc jmploc;
  1049.     struct jmploc *volatile savehandler;
  1050.     int savelen;
  1051.     int t;
  1052.  
  1053.     savepbq = parsebackquote;
  1054.     if (setjmp(jmploc.loc)) {
  1055.         if (str)
  1056.             ckfree(str);
  1057.         parsebackquote = 0;
  1058.         handler = savehandler;
  1059.         longjmp(handler, 1);
  1060.     }
  1061.     INTOFF;
  1062.     str = NULL;
  1063.     savelen = out - stackblock();
  1064.     if (savelen > 0) {
  1065.         str = ckmalloc(savelen);
  1066.         bcopy(stackblock(), str, savelen);
  1067.     }
  1068.     savehandler = handler;
  1069.     handler = &jmploc;
  1070.     INTON;
  1071.     nlpp = &bqlist;
  1072.     while (*nlpp)
  1073.         nlpp = &(*nlpp)->next;
  1074.     *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist));
  1075.     (*nlpp)->next = NULL;
  1076.     parsebackquote = oldstyle;
  1077.     n = list(0);
  1078.     t = oldstyle? TENDBQUOTE : TRP;
  1079.     if (readtoken() != t)
  1080.         synexpect(t);
  1081.     (*nlpp)->n = n;
  1082.     while (stackblocksize() <= savelen)
  1083.         growstackblock();
  1084.     STARTSTACKSTR(out);
  1085.     if (str) {
  1086.         bcopy(str, out, savelen);
  1087.         STADJUST(savelen, out);
  1088.         INTOFF;
  1089.         ckfree(str);
  1090.         str = NULL;
  1091.         INTON;
  1092.     }
  1093.     parsebackquote = savepbq;
  1094.     handler = savehandler;
  1095.     USTPUTC(CTLBACKQ + dblquote, out);
  1096.     if (oldstyle)
  1097.         goto parsebackq_oldreturn;
  1098.     else
  1099.         goto parsebackq_newreturn;
  1100. }
  1101.  
  1102. } /* end of readtoken */
  1103.  
  1104.  
  1105.  
  1106. #ifdef mkinit
  1107. RESET {
  1108.     tokpushback = 0;
  1109. }
  1110. #endif
  1111.  
  1112.  
  1113. #if ATTY
  1114. /*
  1115.  * Called to process a command generated by atty.  We execute the line,
  1116.  * and catch any errors that occur so they don't propagate outside of
  1117.  * this routine.
  1118.  */
  1119.  
  1120. STATIC void
  1121. attyline() {
  1122.     char line[256];
  1123.     struct stackmark smark;
  1124.     struct jmploc jmploc;
  1125.     struct jmploc *volatile savehandler;
  1126.  
  1127.     if (pfgets(line, sizeof line) == NULL)
  1128.         return;                /* "can't happen" */
  1129.     if (setjmp(jmploc.loc)) {
  1130.         if (exception == EXERROR)
  1131.             out2str("\033]D\n");
  1132.         handler = savehandler;
  1133.         longjmp(handler, 1);
  1134.     }
  1135.     savehandler = handler;
  1136.     handler = &jmploc;
  1137.     setstackmark(&smark);
  1138.     evalstring(line);
  1139.     popstackmark(&smark);
  1140.     handler = savehandler;
  1141.     doprompt = 1;
  1142. }
  1143.  
  1144.  
  1145. /*
  1146.  * Output a prompt for atty.  We output the prompt as part of the
  1147.  * appropriate escape sequence.  
  1148.  */
  1149.  
  1150. STATIC void
  1151. putprompt(s)
  1152.     char *s;
  1153.     {
  1154.     register char *p;
  1155.  
  1156.     if (attyset() && ! equal(termval(), "emacs")) {
  1157.         if (strchr(s, '\7'))
  1158.             out2c('\7');
  1159.         out2str("\033]P1;");
  1160.         for (p = s ; *p ; p++) {
  1161.             if ((unsigned)(*p - ' ') <= '~' - ' ')
  1162.                 out2c(*p);
  1163.         }
  1164.         out2c('\n');
  1165.     } else {
  1166.         out2str(s);
  1167.     }
  1168. }
  1169. #endif
  1170.  
  1171.  
  1172.  
  1173. /*
  1174.  * Returns true if the text contains nothing to expand (no dollar signs
  1175.  * or backquotes).
  1176.  */
  1177.  
  1178. STATIC int
  1179. noexpand(text)
  1180.     char *text;
  1181.     {
  1182.     register char *p;
  1183.     register char c;
  1184.  
  1185.     p = text;
  1186.     while ((c = *p++) != '\0') {
  1187.         if (c == CTLESC)
  1188.             p++;
  1189.         else if (BASESYNTAX[c] == CCTL)
  1190.             return 0;
  1191.     }
  1192.     return 1;
  1193. }
  1194.  
  1195.  
  1196. /*
  1197.  * Return true if the argument is a legal variable name (a letter or
  1198.  * underscore followed by zero or more letters, underscores, and digits).
  1199.  */
  1200.  
  1201. int
  1202. goodname(name)
  1203.     char *name;
  1204.     {
  1205.     register char *p;
  1206.  
  1207.     p = name;
  1208.     if (! is_name(*p))
  1209.         return 0;
  1210.     while (*++p) {
  1211.         if (! is_in_name(*p))
  1212.             return 0;
  1213.     }
  1214.     return 1;
  1215. }
  1216.  
  1217.  
  1218. /*
  1219.  * Called when an unexpected token is read during the parse.  The argument
  1220.  * is the token that is expected, or -1 if more than one type of token can
  1221.  * occur at this point.
  1222.  */
  1223.  
  1224. STATIC void
  1225. synexpect(token) {
  1226.     char msg[64];
  1227.  
  1228.     if (token >= 0) {
  1229.         fmtstr(msg, 64, "%s unexpected (expecting %s)",
  1230.             tokname[lasttoken], tokname[token]);
  1231.     } else {
  1232.         fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]);
  1233.     }
  1234.     synerror(msg);
  1235. }
  1236.  
  1237.  
  1238. STATIC void
  1239. synerror(msg)
  1240.     char *msg;
  1241.     {
  1242.     if (commandname)
  1243.         outfmt(&errout, "%s: %d: ", commandname, startlinno);
  1244.     outfmt(&errout, "Syntax error: %s\n", msg);
  1245.     error((char *)NULL);
  1246. }
  1247.