archive_match.c revision 348608
1129198Scognet/*-
2129198Scognet * Copyright (c) 2003-2007 Tim Kientzle
3139735Simp * Copyright (c) 2012 Michihiro NAKAJIMA
4129198Scognet * All rights reserved.
5129198Scognet *
6129198Scognet * Redistribution and use in source and binary forms, with or without
7129198Scognet * modification, are permitted provided that the following conditions
8129198Scognet * are met:
9129198Scognet * 1. Redistributions of source code must retain the above copyright
10129198Scognet *    notice, this list of conditions and the following disclaimer.
11129198Scognet * 2. Redistributions in binary form must reproduce the above copyright
12129198Scognet *    notice, this list of conditions and the following disclaimer in the
13129198Scognet *    documentation and/or other materials provided with the distribution.
14129198Scognet *
15129198Scognet * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16129198Scognet * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17129198Scognet * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18129198Scognet * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19129198Scognet * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20129198Scognet * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21129198Scognet * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22129198Scognet * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23129198Scognet * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24129198Scognet * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25129198Scognet */
26129198Scognet
27129198Scognet#include "archive_platform.h"
28129198Scognet__FBSDID("$FreeBSD$");
29129198Scognet
30129198Scognet#ifdef HAVE_ERRNO_H
31129198Scognet#include <errno.h>
32129198Scognet#endif
33129198Scognet#ifdef HAVE_STDLIB_H
34129198Scognet#include <stdlib.h>
35129198Scognet#endif
36129198Scognet#ifdef HAVE_STRING_H
37129198Scognet#include <string.h>
38129198Scognet#endif
39129198Scognet
40129198Scognet#include "archive.h"
41129198Scognet#include "archive_private.h"
42129198Scognet#include "archive_entry.h"
43129198Scognet#include "archive_getdate.h"
44129198Scognet#include "archive_pathmatch.h"
45129198Scognet#include "archive_rb.h"
46129198Scognet#include "archive_string.h"
47129198Scognet
48129198Scognetstruct match {
49129198Scognet	struct match		*next;
50129198Scognet	int			 matches;
51129198Scognet	struct archive_mstring	 pattern;
52129198Scognet};
53129198Scognet
54129198Scognetstruct match_list {
55129198Scognet	struct match		*first;
56129198Scognet	struct match		**last;
57129198Scognet	int			 count;
58273827Sandrew	int			 unmatched_count;
59129198Scognet	struct match		*unmatched_next;
60129198Scognet	int			 unmatched_eof;
61129198Scognet};
62129198Scognet
63129198Scognetstruct match_file {
64129198Scognet	struct archive_rb_node	 node;
65129198Scognet	struct match_file	*next;
66129198Scognet	struct archive_mstring	 pathname;
67129198Scognet	int			 flag;
68129198Scognet	time_t			 mtime_sec;
69129198Scognet	long			 mtime_nsec;
70129198Scognet	time_t			 ctime_sec;
71129198Scognet	long			 ctime_nsec;
72129198Scognet};
73129198Scognet
74129198Scognetstruct entry_list {
75129198Scognet	struct match_file	*first;
76129198Scognet	struct match_file	**last;
77129198Scognet	int			 count;
78129198Scognet};
79129198Scognet
80129198Scognetstruct id_array {
81129198Scognet	size_t			 size;/* Allocated size */
82129198Scognet	size_t			 count;
83129198Scognet	int64_t			*ids;
84129198Scognet};
85129198Scognet
86129198Scognet#define PATTERN_IS_SET		1
87129198Scognet#define TIME_IS_SET		2
88129198Scognet#define ID_IS_SET		4
89129198Scognet
90129198Scognetstruct archive_match {
91129198Scognet	struct archive		 archive;
92129198Scognet
93129198Scognet	/* exclusion/inclusion set flag. */
94129198Scognet	int			 setflag;
95129198Scognet
96129198Scognet	/* Recursively include directory content? */
97129198Scognet	int			 recursive_include;
98129198Scognet
99129198Scognet	/*
100129198Scognet	 * Matching filename patterns.
101129198Scognet	 */
102129198Scognet	struct match_list	 exclusions;
103129198Scognet	struct match_list	 inclusions;
104129198Scognet
105129198Scognet	/*
106129198Scognet	 * Matching time stamps.
107129198Scognet	 */
108129198Scognet	time_t			 now;
109129198Scognet	int			 newer_mtime_filter;
110129198Scognet	time_t			 newer_mtime_sec;
111129198Scognet	long			 newer_mtime_nsec;
112129198Scognet	int			 newer_ctime_filter;
113129198Scognet	time_t			 newer_ctime_sec;
114129198Scognet	long			 newer_ctime_nsec;
115129198Scognet	int			 older_mtime_filter;
116129198Scognet	time_t			 older_mtime_sec;
117129198Scognet	long			 older_mtime_nsec;
118129198Scognet	int			 older_ctime_filter;
119129198Scognet	time_t			 older_ctime_sec;
120129198Scognet	long			 older_ctime_nsec;
121129198Scognet	/*
122129198Scognet	 * Matching time stamps with its filename.
123129198Scognet	 */
124129198Scognet	struct archive_rb_tree	 exclusion_tree;
125129198Scognet	struct entry_list 	 exclusion_entry_list;
126129198Scognet
127129198Scognet	/*
128129198Scognet	 * Matching file owners.
129129198Scognet	 */
130129198Scognet	struct id_array 	 inclusion_uids;
131129198Scognet	struct id_array 	 inclusion_gids;
132129198Scognet	struct match_list	 inclusion_unames;
133129198Scognet	struct match_list	 inclusion_gnames;
134273827Sandrew};
135239687Sgonzo
136239687Sgonzostatic int	add_pattern_from_file(struct archive_match *,
137239687Sgonzo		    struct match_list *, int, const void *, int);
138239687Sgonzostatic int	add_entry(struct archive_match *, int,
139239687Sgonzo		    struct archive_entry *);
140239687Sgonzostatic int	add_owner_id(struct archive_match *, struct id_array *,
141239687Sgonzo		    int64_t);
142239687Sgonzostatic int	add_owner_name(struct archive_match *, struct match_list *,
143239687Sgonzo		    int, const void *);
144239687Sgonzostatic int	add_pattern_mbs(struct archive_match *, struct match_list *,
145129198Scognet		    const char *);
146236991Simpstatic int	add_pattern_wcs(struct archive_match *, struct match_list *,
147129198Scognet		    const wchar_t *);
148236991Simpstatic int	cmp_key_mbs(const struct archive_rb_node *, const void *);
149129198Scognetstatic int	cmp_key_wcs(const struct archive_rb_node *, const void *);
150129198Scognetstatic int	cmp_node_mbs(const struct archive_rb_node *,
151129198Scognet		    const struct archive_rb_node *);
152129198Scognetstatic int	cmp_node_wcs(const struct archive_rb_node *,
153129198Scognet		    const struct archive_rb_node *);
154129198Scognetstatic void	entry_list_add(struct entry_list *, struct match_file *);
155129198Scognetstatic void	entry_list_free(struct entry_list *);
156129198Scognetstatic void	entry_list_init(struct entry_list *);
157129198Scognetstatic int	error_nomem(struct archive_match *);
158129198Scognetstatic void	match_list_add(struct match_list *, struct match *);
159129198Scognetstatic void	match_list_free(struct match_list *);
160129198Scognetstatic void	match_list_init(struct match_list *);
161129198Scognetstatic int	match_list_unmatched_inclusions_next(struct archive_match *,
162129198Scognet		    struct match_list *, int, const void **);
163129198Scognetstatic int	match_owner_id(struct id_array *, int64_t);
164129198Scognet#if !defined(_WIN32) || defined(__CYGWIN__)
165129198Scognetstatic int	match_owner_name_mbs(struct archive_match *,
166129198Scognet		    struct match_list *, const char *);
167129198Scognet#else
168129198Scognetstatic int	match_owner_name_wcs(struct archive_match *,
169129198Scognet		    struct match_list *, const wchar_t *);
170129198Scognet#endif
171129198Scognetstatic int	match_path_exclusion(struct archive_match *,
172129198Scognet		    struct match *, int, const void *);
173129198Scognetstatic int	match_path_inclusion(struct archive_match *,
174129198Scognet		    struct match *, int, const void *);
175129198Scognetstatic int	owner_excluded(struct archive_match *,
176129198Scognet		    struct archive_entry *);
177129198Scognetstatic int	path_excluded(struct archive_match *, int, const void *);
178129198Scognetstatic int	set_timefilter(struct archive_match *, int, time_t, long,
179129198Scognet		    time_t, long);
180129198Scognetstatic int	set_timefilter_pathname_mbs(struct archive_match *,
181129198Scognet		    int, const char *);
182129198Scognetstatic int	set_timefilter_pathname_wcs(struct archive_match *,
183129198Scognet		    int, const wchar_t *);
184129198Scognetstatic int	set_timefilter_date(struct archive_match *, int, const char *);
185129198Scognetstatic int	set_timefilter_date_w(struct archive_match *, int,
186129198Scognet		    const wchar_t *);
187129198Scognetstatic int	time_excluded(struct archive_match *,
188129198Scognet		    struct archive_entry *);
189129198Scognetstatic int	validate_time_flag(struct archive *, int, const char *);
190129198Scognet
191129198Scognet#define get_date __archive_get_date
192129198Scognet
193129198Scognetstatic const struct archive_rb_tree_ops rb_ops_mbs = {
194129198Scognet	cmp_node_mbs, cmp_key_mbs
195129198Scognet};
196129198Scognet
197129198Scognetstatic const struct archive_rb_tree_ops rb_ops_wcs = {
198129198Scognet	cmp_node_wcs, cmp_key_wcs
199129198Scognet};
200129198Scognet
201129198Scognet/*
202129198Scognet * The matching logic here needs to be re-thought.  I started out to
203129198Scognet * try to mimic gtar's matching logic, but it's not entirely
204129198Scognet * consistent.  In particular 'tar -t' and 'tar -x' interpret patterns
205129198Scognet * on the command line as anchored, but --exclude doesn't.
206129198Scognet */
207129198Scognet
208129198Scognetstatic int
209129198Scogneterror_nomem(struct archive_match *a)
210129198Scognet{
211129198Scognet	archive_set_error(&(a->archive), ENOMEM, "No memory");
212129198Scognet	a->archive.state = ARCHIVE_STATE_FATAL;
213129198Scognet	return (ARCHIVE_FATAL);
214129198Scognet}
215129198Scognet
216129198Scognet/*
217129198Scognet * Create an ARCHIVE_MATCH object.
218129198Scognet */
219129198Scognetstruct archive *
220129198Scognetarchive_match_new(void)
221129198Scognet{
222129198Scognet	struct archive_match *a;
223129198Scognet
224129198Scognet	a = (struct archive_match *)calloc(1, sizeof(*a));
225129198Scognet	if (a == NULL)
226129198Scognet		return (NULL);
227129198Scognet	a->archive.magic = ARCHIVE_MATCH_MAGIC;
228129198Scognet	a->archive.state = ARCHIVE_STATE_NEW;
229129198Scognet	a->recursive_include = 1;
230129198Scognet	match_list_init(&(a->inclusions));
231129198Scognet	match_list_init(&(a->exclusions));
232129198Scognet	__archive_rb_tree_init(&(a->exclusion_tree), &rb_ops_mbs);
233129198Scognet	entry_list_init(&(a->exclusion_entry_list));
234129198Scognet	match_list_init(&(a->inclusion_unames));
235129198Scognet	match_list_init(&(a->inclusion_gnames));
236129198Scognet	time(&a->now);
237129198Scognet	return (&(a->archive));
238129198Scognet}
239129198Scognet
240129198Scognet/*
241129198Scognet * Free an ARCHIVE_MATCH object.
242129198Scognet */
243129198Scognetint
244129198Scognetarchive_match_free(struct archive *_a)
245129198Scognet{
246129198Scognet	struct archive_match *a;
247129198Scognet
248129198Scognet	if (_a == NULL)
249129198Scognet		return (ARCHIVE_OK);
250129198Scognet	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
251129198Scognet	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_match_free");
252129198Scognet	a = (struct archive_match *)_a;
253129198Scognet	match_list_free(&(a->inclusions));
254129198Scognet	match_list_free(&(a->exclusions));
255129198Scognet	entry_list_free(&(a->exclusion_entry_list));
256129198Scognet	free(a->inclusion_uids.ids);
257129198Scognet	free(a->inclusion_gids.ids);
258129198Scognet	match_list_free(&(a->inclusion_unames));
259129198Scognet	match_list_free(&(a->inclusion_gnames));
260129198Scognet	free(a);
261129198Scognet	return (ARCHIVE_OK);
262129198Scognet}
263129198Scognet
264129198Scognet/*
265129198Scognet * Convenience function to perform all exclusion tests.
266129198Scognet *
267129198Scognet * Returns 1 if archive entry is excluded.
268129198Scognet * Returns 0 if archive entry is not excluded.
269129198Scognet * Returns <0 if something error happened.
270129198Scognet */
271129198Scognetint
272129198Scognetarchive_match_excluded(struct archive *_a, struct archive_entry *entry)
273129198Scognet{
274129198Scognet	struct archive_match *a;
275129198Scognet	int r;
276129198Scognet
277129198Scognet	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
278129198Scognet	    ARCHIVE_STATE_NEW, "archive_match_excluded_ae");
279129198Scognet
280129198Scognet	a = (struct archive_match *)_a;
281129198Scognet	if (entry == NULL) {
282129198Scognet		archive_set_error(&(a->archive), EINVAL, "entry is NULL");
283129198Scognet		return (ARCHIVE_FAILED);
284129198Scognet	}
285129198Scognet
286129198Scognet	r = 0;
287129198Scognet	if (a->setflag & PATTERN_IS_SET) {
288129198Scognet#if defined(_WIN32) && !defined(__CYGWIN__)
289129198Scognet		r = path_excluded(a, 0, archive_entry_pathname_w(entry));
290129198Scognet#else
291129198Scognet		r = path_excluded(a, 1, archive_entry_pathname(entry));
292279467Sdim#endif
293129198Scognet		if (r != 0)
294129198Scognet			return (r);
295129198Scognet	}
296129198Scognet
297129198Scognet	if (a->setflag & TIME_IS_SET) {
298129198Scognet		r = time_excluded(a, entry);
299129198Scognet		if (r != 0)
300129198Scognet			return (r);
301129198Scognet	}
302129198Scognet
303129198Scognet	if (a->setflag & ID_IS_SET)
304129198Scognet		r = owner_excluded(a, entry);
305129198Scognet	return (r);
306129198Scognet}
307129198Scognet
308129198Scognet/*
309129198Scognet * Utility functions to manage exclusion/inclusion patterns
310129198Scognet */
311129198Scognet
312129198Scognetint
313129198Scognetarchive_match_exclude_pattern(struct archive *_a, const char *pattern)
314129198Scognet{
315129198Scognet	struct archive_match *a;
316129198Scognet	int r;
317129198Scognet
318129198Scognet	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
319129198Scognet	    ARCHIVE_STATE_NEW, "archive_match_exclude_pattern");
320129198Scognet	a = (struct archive_match *)_a;
321129198Scognet
322129198Scognet	if (pattern == NULL || *pattern == '\0') {
323129198Scognet		archive_set_error(&(a->archive), EINVAL, "pattern is empty");
324129198Scognet		return (ARCHIVE_FAILED);
325129198Scognet	}
326129198Scognet	if ((r = add_pattern_mbs(a, &(a->exclusions), pattern)) != ARCHIVE_OK)
327129198Scognet		return (r);
328129198Scognet	return (ARCHIVE_OK);
329129198Scognet}
330129198Scognet
331129198Scognetint
332129198Scognetarchive_match_exclude_pattern_w(struct archive *_a, const wchar_t *pattern)
333129198Scognet{
334129198Scognet	struct archive_match *a;
335129198Scognet	int r;
336129198Scognet
337129198Scognet	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
338129198Scognet	    ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_w");
339129198Scognet	a = (struct archive_match *)_a;
340129198Scognet
341129198Scognet	if (pattern == NULL || *pattern == L'\0') {
342129198Scognet		archive_set_error(&(a->archive), EINVAL, "pattern is empty");
343129198Scognet		return (ARCHIVE_FAILED);
344236991Simp	}
345129198Scognet	if ((r = add_pattern_wcs(a, &(a->exclusions), pattern)) != ARCHIVE_OK)
346129198Scognet		return (r);
347129198Scognet	return (ARCHIVE_OK);
348129198Scognet}
349129198Scognet
350129198Scognetint
351129198Scognetarchive_match_exclude_pattern_from_file(struct archive *_a,
352129198Scognet    const char *pathname, int nullSeparator)
353129198Scognet{
354129198Scognet	struct archive_match *a;
355129198Scognet
356129198Scognet	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
357129198Scognet	    ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_from_file");
358129198Scognet	a = (struct archive_match *)_a;
359129198Scognet
360129198Scognet	return add_pattern_from_file(a, &(a->exclusions), 1, pathname,
361129198Scognet		nullSeparator);
362129198Scognet}
363129198Scognet
364129198Scognetint
365129198Scognetarchive_match_exclude_pattern_from_file_w(struct archive *_a,
366129198Scognet    const wchar_t *pathname, int nullSeparator)
367129198Scognet{
368129198Scognet	struct archive_match *a;
369129198Scognet
370129198Scognet	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
371129198Scognet	    ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_from_file_w");
372129198Scognet	a = (struct archive_match *)_a;
373129198Scognet
374129198Scognet	return add_pattern_from_file(a, &(a->exclusions), 0, pathname,
375129198Scognet		nullSeparator);
376129198Scognet}
377129198Scognet
378129198Scognetint
379129198Scognetarchive_match_include_pattern(struct archive *_a, const char *pattern)
380129198Scognet{
381129198Scognet	struct archive_match *a;
382129198Scognet	int r;
383129198Scognet
384129198Scognet	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
385129198Scognet	    ARCHIVE_STATE_NEW, "archive_match_include_pattern");
386129198Scognet	a = (struct archive_match *)_a;
387129198Scognet
388129198Scognet	if (pattern == NULL || *pattern == '\0') {
389129198Scognet		archive_set_error(&(a->archive), EINVAL, "pattern is empty");
390129198Scognet		return (ARCHIVE_FAILED);
391129198Scognet	}
392129198Scognet	if ((r = add_pattern_mbs(a, &(a->inclusions), pattern)) != ARCHIVE_OK)
393129198Scognet		return (r);
394129198Scognet	return (ARCHIVE_OK);
395129198Scognet}
396129198Scognet
397129198Scognetint
398129198Scognetarchive_match_include_pattern_w(struct archive *_a, const wchar_t *pattern)
399129198Scognet{
400129198Scognet	struct archive_match *a;
401129198Scognet	int r;
402129198Scognet
403129198Scognet	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
404129198Scognet	    ARCHIVE_STATE_NEW, "archive_match_include_pattern_w");
405129198Scognet	a = (struct archive_match *)_a;
406129198Scognet
407129198Scognet	if (pattern == NULL || *pattern == L'\0') {
408129198Scognet		archive_set_error(&(a->archive), EINVAL, "pattern is empty");
409129198Scognet		return (ARCHIVE_FAILED);
410129198Scognet	}
411129198Scognet	if ((r = add_pattern_wcs(a, &(a->inclusions), pattern)) != ARCHIVE_OK)
412129198Scognet		return (r);
413129198Scognet	return (ARCHIVE_OK);
414129198Scognet}
415129198Scognet
416129198Scognetint
417129198Scognetarchive_match_include_pattern_from_file(struct archive *_a,
418129198Scognet    const char *pathname, int nullSeparator)
419129198Scognet{
420129198Scognet	struct archive_match *a;
421129198Scognet
422129198Scognet	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
423129198Scognet	    ARCHIVE_STATE_NEW, "archive_match_include_pattern_from_file");
424129198Scognet	a = (struct archive_match *)_a;
425129198Scognet
426129198Scognet	return add_pattern_from_file(a, &(a->inclusions), 1, pathname,
427129198Scognet		nullSeparator);
428129198Scognet}
429129198Scognet
430129198Scognetint
431129198Scognetarchive_match_include_pattern_from_file_w(struct archive *_a,
432129198Scognet    const wchar_t *pathname, int nullSeparator)
433129198Scognet{
434129198Scognet	struct archive_match *a;
435129198Scognet
436129198Scognet	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
437129198Scognet	    ARCHIVE_STATE_NEW, "archive_match_include_pattern_from_file_w");
438129198Scognet	a = (struct archive_match *)_a;
439129198Scognet
440129198Scognet	return add_pattern_from_file(a, &(a->inclusions), 0, pathname,
441129198Scognet		nullSeparator);
442129198Scognet}
443129198Scognet
444129198Scognet/*
445129198Scognet * Test functions for pathname patterns.
446129198Scognet *
447129198Scognet * Returns 1 if archive entry is excluded.
448129198Scognet * Returns 0 if archive entry is not excluded.
449129198Scognet * Returns <0 if something error happened.
450129198Scognet */
451129198Scognetint
452129198Scognetarchive_match_path_excluded(struct archive *_a,
453129198Scognet    struct archive_entry *entry)
454129198Scognet{
455129198Scognet	struct archive_match *a;
456129198Scognet
457129198Scognet	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
458129198Scognet	    ARCHIVE_STATE_NEW, "archive_match_path_excluded");
459129198Scognet
460129198Scognet	a = (struct archive_match *)_a;
461129198Scognet	if (entry == NULL) {
462129198Scognet		archive_set_error(&(a->archive), EINVAL, "entry is NULL");
463129198Scognet		return (ARCHIVE_FAILED);
464129198Scognet	}
465129198Scognet
466129198Scognet	/* If we don't have exclusion/inclusion pattern set at all,
467129198Scognet	 * the entry is always not excluded. */
468129198Scognet	if ((a->setflag & PATTERN_IS_SET) == 0)
469129198Scognet		return (0);
470129198Scognet#if defined(_WIN32) && !defined(__CYGWIN__)
471129198Scognet	return (path_excluded(a, 0, archive_entry_pathname_w(entry)));
472129198Scognet#else
473129198Scognet	return (path_excluded(a, 1, archive_entry_pathname(entry)));
474129198Scognet#endif
475129198Scognet}
476129198Scognet
477129198Scognet/*
478129198Scognet * When recursive inclusion of directory content is enabled,
479129198Scognet * an inclusion pattern that matches a directory will also
480129198Scognet * include everything beneath that directory. Enabled by default.
481129198Scognet *
482129198Scognet * For compatibility with GNU tar, exclusion patterns always
483129198Scognet * match if a subset of the full patch matches (i.e., they are
484129198Scognet * are not rooted at the beginning of the path) and thus there
485129198Scognet * is no corresponding non-recursive exclusion mode.
486129198Scognet */
487129198Scognetint
488129198Scognetarchive_match_set_inclusion_recursion(struct archive *_a, int enabled)
489129198Scognet{
490129198Scognet	struct archive_match *a;
491129198Scognet
492129198Scognet	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
493129198Scognet	    ARCHIVE_STATE_NEW, "archive_match_set_inclusion_recursion");
494129198Scognet	a = (struct archive_match *)_a;
495129198Scognet	a->recursive_include = enabled;
496129198Scognet	return (ARCHIVE_OK);
497129198Scognet}
498129198Scognet
499129198Scognet/*
500129198Scognet * Utility functions to get statistic information for inclusion patterns.
501129198Scognet */
502129198Scognetint
503129198Scognetarchive_match_path_unmatched_inclusions(struct archive *_a)
504129198Scognet{
505129198Scognet	struct archive_match *a;
506129198Scognet
507129198Scognet	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
508129198Scognet	    ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions");
509129198Scognet	a = (struct archive_match *)_a;
510129198Scognet
511129198Scognet	return (a->inclusions.unmatched_count);
512129198Scognet}
513129198Scognet
514129198Scognetint
515129198Scognetarchive_match_path_unmatched_inclusions_next(struct archive *_a,
516129198Scognet    const char **_p)
517129198Scognet{
518129198Scognet	struct archive_match *a;
519129198Scognet	const void *v;
520129198Scognet	int r;
521129198Scognet
522129198Scognet	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
523129198Scognet	    ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions_next");
524129198Scognet	a = (struct archive_match *)_a;
525129198Scognet
526129198Scognet	r = match_list_unmatched_inclusions_next(a, &(a->inclusions), 1, &v);
527129198Scognet	*_p = (const char *)v;
528129198Scognet	return (r);
529129198Scognet}
530129198Scognet
531129198Scognetint
532129198Scognetarchive_match_path_unmatched_inclusions_next_w(struct archive *_a,
533129198Scognet    const wchar_t **_p)
534129198Scognet{
535129198Scognet	struct archive_match *a;
536129198Scognet	const void *v;
537129198Scognet	int r;
538129198Scognet
539129198Scognet	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
540129198Scognet	    ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions_next_w");
541129198Scognet	a = (struct archive_match *)_a;
542129198Scognet
543129198Scognet	r = match_list_unmatched_inclusions_next(a, &(a->inclusions), 0, &v);
544129198Scognet	*_p = (const wchar_t *)v;
545129198Scognet	return (r);
546129198Scognet}
547129198Scognet
548129198Scognet/*
549129198Scognet * Add inclusion/exclusion patterns.
550129198Scognet */
551129198Scognetstatic int
552129198Scognetadd_pattern_mbs(struct archive_match *a, struct match_list *list,
553129198Scognet    const char *pattern)
554129198Scognet{
555129198Scognet	struct match *match;
556129198Scognet	size_t len;
557129198Scognet
558129198Scognet	match = calloc(1, sizeof(*match));
559129198Scognet	if (match == NULL)
560129198Scognet		return (error_nomem(a));
561129198Scognet	/* Both "foo/" and "foo" should match "foo/bar". */
562129198Scognet	len = strlen(pattern);
563129198Scognet	if (len && pattern[len - 1] == '/')
564129198Scognet		--len;
565129198Scognet	archive_mstring_copy_mbs_len(&(match->pattern), pattern, len);
566129198Scognet	match_list_add(list, match);
567129198Scognet	a->setflag |= PATTERN_IS_SET;
568129198Scognet	return (ARCHIVE_OK);
569129198Scognet}
570129198Scognet
571129198Scognetstatic int
572129198Scognetadd_pattern_wcs(struct archive_match *a, struct match_list *list,
573129198Scognet    const wchar_t *pattern)
574129198Scognet{
575129198Scognet	struct match *match;
576129198Scognet	size_t len;
577129198Scognet
578129198Scognet	match = calloc(1, sizeof(*match));
579129198Scognet	if (match == NULL)
580129198Scognet		return (error_nomem(a));
581129198Scognet	/* Both "foo/" and "foo" should match "foo/bar". */
582129198Scognet	len = wcslen(pattern);
583129198Scognet	if (len && pattern[len - 1] == L'/')
584129198Scognet		--len;
585129198Scognet	archive_mstring_copy_wcs_len(&(match->pattern), pattern, len);
586129198Scognet	match_list_add(list, match);
587129198Scognet	a->setflag |= PATTERN_IS_SET;
588129198Scognet	return (ARCHIVE_OK);
589129198Scognet}
590129198Scognet
591129198Scognetstatic int
592129198Scognetadd_pattern_from_file(struct archive_match *a, struct match_list *mlist,
593129198Scognet    int mbs, const void *pathname, int nullSeparator)
594129198Scognet{
595129198Scognet	struct archive *ar;
596129198Scognet	struct archive_entry *ae;
597129198Scognet	struct archive_string as;
598129198Scognet	const void *buff;
599129198Scognet	size_t size;
600129198Scognet	int64_t offset;
601129198Scognet	int r;
602129198Scognet
603129198Scognet	ar = archive_read_new();
604129198Scognet	if (ar == NULL) {
605129198Scognet		archive_set_error(&(a->archive), ENOMEM, "No memory");
606129198Scognet		return (ARCHIVE_FATAL);
607129198Scognet	}
608129198Scognet	r = archive_read_support_format_raw(ar);
609129198Scognet	r = archive_read_support_format_empty(ar);
610129198Scognet	if (r != ARCHIVE_OK) {
611129198Scognet		archive_copy_error(&(a->archive), ar);
612129198Scognet		archive_read_free(ar);
613129198Scognet		return (r);
614129198Scognet	}
615129198Scognet	if (mbs)
616129198Scognet		r = archive_read_open_filename(ar, pathname, 512*20);
617129198Scognet	else
618129198Scognet		r = archive_read_open_filename_w(ar, pathname, 512*20);
619129198Scognet	if (r != ARCHIVE_OK) {
620129198Scognet		archive_copy_error(&(a->archive), ar);
621129198Scognet		archive_read_free(ar);
622129198Scognet		return (r);
623129198Scognet	}
624129198Scognet	r = archive_read_next_header(ar, &ae);
625129198Scognet	if (r != ARCHIVE_OK) {
626129198Scognet		archive_read_free(ar);
627129198Scognet		if (r == ARCHIVE_EOF) {
628129198Scognet			return (ARCHIVE_OK);
629129198Scognet		} else {
630129198Scognet			archive_copy_error(&(a->archive), ar);
631129198Scognet			return (r);
632129198Scognet		}
633129198Scognet	}
634129198Scognet
635129198Scognet	archive_string_init(&as);
636129198Scognet
637129198Scognet	while ((r = archive_read_data_block(ar, &buff, &size, &offset))
638129198Scognet	    == ARCHIVE_OK) {
639129198Scognet		const char *b = (const char *)buff;
640129198Scognet
641129198Scognet		while (size) {
642129198Scognet			const char *s = (const char *)b;
643129198Scognet			size_t length = 0;
644129198Scognet			int found_separator = 0;
645129198Scognet
646129198Scognet			while (length < size) {
647129198Scognet				if (nullSeparator) {
648129198Scognet					if (*b == '\0') {
649129198Scognet						found_separator = 1;
650129198Scognet						break;
651129198Scognet					}
652129198Scognet				} else {
653129198Scognet			            	if (*b == 0x0d || *b == 0x0a) {
654129198Scognet						found_separator = 1;
655129198Scognet						break;
656129198Scognet					}
657129198Scognet				}
658129198Scognet				b++;
659129198Scognet				length++;
660129198Scognet			}
661129198Scognet			if (!found_separator) {
662129198Scognet				archive_strncat(&as, s, length);
663129198Scognet				/* Read next data block. */
664129198Scognet				break;
665129198Scognet			}
666129198Scognet			b++;
667129198Scognet			size -= length + 1;
668129198Scognet			archive_strncat(&as, s, length);
669129198Scognet
670129198Scognet			/* If the line is not empty, add the pattern. */
671129198Scognet			if (archive_strlen(&as) > 0) {
672129198Scognet				/* Add pattern. */
673129198Scognet				r = add_pattern_mbs(a, mlist, as.s);
674129198Scognet				if (r != ARCHIVE_OK) {
675129198Scognet					archive_read_free(ar);
676129198Scognet					archive_string_free(&as);
677129198Scognet					return (r);
678129198Scognet				}
679129198Scognet				archive_string_empty(&as);
680129198Scognet			}
681129198Scognet		}
682129198Scognet	}
683129198Scognet
684129198Scognet	/* If an error occurred, report it immediately. */
685129198Scognet	if (r < ARCHIVE_OK) {
686129198Scognet		archive_copy_error(&(a->archive), ar);
687129198Scognet		archive_read_free(ar);
688129198Scognet		archive_string_free(&as);
689129198Scognet		return (r);
690129198Scognet	}
691129198Scognet
692129198Scognet	/* If the line is not empty, add the pattern. */
693129198Scognet	if (r == ARCHIVE_EOF && archive_strlen(&as) > 0) {
694		/* Add pattern. */
695		r = add_pattern_mbs(a, mlist, as.s);
696		if (r != ARCHIVE_OK) {
697			archive_read_free(ar);
698			archive_string_free(&as);
699			return (r);
700		}
701	}
702	archive_read_free(ar);
703	archive_string_free(&as);
704	return (ARCHIVE_OK);
705}
706
707/*
708 * Test if pathname is excluded by inclusion/exclusion patterns.
709 */
710static int
711path_excluded(struct archive_match *a, int mbs, const void *pathname)
712{
713	struct match *match;
714	struct match *matched;
715	int r;
716
717	if (a == NULL)
718		return (0);
719
720	/* Mark off any unmatched inclusions. */
721	/* In particular, if a filename does appear in the archive and
722	 * is explicitly included and excluded, then we don't report
723	 * it as missing even though we don't extract it.
724	 */
725	matched = NULL;
726	for (match = a->inclusions.first; match != NULL;
727	    match = match->next){
728		if (match->matches == 0 &&
729		    (r = match_path_inclusion(a, match, mbs, pathname)) != 0) {
730			if (r < 0)
731				return (r);
732			a->inclusions.unmatched_count--;
733			match->matches++;
734			matched = match;
735		}
736	}
737
738	/* Exclusions take priority */
739	for (match = a->exclusions.first; match != NULL;
740	    match = match->next){
741		r = match_path_exclusion(a, match, mbs, pathname);
742		if (r)
743			return (r);
744	}
745
746	/* It's not excluded and we found an inclusion above, so it's
747	 * included. */
748	if (matched != NULL)
749		return (0);
750
751
752	/* We didn't find an unmatched inclusion, check the remaining ones. */
753	for (match = a->inclusions.first; match != NULL;
754	    match = match->next){
755		/* We looked at previously-unmatched inclusions already. */
756		if (match->matches > 0 &&
757		    (r = match_path_inclusion(a, match, mbs, pathname)) != 0) {
758			if (r < 0)
759				return (r);
760			match->matches++;
761			return (0);
762		}
763	}
764
765	/* If there were inclusions, default is to exclude. */
766	if (a->inclusions.first != NULL)
767	    return (1);
768
769	/* No explicit inclusions, default is to match. */
770	return (0);
771}
772
773/*
774 * This is a little odd, but it matches the default behavior of
775 * gtar.  In particular, 'a*b' will match 'foo/a1111/222b/bar'
776 *
777 */
778static int
779match_path_exclusion(struct archive_match *a, struct match *m,
780    int mbs, const void *pn)
781{
782	int flag = PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END;
783	int r;
784
785	if (mbs) {
786		const char *p;
787		r = archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p);
788		if (r == 0)
789			return (archive_pathmatch(p, (const char *)pn, flag));
790	} else {
791		const wchar_t *p;
792		r = archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p);
793		if (r == 0)
794			return (archive_pathmatch_w(p, (const wchar_t *)pn,
795				flag));
796	}
797	if (errno == ENOMEM)
798		return (error_nomem(a));
799	return (0);
800}
801
802/*
803 * Again, mimic gtar:  inclusions are always anchored (have to match
804 * the beginning of the path) even though exclusions are not anchored.
805 */
806static int
807match_path_inclusion(struct archive_match *a, struct match *m,
808    int mbs, const void *pn)
809{
810	/* Recursive operation requires only a prefix match. */
811	int flag = a->recursive_include ?
812		PATHMATCH_NO_ANCHOR_END :
813		0;
814	int r;
815
816	if (mbs) {
817		const char *p;
818		r = archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p);
819		if (r == 0)
820			return (archive_pathmatch(p, (const char *)pn, flag));
821	} else {
822		const wchar_t *p;
823		r = archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p);
824		if (r == 0)
825			return (archive_pathmatch_w(p, (const wchar_t *)pn,
826				flag));
827	}
828	if (errno == ENOMEM)
829		return (error_nomem(a));
830	return (0);
831}
832
833static void
834match_list_init(struct match_list *list)
835{
836	list->first = NULL;
837	list->last = &(list->first);
838	list->count = 0;
839}
840
841static void
842match_list_free(struct match_list *list)
843{
844	struct match *p, *q;
845
846	for (p = list->first; p != NULL; ) {
847		q = p;
848		p = p->next;
849		archive_mstring_clean(&(q->pattern));
850		free(q);
851	}
852}
853
854static void
855match_list_add(struct match_list *list, struct match *m)
856{
857	*list->last = m;
858	list->last = &(m->next);
859	list->count++;
860	list->unmatched_count++;
861}
862
863static int
864match_list_unmatched_inclusions_next(struct archive_match *a,
865    struct match_list *list, int mbs, const void **vp)
866{
867	struct match *m;
868
869	*vp = NULL;
870	if (list->unmatched_eof) {
871		list->unmatched_eof = 0;
872		return (ARCHIVE_EOF);
873	}
874	if (list->unmatched_next == NULL) {
875		if (list->unmatched_count == 0)
876			return (ARCHIVE_EOF);
877		list->unmatched_next = list->first;
878	}
879
880	for (m = list->unmatched_next; m != NULL; m = m->next) {
881		int r;
882
883		if (m->matches)
884			continue;
885		if (mbs) {
886			const char *p;
887			r = archive_mstring_get_mbs(&(a->archive),
888				&(m->pattern), &p);
889			if (r < 0 && errno == ENOMEM)
890				return (error_nomem(a));
891			if (p == NULL)
892				p = "";
893			*vp = p;
894		} else {
895			const wchar_t *p;
896			r = archive_mstring_get_wcs(&(a->archive),
897				&(m->pattern), &p);
898			if (r < 0 && errno == ENOMEM)
899				return (error_nomem(a));
900			if (p == NULL)
901				p = L"";
902			*vp = p;
903		}
904		list->unmatched_next = m->next;
905		if (list->unmatched_next == NULL)
906			/* To return EOF next time. */
907			list->unmatched_eof = 1;
908		return (ARCHIVE_OK);
909	}
910	list->unmatched_next = NULL;
911	return (ARCHIVE_EOF);
912}
913
914/*
915 * Utility functions to manage inclusion timestamps.
916 */
917int
918archive_match_include_time(struct archive *_a, int flag, time_t sec,
919    long nsec)
920{
921	int r;
922
923	r = validate_time_flag(_a, flag, "archive_match_include_time");
924	if (r != ARCHIVE_OK)
925		return (r);
926	return set_timefilter((struct archive_match *)_a, flag,
927			sec, nsec, sec, nsec);
928}
929
930int
931archive_match_include_date(struct archive *_a, int flag,
932    const char *datestr)
933{
934	int r;
935
936	r = validate_time_flag(_a, flag, "archive_match_include_date");
937	if (r != ARCHIVE_OK)
938		return (r);
939	return set_timefilter_date((struct archive_match *)_a, flag, datestr);
940}
941
942int
943archive_match_include_date_w(struct archive *_a, int flag,
944    const wchar_t *datestr)
945{
946	int r;
947
948	r = validate_time_flag(_a, flag, "archive_match_include_date_w");
949	if (r != ARCHIVE_OK)
950		return (r);
951
952	return set_timefilter_date_w((struct archive_match *)_a, flag, datestr);
953}
954
955int
956archive_match_include_file_time(struct archive *_a, int flag,
957    const char *pathname)
958{
959	int r;
960
961	r = validate_time_flag(_a, flag, "archive_match_include_file_time");
962	if (r != ARCHIVE_OK)
963		return (r);
964	return set_timefilter_pathname_mbs((struct archive_match *)_a,
965			flag, pathname);
966}
967
968int
969archive_match_include_file_time_w(struct archive *_a, int flag,
970    const wchar_t *pathname)
971{
972	int r;
973
974	r = validate_time_flag(_a, flag, "archive_match_include_file_time_w");
975	if (r != ARCHIVE_OK)
976		return (r);
977	return set_timefilter_pathname_wcs((struct archive_match *)_a,
978			flag, pathname);
979}
980
981int
982archive_match_exclude_entry(struct archive *_a, int flag,
983    struct archive_entry *entry)
984{
985	struct archive_match *a;
986	int r;
987
988	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
989	    ARCHIVE_STATE_NEW, "archive_match_time_include_entry");
990	a = (struct archive_match *)_a;
991
992	if (entry == NULL) {
993		archive_set_error(&(a->archive), EINVAL, "entry is NULL");
994		return (ARCHIVE_FAILED);
995	}
996	r = validate_time_flag(_a, flag, "archive_match_exclude_entry");
997	if (r != ARCHIVE_OK)
998		return (r);
999	return (add_entry(a, flag, entry));
1000}
1001
1002/*
1003 * Test function for time stamps.
1004 *
1005 * Returns 1 if archive entry is excluded.
1006 * Returns 0 if archive entry is not excluded.
1007 * Returns <0 if something error happened.
1008 */
1009int
1010archive_match_time_excluded(struct archive *_a,
1011    struct archive_entry *entry)
1012{
1013	struct archive_match *a;
1014
1015	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1016	    ARCHIVE_STATE_NEW, "archive_match_time_excluded_ae");
1017
1018	a = (struct archive_match *)_a;
1019	if (entry == NULL) {
1020		archive_set_error(&(a->archive), EINVAL, "entry is NULL");
1021		return (ARCHIVE_FAILED);
1022	}
1023
1024	/* If we don't have inclusion time set at all, the entry is always
1025	 * not excluded. */
1026	if ((a->setflag & TIME_IS_SET) == 0)
1027		return (0);
1028	return (time_excluded(a, entry));
1029}
1030
1031static int
1032validate_time_flag(struct archive *_a, int flag, const char *_fn)
1033{
1034	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1035	    ARCHIVE_STATE_NEW, _fn);
1036
1037	/* Check a type of time. */
1038	if (flag &
1039	   ((~(ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_CTIME)) & 0xff00)) {
1040		archive_set_error(_a, EINVAL, "Invalid time flag");
1041		return (ARCHIVE_FAILED);
1042	}
1043	if ((flag & (ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_CTIME)) == 0) {
1044		archive_set_error(_a, EINVAL, "No time flag");
1045		return (ARCHIVE_FAILED);
1046	}
1047
1048	/* Check a type of comparison. */
1049	if (flag &
1050	   ((~(ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER
1051			| ARCHIVE_MATCH_EQUAL)) & 0x00ff)) {
1052		archive_set_error(_a, EINVAL, "Invalid comparison flag");
1053		return (ARCHIVE_FAILED);
1054	}
1055	if ((flag & (ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER
1056	    | ARCHIVE_MATCH_EQUAL)) == 0) {
1057		archive_set_error(_a, EINVAL, "No comparison flag");
1058		return (ARCHIVE_FAILED);
1059	}
1060
1061	return (ARCHIVE_OK);
1062}
1063
1064#define JUST_EQUAL(t) (((t) &  (ARCHIVE_MATCH_EQUAL |\
1065	ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER)) == ARCHIVE_MATCH_EQUAL)
1066static int
1067set_timefilter(struct archive_match *a, int timetype,
1068    time_t mtime_sec, long mtime_nsec, time_t ctime_sec, long ctime_nsec)
1069{
1070	if (timetype & ARCHIVE_MATCH_MTIME) {
1071		if ((timetype & ARCHIVE_MATCH_NEWER) || JUST_EQUAL(timetype)) {
1072			a->newer_mtime_filter = timetype;
1073			a->newer_mtime_sec = mtime_sec;
1074			a->newer_mtime_nsec = mtime_nsec;
1075			a->setflag |= TIME_IS_SET;
1076		}
1077		if ((timetype & ARCHIVE_MATCH_OLDER) || JUST_EQUAL(timetype)) {
1078			a->older_mtime_filter = timetype;
1079			a->older_mtime_sec = mtime_sec;
1080			a->older_mtime_nsec = mtime_nsec;
1081			a->setflag |= TIME_IS_SET;
1082		}
1083	}
1084	if (timetype & ARCHIVE_MATCH_CTIME) {
1085		if ((timetype & ARCHIVE_MATCH_NEWER) || JUST_EQUAL(timetype)) {
1086			a->newer_ctime_filter = timetype;
1087			a->newer_ctime_sec = ctime_sec;
1088			a->newer_ctime_nsec = ctime_nsec;
1089			a->setflag |= TIME_IS_SET;
1090		}
1091		if ((timetype & ARCHIVE_MATCH_OLDER) || JUST_EQUAL(timetype)) {
1092			a->older_ctime_filter = timetype;
1093			a->older_ctime_sec = ctime_sec;
1094			a->older_ctime_nsec = ctime_nsec;
1095			a->setflag |= TIME_IS_SET;
1096		}
1097	}
1098	return (ARCHIVE_OK);
1099}
1100
1101static int
1102set_timefilter_date(struct archive_match *a, int timetype, const char *datestr)
1103{
1104	time_t t;
1105
1106	if (datestr == NULL || *datestr == '\0') {
1107		archive_set_error(&(a->archive), EINVAL, "date is empty");
1108		return (ARCHIVE_FAILED);
1109	}
1110	t = get_date(a->now, datestr);
1111	if (t == (time_t)-1) {
1112		archive_set_error(&(a->archive), EINVAL, "invalid date string");
1113		return (ARCHIVE_FAILED);
1114	}
1115	return set_timefilter(a, timetype, t, 0, t, 0);
1116}
1117
1118static int
1119set_timefilter_date_w(struct archive_match *a, int timetype,
1120    const wchar_t *datestr)
1121{
1122	struct archive_string as;
1123	time_t t;
1124
1125	if (datestr == NULL || *datestr == L'\0') {
1126		archive_set_error(&(a->archive), EINVAL, "date is empty");
1127		return (ARCHIVE_FAILED);
1128	}
1129
1130	archive_string_init(&as);
1131	if (archive_string_append_from_wcs(&as, datestr, wcslen(datestr)) < 0) {
1132		archive_string_free(&as);
1133		if (errno == ENOMEM)
1134			return (error_nomem(a));
1135		archive_set_error(&(a->archive), -1,
1136		    "Failed to convert WCS to MBS");
1137		return (ARCHIVE_FAILED);
1138	}
1139	t = get_date(a->now, as.s);
1140	archive_string_free(&as);
1141	if (t == (time_t)-1) {
1142		archive_set_error(&(a->archive), EINVAL, "invalid date string");
1143		return (ARCHIVE_FAILED);
1144	}
1145	return set_timefilter(a, timetype, t, 0, t, 0);
1146}
1147
1148#if defined(_WIN32) && !defined(__CYGWIN__)
1149#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
1150static int
1151set_timefilter_find_data(struct archive_match *a, int timetype,
1152    DWORD ftLastWriteTime_dwHighDateTime, DWORD ftLastWriteTime_dwLowDateTime,
1153    DWORD ftCreationTime_dwHighDateTime, DWORD ftCreationTime_dwLowDateTime)
1154{
1155	ULARGE_INTEGER utc;
1156	time_t ctime_sec, mtime_sec;
1157	long ctime_ns, mtime_ns;
1158
1159	utc.HighPart = ftCreationTime_dwHighDateTime;
1160	utc.LowPart = ftCreationTime_dwLowDateTime;
1161	if (utc.QuadPart >= EPOC_TIME) {
1162		utc.QuadPart -= EPOC_TIME;
1163		ctime_sec = (time_t)(utc.QuadPart / 10000000);
1164		ctime_ns = (long)(utc.QuadPart % 10000000) * 100;
1165	} else {
1166		ctime_sec = 0;
1167		ctime_ns = 0;
1168	}
1169	utc.HighPart = ftLastWriteTime_dwHighDateTime;
1170	utc.LowPart = ftLastWriteTime_dwLowDateTime;
1171	if (utc.QuadPart >= EPOC_TIME) {
1172		utc.QuadPart -= EPOC_TIME;
1173		mtime_sec = (time_t)(utc.QuadPart / 10000000);
1174		mtime_ns = (long)(utc.QuadPart % 10000000) * 100;
1175	} else {
1176		mtime_sec = 0;
1177		mtime_ns = 0;
1178	}
1179	return set_timefilter(a, timetype,
1180			mtime_sec, mtime_ns, ctime_sec, ctime_ns);
1181}
1182
1183static int
1184set_timefilter_pathname_mbs(struct archive_match *a, int timetype,
1185    const char *path)
1186{
1187	/* NOTE: stat() on Windows cannot handle nano seconds. */
1188	HANDLE h;
1189	WIN32_FIND_DATAA d;
1190
1191	if (path == NULL || *path == '\0') {
1192		archive_set_error(&(a->archive), EINVAL, "pathname is empty");
1193		return (ARCHIVE_FAILED);
1194	}
1195	h = FindFirstFileA(path, &d);
1196	if (h == INVALID_HANDLE_VALUE) {
1197		la_dosmaperr(GetLastError());
1198		archive_set_error(&(a->archive), errno,
1199		    "Failed to FindFirstFileA");
1200		return (ARCHIVE_FAILED);
1201	}
1202	FindClose(h);
1203	return set_timefilter_find_data(a, timetype,
1204	    d.ftLastWriteTime.dwHighDateTime, d.ftLastWriteTime.dwLowDateTime,
1205	    d.ftCreationTime.dwHighDateTime, d.ftCreationTime.dwLowDateTime);
1206}
1207
1208static int
1209set_timefilter_pathname_wcs(struct archive_match *a, int timetype,
1210    const wchar_t *path)
1211{
1212	HANDLE h;
1213	WIN32_FIND_DATAW d;
1214
1215	if (path == NULL || *path == L'\0') {
1216		archive_set_error(&(a->archive), EINVAL, "pathname is empty");
1217		return (ARCHIVE_FAILED);
1218	}
1219	h = FindFirstFileW(path, &d);
1220	if (h == INVALID_HANDLE_VALUE) {
1221		la_dosmaperr(GetLastError());
1222		archive_set_error(&(a->archive), errno,
1223		    "Failed to FindFirstFile");
1224		return (ARCHIVE_FAILED);
1225	}
1226	FindClose(h);
1227	return set_timefilter_find_data(a, timetype,
1228	    d.ftLastWriteTime.dwHighDateTime, d.ftLastWriteTime.dwLowDateTime,
1229	    d.ftCreationTime.dwHighDateTime, d.ftCreationTime.dwLowDateTime);
1230}
1231
1232#else /* _WIN32 && !__CYGWIN__ */
1233
1234static int
1235set_timefilter_stat(struct archive_match *a, int timetype, struct stat *st)
1236{
1237	struct archive_entry *ae;
1238	time_t ctime_sec, mtime_sec;
1239	long ctime_ns, mtime_ns;
1240
1241	ae = archive_entry_new();
1242	if (ae == NULL)
1243		return (error_nomem(a));
1244	archive_entry_copy_stat(ae, st);
1245	ctime_sec = archive_entry_ctime(ae);
1246	ctime_ns = archive_entry_ctime_nsec(ae);
1247	mtime_sec = archive_entry_mtime(ae);
1248	mtime_ns = archive_entry_mtime_nsec(ae);
1249	archive_entry_free(ae);
1250	return set_timefilter(a, timetype, mtime_sec, mtime_ns,
1251			ctime_sec, ctime_ns);
1252}
1253
1254static int
1255set_timefilter_pathname_mbs(struct archive_match *a, int timetype,
1256    const char *path)
1257{
1258	struct stat st;
1259
1260	if (path == NULL || *path == '\0') {
1261		archive_set_error(&(a->archive), EINVAL, "pathname is empty");
1262		return (ARCHIVE_FAILED);
1263	}
1264	if (la_stat(path, &st) != 0) {
1265		archive_set_error(&(a->archive), errno, "Failed to stat()");
1266		return (ARCHIVE_FAILED);
1267	}
1268	return (set_timefilter_stat(a, timetype, &st));
1269}
1270
1271static int
1272set_timefilter_pathname_wcs(struct archive_match *a, int timetype,
1273    const wchar_t *path)
1274{
1275	struct archive_string as;
1276	int r;
1277
1278	if (path == NULL || *path == L'\0') {
1279		archive_set_error(&(a->archive), EINVAL, "pathname is empty");
1280		return (ARCHIVE_FAILED);
1281	}
1282
1283	/* Convert WCS filename to MBS filename. */
1284	archive_string_init(&as);
1285	if (archive_string_append_from_wcs(&as, path, wcslen(path)) < 0) {
1286		archive_string_free(&as);
1287		if (errno == ENOMEM)
1288			return (error_nomem(a));
1289		archive_set_error(&(a->archive), -1,
1290		    "Failed to convert WCS to MBS");
1291		return (ARCHIVE_FAILED);
1292	}
1293
1294	r = set_timefilter_pathname_mbs(a, timetype, as.s);
1295	archive_string_free(&as);
1296
1297	return (r);
1298}
1299#endif /* _WIN32 && !__CYGWIN__ */
1300
1301/*
1302 * Call back functions for archive_rb.
1303 */
1304static int
1305cmp_node_mbs(const struct archive_rb_node *n1,
1306    const struct archive_rb_node *n2)
1307{
1308	struct match_file *f1 = (struct match_file *)(uintptr_t)n1;
1309	struct match_file *f2 = (struct match_file *)(uintptr_t)n2;
1310	const char *p1, *p2;
1311
1312	archive_mstring_get_mbs(NULL, &(f1->pathname), &p1);
1313	archive_mstring_get_mbs(NULL, &(f2->pathname), &p2);
1314	if (p1 == NULL)
1315		return (1);
1316	if (p2 == NULL)
1317		return (-1);
1318	return (strcmp(p1, p2));
1319}
1320
1321static int
1322cmp_key_mbs(const struct archive_rb_node *n, const void *key)
1323{
1324	struct match_file *f = (struct match_file *)(uintptr_t)n;
1325	const char *p;
1326
1327	archive_mstring_get_mbs(NULL, &(f->pathname), &p);
1328	if (p == NULL)
1329		return (-1);
1330	return (strcmp(p, (const char *)key));
1331}
1332
1333static int
1334cmp_node_wcs(const struct archive_rb_node *n1,
1335    const struct archive_rb_node *n2)
1336{
1337	struct match_file *f1 = (struct match_file *)(uintptr_t)n1;
1338	struct match_file *f2 = (struct match_file *)(uintptr_t)n2;
1339	const wchar_t *p1, *p2;
1340
1341	archive_mstring_get_wcs(NULL, &(f1->pathname), &p1);
1342	archive_mstring_get_wcs(NULL, &(f2->pathname), &p2);
1343	if (p1 == NULL)
1344		return (1);
1345	if (p2 == NULL)
1346		return (-1);
1347	return (wcscmp(p1, p2));
1348}
1349
1350static int
1351cmp_key_wcs(const struct archive_rb_node *n, const void *key)
1352{
1353	struct match_file *f = (struct match_file *)(uintptr_t)n;
1354	const wchar_t *p;
1355
1356	archive_mstring_get_wcs(NULL, &(f->pathname), &p);
1357	if (p == NULL)
1358		return (-1);
1359	return (wcscmp(p, (const wchar_t *)key));
1360}
1361
1362static void
1363entry_list_init(struct entry_list *list)
1364{
1365	list->first = NULL;
1366	list->last = &(list->first);
1367	list->count = 0;
1368}
1369
1370static void
1371entry_list_free(struct entry_list *list)
1372{
1373	struct match_file *p, *q;
1374
1375	for (p = list->first; p != NULL; ) {
1376		q = p;
1377		p = p->next;
1378		archive_mstring_clean(&(q->pathname));
1379		free(q);
1380	}
1381}
1382
1383static void
1384entry_list_add(struct entry_list *list, struct match_file *file)
1385{
1386	*list->last = file;
1387	list->last = &(file->next);
1388	list->count++;
1389}
1390
1391static int
1392add_entry(struct archive_match *a, int flag,
1393    struct archive_entry *entry)
1394{
1395	struct match_file *f;
1396	const void *pathname;
1397	int r;
1398
1399	f = calloc(1, sizeof(*f));
1400	if (f == NULL)
1401		return (error_nomem(a));
1402
1403#if defined(_WIN32) && !defined(__CYGWIN__)
1404	pathname = archive_entry_pathname_w(entry);
1405	if (pathname == NULL) {
1406		free(f);
1407		archive_set_error(&(a->archive), EINVAL, "pathname is NULL");
1408		return (ARCHIVE_FAILED);
1409	}
1410	archive_mstring_copy_wcs(&(f->pathname), pathname);
1411	a->exclusion_tree.rbt_ops = &rb_ops_wcs;
1412#else
1413	(void)rb_ops_wcs;
1414	pathname = archive_entry_pathname(entry);
1415	if (pathname == NULL) {
1416		free(f);
1417		archive_set_error(&(a->archive), EINVAL, "pathname is NULL");
1418		return (ARCHIVE_FAILED);
1419	}
1420	archive_mstring_copy_mbs(&(f->pathname), pathname);
1421	a->exclusion_tree.rbt_ops = &rb_ops_mbs;
1422#endif
1423	f->flag = flag;
1424	f->mtime_sec = archive_entry_mtime(entry);
1425	f->mtime_nsec = archive_entry_mtime_nsec(entry);
1426	f->ctime_sec = archive_entry_ctime(entry);
1427	f->ctime_nsec = archive_entry_ctime_nsec(entry);
1428	r = __archive_rb_tree_insert_node(&(a->exclusion_tree), &(f->node));
1429	if (!r) {
1430		struct match_file *f2;
1431
1432		/* Get the duplicated file. */
1433		f2 = (struct match_file *)__archive_rb_tree_find_node(
1434			&(a->exclusion_tree), pathname);
1435
1436		/*
1437		 * We always overwrite comparison condition.
1438		 * If you do not want to overwrite it, you should not
1439		 * call archive_match_exclude_entry(). We cannot know
1440		 * what behavior you really expect since overwriting
1441		 * condition might be different with the flag.
1442		 */
1443		if (f2 != NULL) {
1444			f2->flag = f->flag;
1445			f2->mtime_sec = f->mtime_sec;
1446			f2->mtime_nsec = f->mtime_nsec;
1447			f2->ctime_sec = f->ctime_sec;
1448			f2->ctime_nsec = f->ctime_nsec;
1449		}
1450		/* Release the duplicated file. */
1451		archive_mstring_clean(&(f->pathname));
1452		free(f);
1453		return (ARCHIVE_OK);
1454	}
1455	entry_list_add(&(a->exclusion_entry_list), f);
1456	a->setflag |= TIME_IS_SET;
1457	return (ARCHIVE_OK);
1458}
1459
1460/*
1461 * Test if entry is excluded by its timestamp.
1462 */
1463static int
1464time_excluded(struct archive_match *a, struct archive_entry *entry)
1465{
1466	struct match_file *f;
1467	const void *pathname;
1468	time_t sec;
1469	long nsec;
1470
1471	/*
1472	 * If this file/dir is excluded by a time comparison, skip it.
1473	 */
1474	if (a->newer_ctime_filter) {
1475		/* If ctime is not set, use mtime instead. */
1476		if (archive_entry_ctime_is_set(entry))
1477			sec = archive_entry_ctime(entry);
1478		else
1479			sec = archive_entry_mtime(entry);
1480		if (sec < a->newer_ctime_sec)
1481			return (1); /* Too old, skip it. */
1482		if (sec == a->newer_ctime_sec) {
1483			if (archive_entry_ctime_is_set(entry))
1484				nsec = archive_entry_ctime_nsec(entry);
1485			else
1486				nsec = archive_entry_mtime_nsec(entry);
1487			if (nsec < a->newer_ctime_nsec)
1488				return (1); /* Too old, skip it. */
1489			if (nsec == a->newer_ctime_nsec &&
1490			    (a->newer_ctime_filter & ARCHIVE_MATCH_EQUAL)
1491			      == 0)
1492				return (1); /* Equal, skip it. */
1493		}
1494	}
1495	if (a->older_ctime_filter) {
1496		/* If ctime is not set, use mtime instead. */
1497		if (archive_entry_ctime_is_set(entry))
1498			sec = archive_entry_ctime(entry);
1499		else
1500			sec = archive_entry_mtime(entry);
1501		if (sec > a->older_ctime_sec)
1502			return (1); /* Too new, skip it. */
1503		if (sec == a->older_ctime_sec) {
1504			if (archive_entry_ctime_is_set(entry))
1505				nsec = archive_entry_ctime_nsec(entry);
1506			else
1507				nsec = archive_entry_mtime_nsec(entry);
1508			if (nsec > a->older_ctime_nsec)
1509				return (1); /* Too new, skip it. */
1510			if (nsec == a->older_ctime_nsec &&
1511			    (a->older_ctime_filter & ARCHIVE_MATCH_EQUAL)
1512			      == 0)
1513				return (1); /* Equal, skip it. */
1514		}
1515	}
1516	if (a->newer_mtime_filter) {
1517		sec = archive_entry_mtime(entry);
1518		if (sec < a->newer_mtime_sec)
1519			return (1); /* Too old, skip it. */
1520		if (sec == a->newer_mtime_sec) {
1521			nsec = archive_entry_mtime_nsec(entry);
1522			if (nsec < a->newer_mtime_nsec)
1523				return (1); /* Too old, skip it. */
1524			if (nsec == a->newer_mtime_nsec &&
1525			    (a->newer_mtime_filter & ARCHIVE_MATCH_EQUAL)
1526			       == 0)
1527				return (1); /* Equal, skip it. */
1528		}
1529	}
1530	if (a->older_mtime_filter) {
1531		sec = archive_entry_mtime(entry);
1532		if (sec > a->older_mtime_sec)
1533			return (1); /* Too new, skip it. */
1534		nsec = archive_entry_mtime_nsec(entry);
1535		if (sec == a->older_mtime_sec) {
1536			if (nsec > a->older_mtime_nsec)
1537				return (1); /* Too new, skip it. */
1538			if (nsec == a->older_mtime_nsec &&
1539			    (a->older_mtime_filter & ARCHIVE_MATCH_EQUAL)
1540			       == 0)
1541				return (1); /* Equal, skip it. */
1542		}
1543	}
1544
1545	/* If there is no exclusion list, include the file. */
1546	if (a->exclusion_entry_list.count == 0)
1547		return (0);
1548
1549#if defined(_WIN32) && !defined(__CYGWIN__)
1550	pathname = archive_entry_pathname_w(entry);
1551	a->exclusion_tree.rbt_ops = &rb_ops_wcs;
1552#else
1553	(void)rb_ops_wcs;
1554	pathname = archive_entry_pathname(entry);
1555	a->exclusion_tree.rbt_ops = &rb_ops_mbs;
1556#endif
1557	if (pathname == NULL)
1558		return (0);
1559
1560	f = (struct match_file *)__archive_rb_tree_find_node(
1561		&(a->exclusion_tree), pathname);
1562	/* If the file wasn't rejected, include it. */
1563	if (f == NULL)
1564		return (0);
1565
1566	if (f->flag & ARCHIVE_MATCH_CTIME) {
1567		sec = archive_entry_ctime(entry);
1568		if (f->ctime_sec > sec) {
1569			if (f->flag & ARCHIVE_MATCH_OLDER)
1570				return (1);
1571		} else if (f->ctime_sec < sec) {
1572			if (f->flag & ARCHIVE_MATCH_NEWER)
1573				return (1);
1574		} else {
1575			nsec = archive_entry_ctime_nsec(entry);
1576			if (f->ctime_nsec > nsec) {
1577				if (f->flag & ARCHIVE_MATCH_OLDER)
1578					return (1);
1579			} else if (f->ctime_nsec < nsec) {
1580				if (f->flag & ARCHIVE_MATCH_NEWER)
1581					return (1);
1582			} else if (f->flag & ARCHIVE_MATCH_EQUAL)
1583				return (1);
1584		}
1585	}
1586	if (f->flag & ARCHIVE_MATCH_MTIME) {
1587		sec = archive_entry_mtime(entry);
1588		if (f->mtime_sec > sec) {
1589			if (f->flag & ARCHIVE_MATCH_OLDER)
1590				return (1);
1591		} else if (f->mtime_sec < sec) {
1592			if (f->flag & ARCHIVE_MATCH_NEWER)
1593				return (1);
1594		} else {
1595			nsec = archive_entry_mtime_nsec(entry);
1596			if (f->mtime_nsec > nsec) {
1597				if (f->flag & ARCHIVE_MATCH_OLDER)
1598					return (1);
1599			} else if (f->mtime_nsec < nsec) {
1600				if (f->flag & ARCHIVE_MATCH_NEWER)
1601					return (1);
1602			} else if (f->flag & ARCHIVE_MATCH_EQUAL)
1603				return (1);
1604		}
1605	}
1606	return (0);
1607}
1608
1609/*
1610 * Utility functions to manage inclusion owners
1611 */
1612
1613int
1614archive_match_include_uid(struct archive *_a, la_int64_t uid)
1615{
1616	struct archive_match *a;
1617
1618	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1619	    ARCHIVE_STATE_NEW, "archive_match_include_uid");
1620	a = (struct archive_match *)_a;
1621	return (add_owner_id(a, &(a->inclusion_uids), uid));
1622}
1623
1624int
1625archive_match_include_gid(struct archive *_a, la_int64_t gid)
1626{
1627	struct archive_match *a;
1628
1629	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1630	    ARCHIVE_STATE_NEW, "archive_match_include_gid");
1631	a = (struct archive_match *)_a;
1632	return (add_owner_id(a, &(a->inclusion_gids), gid));
1633}
1634
1635int
1636archive_match_include_uname(struct archive *_a, const char *uname)
1637{
1638	struct archive_match *a;
1639
1640	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1641	    ARCHIVE_STATE_NEW, "archive_match_include_uname");
1642	a = (struct archive_match *)_a;
1643	return (add_owner_name(a, &(a->inclusion_unames), 1, uname));
1644}
1645
1646int
1647archive_match_include_uname_w(struct archive *_a, const wchar_t *uname)
1648{
1649	struct archive_match *a;
1650
1651	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1652	    ARCHIVE_STATE_NEW, "archive_match_include_uname_w");
1653	a = (struct archive_match *)_a;
1654	return (add_owner_name(a, &(a->inclusion_unames), 0, uname));
1655}
1656
1657int
1658archive_match_include_gname(struct archive *_a, const char *gname)
1659{
1660	struct archive_match *a;
1661
1662	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1663	    ARCHIVE_STATE_NEW, "archive_match_include_gname");
1664	a = (struct archive_match *)_a;
1665	return (add_owner_name(a, &(a->inclusion_gnames), 1, gname));
1666}
1667
1668int
1669archive_match_include_gname_w(struct archive *_a, const wchar_t *gname)
1670{
1671	struct archive_match *a;
1672
1673	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1674	    ARCHIVE_STATE_NEW, "archive_match_include_gname_w");
1675	a = (struct archive_match *)_a;
1676	return (add_owner_name(a, &(a->inclusion_gnames), 0, gname));
1677}
1678
1679/*
1680 * Test function for owner(uid, gid, uname, gname).
1681 *
1682 * Returns 1 if archive entry is excluded.
1683 * Returns 0 if archive entry is not excluded.
1684 * Returns <0 if something error happened.
1685 */
1686int
1687archive_match_owner_excluded(struct archive *_a,
1688    struct archive_entry *entry)
1689{
1690	struct archive_match *a;
1691
1692	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1693	    ARCHIVE_STATE_NEW, "archive_match_id_excluded_ae");
1694
1695	a = (struct archive_match *)_a;
1696	if (entry == NULL) {
1697		archive_set_error(&(a->archive), EINVAL, "entry is NULL");
1698		return (ARCHIVE_FAILED);
1699	}
1700
1701	/* If we don't have inclusion id set at all, the entry is always
1702	 * not excluded. */
1703	if ((a->setflag & ID_IS_SET) == 0)
1704		return (0);
1705	return (owner_excluded(a, entry));
1706}
1707
1708static int
1709add_owner_id(struct archive_match *a, struct id_array *ids, int64_t id)
1710{
1711	unsigned i;
1712
1713	if (ids->count + 1 >= ids->size) {
1714		void *p;
1715
1716		if (ids->size == 0)
1717			ids->size = 8;
1718		else
1719			ids->size *= 2;
1720		p = realloc(ids->ids, sizeof(*ids->ids) * ids->size);
1721		if (p == NULL)
1722			return (error_nomem(a));
1723		ids->ids = (int64_t *)p;
1724	}
1725
1726	/* Find an insert point. */
1727	for (i = 0; i < ids->count; i++) {
1728		if (ids->ids[i] >= id)
1729			break;
1730	}
1731
1732	/* Add owner id. */
1733	if (i == ids->count)
1734		ids->ids[ids->count++] = id;
1735	else if (ids->ids[i] != id) {
1736		memmove(&(ids->ids[i+1]), &(ids->ids[i]),
1737		    (ids->count - i) * sizeof(ids->ids[0]));
1738		ids->ids[i] = id;
1739		ids->count++;
1740	}
1741	a->setflag |= ID_IS_SET;
1742	return (ARCHIVE_OK);
1743}
1744
1745static int
1746match_owner_id(struct id_array *ids, int64_t id)
1747{
1748	unsigned b, m, t;
1749
1750	t = 0;
1751	b = (unsigned)ids->count;
1752	while (t < b) {
1753		m = (t + b)>>1;
1754		if (ids->ids[m] == id)
1755			return (1);
1756		if (ids->ids[m] < id)
1757			t = m + 1;
1758		else
1759			b = m;
1760	}
1761	return (0);
1762}
1763
1764static int
1765add_owner_name(struct archive_match *a, struct match_list *list,
1766    int mbs, const void *name)
1767{
1768	struct match *match;
1769
1770	match = calloc(1, sizeof(*match));
1771	if (match == NULL)
1772		return (error_nomem(a));
1773	if (mbs)
1774		archive_mstring_copy_mbs(&(match->pattern), name);
1775	else
1776		archive_mstring_copy_wcs(&(match->pattern), name);
1777	match_list_add(list, match);
1778	a->setflag |= ID_IS_SET;
1779	return (ARCHIVE_OK);
1780}
1781
1782#if !defined(_WIN32) || defined(__CYGWIN__)
1783static int
1784match_owner_name_mbs(struct archive_match *a, struct match_list *list,
1785    const char *name)
1786{
1787	struct match *m;
1788	const char *p;
1789
1790	if (name == NULL || *name == '\0')
1791		return (0);
1792	for (m = list->first; m; m = m->next) {
1793		if (archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p)
1794		    < 0 && errno == ENOMEM)
1795			return (error_nomem(a));
1796		if (p != NULL && strcmp(p, name) == 0) {
1797			m->matches++;
1798			return (1);
1799		}
1800	}
1801	return (0);
1802}
1803#else
1804static int
1805match_owner_name_wcs(struct archive_match *a, struct match_list *list,
1806    const wchar_t *name)
1807{
1808	struct match *m;
1809	const wchar_t *p;
1810
1811	if (name == NULL || *name == L'\0')
1812		return (0);
1813	for (m = list->first; m; m = m->next) {
1814		if (archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p)
1815		    < 0 && errno == ENOMEM)
1816			return (error_nomem(a));
1817		if (p != NULL && wcscmp(p, name) == 0) {
1818			m->matches++;
1819			return (1);
1820		}
1821	}
1822	return (0);
1823}
1824#endif
1825
1826/*
1827 * Test if entry is excluded by uid, gid, uname or gname.
1828 */
1829static int
1830owner_excluded(struct archive_match *a, struct archive_entry *entry)
1831{
1832	int r;
1833
1834	if (a->inclusion_uids.count) {
1835		if (!match_owner_id(&(a->inclusion_uids),
1836		    archive_entry_uid(entry)))
1837			return (1);
1838	}
1839
1840	if (a->inclusion_gids.count) {
1841		if (!match_owner_id(&(a->inclusion_gids),
1842		    archive_entry_gid(entry)))
1843			return (1);
1844	}
1845
1846	if (a->inclusion_unames.count) {
1847#if defined(_WIN32) && !defined(__CYGWIN__)
1848		r = match_owner_name_wcs(a, &(a->inclusion_unames),
1849			archive_entry_uname_w(entry));
1850#else
1851		r = match_owner_name_mbs(a, &(a->inclusion_unames),
1852			archive_entry_uname(entry));
1853#endif
1854		if (!r)
1855			return (1);
1856		else if (r < 0)
1857			return (r);
1858	}
1859
1860	if (a->inclusion_gnames.count) {
1861#if defined(_WIN32) && !defined(__CYGWIN__)
1862		r = match_owner_name_wcs(a, &(a->inclusion_gnames),
1863			archive_entry_gname_w(entry));
1864#else
1865		r = match_owner_name_mbs(a, &(a->inclusion_gnames),
1866			archive_entry_gname(entry));
1867#endif
1868		if (!r)
1869			return (1);
1870		else if (r < 0)
1871			return (r);
1872	}
1873	return (0);
1874}
1875
1876