1 /*
2  * Copyright © 2005-2020 Rich Felker
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 #define _GNU_SOURCE
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <fnmatch.h>
28 #include <unistd.h>
29 
30 /* adapted from dietlibc's test-newfnmatch.c */
31 
32 /*
33  * xlat / printflags adapted from strace
34  * http://www.liacs.nl/~wichert/strace/
35  */
36 
37 #define FLAG(f)	{ f, #f }
38 
39 struct xlat {
40   int val;
41   char *str;
42 } fnmatch_flags[] = {
43   FLAG(FNM_NOESCAPE),
44   FLAG(FNM_PATHNAME),
45   FLAG(FNM_PERIOD),
46   {0,            NULL},
47 };
48 
printflags(const struct xlat * map,int flags)49 static void printflags(const struct xlat *map, int flags) {
50   char * sep;
51 
52   if (! flags) {
53     printf("0");
54     return;
55   }
56 
57   sep = "";
58   for (; map->str; map++) {
59     if (map->val && (flags & map->val) == map->val) {
60       printf("%s%s", sep, map->str);
61       sep = "|";
62       flags &= ~(map->val);
63     }
64   }
65   if (flags) printf("%sunknown=%#x", sep, flags);
66 }
67 
68 /*
69  * tests harness adapted from glibc testfnm.c
70  */
71 struct {
72     const char *pattern;
73     const char *string;
74     int flags;
75     int expected;
76 } tests[] = {
77     /* begin dietlibc tests */
78     { "*.c", "foo.c", 0, 0 },
79     { "*.c", ".c", 0, 0 },
80     { "*.a", "foo.c", 0, FNM_NOMATCH },
81     { "*.c", ".foo.c", 0, 0 },
82     { "*.c", ".foo.c", FNM_PERIOD, FNM_NOMATCH },
83     { "*.c", "foo.c", FNM_PERIOD, 0 },
84     { "a\\*.c", "a*.c", FNM_NOESCAPE, FNM_NOMATCH },
85     { "a\\*.c", "ax.c", 0, FNM_NOMATCH },
86     { "a[xy].c", "ax.c", 0, 0 },
87     { "a[!y].c", "ax.c", 0, 0 },
88     { "a[a/z]*.c", "a/x.c", FNM_PATHNAME, FNM_NOMATCH },
89     { "a/*.c", "a/x.c", FNM_PATHNAME, 0 },
90     { "a*.c", "a/x.c", FNM_PATHNAME, FNM_NOMATCH },
91     { "*/foo", "/foo", FNM_PATHNAME, 0 },
92     { "-O[01]", "-O1", 0, 0 },
93 #ifndef __PICOLIBC__
94     { "[[?*\\]", "\\", 0, 0 },
95     { "[]?*\\]", "]", 0, 0 },
96 #endif
97     /* initial right-bracket tests */
98     { "[!]a-]", "b", 0, 0 },
99     { "[]-_]", "^", 0, 0 },   /* range: ']', '^', '_' */
100     { "[!]-_]", "X", 0, 0 },
101     { "??", "-", 0, FNM_NOMATCH },
102     /* begin glibc tests */
103     { "*LIB*", "lib", FNM_PERIOD, FNM_NOMATCH },
104     { "a[/]b", "a/b", 0, 0 },
105     { "a[/]b", "a/b", FNM_PATHNAME, FNM_NOMATCH },
106     { "[a-z]/[a-z]", "a/b", 0, 0 },
107     { "*", "a/b", FNM_PATHNAME, FNM_NOMATCH },
108     { "*[/]b", "a/b", FNM_PATHNAME, FNM_NOMATCH },
109     { "*[b]", "a/b", FNM_PATHNAME, FNM_NOMATCH },
110     { "[*]/b", "a/b", 0, FNM_NOMATCH },
111     { "[*]/b", "*/b", 0, 0 },
112     { "[?]/b", "a/b", 0, FNM_NOMATCH },
113     { "[?]/b", "?/b", 0, 0 },
114     { "[[a]/b", "a/b", 0, 0 },
115     { "[[a]/b", "[/b", 0, 0 },
116     { "\\*/b", "a/b", 0, FNM_NOMATCH },
117     { "\\*/b", "*/b", 0, 0 },
118     { "\\?/b", "a/b", 0, FNM_NOMATCH },
119     { "\\?/b", "?/b", 0, 0 },
120     { "[/b", "[/b", 0, 0 },
121     { "\\[/b", "[/b", 0, 0 },
122     { "??""/b", "aa/b", 0, 0 },
123     { "???b", "aa/b", 0, 0 },
124     { "???b", "aa/b", FNM_PATHNAME, FNM_NOMATCH },
125     { "?a/b", ".a/b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
126     { "a/?b", "a/.b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
127     { "*a/b", ".a/b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
128     { "a/*b", "a/.b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
129     { "[.]a/b", ".a/b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
130     { "a/[.]b", "a/.b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
131     { "*/?", "a/b", FNM_PATHNAME|FNM_PERIOD, 0 },
132     { "?/*", "a/b", FNM_PATHNAME|FNM_PERIOD, 0 },
133     { ".*/?", ".a/b", FNM_PATHNAME|FNM_PERIOD, 0 },
134     { "*/.?", "a/.b", FNM_PATHNAME|FNM_PERIOD, 0 },
135     { "*/*", "a/.b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
136     { "*?*/*", "a/.b", FNM_PERIOD, 0 },
137     { "*[.]/b", "a./b", FNM_PATHNAME|FNM_PERIOD, 0 },
138 #ifndef __PICOLIBC__
139     { "*[[:alpha:]]/*[[:alnum:]]", "a/b", FNM_PATHNAME, 0 },
140 #endif
141     /* These three tests should result in error according to SUSv3.
142      * See XCU 2.13.1, XBD 9.3.5, & fnmatch() */
143     { "*[![:digit:]]*/[![:d-d]", "a/b", FNM_PATHNAME, -FNM_NOMATCH },
144     { "*[![:digit:]]*/[[:d-d]", "a/[", FNM_PATHNAME, -FNM_NOMATCH },
145     { "*[![:digit:]]*/[![:d-d]", "a/[", FNM_PATHNAME, -FNM_NOMATCH },
146     { "a?b", "a.b", FNM_PATHNAME|FNM_PERIOD, 0 },
147     { "a*b", "a.b", FNM_PATHNAME|FNM_PERIOD, 0 },
148     { "a[.]b", "a.b", FNM_PATHNAME|FNM_PERIOD, 0 },
149 };
150 
test_fnmatch(void)151 int test_fnmatch(void) {
152     int i;
153     unsigned int failed = 0;
154 
155     for (i = 0; i < (int)(sizeof(tests) / sizeof(*tests)); i++) {
156         int r, x;
157 
158         r = fnmatch(tests[i].pattern, tests[i].string, tests[i].flags);
159 	x = tests[i].expected;
160 	if (r != x && (r != FNM_NOMATCH || x != -FNM_NOMATCH)) {
161             failed++;
162             printf("fail - fnmatch(\"%s\", \"%s\", ",
163                     tests[i].pattern, tests[i].string);
164             printflags(fnmatch_flags, tests[i].flags);
165             printf(") => %d (expected %d)\n", r, tests[i].expected);
166 	}
167     }
168 
169     return failed;
170 }
171 
172 #define TEST_NAME fnmatch
173 #include "testcase.h"
174