home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / MISC / GNU / SH164AS.ZIP / SHELL / SH4.C < prev    next >
Encoding:
C/C++ Source or Header  |  1992-02-28  |  24.0 KB  |  1,282 lines

  1. /* MS-DOS SHELL - 'word' Interpretator
  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/sh4.c 1.11 90/09/11 20:01:23 Ian_Stewartson Exp $
  17.  *
  18.  *    $Log:    sh4.c $
  19.  * Revision 1.11  90/09/11  20:01:23  Ian_Stewartson
  20.  * Fix ${#*} functionality to match POSIX
  21.  * 
  22.  * Revision 1.10  90/08/24  21:56:18  Ian_Stewartson
  23.  * Add support for POSIX macro commands {x#y} and {x%y}
  24.  * 
  25.  * Revision 1.9  90/08/16  10:29:27  Ian_Stewartson
  26.  * Add support from String length option in ${}.
  27.  * Restore the memcpys
  28.  * 
  29.  * Revision 1.8  90/08/14  23:34:35  MS_user
  30.  * Fix memory bugs - ensure string copy is terminated
  31.  * Change some memcpys to local copy functions - don't know why.
  32.  * 
  33.  * Revision 1.7  90/06/21  11:11:51  MS_user
  34.  * Ensure Areanum is set correctly for memory areas
  35.  * 
  36.  * Revision 1.6  90/04/25  22:35:26  MS_user
  37.  * Make anys a global function
  38.  * 
  39.  * Revision 1.5  90/03/27  20:33:41  MS_user
  40.  * Clear extended file name on interrupt
  41.  * 
  42.  * Revision 1.4  90/03/16  21:27:33  MS_user
  43.  * Stop grave changing NL to SP for here documents
  44.  * 
  45.  * Revision 1.3  90/03/16  11:50:41  MS_user
  46.  * Correct Bug which prevents $$, $#, $!, $? and $- working.
  47.  * 
  48.  * Revision 1.2  90/03/14  19:30:34  MS_user
  49.  * Change subgetc for here document processing.  In particular `list`
  50.  * processing.  I hope the detection method for this is right!
  51.  * 
  52.  * Revision 1.1  90/01/25  13:41:38  MS_user
  53.  * Initial revision
  54.  * 
  55.  */
  56.  
  57. #include <sys/types.h>
  58. #include <sys/stat.h>
  59. #include <signal.h>
  60. #include <errno.h>
  61. #include <setjmp.h>
  62. #include <dirent.h>
  63. #include <string.h>
  64. #include <stdlib.h>
  65. #include <unistd.h>
  66. #include <ctype.h>
  67. #include <bios.h>
  68. #include <dos.h>
  69. #include "sh.h"
  70.  
  71. /*
  72.  * ${}, `command`, blank interpretation, quoting and file name expansion
  73.  */
  74.  
  75. #define    NSTART        16        /* default number of words to    */
  76.                     /* allow for initially        */
  77. static Word_B        *C_EList;    /* For expand functions        */
  78. static Word_B        *New_Elist;
  79. static char        *spcl  = "[?*";
  80. static char        *spcl1 = "\"'";
  81. static char        *bad_subs = "sh: bad substitution\n";
  82.  
  83. static void        globname (char *, char *);
  84. static bool        expand (char *, Word_B **, int);
  85. static char        dollar (bool);
  86. static bool        grave (bool);
  87. static Word_B        *Expand_globs (char *, Word_B *);
  88. static bool        anyspcl (Word_B *);
  89. static char        *blank (int);
  90. static char        *generate (char *, char *, char *, char *);
  91. static char        *unquote (char *);
  92. static Word_B        *newword (int);
  93. static char        *anys_p (char *, char *);
  94. static void        Glob_MDrives (char *, char *);
  95. static char        *Check_Multi_Drive (char *);
  96.  
  97. /*
  98.  * Expand all words to their full potential
  99.  */
  100.  
  101. char        **eval(ap, f)
  102. register char    **ap;
  103. {
  104.     Word_B    *wb = (Word_B *)NULL;
  105.     char    **wp = (char **)NULL;
  106.     char    **wf = (char **)NULL;
  107.     jmp_buf    ev;
  108.  
  109.     if (newenv (setjmp (errpt = ev)) == FALSE)
  110.     {
  111.     while ((*ap != (char *)NULL) && isassign (*ap))
  112.         expand (*(ap++), &wb, f & ~DOGLOB);
  113.  
  114.     if (FL_TEST ('k'))
  115.     {
  116.         for (wf = ap; *wf != (char *)NULL; wf++)
  117.         {
  118.         if (isassign (*wf))
  119.             expand (*wf, &wb, f & ~DOGLOB);
  120.         }
  121.     }
  122.  
  123. /* Now expand the words */
  124.  
  125.     for (wb = addword ((char *)NULL, wb); *ap; ap++)
  126.     {
  127.         if (!FL_TEST ('k') || !isassign(*ap))
  128.         expand (*ap, &wb, f & ~DOKEY);
  129.     }
  130.  
  131. /* Get the word list */
  132.  
  133.     wp = getwords (wb = addword ((char *)NULL, wb));
  134.     quitenv ();
  135.     }
  136.  
  137.     else
  138.     gflg = 1;
  139.  
  140.     return gflg ? (char **)NULL : wp;
  141. }
  142.  
  143. /*
  144.  * Make the exported environment from the exported names in the dictionary.
  145.  * Keyword assignments will already have been done.  Convert to MSDOS
  146.  * format if flag set and m enabled
  147.  */
  148.  
  149. char    **makenv ()
  150. {
  151.     register Word_B    *wb = (Word_B *)NULL;
  152.     register Var_List    *vp;
  153.     char        *cp;
  154.     int            len = 0;
  155.  
  156.     for (vp = vlist; vp != (Var_List *)NULL; vp = vp->next)
  157.     {
  158.     if (vp->status & EXPORT)
  159.     {
  160.         if ((len += (strlen (vp->name) + 1)) >= 0x7f00)
  161.         return (char **)NULL;
  162.  
  163.         wb = addword (vp->name, wb);
  164.  
  165. /* If MSDOS mode, we need to copy the variable, convert / to \ and put
  166.  * the copy in the environment list instead
  167.  */
  168.  
  169.         if (FL_TEST ('m') && (vp->status & C_MSDOS))
  170.         {
  171.         cp = strsave (wb->w_words[wb->w_nword - 1], areanum);
  172.         wb->w_words[wb->w_nword - 1] = cp;
  173.         Convert_Slashes (cp);
  174.         }
  175.     }
  176.     }
  177.  
  178.     return getwords (wb = addword ((char *)NULL, wb));
  179. }
  180.  
  181. char        *evalstr(cp, f)
  182. register char    *cp;
  183. int        f;
  184. {
  185.     Word_B    *wb = (Word_B *)NULL;
  186.  
  187.     if (expand (cp, &wb, f))
  188.     {
  189.     if ((wb == (Word_B *)NULL) || (wb->w_nword == 0) ||
  190.         ((cp = wb->w_words[0]) == (char *)NULL))
  191.         cp = null;
  192.  
  193.     DELETE (wb);
  194.     }
  195.  
  196.     else
  197.     cp = (char *)NULL;
  198.  
  199.     return cp;
  200. }
  201.  
  202. /* Expand special characters and variables */
  203.  
  204. static bool        expand (cp, wbp, f)
  205. register char        *cp;            /* String to process    */
  206. register Word_B        **wbp;            /* Word block        */
  207. int            f;            /* Expand mode        */
  208. {
  209.     jmp_buf    ev;
  210.  
  211.     gflg = 0;
  212.  
  213.     if (cp == (char *)NULL)
  214.     return FALSE;
  215.  
  216. /* If there are no special characters and no separators, nothing to do,
  217.  * just save the word
  218.  */
  219.  
  220.     if (!anys (spcl2, cp) && !anys (ifs->value, cp) &&
  221.     ((f & DOGLOB) == 0 || !anys (spcl, cp)))
  222.     {
  223.     cp = strsave (cp, areanum);
  224.  
  225.     if (f & DOTRIM)
  226.         unquote (cp);
  227.  
  228.     *wbp = addword (cp, *wbp);
  229.     return TRUE;
  230.     }
  231.  
  232. /* Set up to read the word back in */
  233.  
  234.     if (newenv (setjmp (errpt = ev)) == FALSE)
  235.     {
  236.     PUSHIO (aword, cp, strchar);
  237.     e.iobase = e.iop;
  238.  
  239.     while ((cp = blank (f)) && gflg == 0)
  240.     {
  241.         e.linep = cp;
  242.         cp = strsave (cp, areanum);
  243.  
  244. /* Global expansion disabled ? */
  245.  
  246.         if (((f & DOGLOB) == 0) || FL_TEST ('f'))
  247.         {
  248.         if (f & DOTRIM)
  249.             unquote (cp);
  250.  
  251.         *wbp = addword (cp, *wbp);
  252.         }
  253.  
  254.         else
  255.         *wbp = Expand_globs (cp, *wbp);
  256.     }
  257.  
  258.     quitenv ();
  259.     }
  260.  
  261.     else
  262.     gflg = 1;
  263.  
  264.     return (gflg == 0) ? TRUE : FALSE;
  265. }
  266.  
  267. /*
  268.  * Blank interpretation and quoting
  269.  */
  270.  
  271. static char    *blank(f)
  272. {
  273.     register int    c, c1;
  274.     register char    *sp = e.linep;
  275.     int            scanequals = (f & DOKEY) ? TRUE : FALSE;
  276.     bool        foundequals = FALSE;
  277.  
  278. loop:
  279.     switch (c = subgetc ('"', foundequals))
  280.     {
  281.     case 0:
  282.         if (sp == e.linep)
  283.         return (char *)NULL;
  284.  
  285.         *e.linep++ = 0;
  286.         return sp;
  287.  
  288.     default:
  289.         if ((f & DOBLANK) && any ((char)c, ifs->value))
  290.         goto loop;
  291.  
  292.         break;
  293.  
  294.     case '"':
  295.     case '\'':
  296.         scanequals = FALSE;
  297.         if (INSUB())
  298.         break;
  299.  
  300.         for (c1 = c; (c = subgetc ((char)c1, TRUE)) != c1;)
  301.         {
  302.         if (c == 0)
  303.             break;
  304.  
  305.         if ((c == '\'') || !any ((char)c, "$`\""))
  306.             c |= QUOTE;
  307.  
  308.         *e.linep++ = (char)c;
  309.         }
  310.  
  311.         c = 0;
  312.     }
  313.  
  314.     unget(c);
  315.  
  316.     if (!isalpha (c))
  317.     scanequals = FALSE;
  318.  
  319.     while (1)
  320.     {
  321.     if (((c = subgetc ('"', foundequals)) == 0) ||
  322.         (f & DOBLANK) && any ((char)c, ifs->value) ||
  323.         !INSUB() && any ((char)c, spcl1))
  324.     {
  325.         scanequals = FALSE;
  326.         unget (c);
  327.  
  328.         if (any ((char)c, spcl1))
  329.         goto loop;
  330.  
  331.         break;
  332.     }
  333.  
  334.     if (scanequals)
  335.     {
  336.         if (c == '=')
  337.         {
  338.         foundequals = TRUE;
  339.         scanequals  = FALSE;
  340.         }
  341.  
  342.         else if (!isalnum (c))
  343.         scanequals = FALSE;
  344.     }
  345.  
  346.     *e.linep++ = (char)c;
  347.     }
  348.  
  349.     *e.linep++ = 0;
  350.     return sp;
  351. }
  352.  
  353. /*
  354.  * Get characters, substituting for ` and $
  355.  */
  356.  
  357. int        subgetc (ec, quoted)
  358. register char    ec;
  359. bool        quoted;
  360. {
  361.     register char    c;
  362.  
  363.     while (1)
  364.     {
  365.     c = (char)Getc (ec);
  366.  
  367.     if (!INSUB() && ec != '\'')
  368.     {
  369.  
  370. /* Found a ` - execute the command */
  371.  
  372.         if (c == '`')
  373.         {
  374.  
  375. /* If both ec (end character) is zero and quoted flag is FALSE, this is execute
  376.  * command request is in a here document, so we have to collect the rest of
  377.  * the command from input.  Otherwise, the command is in e.iop->argp->aword.
  378.  *
  379.  * We also need to set quoted so that NL are not processed when reading
  380.  * the output from the command.
  381.  */
  382.         if (!ec && !quoted)
  383.         {
  384.             e.linep = e.cline;
  385.             if (collect (c, c) != 0)
  386.             return 0;
  387.  
  388.             e.iop->argp->aword = e.cline + 1;
  389.             quoted = MAYBE;
  390.         }
  391.  
  392.         if (grave (quoted) == 0)
  393.             return 0;
  394.  
  395. /* Re-read the character from the Grave function */
  396.  
  397.         e.iop->task = XGRAVE;
  398.         }
  399.  
  400. /* $ - check for environment variable subsitution */
  401.  
  402.         else if (c == '$' && (c = dollar (quoted)) == 0)
  403.         e.iop->task = XDOLL;
  404.  
  405. /* No special processing required - return the character */
  406.  
  407.         else
  408.         return c;
  409.     }
  410.  
  411.     else
  412.         return c;
  413.     }
  414. }
  415.  
  416. /*
  417.  * Prepare to generate the string returned by ${} substitution.
  418.  */
  419.  
  420. static char    dollar (quoted)
  421. bool        quoted;
  422. {
  423.     IO_State        *oiop;
  424.     char        *dolp, otask;
  425.     register char    *s, c, *cp;
  426.     Var_List        *vp;
  427.     bool        colon_f = FALSE;
  428.     bool        hash_f = FALSE;
  429.     char        *dol_special = "$ ";
  430.  
  431.     c = (char)readc ();
  432.     s = e.linep;
  433.  
  434. /* Bracketed or not ? */
  435.  
  436.     if (c != '{')
  437.     {
  438.  
  439. /* Get the string, while it is a alpha character */
  440.  
  441.     *e.linep++ = c;
  442.  
  443.     if (isalpha (c))
  444.     {
  445.         while (((c = (char)readc ()) != 0) && isalnum (c))
  446.         {
  447.         if (e.linep < e.eline)
  448.             *e.linep++ = c;
  449.         }
  450.  
  451.         unget(c);
  452.     }
  453.  
  454.     c = 0;
  455.     }
  456.  
  457. /* Bracketed - special case */
  458.  
  459.     else
  460.     {
  461.     oiop = e.iop;
  462.     otask = e.iop->task;
  463.     e.iop->task = XOTHER;
  464.  
  465.     while (((c = (char)subgetc ('"', FALSE)) != 0) &&
  466.            (c != '}') && (c != NL))
  467.     {
  468.         if (e.linep < e.eline)
  469.         *e.linep++ = c;
  470.     }
  471.  
  472.     if (oiop == e.iop)
  473.         e.iop->task = otask;
  474.  
  475. /* Check terminate correctly */
  476.  
  477.     if (c != '}')
  478.     {
  479.         print_error ("sh: unclosed ${\n");
  480.         gflg++;
  481.         return c;
  482.     }
  483.  
  484. /* Check for Hash at start */
  485.  
  486.     if ((*s == '#') && ((s - e.linep) > 1))
  487.     {
  488.         hash_f = TRUE;
  489.         memcpy (s, s + 1, e.linep - s - 1);
  490.         --e.linep;
  491.     }
  492.  
  493. /* Check for zero length string */
  494.  
  495.     if (s == e.linep)
  496.     {
  497.         print_error (bad_subs);
  498.         gflg++;
  499.         return c;
  500.     }
  501.     }
  502.  
  503. /* Check line length */
  504.  
  505.     if (e.linep >= e.eline)
  506.     {
  507.     print_error ("sh: string in ${} too long\n");
  508.     gflg++;
  509.     e.linep -= 10;
  510.     }
  511.  
  512.     *e.linep = 0;
  513.  
  514. /* Scan for =-+?%# in string */
  515.  
  516.     if (*s)
  517.     {
  518.     for (cp = s + 1; *cp; cp++)
  519.     {
  520.  
  521. /* Check for end character other than null (=-+?) */
  522.  
  523.         if (any (*cp, "=-+?%#"))
  524.         {
  525.         c = *cp;
  526.  
  527. /* Skip next section in case of % or #.  Check for case of :[=-+?]. 
  528.  * If found - set flag
  529.  */
  530.         if ((c != '%') && (c != '#') && (*(cp - 1) == ':'))
  531.         {
  532.             colon_f = TRUE;
  533.             *(cp - 1) = 0;
  534.         }
  535.  
  536.         *(cp++) = 0;
  537.         break;
  538.         }
  539.     }
  540.     }
  541.  
  542. /* Cannot have both # & : */
  543.  
  544.     if (hash_f && colon_f)
  545.     {
  546.     print_error (bad_subs);
  547.     gflg++;
  548.     return c;
  549.     }
  550.  
  551. /* Check for * and @ processing */
  552.  
  553.     if (s[1] == 0 && (*s == '*' || *s == '@'))
  554.     {
  555.     if (dolc > 1)
  556.     {
  557.         e.linep = s;
  558.  
  559. /* If hash flag set, convert to string length */
  560.  
  561.         if (hash_f)
  562.         {
  563.         dolp = strsave (putn (dolc - 1), areanum);
  564.         PUSHIO (aword, dolp, quoted ? qstrchar : strchar);
  565.         }
  566.  
  567.         else
  568.         {
  569.         PUSHIO (awordlist, dolv + 1, dol_char);
  570.         e.iop->dflag = (char)(!quoted ? DSA_NULL
  571.                           : ((*s == '*') ? DSA_STAR
  572.                                  : DSA_AMP));
  573.         }
  574.  
  575.         return 0;
  576.     }
  577.  
  578. /* trap the nasty ${=} */
  579.  
  580.     else
  581.     {
  582.         s[0] = '1';
  583.         s[1] = 0;
  584.     }
  585.     }
  586.  
  587. /* Find the current value
  588.  *
  589.  * $~xxx variables are used by the Shell internally and cannot be accessed
  590.  * by the user.
  591.  */
  592.  
  593.     if (*s == '~')
  594.     dolp = null;
  595.  
  596.     else if (!*s || !(isalnum (*s) || any (*s, "#-?$!")))
  597.     {
  598.     dol_special[1] = *s;
  599.     dolp = dol_special;
  600.     }
  601.  
  602.     else if ((dolp = (vp = lookup (s, FALSE))->value) == null)
  603.     {
  604.     switch (c)
  605.     {
  606.         case '=':
  607.         if (isdigit (*s))
  608.         {
  609.             print_error ("sh: cannot use ${...=...} with $n\n");
  610.             gflg++;
  611.             break;
  612.         }
  613.  
  614.         setval ((vp = lookup (s, TRUE)), cp);
  615.         dolp = vp->value;
  616.         break;
  617.  
  618.         case '-':
  619.         dolp = strsave (cp, areanum);
  620.         break;
  621.  
  622.         case '?':
  623.         if (*cp == 0)
  624.             cp = "parameter null or not set";
  625.  
  626.         print_error ("%s: %s\n", s, cp);
  627.  
  628.         gflg++;
  629.         break;
  630.     }
  631.     }
  632.  
  633. /* String exists - other processing */
  634.  
  635.     else
  636.     {
  637.     char        *pos;        /* Position for substitute    */
  638.     char        *tsp;
  639.     int        mode;        /* Mode for substitute        */
  640.  
  641.     switch (c)
  642.     {
  643.         case '+':
  644.         dolp = strsave (cp, areanum);
  645.         break;
  646.  
  647.         case '#':            /* Remove prefix */
  648.         case '%':            /* Remove suffix */
  649.         mode = GM_SHORTEST;
  650.  
  651.         if (*cp == c)
  652.         {
  653.             mode = GM_LONGEST;
  654.             ++cp;
  655.         }
  656.  
  657.         if (c == '#')
  658.         {
  659.             if (gmatch (dolp, cp, FALSE, &pos, mode))
  660.             dolp = strsave (pos, areanum);
  661.         }
  662.  
  663.         else if (gmatch_suffix (dolp, cp, FALSE, &pos, mode))
  664.         {
  665.             tsp = strsave (dolp, areanum);
  666.             tsp[pos - dolp] = 0;
  667.             dolp = tsp;
  668.         }
  669.  
  670.         break;
  671.     }
  672.     }
  673.  
  674. /* Check for unset values */
  675.  
  676.     if (FL_TEST ('u') && dolp == null)
  677.     {
  678.     print_error ("sh: unset variable %s\n", s);
  679.     gflg++;
  680.     }
  681.  
  682. /* If hash flag set, convert to string length */
  683.  
  684.     if (hash_f)
  685.     dolp = strsave (putn (strlen (dolp)), areanum);
  686.  
  687.     e.linep = s;
  688.     PUSHIO (aword, dolp, quoted ? qstrchar : strchar);
  689.     return 0;
  690. }
  691.  
  692. /*
  693.  * Run the command in `...` and read its output.
  694.  */
  695.  
  696. static bool    grave (quoted)
  697. bool        quoted;
  698. {
  699.     char        *cp, *sp;
  700.     int            localpipe, rv;
  701.     jmp_buf        ev, rt;
  702.     C_Op        *outtree;
  703.     Break_C        bc;
  704.     int            (*iof)(IO_State *);
  705.  
  706. /* Save area */
  707.  
  708.     long        s_flags = flags;
  709.     Word_B        *s_wdlist = wdlist;
  710.     Word_B        *s_iolist = iolist;
  711.     Break_C        *S_RList = Return_List;    /* Save loval links    */
  712.     Break_C        *S_BList = Break_List;
  713.     Break_C        *S_SList = SShell_List;
  714.     int            *s_fail = failpt;
  715.     int            s_execflg = execflg;
  716.     int            Local_depth;
  717.  
  718. /* Check there is an ending grave */
  719.  
  720.     if ((cp = strchr (e.iop->argp->aword, '`')) == (char *)NULL)
  721.     {
  722.     print_error ("sh: no closing `\n");
  723.     return FALSE;
  724.     }
  725.  
  726. /* Create the pipe to read the output from the command string */
  727.  
  728.     if ((localpipe = openpipe ()) < 0)
  729.     return FALSE;
  730.  
  731. /* Terminate string and initialise save area */
  732.  
  733.     *cp = 0;
  734.  
  735. /* Create a new environment */
  736.  
  737.     S_dup2 (localpipe, 1);
  738.  
  739.     FL_CLEAR ('e');
  740.     FL_CLEAR ('v');
  741.     FL_CLEAR ('n');
  742.  
  743.     sp = strsave (e.iop->argp->aword, areanum++);
  744.     unquote (sp);
  745.  
  746. /* Set up new environment */
  747.  
  748.     Local_depth = Execute_stack_depth++;
  749.     rv = Create_NG_VL ();
  750.  
  751.     if ((rv != -1) && (newenv (setjmp (errpt = ev)) == FALSE))
  752.     {
  753.     Return_List = (Break_C *)NULL;
  754.     Break_List  = (Break_C *)NULL;
  755.     wdlist        = (Word_B *)NULL;
  756.     wdlist        = (Word_B *)NULL;
  757.     iolist        = (Word_B *)NULL;
  758.  
  759.     PUSHIO (aword, sp, nlchar);
  760.     e.cline = space (LINE_MAX);
  761.     e.eline = e.cline + LINE_MAX - 5;
  762.     e.linep = e.cline;
  763.     e.iobase = e.iop;
  764.  
  765. /* Clear interrupt, error, multiline, parse and execute flags.  */
  766.  
  767.     SW_intr = 0;
  768.     yynerrs = 0;
  769.     multiline = 0;
  770.     inparse = 0;
  771.     execflg = 1;
  772.  
  773. /* Parse the line and execute it */
  774.  
  775.     if ((setjmp (failpt = rt) == 0) &&
  776.         ((outtree = yyparse ()) != (C_Op *)NULL))
  777.     {
  778.         if (setjmp (bc.brkpt) == 0)
  779.         {
  780.         bc.nextlev = SShell_List;
  781.         SShell_List = &bc;
  782.         execute (outtree, NOPIPE, NOPIPE, 0);
  783.         }
  784.     }
  785.  
  786. /* Clean up any files around we nolonger need */
  787.  
  788.     Clear_Extended_File ();
  789.     quitenv ();
  790.     }
  791.  
  792. /* Fail - close pipe and delete it */
  793.  
  794.     else
  795.     {
  796.     S_Delete (localpipe);
  797.     S_close (localpipe, TRUE);
  798.     }
  799.  
  800. /* Restore environment */
  801.  
  802.     Restore_Environment (0, Local_depth);
  803.  
  804. /* Free old space */
  805.  
  806.     freehere (areanum);
  807.     freearea (areanum--);    /* free old space */
  808.  
  809. /* Ok - completed processing - restore environment and read the pipe */
  810.  
  811.     execflg    = s_execflg;
  812.     flags    = s_flags;
  813.     wdlist    = s_wdlist;
  814.     iolist    = s_iolist;
  815.     failpt    = s_fail;
  816.     Return_List = S_RList;
  817.     Break_List    = S_BList;
  818.     SShell_List = S_SList;
  819.  
  820. /* Move pipe to start so we can read it */
  821.  
  822.     *(cp++) = '`';
  823.     lseek (localpipe, 0L, SEEK_SET);
  824.     e.iop->argp->aword = cp;
  825.     iof = (!quoted) ? gravechar
  826.             : ((quoted == MAYBE) ? sgravechar : qgravechar);
  827.     PUSHIO (afile, remap (localpipe), iof);
  828.     return TRUE;
  829. }
  830.  
  831. /*
  832.  * Remove Quotes from a string
  833.  */
  834.  
  835. static char    *unquote (as)
  836. register char    *as;
  837. {
  838.     register char    *s;
  839.  
  840.     if ((s = as) != (char *)NULL)
  841.     {
  842.     while (*s)
  843.         *(s++) &= ~QUOTE;
  844.     }
  845.  
  846.     return as;
  847. }
  848.  
  849. /*
  850.  * Expand *, [] and ?
  851.  */
  852.  
  853. static Word_B    *Expand_globs (cp, wb)
  854. char        *cp;
  855. Word_B        *wb;
  856. {
  857.     register int    i = 0;
  858.     register char    *pp;
  859.  
  860. /* Ignore null strings */
  861.  
  862.     if (cp == (char *)NULL)
  863.     return wb;
  864.  
  865. /* Any special characters */
  866.  
  867.     for (pp = cp; *pp; pp++)
  868.     {
  869.     if (any (*pp, spcl))
  870.         i++;
  871.  
  872.     else if (!any (*pp & ~QUOTE, spcl))
  873.         *pp &= ~QUOTE;
  874.     }
  875.  
  876. /* No - just add the word to the selected block */
  877.  
  878.     if (i == 0)
  879.     return addword (unquote (cp), wb);
  880.  
  881. /* OK - we have to expand the word whilst any words in cl have special
  882.  * characters in them
  883.  */
  884.  
  885.     for (C_EList = addword (strsave (cp, areanum), (Word_B *)NULL);
  886.      anyspcl (C_EList); C_EList = New_Elist)
  887.     {
  888.  
  889. /* Get a new block for this pass of the expansion */
  890.  
  891.     New_Elist = newword (C_EList->w_nword * 2);
  892.  
  893. /* For each word, expand it */
  894.  
  895.     for (i = 0; i < C_EList->w_nword; i++)
  896.     {
  897.         if ((pp = anys_p (C_EList->w_words[i], spcl)) != (char *)NULL)
  898.         Glob_MDrives (C_EList->w_words[i], pp);
  899.  
  900.         else
  901.         New_Elist = addword (strsave (C_EList->w_words[i], areanum),
  902.                      New_Elist);
  903.     }
  904.  
  905. /* The current list is now the previous list, so delete it */
  906.  
  907.     for (i = 0; i < C_EList->w_nword; i++)
  908.         DELETE (C_EList->w_words[i]);
  909.  
  910.     DELETE (C_EList);
  911.     }
  912.  
  913.     for (i = 0; i < C_EList->w_nword; i++)
  914.     unquote (C_EList->w_words[i]);
  915.  
  916. /* Did we find any files matching the specification.  Yes - add them to
  917.  * the block
  918.  */
  919.  
  920.     if (C_EList->w_nword)
  921.     {
  922.     qsort (C_EList->w_words, C_EList->w_nword, sizeof (char *),
  923.            sort_compare);
  924.  
  925.     for (i = 0; i < C_EList->w_nword; i++)
  926.         wb = addword (C_EList->w_words[i], wb);
  927.  
  928.     DELETE (C_EList);
  929.     return wb;
  930.     }
  931.  
  932. /* No - add the original word */
  933.  
  934.     else
  935.     return addword (unquote (cp), wb);
  936. }
  937.  
  938. /*
  939.  * Read a directory for matches against the specified name
  940.  */
  941.  
  942. static void    globname (we, pp)
  943. char        *we;            /* Start            */
  944. register char    *pp;            /* First special character    */
  945. {
  946.     register char    *np, *cp;
  947.     char        *name, *gp, *dp;
  948.     struct dirent    *d_ce;
  949.     char        dname[NAME_MAX + 1];
  950.     struct stat        dbuf;
  951.  
  952. /* Find the previous directory separator */
  953.  
  954.     for (np = we; np != pp; pp--)
  955.     {
  956.     if (pp[-1] == '/')
  957.         break;
  958.     }
  959.  
  960. /* If we don't find it, check for a drive */
  961.  
  962.     if ((np == pp) && (strlen (we) > 2) && (we[1] == ':'))
  963.     pp += 2;
  964.  
  965. /* Save copy of directory name */
  966.  
  967.     for (dp = cp = space ((int)(pp - np) + 3); np < pp;)
  968.     *cp++ = *np++;
  969.  
  970.     *cp++ = '.';
  971.     *cp = '\0';
  972.  
  973. /* Save copy of pattern for this directory.  NP is left pointing to the
  974.  * rest of the string for any subdirectories
  975.  */
  976.  
  977.     for (gp = cp = space (strlen (pp) + 1); *np && *np != '/';)
  978.     *cp++ = *np++;
  979.  
  980.     *cp = '\0';
  981.  
  982. /* Open the directory */
  983.  
  984.     if ((e.cdir = opendir (dp)) == (DIR *)NULL)
  985.     {
  986.     DELETE (dp);
  987.     DELETE (gp);
  988.     return;
  989.     }
  990.  
  991. /* Scan for matches */
  992.  
  993.     while ((d_ce = readdir (e.cdir)) != (struct dirent *)NULL)
  994.     {
  995.     if ((*(strncpy (dname, d_ce->d_name, NAME_MAX)) == '.') && (*gp != '.'))
  996.         continue;
  997.  
  998.     dname[NAME_MAX] = 0;
  999.     for (cp = dname; *cp; cp++)
  1000.     {
  1001.         if (any (*cp, spcl))
  1002.         *cp |= QUOTE;
  1003.     }
  1004.  
  1005. /* Check for a match */
  1006.  
  1007.     if (gmatch (dname, gp, TRUE, (char **)NULL, GM_ALL))
  1008.     {
  1009.  
  1010. /* If there are no special characters in the new full name, the file must
  1011.  * exist
  1012.  */
  1013.  
  1014.         name = generate (we, pp, dname, np);
  1015.  
  1016.         if (*np && !anys (np, spcl))
  1017.         {
  1018.         if (stat (name, &dbuf))
  1019.         {
  1020.             DELETE (name);
  1021.             continue;
  1022.         }
  1023.         }
  1024.  
  1025. /* Ok save the name */
  1026.  
  1027.         New_Elist = addword (name, New_Elist);
  1028.     }
  1029.     }
  1030.  
  1031.     closedir (e.cdir);
  1032.     e.cdir = (DIR *)NULL;
  1033.     DELETE (dp);
  1034.     DELETE (gp);
  1035. }
  1036.  
  1037. /*
  1038.  * generate a pathname as below.  start..end1 / middle end.  The slashes come
  1039.  * for free
  1040.  */
  1041.  
  1042. static char    *generate (start1, end1, middle, end)
  1043. char        *start1;
  1044. register char    *end1;
  1045. char        *middle, *end;
  1046. {
  1047.     register char    *op;
  1048.     int            clen = (int)(end1 - start1);
  1049.  
  1050.     op = space (clen + strlen (middle) + strlen (end) + 2);
  1051.  
  1052.     strncpy (op, start1, clen);
  1053.     strcat (strcpy (&op[clen], middle), end);
  1054.     return op;
  1055. }
  1056.  
  1057. /*
  1058.  * Scan a Word Block for special characters
  1059.  */
  1060.  
  1061. static bool    anyspcl (wb)
  1062. register Word_B    *wb;
  1063. {
  1064.     register int    i;
  1065.     register char    **wd = wb->w_words;
  1066.  
  1067.     for (i = 0; i < wb->w_nword; i++)
  1068.     {
  1069.     if (anys (spcl, *wd++))
  1070.         return TRUE;
  1071.     }
  1072.  
  1073.     return FALSE;
  1074. }
  1075.  
  1076. /*
  1077.  * Create a new Word Block
  1078.  */
  1079.  
  1080. static Word_B    *newword (nw)
  1081. register int    nw;
  1082. {
  1083.     register Word_B    *wb;
  1084.  
  1085.     wb = (Word_B *) space (sizeof (Word_B) + nw * sizeof (char *));
  1086.     wb->w_bsize = nw;
  1087.     wb->w_nword = 0;
  1088.  
  1089.     return wb;
  1090. }
  1091.  
  1092. /*
  1093.  * Add a new word to a Word Block or list
  1094.  */
  1095.  
  1096. Word_B        *addword (wd, wb)
  1097. char        *wd;
  1098. register Word_B    *wb;
  1099. {
  1100.     register Word_B    *wb2;
  1101.     register int    nw;
  1102.  
  1103.     if (wb == (Word_B *)NULL)
  1104.     wb = newword (NSTART);
  1105.  
  1106. /* Do we require more space ? */
  1107.  
  1108.     if ((nw = wb->w_nword) >= wb->w_bsize)
  1109.     {
  1110.     wb2 = newword (nw * 2);
  1111.     memcpy ((char *)wb2->w_words, (char *)wb->w_words, nw*sizeof(char *));
  1112.     wb2->w_nword = nw;
  1113.     DELETE (wb);
  1114.     wb = wb2;
  1115.     }
  1116.  
  1117. /* Add to the list */
  1118.  
  1119.     wb->w_words[wb->w_nword++] = wd;
  1120.     return wb;
  1121. }
  1122.  
  1123. /*
  1124.  * Convert a word block structure into a array of strings
  1125.  */
  1126.  
  1127. char        **getwords(wb)
  1128. register Word_B    *wb;
  1129. {
  1130.     register char    **wd;
  1131.     register int    nb;
  1132.  
  1133. /* If the word block is empty or does not exist, return no list */
  1134.  
  1135.     if (wb == (Word_B **)NULL)
  1136.     return (char *)NULL;
  1137.  
  1138. /* Get some space for the array and set it up */
  1139.  
  1140.     if (((nb = sizeof (char *) * wb->w_nword) == 0) ||
  1141.     ((wd = (char **)space (nb)) == (char **)NULL))
  1142.     {
  1143.     DELETE (wb);
  1144.     return (char *)NULL;
  1145.     }
  1146.  
  1147.     memcpy ((char *)wd, (char *)wb->w_words, nb);
  1148.     DELETE (wb);    /* perhaps should done by caller */
  1149.     return wd;
  1150. }
  1151.  
  1152. /*
  1153.  * Is any character from s1 in s2?  Return a boolean.
  1154.  */
  1155.  
  1156. bool        anys (s1, s2)
  1157. register char    *s1, *s2;
  1158. {
  1159.     while (*s1)
  1160.     {
  1161.     if (any (*(s1++), s2))
  1162.         return TRUE;
  1163.     }
  1164.  
  1165.     return FALSE;
  1166. }
  1167.  
  1168. /*
  1169.  * Is any character from s1 in s2? Yes - return a pointer to that
  1170.  * character.
  1171.  */
  1172.  
  1173. static char    *anys_p (s1, s2)
  1174. register char    *s1, *s2;
  1175. {
  1176.     while (*s1)
  1177.     {
  1178.     if (any (*(s1++), s2))
  1179.         return --s1;
  1180.     }
  1181.  
  1182.     return (char *)NULL;
  1183. }
  1184.  
  1185. /*
  1186.  * Expansion - check for multiple drive request
  1187.  *
  1188.  * If there is a multi-drive expansion (*:, ?: or []:), we have to check
  1189.  * out each existing drive and then expand.  So we check for a multi-drive
  1190.  * condition and then for each existing drive, we check that pattern
  1191.  * against the drive and then expand the rest of the pattern.
  1192.  *
  1193.  * Otherwise, we just expand the pattern.
  1194.  */
  1195.  
  1196. static void    Glob_MDrives (pattern, start)
  1197. char        *pattern;
  1198. char        *start;
  1199. {
  1200.     unsigned int    c_drive;    /* Current drive        */
  1201.     unsigned int    m_drive;    /* Max drive            */
  1202.     unsigned int    s_drive;    /* Selected drive        */
  1203.     unsigned int    x_drive, y_drive;    /* Dummies        */
  1204.     char        *multi;        /* Multi-drive flag        */
  1205.     static char        *t_drive = "x";
  1206.     char        *new_pattern;
  1207.  
  1208. /* Search all drives ? */
  1209.  
  1210.     if ((multi = Check_Multi_Drive (pattern)) != (char *)NULL)
  1211.     {
  1212.     _dos_getdrive (&c_drive);    /* Get number of drives        */
  1213.     _dos_setdrive (c_drive, &m_drive);
  1214.     new_pattern = space (strlen (multi) + 2);
  1215.  
  1216.     strcpy (new_pattern + 1, multi);
  1217.     *multi = 0;
  1218.  
  1219.     for (s_drive = 1; s_drive <= m_drive; ++s_drive)
  1220.     {
  1221.         _dos_setdrive (s_drive, &x_drive);
  1222.         _dos_getdrive (&y_drive);
  1223.         _dos_setdrive (c_drive, &x_drive);
  1224.  
  1225. /* Check to see if the second diskette drive is really there */
  1226.  
  1227.         if (((_bios_equiplist () & 0x00c0) == 0x0000) && (s_drive == 2))
  1228.         continue;
  1229.  
  1230. /* If the drive exists and is in our list - process it */
  1231.  
  1232.         *t_drive = (char)(s_drive + 'a' - 1);
  1233.  
  1234.         if ((y_drive == s_drive) &&
  1235.          gmatch (t_drive, pattern, TRUE, (char **)NULL, GM_ALL))
  1236.         {
  1237.         *new_pattern = *t_drive;
  1238.         globname (new_pattern, &new_pattern[2]);
  1239.         }
  1240.     }
  1241.  
  1242. /* Restore and delete space */
  1243.  
  1244.     *multi = ':';
  1245.     DELETE (new_pattern);
  1246.     }
  1247.  
  1248. /* No drive specifier - just check it out */
  1249.  
  1250.     else
  1251.     globname (pattern, start);
  1252. }
  1253.  
  1254. /*
  1255.  * Check for multi_drive prefix - *:, ?: or []:
  1256.  *
  1257.  * Return NULL or the address of the colon character
  1258.  */
  1259.  
  1260. static char    *Check_Multi_Drive (pattern)
  1261. char        *pattern;
  1262. {
  1263.     if (strlen (pattern) < 3)
  1264.     return (char *)NULL;
  1265.  
  1266.     if (((*pattern == '*') || (*pattern == '?')) && (pattern[1] == ':'))
  1267.     return pattern + 1;
  1268.  
  1269.     if (*pattern != '[')
  1270.     return (char *)NULL;
  1271.  
  1272.     while (*pattern && (*pattern != ']'))
  1273.     {
  1274.     if ((*pattern == '\\') && (*(pattern + 1)))
  1275.         ++pattern;
  1276.  
  1277.     ++pattern;
  1278.     }
  1279.  
  1280.     return (*pattern && (*(pattern + 1) == ':')) ? pattern + 1 : (char *)NULL;
  1281. }
  1282.