home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / k / ksh48.zip / sh / lex.c < prev    next >
C/C++ Source or Header  |  1992-09-01  |  14KB  |  675 lines

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