96b8e06dec19ae207b54eec247dd44f56bd100f3
[libc-test] / src / functional / fnmatch.c
1 #define _GNU_SOURCE 1
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <fnmatch.h>
5 #include <unistd.h>
6 #include "test.h"
7
8 /* adapted from dietlibc's test-newfnmatch.c */
9
10 /* xlat / printflags adapted from http://www.liacs.nl/~wichert/strace/ */
11 #define FLAG(f) { f, #f }
12
13 struct xlat {
14         int val;
15         char *str;
16 } fnmatch_flags[] = {
17         FLAG(FNM_NOESCAPE),
18         FLAG(FNM_PATHNAME),
19         FLAG(FNM_PERIOD),
20 #ifdef FNM_CASEFOLD
21         FLAG(FNM_CASEFOLD),
22 #endif
23         {0, NULL},
24 };
25
26 static char *flagstr(const struct xlat *map, int flags)
27 {
28         static char buf[1000];
29         char *sep;
30         int n;
31
32         if (!flags) {
33                 sprintf(buf, "0");
34                 return buf;
35         }
36         n = 0;
37         sep = "";
38         for (; map->str; map++) {
39                 if (map->val && (flags & map->val) == map->val) {
40                         n += sprintf(buf+n, "%s%s", sep, map->str);
41                         sep = "|";
42                         flags &= ~(map->val);
43                 }
44         }
45         if (flags)
46                 sprintf(buf, "%sunknown=%#x", sep, flags);
47         return buf;
48 }
49
50 /* tests harness adapted from glibc testfnm.c */
51 struct {
52         const char *pattern;
53         const char *string;
54         int flags;
55         int expected;
56 } tests[] = {
57         /* begin dietlibc tests */
58         { "*.c", "foo.c", 0, 0 },
59         { "*.c", ".c", 0, 0 },
60         { "*.a", "foo.c", 0, FNM_NOMATCH },
61         { "*.c", ".foo.c", 0, 0 },
62         { "*.c", ".foo.c", FNM_PERIOD, FNM_NOMATCH },
63         { "*.c", "foo.c", FNM_PERIOD, 0 },
64         { "a\\*.c", "a*.c", FNM_NOESCAPE, FNM_NOMATCH },
65         { "a\\*.c", "ax.c", 0, FNM_NOMATCH },
66         { "a[xy].c", "ax.c", 0, 0 },
67         { "a[!y].c", "ax.c", 0, 0 },
68         { "a[a/z]*.c", "a/x.c", FNM_PATHNAME, FNM_NOMATCH },
69         { "a/*.c", "a/x.c", FNM_PATHNAME, 0 },
70         { "a*.c", "a/x.c", FNM_PATHNAME, FNM_NOMATCH },
71         { "*/foo", "/foo", FNM_PATHNAME, 0 },
72         { "-O[01]", "-O1", 0, 0 },
73         { "[[?*\\]", "\\", 0, 0 },
74         { "[]?*\\]", "]", 0, 0 },
75         /* initial right-bracket tests */
76         { "[!]a-]", "b", 0, 0 },
77         { "[]-_]", "^", 0, 0 }, /* range: ']', '^', '_' */
78         { "[!]-_]", "X", 0, 0 },
79         { "??", "-", 0, FNM_NOMATCH },
80         /* begin glibc tests */
81         { "*LIB*", "lib", FNM_PERIOD, FNM_NOMATCH },
82         { "a[/]b", "a/b", 0, 0 },
83         { "a[/]b", "a/b", FNM_PATHNAME, FNM_NOMATCH },
84         { "[a-z]/[a-z]", "a/b", 0, 0 },
85         { "*", "a/b", FNM_PATHNAME, FNM_NOMATCH },
86         { "*[/]b", "a/b", FNM_PATHNAME, FNM_NOMATCH },
87         { "*[b]", "a/b", FNM_PATHNAME, FNM_NOMATCH },
88         { "[*]/b", "a/b", 0, FNM_NOMATCH },
89         { "[*]/b", "*/b", 0, 0 },
90         { "[?]/b", "a/b", 0, FNM_NOMATCH },
91         { "[?]/b", "?/b", 0, 0 },
92         { "[[a]/b", "a/b", 0, 0 },
93         { "[[a]/b", "[/b", 0, 0 },
94         { "\\*/b", "a/b", 0, FNM_NOMATCH },
95         { "\\*/b", "*/b", 0, 0 },
96         { "\\?/b", "a/b", 0, FNM_NOMATCH },
97         { "\\?/b", "?/b", 0, 0 },
98         { "[/b", "[/b", 0, 0 },
99         { "\\[/b", "[/b", 0, 0 },
100         { "??""/b", "aa/b", 0, 0 },
101         { "???b", "aa/b", 0, 0 },
102         { "???b", "aa/b", FNM_PATHNAME, FNM_NOMATCH },
103         { "?a/b", ".a/b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
104         { "a/?b", "a/.b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
105         { "*a/b", ".a/b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
106         { "a/*b", "a/.b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
107         { "[.]a/b", ".a/b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
108         { "a/[.]b", "a/.b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
109         { "*/?", "a/b", FNM_PATHNAME|FNM_PERIOD, 0 },
110         { "?/*", "a/b", FNM_PATHNAME|FNM_PERIOD, 0 },
111         { ".*/?", ".a/b", FNM_PATHNAME|FNM_PERIOD, 0 },
112         { "*/.?", "a/.b", FNM_PATHNAME|FNM_PERIOD, 0 },
113         { "*/*", "a/.b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
114         { "*?*/*", "a/.b", FNM_PERIOD, 0 },
115         { "*[.]/b", "a./b", FNM_PATHNAME|FNM_PERIOD, 0 },
116         { "*[[:alpha:]]/*[[:alnum:]]", "a/b", FNM_PATHNAME, 0 },
117         /* These three tests should result in error according to SUSv3.
118          * See XCU 2.13.1, XBD 9.3.5, & fnmatch() */
119         { "*[![:digit:]]*/[![:d-d]", "a/b", FNM_PATHNAME, -FNM_NOMATCH },
120         { "*[![:digit:]]*/[[:d-d]", "a/[", FNM_PATHNAME, -FNM_NOMATCH },
121         { "*[![:digit:]]*/[![:d-d]", "a/[", FNM_PATHNAME, -FNM_NOMATCH },
122         { "a?b", "a.b", FNM_PATHNAME|FNM_PERIOD, 0 },
123         { "a*b", "a.b", FNM_PATHNAME|FNM_PERIOD, 0 },
124         { "a[.]b", "a.b", FNM_PATHNAME|FNM_PERIOD, 0 },
125
126         /* posix 2008 is unclear about these cases */
127         { "\\", "\\", 0, 0 },
128         { "\\", "", 0, FNM_NOMATCH },
129
130         /* musl bug fixed in da0fcdb8e913ca7cdf8931328f2b37e93309b2c5 */
131         { "/", "\0", FNM_PATHNAME, FNM_NOMATCH },
132         /* musl bug fixed in 6ec82a3b58ee1b873ff0dfad8fa9d41c3d25dcc0 */
133         { "\\/", "/", FNM_PATHNAME, 0 },
134
135 #ifdef FNM_CASEFOLD
136         { "a", "A", FNM_CASEFOLD, 0 },
137         { "aaAA", "AaAa", FNM_CASEFOLD, 0 },
138         { "[a]", "A", FNM_CASEFOLD, 0 },
139         { "[!a]", "A", FNM_CASEFOLD, FNM_NOMATCH },
140         { "[!A-C]", "b", FNM_CASEFOLD, FNM_NOMATCH },
141         { "[!a-c]", "B", FNM_CASEFOLD, FNM_NOMATCH },
142         { "[!a-c]", "d", FNM_CASEFOLD, 0 },
143 #endif
144 };
145
146 int main(void)
147 {
148         int i;
149
150         for (i = 0; i < sizeof(tests) / sizeof(*tests); i++) {
151                 int r, x;
152
153                 r = fnmatch(tests[i].pattern, tests[i].string, tests[i].flags);
154                 x = tests[i].expected;
155                 if (r != x && (r != FNM_NOMATCH || x != -FNM_NOMATCH)) {
156                         t_error("fnmatch(\"%s\", \"%s\", %s) failed, got %d want %d\n",
157                                 tests[i].pattern, tests[i].string,
158                                 flagstr(fnmatch_flags, tests[i].flags),
159                                 r, x);
160                 }
161         }
162         return t_status;
163 }