1 /* 2 * Copyright (c) 2010 Jilles Tjoelker 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/param.h> 28 #include <errno.h> 29 #include <fnmatch.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <unistd.h> 34 35 struct testcase { 36 const char *pattern; 37 const char *string; 38 int flags; 39 int result; 40 } testcases[] = { 41 { "", "", 0, 0 }, 42 { "a", "a", 0, 0 }, 43 { "a", "b", 0, FNM_NOMATCH }, 44 { "a", "A", 0, FNM_NOMATCH }, 45 { "*", "a", 0, 0 }, 46 { "*", "aa", 0, 0 }, 47 { "*a", "a", 0, 0 }, 48 { "*a", "b", 0, FNM_NOMATCH }, 49 { "*a*", "b", 0, FNM_NOMATCH }, 50 { "*a*b*", "ab", 0, 0 }, 51 { "*a*b*", "qaqbq", 0, 0 }, 52 { "*a*bb*", "qaqbqbbq", 0, 0 }, 53 { "*a*bc*", "qaqbqbcq", 0, 0 }, 54 { "*a*bb*", "qaqbqbb", 0, 0 }, 55 { "*a*bc*", "qaqbqbc", 0, 0 }, 56 { "*a*bb", "qaqbqbb", 0, 0 }, 57 { "*a*bc", "qaqbqbc", 0, 0 }, 58 { "*a*bb", "qaqbqbbq", 0, FNM_NOMATCH }, 59 { "*a*bc", "qaqbqbcq", 0, FNM_NOMATCH }, 60 { "*a*a*a*a*a*a*a*a*a*a*", "aaaaaaaaa", 0, FNM_NOMATCH }, 61 { "*a*a*a*a*a*a*a*a*a*a*", "aaaaaaaaaa", 0, 0 }, 62 { "*a*a*a*a*a*a*a*a*a*a*", "aaaaaaaaaaa", 0, 0 }, 63 { ".*.*.*.*.*.*.*.*.*.*", ".........", 0, FNM_NOMATCH }, 64 { ".*.*.*.*.*.*.*.*.*.*", "..........", 0, 0 }, 65 { ".*.*.*.*.*.*.*.*.*.*", "...........", 0, 0 }, 66 { "*?*?*?*?*?*?*?*?*?*?*", "123456789", 0, FNM_NOMATCH }, 67 { "??????????*", "123456789", 0, FNM_NOMATCH }, 68 { "*??????????", "123456789", 0, FNM_NOMATCH }, 69 { "*?*?*?*?*?*?*?*?*?*?*", "1234567890", 0, 0 }, 70 { "??????????*", "1234567890", 0, 0 }, 71 { "*??????????", "1234567890", 0, 0 }, 72 { "*?*?*?*?*?*?*?*?*?*?*", "12345678901", 0, 0 }, 73 { "??????????*", "12345678901", 0, 0 }, 74 { "*??????????", "12345678901", 0, 0 }, 75 { "[x]", "x", 0, 0 }, 76 { "[*]", "*", 0, 0 }, 77 { "[?]", "?", 0, 0 }, 78 { "[", "[", 0, 0 }, 79 { "[[]", "[", 0, 0 }, 80 { "[[]", "x", 0, FNM_NOMATCH }, 81 { "[*]", "", 0, FNM_NOMATCH }, 82 { "[*]", "x", 0, FNM_NOMATCH }, 83 { "[?]", "x", 0, FNM_NOMATCH }, 84 { "*[*]*", "foo*foo", 0, 0 }, 85 { "*[*]*", "foo", 0, FNM_NOMATCH }, 86 { "[0-9]", "0", 0, 0 }, 87 { "[0-9]", "5", 0, 0 }, 88 { "[0-9]", "9", 0, 0 }, 89 { "[0-9]", "/", 0, FNM_NOMATCH }, 90 { "[0-9]", ":", 0, FNM_NOMATCH }, 91 { "[0-9]", "*", 0, FNM_NOMATCH }, 92 { "[!0-9]", "0", 0, FNM_NOMATCH }, 93 { "[!0-9]", "5", 0, FNM_NOMATCH }, 94 { "[!0-9]", "9", 0, FNM_NOMATCH }, 95 { "[!0-9]", "/", 0, 0 }, 96 { "[!0-9]", ":", 0, 0 }, 97 { "[!0-9]", "*", 0, 0 }, 98 { "*[0-9]", "a0", 0, 0 }, 99 { "*[0-9]", "a5", 0, 0 }, 100 { "*[0-9]", "a9", 0, 0 }, 101 { "*[0-9]", "a/", 0, FNM_NOMATCH }, 102 { "*[0-9]", "a:", 0, FNM_NOMATCH }, 103 { "*[0-9]", "a*", 0, FNM_NOMATCH }, 104 { "*[!0-9]", "a0", 0, FNM_NOMATCH }, 105 { "*[!0-9]", "a5", 0, FNM_NOMATCH }, 106 { "*[!0-9]", "a9", 0, FNM_NOMATCH }, 107 { "*[!0-9]", "a/", 0, 0 }, 108 { "*[!0-9]", "a:", 0, 0 }, 109 { "*[!0-9]", "a*", 0, 0 }, 110 { "*[0-9]", "a00", 0, 0 }, 111 { "*[0-9]", "a55", 0, 0 }, 112 { "*[0-9]", "a99", 0, 0 }, 113 { "*[0-9]", "a0a0", 0, 0 }, 114 { "*[0-9]", "a5a5", 0, 0 }, 115 { "*[0-9]", "a9a9", 0, 0 }, 116 { "\\*", "*", 0, 0 }, 117 { "\\?", "?", 0, 0 }, 118 { "\\[x]", "[x]", 0, 0 }, 119 { "\\[", "[", 0, 0 }, 120 { "\\\\", "\\", 0, 0 }, 121 { "*\\**", "foo*foo", 0, 0 }, 122 { "*\\**", "foo", 0, FNM_NOMATCH }, 123 { "*\\\\*", "foo\\foo", 0, 0 }, 124 { "*\\\\*", "foo", 0, FNM_NOMATCH }, 125 { "\\(", "(", 0, 0 }, 126 { "\\a", "a", 0, 0 }, 127 { "\\*", "a", 0, FNM_NOMATCH }, 128 { "\\?", "a", 0, FNM_NOMATCH }, 129 { "\\*", "\\*", 0, FNM_NOMATCH }, 130 { "\\?", "\\?", 0, FNM_NOMATCH }, 131 { "\\[x]", "\\[x]", 0, FNM_NOMATCH }, 132 { "\\[x]", "\\x", 0, FNM_NOMATCH }, 133 { "\\[", "\\[", 0, FNM_NOMATCH }, 134 { "\\(", "\\(", 0, FNM_NOMATCH }, 135 { "\\a", "\\a", 0, FNM_NOMATCH }, 136 { "\\", "\\", 0, FNM_NOMATCH }, 137 { "\\", "", 0, FNM_NOMATCH }, 138 { "\\*", "\\*", FNM_NOESCAPE, 0 }, 139 { "\\?", "\\?", FNM_NOESCAPE, 0 }, 140 { "\\", "\\", FNM_NOESCAPE, 0 }, 141 { "\\\\", "\\", FNM_NOESCAPE, FNM_NOMATCH }, 142 { "\\\\", "\\\\", FNM_NOESCAPE, 0 }, 143 { "*\\*", "foo\\foo", FNM_NOESCAPE, 0 }, 144 { "*\\*", "foo", FNM_NOESCAPE, FNM_NOMATCH }, 145 { "*", ".", FNM_PERIOD, FNM_NOMATCH }, 146 { "?", ".", FNM_PERIOD, FNM_NOMATCH }, 147 { ".*", ".", 0, 0 }, 148 { ".*", "..", 0, 0 }, 149 { ".*", ".a", 0, 0 }, 150 { "[0-9]", ".", FNM_PERIOD, FNM_NOMATCH }, 151 { "a*", "a.", 0, 0 }, 152 { "a/a", "a/a", FNM_PATHNAME, 0 }, 153 { "a/*", "a/a", FNM_PATHNAME, 0 }, 154 { "*/a", "a/a", FNM_PATHNAME, 0 }, 155 { "*/*", "a/a", FNM_PATHNAME, 0 }, 156 { "a*b/*", "abbb/x", FNM_PATHNAME, 0 }, 157 { "a*b/*", "abbb/.x", FNM_PATHNAME, 0 }, 158 { "*", "a/a", FNM_PATHNAME, FNM_NOMATCH }, 159 { "*/*", "a/a/a", FNM_PATHNAME, FNM_NOMATCH }, 160 { "b/*", "b/.x", FNM_PATHNAME | FNM_PERIOD, FNM_NOMATCH }, 161 { "b*/*", "a/.x", FNM_PATHNAME | FNM_PERIOD, FNM_NOMATCH }, 162 { "b/.*", "b/.x", FNM_PATHNAME | FNM_PERIOD, 0 }, 163 { "b*/.*", "b/.x", FNM_PATHNAME | FNM_PERIOD, 0 }, 164 { "a", "A", FNM_CASEFOLD, 0 }, 165 { "A", "a", FNM_CASEFOLD, 0 }, 166 { "[a]", "A", FNM_CASEFOLD, 0 }, 167 { "[A]", "a", FNM_CASEFOLD, 0 }, 168 { "a", "b", FNM_CASEFOLD, FNM_NOMATCH }, 169 { "a", "a/b", FNM_PATHNAME, FNM_NOMATCH }, 170 { "*", "a/b", FNM_PATHNAME, FNM_NOMATCH }, 171 { "*b", "a/b", FNM_PATHNAME, FNM_NOMATCH }, 172 { "a", "a/b", FNM_PATHNAME | FNM_LEADING_DIR, 0 }, 173 { "*", "a/b", FNM_PATHNAME | FNM_LEADING_DIR, 0 }, 174 { "*", ".a/b", FNM_PATHNAME | FNM_LEADING_DIR, 0 }, 175 { "*a", ".a/b", FNM_PATHNAME | FNM_LEADING_DIR, 0 }, 176 { "*", ".a/b", FNM_PATHNAME | FNM_PERIOD | FNM_LEADING_DIR, 177 FNM_NOMATCH }, 178 { "*a", ".a/b", FNM_PATHNAME | FNM_PERIOD | FNM_LEADING_DIR, 179 FNM_NOMATCH }, 180 { "a*b/*", "abbb/.x", FNM_PATHNAME | FNM_PERIOD, FNM_NOMATCH }, 181 }; 182 183 static const char * 184 flags_to_string(int flags) 185 { 186 static const int flagvalues[] = { FNM_NOESCAPE, FNM_PATHNAME, 187 FNM_PERIOD, FNM_LEADING_DIR, FNM_CASEFOLD, 0 }; 188 static const char flagnames[] = "FNM_NOESCAPE\0FNM_PATHNAME\0" 189 "FNM_PERIOD\0FNM_LEADING_DIR\0FNM_CASEFOLD\0"; 190 static char result[sizeof (flagnames) + 3 * sizeof (int) + 2]; 191 char *p; 192 size_t i, len; 193 const char *fp; 194 195 p = result; 196 fp = flagnames; 197 for (i = 0; flagvalues[i] != 0; i++) { 198 len = strlen(fp); 199 if (flags & flagvalues[i]) { 200 if (p != result) 201 *p++ = '|'; 202 memcpy(p, fp, len); 203 p += len; 204 flags &= ~flagvalues[i]; 205 } 206 fp += len + 1; 207 } 208 if (p == result) 209 memcpy(p, "0", 2); 210 else if (flags != 0) 211 sprintf(p, "%d", flags); 212 else 213 *p = '\0'; 214 return (result); 215 } 216 217 int 218 main(void) 219 { 220 size_t i; 221 int flags, result; 222 int ret = 0; 223 struct testcase *t; 224 225 for (i = 0; i < sizeof (testcases) / sizeof (struct testcase); i++) { 226 t = &testcases[i]; 227 flags = t->flags; 228 do { 229 result = fnmatch(t->pattern, t->string, flags); 230 if (result != t->result) 231 break; 232 if (strchr(t->pattern, '\\') == NULL && 233 !(flags & FNM_NOESCAPE)) { 234 flags |= FNM_NOESCAPE; 235 result = fnmatch(t->pattern, t->string, flags); 236 if (result != t->result) 237 break; 238 flags = t->flags; 239 } 240 if (strchr(t->pattern, '\\') != NULL && 241 strchr(t->string, '\\') == NULL && 242 t->result == FNM_NOMATCH && 243 !(flags & (FNM_NOESCAPE | FNM_LEADING_DIR))) { 244 flags |= FNM_NOESCAPE; 245 result = fnmatch(t->pattern, t->string, flags); 246 if (result != t->result) 247 break; 248 flags = t->flags; 249 } 250 if ((t->string[0] != '.' || t->pattern[0] == '.' || 251 t->result == FNM_NOMATCH) && 252 !(flags & (FNM_PATHNAME | FNM_PERIOD))) { 253 flags |= FNM_PERIOD; 254 result = fnmatch(t->pattern, t->string, flags); 255 if (result != t->result) 256 break; 257 flags = t->flags; 258 } 259 if ((strchr(t->string, '/') == NULL || 260 t->result == FNM_NOMATCH) && 261 !(flags & FNM_PATHNAME)) { 262 flags |= FNM_PATHNAME; 263 result = fnmatch(t->pattern, t->string, flags); 264 if (result != t->result) 265 break; 266 flags = t->flags; 267 } 268 if ((((t->string[0] != '.' || t->pattern[0] == '.') && 269 strstr(t->string, "/.") == NULL) || 270 t->result == FNM_NOMATCH) && 271 flags & FNM_PATHNAME && !(flags & FNM_PERIOD)) { 272 flags |= FNM_PERIOD; 273 result = fnmatch(t->pattern, t->string, flags); 274 if (result != t->result) 275 break; 276 flags = t->flags; 277 } 278 if ((((t->string[0] != '.' || t->pattern[0] == '.') && 279 strchr(t->string, '/') == NULL) || 280 t->result == FNM_NOMATCH) && 281 !(flags & (FNM_PATHNAME | FNM_PERIOD))) { 282 flags |= FNM_PATHNAME | FNM_PERIOD; 283 result = fnmatch(t->pattern, t->string, flags); 284 if (result != t->result) 285 break; 286 flags = t->flags; 287 } 288 if ((strchr(t->string, '/') == NULL || 289 t->result == 0) && 290 !(flags & FNM_LEADING_DIR)) { 291 flags |= FNM_LEADING_DIR; 292 result = fnmatch(t->pattern, t->string, flags); 293 if (result != t->result) 294 break; 295 flags = t->flags; 296 } 297 if (t->result == 0 && !(flags & FNM_CASEFOLD)) { 298 flags |= FNM_CASEFOLD; 299 result = fnmatch(t->pattern, t->string, flags); 300 if (result != t->result) 301 break; 302 flags = t->flags; 303 } 304 if (strchr(t->pattern, '\\') == NULL && 305 t->result == 0 && 306 !(flags & (FNM_NOESCAPE | FNM_CASEFOLD))) { 307 flags |= FNM_NOESCAPE | FNM_CASEFOLD; 308 result = fnmatch(t->pattern, t->string, flags); 309 if (result != t->result) 310 break; 311 flags = t->flags; 312 } 313 } while (0); 314 315 if (result != t->result) { 316 printf("fnmatch(\"%s\", \"%s\", %s) != %d (was %d)\n", 317 t->pattern, t->string, flags_to_string(flags), 318 t->result, result); 319 ret = 1; 320 } 321 } 322 323 return (ret); 324 } 325