1 /* 2 * Copyright (c) 1989, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Guido van Rossum. 7 * 8 * %sccs.include.redist.c% 9 */ 10 11 #if defined(LIBC_SCCS) && !defined(lint) 12 static char sccsid[] = "@(#)fnmatch.c 8.2 (Berkeley) 04/16/94"; 13 #endif /* LIBC_SCCS and not lint */ 14 15 /* 16 * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6. 17 * Compares a filename or pathname to a pattern. 18 */ 19 20 #include <fnmatch.h> 21 #include <string.h> 22 23 #define EOS '\0' 24 25 static const char *rangematch __P((const char *, int, int)); 26 27 int 28 fnmatch(pattern, string, flags) 29 const char *pattern, *string; 30 int flags; 31 { 32 const char *stringstart; 33 char c, test; 34 35 for (stringstart = string;;) 36 switch (c = *pattern++) { 37 case EOS: 38 return (*string == EOS ? 0 : FNM_NOMATCH); 39 case '?': 40 if (*string == EOS) 41 return (FNM_NOMATCH); 42 if (*string == '/' && (flags & FNM_PATHNAME)) 43 return (FNM_NOMATCH); 44 if (*string == '.' && (flags & FNM_PERIOD) && 45 (string == stringstart || 46 ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) 47 return (FNM_NOMATCH); 48 ++string; 49 break; 50 case '*': 51 c = *pattern; 52 /* Collapse multiple stars. */ 53 while (c == '*') 54 c = *++pattern; 55 56 if (*string == '.' && (flags & FNM_PERIOD) && 57 (string == stringstart || 58 ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) 59 return (FNM_NOMATCH); 60 61 /* Optimize for pattern with * at end or before /. */ 62 if (c == EOS) 63 if (flags & FNM_PATHNAME) 64 return (strchr(string, '/') == NULL ? 65 0 : FNM_NOMATCH); 66 else 67 return (0); 68 else if (c == '/' && flags & FNM_PATHNAME) { 69 if ((string = strchr(string, '/')) == NULL) 70 return (FNM_NOMATCH); 71 break; 72 } 73 74 /* General case, use recursion. */ 75 while ((test = *string) != EOS) { 76 if (!fnmatch(pattern, string, flags & ~FNM_PERIOD)) 77 return (0); 78 if (test == '/' && flags & FNM_PATHNAME) 79 break; 80 ++string; 81 } 82 return (FNM_NOMATCH); 83 case '[': 84 if (*string == EOS) 85 return (FNM_NOMATCH); 86 if (*string == '/' && flags & FNM_PATHNAME) 87 return (FNM_NOMATCH); 88 if ((pattern = 89 rangematch(pattern, *string, flags)) == NULL) 90 return (FNM_NOMATCH); 91 ++string; 92 break; 93 case '\\': 94 if (!(flags & FNM_NOESCAPE)) { 95 if ((c = *pattern++) == EOS) { 96 c = '\\'; 97 --pattern; 98 } 99 } 100 /* FALLTHROUGH */ 101 default: 102 if (c != *string++) 103 return (FNM_NOMATCH); 104 break; 105 } 106 /* NOTREACHED */ 107 } 108 109 static const char * 110 rangematch(pattern, test, flags) 111 const char *pattern; 112 int test, flags; 113 { 114 int negate, ok; 115 char c, c2; 116 117 /* 118 * A bracket expression starting with an unquoted circumflex 119 * character produces unspecified results (IEEE 1003.2-1992, 120 * 3.13.2). This implementation treats it like '!', for 121 * consistency with the regular expression syntax. 122 * J.T. Conklin (conklin@ngai.kaleida.com) 123 */ 124 if (negate = (*pattern == '!' || *pattern == '^')) 125 ++pattern; 126 127 for (ok = 0; (c = *pattern++) != ']';) { 128 if (c == '\\' && !(flags & FNM_NOESCAPE)) 129 c = *pattern++; 130 if (c == EOS) 131 return (NULL); 132 if (*pattern == '-' 133 && (c2 = *(pattern+1)) != EOS && c2 != ']') { 134 pattern += 2; 135 if (c2 == '\\' && !(flags & FNM_NOESCAPE)) 136 c2 = *pattern++; 137 if (c2 == EOS) 138 return (NULL); 139 if (c <= test && test <= c2) 140 ok = 1; 141 } else if (c == test) 142 ok = 1; 143 } 144 return (ok == negate ? NULL : pattern); 145 } 146