| 1 | /*************************************************************************** |
| 2 | * _ _ ____ _ |
| 3 | * Project ___| | | | _ \| | |
| 4 | * / __| | | | |_) | | |
| 5 | * | (__| |_| | _ <| |___ |
| 6 | * \___|\___/|_| \_\_____| |
| 7 | * |
| 8 | * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. |
| 9 | * |
| 10 | * This software is licensed as described in the file COPYING, which |
| 11 | * you should have received as part of this distribution. The terms |
| 12 | * are also available at https://curl.haxx.se/docs/copyright.html. |
| 13 | * |
| 14 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
| 15 | * copies of the Software, and permit persons to whom the Software is |
| 16 | * furnished to do so, under the terms of the COPYING file. |
| 17 | * |
| 18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| 19 | * KIND, either express or implied. |
| 20 | * |
| 21 | ***************************************************************************/ |
| 22 | #include "curlcheck.h" |
| 23 | |
| 24 | #include "curl_fnmatch.h" |
| 25 | |
| 26 | static CURLcode unit_setup(void) |
| 27 | { |
| 28 | return CURLE_OK; |
| 29 | } |
| 30 | |
| 31 | static void unit_stop(void) |
| 32 | { |
| 33 | } |
| 34 | |
| 35 | #ifndef CURL_DISABLE_FTP |
| 36 | |
| 37 | /* |
| 38 | CURL_FNMATCH_MATCH 0 |
| 39 | CURL_FNMATCH_NOMATCH 1 |
| 40 | CURL_FNMATCH_FAIL 2 |
| 41 | */ |
| 42 | |
| 43 | #define MATCH CURL_FNMATCH_MATCH |
| 44 | #define NOMATCH CURL_FNMATCH_NOMATCH |
| 45 | |
| 46 | #define LINUX_DIFFER 0x80 |
| 47 | #define LINUX_SHIFT 8 |
| 48 | #define LINUX_MATCH ((CURL_FNMATCH_MATCH << LINUX_SHIFT) | LINUX_DIFFER) |
| 49 | #define LINUX_NOMATCH ((CURL_FNMATCH_NOMATCH << LINUX_SHIFT) | LINUX_DIFFER) |
| 50 | #define LINUX_FAIL ((CURL_FNMATCH_FAIL << LINUX_SHIFT) | LINUX_DIFFER) |
| 51 | |
| 52 | #define MAC_DIFFER 0x40 |
| 53 | #define MAC_SHIFT 16 |
| 54 | #define MAC_MATCH ((CURL_FNMATCH_MATCH << MAC_SHIFT) | MAC_DIFFER) |
| 55 | #define MAC_NOMATCH ((CURL_FNMATCH_NOMATCH << MAC_SHIFT) | MAC_DIFFER) |
| 56 | #define MAC_FAIL ((CURL_FNMATCH_FAIL << MAC_SHIFT) | MAC_DIFFER) |
| 57 | |
| 58 | struct testcase { |
| 59 | const char *pattern; |
| 60 | const char *string; |
| 61 | int result; |
| 62 | }; |
| 63 | |
| 64 | static const struct testcase tests[] = { |
| 65 | /* brackets syntax */ |
| 66 | {"*[*[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" |
| 67 | "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" |
| 68 | "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\001\177[[[[[[[[[[[[[[[[[[[[[" , |
| 69 | "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" |
| 70 | "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" |
| 71 | "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" , |
| 72 | NOMATCH|MAC_FAIL}, |
| 73 | |
| 74 | { "\\[" , "[" , MATCH }, |
| 75 | { "[" , "[" , NOMATCH|LINUX_MATCH|MAC_FAIL}, |
| 76 | { "[]" , "[]" , NOMATCH|LINUX_MATCH|MAC_FAIL}, |
| 77 | { "[][]" , "[" , MATCH }, |
| 78 | { "[][]" , "]" , MATCH }, |
| 79 | { "[[]" , "[" , MATCH }, |
| 80 | { "[[[]" , "[" , MATCH }, |
| 81 | { "[[[[]" , "[" , MATCH }, |
| 82 | { "[[[[]" , "[" , MATCH }, |
| 83 | |
| 84 | { "[][[]" , "]" , MATCH }, |
| 85 | { "[][[[]" , "[" , MATCH }, |
| 86 | { "[[]" , "]" , NOMATCH }, |
| 87 | |
| 88 | { "[a@]" , "a" , MATCH }, |
| 89 | |
| 90 | { "[a-z]" , "a" , MATCH }, |
| 91 | { "[a-z]" , "A" , NOMATCH }, |
| 92 | { "?[a-z]" , "?Z" , NOMATCH }, |
| 93 | { "[A-Z]" , "C" , MATCH }, |
| 94 | { "[A-Z]" , "c" , NOMATCH }, |
| 95 | { "[0-9]" , "7" , MATCH }, |
| 96 | { "[7-8]" , "7" , MATCH }, |
| 97 | { "[7-]" , "7" , MATCH }, |
| 98 | { "[7-]" , "-" , MATCH }, |
| 99 | { "[7-]" , "[" , NOMATCH }, |
| 100 | { "[a-bA-F]" , "F" , MATCH }, |
| 101 | { "[a-bA-B9]" , "9" , MATCH }, |
| 102 | { "[a-bA-B98]" , "8" , MATCH }, |
| 103 | { "[a-bA-B98]" , "C" , NOMATCH }, |
| 104 | { "[a-bA-Z9]" , "F" , MATCH }, |
| 105 | { "[a-bA-Z9]ero*" , "Zero chance." , MATCH }, |
| 106 | { "S[a-][x]opho*" , "Saxophone" , MATCH }, |
| 107 | { "S[a-][x]opho*" , "SaXophone" , NOMATCH }, |
| 108 | { "S[a-][x]*.txt" , "S-x.txt" , MATCH }, |
| 109 | { "[\\a-\\b]" , "a" , MATCH }, |
| 110 | { "[\\a-\\b]" , "b" , MATCH }, |
| 111 | { "[?*[][?*[][?*[]" , "?*[" , MATCH }, |
| 112 | { "[][?*-]" , "]" , MATCH }, |
| 113 | { "[][?*-]" , "[" , MATCH }, |
| 114 | { "[][?*-]" , "?" , MATCH }, |
| 115 | { "[][?*-]" , "*" , MATCH }, |
| 116 | { "[][?*-]" , "-" , MATCH }, |
| 117 | { "[]?*-]" , "-" , MATCH }, |
| 118 | { "[\xFF]" , "\xFF" , MATCH|LINUX_FAIL|MAC_FAIL}, |
| 119 | { "?/b/c" , "a/b/c" , MATCH }, |
| 120 | { "^_{}~" , "^_{}~" , MATCH }, |
| 121 | { "!#%+,-./01234567889" , "!#%+,-./01234567889" , MATCH }, |
| 122 | { "PQRSTUVWXYZ]abcdefg" , "PQRSTUVWXYZ]abcdefg" , MATCH }, |
| 123 | { ":;=@ABCDEFGHIJKLMNO" , ":;=@ABCDEFGHIJKLMNO" , MATCH }, |
| 124 | |
| 125 | /* negate */ |
| 126 | { "[!a]" , "b" , MATCH }, |
| 127 | { "[!a]" , "a" , NOMATCH }, |
| 128 | { "[^a]" , "b" , MATCH }, |
| 129 | { "[^a]" , "a" , NOMATCH }, |
| 130 | { "[^a-z0-9A-Z]" , "a" , NOMATCH }, |
| 131 | { "[^a-z0-9A-Z]" , "-" , MATCH }, |
| 132 | { "curl[!a-z]lib" , "curl lib" , MATCH }, |
| 133 | { "curl[! ]lib" , "curl lib" , NOMATCH }, |
| 134 | { "[! ][ ]" , " " , NOMATCH }, |
| 135 | { "[! ][ ]" , "a " , MATCH }, |
| 136 | { "*[^a].t?t" , "a.txt" , NOMATCH }, |
| 137 | { "*[^a].t?t" , "ba.txt" , NOMATCH }, |
| 138 | { "*[^a].t?t" , "ab.txt" , MATCH }, |
| 139 | { "*[^a]" , "" , NOMATCH }, |
| 140 | { "[!\xFF]" , "" , NOMATCH|LINUX_FAIL}, |
| 141 | { "[!\xFF]" , "\xFF" , NOMATCH|LINUX_FAIL|MAC_FAIL}, |
| 142 | { "[!\xFF]" , "a" , MATCH|LINUX_FAIL|MAC_FAIL}, |
| 143 | { "[!?*[]" , "?" , NOMATCH }, |
| 144 | { "[!!]" , "!" , NOMATCH }, |
| 145 | { "[!!]" , "x" , MATCH }, |
| 146 | |
| 147 | { "[[:alpha:]]" , "a" , MATCH }, |
| 148 | { "[[:alpha:]]" , "9" , NOMATCH }, |
| 149 | { "[[:alnum:]]" , "a" , MATCH }, |
| 150 | { "[[:alnum:]]" , "[" , NOMATCH }, |
| 151 | { "[[:alnum:]]" , "]" , NOMATCH }, |
| 152 | { "[[:alnum:]]" , "9" , MATCH }, |
| 153 | { "[[:digit:]]" , "9" , MATCH }, |
| 154 | { "[[:xdigit:]]" , "9" , MATCH }, |
| 155 | { "[[:xdigit:]]" , "F" , MATCH }, |
| 156 | { "[[:xdigit:]]" , "G" , NOMATCH }, |
| 157 | { "[[:upper:]]" , "U" , MATCH }, |
| 158 | { "[[:upper:]]" , "u" , NOMATCH }, |
| 159 | { "[[:lower:]]" , "l" , MATCH }, |
| 160 | { "[[:lower:]]" , "L" , NOMATCH }, |
| 161 | { "[[:print:]]" , "L" , MATCH }, |
| 162 | { "[[:print:]]" , "\10" , NOMATCH }, |
| 163 | { "[[:print:]]" , "\10" , NOMATCH }, |
| 164 | { "[[:space:]]" , " " , MATCH }, |
| 165 | { "[[:space:]]" , "x" , NOMATCH }, |
| 166 | { "[[:graph:]]" , " " , NOMATCH }, |
| 167 | { "[[:graph:]]" , "x" , MATCH }, |
| 168 | { "[[:blank:]]" , "\t" , MATCH }, |
| 169 | { "[[:blank:]]" , " " , MATCH }, |
| 170 | { "[[:blank:]]" , "\r" , NOMATCH }, |
| 171 | { "[^[:blank:]]" , "\t" , NOMATCH }, |
| 172 | { "[^[:print:]]" , "\10" , MATCH }, |
| 173 | { "[[:lower:]][[:lower:]]" , "ll" , MATCH }, |
| 174 | { "[[:foo:]]" , "bar" , NOMATCH|MAC_FAIL}, |
| 175 | { "[[:foo:]]" , "f]" , MATCH|LINUX_NOMATCH|MAC_FAIL}, |
| 176 | |
| 177 | { "Curl[[:blank:]];-)" , "Curl ;-)" , MATCH }, |
| 178 | { "*[[:blank:]]*" , " " , MATCH }, |
| 179 | { "*[[:blank:]]*" , "" , NOMATCH }, |
| 180 | { "*[[:blank:]]*" , "hi, im_Pavel" , MATCH }, |
| 181 | |
| 182 | /* common using */ |
| 183 | { "filename.dat" , "filename.dat" , MATCH }, |
| 184 | { "*curl*" , "lets use curl!!" , MATCH }, |
| 185 | { "filename.txt" , "filename.dat" , NOMATCH }, |
| 186 | { "*.txt" , "text.txt" , MATCH }, |
| 187 | { "*.txt" , "a.txt" , MATCH }, |
| 188 | { "*.txt" , ".txt" , MATCH }, |
| 189 | { "*.txt" , "txt" , NOMATCH }, |
| 190 | { "??.txt" , "99.txt" , MATCH }, |
| 191 | { "??.txt" , "a99.txt" , NOMATCH }, |
| 192 | { "?.???" , "a.txt" , MATCH }, |
| 193 | { "*.???" , "somefile.dat" , MATCH }, |
| 194 | { "*.???" , "photo.jpeg" , NOMATCH }, |
| 195 | { ".*" , ".htaccess" , MATCH }, |
| 196 | { ".*" , "." , MATCH }, |
| 197 | { ".*" , ".." , MATCH }, |
| 198 | |
| 199 | /* many stars => one star */ |
| 200 | { "**.txt" , "text.txt" , MATCH }, |
| 201 | { "***.txt" , "t.txt" , MATCH }, |
| 202 | { "****.txt" , ".txt" , MATCH }, |
| 203 | |
| 204 | /* empty string or pattern */ |
| 205 | { "" , "" , MATCH }, |
| 206 | { "" , "hello" , NOMATCH }, |
| 207 | { "file" , "" , NOMATCH }, |
| 208 | { "?" , "" , NOMATCH }, |
| 209 | { "*" , "" , MATCH }, |
| 210 | { "x" , "" , NOMATCH }, |
| 211 | |
| 212 | /* backslash */ |
| 213 | { "\\" , "\\" , MATCH|LINUX_NOMATCH}, |
| 214 | { "\\\\" , "\\" , MATCH }, |
| 215 | { "\\\\" , "\\\\" , NOMATCH }, |
| 216 | { "\\?" , "?" , MATCH }, |
| 217 | { "\\*" , "*" , MATCH }, |
| 218 | { "?.txt" , "?.txt" , MATCH }, |
| 219 | { "*.txt" , "*.txt" , MATCH }, |
| 220 | { "\\?.txt" , "?.txt" , MATCH }, |
| 221 | { "\\*.txt" , "*.txt" , MATCH }, |
| 222 | { "\\?.txt" , "x.txt" , NOMATCH }, |
| 223 | { "\\*.txt" , "x.txt" , NOMATCH }, |
| 224 | { "\\*\\\\.txt" , "*\\.txt" , MATCH }, |
| 225 | { "*\\**\\?*\\\\*" , "cc*cc?cccc" , NOMATCH }, |
| 226 | { "*\\?*\\**" , "cc?cc" , NOMATCH }, |
| 227 | { "\\\"\\$\\&\\'\\(\\)" , "\"$&'()" , MATCH }, |
| 228 | { "\\*\\?\\[\\\\\\`\\|" , "*?[\\`|" , MATCH }, |
| 229 | { "[\\a\\b]c" , "ac" , MATCH }, |
| 230 | { "[\\a\\b]c" , "bc" , MATCH }, |
| 231 | { "[\\a\\b]d" , "bc" , NOMATCH }, |
| 232 | { "[a-bA-B\\?]" , "?" , MATCH }, |
| 233 | { "cu[a-ab-b\\r]l" , "curl" , MATCH }, |
| 234 | { "[\\a-z]" , "c" , MATCH }, |
| 235 | |
| 236 | { "?*?*?.*?*" , "abc.c" , MATCH }, |
| 237 | { "?*?*?.*?*" , "abcc" , NOMATCH }, |
| 238 | { "?*?*?.*?*" , "abc." , NOMATCH }, |
| 239 | { "?*?*?.*?*" , "abc.c++" , MATCH }, |
| 240 | { "?*?*?.*?*" , "abcdef.c++" , MATCH }, |
| 241 | { "?*?*?.?" , "abcdef.c" , MATCH }, |
| 242 | { "?*?*?.?" , "abcdef.cd" , NOMATCH }, |
| 243 | |
| 244 | { "Lindmätarv" , "Lindmätarv" , MATCH }, |
| 245 | |
| 246 | { "" , "" , MATCH}, |
| 247 | {"**]*[*[\x13]**[*\x13)]*]*[**[*\x13~r-]*]**[.*]*[\xe3\xe3\xe3\xe3\xe3\xe3" |
| 248 | "\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3" |
| 249 | "\xe3\xe3\xe3\xe3\xe3*[\x13]**[*\x13)]*]*[*[\x13]*[~r]*]*\xba\x13\xa6~b-]*" , |
| 250 | "a" , NOMATCH|LINUX_FAIL} |
| 251 | }; |
| 252 | |
| 253 | static const char *ret2name(int i) |
| 254 | { |
| 255 | switch(i) { |
| 256 | case 0: |
| 257 | return "MATCH" ; |
| 258 | case 1: |
| 259 | return "NOMATCH" ; |
| 260 | case 2: |
| 261 | return "FAIL" ; |
| 262 | default: |
| 263 | return "unknown" ; |
| 264 | } |
| 265 | /* not reached */ |
| 266 | } |
| 267 | |
| 268 | enum system { |
| 269 | SYSTEM_CUSTOM, |
| 270 | SYSTEM_LINUX, |
| 271 | SYSTEM_MACOS |
| 272 | }; |
| 273 | |
| 274 | UNITTEST_START |
| 275 | { |
| 276 | int testnum = sizeof(tests) / sizeof(struct testcase); |
| 277 | int i; |
| 278 | enum system machine; |
| 279 | |
| 280 | #ifdef HAVE_FNMATCH |
| 281 | if(strstr(OS, "apple" ) || strstr(OS, "darwin" )) { |
| 282 | machine = SYSTEM_MACOS; |
| 283 | } |
| 284 | else |
| 285 | machine = SYSTEM_LINUX; |
| 286 | printf("Tested with system fnmatch(), %s-style\n" , |
| 287 | machine == SYSTEM_LINUX ? "linux" : "mac" ); |
| 288 | #else |
| 289 | printf("Tested with custom fnmatch()\n" ); |
| 290 | machine = SYSTEM_CUSTOM; |
| 291 | #endif |
| 292 | |
| 293 | for(i = 0; i < testnum; i++) { |
| 294 | int result = tests[i].result; |
| 295 | int rc = Curl_fnmatch(NULL, tests[i].pattern, tests[i].string); |
| 296 | if(result & (LINUX_DIFFER|MAC_DIFFER)) { |
| 297 | if((result & LINUX_DIFFER) && (machine == SYSTEM_LINUX)) |
| 298 | result >>= LINUX_SHIFT; |
| 299 | else if((result & MAC_DIFFER) && (machine == SYSTEM_MACOS)) |
| 300 | result >>= MAC_SHIFT; |
| 301 | result &= 0x03; /* filter off all high bits */ |
| 302 | } |
| 303 | if(rc != result) { |
| 304 | printf("Curl_fnmatch(\"%s\", \"%s\") should return %s (returns %s)" |
| 305 | " [%d]\n" , |
| 306 | tests[i].pattern, tests[i].string, ret2name(result), |
| 307 | ret2name(rc), i); |
| 308 | fail("pattern mismatch" ); |
| 309 | } |
| 310 | } |
| 311 | } |
| 312 | UNITTEST_STOP |
| 313 | |
| 314 | #else |
| 315 | |
| 316 | UNITTEST_START |
| 317 | { |
| 318 | /* nothing to do, just fail */ |
| 319 | return 1; |
| 320 | } |
| 321 | UNITTEST_STOP |
| 322 | |
| 323 | #endif |
| 324 | |