]> wimlib.net Git - wimlib/blob - src/wildcard.c
wimboot.c: Attach WoF driver if not running on target volume
[wimlib] / src / wildcard.c
1 /*
2  * wildcard.c
3  *
4  * Wildcard matching functions.
5  */
6
7 /*
8  * Copyright (C) 2013 Eric Biggers
9  *
10  * This file is part of wimlib, a library for working with WIM files.
11  *
12  * wimlib is free software; you can redistribute it and/or modify it under the
13  * terms of the GNU General Public License as published by the Free
14  * Software Foundation; either version 3 of the License, or (at your option)
15  * any later version.
16  *
17  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
18  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
19  * A PARTICULAR PURPOSE. See the GNU General Public License for more
20  * details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with wimlib; if not, see http://www.gnu.org/licenses/.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #  include "config.h"
28 #endif
29
30 #include <ctype.h>
31 #include "wimlib/dentry.h"
32 #include "wimlib/encoding.h"
33 #include "wimlib/error.h"
34 #include "wimlib/metadata.h"
35 #include "wimlib/wildcard.h"
36
37 struct match_dentry_ctx {
38         int (*consume_dentry)(struct wim_dentry *, void *);
39         void *consume_dentry_ctx;
40         size_t consume_dentry_count;
41         tchar *wildcard_path;
42         size_t cur_component_offset;
43         size_t cur_component_len;
44         bool case_insensitive;
45 };
46
47 #define PLATFORM_SUPPORTS_FNMATCH
48
49 #ifdef __WIN32__
50 /* PathMatchSpec() could provide a fnmatch() alternative, but it isn't
51  * documented properly, nor does it work properly.  For example, it returns that
52  * any name matches *.* even if that name doesn't actually contain a period.  */
53 #  undef PLATFORM_SUPPORTS_FNMATCH
54 #endif
55
56 #ifndef PLATFORM_SUPPORTS_FNMATCH
57 static bool
58 do_match_wildcard(const tchar *string, size_t string_len,
59                   const tchar *wildcard, size_t wildcard_len,
60                   bool ignore_case)
61 {
62         for (;;) {
63                 if (string_len == 0) {
64                         while (wildcard_len != 0 && *wildcard == T('*')) {
65                                 wildcard++;
66                                 wildcard_len--;
67                         }
68                         return (wildcard_len == 0);
69                 } else if (wildcard_len == 0) {
70                         return false;
71                 } else if (*string == *wildcard || *wildcard == T('?') ||
72                            (ignore_case && totlower(*string) == totlower(*wildcard)))
73                 {
74                         string++;
75                         string_len--;
76                         wildcard_len--;
77                         wildcard++;
78                         continue;
79                 } else if (*wildcard == T('*')) {
80                         return do_match_wildcard(string, string_len,
81                                                  wildcard + 1, wildcard_len - 1,
82                                                  ignore_case) ||
83                                do_match_wildcard(string + 1, string_len - 1,
84                                                  wildcard, wildcard_len,
85                                                  ignore_case);
86                 } else {
87                         return false;
88                 }
89         }
90 }
91 #endif /* ! PLATFORM_SUPPORTS_FNMATCH  */
92
93 static bool
94 match_wildcard(const tchar *string, tchar *wildcard,
95                size_t wildcard_len, bool ignore_case)
96 {
97 #ifdef PLATFORM_SUPPORTS_FNMATCH
98         char orig;
99         int ret;
100         int flags = FNM_NOESCAPE;
101         if (ignore_case)
102                 flags |= FNM_CASEFOLD;
103
104         orig = wildcard[wildcard_len];
105         wildcard[wildcard_len] = T('\0');
106
107         ret = fnmatch(wildcard, string, flags);
108
109         wildcard[wildcard_len] = orig;
110         return (ret == 0);
111 #else
112         return do_match_wildcard(string, tstrlen(string),
113                                  wildcard, wildcard_len, ignore_case);
114 #endif
115 }
116
117 static int
118 expand_wildcard_recursive(struct wim_dentry *cur_dentry,
119                           struct match_dentry_ctx *ctx);
120
121 enum {
122         WILDCARD_STATUS_DONE_FULLY,
123         WILDCARD_STATUS_DONE_TRAILING_SLASHES,
124         WILDCARD_STATUS_NOT_DONE,
125 };
126
127 static int
128 wildcard_status(const tchar *wildcard)
129 {
130         if (*wildcard == T('\0'))
131                 return WILDCARD_STATUS_DONE_FULLY;
132         while (*wildcard == WIM_PATH_SEPARATOR)
133                 wildcard++;
134         if (*wildcard == T('\0'))
135                 return WILDCARD_STATUS_DONE_TRAILING_SLASHES;
136
137         return WILDCARD_STATUS_NOT_DONE;
138 }
139
140 static int
141 match_dentry(struct wim_dentry *cur_dentry, struct match_dentry_ctx *ctx)
142 {
143         tchar *name;
144         size_t name_len;
145         int ret;
146
147         if (cur_dentry->file_name_nbytes == 0)
148                 return 0;
149
150 #if TCHAR_IS_UTF16LE
151         name = cur_dentry->file_name;
152         name_len = cur_dentry->file_name_nbytes;
153 #else
154         ret = utf16le_to_tstr(cur_dentry->file_name,
155                               cur_dentry->file_name_nbytes,
156                               &name, &name_len);
157         if (ret)
158                 return ret;
159 #endif
160         name_len /= sizeof(tchar);
161
162         if (match_wildcard(name,
163                            &ctx->wildcard_path[ctx->cur_component_offset],
164                            ctx->cur_component_len,
165                            ctx->case_insensitive))
166         {
167                 switch (wildcard_status(&ctx->wildcard_path[
168                                 ctx->cur_component_offset +
169                                 ctx->cur_component_len]))
170                 {
171                 case WILDCARD_STATUS_DONE_TRAILING_SLASHES:
172                         if (!dentry_is_directory(cur_dentry)) {
173                                 ret = 0;
174                                 break;
175                         }
176                         /* Fall through  */
177                 case WILDCARD_STATUS_DONE_FULLY:
178                         ret = (*ctx->consume_dentry)(cur_dentry,
179                                                      ctx->consume_dentry_ctx);
180                         ctx->consume_dentry_count++;
181                         break;
182                 case WILDCARD_STATUS_NOT_DONE:
183                         ret = expand_wildcard_recursive(cur_dentry, ctx);
184                         break;
185                 }
186         } else {
187                 ret = 0;
188         }
189
190 #if !TCHAR_IS_UTF16LE
191         FREE(name);
192 #endif
193         return ret;
194 }
195
196 static int
197 expand_wildcard_recursive(struct wim_dentry *cur_dentry,
198                           struct match_dentry_ctx *ctx)
199 {
200         tchar *w;
201         size_t begin;
202         size_t end;
203         size_t len;
204         size_t offset_save;
205         size_t len_save;
206         int ret;
207         struct wim_dentry *child;
208
209         w = ctx->wildcard_path;
210
211         begin = ctx->cur_component_offset + ctx->cur_component_len;
212         while (w[begin] == WIM_PATH_SEPARATOR)
213                 begin++;
214
215         end = begin;
216
217         while (w[end] != T('\0') && w[end] != WIM_PATH_SEPARATOR)
218                 end++;
219
220         len = end - begin;
221
222         if (len == 0)
223                 return 0;
224
225         offset_save = ctx->cur_component_offset;
226         len_save = ctx->cur_component_len;
227
228         ctx->cur_component_offset = begin;
229         ctx->cur_component_len = len;
230
231         ret = 0;
232         for_dentry_child(child, cur_dentry) {
233                 ret = match_dentry(child, ctx);
234                 if (ret)
235                         break;
236         }
237
238         ctx->cur_component_len = len_save;
239         ctx->cur_component_offset = offset_save;
240
241         return ret;
242 }
243
244 /* Expand a wildcard relative to the current WIM image.
245  *
246  * @wim
247  *      WIMStruct whose currently selected image is searched to expand the
248  *      wildcard.
249  * @wildcard_path
250  *      Wildcard path to expand, which may contain the '?' and '*' characters.
251  *      Path separators must be WIM_PATH_SEPARATOR.  Leading path separators are
252  *      ignored, whereas one or more trailing path separators indicate that the
253  *      wildcard path can only match directories (and not reparse points).
254  * @consume_dentry
255  *      Callback function which will receive each directory entry matched by the
256  *      wildcard.
257  * @consume_dentry_ctx
258  *      Argument to pass to @consume_dentry.
259  * @flags
260  *      Zero or more of the following flags:
261  *
262  *      WILDCARD_FLAG_WARN_IF_NO_MATCH:
263  *              Issue a warning if the wildcard does not match any dentries.
264  *
265  *      WILDCARD_FLAG_ERROR_IF_NO_MATCH:
266  *              Issue an error and return WIMLIB_ERR_PATH_DOES_NOT_EXIST if the
267  *              wildcard does not match any dentries.
268  *
269  *      WILDCARD_FLAG_CASE_INSENSITIVE:
270  *              Perform the matching case insensitively.  Note that this may
271  *              cause @wildcard to match multiple dentries, even if it does not
272  *              contain wildcard characters.
273  *
274  * @return 0 on success; a positive error code on error; or the first nonzero
275  * value returned by @consume_dentry.
276  */
277 int
278 expand_wildcard(WIMStruct *wim,
279                 const tchar *wildcard_path,
280                 int (*consume_dentry)(struct wim_dentry *, void *),
281                 void *consume_dentry_ctx,
282                 u32 flags)
283 {
284         struct wim_dentry *root;
285         int ret;
286
287         root = wim_root_dentry(wim);
288         if (root == NULL)
289                 goto no_match;
290
291         struct match_dentry_ctx ctx = {
292                 .consume_dentry = consume_dentry,
293                 .consume_dentry_ctx = consume_dentry_ctx,
294                 .consume_dentry_count = 0,
295                 .wildcard_path = TSTRDUP(wildcard_path),
296                 .cur_component_offset = 0,
297                 .cur_component_len = 0,
298                 .case_insensitive = ((flags & WILDCARD_FLAG_CASE_INSENSITIVE) != 0),
299         };
300
301         if (ctx.wildcard_path == NULL)
302                 return WIMLIB_ERR_NOMEM;
303
304         ret = expand_wildcard_recursive(root, &ctx);
305         FREE(ctx.wildcard_path);
306         if (ret == 0 && ctx.consume_dentry_count == 0)
307                 goto no_match;
308         return ret;
309
310 no_match:
311         ret = 0;
312         if (flags & WILDCARD_FLAG_WARN_IF_NO_MATCH)
313                 WARNING("No matches for wildcard path \"%"TS"\"", wildcard_path);
314
315         if (flags & WILDCARD_FLAG_ERROR_IF_NO_MATCH) {
316                 ERROR("No matches for wildcard path \"%"TS"\"", wildcard_path);
317                 ret = WIMLIB_ERR_PATH_DOES_NOT_EXIST;
318         }
319         return ret;
320 }