home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / pdksh-4.9-src.tgz / tar.out / contrib / pdksh / sh / lex.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  13KB  |  665 lines

  1. /*
  2.  * lexical analysis and source input
  3.  */
  4.  
  5. #ifndef lint
  6. static char *RCSid = "$Id: lex.c,v 1.4 93/05/05 21:16:46 sjg Exp $";
  7. #endif
  8.  
  9. #include "stdh.h"
  10. #include <errno.h>
  11. #include <setjmp.h>
  12. #include <unistd.h>
  13. #include <assert.h>
  14. #include "sh.h"
  15. #include "expand.h"
  16.  
  17.     int    ttyfd = -1;        /* tty fd for edit and jobs */
  18. #ifdef EASY_HISTORY
  19.     char   *history[HISTORY];    /* saved commands */
  20.     char  **histptr = history - 1;    /* last history item */
  21.     int    histpush;        /* number of pushed fc commands */
  22. #endif
  23.  
  24. /* we set s->str to NULLSTR instead of "", so that ungetsc() works */
  25. static    char    nullstr [] = {0, 0};
  26. #define    NULLSTR    (nullstr + 1)
  27.  
  28. static    int    expanding_alias;
  29. static    int    alias;
  30.  
  31. static void     readhere    ARGS((struct ioword *iop));
  32. static int      getsc_      ARGS((void));
  33.  
  34. /* optimized getsc_() */
  35. #define    getsc()    ((*source->str != 0) ? *source->str++ : getsc_())
  36. #define    ungetsc() (source->str--)
  37.  
  38. /*
  39.  * Lexical analyzer
  40.  *
  41.  * tokens are not regular expressions, they are LL(1).
  42.  * for example, "${var:-${PWD}}", and "$(size $(whence ksh))".
  43.  * hence the state stack.
  44.  */
  45.  
  46. int
  47. yylex(cf)
  48.     int cf;
  49. {
  50.     register int c, state;
  51.     char states [64], *statep = states;
  52.     XString ws;        /* expandable output word */
  53.     register char *wp;    /* output word pointer */
  54.     register char *sp, *dp;
  55.     int istate;
  56.     int c2;
  57.     static int rec_alias_cnt = 0;
  58.     static struct tbl *rec_alias_table[20];
  59.  
  60.  
  61.     if (expanding_alias) {
  62.         expanding_alias = 0;
  63.         while (rec_alias_cnt-- > 0)
  64.             rec_alias_table[rec_alias_cnt]->flag &= ~EXPALIAS;
  65.         rec_alias_cnt = 0;
  66.     }
  67.   Again:
  68.     Xinit(ws, wp, 64);
  69.  
  70.     if (cf&ONEWORD)
  71.         istate = SWORD;
  72.     else if (cf&LETEXPR)
  73.         istate = SDPAREN;
  74.     else {            /* normal lexing */
  75.         istate = SBASE;
  76.         while ((c = getsc()) == ' ' || c == '\t')
  77.             ;
  78.         if (c == '#')
  79.             while ((c = getsc()) != 0 && c != '\n')
  80.                 ;
  81.         ungetsc();
  82.     }
  83.     if (alias) {            /* trailing ' ' in alias definition */
  84.         alias = 0;
  85.         cf |= ALIAS;
  86.     }
  87.  
  88.     /* collect non-special or quoted characters to form word */
  89.     for (*statep = state = istate;
  90.          !((c = getsc()) == 0 || (state == SBASE && ctype(c, C_LEX1))); ) {
  91.         Xcheck(ws, wp);
  92.         switch (state) {
  93.           case SBASE:
  94.           Sbase:
  95.             switch (c) {
  96.               case '\\':
  97.                 c = getsc();
  98.                 if (c != '\n')
  99.                     *wp++ = QCHAR, *wp++ = c;
  100.                 else
  101.                     if (wp == Xstring(ws, wp))
  102.                         goto Again;
  103.                 break;
  104.               case '\'':
  105.                 *++statep = state = SSQUOTE;
  106.                 *wp++ = OQUOTE;
  107.                 break;
  108.               case '"':
  109.                 *++statep = state = SDQUOTE;
  110.                 *wp++ = OQUOTE;
  111.                 break;
  112.               default:
  113.                 goto Subst;
  114.             }
  115.             break;
  116.  
  117.           Subst:
  118.             switch (c) {
  119.               case '\\':
  120.                 c = getsc();
  121.                 switch (c) {
  122.                   case '\n':
  123.                     break;
  124.                   case '"': case '\\':
  125.                   case '$': case '`':
  126.                       Xcheck(ws, wp);
  127.                     *wp++ = QCHAR, *wp++ = c;
  128.                     break;
  129.                   default:
  130.                     Xcheck(ws, wp);
  131.                     *wp++ = CHAR, *wp++ = '\\';
  132.                     *wp++ = CHAR, *wp++ = c;
  133.                     break;
  134.                 }
  135.                 break;
  136.               case '$':
  137.                 c = getsc();
  138.                 if (c == '(') {
  139.                     *++statep = state = SPAREN;
  140.                     *wp++ = COMSUB;
  141.                 } else
  142.                 if (c == '{') {
  143.                     *++statep = state = SBRACE;
  144.                     *wp++ = OSUBST;
  145.                     c = getsc();
  146.                     do {
  147.                         Xcheck(ws, wp);
  148.                         *wp++ = c;
  149.                         c = getsc();
  150.                     } while (ctype(c, C_ALPHA|C_DIGIT));
  151.                     *wp++ = 0;
  152.                     /* todo: more compile-time checking */
  153.                     if (c == '}')
  154.                         ungetsc();
  155.                     else if (c == '#' || c == '%') {
  156.                         /* Korn pattern trimming */
  157.                         if (getsc() == c)
  158.                             c |= 0x80;
  159.                         else
  160.                             ungetsc();
  161.                         *wp++ = c;
  162.                     } else if (c == ':')
  163.                         *wp++ = 0x80|getsc();
  164.                     else
  165.                         *wp++ = c;
  166.                 } else if (ctype(c, C_ALPHA)) {
  167.                     *wp++ = OSUBST;
  168.                     do {
  169.                         Xcheck(ws, wp);
  170.                         *wp++ = c;
  171.                         c = getsc();
  172.                     } while (ctype(c, C_ALPHA|C_DIGIT));
  173.                     *wp++ = 0;
  174.                     *wp++ = CSUBST;
  175.                     ungetsc();
  176.                 } else if (ctype(c, C_DIGIT|C_VAR1)) {
  177.                     Xcheck(ws, wp);
  178.                     *wp++ = OSUBST;
  179.                     *wp++ = c;
  180.                     *wp++ = 0;
  181.                     *wp++ = CSUBST;
  182.                 } else {
  183.                     *wp++ = CHAR, *wp++ = '$';
  184.                     ungetsc();
  185.                 }
  186.                 break;
  187.               case '`':
  188.                 *++statep = state = SBQUOTE;
  189.                 *wp++ = COMSUB;
  190.                 break;
  191.               default:
  192.                 *wp++ = CHAR, *wp++ = c;
  193.             }
  194.             break;
  195.  
  196.           case SSQUOTE:
  197.             if (c == '\'') {
  198.                 state = *--statep;
  199.                 *wp++ = CQUOTE;
  200.             } else
  201.                 *wp++ = QCHAR, *wp++ = c;
  202.             break;
  203.  
  204.           case SDQUOTE:
  205.             if (c == '"') {
  206.                 state = *--statep;
  207.                 *wp++ = CQUOTE;
  208.             } else
  209.                 goto Subst;
  210.             break;
  211.  
  212.           case SPAREN:
  213.             if (c == '(')
  214.                 *++statep = state;
  215.             else if (c == ')')
  216.                 state = *--statep;
  217.             if (state == SPAREN)
  218.                 *wp++ = c;
  219.             else
  220.                 *wp++ = 0; /* end of COMSUB */
  221.             break;
  222.  
  223.           case SBRACE:
  224.             if (c == '}') {
  225.                 state = *--statep;
  226.                 *wp++ = CSUBST;
  227.             } else
  228.                 goto Sbase;
  229.             break;
  230.  
  231.           case SBQUOTE:
  232.             if (c == '`') {
  233.                 *wp++ = 0;
  234.                 state = *--statep;
  235.             } else if (c == '\\') {
  236.                 switch (c = getsc()) {
  237.                   case '\n':
  238.                     break;
  239.                   case '\\':
  240.                   case '$': case '`':
  241.                     *wp++ = c;
  242.                     break;
  243.                   default:
  244.                     *wp++ = '\\';
  245.                     *wp++ = c;
  246.                     break;
  247.                 }
  248.             } else
  249.                 *wp++ = c;
  250.             break;
  251.  
  252.           case SWORD:    /* ONEWORD */
  253.             goto Subst;
  254.  
  255.           case SDPAREN:    /* LETEXPR */
  256.             if (c == ')') {
  257.                 if (getsc() == ')') {
  258.                     c = 0;
  259.                     goto Done;
  260.                 } else
  261.                     ungetsc();
  262.             }
  263.             goto Subst;
  264.         }
  265.     }
  266. Done:
  267.     Xcheck(ws, wp);
  268.     if (state != istate)
  269.         yyerror("no closing quote");
  270.  
  271.     if (c == '<' || c == '>') {
  272.         char *cp = Xstring(ws, wp);
  273.         if (wp > cp && cp[0] == CHAR && digit(cp[1])) {
  274.             wp = cp; /* throw away word */
  275.             c2/*unit*/ = cp[1] - '0';
  276.         } else
  277.             c2/*unit*/ = c == '>'; /* 0 for <, 1 for > */
  278.     }
  279.  
  280.     if (wp == Xstring(ws, wp) && state == SBASE) {
  281.         Xfree(ws, sp);    /* free word */
  282.         /* no word, process LEX1 character */
  283.         switch (c) {
  284.           default:
  285.             return c;
  286.  
  287.           case '|':
  288.           case '&':
  289.           case ';':
  290.             if (getsc() == c)
  291.                 c = (c == ';') ? BREAK :
  292.                     (c == '|') ? LOGOR :
  293.                     (c == '&') ? LOGAND :
  294.                     YYERRCODE;
  295.             else
  296.                 ungetsc();
  297.             return c;
  298.  
  299.           case '>':
  300.           case '<': {
  301.             register struct ioword *iop;
  302.  
  303.             iop = (struct ioword *) alloc(sizeof(*iop), ATEMP);
  304.             iop->unit = c2/*unit*/;
  305.  
  306.             c2 = getsc();
  307.             if (c2 == '>' || c2 == '<') {
  308.                 iop->flag = c != c2 ? IORDWR : c == '>' ? IOCAT : IOHERE;
  309.                 c2 = getsc();
  310.             } else
  311.                 iop->flag = c == '>' ? IOWRITE : IOREAD;
  312.  
  313.             if (iop->flag == IOHERE)
  314.                 if (c2 == '-')
  315.                     iop->flag |= IOSKIP;
  316.                 else
  317.                     ungetsc();
  318.             else
  319.                 if (c2 == '&')
  320.                     iop->flag = IODUP;
  321.                 else if (c2 == '!' && iop->flag == IOWRITE)
  322.                     iop->flag |= IOCLOB;
  323.                 else
  324.                     ungetsc();
  325.             yylval.iop = iop;
  326.             return REDIR;
  327.             }
  328.           case '\n':
  329.             gethere();
  330.             if (cf & CONTIN)
  331.                 goto Again;
  332.             return c;
  333.  
  334.           case '(':
  335.             c2 = getsc();
  336.             if (c2 == ')')
  337.                 c = MPAREN;
  338.             else if (c2 == '(')
  339.                 c = MDPAREN;
  340.             else
  341.                 ungetsc();
  342.           case ')':
  343.             return c;
  344.         }
  345.     }
  346.  
  347.     *wp++ = EOS;        /* terminate word */
  348.     yylval.cp = Xclose(ws, wp);
  349.     if (state == SWORD || state == SDPAREN)    /* ONEWORD? */
  350.         return LWORD;
  351.     ungetsc();        /* unget terminator */
  352.  
  353.     /* copy word to unprefixed string ident */
  354.     for (sp = yylval.cp, dp = ident; dp < ident+IDENT && (c = *sp++) == CHAR; )
  355.         *dp++ = *sp++;
  356.     /* Make sure the ident array stays '\0' padded */
  357.     while (dp <= ident+IDENT)
  358.         *dp++ = '\0';
  359. #if 0
  360.     if (*ident == '~' || (dp = strchr(ident, '=')) != NULL && dp[1] == '~')
  361.         "Tilde expansion";
  362. #endif
  363.     if (c != EOS)
  364.         *ident = 0;    /* word is not unquoted */
  365.  
  366.     if (*ident != 0 && (cf&(KEYWORD|ALIAS))) {
  367.         register struct tbl *p;
  368.  
  369.         p = tsearch(&lexicals, ident, hash(ident));
  370.         if (p != NULL && (p->flag&ISSET))
  371.             if (p->type == CKEYWD && (cf&KEYWORD)) {
  372.                 afree(yylval.cp, ATEMP);
  373.                 return p->val.i;
  374.             } else if (p->type == CALIAS && (cf&ALIAS) &&
  375.                    !(p->flag&EXPALIAS)) {
  376.                 register Source *s;
  377.  
  378.                 if (rec_alias_cnt == sizeof(rec_alias_table)/sizeof(rec_alias_table[0]))
  379.                     yyerror("excessive recusrsive aliasing");
  380.                 else
  381.                     rec_alias_table[rec_alias_cnt++] = p;
  382.                 p->flag |= EXPALIAS;
  383.                 /* check for recursive aliasing */
  384.                 for (s = source; s->type == SALIAS; s = s->next)
  385.                     if (s->u.tblp == p)
  386.                         return LWORD;
  387.                 afree(yylval.cp, ATEMP);
  388.  
  389.                 /* push alias expansion */
  390.                 s = pushs(SALIAS);
  391.                 s->str = p->val.s;
  392.                 s->u.tblp = p;
  393.                 s->next = source;
  394.                 source = s;
  395.                 goto Again;
  396.             } 
  397.     }
  398.  
  399.     return LWORD;
  400. }
  401.  
  402. static void readhere();
  403.  
  404. gethere()
  405. {
  406.     register struct ioword **p;
  407.  
  408.     for (p = heres; p < herep; p++)
  409.         readhere(*p);
  410.     herep = heres;
  411. }
  412.  
  413. /*
  414.  * read "<<word" text into temp file
  415.  * todo: set up E_ERR to fclose(f) on unwind
  416.  */
  417.  
  418. static void
  419. readhere(iop)
  420.     register struct ioword *iop;
  421. {
  422.     register FILE *f;
  423.     struct temp *h;
  424.     register int c;
  425.     char *eof;
  426.     register char *cp;
  427.     char line [LINE+1];
  428.  
  429.     eof = evalstr(iop->name, 0);
  430.  
  431.     h = maketemp(ATEMP);
  432.     h->next = e.temps; e.temps = h;
  433.     iop->name = h->name;
  434.     f = fopen(h->name, "w");
  435.     if (f == NULL)
  436.         errorf("Cannot create temporary file\n");
  437.     setvbuf(f, (char *)NULL, _IOFBF, BUFSIZ);
  438.  
  439.     for (;;) {
  440.         cp = line;
  441.         while ((c = getsc()) != '\n') {
  442.             if (c == 0)
  443.                 errorf("here document `%s' unclosed\n", eof);
  444.             if (cp >= line+LINE)
  445.                 break;
  446.             *cp++ = c;
  447.         }
  448.         ungetsc();
  449.         *cp = 0;
  450.         for (cp = line; iop->flag&IOSKIP && *cp == '\t'; cp++)
  451.             ;
  452.         if (strcmp(eof, cp) == 0 || c == 0)
  453.             break;
  454.         while ((c = *cp++) != '\0')
  455.             putc(c, f);
  456.         while ((c = getsc()) != '\n') {
  457.             if (c == 0)
  458.                 errorf("here document `%s' unclosed\n", eof);
  459.             putc(c, f);
  460.         }
  461.         putc(c, f);
  462.     }
  463.     fclose(f);
  464. }
  465.  
  466. void
  467. yyerror(msg)
  468.     const char *msg;
  469. {
  470.     yynerrs++;
  471.     while (source->type == SALIAS) /* pop aliases */
  472.         source = source->next;
  473.     if (source->file != NULL)
  474.         shellf("%s[%d]: ", source->file, source->line);
  475.     source->str = NULLSTR;    /* zap pending input */
  476.     errorf("%s\n", msg);
  477. }
  478.  
  479. /*
  480.  * input for yylex with alias expansion
  481.  */
  482.  
  483. Source *
  484. pushs(type)
  485.     int type;
  486. {
  487.     register Source *s;
  488.  
  489.     s = (Source *) alloc(sizeof(Source), ATEMP);
  490.     s->type = type;
  491.     s->str = NULLSTR;
  492.     s->line = 0;
  493.     s->file = NULL;
  494.     s->echo = 0;
  495.     s->next = NULL;
  496.     return s;
  497. }
  498.  
  499. static int
  500. getsc_()
  501. {
  502.     register Source *s = source;
  503.     register int c;
  504.     extern void    mprint();
  505.     
  506.     while ((c = *s->str++) == 0) {
  507.         s->str = NULL;        /* return 0 for EOF by default */
  508.         switch (s->type) {
  509.           case SEOF:
  510.             s->str = NULLSTR;
  511.             return 0;
  512.  
  513.           case STTY:
  514.             if (histpush < 0) {    /* commands pushed by dofc */
  515.                 s->type = SHIST;
  516.                 s->str = NULLSTR;
  517.                 continue;
  518.             }
  519.             s->line++;
  520.             s->str = line;
  521.             line[0] = '\0';
  522.             mprint();
  523.             pprompt(prompt);
  524.             flushshf(1);    flushshf(2);
  525.             /*
  526.              * This allows the arival of a SIGCHLD 
  527.              * to not disturb us until we are ready. 
  528.              * BSD and other systems that 
  529.              * automatically rety a read after an 
  530.              * interrupt don't need this but it 
  531.              * doesn't do any harm either.
  532.              */
  533.               retry:
  534. #ifdef EDIT
  535. #ifdef EMACS
  536.             if (flag[FEMACS])
  537.                 c = x_read(ttyfd, line, LINE);
  538.             else
  539. #endif
  540. #ifdef VI
  541.             if (flag[FVI])
  542.                 c = x_read(ttyfd, line, LINE);
  543.             else
  544. #endif
  545. #endif
  546. #ifdef amigaos
  547.                 c = tty_read(ttyfd, line, LINE);
  548. #else
  549.                 c = read(ttyfd, line, LINE);
  550. #endif
  551.             if (c < 0 && sigchld_caught)
  552.             {
  553.               goto retry;
  554.             }
  555.             if (c < 0)    /* read error */
  556.                 c = 0;
  557.             line[c] = '\0';
  558.             prompt = strval(global("PS2"));
  559.             if (c == 0) { /* EOF */
  560.                 s->str = NULL;
  561.                 s->line--;
  562.             } else {
  563.                 c = 0;
  564.                 while(line[c] && ctype(line[c], C_IFS))
  565.                     c++;
  566.                 if (!line[c]) {
  567.                     s->str = &line[c - 1];
  568.                     s->line--;
  569.                 } else {
  570.                     s->str = &line[c];
  571. #ifdef EASY_HISTORY
  572.                     histsave(s->str);
  573. #else
  574.                     histsave(s->line, s->str, 1);
  575. #endif
  576.                 }
  577.             }
  578.             break;
  579.  
  580.           case SHIST:
  581.             if (histpush == 0) {
  582.                 s->type = STTY;
  583.                 s->str = NULLSTR;
  584.                 continue;
  585.             }
  586.             s->line++;
  587.             s->str = histptr[++histpush];
  588. #if 0
  589.             pprompt("!< ");    /* todo: PS9 */
  590. #endif
  591.             shellf("%s\n", s->str);
  592.             strcpy(line, s->str);
  593.             s->str = strchr(line, 0);
  594.             *s->str++ = '\n';
  595.             *s->str = 0;
  596.             s->str = line;
  597.             break;
  598.  
  599.           case SFILE:
  600.             s->line++;
  601.             s->str = fgets(line, LINE, s->u.file);
  602.             if (s->str == NULL)
  603.                 if (s->u.file != stdin)
  604.                     fclose(s->u.file);
  605.             break;
  606.  
  607.           case SWSTR:
  608.             break;
  609.  
  610.           case SSTRING:
  611.             s->str = "\n";
  612.             s->type = SEOF;
  613.             break;
  614.  
  615.           case SWORDS:
  616.             s->str = *s->u.strv++;
  617.             s->type = SWORDSEP;
  618.             break;
  619.  
  620.           case SWORDSEP:
  621.             if (*s->u.strv == NULL) {
  622.                 s->str = "\n";
  623.                 s->type = SEOF;
  624.             } else {
  625.                 s->str = " ";
  626.                 s->type = SWORDS;
  627.             }
  628.             break;
  629.  
  630.           case SALIAS:
  631.             s->str = s->u.tblp->val.s;
  632.             if (s->str[0] != 0) {
  633.                 c = strchr(s->str, 0)[-1];
  634.                 if (c == ' ' || c == '\t')
  635.                     alias = 1;    /* trailing ' ' */
  636.             }
  637.             source = s = s->next;
  638.             expanding_alias = 1;
  639.             continue;
  640.         }
  641.         if (s->str == NULL) {
  642.             s->type = SEOF;
  643.             s->str = NULLSTR;
  644.             return 0;
  645.         }
  646.         if (s->echo)
  647.             fputs(s->str, shlout);
  648.     }
  649.     return c;
  650. }
  651.  
  652. pprompt(cp)
  653.     register char *cp;
  654. {
  655.     while (*cp != 0)
  656.         if (*cp != '!')
  657.             putc(*cp++, shlout);
  658.         else
  659.             if (*++cp == '!')
  660.                 putc(*cp++, shlout);
  661.             else
  662.                 shellf("%d", source->line);
  663.     fflush(shlout);
  664. }
  665.