4 * Wildcard matching functions.
8 * Copyright (C) 2013 Eric Biggers
10 * This file is part of wimlib, a library for working with WIM files.
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)
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
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/.
30 #include "wimlib/dentry.h"
31 #include "wimlib/encoding.h"
32 #include "wimlib/error.h"
33 #include "wimlib/metadata.h"
34 #include "wimlib/wildcard.h"
36 struct match_dentry_ctx {
37 int (*consume_dentry)(struct wim_dentry *, void *);
38 void *consume_dentry_ctx;
39 size_t consume_dentry_count;
41 size_t cur_component_offset;
42 size_t cur_component_len;
43 bool case_insensitive;
48 match_wildcard_case_sensitive(const tchar *string, size_t string_len,
49 const tchar *wildcard, size_t wildcard_len)
52 if (string_len == 0) {
53 while (wildcard_len != 0 && *wildcard == T('*')) {
57 return (wildcard_len == 0);
58 } else if (wildcard_len == 0) {
60 } else if (*string == *wildcard || *wildcard == T('?')) {
66 } else if (*wildcard == T('*')) {
67 return match_wildcard_case_sensitive(
69 wildcard + 1, wildcard_len - 1) ||
70 match_wildcard_case_sensitive(
71 string + 1, string_len - 1,
72 wildcard, wildcard_len);
78 #endif /* __WIN32__ */
81 match_wildcard(const tchar *string, tchar *wildcard,
82 size_t wildcard_len, bool case_insensitive)
84 /* Note: in Windows builds fnmatch() calls a replacement function. It
85 * does not support case-sensitive globbing. */
92 int flags = FNM_NOESCAPE;
94 flags |= FNM_CASEFOLD;
96 orig = wildcard[wildcard_len];
97 wildcard[wildcard_len] = T('\0');
99 ret = fnmatch(wildcard, string, flags);
101 wildcard[wildcard_len] = orig;
107 return match_wildcard_case_sensitive(string,
116 expand_wildcard_recursive(struct wim_dentry *cur_dentry,
117 struct match_dentry_ctx *ctx);
120 WILDCARD_STATUS_DONE_FULLY,
121 WILDCARD_STATUS_DONE_TRAILING_SLASHES,
122 WILDCARD_STATUS_NOT_DONE,
126 wildcard_status(const tchar *wildcard)
128 if (*wildcard == T('\0'))
129 return WILDCARD_STATUS_DONE_FULLY;
130 while (is_any_path_separator(*wildcard))
132 if (*wildcard == T('\0'))
133 return WILDCARD_STATUS_DONE_TRAILING_SLASHES;
135 return WILDCARD_STATUS_NOT_DONE;
139 match_dentry(struct wim_dentry *cur_dentry, void *_ctx)
141 struct match_dentry_ctx *ctx = _ctx;
146 if (cur_dentry->file_name_nbytes == 0)
150 name = cur_dentry->file_name;
151 name_len = cur_dentry->file_name_nbytes;
153 ret = utf16le_to_tstr(cur_dentry->file_name,
154 cur_dentry->file_name_nbytes,
159 name_len /= sizeof(tchar);
161 if (match_wildcard(name,
162 &ctx->wildcard_path[ctx->cur_component_offset],
163 ctx->cur_component_len,
164 ctx->case_insensitive))
166 switch (wildcard_status(&ctx->wildcard_path[
167 ctx->cur_component_offset +
168 ctx->cur_component_len]))
170 case WILDCARD_STATUS_DONE_TRAILING_SLASHES:
171 if (!dentry_is_directory(cur_dentry)) {
176 case WILDCARD_STATUS_DONE_FULLY:
177 ret = (*ctx->consume_dentry)(cur_dentry,
178 ctx->consume_dentry_ctx);
179 ctx->consume_dentry_count++;
181 case WILDCARD_STATUS_NOT_DONE:
182 ret = expand_wildcard_recursive(cur_dentry, ctx);
189 #if !TCHAR_IS_UTF16LE
196 expand_wildcard_recursive(struct wim_dentry *cur_dentry,
197 struct match_dentry_ctx *ctx)
207 w = ctx->wildcard_path;
209 begin = ctx->cur_component_offset + ctx->cur_component_len;
210 while (is_any_path_separator(w[begin]))
215 while (w[end] != T('\0') && !is_any_path_separator(w[end]))
223 offset_save = ctx->cur_component_offset;
224 len_save = ctx->cur_component_len;
226 ctx->cur_component_offset = begin;
227 ctx->cur_component_len = len;
229 ret = for_dentry_child(cur_dentry, match_dentry, ctx);
231 ctx->cur_component_len = len_save;
232 ctx->cur_component_offset = offset_save;
237 /* Expand a wildcard relative to the current WIM image.
240 * WIMStruct whose currently selected image is searched to expand the
243 * Wildcard path to expand, which may contain the '?' and '*' characters.
244 * Path separators may be either forward slashes, and leading path
245 * separators are ignored. Trailing path separators indicate that the
246 * wildcard can only match directories.
248 * Callback function which will receive each directory entry matched by the
250 * @consume_dentry_ctx
251 * Argument to pass to @consume_dentry.
253 * Zero or more of the following flags:
255 * WILDCARD_FLAG_WARN_IF_NO_MATCH:
256 * Issue a warning if the wildcard does not match any dentries.
258 * WILDCARD_FLAG_ERROR_IF_NO_MATCH:
259 * Issue an error and return WIMLIB_ERR_PATH_DOES_NOT_EXIST if the
260 * wildcard does not match any dentries.
262 * WILDCARD_FLAG_CASE_INSENSITIVE:
263 * Perform the matching case insensitively. Note that this may
264 * cause @wildcard to match multiple dentries, even if it does not
265 * contain wildcard characters.
267 * @return 0 on success; a positive error code on error; or the first nonzero
268 * value returned by @consume_dentry.
270 * Note: this function uses the @tmp_list field of dentries it attempts to
274 expand_wildcard(WIMStruct *wim,
275 const tchar *wildcard_path,
276 int (*consume_dentry)(struct wim_dentry *, void *),
277 void *consume_dentry_ctx,
280 struct wim_dentry *root;
283 root = wim_root_dentry(wim);
287 struct match_dentry_ctx ctx = {
288 .consume_dentry = consume_dentry,
289 .consume_dentry_ctx = consume_dentry_ctx,
290 .consume_dentry_count = 0,
291 .wildcard_path = TSTRDUP(wildcard_path),
292 .cur_component_offset = 0,
293 .cur_component_len = 0,
294 .case_insensitive = ((flags & WILDCARD_FLAG_CASE_INSENSITIVE) != 0),
297 if (ctx.wildcard_path == NULL)
298 return WIMLIB_ERR_NOMEM;
300 ret = expand_wildcard_recursive(root, &ctx);
301 FREE(ctx.wildcard_path);
302 if (ret == 0 && ctx.consume_dentry_count == 0)
308 if (flags & WILDCARD_FLAG_WARN_IF_NO_MATCH)
309 WARNING("No matches for wildcard path \"%"TS"\"", wildcard_path);
311 if (flags & WILDCARD_FLAG_ERROR_IF_NO_MATCH) {
312 ERROR("No matches for wildcard path \"%"TS"\"", wildcard_path);
313 ret = WIMLIB_ERR_PATH_DOES_NOT_EXIST;