1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "testutil.h"
18#include "apr_file_info.h"
19#include "apr_fnmatch.h"
20#include "apr_tables.h"
21
22/* XXX NUM_FILES must be equal to the nummber of expected files with a
23 * .txt extension in the data directory at the time testfnmatch
24 * happens to be run (!?!). */
25
26#define NUM_FILES (5)
27
28#define APR_FNM_BITS    15
29#define APR_FNM_FAILBIT 256
30
31#define FAILS_IF(X)     0, X
32#define SUCCEEDS_IF(X)  X, 256
33#define SUCCEEDS        0, 256
34#define FAILS           256, 0
35
36static struct pattern_s {
37    const char *pattern;
38    const char *string;
39    int         require_flags;
40    int         fail_flags;
41} patterns[] = {
42
43/*   Pattern,  String to Test,          Flags to Match  */
44    {"", "test",                        FAILS},
45    {"", "*",                           FAILS},
46    {"test", "*",                       FAILS},
47    {"test", "test",                    SUCCEEDS},
48
49     /* Remember C '\\' is a single backslash in pattern */
50    {"te\\st", "test",                  FAILS_IF(APR_FNM_NOESCAPE)},
51    {"te\\\\st", "te\\st",              FAILS_IF(APR_FNM_NOESCAPE)},
52    {"te\\*t", "te*t",                  FAILS_IF(APR_FNM_NOESCAPE)},
53    {"te\\*t", "test",                  FAILS},
54    {"te\\?t", "te?t",                  FAILS_IF(APR_FNM_NOESCAPE)},
55    {"te\\?t", "test",                  FAILS},
56
57    {"tesT", "test",                    SUCCEEDS_IF(APR_FNM_CASE_BLIND)},
58    {"test", "Test",                    SUCCEEDS_IF(APR_FNM_CASE_BLIND)},
59    {"tEst", "teSt",                    SUCCEEDS_IF(APR_FNM_CASE_BLIND)},
60
61    {"?est", "test",                    SUCCEEDS},
62    {"te?t", "test",                    SUCCEEDS},
63    {"tes?", "test",                    SUCCEEDS},
64    {"test?", "test",                   FAILS},
65
66    {"*", "",                           SUCCEEDS},
67    {"*", "test",                       SUCCEEDS},
68    {"*test", "test",                   SUCCEEDS},
69    {"*est", "test",                    SUCCEEDS},
70    {"*st", "test",                     SUCCEEDS},
71    {"t*t", "test",                     SUCCEEDS},
72    {"te*t", "test",                    SUCCEEDS},
73    {"te*st", "test",                   SUCCEEDS},
74    {"te*", "test",                     SUCCEEDS},
75    {"tes*", "test",                    SUCCEEDS},
76    {"test*", "test",                   SUCCEEDS},
77
78    {".[\\-\\t]", ".t",                 SUCCEEDS},
79    {"test*?*[a-z]*", "testgoop",       SUCCEEDS},
80    {"te[^x]t", "test",                 SUCCEEDS},
81    {"te[^abc]t", "test",               SUCCEEDS},
82    {"te[^x]t", "test",                 SUCCEEDS},
83    {"te[!x]t", "test",                 SUCCEEDS},
84    {"te[^x]t", "text",                 FAILS},
85    {"te[^\\x]t", "text",               FAILS},
86    {"te[^x\\", "text",                 FAILS},
87    {"te[/]t", "text",                  FAILS},
88    {"te[S]t", "test",                  SUCCEEDS_IF(APR_FNM_CASE_BLIND)},
89    {"te[r-t]t", "test",                SUCCEEDS},
90    {"te[r-t]t", "teSt",                SUCCEEDS_IF(APR_FNM_CASE_BLIND)},
91    {"te[r-T]t", "test",                SUCCEEDS_IF(APR_FNM_CASE_BLIND)},
92    {"te[R-T]t", "test",                SUCCEEDS_IF(APR_FNM_CASE_BLIND)},
93    {"te[r-Tz]t", "tezt",               SUCCEEDS},
94    {"te[R-T]t", "tent",                FAILS},
95    {"tes[]t]", "test",                 SUCCEEDS},
96    {"tes[t-]", "test",                 SUCCEEDS},
97    {"tes[t-]]", "test]",               SUCCEEDS},
98    {"tes[t-]]", "test",                FAILS},
99    {"tes[u-]", "test",                 FAILS},
100    {"tes[t-]", "tes[t-]",              FAILS},
101    {"test[/-/]", "test[/-/]",          SUCCEEDS_IF(APR_FNM_PATHNAME)},
102    {"test[\\/-/]", "test[/-/]",        APR_FNM_PATHNAME, APR_FNM_NOESCAPE},
103    {"test[/-\\/]", "test[/-/]",        APR_FNM_PATHNAME, APR_FNM_NOESCAPE},
104    {"test[/-/]", "test/",              FAILS_IF(APR_FNM_PATHNAME)},
105    {"test[\\/-/]", "test/",            FAILS_IF(APR_FNM_PATHNAME)},
106    {"test[/-\\/]", "test/",            FAILS_IF(APR_FNM_PATHNAME)},
107
108    {"/", "",                           FAILS},
109    {"", "/",                           FAILS},
110    {"/test", "test",                   FAILS},
111    {"test", "/test",                   FAILS},
112    {"test/", "test",                   FAILS},
113    {"test", "test/",                   FAILS},
114    {"\\/test", "/test",                FAILS_IF(APR_FNM_NOESCAPE)},
115    {"*test", "/test",                  FAILS_IF(APR_FNM_PATHNAME)},
116    {"/*/test/", "/test",               FAILS},
117    {"/*/test/", "/test/test/",         SUCCEEDS},
118    {"test/this", "test/",              FAILS},
119    {"test/", "test/this",              FAILS},
120    {"test*/this", "test/this",         SUCCEEDS},
121    {"test*/this", "test/that",         FAILS},
122    {"test/*this", "test/this",         SUCCEEDS},
123
124    {".*", ".this",                     SUCCEEDS},
125    {"*", ".this",                      FAILS_IF(APR_FNM_PERIOD)},
126    {"?this", ".this",                  FAILS_IF(APR_FNM_PERIOD)},
127    {"[.]this", ".this",                FAILS_IF(APR_FNM_PERIOD)},
128
129    {"test/this", "test/this",          SUCCEEDS},
130    {"test?this", "test/this",          FAILS_IF(APR_FNM_PATHNAME)},
131    {"test*this", "test/this",          FAILS_IF(APR_FNM_PATHNAME)},
132    {"test[/]this", "test/this",        FAILS_IF(APR_FNM_PATHNAME)},
133
134    {"test/.*", "test/.this",           SUCCEEDS},
135    {"test/*", "test/.this",            FAILS_IF(APR_FNM_PERIOD | APR_FNM_PATHNAME)},
136    {"test/?this", "test/.this",        FAILS_IF(APR_FNM_PERIOD | APR_FNM_PATHNAME)},
137    {"test/[.]this", "test/.this",      FAILS_IF(APR_FNM_PERIOD | APR_FNM_PATHNAME)},
138
139    {NULL, NULL, 0}
140};
141
142
143
144static void test_fnmatch(abts_case *tc, void *data)
145{
146    struct pattern_s *test = patterns;
147    char buf[80];
148    int i = APR_FNM_BITS + 1;
149    int res;
150
151    for (test = patterns; test->pattern; ++test)
152    {
153        for (i = 0; i <= APR_FNM_BITS; ++i)
154        {
155            res = apr_fnmatch(test->pattern, test->string, i);
156            if (((i & test->require_flags) != test->require_flags)
157                || ((i & test->fail_flags) == test->fail_flags)) {
158                if (res != APR_FNM_NOMATCH)
159                    break;
160            }
161            else {
162                if (res != 0)
163                    break;
164            }
165        }
166        if (i <= APR_FNM_BITS)
167            break;
168    }
169
170    if (i <= APR_FNM_BITS) {
171        sprintf(buf, "apr_fnmatch(\"%s\", \"%s\", %d) returns %d\n",
172                test->pattern, test->string, i, res);
173        abts_fail(tc, buf, __LINE__);
174    }
175}
176
177static void test_fnmatch_test(abts_case *tc, void *data)
178{
179    static const struct test {
180        const char *pattern;
181        int result;
182    } ft_tests[] = {
183        { "a*b", 1 },
184        { "a?", 1 },
185        { "a\\b?", 1 },
186        { "a[b-c]", 1 },
187        { "a", 0 },
188        { "a\\", 0 },
189        { NULL, 0 }
190    };
191    const struct test *t;
192
193    for (t = ft_tests; t->pattern != NULL; t++) {
194        int res = apr_fnmatch_test(t->pattern);
195
196        if (res != t->result) {
197            char buf[128];
198
199            sprintf(buf, "apr_fnmatch_test(\"%s\") = %d, expected %d\n",
200                    t->pattern, res, t->result);
201            abts_fail(tc, buf, __LINE__);
202        }
203    }
204}
205
206static void test_glob(abts_case *tc, void *data)
207{
208    int i;
209    char **list;
210    apr_array_header_t *result;
211
212    APR_ASSERT_SUCCESS(tc, "glob match against data/*.txt",
213                       apr_match_glob("data\\*.txt", &result, p));
214
215    ABTS_INT_EQUAL(tc, NUM_FILES, result->nelts);
216
217    list = (char **)result->elts;
218    for (i = 0; i < result->nelts; i++) {
219        char *dot = strrchr(list[i], '.');
220        ABTS_STR_EQUAL(tc, ".txt", dot);
221    }
222}
223
224static void test_glob_currdir(abts_case *tc, void *data)
225{
226    int i;
227    char **list;
228    apr_array_header_t *result;
229    apr_filepath_set("data", p);
230
231    APR_ASSERT_SUCCESS(tc, "glob match against *.txt with data as current",
232                       apr_match_glob("*.txt", &result, p));
233
234
235    ABTS_INT_EQUAL(tc, NUM_FILES, result->nelts);
236
237    list = (char **)result->elts;
238    for (i = 0; i < result->nelts; i++) {
239        char *dot = strrchr(list[i], '.');
240        ABTS_STR_EQUAL(tc, ".txt", dot);
241    }
242    apr_filepath_set("..", p);
243}
244
245abts_suite *testfnmatch(abts_suite *suite)
246{
247    suite = ADD_SUITE(suite)
248
249    abts_run_test(suite, test_fnmatch, NULL);
250    abts_run_test(suite, test_fnmatch_test, NULL);
251    abts_run_test(suite, test_glob, NULL);
252    abts_run_test(suite, test_glob_currdir, NULL);
253
254    return suite;
255}
256
257