home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 4 / FreshFish_May-June1994.bin / bbs / gnu / pdksh-src.lha / src / amiga / pdksh / sh / lex.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-01  |  12.4 KB  |  659 lines

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