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