home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / vile-src.zip / vile-8.1 / glob.c < prev    next >
C/C++ Source or Header  |  1998-04-28  |  22KB  |  1,002 lines

  1. /*
  2.  *    glob.c
  3.  *
  4.  * Performs wildcard-expansion for UNIX, VMS and MS-DOS systems.
  5.  * Written by T.E.Dickey for vile (april 1993).
  6.  *
  7.  * (MS-DOS code was originally taken from the winc app example of the
  8.  * zortech compiler - pjr)
  9.  *
  10.  * To do:
  11.  *    make the wildcard expansion know about escaped characters (e.g.,
  12.  *    with backslash a la UNIX.
  13.  *
  14.  *    modify (ifdef-style) 'expand_leaf()' to allow ellipsis.
  15.  *
  16.  * $Header: /usr/build/vile/vile/RCS/glob.c,v 1.58 1998/04/28 10:16:32 tom Exp $
  17.  *
  18.  */
  19.  
  20. #ifdef _WIN32
  21. # include <windows.h>
  22. #endif
  23.  
  24. #include "estruct.h"    /* global structures and defines */
  25. #include "edef.h"    /* defines 'slash' */
  26. #if SYS_OS2
  27. # define INCL_DOSFILEMGR
  28. # define INCL_ERRORS
  29. # include <os2.h>
  30. #else
  31. # include "dirstuff.h"    /* directory-scanning interface & definitions */
  32. #endif
  33.  
  34. #define BAKTIK '`'    /* used in UNIX shell for pipe */
  35. #define    isname(c)    (isAlnum(c) || ((c) == '_'))
  36. #define    isdelim(c)    ((c) == '(' || ((c) == '{'))
  37.  
  38. #if SYS_MSDOS || SYS_WIN31 || SYS_OS2 || SYS_WINNT
  39. # define UNIX_GLOBBING OPT_GLOB_ENVIRON
  40. # if UNIX_GLOBBING
  41. #  define DirEntryStr(p)        p->d_name
  42. # else
  43. #  ifdef __ZTC__
  44. #   define DeclareFind(p)        struct FIND *p
  45. #   define DirEntryStr(p)        p->name
  46. #   define DirFindFirst(path,p)        (p = findfirst(path, 0))
  47. #   define DirFindNext(p)        (p = findnext())
  48. #  else
  49. #   define DeclareFind(p)        struct find_t p
  50. #   define DirEntryStr(p)        p.name
  51. #   define DirFindFirst(path,p)        (!_dos_findfirst(path, 0, &p))
  52. #   define DirFindNext(p)        (!_dos_findnext(&p))
  53. #  endif
  54. # endif
  55. # define DirEntryLen(p)            strlen(DirEntryStr(p))
  56. #endif    /* SYS_MSDOS */
  57.  
  58. /*
  59.  * Make the default unix globbing code use 'echo' rather than our internal
  60.  * globber if we do not configure the 'glob' string-mode.
  61.  */
  62. #if SYS_UNIX && defined(GVAL_GLOB) && !OPT_VMS_PATH
  63. # define UNIX_GLOBBING 1
  64. #endif
  65.  
  66. #ifndef UNIX_GLOBBING
  67. # define UNIX_GLOBBING 0
  68. #endif
  69.  
  70. /*
  71.  * Verify that we don't have both boolean- and string-valued 'glob' modes.
  72.  */
  73. #if defined(GMDGLOB) && defined(GVAL_GLOB)
  74.     huh??
  75. #else
  76. # ifdef GMDGLOB        /* boolean */
  77. #  define globbing_active() global_g_val(GMDGLOB)
  78. # endif
  79. # ifdef GVAL_GLOB    /* string */
  80. #  define globbing_active() !is_falsem(global_g_val_ptr(GVAL_GLOB))
  81. # endif
  82. # ifndef globbing_active
  83. #  define globbing_active() TRUE
  84. # endif
  85. #endif
  86.  
  87. /*
  88.  * Some things simply don't work on VMS: pipes and $variables
  89.  */
  90. #if OPT_VMS_PATH
  91. #
  92. # undef  UNIX_GLOBBING
  93. # define UNIX_GLOBBING 0
  94. #
  95. # undef  OPT_GLOB_ENVIRON
  96. # define OPT_GLOB_ENVIRON 0
  97. #
  98. # undef  OPT_GLOB_PIPE
  99. # define OPT_GLOB_PIPE 0
  100. #
  101. #endif
  102.  
  103. /*--------------------------------------------------------------------------*/
  104.  
  105. /* the expanded list is defined outside of the functions because if we
  106.  * handle ellipsis, the generating function must be recursive.
  107.  */
  108. static    SIZE_T    myMax, myLen;    /* length and index of the expanded list */
  109. static    char **    myVec;        /* the expanded list */
  110.  
  111. /*--------------------------------------------------------------------------*/
  112. static int
  113. string_has_wildcards (const char *item)
  114. {
  115. #if OPT_VMS_PATH || SYS_UNIX || OPT_MSDOS_PATH
  116.     while (*item != EOS) {
  117. #if UNIX_GLOBBING
  118.         if (iswild(*item))
  119.             return TRUE;
  120. #endif
  121.         if (*item == GLOB_SINGLE || *item == GLOB_MULTI)
  122.             return TRUE;
  123. #if OPT_GLOB_ELLIPSIS || SYS_VMS
  124.         if (!strncmp(item, GLOB_ELLIPSIS, sizeof(GLOB_ELLIPSIS)-1))
  125.             return TRUE;
  126. #endif
  127. #if !OPT_VMS_PATH
  128. #if OPT_GLOB_RANGE
  129.         if (*item == GLOB_RANGE[0])
  130.             return TRUE;
  131. #endif
  132. #if OPT_GLOB_ENVIRON
  133.         if (*item == '$' && (isname(item[1]) || isdelim(item[1])))
  134.             return TRUE;
  135. #endif
  136. #endif
  137.         item++;
  138.     }
  139. #endif
  140.     return FALSE;
  141. }
  142.  
  143. /*
  144.  * Record a pattern-match in 'myVec[]', returning false if an error occurs
  145.  */
  146. static int
  147. record_a_match(char *item)
  148. {
  149.     if (item != 0 && *item != EOS) {
  150.         if ((item = strmalloc(item)) == 0)
  151.             return no_memory("glob-match");
  152.  
  153.         if (myLen + 2 >= myMax) {
  154.             myMax = myLen + 2;
  155.             if (myVec == 0)
  156.                 myVec = typeallocn(char *, myMax);
  157.             else
  158.                 myVec = typereallocn(char *, myVec, myMax);
  159.         }
  160.         if (myVec == 0)
  161.             return no_memory("glob-pointers");
  162.  
  163.         myVec[myLen++] = item;
  164.         myVec[myLen] = 0;
  165.     }
  166.     return TRUE;
  167. }
  168.  
  169. #if UNIX_GLOBBING
  170. /*
  171.  * Point to the leaf following the given string (i.e., skip a slash), returns
  172.  * null if none is found.
  173.  */
  174. static char *
  175. next_leaf (char *path)
  176. {
  177.     if (path != 0) {
  178.         while (*path != EOS) {
  179.             if (is_slashc(*path))
  180.                 return path+1;
  181.             path++;
  182.         }
  183.     }
  184.     return 0;
  185. }
  186.  
  187. /*
  188.  * Point to the beginning (after slash, if present) of the first leaf in
  189.  * the given pattern argument that contains a wildcard.
  190.  */
  191. static char *
  192. wild_leaf (char *pattern)
  193. {
  194.     register int    j, k, ok;
  195.     register char    c;
  196.  
  197.     /* skip leading slashes */
  198.     for (j = 0; pattern[j] != EOS && is_slashc(pattern[j]); j++)
  199.         ;
  200.  
  201.     /* skip to the leaf with wildcards */
  202.     while (pattern[j] != EOS) {
  203.         int    skip = FALSE;
  204.         for (k = j+1; (c = pattern[k]) != EOS; k++) {
  205.             if (is_slashc(c)) {
  206.                 pattern[k] = EOS;
  207.                 ok = string_has_wildcards(pattern+j);
  208.                 pattern[k] = c;
  209.                 if (ok)
  210.                     return pattern+j;
  211.                 skip = TRUE;    /* skip this leaf */
  212.                 break;
  213.             }
  214.         }
  215.         if (skip)
  216.             j = k+1;    /* point past slash */
  217.         else if (c == EOS)
  218.             break;
  219.         else
  220.             j++;        /* leaf is empty */
  221.     }
  222.     return string_has_wildcards(pattern+j) ? pattern+j : 0;
  223. }
  224.  
  225. #if OPT_CASELESS
  226. static int
  227. cs_char(int ch)
  228. {
  229.     return isUpper(ch) ? toLower(ch) : ch;
  230. }
  231. #else
  232. #define cs_char(ch) (ch)
  233. #endif
  234.  
  235. /*
  236.  * This is the heart of the wildcard matching.  We are given a directory
  237.  * leaf and a pointer to the leaf that contains wildcards that we must
  238.  * match against the leaf.
  239.  */
  240. static int
  241. match_leaf(char *leaf, char *pattern)
  242. {
  243.     while (*leaf != EOS && *pattern != EOS) {
  244.         if (*pattern == GLOB_SINGLE) {
  245.             leaf++;
  246.             pattern++;
  247.         } else if (*pattern == GLOB_MULTI) {
  248.             int    multi = FALSE;
  249.             pattern++;
  250.             while (*leaf != EOS) {
  251.                 if (match_leaf(leaf, pattern)) {
  252.                     multi = TRUE;
  253.                     break;
  254.                 }
  255.                 leaf++;
  256.             }
  257.             if (!multi && *leaf != EOS)
  258.                 return FALSE;
  259. #if OPT_GLOB_RANGE
  260.         } else if (*pattern == GLOB_RANGE[0]) {
  261.             int    found    = FALSE;
  262.             char *    first    = ++pattern;
  263.             int    negate    = 0;
  264.  
  265.             if (*first == GLOB_NEGATE[0] ||
  266.                 (GLOB_NEGATE[1] && *first == GLOB_NEGATE[1])) {
  267.                 negate = 1;
  268.                 first = pattern++;
  269.             }
  270.             while (*pattern != EOS) {
  271.                 if (*pattern == GLOB_RANGE[1]) {
  272.                     pattern++;
  273.                     break;
  274.                 }
  275.                 if (*pattern == '-' && pattern != first) {
  276.                     int    lo = pattern[-1];
  277.                     int    hi = pattern[1];
  278.                     if (hi == GLOB_RANGE[1])
  279.                         hi = '~';
  280.                     if (((cs_char(lo) <= cs_char(*leaf)) && (cs_char(*leaf) <= cs_char(hi))) != negate)
  281.                         found = TRUE;
  282.                     if (pattern[1] != GLOB_RANGE[1])
  283.                         pattern++;
  284.                 } else if ((cs_char(*pattern++) == cs_char(*leaf)) != negate)
  285.                     found = TRUE;
  286.             }
  287.             if (!found)
  288.                 return FALSE;
  289.             leaf++;
  290. #endif
  291.         } else if (cs_char(*pattern++) != cs_char(*leaf++))
  292.             return FALSE;
  293.     }
  294.     return (*leaf == EOS && *pattern == EOS);
  295. }
  296.  
  297. #if !SYS_OS2
  298. /*
  299.  * Recursive procedure that allows any leaf (or all!) leaves in a path to
  300.  * have wildcards.  Except for an ellipsis, each wildcard is completed
  301.  * within a single leaf.
  302.  *
  303.  * Returns false if we ran out of memory (a problem on ms-dos), and true
  304.  * if everything went well (e.g., matches).
  305.  */
  306. static int
  307. expand_leaf (
  308. char    *path,        /* built-up pathname, top-level */
  309. char    *pattern)
  310. {
  311.     DIR    *dp;
  312.     DIRENT    *de;
  313.     int    result    = TRUE;
  314.     char    save    = 0; /* warning suppression */
  315.     SIZE_T    len;
  316.     char    *leaf;
  317.     char    *wild    = wild_leaf(pattern);
  318.     char    *next    = next_leaf(wild);
  319.     register char    *s;
  320.  
  321.     /* Fill-in 'path[]' with the non-wild leaves that we skipped to get
  322.      * to 'wild'.
  323.      */
  324.     if (wild == pattern) {    /* top-level, first leaf is wild */
  325.         if (*path == EOS)
  326.             (void)strcpy(path, ".");
  327.         leaf = skip_string(path) + 1;
  328.     } else {
  329.         len = (int)(wild - pattern) - 1;
  330.         if (*(s = path) != EOS) {
  331.             s += strlen(s);
  332.             *s++ = SLASHC;
  333.         }
  334.         if (len != 0) {
  335.             (void)strncpy(s, pattern, len);
  336.             s[len] = EOS;
  337.         }
  338.         if (*path == EOS) {
  339.             path[0] = SLASHC;
  340.             path[1] = EOS;
  341.             leaf = path + 1;
  342.         }
  343. #if OPT_MSDOS_PATH
  344.         /* Force the strncpy from 'pattern' to pick up a slash just
  345.          * after the ':' in a drive specification.
  346.          */
  347.         else if ((s = is_msdos_drive(path)) != 0 && s[0] == EOS) {
  348.             s[0] = SLASHC;
  349.             s[1] = EOS;
  350.             leaf = s + 1;
  351.         }
  352. #endif
  353.         else {
  354.             leaf = skip_string(path) + 1;
  355.         }
  356.     }
  357.  
  358.     if (next != 0) {
  359.         save = next[-1];
  360.         next[-1] = EOS;        /* restrict 'wild[]' to one leaf */
  361.     }
  362.  
  363.     /* Scan the directory, looking for leaves that match the pattern.
  364.      */
  365.     if ((dp = opendir(SL_TO_BSL(path))) != 0) {
  366.         leaf[-1] = SLASHC;    /* connect the path to the leaf */
  367.         while ((de = readdir(dp)) != 0) {
  368. #if OPT_MSDOS_PATH
  369.             (void)strcpy(leaf, de->d_name);
  370. #if !OPT_CASELESS
  371.             (void)mklower(leaf);
  372. #endif
  373.             if (strchr(pattern, '.') && !strchr(leaf, '.'))
  374.                 (void)strcat(leaf, ".");
  375. #else
  376. #if USE_D_NAMLEN
  377.             len = de->d_namlen;
  378.             (void)strncpy(leaf, de->d_name, len);
  379.             leaf[len] = EOS;
  380. #else
  381.             (void)strcpy(leaf, de->d_name);
  382. #endif
  383. #endif
  384.             if (!strcmp(leaf, ".")
  385.              || !strcmp(leaf, ".."))
  386.                 continue;
  387.             if (!match_leaf(leaf, wild))
  388.                 continue;
  389.             if (next != 0) {    /* there are more leaves */
  390.                 if (!string_has_wildcards(next)) {
  391.                     s = skip_string(leaf);
  392.                     *s++ = SLASHC;
  393.                     (void)strcpy(s, next);
  394.                     if (ffexists(path)
  395.                      && !record_a_match(path)) {
  396.                         result = FALSE;
  397.                         break;
  398.                     }
  399.                 } else if (is_directory(path)) {
  400. #if SYS_MSDOS || SYS_WIN31
  401.                     s = strrchr(path, '.');
  402.                     if (s[1] == EOS)
  403.                         s[0] = EOS;
  404. #endif
  405.                     if (!expand_leaf(path, next)) {
  406.                         result = FALSE;
  407.                         break;
  408.                     }
  409.                 }
  410.             } else if (!record_a_match(path)) {
  411.                 result = FALSE;
  412.                 break;
  413.             }
  414.         }
  415.         (void)closedir(dp);
  416.     } else
  417.         result = SORTOFTRUE;    /* at least we didn't run out of memory */
  418.  
  419.     if (next != 0)
  420.         next[-1] = save;
  421.  
  422.     return result;
  423. }
  424.  
  425. #else /* SYS_OS2 */
  426.  
  427. /*
  428.  * Recursive procedure that allows any leaf (or all!) leaves in a path to
  429.  * have wildcards.  Except for an ellipsis, each wildcard is completed
  430.  * within a single leaf.
  431.  *
  432.  * Returns false if we ran out of memory (a problem on ms-dos), and true
  433.  * if everything went well (e.g., matches).
  434.  */
  435. static int
  436. expand_leaf (
  437. char    *path,        /* built-up pathname, top-level */
  438. char    *pattern)
  439. {
  440.     FILEFINDBUF3 fb;
  441.     ULONG entries;
  442.     HDIR hdir;
  443.     APIRET rc;
  444.  
  445.     int    result    = TRUE;
  446.     char    save = 0; /* warning suppression */
  447.     SIZE_T    len;
  448.     char    *leaf;
  449.     char    *wild    = wild_leaf(pattern);
  450.     char    *next    = next_leaf(wild);
  451.     register char    *s;
  452.  
  453.     /* Fill-in 'path[]' with the non-wild leaves that we skipped to get
  454.      * to 'wild'.
  455.      */
  456.     if (wild == pattern) {    /* top-level, first leaf is wild */
  457.         if (*path == EOS)
  458.             (void)strcpy(path, ".");
  459.     } else {
  460.         len = wild - pattern - 1;
  461.         if (*(s = path) != EOS) {
  462.             s += strlen(s);
  463.             *s++ = SLASHC;
  464.         }
  465.         if (len != 0)
  466.             strncpy(s, pattern, len)[len] = EOS;
  467.     }
  468.     leaf = skip_string(path) + 1;
  469.  
  470.     if (next != 0) {
  471.         save = next[-1];
  472.         next[-1] = EOS;        /* restrict 'wild[]' to one leaf */
  473.     }
  474.  
  475.     /* Scan the directory, looking for leaves that match the pattern.
  476.      */
  477.     len = strlen(path);
  478.     if (!is_slashc(path[len - 1]))
  479.         (void)strcat(path, "/*.*");
  480.     else
  481.         (void)strcat(path, "*.*");
  482.  
  483.     hdir = HDIR_CREATE;
  484.     entries = 1;
  485.     rc = DosFindFirst(SL_TO_BSL(path), &hdir,
  486.             FILE_DIRECTORY | FILE_READONLY,
  487.             &fb, sizeof(fb), &entries, FIL_STANDARD);
  488.     if (rc == NO_ERROR)
  489.     {
  490.         leaf[-1] = SLASHC;
  491.  
  492.         do
  493.         {
  494.             (void) mklower(strcpy(leaf, fb.achName));
  495.  
  496.             if (strcmp(leaf, ".") == 0 || strcmp(leaf, "..") == 0)
  497.                  continue;
  498.             if (!match_leaf(leaf, wild))
  499.                 continue;
  500.  
  501.             if (next != 0) {    /* there are more leaves */
  502.                 if (!string_has_wildcards(next)) {
  503.                     s = skip_string(leaf);
  504.                     *s++ = SLASHC;
  505.                     (void)strcpy(s, next);
  506.                     if (!record_a_match(path)) {
  507.                         result = FALSE;
  508.                         break;
  509.                     }
  510.                 } else if (is_directory(path)) {
  511.                     s = strrchr(path, '.');
  512.                     if (s[1] == EOS)
  513.                         s[0] = EOS;
  514.                     if (!expand_leaf(path, next)) {
  515.                         result = FALSE;
  516.                         break;
  517.                     }
  518.                 }
  519.             } else if (!record_a_match(path)) {
  520.                 result = FALSE;
  521.                 break;
  522.             }
  523.  
  524.         } while (entries = 1,
  525.                  DosFindNext(hdir, &fb, sizeof(fb), &entries) == NO_ERROR
  526.                  && entries == 1);
  527.  
  528.         DosFindClose(hdir);
  529.     }
  530.     else
  531.     {
  532.         result = SORTOFTRUE;    /* at least we didn't run out of memory */
  533.     }
  534.  
  535.     if (next != 0)
  536.         next[-1] = save;
  537.  
  538.     return result;
  539. }
  540. #endif /* SYS_OS2 */
  541.  
  542. #if ! ANSI_QSORT
  543. static int
  544. compar(char **a, char **b)
  545. #else
  546. static int compar (const void *a, const void *b)
  547. #endif
  548. {
  549. #if OPT_CASELESS
  550.     return stricmp(*(char **)a, *(char **)b);
  551. #else
  552.     return strcmp(*(char *const *)a, *(char *const *)b);
  553. #endif
  554. }
  555. #endif
  556.  
  557. #if OPT_GLOB_PIPE
  558. static int
  559. glob_from_pipe(const char *pattern)
  560. {
  561. #ifdef GVAL_GLOB
  562.     char    *cmd = global_g_val_ptr(GVAL_GLOB);
  563.     int    single;
  564. #else
  565.     static    char    cmd[] = "!echo %s";
  566.     static    int    single    = TRUE;
  567. #endif
  568.     FILE    *cf;
  569.     char    tmp[NFILEN];
  570.     int    result = FALSE;
  571.     register SIZE_T len;
  572.     register char *s, *d;
  573.  
  574. #ifdef GVAL_GLOB
  575.  
  576.     /*
  577.      * For now, assume that only 'echo' will supply the result all on one
  578.      * line.  Other programs (e.g., 'ls' and 'find' do the sensible thing
  579.      * and break up the output with newlines.
  580.      */
  581.     if (!isShellOrPipe(cmd)) {
  582.         cmd = "!echo %s";
  583.         single = TRUE;
  584.         d = cmd + 1;
  585.     } else {
  586.         char    save = EOS;
  587.         for (d = cmd+1; *d != EOS && isSpace(*d); d++)
  588.             ;
  589.         for (s = d; *s != EOS; s++) {
  590.             if (isSpace(*s)) {
  591.                 save = *s;
  592.                 *s = EOS;
  593.                 break;
  594.             }
  595.         }
  596.         single = !strcmp(pathleaf(d), "echo");
  597.         if (save != EOS)
  598.             *s = save;
  599.     }
  600. #else
  601.     d = cmd+1;
  602. #endif
  603.  
  604.     (void)lsprintf(tmp, d, pattern);
  605.     if ((cf = npopen(tmp, "r")) != 0) {
  606.         char    old[NFILEN+1];
  607.  
  608.         *(d = old) = EOS;
  609.  
  610.         while ((len = fread(tmp, sizeof(*tmp), sizeof(tmp), cf)) > 0) {
  611.             /*
  612.              * Split the buffer up.  If 'single', split on all
  613.              * whitespace, otherwise only on newlines.
  614.              */
  615.             for (s = tmp; (SIZE_T)(s-tmp) < len; s++) {
  616.                 if ((single && isSpace(*s))
  617.                  || (!single && (*s == '\n' || *s == EOS))) {
  618.                     *d = EOS;
  619.                     result = record_a_match(d = old);
  620.                     *d = EOS;
  621.                     if (!result)
  622.                         break;
  623.                  } else {
  624.                      *d++ = *s;
  625.                  }
  626.             }
  627.         }
  628.         if (*old != EOS)
  629.             result = record_a_match(old);
  630.         npclose(cf);
  631.     } else
  632.         result = FALSE;
  633.  
  634.     return result;
  635. }
  636. #endif
  637.  
  638. #if OPT_GLOB_ENVIRON && UNIX_GLOBBING
  639. /*
  640.  * Expand environment variables in 'pattern[]'
  641.  * It allows names of the form
  642.  *
  643.  *    $NAME
  644.  *    $(NAME)
  645.  *    ${NAME}
  646.  */
  647. static void
  648. expand_environ(char *pattern)
  649. {
  650.     register int    j, k;
  651.     int    delim,
  652.         left,
  653.         right;
  654.     const char *s;
  655.     char    save[NFILEN];
  656.  
  657.     for (j = 0; pattern[j] != EOS; j++) {
  658.         if (pattern[j] == '$') {
  659.  
  660.             k = j+1;
  661.             if (pattern[k] == '(')        delim = ')';
  662.             else if (pattern[k] == '{')    delim = '}';
  663.             else                delim = EOS;
  664.  
  665.             if (delim != EOS)
  666.                 k++;
  667.             left    =
  668.             right    = k;
  669.  
  670.             while (pattern[k] != EOS) {
  671.                 right = k;
  672.                 if (delim != EOS) {
  673.                     if (pattern[k++] == delim)
  674.                         break;
  675.                 } else if (isname(pattern[k])) {
  676.                     k++;
  677.                 } else {
  678.                     break;
  679.                 }
  680.             }
  681.             if (delim == EOS)
  682.                 right = k;
  683.  
  684.             (void)strcpy(save, pattern+k);
  685.             if (right != left) {
  686.                 pattern[right] = EOS;
  687. #if SYS_MSDOS || SYS_OS2
  688.                 mkupper(pattern+left);
  689. #endif
  690.                 if ((s = getenv(pattern+left)) == 0)
  691.                     s = "";
  692.             } else
  693.                 s = "";
  694.  
  695.             (void)strcpy(pattern+j, s);
  696.             (void)strcat(pattern, save);
  697.             j += strlen(s) - 1;
  698.         }
  699.     }
  700. }
  701. #else
  702. #define    expand_environ(pattern)
  703. #endif
  704.  
  705. /*
  706.  * Notes:
  707.  *    VMS's sys$search function (embedded in our fake 'readdir()') handles
  708.  *    all of the VMS wildcards.
  709.  *
  710.  *    MS-DOS has non-UNIX functions that scan a directory and recognize DOS-style
  711.  *    wildcards.  Use these to get the smallest executable.  However, DOS-
  712.  *    style wildcards are crude and clumsy compared to UNIX, so we provide them as
  713.  *    an option.  (For example, the DOS-wildcards won't match "..\*\*.bak").
  714.  */
  715. static int
  716. expand_pattern (char *item)
  717. {
  718.     int    result = FALSE;
  719. #if OPT_VMS_PATH && !SYS_UNIX
  720.     DIR    *dp;
  721.     DIRENT    *de;
  722.  
  723.     if ((dp = opendir(SL_TO_BSL(item))) != 0) {
  724.         result = TRUE;
  725.         while ((de = readdir(dp)) != 0) {
  726.             char    temp[NFILEN];
  727.             size_t    len = de->d_namlen;
  728.             (void)strncpy(temp, de->d_name, len);
  729.             temp[len] = EOS;
  730.             if (!record_a_match(temp)) {
  731.                 result = FALSE;
  732.                 break;
  733.             }
  734.         }
  735.         (void)closedir(dp);
  736.     }
  737.  
  738. #else    /* UNIX or MSDOS, etc. */
  739.  
  740. #if OPT_GLOB_PIPE
  741. # ifdef GVAL_GLOB
  742.     /*
  743.      * The 'glob' mode value can be on/off or set to a pipe expression,
  744.      * e.g., "!echo %s".  This allows using the shell to expand the
  745.      * pattern, which is slower than vile's internal code, but may allow
  746.      * using specific features to which the user is accustomed.
  747.      *
  748.      * As a special case, we read from a pipe if the expression begins with
  749.      * a back-tick (e.g., `which script`).
  750.      */
  751.     if (isShellOrPipe(global_g_val_ptr(GVAL_GLOB))
  752.      || *item == BAKTIK) {
  753.         result = glob_from_pipe(item);
  754.     } else
  755. # else
  756.     result = glob_from_pipe(item);
  757. #  if UNIX_GLOBBING
  758.     huh ??        /* thought I turned that off ... */
  759. #  endif
  760. # endif
  761. #endif
  762. #if UNIX_GLOBBING
  763.     {
  764.     char    builtup[NFILEN];
  765.     char    pattern[NFILEN];
  766.     SIZE_T    first    = myLen;
  767.  
  768.     (void)strcpy(pattern, item);
  769.     *builtup = EOS;
  770. #if OPT_MSDOS_PATH
  771. #if !OPT_CASELESS
  772.     (void)mklower(pattern);
  773. #endif
  774. #endif
  775.     expand_environ(pattern);
  776.     if (string_has_wildcards(pattern)) {
  777.         if ((result = expand_leaf(builtup, pattern)) != FALSE
  778.          && (myLen-first > 1)) {
  779.             qsort((char *)&myVec[first], myLen-first,
  780.                         sizeof(*myVec), compar);
  781.         }
  782.     } else
  783.         result = record_a_match(pattern);
  784.     }
  785. #endif                /* UNIX-style globbing */
  786. #if (SYS_MSDOS || SYS_WIN31 || SYS_OS2 || SYS_WINNT) && !UNIX_GLOBBING
  787.     /* native DOS-wildcards */
  788.     DeclareFind(p);
  789.     char    temp[FILENAME_MAX + 1];
  790.     char    path[FILENAME_MAX + 1];
  791.     char *cp = pathleaf(
  792.             strcpy(temp,
  793.                 strcpy(path, item)));
  794.  
  795.     if (DirFindFirst(path,p)) {
  796.         result = TRUE;
  797.         do {
  798.             (void)strcpy(cp, DirEntryStr(p));
  799.             if (!record_a_match(temp)) {
  800.                 result = FALSE;
  801.                 break;
  802.             }
  803.         } while (DirFindNext(p));
  804.     }
  805. #endif                /* native MS-DOS globbing */
  806. #endif    /* OPT_VMS_PATH */
  807.     return result;        /* true iff no errors noticed */
  808. }
  809.  
  810. /*--------------------------------------------------------------------------*/
  811.  
  812. /*
  813.  * Tests a list of items to see if at least one of them needs to be globbed.
  814.  */
  815. #if !SYS_UNIX
  816. int
  817. glob_needed (char **list_of_items)
  818. {
  819.     register int     n;
  820.  
  821.     for (n = 0; list_of_items[n] != 0; n++)
  822.         if (string_has_wildcards(list_of_items[n]))
  823.             return TRUE;
  824.     return FALSE;
  825. }
  826. #endif
  827.  
  828. /*
  829.  * Expands the items in a list, returning an entirely new list (so it can be
  830.  * freed without knowing where it came from originally).  This should only
  831.  * return 0 if we run out of memory.
  832.  */
  833. static char **
  834. glob_expand (char **list_of_items)
  835. {
  836.     int    len = glob_length(list_of_items);
  837.     int    i;
  838.  
  839.     myMax = 0;
  840.     myLen = 0;
  841.     myVec = 0;
  842.  
  843.     for (i = 0; i < len; ++i) {
  844.         char *item = list_of_items[i];
  845.         /*
  846.          * For UNIX, expand '~' expressions in case we've got a pattern
  847.          * like "~/test*.log".
  848.          */
  849. #if SYS_UNIX
  850.         char    temp[NFILEN];
  851.         item = home_path(strcpy(temp, item));
  852. #endif
  853.         if (!isInternalName(item)
  854.          && globbing_active()
  855.          && string_has_wildcards(item)) {
  856.             if (!expand_pattern(item))
  857.                 return 0;
  858.         } else if (!record_a_match(item)) {
  859.             return 0;
  860.         }
  861.     }
  862.     return myVec;
  863. }
  864.  
  865. /*
  866.  * A special case of 'glob_expand()', expands a single string into a list.
  867.  */
  868. char **
  869. glob_string (char *item)
  870. {
  871.     char *vec[2];
  872.  
  873.     vec[0] = item;
  874.     vec[1] = 0;
  875.  
  876.     return glob_expand(vec);
  877. }
  878.  
  879. /*
  880.  * Counts the number of items in a list of strings.  This is simpler (and
  881.  * more useful) than returning the length and the list as arguments from
  882.  * a procedure.  Note that since the standard argc/argv convention puts a
  883.  * null pointer on the end, this function is applicable to the 'argv[]'
  884.  * parameter of the main program as well.
  885.  */
  886. int
  887. glob_length (char **list_of_items)
  888. {
  889.     register int    len;
  890.     if (list_of_items != 0) {
  891.         for (len = 0; list_of_items[len] != 0; len++)
  892.             ;
  893.     } else
  894.         len = 0;
  895.     return len;
  896. }
  897.  
  898. /*
  899.  * Frees the strings in a list, and the list itself.  Note that this should
  900.  * not be used for the main program's original argv, because on some systems
  901.  * it is a part of a larger data area, as are the command strings.
  902.  */
  903. char **
  904. glob_free (char **list_of_items)
  905. {
  906.     register int    len;
  907.     if (list_of_items != 0) {
  908.         for (len = 0; list_of_items[len] != 0; len++)
  909.             free(list_of_items[len]);
  910.         free ((char *)list_of_items);
  911.     }
  912.     return 0;
  913. }
  914.  
  915.  
  916. #if !SYS_UNIX
  917. /*
  918.  * Expand wildcards for the main program a la UNIX shell.
  919.  */
  920. void
  921. expand_wild_args(int *argcp, char ***argvp)
  922. {
  923. #if SYS_VMS
  924.     int    j, k;
  925.     int    comma    = 0;
  926.     int    option    = 0;
  927.     /*
  928.      * VMS command-line arguments may be a comma-separated list of
  929.      * filenames, with an optional "/read_only" flag appended.
  930.      */
  931.     for (j = 1; j < *argcp; j++) {
  932.         char    *s = (*argvp)[j];
  933.         int    c;
  934.         while ((c = *s++) != EOS) {
  935.             if (c == ',')
  936.                 comma++;
  937.             if (c == '/')
  938.                 option++;
  939.         }
  940.     }
  941.     if (comma || option) {
  942.         char    **newvec = typeallocn(char *, comma + *argcp + 1);
  943.         for (j = k = 0; j < *argcp; j++) {
  944.             char    *the_arg = strmalloc((*argvp)[j]);
  945.             char    *item;
  946.  
  947.             /* strip off VMS options */
  948.             while ((item = strrchr(the_arg, '/')) != 0) {
  949.                 mkupper(item);
  950.                 if (!strncmp(item, "/READ_ONLY", strlen(item))) {
  951.                     set_global_b_val(MDVIEW,TRUE);
  952.                 }
  953.                 *item = EOS;
  954.             }
  955.  
  956.             while (*the_arg != EOS) {
  957.                 item = strchr(the_arg, ',');
  958.                 if (item == 0)
  959.                     item = skip_string(the_arg);
  960.                 else
  961.                     *item++ = EOS;
  962.                 newvec[k++] = the_arg;
  963.                 the_arg = item;
  964.             }
  965.         }
  966.         newvec[k] = 0;
  967.         *argcp = k;
  968.         *argvp = newvec;
  969.     }
  970. #endif
  971.     if (glob_needed(*argvp)) {
  972.         char    **newargs = glob_expand(*argvp);
  973.         if (newargs != 0) {
  974.             *argvp = newargs;
  975.             *argcp = glob_length(newargs);
  976.         }
  977.     }
  978. }
  979. #endif
  980.  
  981. /*
  982.  * Expand a string, permitting only one match.
  983.  */
  984. int
  985. doglob (char *path)
  986. {
  987.     char    **expand = glob_string(path);
  988.     int    len = glob_length(expand);
  989.  
  990.     if (len > 1) {
  991.         if (mlyesno("Too many filenames.  Use first") != TRUE) {
  992.             (void)glob_free(expand);
  993.             return FALSE;
  994.         }
  995.     }
  996.     if (len > 0) {
  997.         (void)strcpy(path, expand[0]);
  998.         (void)glob_free(expand);
  999.     }
  1000.     return TRUE;
  1001. }
  1002.