home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume30 / rc / part05 / glob.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-05-30  |  6.1 KB  |  241 lines

  1. /* glob.c: rc's (ugly) globber. This code is not elegant, but it works */
  2.  
  3. #include <sys/types.h>
  4. #include <sys/stat.h>
  5. #include "rc.h"
  6. #ifdef NODIRENT
  7. #include <sys/dir.h>
  8. #define dirent direct /* need to get the struct declaraction right */
  9. #else
  10. #include <dirent.h>
  11. #endif
  12.  
  13. static List *dmatch(char *, char *, char *);
  14. static List *doglob(char *, char *);
  15. static List *lglob(List *, char *, char *, SIZE_T);
  16. static List *sort(List *);
  17.  
  18. /*
  19.    Matches a list of words s against a list of patterns p. Returns true iff
  20.    a pattern in p matches a word in s. () matches (), but otherwise null
  21.    patterns match nothing.
  22. */
  23.  
  24. extern bool lmatch(List *s, List *p) {
  25.     List *q;
  26.     int i;
  27.     bool okay;
  28.     if (s == NULL) {
  29.         if (p == NULL) /* null matches null */
  30.             return TRUE;
  31.         for (; p != NULL; p = p->n) { /* one or more stars match null */
  32.             if (*p->w != '\0') { /* the null string is a special case; it does *not* match () */
  33.                 okay = TRUE;
  34.                 for (i = 0; p->w[i] != '\0'; i++)
  35.                     if (p->w[i] != '*' || p->m[i] != 1) {
  36.                         okay = FALSE;
  37.                         break;
  38.                     }
  39.                 if (okay)
  40.                     return TRUE;
  41.             }
  42.         }
  43.         return FALSE;
  44.     }
  45.     for (; s != NULL; s = s->n)
  46.         for (q = p; q != NULL; q = q->n)
  47.             if (match(q->w, q->m, s->w))
  48.                 return TRUE;
  49.     return FALSE;
  50. }
  51.  
  52. /*
  53.    Globs a list; checks to see if each element in the list has a metacharacter. If it
  54.    does, it is globbed, and the output is sorted.
  55. */
  56.  
  57. extern List *glob(List *s) {
  58.     List *top, *r;
  59.     bool meta;
  60.     for (r = s, meta = FALSE; r != NULL; r = r->n)
  61.         if (r->m != NULL)
  62.             meta = TRUE;
  63.     if (!meta)
  64.         return s; /* don't copy lists with no metacharacters in them */
  65.     for (top = r = NULL; s != NULL; s = s->n) {
  66.         if (s->m == NULL) { /* no metacharacters; just tack on to the return list */
  67.             if (top == NULL)
  68.                 top = r = nnew(List);
  69.             else
  70.                 r = r->n = nnew(List);
  71.             r->w = s->w;
  72.         } else {
  73.             if (top == NULL)
  74.                 top = r = sort(doglob(s->w, s->m));
  75.             else
  76.                 r->n = sort(doglob(s->w, s->m));
  77.             while (r->n != NULL)
  78.                 r = r->n;
  79.         }
  80.     }
  81.     r->n = NULL;
  82.     return top;
  83. }
  84.  
  85. /* Matches a pattern p against the contents of directory d */
  86.  
  87. static List *dmatch(char *d, char *p, char *m) {
  88.     bool matched = FALSE;
  89.     List *top, *r;
  90.     static DIR *dirp;
  91.     static struct dirent *dp;
  92.     static struct stat s;
  93.     /* prototypes for XXXdir functions. comment out if necessary */
  94.     extern DIR *opendir(const char *);
  95.     extern struct dirent *readdir(DIR *);
  96.     /*extern int closedir(DIR *);*/
  97.  
  98.     top = r = NULL;
  99.     /* opendir succeeds on regular files on some systems, so the stat() call is necessary (sigh) */
  100.     if (stat(d, &s) < 0 || (s.st_mode & S_IFMT) != S_IFDIR || (dirp = opendir(d)) == NULL)
  101.         return NULL;
  102.     while ((dp = readdir(dirp)) != NULL)
  103.         if ((*dp->d_name != '.' || *p == '.') && match(p, m, dp->d_name)) { /* match ^. explicitly */
  104.             matched = TRUE;
  105.             if (top == NULL)
  106.                 top = r = nnew(List);
  107.             else
  108.                 r = r->n = nnew(List);
  109.             r->w = ncpy(dp->d_name);
  110.             r->m = NULL;
  111.         }
  112.     closedir(dirp);
  113.     if (!matched)
  114.         return NULL;
  115.     r->n = NULL;
  116.     return top;
  117. }
  118.  
  119. /*
  120.    lglob() globs a pattern agains a list of directory roots. e.g., (/tmp /usr /bin) "*"
  121.    will return a list with all the files in /tmp, /usr, and /bin. NULL on no match.
  122.    slashcount indicates the number of slashes to stick between the directory and the
  123.    matched name. e.g., for matching ////tmp/////foo*
  124. */
  125.  
  126. static List *lglob(List *s, char *p, char *m, SIZE_T slashcount) {
  127.     List *q, *r, *top, foo;
  128.     static List slash;
  129.     static SIZE_T slashsize = 0;
  130.     if (slashcount + 1 > slashsize) {
  131.         slash.w = ealloc(slashcount + 1);
  132.         slashsize = slashcount;
  133.     }
  134.     slash.w[slashcount] = '\0';
  135.     while (slashcount > 0)
  136.         slash.w[--slashcount] = '/';
  137.     for (top = r = NULL; s != NULL; s = s->n) {
  138.         q = dmatch(s->w, p, m);
  139.         if (q != NULL) {
  140.             foo.w = s->w;
  141.             foo.m = NULL;
  142.             foo.n = NULL;
  143.             if (!(s->w[0] == '/' && s->w[1] == '\0')) /* need to separate */
  144.                 q = concat(&slash, q);          /* dir/name with slash */
  145.             q = concat(&foo, q);
  146.             if (r == NULL)
  147.                 top = r = q;
  148.             else
  149.                 r->n = q;
  150.             while (r->n != NULL)
  151.                 r = r->n;
  152.         }
  153.     }
  154.     return top;
  155. }
  156.  
  157. /*
  158.    Doglob globs a pathname in pattern form against a unix path. Returns the original
  159.    pattern (cleaned of metacharacters) on failure, or the globbed string(s).
  160. */
  161.  
  162. static List *doglob(char *w, char *m) {
  163.     static char *dir = NULL, *pattern = NULL, *metadir = NULL, *metapattern = NULL;
  164.     static SIZE_T dsize = 0;
  165.     char *d, *p, *md, *mp;
  166.     SIZE_T psize;
  167.     char *s = w;
  168.     List firstdir;
  169.     List *matched;
  170.     if ((psize = strlen(w) + 1) > dsize || dir == NULL) {
  171.         efree(dir); efree(pattern); efree(metadir); efree(metapattern);
  172.         dir = ealloc(psize);
  173.         pattern = ealloc(psize);
  174.         metadir = ealloc(psize);
  175.         metapattern = ealloc(psize);
  176.         dsize = psize;
  177.     }
  178.     d = dir;
  179.     p = pattern;
  180.     md = metadir;
  181.     mp = metapattern;
  182.     if (*s == '/')
  183.         while (*s == '/')
  184.             *d++ = *s++, *md++ = *m++;
  185.     else
  186.         while (*s != '/' && *s != '\0')
  187.             *d++ = *s++, *md++ = *m++; /* get first directory component */
  188.     *d = '\0';
  189.     /*
  190.        Special case: no slashes in the pattern, i.e., open the current directory.
  191.        Remember that w cannot consist of slashes alone (the other way *s could be
  192.        zero) since doglob gets called iff there's a metacharacter to be matched
  193.     */
  194.     if (*s == '\0') {
  195.         matched = dmatch(".", dir, metadir);
  196.         goto end;
  197.     }
  198.     if (*w == '/') {
  199.         firstdir.w = dir;
  200.         firstdir.m = metadir;
  201.         firstdir.n = NULL;
  202.         matched = &firstdir;
  203.     } else {
  204.         /*
  205.            we must glob against current directory,
  206.            since the first character is not a slash.
  207.         */
  208.         matched = dmatch(".", dir, metadir);
  209.     }
  210.     do {
  211.         SIZE_T slashcount;
  212.         SIGCHK;
  213.         for (slashcount = 0; *s == '/'; s++, m++)
  214.             slashcount++; /* skip slashes */
  215.         while (*s != '/' && *s != '\0')
  216.             *p++ = *s++, *mp++ = *m++; /* get pattern */
  217.         *p = '\0';
  218.         matched = lglob(matched, pattern, metapattern, slashcount);
  219.         p = pattern, mp = metapattern;
  220.     } while (*s != '\0');
  221. end:    if (matched == NULL) {
  222.         matched = nnew(List);
  223.         matched->w = w;
  224.         matched->m = NULL;
  225.         matched->n = NULL;
  226.     }
  227.     return matched;
  228. }
  229.  
  230. static List *sort(List *s) {
  231.     SIZE_T nel = listnel(s);
  232.     if (nel > 1) {
  233.         char **a;
  234.         List *t;
  235.         qsort(a = list2array(s, FALSE), nel, sizeof(char *), starstrcmp);
  236.         for (t = s; t != NULL; t = t->n)
  237.             t->w = *a++;
  238.     }
  239.     return s;
  240. }
  241.