home *** CD-ROM | disk | FTP | other *** search
/ Der Mediaplex Sampler - Die 6 von Plex / 6_v_plex.zip / 6_v_plex / DISK6 / OS_15 / SH.ZIP / SH2.C < prev    next >
C/C++ Source or Header  |  1994-01-24  |  18KB  |  975 lines

  1. /* MS-DOS SHELL - Parser
  2.  *
  3.  * MS-DOS SHELL - Copyright (c) 1990 Data Logic Limited and Charles Forsyth
  4.  *
  5.  * This code is based on (in part) the shell program written by Charles
  6.  * Forsyth and is subject to the following copyright restrictions:
  7.  *
  8.  * 1.  Redistribution and use in source and binary forms are permitted
  9.  *     provided that the above copyright notice is duplicated in the
  10.  *     source form and the copyright notice in file sh6.c is displayed
  11.  *     on entry to the program.
  12.  *
  13.  * 2.  The sources (or parts thereof) or objects generated from the sources
  14.  *     (or parts of sources) cannot be sold under any circumstances.
  15.  *
  16.  *    $Header: C:/SRC/SHELL/RCS/sh2.c 1.7 90/09/11 20:00:01 Ian_Stewartson Exp $
  17.  *
  18.  *    $Log:    sh2.c $
  19.  * Revision 1.7  90/09/11  20:00:01  Ian_Stewartson
  20.  * Add support for $() POSIX functionality
  21.  *
  22.  * Revision 1.6  90/08/14  23:30:26  Ian_Stewartson
  23.  * Add support for read/write IO
  24.  *
  25.  * Revision 1.5  90/04/25  09:18:38  MS_user
  26.  * Fix for ... do to not require terminating colon
  27.  *
  28.  * Revision 1.4  90/03/14  19:30:06  MS_user
  29.  * Make collect a global for here document processing.
  30.  * Add IOTHERE support to detect <<- redirection
  31.  *
  32.  * Revision 1.3  90/03/06  16:49:42  MS_user
  33.  * Add disable history option
  34.  *
  35.  * Revision 1.2  90/03/05  13:49:41  MS_user
  36.  * Change talking checks
  37.  *
  38.  * Revision 1.1  90/01/25  13:41:12  MS_user
  39.  * Initial revision
  40.  *
  41.  */
  42.  
  43. #include <sys/types.h>
  44. #include <stddef.h>
  45. #include <stdio.h>
  46. #include <signal.h>
  47. #include <errno.h>
  48. #include <setjmp.h>
  49. #include <string.h>
  50. #include <ctype.h>
  51. #include <unistd.h>
  52. #include <limits.h>
  53. #include <dir.h>
  54. #include "sh.h"
  55.  
  56. /*
  57.  * shell: syntax (C version)
  58.  */
  59.  
  60. typedef union {
  61.     char    *cp;
  62.     char    **wp;
  63.     int        i;
  64.     C_Op    *o;
  65. } YYSTYPE;
  66.  
  67. #define    WORD    256
  68. #define    LOGAND    257
  69. #define    LOGOR    258
  70. #define    BREAK    259
  71. #define    IF    260
  72. #define    THEN    261
  73. #define    ELSE    262
  74. #define    ELIF    263
  75. #define    FI    264
  76. #define    CASE    265
  77. #define    ESAC    266
  78. #define    FOR    267
  79. #define    WHILE    268
  80. #define    UNTIL    269
  81. #define    DO    270
  82. #define    DONE    271
  83. #define    IN    272
  84. #define    YYERRCODE 300
  85.  
  86. /* flags to yylex */
  87.  
  88. #define    CONTIN    01    /* skip new lines to complete command */
  89.  
  90. static bool        startl;
  91. static int        peeksym;
  92. static bool        Allow_funcs;
  93. static int        iounit = IODEFAULT;
  94. static C_Op        *tp;
  95. static YYSTYPE        yylval;
  96. static char        *syntax_err = "sh: syntax error\n";
  97.  
  98. static C_Op        *pipeline (int);
  99. static C_Op        *andor (void);
  100. static C_Op        *c_list (bool);
  101. static bool        synio (int);
  102. static void        musthave (int, int);
  103. static C_Op        *simple (void);
  104. static C_Op        *nested (int, int);
  105. static C_Op        *command (int);
  106. static C_Op        *dogroup (int);
  107. static C_Op        *thenpart (void);
  108. static C_Op        *elsepart (void);
  109. static C_Op        *caselist (void);
  110. static C_Op        *casepart (void);
  111. static char        **pattern (void);
  112. static char        **wordlist (void);
  113. static C_Op        *list (C_Op *, C_Op *);
  114. static C_Op        *block (int, C_Op *, C_Op *, char **);
  115. static int        rlookup (char *);
  116. static C_Op        *namelist (C_Op *);
  117. static char        **copyw (void);
  118. static void        word (char *);
  119. static IO_Actions    **copyio (void);
  120. static IO_Actions    *io (int, int, char *);
  121. static void        yyerror (char *);
  122. static int        yylex (int);
  123. static int        dual (int);
  124. static void        diag (int);
  125. static char        *tree (unsigned int);
  126.  
  127. C_Op    *yyparse ()
  128. {
  129.     C_Op    *outtree;
  130.  
  131.     startl  = TRUE;
  132.     peeksym = 0;
  133.     yynerrs = 0;
  134.     outtree = c_list (TRUE);
  135.     musthave (NL, 0);
  136.  
  137.     return (yynerrs != 0) ? (C_Op *)NULL : outtree;
  138. }
  139.  
  140. static C_Op    *pipeline (cf)
  141. int        cf;
  142. {
  143.     register C_Op    *t, *p;
  144.     register int    c;
  145.  
  146.     if ((t = command (cf)) != (C_Op *)NULL)
  147.     {
  148.     Allow_funcs = FALSE;
  149.     while ((c = yylex (0)) == '|')
  150.     {
  151.         if ((p = command (CONTIN)) == (C_Op *)NULL)
  152.         yyerror (syntax_err);
  153.  
  154. /* shell statement */
  155.  
  156.         if ((t->type != TPAREN) && (t->type != TCOM))
  157.         t = block (TPAREN, t, NOBLOCK, NOWORDS);
  158.  
  159.         t = block (TPIPE, t, p, NOWORDS);
  160.     }
  161.  
  162.     peeksym = c;
  163.     }
  164.  
  165.     return t;
  166. }
  167.  
  168. static C_Op    *andor ()
  169. {
  170.     register C_Op    *t, *p;
  171.     register int    c;
  172.  
  173.     if ((t = pipeline (0)) != (C_Op *)NULL)
  174.     {
  175.     Allow_funcs = FALSE;
  176.     while (((c = yylex (0)) == LOGAND) || (c == LOGOR))
  177.     {
  178.         if ((p = pipeline (CONTIN)) == (C_Op *)NULL)
  179.         yyerror (syntax_err);
  180.  
  181.         t = block ((c == LOGAND) ? TAND : TOR, t, p, NOWORDS);
  182.     }
  183.  
  184.     peeksym = c;
  185.     }
  186.  
  187.     return t;
  188. }
  189.  
  190. static C_Op    *c_list (allow)
  191. bool        allow;
  192. {
  193.     register C_Op    *t, *p;
  194.     register int    c;
  195.  
  196. /* Functions are only allowed at the start of a line */
  197.  
  198.     Allow_funcs = allow;
  199.  
  200.     if ((t = andor ()) != (C_Op *)NULL)
  201.     {
  202.     Allow_funcs = FALSE;
  203.  
  204.     if ((peeksym = yylex (0)) == '&')
  205.         t = block (TASYNC, t, NOBLOCK, NOWORDS);
  206.  
  207.     while ((c = yylex(0)) == ';' || c == '&' || multiline && c == NL)
  208.     {
  209.         if ((p = andor ()) == (C_Op *)NULL)
  210.         return t;
  211.  
  212.         if ((peeksym = yylex (0)) == '&')
  213.         p = block (TASYNC, p, NOBLOCK, NOWORDS);
  214.  
  215.         t = list (t, p);
  216.     }
  217.     peeksym = c;
  218.     }
  219.  
  220.     return t;
  221. }
  222.  
  223.  
  224. static bool    synio (cf)
  225. int        cf;
  226. {
  227.     register IO_Actions    *iop;
  228.     register int    i;
  229.     register int    c;
  230.  
  231.     if (((c = yylex (cf)) != '<') && (c != '>'))
  232.     {
  233.     peeksym = c;
  234.     return FALSE;
  235.     }
  236.  
  237.     i = yylval.i;
  238.     musthave (WORD, 0);
  239.     iop = io (iounit, i, yylval.cp);
  240.     iounit = IODEFAULT;
  241.  
  242.     if (i & IOHERE)
  243.     markhere (yylval.cp, iop);
  244.  
  245.     return TRUE;
  246. }
  247.  
  248. static void    musthave (c, cf)
  249. int        c, cf;
  250. {
  251.     if ((peeksym = yylex (cf)) != c)
  252.     yyerror (syntax_err);
  253.  
  254.     peeksym = 0;
  255. }
  256.  
  257. static C_Op    *simple ()
  258. {
  259.     register C_Op    *t = (C_Op *)NULL;
  260.  
  261.     while (1)
  262.     {
  263.     switch (peeksym = yylex (0))
  264.     {
  265.         case '<':
  266.         case '>':
  267.         synio (0);
  268.         break;
  269.  
  270.         case WORD:
  271.         if (t == (C_Op *)NULL)
  272.             (t = (C_Op *)tree (sizeof (C_Op)))->type = TCOM;
  273.  
  274.         peeksym = 0;
  275.         word (yylval.cp);
  276.         break;
  277.  
  278. /* Check for function - name () { word; } */
  279.  
  280.         case '(':
  281.         if ((t != (C_Op *)NULL) && (Allow_funcs == TRUE) &&
  282.             (wdlist != (Word_B *)NULL) && (wdlist->w_nword == 1))
  283.         {
  284.             Word_B    *save;
  285.  
  286.             peeksym = 0;
  287.             musthave (')', 0);
  288.             musthave ('{', 0);
  289.             save = wdlist;
  290.             wdlist = (Word_B *)NULL;
  291.             t->type = TFUNC;
  292.             t->left = nested (TBRACE, '}');
  293.             wdlist = save;
  294.             Allow_funcs = FALSE;
  295.             musthave (NL, 0);
  296.             peeksym = NL;
  297.         }
  298.  
  299.         default:
  300.         return t;
  301.     }
  302.     }
  303. }
  304.  
  305. static C_Op    *nested (type, mark)
  306. int        type, mark;
  307. {
  308.     register C_Op    *t;
  309.  
  310.     multiline++;
  311.     t = c_list (FALSE);
  312.     musthave (mark, 0);
  313.     multiline--;
  314.     return block (type, t, NOBLOCK, NOWORDS);
  315. }
  316.  
  317. static C_Op    *command (cf)
  318. int        cf;
  319. {
  320.     register C_Op    *t;
  321.     Word_B        *iosave = iolist;
  322.     register int    c;
  323.  
  324.     iolist = (Word_B *)NULL;
  325.  
  326.     if (multiline)
  327.     cf |= CONTIN;
  328.  
  329.     while (synio (cf))
  330.     cf = 0;
  331.  
  332.     switch (c = yylex (cf))
  333.     {
  334.     default:
  335.         peeksym = c;
  336.  
  337.         if ((t = simple ()) == (C_Op *)NULL)
  338.         {
  339.         if (iolist == (Word_B *)NULL)
  340.             return (C_Op *)NULL;
  341.  
  342.         (t = (C_Op *)tree (sizeof (C_Op)))->type = TCOM;
  343.         }
  344.  
  345.         break;
  346.  
  347.     case '(':
  348.         t = nested (TPAREN, ')');
  349.         break;
  350.  
  351.     case '{':
  352.         t = nested (TBRACE, '}');
  353.         break;
  354.  
  355.     case FOR:
  356.         (t = (C_Op *)tree (sizeof (C_Op)))->type = TFOR;
  357.         musthave (WORD, 0);
  358.         startl = TRUE;
  359.         t->str = yylval.cp;
  360.         multiline++;
  361.         t->words = wordlist ();
  362.  
  363. /* CHeck for "for word in word...; do" versus "for word do" */
  364.  
  365.         c = yylex (0);
  366.  
  367.         if ((t->words == (char **)NULL) && (c != NL))
  368.         peeksym = c;
  369.  
  370.         else if ((t->words != (char **)NULL) && (c != NL) && (c != ';'))
  371.         yyerror (syntax_err);
  372.  
  373.         t->left = dogroup (0);
  374.         multiline--;
  375.         break;
  376.  
  377.     case WHILE:
  378.     case UNTIL:
  379.         multiline++;
  380.         t = (C_Op *)tree (sizeof (C_Op));
  381.         t->type = (c == WHILE) ? TWHILE : TUNTIL;
  382.         t->left = c_list (FALSE);
  383.         t->right = dogroup (1);
  384.         t->words = (char **)NULL;
  385.         multiline--;
  386.         break;
  387.  
  388.     case CASE:
  389.         (t = (C_Op *)tree (sizeof (C_Op)))->type = TCASE;
  390.         musthave (WORD, 0);
  391.         t->str = yylval.cp;
  392.         startl = TRUE;
  393.         multiline++;
  394.         musthave (IN, CONTIN);
  395.         startl = TRUE;
  396.         t->left = caselist();
  397.         musthave (ESAC, 0);
  398.         multiline--;
  399.         break;
  400.  
  401.     case IF:
  402.         multiline++;
  403.         (t = (C_Op *)tree (sizeof (C_Op)))->type = TIF;
  404.         t->left = c_list (FALSE);
  405.         t->right = thenpart ();
  406.         musthave (FI, 0);
  407.         multiline--;
  408.         break;
  409.     }
  410.  
  411.     while (synio (0))
  412.     ;
  413.  
  414.     t = namelist (t);
  415.     iolist = iosave;
  416.     return t;
  417. }
  418.  
  419. static C_Op    *dogroup (onlydone)
  420. int        onlydone;
  421. {
  422.     register int    c;
  423.     register C_Op    *list;
  424.  
  425.     if (((c = yylex (CONTIN)) == DONE) && onlydone)
  426.     return (C_Op *)NULL;
  427.  
  428.     if (c != DO)
  429.     yyerror (syntax_err);
  430.  
  431.     list = c_list (FALSE);
  432.     musthave (DONE, 0);
  433.     return list;
  434. }
  435.  
  436. static C_Op    *thenpart ()
  437. {
  438.     register int    c;
  439.     register C_Op    *t;
  440.  
  441.     if ((c = yylex (0)) != THEN)
  442.     {
  443.     peeksym = c;
  444.     return (C_Op *)NULL;
  445.     }
  446.  
  447.     (t = (C_Op *)tree (sizeof (C_Op)))->type = 0;
  448.  
  449.     if ((t->left = c_list (FALSE)) == (C_Op *)NULL)
  450.     yyerror (syntax_err);
  451.  
  452.     t->right = elsepart ();
  453.     return t;
  454. }
  455.  
  456. static C_Op    *elsepart ()
  457. {
  458.     register int    c;
  459.     register C_Op    *t;
  460.  
  461.     switch (c = yylex (0))
  462.     {
  463.     case ELSE:
  464.         if ((t = c_list (FALSE)) == (C_Op *)NULL)
  465.         yyerror (syntax_err);
  466.  
  467.         return t;
  468.  
  469.     case ELIF:
  470.         (t = (C_Op *)tree (sizeof (C_Op)))->type = TELIF;
  471.         t->left = c_list (FALSE);
  472.         t->right = thenpart ();
  473.         return t;
  474.  
  475.     default:
  476.         peeksym = c;
  477.         return (C_Op *)NULL;
  478.     }
  479. }
  480.  
  481. static C_Op    *caselist()
  482. {
  483.     register C_Op    *t = (C_Op *)NULL;
  484.  
  485.     while ((peeksym = yylex (CONTIN)) != ESAC)
  486.     t = list (t, casepart ());
  487.  
  488.     return t;
  489. }
  490.  
  491. static C_Op    *casepart ()
  492. {
  493.     register C_Op    *t = (C_Op *)tree (sizeof (C_Op));
  494.  
  495.     t->type = TPAT;
  496.     t->words = pattern ();
  497.     musthave (')', 0);
  498.     t->left = c_list (FALSE);
  499.  
  500.     if ((peeksym = yylex (CONTIN)) != ESAC)
  501.     musthave (BREAK, CONTIN);
  502.  
  503.     return t;
  504. }
  505.  
  506. static char    **pattern()
  507. {
  508.     register int    c, cf;
  509.  
  510.     cf = CONTIN;
  511.  
  512.     do
  513.     {
  514.     musthave (WORD, cf);
  515.     word (yylval.cp);
  516.     cf = 0;
  517.     } while ((c = yylex(0)) == '|');
  518.  
  519.     peeksym = c;
  520.     word (NOWORD);
  521.     return copyw();
  522. }
  523.  
  524. static char    **wordlist()
  525. {
  526.     register int    c;
  527.  
  528.     if ((c = yylex(0)) != IN)
  529.     {
  530.     peeksym = c;
  531.     return (char **)NULL;
  532.     }
  533.  
  534.     startl = FALSE;
  535.     while ((c = yylex (0)) == WORD)
  536.     word (yylval.cp);
  537.  
  538.     word (NOWORD);
  539.     peeksym = c;
  540.  
  541.     return copyw();
  542. }
  543.  
  544. /*
  545.  * supporting functions
  546.  */
  547.  
  548. static C_Op    *list (t1, t2)
  549. register C_Op    *t1, *t2;
  550. {
  551.     if (t1 == (C_Op *)NULL)
  552.     return t2;
  553.  
  554.     if (t2 == (C_Op *)NULL)
  555.     return t1;
  556.  
  557.     return block (TLIST, t1, t2, NOWORDS);
  558. }
  559.  
  560. static C_Op    *block (type, t1, t2, wp)
  561. int             type;
  562. C_Op        *t1, *t2;
  563. char        **wp;
  564. {
  565.     register C_Op *t = (C_Op *)tree (sizeof (C_Op));
  566.  
  567.     t->type = type;
  568.     t->left = t1;
  569.     t->right = t2;
  570.     t->words = wp;
  571.     return t;
  572. }
  573.  
  574. static struct res {
  575.     char    *r_name;
  576.     int        r_val;
  577. } restab[] = {
  578.     {    "for",        FOR},        {"case",    CASE},
  579.     {"esac",    ESAC},        {"while",    WHILE},
  580.     {"do",        DO},        {"done",    DONE},
  581.     {"if",        IF},        {"in",        IN},
  582.     {"then",    THEN},        {"else",    ELSE},
  583.     {"elif",    ELIF},        {"until",    UNTIL},
  584.     {"fi",        FI},
  585.  
  586.     {";;",        BREAK},        {"||",        LOGOR},
  587.     {"&&",        LOGAND},    {"{",        '{'},
  588.     {"}",        '}'},
  589.  
  590.     {(char *)NULL,    0}
  591. };
  592.  
  593. static int    rlookup (n)
  594. register char    *n;
  595. {
  596.     register struct res        *rp = restab;
  597.  
  598.     while ((rp->r_name != (char *)NULL) && strcmp (rp->r_name, n))
  599.     rp++;
  600.  
  601.     return rp->r_val;
  602. }
  603.  
  604. static C_Op    *namelist(t)
  605. register C_Op    *t;
  606. {
  607.     if (iolist)
  608.     {
  609.     iolist = addword ((char *)NULL, iolist);
  610.     t->ioact = copyio ();
  611.     }
  612.  
  613.     else
  614.     t->ioact = (IO_Actions **)NULL;
  615.  
  616.     if ((t->type != TCOM) && (t->type != TFUNC))
  617.     {
  618.     if ((t->type != TPAREN) && (t->ioact != (IO_Actions **)NULL))
  619.     {
  620.         t = block (TPAREN, t, NOBLOCK, NOWORDS);
  621.         t->ioact = t->left->ioact;
  622.         t->left->ioact = (IO_Actions **)NULL;
  623.     }
  624.     }
  625.  
  626.     else
  627.     {
  628.     word (NOWORD);
  629.     t->words = copyw();
  630.     }
  631.  
  632.     return t;
  633. }
  634.  
  635. static char    **copyw ()
  636. {
  637.     register char **wd = getwords (wdlist);
  638.  
  639.     wdlist = (Word_B *)NULL;
  640.     return wd;
  641. }
  642.  
  643. static void    word (cp)
  644. char        *cp;
  645. {
  646.     wdlist = addword (cp, wdlist);
  647. }
  648.  
  649. static IO_Actions    **copyio ()
  650. {
  651.     IO_Actions    **iop = (IO_Actions **)getwords (iolist);
  652.  
  653.     iolist = (Word_B *)NULL;
  654.     return iop;
  655. }
  656.  
  657. static IO_Actions    *io (u, f, cp)
  658. int            f, u;
  659. char            *cp;
  660. {
  661.     register IO_Actions *iop = (IO_Actions *)tree (sizeof (IO_Actions));
  662.  
  663.     iop->io_unit = u;
  664.     iop->io_flag = f;
  665.     iop->io_name = cp;
  666.     iolist = addword ((char *)iop, iolist);
  667.     return iop;
  668. }
  669.  
  670. static void    yyerror (s)
  671. char        *s;
  672. {
  673.     yynerrs++;
  674.  
  675.     if (Interactive ())
  676.     {
  677.     multiline = 0;
  678.  
  679.     while ((eofc () == 0) && (yylex (0) != NL))
  680.         ;
  681.     }
  682.  
  683.     print_error (s);
  684.     fail ();
  685. }
  686.  
  687. static int    yylex (cf)
  688. int        cf;
  689. {
  690.     register int    c, c1;
  691.     bool        atstart;
  692.  
  693.     if ((c = peeksym) > 0)
  694.     {
  695.     peeksym = 0;
  696.  
  697.     if (c == NL)
  698.         startl = TRUE;
  699.  
  700.     return c;
  701.     }
  702.  
  703.     e.linep = e.cline;
  704.     atstart = startl;
  705.     startl = FALSE;
  706.     yylval.i = 0;
  707.  
  708. loop:
  709.     while ((c = Getc (0)) == SP || c == '\t')
  710.     ;
  711.  
  712.     switch (c)
  713.     {
  714.     default:
  715.         if (isdigit (c))
  716.         {
  717.         unget (c1 = Getc(0));
  718.  
  719.         if ((c1 == '<') || (c1 == '>'))
  720.         {
  721.             iounit = c - '0';
  722.             goto loop;
  723.         }
  724.  
  725.         *e.linep++ = (char)c;
  726.         c = c1;
  727.         }
  728.  
  729.         break;
  730.  
  731.     case '#':
  732.         while ((c = Getc(0)) != 0 && (c != NL))
  733.         ;
  734.  
  735.         unget(c);
  736.         goto loop;
  737.  
  738.     case 0:
  739.         return c;
  740.  
  741. /* Allow $name, ${name}, $(command) and support $[arthmetic functions] */
  742.  
  743.     case '$':
  744.         *e.linep++ = (char)c;
  745.  
  746.         if (((c = Getc(0)) == '{') || (c == '('))
  747.         {
  748.         if ((c = collect (c, (c == '{') ? '}' : ')')) != '\0')
  749.             return (c);
  750.  
  751.         goto pack;
  752.         }
  753.  
  754.         break;
  755.  
  756.     case '`':
  757.     case '\'':
  758.     case '"':
  759.         if ((c = collect (c, c)) != '\0')
  760.         return c;
  761.  
  762.         goto pack;
  763.  
  764.     case '|':
  765.     case '&':
  766.     case ';':
  767.         if ((c1 = dual (c)) != '\0')
  768.         {
  769.         startl = TRUE;
  770.         return c1;
  771.         }
  772.  
  773.     case '(':
  774.     case ')':
  775.         startl = TRUE;
  776.         return c;
  777.  
  778.     case '^':
  779.         startl = TRUE;
  780.         return '|';
  781.  
  782.     case '>':
  783.     case '<':
  784.         diag (c);
  785.         return c;
  786.  
  787.     case NL:
  788.         gethere ();
  789.         startl = TRUE;
  790.  
  791.         if (multiline || (cf & CONTIN))
  792.         {
  793.         if (Interactive ())
  794.         {
  795. #ifndef NO_HISTORY
  796.             Add_History (FALSE);
  797. #endif
  798.             put_prompt (ps2->value);
  799.         }
  800.  
  801.         if (cf & CONTIN)
  802.             goto loop;
  803.         }
  804.  
  805.         return(c);
  806.     }
  807.  
  808.     unget (c);
  809.  
  810. pack:
  811.     while (((c = Getc (0)) != 0) && (!any ((char)c, "`$ '\"\t;&<>()|^\n")))
  812.     {
  813.     if (e.linep >= e.eline)
  814.         print_error ("sh: word too long\n");
  815.  
  816.     else
  817.         *e.linep++ = (char)c;
  818.     }
  819.  
  820.     unget (c);
  821.  
  822.     if (any ((char)c, spcl2))
  823.     goto loop;
  824.  
  825.     *e.linep++ = '\0';
  826.  
  827.     if (atstart && (c = rlookup (e.cline)) != 0)
  828.     {
  829.     startl = TRUE;
  830.     return c;
  831.     }
  832.  
  833. /* Special processing for $(command) to convert it to `command` */
  834.  
  835.     if (strncmp (e.cline, "$(", 2) == 0)
  836.     {
  837.     yylval.cp = strsave (e.cline + 1, areanum);
  838.     *yylval.cp = '`';
  839.     yylval.cp[strlen (yylval.cp) - 1] = '`';
  840.     }
  841.  
  842. /* Otherwise, handle words beginning with a ~ */
  843.  
  844.     else if (*e.cline == '~')
  845.     {
  846.     char    *dir = lookup (home, FALSE)->value;
  847.  
  848.     yylval.cp = tree (strlen (e.cline) + strlen (dir));
  849.     strcat (strcpy (yylval.cp, dir), e.cline + 1);
  850.     }
  851.  
  852. /* Otherwise, just save it */
  853.  
  854.     else
  855.     yylval.cp = strsave (e.cline, areanum);
  856.  
  857.     return WORD;
  858. }
  859.  
  860. /* Read input until we read the specified end character */
  861.  
  862. int        collect (c, c1)
  863. register int    c, c1;
  864. {
  865.     char *s = "x\n";
  866.  
  867.     *e.linep++ = (char)c;        /* Save the current character    */
  868.  
  869.     while ((c = Getc (c1)) != c1)
  870.     {
  871.     if (c == 0)             /* End of file - abort        */
  872.     {
  873.         unget (c);
  874.         *s = (char)c1;
  875.         S_puts ("sh: no closing ");
  876.         yyerror (s);
  877.         return YYERRCODE;
  878.     }
  879.  
  880.     if (Interactive () && (c == NL))
  881.     {
  882. #ifndef NO_HISTORY
  883.         Add_History (FALSE);
  884. #endif
  885.         put_prompt (ps2->value);
  886.     }
  887.  
  888.     *e.linep++ = (char)c;
  889.     }
  890.  
  891.     *e.linep++ = (char)c;
  892.     return 0;
  893. }
  894.  
  895. /* Check for &&, || and ;; */
  896.  
  897. static int    dual (c)
  898. register int    c;
  899. {
  900.     char        s[3];
  901.     register char    *cp = s;
  902.  
  903. /* Get the next character and set up double string.  Look up in valid
  904.  * operators.  If invalid, unget character
  905.  */
  906.  
  907.     *cp++ = (char)c;
  908.     *cp++ = (char)Getc (0);
  909.     *cp = 0;
  910.  
  911.     if ((c = rlookup (s)) == 0)
  912.     unget (*--cp);
  913.  
  914.     return c;
  915. }
  916.  
  917. /* Process I/O re-direction */
  918.  
  919. static void    diag (ec)
  920. register int    ec;
  921. {
  922.     register int    c;
  923.  
  924. /* Get the next character to see if it is a re-direction character as well */
  925.  
  926.     if (((c = Getc (0)) == '>') || (c == '<'))
  927.     {
  928.  
  929. /* Check for open in read/write mode */
  930.  
  931.     if ((ec == '<') && (c == '>'))
  932.         yylval.i = IOWRITE | IOREAD;
  933.  
  934. /* Otherwise, we must have a double character */
  935.  
  936.     else if (c != ec)
  937.         yyerror (syntax_err);
  938.  
  939.     else
  940.         yylval.i = (ec == '>') ? IOWRITE | IOCAT : IOHERE;
  941.  
  942.     c = Getc (0);
  943.     }
  944.  
  945.     else
  946.     yylval.i = (ec == '>') ? IOWRITE : IOREAD;
  947.  
  948. /* Check for >&, <& and <<- */
  949.  
  950.     if ((c == '-') && (yylval.i == IOHERE))
  951.     yylval.i |= IOTHERE;
  952.  
  953.     else if ((c != '&') || (yylval.i == IOHERE))
  954.     unget (c);
  955.  
  956.     else
  957.     yylval.i |= IODUP;
  958. }
  959.  
  960. /* Get a new tree leaf structure */
  961.  
  962. static char    *tree (size)
  963. unsigned int    size;
  964. {
  965.     register char *t;
  966.  
  967.     if ((t = getcell (size)) == (char *)NULL)
  968.     {
  969.     S_puts ("sh: command line too complicated\n");
  970.     fail ();
  971.     }
  972.  
  973.     return t;
  974. }
  975.