home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume36 / ldb / part03 / flist_unix.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-04-11  |  13.4 KB  |  467 lines

  1. /*    flist_unix.c        4/24/91
  2.  *
  3.  * Copyright 1991  Perry R. Ross
  4.  *
  5.  * Permission to use, copy, modify, and distribute this software and its
  6.  * documentation without fee is hereby granted, subject to the restrictions
  7.  * detailed in the README file, which is included here by reference.
  8.  * Any other use requires written permission from the author.  This software
  9.  * is distributed "as is" without any warranty, including any implied
  10.  * warranties of merchantability or fitness for a particular purpose.
  11.  * The author shall not be liable for any damages resulting from the
  12.  * use of this software.  By using this software, the user agrees
  13.  * to these terms.
  14.  */
  15.  
  16. /* This file uses the "regex-glob" routines, written by John Kercheval, and
  17.  * posted to comp.sources.misc.  The code appears at the end of this
  18.  * file with all original comments.
  19.  */
  20.  
  21. #include "ldb.h"
  22.  
  23.  
  24. /*----------------------------------------------------------------------
  25.  *    filelist -- generate a list of all matching files.
  26.  *
  27.  * This function generates a list of all files that match a pattern.
  28.  * Each file is stored in an instance of struct flist, which just
  29.  * links the names in a linked list.  A pointer to the beginning of
  30.  * the list is returned.  It is the callers responsibility to free
  31.  * the list when it is no longer needed.
  32.  *
  33.  * This function will only recognize wildcards in file names, NOT
  34.  * the directories leading up to them.  For example, nuts/ldb*.txt is
  35.  * fine, but *ts/ldb*.txt is not.
  36.  *
  37.  * This function uses the "new" directory routines (i.e. opendir,
  38.  * readdir, et. al).  If these are not on your system, you should
  39.  * have defined NEED_READDIR in your Makefile.
  40.  *----------------------------------------------------------------------
  41.  */
  42.  
  43. struct flist *filelist(ptn)
  44. char *ptn;
  45. {
  46. struct flist *head, *tail, *cur;
  47. DIR *dp;
  48. char *s;
  49. char *dn, *pn;
  50. struct direct *p;
  51.  
  52. head = NULL;
  53. if (is_pattern(ptn) == 0) {    /* no wildcards, just a file name */
  54.     if ( (cur = (struct flist *) calloc(sizeof(struct flist),1)) == NULL)
  55.         fatal("Out of memory!");
  56.     head = cur;
  57.     tail = cur;
  58.     cur->name = save(ptn);
  59.     return(cur);
  60.     }
  61. if ( (s = strrchr(ptn,'/')) != NULL) {    /* strip off directory name */
  62.     *s = '\0';
  63.     dn = save(ptn);        /* dir = everything before last /  */
  64.     pn = save(s+1);        /* pattern = everything after last /  */
  65.     *s = '/';
  66.     }
  67. else {
  68.     dn = save(".");
  69.     pn = save(ptn);
  70.     }
  71. if ( (dp = opendir(dn)) == NULL) {
  72.     free(dn);
  73.     free(pn);
  74.     return(NULL);
  75.     }
  76. while ( (p = readdir(dp)) != NULL) {
  77.     if ( (strcmp(p->d_name,".") == 0) || (strcmp(p->d_name,"..") == 0) )
  78.         continue;
  79.     if (match(pn,p->d_name) == 0)
  80.         continue;
  81.     if ( (cur = (struct flist *) calloc(sizeof(struct flist),1)) == NULL)
  82.         fatal("Out of memory!");
  83.     if (head == NULL) {
  84.         head = cur;
  85.         tail = cur;
  86.         }
  87.     else {
  88.         tail->next = cur;
  89.         tail = cur;
  90.         }
  91.     if (strcmp(dn,".") == 0)    /* file in current dir */
  92.         cur->name = save(p->d_name);    /* just save name */
  93.     else {                /* include directory name */
  94.         cur->name = (char *) malloc(strlen(dn)+strlen(p->d_name)+2);
  95.         if (cur->name == NULL)
  96.             fatal("Out of memory!");
  97.         sprintf(cur->name,"%s/%s",dn,p->d_name);
  98.         }
  99.     }
  100. closedir(dp);
  101. free(dn);
  102. free(pn);
  103. return(head);
  104. }
  105.  
  106.  
  107. /* regex-glob code follows: (de-ansified by P. Ross -- sorry) */
  108.  
  109. /*
  110.  EPSHeader
  111.  
  112.    File: match.c
  113.    Author: J. Kercheval
  114.    Created: Sat, 01/05/1991  22:21:49
  115. */
  116. /*
  117.  EPSRevision History
  118.  
  119.    J. Kercheval  Wed, 02/20/1991  22:29:01  Released to Public Domain
  120. */
  121.  
  122. /*
  123.    Wildcard Pattern Matching
  124. */
  125.  
  126.  
  127. /* #include "match.h" -- match.h included here for simplicity  P. Ross */
  128.  
  129. /*
  130.  EPSHeader
  131.  
  132.    File: match.h
  133.    Author: J. Kercheval
  134.    Created: Sat, 01/05/1991  22:27:18
  135. */
  136. /*
  137.  EPSRevision History
  138.  
  139.    J. Kercheval  Wed, 02/20/1991  22:28:37  Released to Public Domain
  140. */
  141.  
  142. /*
  143.    Wildcard Pattern Matching
  144. */
  145.  
  146. #ifndef BOOLEAN
  147. # define BOOLEAN int
  148. #undef TRUE
  149. #undef FALSE
  150. # define TRUE 1
  151. # define FALSE 0
  152. #endif
  153.  
  154. /*----------------------------------------------------------------------------
  155. *
  156. *  Match the pattern PATTERN against the string TEXT;
  157. *  return TRUE if it matches, FALSE otherwise.
  158. *
  159. *  A match means the entire string TEXT is used up in matching.
  160. *
  161. *  In the pattern string:
  162. *       `*' matches any sequence of characters
  163. *       `?' matches any character
  164. *       [SET] matches any character in the specified set,
  165. *       [!SET] or [^SET] matches any character not in the specified set.
  166. *
  167. *  Note: the standard regex character '+' (one or more) should by
  168. *        simulated by using "?*" which is equivelant here.
  169. *
  170. *  A set is composed of characters or ranges; a range looks like
  171. *  character hyphen character (as in 0-9 or A-Z).
  172. *  [0-9a-zA-Z_] is the set of characters allowed in C identifiers.
  173. *  Any other character in the pattern must be matched exactly.
  174. *
  175. *  To suppress the special syntactic significance of any of `[]*?!^-\',
  176. *  and match the character exactly, precede it with a `\'.
  177. *
  178. ----------------------------------------------------------------------------*/
  179.  
  180. BOOLEAN match ( /* char *pattern, char *text */ );
  181.  
  182. /*----------------------------------------------------------------------------
  183. *
  184. * Return TRUE if PATTERN has any special wildcard characters
  185. *
  186. ----------------------------------------------------------------------------*/
  187.  
  188. BOOLEAN is_pattern ( /* char *pattern */ );
  189.  
  190. /* -- end of match.h  P. Ross -- */
  191.  
  192. #define ABORT 2     /* end of search indicator */
  193.  
  194. BOOLEAN regex_match_after_star ( /* char *pattern, char *text */ );
  195.  
  196. /*----------------------------------------------------------------------------
  197. *
  198. * Return TRUE if PATTERN has any special wildcard characters
  199. *
  200. ----------------------------------------------------------------------------*/
  201.  
  202. BOOLEAN is_pattern (p)
  203. char *p;
  204. {
  205.     while ( *p ) {
  206.         switch ( *p++ ) {
  207.             case '?':
  208.             case '*':
  209.             case '[':
  210.                 return TRUE;
  211.             case '\\':
  212.                 if ( !*p++ ) return FALSE;
  213.         }
  214.     }
  215.     return FALSE;
  216. }
  217.  
  218.  
  219. /*----------------------------------------------------------------------------
  220. *
  221. *  Match the pattern PATTERN against the string TEXT;
  222. *  return TRUE if it matches, FALSE otherwise.
  223. *
  224. *  A match means the entire string TEXT is used up in matching.
  225. *
  226. *  In the pattern string:
  227. *       `*' matches any sequence of characters
  228. *       `?' matches any character
  229. *       [SET] matches any character in the specified set,
  230. *       [!SET] or [^SET] matches any character not in the specified set.
  231. *
  232. *  Note: the standard regex character '+' (one or more) should by
  233. *        simulated by using "?*" which is equivelant here.
  234. *
  235. *  A set is composed of characters or ranges; a range looks like
  236. *  character hyphen character (as in 0-9 or A-Z).
  237. *  [0-9a-zA-Z_] is the set of characters allowed in C identifiers.
  238. *  Any other character in the pattern must be matched exactly.
  239. *
  240. *  To suppress the special syntactic significance of any of `[]*?!^-\',
  241. *  and match the character exactly, precede it with a `\'.
  242. *
  243. ----------------------------------------------------------------------------*/
  244.  
  245. BOOLEAN regex_match (p, t)
  246. register char *p;
  247. register char *t;
  248. {
  249.     register char range_start, range_end;  /* start and end in range */
  250.  
  251.     BOOLEAN invert;             /* is this [..] or [!..] */
  252.     BOOLEAN member_match;       /* have I matched the [..] construct? */
  253.     BOOLEAN loop;               /* should I terminate? */
  254.  
  255.     for ( ; *p; p++, t++ ) {
  256.  
  257.         /* if this is the end of the text then this is the end of the match */
  258.         if (!*t) {
  259.             return ( *p == '*' && *++p == '\0' ) ? TRUE : ABORT;
  260.         }
  261.  
  262.         /* determine and react to pattern type */
  263.         switch ( *p ) {
  264.  
  265.             /* single any character match */
  266.             case '?':
  267.                 break;
  268.  
  269.             /* multiple any character match */
  270.             case '*':
  271.                 return regex_match_after_star (p, t);
  272.  
  273.             /* [..] construct, single member/exclusion character match */
  274.             case '[': {
  275.  
  276.                 /* move to beginning of range */
  277.                 p++;
  278.  
  279.                 /* check if this is a member match or exclusion match */
  280.                 invert = FALSE;
  281.                 if ( *p == '!' || *p == '^') {
  282.                     invert = TRUE;
  283.                     p++;
  284.                 }
  285.  
  286.                 /* if closing bracket here or at range start then we have a
  287.                    malformed pattern */
  288.                 if ( *p == ']' ) {
  289.                     return ABORT;
  290.                 }
  291.  
  292.                 member_match = FALSE;
  293.                 loop = TRUE;
  294.  
  295.                 while ( loop ) {
  296.  
  297.                     /* if end of construct then loop is done */
  298.                     if (*p == ']') {
  299.                         loop = FALSE;
  300.                         continue;
  301.                     }
  302.  
  303.                     /* matching a '!', '^', '-', '\' or a ']' */
  304.                     if ( *p == '\\' ) {
  305.                         range_start = range_end = *++p;
  306.                     }
  307.                     else {
  308.                         range_start = range_end = *p;
  309.                     }
  310.  
  311.                     /* if end of pattern then bad pattern (Missing ']') */
  312.                     if (!range_start)
  313.                         return ABORT;
  314.  
  315.                     /* move to next pattern char */
  316.                     p++;
  317.  
  318.                     /* check for range bar */
  319.                     if (*p == '-') {
  320.  
  321.                         /* get the range end */
  322.                         range_end = *++p;
  323.  
  324.                         /* special character range end */
  325.                         if (range_end == '\\')
  326.                             range_end = *++p;
  327.  
  328.                         /* if end of pattern or construct then bad pattern */
  329.                         if (range_end == '\0' || range_end == ']')
  330.                             return ABORT;
  331.                     }
  332.  
  333.                     /* if the text character is in range then match found.
  334.                        make sure the range letters have the proper
  335.                        relationship to one another before comparison */
  336.                     if ( range_start < range_end  ) {
  337.                         if (*t >= range_start && *t <= range_end) {
  338.                             member_match = TRUE;
  339.                             loop = FALSE;
  340.                         }
  341.                     }
  342.                     else {
  343.                         if (*t >= range_end && *t <= range_start) {
  344.                             member_match = TRUE;
  345.                             loop = FALSE;
  346.                         }
  347.                     }
  348.                 }
  349.  
  350.                 /* if there was a match in an exclusion set then no match */
  351.                 /* if there was no match in a member set then no match */
  352.                 if ((invert && member_match) ||
  353.                    !(invert || member_match))
  354.                     return FALSE;
  355.  
  356.                 /* if this is not an exclusion then skip the rest of the [...]
  357.                     construct that already matched. */
  358.                 if (member_match) {
  359.                     while (*p != ']') {
  360.  
  361.                         /* bad pattern (Missing ']') */
  362.                         if (!*p)
  363.                             return ABORT;
  364.  
  365.                         /* skip exact match */
  366.                         if (*p == '\\') {
  367.                             p++;
  368.                         }
  369.  
  370.                         /* move to next pattern char */
  371.                         p++;
  372.                     }
  373.                 }
  374.  
  375.                 break;
  376.             }
  377.  
  378.             /* next character is quoted and must match exactly */
  379.             case '\\':
  380.  
  381.                 /* move pattern pointer to quoted char and fall through */
  382.                 p++;
  383.  
  384.             /* must match this character exactly */
  385.             default:
  386.                 if (*p != *t)
  387.                     return FALSE;
  388.         }
  389.     }
  390.  
  391.     /* if end of text not reached then the pattern fails */
  392.     return !*t;
  393. }
  394.  
  395.  
  396. /*----------------------------------------------------------------------------
  397. *
  398. * recursively call regex_match with final segment of PATTERN and of TEXT.
  399. *
  400. ----------------------------------------------------------------------------*/
  401.  
  402. BOOLEAN regex_match_after_star (p, t)
  403. register char *p;
  404. register char *t;
  405. {
  406.     register BOOLEAN match;
  407.     register nextp;
  408.  
  409.     /* pass over existing ? and * in pattern */
  410.     while ( *p == '?' || *p == '*' ) {
  411.  
  412.         /* take one char for each ? */
  413.         if ( *p == '?' ) {
  414.  
  415.             /* if end of text then no match */
  416.             if ( !*t++ ) {
  417.                 return ABORT;
  418.             }
  419.         }
  420.  
  421.         /* move to next char in pattern */
  422.         p++;
  423.     }
  424.  
  425.     /* if end of pattern we have matched regardless of text left */
  426.     if ( !*p ) {
  427.         return TRUE;
  428.     }
  429.  
  430.     /* get the next character to match which must be a literal or '[' */
  431.     nextp = *p;
  432.     if ( nextp == '\\' )
  433.         nextp = p[1];
  434.  
  435.     /* Continue until we run out of text or definite result seen */
  436.     match = FALSE;
  437.     while ( match == FALSE ) {
  438.  
  439.         /* a precondition for matching is that the next character
  440.            in the pattern match the next character in the text or that
  441.            the next pattern is the beginning of a range.  Increment text
  442.            pointer as we go here */
  443.         if ( *p == *t || nextp == '[' ) {
  444.             match = regex_match(p, t);
  445.         }
  446.  
  447.         /* if the end of text is reached then no match */
  448.         if ( !*t++ ) match = ABORT;
  449.     }
  450.  
  451.     /* return result */
  452.     return match;
  453. }
  454.  
  455. /*----------------------------------------------------------------------------
  456. *
  457. * This is a shell to regex_match to return only a true BOOLEAN value
  458. *
  459. ----------------------------------------------------------------------------*/
  460.  
  461. BOOLEAN match(p, t)
  462. char *p;
  463. char *t;
  464. {
  465.     return ( regex_match(p,t) == TRUE ) ? TRUE : FALSE;
  466. }
  467.