1238825Smm/*-
2238825Smm * Copyright (c) 2003-2007 Tim Kientzle
3238825Smm * Copyright (c) 2012 Michihiro NAKAJIMA
4238825Smm * All rights reserved.
5238825Smm *
6238825Smm * Redistribution and use in source and binary forms, with or without
7238825Smm * modification, are permitted provided that the following conditions
8238825Smm * are met:
9238825Smm * 1. Redistributions of source code must retain the above copyright
10238825Smm *    notice, this list of conditions and the following disclaimer.
11238825Smm * 2. Redistributions in binary form must reproduce the above copyright
12238825Smm *    notice, this list of conditions and the following disclaimer in the
13238825Smm *    documentation and/or other materials provided with the distribution.
14238825Smm *
15238825Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16238825Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17238825Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18238825Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19238825Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20238825Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21238825Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22238825Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23238825Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24238825Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25238825Smm */
26238825Smm
27238825Smm#include "archive_platform.h"
28238825Smm__FBSDID("$FreeBSD$");
29238825Smm
30238825Smm#ifdef HAVE_ERRNO_H
31238825Smm#include <errno.h>
32238825Smm#endif
33238825Smm#ifdef HAVE_STDLIB_H
34238825Smm#include <stdlib.h>
35238825Smm#endif
36238825Smm#ifdef HAVE_STRING_H
37238825Smm#include <string.h>
38238825Smm#endif
39238825Smm
40238825Smm#include "archive.h"
41238825Smm#include "archive_private.h"
42238825Smm#include "archive_entry.h"
43238825Smm#include "archive_pathmatch.h"
44238825Smm#include "archive_rb.h"
45238825Smm#include "archive_string.h"
46238825Smm
47238825Smmstruct match {
48238825Smm	struct match		*next;
49238825Smm	int			 matches;
50238825Smm	struct archive_mstring	 pattern;
51238825Smm};
52238825Smm
53238825Smmstruct match_list {
54238825Smm	struct match		*first;
55238825Smm	struct match		**last;
56238825Smm	int			 count;
57238825Smm	int			 unmatched_count;
58238825Smm	struct match		*unmatched_next;
59238825Smm	int			 unmatched_eof;
60238825Smm};
61238825Smm
62238825Smmstruct match_file {
63238825Smm	struct archive_rb_node	 node;
64238825Smm	struct match_file	*next;
65238825Smm	struct archive_mstring	 pathname;
66238825Smm	int			 flag;
67238825Smm	time_t			 mtime_sec;
68238825Smm	long			 mtime_nsec;
69238825Smm	time_t			 ctime_sec;
70238825Smm	long			 ctime_nsec;
71238825Smm};
72238825Smm
73238825Smmstruct entry_list {
74238825Smm	struct match_file	*first;
75238825Smm	struct match_file	**last;
76238825Smm	int			 count;
77238825Smm};
78238825Smm
79238825Smmstruct id_array {
80238825Smm	size_t			 size;/* Allocated size */
81238825Smm	size_t			 count;
82238825Smm	int64_t			*ids;
83238825Smm};
84238825Smm
85238825Smm#define PATTERN_IS_SET		1
86238825Smm#define TIME_IS_SET		2
87238825Smm#define ID_IS_SET		4
88238825Smm
89238825Smmstruct archive_match {
90238825Smm	struct archive		 archive;
91238825Smm
92238825Smm	/* exclusion/inclusion set flag. */
93238825Smm	int			 setflag;
94238825Smm
95238825Smm	/*
96238825Smm	 * Matching filename patterns.
97238825Smm	 */
98238825Smm	struct match_list	 exclusions;
99238825Smm	struct match_list	 inclusions;
100238825Smm
101238825Smm	/*
102238825Smm	 * Matching time stamps.
103238825Smm	 */
104238825Smm	time_t			 now;
105238825Smm	int			 newer_mtime_filter;
106238825Smm	time_t			 newer_mtime_sec;
107238825Smm	long			 newer_mtime_nsec;
108238825Smm	int			 newer_ctime_filter;
109238825Smm	time_t			 newer_ctime_sec;
110238825Smm	long			 newer_ctime_nsec;
111238825Smm	int			 older_mtime_filter;
112238825Smm	time_t			 older_mtime_sec;
113238825Smm	long			 older_mtime_nsec;
114238825Smm	int			 older_ctime_filter;
115238825Smm	time_t			 older_ctime_sec;
116238825Smm	long			 older_ctime_nsec;
117238825Smm	/*
118238825Smm	 * Matching time stamps with its filename.
119238825Smm	 */
120238825Smm	struct archive_rb_tree	 exclusion_tree;
121238825Smm	struct entry_list 	 exclusion_entry_list;
122238825Smm
123238825Smm	/*
124238825Smm	 * Matching file owners.
125238825Smm	 */
126238825Smm	struct id_array 	 inclusion_uids;
127238825Smm	struct id_array 	 inclusion_gids;
128238825Smm	struct match_list	 inclusion_unames;
129238825Smm	struct match_list	 inclusion_gnames;
130238825Smm};
131238825Smm
132238825Smmstatic int	add_pattern_from_file(struct archive_match *,
133238825Smm		    struct match_list *, int, const void *, int);
134238825Smmstatic int	add_entry(struct archive_match *, int,
135238825Smm		    struct archive_entry *);
136238825Smmstatic int	add_owner_id(struct archive_match *, struct id_array *,
137238825Smm		    int64_t);
138238825Smmstatic int	add_owner_name(struct archive_match *, struct match_list *,
139238825Smm		    int, const void *);
140238825Smmstatic int	add_pattern_mbs(struct archive_match *, struct match_list *,
141238825Smm		    const char *);
142238825Smmstatic int	add_pattern_wcs(struct archive_match *, struct match_list *,
143238825Smm		    const wchar_t *);
144238825Smmstatic int	cmp_key_mbs(const struct archive_rb_node *, const void *);
145238825Smmstatic int	cmp_key_wcs(const struct archive_rb_node *, const void *);
146238825Smmstatic int	cmp_node_mbs(const struct archive_rb_node *,
147238825Smm		    const struct archive_rb_node *);
148238825Smmstatic int	cmp_node_wcs(const struct archive_rb_node *,
149238825Smm		    const struct archive_rb_node *);
150238825Smmstatic void	entry_list_add(struct entry_list *, struct match_file *);
151238825Smmstatic void	entry_list_free(struct entry_list *);
152238825Smmstatic void	entry_list_init(struct entry_list *);
153238825Smmstatic int	error_nomem(struct archive_match *);
154238825Smmstatic void	match_list_add(struct match_list *, struct match *);
155238825Smmstatic void	match_list_free(struct match_list *);
156238825Smmstatic void	match_list_init(struct match_list *);
157238825Smmstatic int	match_list_unmatched_inclusions_next(struct archive_match *,
158238825Smm		    struct match_list *, int, const void **);
159238825Smmstatic int	match_owner_id(struct id_array *, int64_t);
160238825Smm#if !defined(_WIN32) || defined(__CYGWIN__)
161238825Smmstatic int	match_owner_name_mbs(struct archive_match *,
162238825Smm		    struct match_list *, const char *);
163238825Smm#else
164238825Smmstatic int	match_owner_name_wcs(struct archive_match *,
165238825Smm		    struct match_list *, const wchar_t *);
166238825Smm#endif
167238825Smmstatic int	match_path_exclusion(struct archive_match *,
168238825Smm		    struct match *, int, const void *);
169238825Smmstatic int	match_path_inclusion(struct archive_match *,
170238825Smm		    struct match *, int, const void *);
171238825Smmstatic int	owner_excluded(struct archive_match *,
172238825Smm		    struct archive_entry *);
173238825Smmstatic int	path_excluded(struct archive_match *, int, const void *);
174238825Smmstatic int	set_timefilter(struct archive_match *, int, time_t, long,
175238825Smm		    time_t, long);
176238825Smmstatic int	set_timefilter_pathname_mbs(struct archive_match *,
177238825Smm		    int, const char *);
178238825Smmstatic int	set_timefilter_pathname_wcs(struct archive_match *,
179238825Smm		    int, const wchar_t *);
180238825Smmstatic int	set_timefilter_date(struct archive_match *, int, const char *);
181238825Smmstatic int	set_timefilter_date_w(struct archive_match *, int,
182238825Smm		    const wchar_t *);
183238825Smmstatic int	time_excluded(struct archive_match *,
184238825Smm		    struct archive_entry *);
185238825Smmstatic int	validate_time_flag(struct archive *, int, const char *);
186238825Smm
187238825Smmtime_t __archive_get_date(time_t now, const char *);
188238825Smm#define get_date __archive_get_date
189238825Smm
190238825Smmstatic const struct archive_rb_tree_ops rb_ops_mbs = {
191238825Smm	cmp_node_mbs, cmp_key_mbs
192238825Smm};
193238825Smm
194238825Smmstatic const struct archive_rb_tree_ops rb_ops_wcs = {
195238825Smm	cmp_node_wcs, cmp_key_wcs
196238825Smm};
197238825Smm
198238825Smm/*
199238825Smm * The matching logic here needs to be re-thought.  I started out to
200238825Smm * try to mimic gtar's matching logic, but it's not entirely
201238825Smm * consistent.  In particular 'tar -t' and 'tar -x' interpret patterns
202238825Smm * on the command line as anchored, but --exclude doesn't.
203238825Smm */
204238825Smm
205238825Smmstatic int
206238825Smmerror_nomem(struct archive_match *a)
207238825Smm{
208238825Smm	archive_set_error(&(a->archive), ENOMEM, "No memory");
209238825Smm	a->archive.state = ARCHIVE_STATE_FATAL;
210238825Smm	return (ARCHIVE_FATAL);
211238825Smm}
212238825Smm
213238825Smm/*
214238825Smm * Create an ARCHIVE_MATCH object.
215238825Smm */
216238825Smmstruct archive *
217238825Smmarchive_match_new(void)
218238825Smm{
219238825Smm	struct archive_match *a;
220238825Smm
221238825Smm	a = (struct archive_match *)calloc(1, sizeof(*a));
222238825Smm	if (a == NULL)
223238825Smm		return (NULL);
224238825Smm	a->archive.magic = ARCHIVE_MATCH_MAGIC;
225238825Smm	a->archive.state = ARCHIVE_STATE_NEW;
226238825Smm	match_list_init(&(a->inclusions));
227238825Smm	match_list_init(&(a->exclusions));
228238825Smm	__archive_rb_tree_init(&(a->exclusion_tree), &rb_ops_mbs);
229238825Smm	entry_list_init(&(a->exclusion_entry_list));
230238825Smm	match_list_init(&(a->inclusion_unames));
231238825Smm	match_list_init(&(a->inclusion_gnames));
232238825Smm	time(&a->now);
233238825Smm	return (&(a->archive));
234238825Smm}
235238825Smm
236238825Smm/*
237238825Smm * Free an ARCHIVE_MATCH object.
238238825Smm */
239238825Smmint
240238825Smmarchive_match_free(struct archive *_a)
241238825Smm{
242238825Smm	struct archive_match *a;
243238825Smm
244238825Smm	if (_a == NULL)
245238825Smm		return (ARCHIVE_OK);
246238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
247238825Smm	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_match_free");
248238825Smm	a = (struct archive_match *)_a;
249238825Smm	match_list_free(&(a->inclusions));
250238825Smm	match_list_free(&(a->exclusions));
251238825Smm	entry_list_free(&(a->exclusion_entry_list));
252238825Smm	free(a->inclusion_uids.ids);
253238825Smm	free(a->inclusion_gids.ids);
254238825Smm	match_list_free(&(a->inclusion_unames));
255238825Smm	match_list_free(&(a->inclusion_gnames));
256238825Smm	free(a);
257238825Smm	return (ARCHIVE_OK);
258238825Smm}
259238825Smm
260238825Smm/*
261238825Smm * Convenience function to perform all exclusion tests.
262238825Smm *
263238825Smm * Returns 1 if archive entry is excluded.
264238825Smm * Returns 0 if archive entry is not excluded.
265238825Smm * Returns <0 if something error happened.
266238825Smm */
267238825Smmint
268238825Smmarchive_match_excluded(struct archive *_a, struct archive_entry *entry)
269238825Smm{
270238825Smm	struct archive_match *a;
271238825Smm	int r;
272238825Smm
273238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
274238825Smm	    ARCHIVE_STATE_NEW, "archive_match_excluded_ae");
275238825Smm
276238825Smm	a = (struct archive_match *)_a;
277238825Smm	if (entry == NULL) {
278238825Smm		archive_set_error(&(a->archive), EINVAL, "entry is NULL");
279238825Smm		return (ARCHIVE_FAILED);
280238825Smm	}
281238825Smm
282238825Smm	r = 0;
283238825Smm	if (a->setflag & PATTERN_IS_SET) {
284238825Smm#if defined(_WIN32) && !defined(__CYGWIN__)
285238825Smm		r = path_excluded(a, 0, archive_entry_pathname_w(entry));
286238825Smm#else
287238825Smm		r = path_excluded(a, 1, archive_entry_pathname(entry));
288238825Smm#endif
289238825Smm		if (r != 0)
290238825Smm			return (r);
291238825Smm	}
292238825Smm
293238825Smm	if (a->setflag & TIME_IS_SET) {
294238825Smm		r = time_excluded(a, entry);
295238825Smm		if (r != 0)
296238825Smm			return (r);
297238825Smm	}
298238825Smm
299238825Smm	if (a->setflag & ID_IS_SET)
300238825Smm		r = owner_excluded(a, entry);
301238825Smm	return (r);
302238825Smm}
303238825Smm
304238825Smm/*
305238825Smm * Utility functions to manage exclusion/inclusion patterns
306238825Smm */
307238825Smm
308238825Smmint
309238825Smmarchive_match_exclude_pattern(struct archive *_a, const char *pattern)
310238825Smm{
311238825Smm	struct archive_match *a;
312238825Smm	int r;
313238825Smm
314238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
315238825Smm	    ARCHIVE_STATE_NEW, "archive_match_exclude_pattern");
316238825Smm	a = (struct archive_match *)_a;
317238825Smm
318238825Smm	if (pattern == NULL || *pattern == '\0') {
319238825Smm		archive_set_error(&(a->archive), EINVAL, "pattern is empty");
320238825Smm		return (ARCHIVE_FAILED);
321238825Smm	}
322238825Smm	if ((r = add_pattern_mbs(a, &(a->exclusions), pattern)) != ARCHIVE_OK)
323238825Smm		return (r);
324238825Smm	return (ARCHIVE_OK);
325238825Smm}
326238825Smm
327238825Smmint
328238825Smmarchive_match_exclude_pattern_w(struct archive *_a, const wchar_t *pattern)
329238825Smm{
330238825Smm	struct archive_match *a;
331238825Smm	int r;
332238825Smm
333238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
334238825Smm	    ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_w");
335238825Smm	a = (struct archive_match *)_a;
336238825Smm
337238825Smm	if (pattern == NULL || *pattern == L'\0') {
338238825Smm		archive_set_error(&(a->archive), EINVAL, "pattern is empty");
339238825Smm		return (ARCHIVE_FAILED);
340238825Smm	}
341238825Smm	if ((r = add_pattern_wcs(a, &(a->exclusions), pattern)) != ARCHIVE_OK)
342238825Smm		return (r);
343238825Smm	return (ARCHIVE_OK);
344238825Smm}
345238825Smm
346238825Smmint
347238825Smmarchive_match_exclude_pattern_from_file(struct archive *_a,
348238825Smm    const char *pathname, int nullSeparator)
349238825Smm{
350238825Smm	struct archive_match *a;
351238825Smm
352238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
353238825Smm	    ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_from_file");
354238825Smm	a = (struct archive_match *)_a;
355238825Smm
356238825Smm	return add_pattern_from_file(a, &(a->exclusions), 1, pathname,
357238825Smm		nullSeparator);
358238825Smm}
359238825Smm
360238825Smmint
361238825Smmarchive_match_exclude_pattern_from_file_w(struct archive *_a,
362238825Smm    const wchar_t *pathname, int nullSeparator)
363238825Smm{
364238825Smm	struct archive_match *a;
365238825Smm
366238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
367238825Smm	    ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_from_file_w");
368238825Smm	a = (struct archive_match *)_a;
369238825Smm
370238825Smm	return add_pattern_from_file(a, &(a->exclusions), 0, pathname,
371238825Smm		nullSeparator);
372238825Smm}
373238825Smm
374238825Smmint
375238825Smmarchive_match_include_pattern(struct archive *_a, const char *pattern)
376238825Smm{
377238825Smm	struct archive_match *a;
378238825Smm	int r;
379238825Smm
380238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
381238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_pattern");
382238825Smm	a = (struct archive_match *)_a;
383238825Smm
384238825Smm	if (pattern == NULL || *pattern == '\0') {
385238825Smm		archive_set_error(&(a->archive), EINVAL, "pattern is empty");
386238825Smm		return (ARCHIVE_FAILED);
387238825Smm	}
388238825Smm	if ((r = add_pattern_mbs(a, &(a->inclusions), pattern)) != ARCHIVE_OK)
389238825Smm		return (r);
390238825Smm	return (ARCHIVE_OK);
391238825Smm}
392238825Smm
393238825Smmint
394238825Smmarchive_match_include_pattern_w(struct archive *_a, const wchar_t *pattern)
395238825Smm{
396238825Smm	struct archive_match *a;
397238825Smm	int r;
398238825Smm
399238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
400238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_pattern_w");
401238825Smm	a = (struct archive_match *)_a;
402238825Smm
403238825Smm	if (pattern == NULL || *pattern == L'\0') {
404238825Smm		archive_set_error(&(a->archive), EINVAL, "pattern is empty");
405238825Smm		return (ARCHIVE_FAILED);
406238825Smm	}
407238825Smm	if ((r = add_pattern_wcs(a, &(a->inclusions), pattern)) != ARCHIVE_OK)
408238825Smm		return (r);
409238825Smm	return (ARCHIVE_OK);
410238825Smm}
411238825Smm
412238825Smmint
413238825Smmarchive_match_include_pattern_from_file(struct archive *_a,
414238825Smm    const char *pathname, int nullSeparator)
415238825Smm{
416238825Smm	struct archive_match *a;
417238825Smm
418238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
419238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_pattern_from_file");
420238825Smm	a = (struct archive_match *)_a;
421238825Smm
422238825Smm	return add_pattern_from_file(a, &(a->inclusions), 1, pathname,
423238825Smm		nullSeparator);
424238825Smm}
425238825Smm
426238825Smmint
427238825Smmarchive_match_include_pattern_from_file_w(struct archive *_a,
428238825Smm    const wchar_t *pathname, int nullSeparator)
429238825Smm{
430238825Smm	struct archive_match *a;
431238825Smm
432238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
433238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_pattern_from_file_w");
434238825Smm	a = (struct archive_match *)_a;
435238825Smm
436238825Smm	return add_pattern_from_file(a, &(a->inclusions), 0, pathname,
437238825Smm		nullSeparator);
438238825Smm}
439238825Smm
440238825Smm/*
441238825Smm * Test functions for pathname patterns.
442238825Smm *
443238825Smm * Returns 1 if archive entry is excluded.
444238825Smm * Returns 0 if archive entry is not excluded.
445238825Smm * Returns <0 if something error happened.
446238825Smm */
447238825Smmint
448238825Smmarchive_match_path_excluded(struct archive *_a,
449238825Smm    struct archive_entry *entry)
450238825Smm{
451238825Smm	struct archive_match *a;
452238825Smm
453238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
454238825Smm	    ARCHIVE_STATE_NEW, "archive_match_path_excluded");
455238825Smm
456238825Smm	a = (struct archive_match *)_a;
457238825Smm	if (entry == NULL) {
458238825Smm		archive_set_error(&(a->archive), EINVAL, "entry is NULL");
459238825Smm		return (ARCHIVE_FAILED);
460238825Smm	}
461238825Smm
462238825Smm	/* If we don't have exclusion/inclusion pattern set at all,
463238825Smm	 * the entry is always not excluded. */
464238825Smm	if ((a->setflag & PATTERN_IS_SET) == 0)
465238825Smm		return (0);
466238825Smm#if defined(_WIN32) && !defined(__CYGWIN__)
467238825Smm	return (path_excluded(a, 0, archive_entry_pathname_w(entry)));
468238825Smm#else
469238825Smm	return (path_excluded(a, 1, archive_entry_pathname(entry)));
470238825Smm#endif
471238825Smm}
472238825Smm
473238825Smm/*
474238825Smm * Utilty functions to get statistic information for inclusion patterns.
475238825Smm */
476238825Smmint
477238825Smmarchive_match_path_unmatched_inclusions(struct archive *_a)
478238825Smm{
479238825Smm	struct archive_match *a;
480238825Smm
481238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
482238825Smm	    ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions");
483238825Smm	a = (struct archive_match *)_a;
484238825Smm
485238825Smm	return (a->inclusions.unmatched_count);
486238825Smm}
487238825Smm
488238825Smmint
489238825Smmarchive_match_path_unmatched_inclusions_next(struct archive *_a,
490238825Smm    const char **_p)
491238825Smm{
492238825Smm	struct archive_match *a;
493238825Smm	const void *v;
494238825Smm	int r;
495238825Smm
496238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
497238825Smm	    ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions_next");
498238825Smm	a = (struct archive_match *)_a;
499238825Smm
500238825Smm	r = match_list_unmatched_inclusions_next(a, &(a->inclusions), 1, &v);
501238825Smm	*_p = (const char *)v;
502238825Smm	return (r);
503238825Smm}
504238825Smm
505238825Smmint
506238825Smmarchive_match_path_unmatched_inclusions_next_w(struct archive *_a,
507238825Smm    const wchar_t **_p)
508238825Smm{
509238825Smm	struct archive_match *a;
510238825Smm	const void *v;
511238825Smm	int r;
512238825Smm
513238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
514238825Smm	    ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions_next_w");
515238825Smm	a = (struct archive_match *)_a;
516238825Smm
517238825Smm	r = match_list_unmatched_inclusions_next(a, &(a->inclusions), 0, &v);
518238825Smm	*_p = (const wchar_t *)v;
519238825Smm	return (r);
520238825Smm}
521238825Smm
522238825Smm/*
523238825Smm * Add inclusion/exclusion patterns.
524238825Smm */
525238825Smmstatic int
526238825Smmadd_pattern_mbs(struct archive_match *a, struct match_list *list,
527238825Smm    const char *pattern)
528238825Smm{
529238825Smm	struct match *match;
530238825Smm	size_t len;
531238825Smm
532238825Smm	match = calloc(1, sizeof(*match));
533238825Smm	if (match == NULL)
534238825Smm		return (error_nomem(a));
535238825Smm	/* Both "foo/" and "foo" should match "foo/bar". */
536238825Smm	len = strlen(pattern);
537238825Smm	if (len && pattern[len - 1] == '/')
538238825Smm		--len;
539238825Smm	archive_mstring_copy_mbs_len(&(match->pattern), pattern, len);
540238825Smm	match_list_add(list, match);
541238825Smm	a->setflag |= PATTERN_IS_SET;
542238825Smm	return (ARCHIVE_OK);
543238825Smm}
544238825Smm
545238825Smmstatic int
546238825Smmadd_pattern_wcs(struct archive_match *a, struct match_list *list,
547238825Smm    const wchar_t *pattern)
548238825Smm{
549238825Smm	struct match *match;
550238825Smm	size_t len;
551238825Smm
552238825Smm	match = calloc(1, sizeof(*match));
553238825Smm	if (match == NULL)
554238825Smm		return (error_nomem(a));
555238825Smm	/* Both "foo/" and "foo" should match "foo/bar". */
556238825Smm	len = wcslen(pattern);
557238825Smm	if (len && pattern[len - 1] == L'/')
558238825Smm		--len;
559238825Smm	archive_mstring_copy_wcs_len(&(match->pattern), pattern, len);
560238825Smm	match_list_add(list, match);
561238825Smm	a->setflag |= PATTERN_IS_SET;
562238825Smm	return (ARCHIVE_OK);
563238825Smm}
564238825Smm
565238825Smmstatic int
566238825Smmadd_pattern_from_file(struct archive_match *a, struct match_list *mlist,
567238825Smm    int mbs, const void *pathname, int nullSeparator)
568238825Smm{
569238825Smm	struct archive *ar;
570238825Smm	struct archive_entry *ae;
571238825Smm	struct archive_string as;
572238825Smm	const void *buff;
573238825Smm	size_t size;
574238825Smm	int64_t offset;
575238825Smm	int r;
576238825Smm
577238825Smm	ar = archive_read_new();
578238825Smm	if (ar == NULL) {
579238825Smm		archive_set_error(&(a->archive), ENOMEM, "No memory");
580238825Smm		return (ARCHIVE_FATAL);
581238825Smm	}
582238825Smm	r = archive_read_support_format_raw(ar);
583238825Smm	if (r != ARCHIVE_OK) {
584238825Smm		archive_copy_error(&(a->archive), ar);
585238825Smm		archive_read_free(ar);
586238825Smm		return (r);
587238825Smm	}
588238825Smm	if (mbs)
589238825Smm		r = archive_read_open_filename(ar, pathname, 512*20);
590238825Smm	else
591238825Smm		r = archive_read_open_filename_w(ar, pathname, 512*20);
592238825Smm	if (r != ARCHIVE_OK) {
593238825Smm		archive_copy_error(&(a->archive), ar);
594238825Smm		archive_read_free(ar);
595238825Smm		return (r);
596238825Smm	}
597238825Smm	r = archive_read_next_header(ar, &ae);
598238825Smm	if (r != ARCHIVE_OK) {
599238825Smm		archive_copy_error(&(a->archive), ar);
600238825Smm		archive_read_free(ar);
601238825Smm		return (r);
602238825Smm	}
603238825Smm
604238825Smm	archive_string_init(&as);
605238825Smm
606238825Smm	while ((r = archive_read_data_block(ar, &buff, &size, &offset))
607238825Smm	    == ARCHIVE_OK) {
608238825Smm		const char *b = (const char *)buff;
609238825Smm
610238825Smm		while (size) {
611238825Smm			const char *s = (const char *)b;
612238825Smm			size_t length = 0;
613238825Smm			int found_separator = 0;
614238825Smm
615238825Smm			while (length < size) {
616238825Smm				if (nullSeparator) {
617238825Smm					if (*b == '\0') {
618238825Smm						found_separator = 1;
619238825Smm						break;
620238825Smm					}
621238825Smm				} else {
622238825Smm			            	if (*b == 0x0d || *b == 0x0a) {
623238825Smm						found_separator = 1;
624238825Smm						break;
625238825Smm					}
626238825Smm				}
627238825Smm				b++;
628238825Smm				length++;
629238825Smm			}
630238825Smm			if (!found_separator) {
631238825Smm				archive_strncat(&as, s, length);
632238825Smm				/* Read next data block. */
633238825Smm				break;
634238825Smm			}
635238825Smm			b++;
636238825Smm			size -= length + 1;
637238825Smm			archive_strncat(&as, s, length);
638238825Smm
639238825Smm			/* If the line is not empty, add the pattern. */
640238825Smm			if (archive_strlen(&as) > 0) {
641238825Smm				/* Add pattern. */
642238825Smm				r = add_pattern_mbs(a, mlist, as.s);
643238825Smm				if (r != ARCHIVE_OK) {
644238825Smm					archive_read_free(ar);
645238825Smm					archive_string_free(&as);
646238825Smm					return (r);
647238825Smm				}
648238825Smm				archive_string_empty(&as);
649238825Smm			}
650238825Smm		}
651238825Smm	}
652238825Smm
653238825Smm	/* If something error happend, report it immediately. */
654238825Smm	if (r < ARCHIVE_OK) {
655238825Smm		archive_copy_error(&(a->archive), ar);
656238825Smm		archive_read_free(ar);
657238825Smm		archive_string_free(&as);
658238825Smm		return (r);
659238825Smm	}
660238825Smm
661238825Smm	/* If the line is not empty, add the pattern. */
662238825Smm	if (r == ARCHIVE_EOF && archive_strlen(&as) > 0) {
663238825Smm		/* Add pattern. */
664238825Smm		r = add_pattern_mbs(a, mlist, as.s);
665238825Smm		if (r != ARCHIVE_OK) {
666238825Smm			archive_read_free(ar);
667238825Smm			archive_string_free(&as);
668238825Smm			return (r);
669238825Smm		}
670238825Smm	}
671238825Smm	archive_read_free(ar);
672238825Smm	archive_string_free(&as);
673238825Smm	return (ARCHIVE_OK);
674238825Smm}
675238825Smm
676238825Smm/*
677238825Smm * Test if pathname is excluded by inclusion/exclusion patterns.
678238825Smm */
679238825Smmstatic int
680238825Smmpath_excluded(struct archive_match *a, int mbs, const void *pathname)
681238825Smm{
682238825Smm	struct match *match;
683238825Smm	struct match *matched;
684238825Smm	int r;
685238825Smm
686238825Smm	if (a == NULL)
687238825Smm		return (0);
688238825Smm
689238825Smm	/* Mark off any unmatched inclusions. */
690238825Smm	/* In particular, if a filename does appear in the archive and
691238825Smm	 * is explicitly included and excluded, then we don't report
692238825Smm	 * it as missing even though we don't extract it.
693238825Smm	 */
694238825Smm	matched = NULL;
695238825Smm	for (match = a->inclusions.first; match != NULL;
696238825Smm	    match = match->next){
697238825Smm		if (match->matches == 0 &&
698238825Smm		    (r = match_path_inclusion(a, match, mbs, pathname)) != 0) {
699238825Smm			if (r < 0)
700238825Smm				return (r);
701238825Smm			a->inclusions.unmatched_count--;
702238825Smm			match->matches++;
703238825Smm			matched = match;
704238825Smm		}
705238825Smm	}
706238825Smm
707238825Smm	/* Exclusions take priority */
708238825Smm	for (match = a->exclusions.first; match != NULL;
709238825Smm	    match = match->next){
710238825Smm		r = match_path_exclusion(a, match, mbs, pathname);
711238825Smm		if (r)
712238825Smm			return (r);
713238825Smm	}
714238825Smm
715238825Smm	/* It's not excluded and we found an inclusion above, so it's
716238825Smm	 * included. */
717238825Smm	if (matched != NULL)
718238825Smm		return (0);
719238825Smm
720238825Smm
721238825Smm	/* We didn't find an unmatched inclusion, check the remaining ones. */
722238825Smm	for (match = a->inclusions.first; match != NULL;
723238825Smm	    match = match->next){
724238825Smm		/* We looked at previously-unmatched inclusions already. */
725238825Smm		if (match->matches > 0 &&
726238825Smm		    (r = match_path_inclusion(a, match, mbs, pathname)) != 0) {
727238825Smm			if (r < 0)
728238825Smm				return (r);
729238825Smm			match->matches++;
730238825Smm			return (0);
731238825Smm		}
732238825Smm	}
733238825Smm
734238825Smm	/* If there were inclusions, default is to exclude. */
735238825Smm	if (a->inclusions.first != NULL)
736238825Smm	    return (1);
737238825Smm
738238825Smm	/* No explicit inclusions, default is to match. */
739238825Smm	return (0);
740238825Smm}
741238825Smm
742238825Smm/*
743238825Smm * This is a little odd, but it matches the default behavior of
744238825Smm * gtar.  In particular, 'a*b' will match 'foo/a1111/222b/bar'
745238825Smm *
746238825Smm */
747238825Smmstatic int
748238825Smmmatch_path_exclusion(struct archive_match *a, struct match *m,
749238825Smm    int mbs, const void *pn)
750238825Smm{
751238825Smm	int flag = PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END;
752238825Smm	int r;
753238825Smm
754238825Smm	if (mbs) {
755238825Smm		const char *p;
756238825Smm		r = archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p);
757238825Smm		if (r == 0)
758238825Smm			return (archive_pathmatch(p, (const char *)pn, flag));
759238825Smm	} else {
760238825Smm		const wchar_t *p;
761238825Smm		r = archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p);
762238825Smm		if (r == 0)
763238825Smm			return (archive_pathmatch_w(p, (const wchar_t *)pn,
764238825Smm				flag));
765238825Smm	}
766238825Smm	if (errno == ENOMEM)
767238825Smm		return (error_nomem(a));
768238825Smm	return (0);
769238825Smm}
770238825Smm
771238825Smm/*
772238825Smm * Again, mimic gtar:  inclusions are always anchored (have to match
773238825Smm * the beginning of the path) even though exclusions are not anchored.
774238825Smm */
775238825Smmstatic int
776238825Smmmatch_path_inclusion(struct archive_match *a, struct match *m,
777238825Smm    int mbs, const void *pn)
778238825Smm{
779238825Smm	int flag = PATHMATCH_NO_ANCHOR_END;
780238825Smm	int r;
781238825Smm
782238825Smm	if (mbs) {
783238825Smm		const char *p;
784238825Smm		r = archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p);
785238825Smm		if (r == 0)
786238825Smm			return (archive_pathmatch(p, (const char *)pn, flag));
787238825Smm	} else {
788238825Smm		const wchar_t *p;
789238825Smm		r = archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p);
790238825Smm		if (r == 0)
791238825Smm			return (archive_pathmatch_w(p, (const wchar_t *)pn,
792238825Smm				flag));
793238825Smm	}
794238825Smm	if (errno == ENOMEM)
795238825Smm		return (error_nomem(a));
796238825Smm	return (0);
797238825Smm}
798238825Smm
799238825Smmstatic void
800238825Smmmatch_list_init(struct match_list *list)
801238825Smm{
802238825Smm	list->first = NULL;
803238825Smm	list->last = &(list->first);
804238825Smm	list->count = 0;
805238825Smm}
806238825Smm
807238825Smmstatic void
808238825Smmmatch_list_free(struct match_list *list)
809238825Smm{
810238825Smm	struct match *p, *q;
811238825Smm
812238825Smm	for (p = list->first; p != NULL; ) {
813238825Smm		q = p;
814238825Smm		p = p->next;
815238825Smm		archive_mstring_clean(&(q->pattern));
816238825Smm		free(q);
817238825Smm	}
818238825Smm}
819238825Smm
820238825Smmstatic void
821238825Smmmatch_list_add(struct match_list *list, struct match *m)
822238825Smm{
823238825Smm	*list->last = m;
824238825Smm	list->last = &(m->next);
825238825Smm	list->count++;
826238825Smm	list->unmatched_count++;
827238825Smm}
828238825Smm
829238825Smmstatic int
830238825Smmmatch_list_unmatched_inclusions_next(struct archive_match *a,
831238825Smm    struct match_list *list, int mbs, const void **vp)
832238825Smm{
833238825Smm	struct match *m;
834238825Smm
835238825Smm	*vp = NULL;
836238825Smm	if (list->unmatched_eof) {
837238825Smm		list->unmatched_eof = 0;
838238825Smm		return (ARCHIVE_EOF);
839238825Smm	}
840238825Smm	if (list->unmatched_next == NULL) {
841238825Smm		if (list->unmatched_count == 0)
842238825Smm			return (ARCHIVE_EOF);
843238825Smm		list->unmatched_next = list->first;
844238825Smm	}
845238825Smm
846238825Smm	for (m = list->unmatched_next; m != NULL; m = m->next) {
847238825Smm		int r;
848238825Smm
849238825Smm		if (m->matches)
850238825Smm			continue;
851238825Smm		if (mbs) {
852238825Smm			const char *p;
853238825Smm			r = archive_mstring_get_mbs(&(a->archive),
854238825Smm				&(m->pattern), &p);
855238825Smm			if (r < 0 && errno == ENOMEM)
856238825Smm				return (error_nomem(a));
857238825Smm			if (p == NULL)
858238825Smm				p = "";
859238825Smm			*vp = p;
860238825Smm		} else {
861238825Smm			const wchar_t *p;
862238825Smm			r = archive_mstring_get_wcs(&(a->archive),
863238825Smm				&(m->pattern), &p);
864238825Smm			if (r < 0 && errno == ENOMEM)
865238825Smm				return (error_nomem(a));
866238825Smm			if (p == NULL)
867238825Smm				p = L"";
868238825Smm			*vp = p;
869238825Smm		}
870238825Smm		list->unmatched_next = m->next;
871238825Smm		if (list->unmatched_next == NULL)
872238825Smm			/* To return EOF next time. */
873238825Smm			list->unmatched_eof = 1;
874238825Smm		return (ARCHIVE_OK);
875238825Smm	}
876238825Smm	list->unmatched_next = NULL;
877238825Smm	return (ARCHIVE_EOF);
878238825Smm}
879238825Smm
880238825Smm/*
881238825Smm * Utility functions to manage inclusion timestamps.
882238825Smm */
883238825Smmint
884238825Smmarchive_match_include_time(struct archive *_a, int flag, time_t sec,
885238825Smm    long nsec)
886238825Smm{
887238825Smm	int r;
888238825Smm
889238825Smm	r = validate_time_flag(_a, flag, "archive_match_include_time");
890238825Smm	if (r != ARCHIVE_OK)
891238825Smm		return (r);
892238825Smm	return set_timefilter((struct archive_match *)_a, flag,
893238825Smm			sec, nsec, sec, nsec);
894238825Smm}
895238825Smm
896238825Smmint
897238825Smmarchive_match_include_date(struct archive *_a, int flag,
898238825Smm    const char *datestr)
899238825Smm{
900238825Smm	int r;
901238825Smm
902238825Smm	r = validate_time_flag(_a, flag, "archive_match_include_date");
903238825Smm	if (r != ARCHIVE_OK)
904238825Smm		return (r);
905238825Smm	return set_timefilter_date((struct archive_match *)_a, flag, datestr);
906238825Smm}
907238825Smm
908238825Smmint
909238825Smmarchive_match_include_date_w(struct archive *_a, int flag,
910238825Smm    const wchar_t *datestr)
911238825Smm{
912238825Smm	int r;
913238825Smm
914238825Smm	r = validate_time_flag(_a, flag, "archive_match_include_date_w");
915238825Smm	if (r != ARCHIVE_OK)
916238825Smm		return (r);
917238825Smm
918238825Smm	return set_timefilter_date_w((struct archive_match *)_a, flag, datestr);
919238825Smm}
920238825Smm
921238825Smmint
922238825Smmarchive_match_include_file_time(struct archive *_a, int flag,
923238825Smm    const char *pathname)
924238825Smm{
925238825Smm	int r;
926238825Smm
927238825Smm	r = validate_time_flag(_a, flag, "archive_match_include_file_time");
928238825Smm	if (r != ARCHIVE_OK)
929238825Smm		return (r);
930238825Smm	return set_timefilter_pathname_mbs((struct archive_match *)_a,
931238825Smm			flag, pathname);
932238825Smm}
933238825Smm
934238825Smmint
935238825Smmarchive_match_include_file_time_w(struct archive *_a, int flag,
936238825Smm    const wchar_t *pathname)
937238825Smm{
938238825Smm	int r;
939238825Smm
940238825Smm	r = validate_time_flag(_a, flag, "archive_match_include_file_time_w");
941238825Smm	if (r != ARCHIVE_OK)
942238825Smm		return (r);
943238825Smm	return set_timefilter_pathname_wcs((struct archive_match *)_a,
944238825Smm			flag, pathname);
945238825Smm}
946238825Smm
947238825Smmint
948238825Smmarchive_match_exclude_entry(struct archive *_a, int flag,
949238825Smm    struct archive_entry *entry)
950238825Smm{
951238825Smm	struct archive_match *a;
952238825Smm	int r;
953238825Smm
954238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
955238825Smm	    ARCHIVE_STATE_NEW, "archive_match_time_include_entry");
956238825Smm	a = (struct archive_match *)_a;
957238825Smm
958238825Smm	if (entry == NULL) {
959238825Smm		archive_set_error(&(a->archive), EINVAL, "entry is NULL");
960238825Smm		return (ARCHIVE_FAILED);
961238825Smm	}
962238825Smm	r = validate_time_flag(_a, flag, "archive_match_exclude_entry");
963238825Smm	if (r != ARCHIVE_OK)
964238825Smm		return (r);
965238825Smm	return (add_entry(a, flag, entry));
966238825Smm}
967238825Smm
968238825Smm/*
969238825Smm * Test function for time stamps.
970238825Smm *
971238825Smm * Returns 1 if archive entry is excluded.
972238825Smm * Returns 0 if archive entry is not excluded.
973238825Smm * Returns <0 if something error happened.
974238825Smm */
975238825Smmint
976238825Smmarchive_match_time_excluded(struct archive *_a,
977238825Smm    struct archive_entry *entry)
978238825Smm{
979238825Smm	struct archive_match *a;
980238825Smm
981238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
982238825Smm	    ARCHIVE_STATE_NEW, "archive_match_time_excluded_ae");
983238825Smm
984238825Smm	a = (struct archive_match *)_a;
985238825Smm	if (entry == NULL) {
986238825Smm		archive_set_error(&(a->archive), EINVAL, "entry is NULL");
987238825Smm		return (ARCHIVE_FAILED);
988238825Smm	}
989238825Smm
990238825Smm	/* If we don't have inclusion time set at all, the entry is always
991238825Smm	 * not excluded. */
992238825Smm	if ((a->setflag & TIME_IS_SET) == 0)
993238825Smm		return (0);
994238825Smm	return (time_excluded(a, entry));
995238825Smm}
996238825Smm
997238825Smmstatic int
998238825Smmvalidate_time_flag(struct archive *_a, int flag, const char *_fn)
999238825Smm{
1000238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1001238825Smm	    ARCHIVE_STATE_NEW, _fn);
1002238825Smm
1003238825Smm	/* Check a type of time. */
1004238825Smm	if (flag &
1005238825Smm	   ((~(ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_CTIME)) & 0xff00)) {
1006238825Smm		archive_set_error(_a, EINVAL, "Invalid time flag");
1007238825Smm		return (ARCHIVE_FAILED);
1008238825Smm	}
1009238825Smm	if ((flag & (ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_CTIME)) == 0) {
1010238825Smm		archive_set_error(_a, EINVAL, "No time flag");
1011238825Smm		return (ARCHIVE_FAILED);
1012238825Smm	}
1013238825Smm
1014238825Smm	/* Check a type of comparison. */
1015238825Smm	if (flag &
1016238825Smm	   ((~(ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER
1017238825Smm			| ARCHIVE_MATCH_EQUAL)) & 0x00ff)) {
1018238825Smm		archive_set_error(_a, EINVAL, "Invalid comparison flag");
1019238825Smm		return (ARCHIVE_FAILED);
1020238825Smm	}
1021238825Smm	if ((flag & (ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER
1022238825Smm	    | ARCHIVE_MATCH_EQUAL)) == 0) {
1023238825Smm		archive_set_error(_a, EINVAL, "No comparison flag");
1024238825Smm		return (ARCHIVE_FAILED);
1025238825Smm	}
1026238825Smm
1027238825Smm	return (ARCHIVE_OK);
1028238825Smm}
1029238825Smm
1030238825Smm#define JUST_EQUAL(t) (((t) &  (ARCHIVE_MATCH_EQUAL |\
1031238825Smm	ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER)) == ARCHIVE_MATCH_EQUAL)
1032238825Smmstatic int
1033238825Smmset_timefilter(struct archive_match *a, int timetype,
1034238825Smm    time_t mtime_sec, long mtime_nsec, time_t ctime_sec, long ctime_nsec)
1035238825Smm{
1036238825Smm	if (timetype & ARCHIVE_MATCH_MTIME) {
1037238825Smm		if ((timetype & ARCHIVE_MATCH_NEWER) || JUST_EQUAL(timetype)) {
1038238825Smm			a->newer_mtime_filter = timetype;
1039238825Smm			a->newer_mtime_sec = mtime_sec;
1040238825Smm			a->newer_mtime_nsec = mtime_nsec;
1041238825Smm			a->setflag |= TIME_IS_SET;
1042238825Smm		}
1043238825Smm		if ((timetype & ARCHIVE_MATCH_OLDER) || JUST_EQUAL(timetype)) {
1044238825Smm			a->older_mtime_filter = timetype;
1045238825Smm			a->older_mtime_sec = mtime_sec;
1046238825Smm			a->older_mtime_nsec = mtime_nsec;
1047238825Smm			a->setflag |= TIME_IS_SET;
1048238825Smm		}
1049238825Smm	}
1050238825Smm	if (timetype & ARCHIVE_MATCH_CTIME) {
1051238825Smm		if ((timetype & ARCHIVE_MATCH_NEWER) || JUST_EQUAL(timetype)) {
1052238825Smm			a->newer_ctime_filter = timetype;
1053238825Smm			a->newer_ctime_sec = ctime_sec;
1054238825Smm			a->newer_ctime_nsec = ctime_nsec;
1055238825Smm			a->setflag |= TIME_IS_SET;
1056238825Smm		}
1057238825Smm		if ((timetype & ARCHIVE_MATCH_OLDER) || JUST_EQUAL(timetype)) {
1058238825Smm			a->older_ctime_filter = timetype;
1059238825Smm			a->older_ctime_sec = ctime_sec;
1060238825Smm			a->older_ctime_nsec = ctime_nsec;
1061238825Smm			a->setflag |= TIME_IS_SET;
1062238825Smm		}
1063238825Smm	}
1064238825Smm	return (ARCHIVE_OK);
1065238825Smm}
1066238825Smm
1067238825Smmstatic int
1068238825Smmset_timefilter_date(struct archive_match *a, int timetype, const char *datestr)
1069238825Smm{
1070238825Smm	time_t t;
1071238825Smm
1072238825Smm	if (datestr == NULL || *datestr == '\0') {
1073238825Smm		archive_set_error(&(a->archive), EINVAL, "date is empty");
1074238825Smm		return (ARCHIVE_FAILED);
1075238825Smm	}
1076238825Smm	t = get_date(a->now, datestr);
1077238825Smm	if (t == (time_t)-1) {
1078238825Smm		archive_set_error(&(a->archive), EINVAL, "invalid date string");
1079238825Smm		return (ARCHIVE_FAILED);
1080238825Smm	}
1081238825Smm	return set_timefilter(a, timetype, t, 0, t, 0);
1082238825Smm}
1083238825Smm
1084238825Smmstatic int
1085238825Smmset_timefilter_date_w(struct archive_match *a, int timetype,
1086238825Smm    const wchar_t *datestr)
1087238825Smm{
1088238825Smm	struct archive_string as;
1089238825Smm	time_t t;
1090238825Smm
1091238825Smm	if (datestr == NULL || *datestr == L'\0') {
1092238825Smm		archive_set_error(&(a->archive), EINVAL, "date is empty");
1093238825Smm		return (ARCHIVE_FAILED);
1094238825Smm	}
1095238825Smm
1096238825Smm	archive_string_init(&as);
1097238825Smm	if (archive_string_append_from_wcs(&as, datestr, wcslen(datestr)) < 0) {
1098238825Smm		archive_string_free(&as);
1099238825Smm		if (errno == ENOMEM)
1100238825Smm			return (error_nomem(a));
1101238825Smm		archive_set_error(&(a->archive), -1,
1102238825Smm		    "Failed to convert WCS to MBS");
1103238825Smm		return (ARCHIVE_FAILED);
1104238825Smm	}
1105238825Smm	t = get_date(a->now, as.s);
1106238825Smm	archive_string_free(&as);
1107238825Smm	if (t == (time_t)-1) {
1108238825Smm		archive_set_error(&(a->archive), EINVAL, "invalid date string");
1109238825Smm		return (ARCHIVE_FAILED);
1110238825Smm	}
1111238825Smm	return set_timefilter(a, timetype, t, 0, t, 0);
1112238825Smm}
1113238825Smm
1114238825Smm#if defined(_WIN32) && !defined(__CYGWIN__)
1115238825Smm#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
1116238825Smmstatic int
1117238825Smmset_timefilter_find_data(struct archive_match *a, int timetype,
1118238825Smm    DWORD ftLastWriteTime_dwHighDateTime, DWORD ftLastWriteTime_dwLowDateTime,
1119238825Smm    DWORD ftCreationTime_dwHighDateTime, DWORD ftCreationTime_dwLowDateTime)
1120238825Smm{
1121238825Smm	ULARGE_INTEGER utc;
1122238825Smm	time_t ctime_sec, mtime_sec;
1123238825Smm	long ctime_ns, mtime_ns;
1124238825Smm
1125238825Smm	utc.HighPart = ftCreationTime_dwHighDateTime;
1126238825Smm	utc.LowPart = ftCreationTime_dwLowDateTime;
1127238825Smm	if (utc.QuadPart >= EPOC_TIME) {
1128238825Smm		utc.QuadPart -= EPOC_TIME;
1129238825Smm		ctime_sec = (time_t)(utc.QuadPart / 10000000);
1130238825Smm		ctime_ns = (long)(utc.QuadPart % 10000000) * 100;
1131238825Smm	} else {
1132238825Smm		ctime_sec = 0;
1133238825Smm		ctime_ns = 0;
1134238825Smm	}
1135238825Smm	utc.HighPart = ftLastWriteTime_dwHighDateTime;
1136238825Smm	utc.LowPart = ftLastWriteTime_dwLowDateTime;
1137238825Smm	if (utc.QuadPart >= EPOC_TIME) {
1138238825Smm		utc.QuadPart -= EPOC_TIME;
1139238825Smm		mtime_sec = (time_t)(utc.QuadPart / 10000000);
1140238825Smm		mtime_ns = (long)(utc.QuadPart % 10000000) * 100;
1141238825Smm	} else {
1142238825Smm		mtime_sec = 0;
1143238825Smm		mtime_ns = 0;
1144238825Smm	}
1145238825Smm	return set_timefilter(a, timetype,
1146238825Smm			mtime_sec, mtime_ns, ctime_sec, ctime_ns);
1147238825Smm}
1148238825Smm
1149238825Smmstatic int
1150238825Smmset_timefilter_pathname_mbs(struct archive_match *a, int timetype,
1151238825Smm    const char *path)
1152238825Smm{
1153238825Smm	/* NOTE: stat() on Windows cannot handle nano seconds. */
1154238825Smm	HANDLE h;
1155238825Smm	WIN32_FIND_DATA d;
1156238825Smm
1157238825Smm	if (path == NULL || *path == '\0') {
1158238825Smm		archive_set_error(&(a->archive), EINVAL, "pathname is empty");
1159238825Smm		return (ARCHIVE_FAILED);
1160238825Smm	}
1161238825Smm	h = FindFirstFileA(path, &d);
1162238825Smm	if (h == INVALID_HANDLE_VALUE) {
1163238825Smm		la_dosmaperr(GetLastError());
1164238825Smm		archive_set_error(&(a->archive), errno,
1165238825Smm		    "Failed to FindFirstFileA");
1166238825Smm		return (ARCHIVE_FAILED);
1167238825Smm	}
1168238825Smm	FindClose(h);
1169238825Smm	return set_timefilter_find_data(a, timetype,
1170238825Smm	    d.ftLastWriteTime.dwHighDateTime, d.ftLastWriteTime.dwLowDateTime,
1171238825Smm	    d.ftCreationTime.dwHighDateTime, d.ftCreationTime.dwLowDateTime);
1172238825Smm}
1173238825Smm
1174238825Smmstatic int
1175238825Smmset_timefilter_pathname_wcs(struct archive_match *a, int timetype,
1176238825Smm    const wchar_t *path)
1177238825Smm{
1178238825Smm	HANDLE h;
1179238825Smm	WIN32_FIND_DATAW d;
1180238825Smm
1181238825Smm	if (path == NULL || *path == L'\0') {
1182238825Smm		archive_set_error(&(a->archive), EINVAL, "pathname is empty");
1183238825Smm		return (ARCHIVE_FAILED);
1184238825Smm	}
1185238825Smm	h = FindFirstFileW(path, &d);
1186238825Smm	if (h == INVALID_HANDLE_VALUE) {
1187238825Smm		la_dosmaperr(GetLastError());
1188238825Smm		archive_set_error(&(a->archive), errno,
1189238825Smm		    "Failed to FindFirstFile");
1190238825Smm		return (ARCHIVE_FAILED);
1191238825Smm	}
1192238825Smm	FindClose(h);
1193238825Smm	return set_timefilter_find_data(a, timetype,
1194238825Smm	    d.ftLastWriteTime.dwHighDateTime, d.ftLastWriteTime.dwLowDateTime,
1195238825Smm	    d.ftCreationTime.dwHighDateTime, d.ftCreationTime.dwLowDateTime);
1196238825Smm}
1197238825Smm
1198238825Smm#else /* _WIN32 && !__CYGWIN__ */
1199238825Smm
1200238825Smmstatic int
1201238825Smmset_timefilter_stat(struct archive_match *a, int timetype, struct stat *st)
1202238825Smm{
1203238825Smm	struct archive_entry *ae;
1204238825Smm	time_t ctime_sec, mtime_sec;
1205238825Smm	long ctime_ns, mtime_ns;
1206238825Smm
1207238825Smm	ae = archive_entry_new();
1208238825Smm	if (ae == NULL)
1209238825Smm		return (error_nomem(a));
1210238825Smm	archive_entry_copy_stat(ae, st);
1211238825Smm	ctime_sec = archive_entry_ctime(ae);
1212238825Smm	ctime_ns = archive_entry_ctime_nsec(ae);
1213238825Smm	mtime_sec = archive_entry_mtime(ae);
1214238825Smm	mtime_ns = archive_entry_mtime_nsec(ae);
1215238825Smm	archive_entry_free(ae);
1216238825Smm	return set_timefilter(a, timetype, mtime_sec, mtime_ns,
1217238825Smm			ctime_sec, ctime_ns);
1218238825Smm}
1219238825Smm
1220238825Smmstatic int
1221238825Smmset_timefilter_pathname_mbs(struct archive_match *a, int timetype,
1222238825Smm    const char *path)
1223238825Smm{
1224238825Smm	struct stat st;
1225238825Smm
1226238825Smm	if (path == NULL || *path == '\0') {
1227238825Smm		archive_set_error(&(a->archive), EINVAL, "pathname is empty");
1228238825Smm		return (ARCHIVE_FAILED);
1229238825Smm	}
1230238825Smm	if (stat(path, &st) != 0) {
1231238825Smm		archive_set_error(&(a->archive), errno, "Failed to stat()");
1232238825Smm		return (ARCHIVE_FAILED);
1233238825Smm	}
1234238825Smm	return (set_timefilter_stat(a, timetype, &st));
1235238825Smm}
1236238825Smm
1237238825Smmstatic int
1238238825Smmset_timefilter_pathname_wcs(struct archive_match *a, int timetype,
1239238825Smm    const wchar_t *path)
1240238825Smm{
1241238825Smm	struct archive_string as;
1242238825Smm	int r;
1243238825Smm
1244238825Smm	if (path == NULL || *path == L'\0') {
1245238825Smm		archive_set_error(&(a->archive), EINVAL, "pathname is empty");
1246238825Smm		return (ARCHIVE_FAILED);
1247238825Smm	}
1248238825Smm
1249238825Smm	/* Convert WCS filename to MBS filename. */
1250238825Smm	archive_string_init(&as);
1251238825Smm	if (archive_string_append_from_wcs(&as, path, wcslen(path)) < 0) {
1252238825Smm		archive_string_free(&as);
1253238825Smm		if (errno == ENOMEM)
1254238825Smm			return (error_nomem(a));
1255238825Smm		archive_set_error(&(a->archive), -1,
1256238825Smm		    "Failed to convert WCS to MBS");
1257238825Smm		return (ARCHIVE_FAILED);
1258238825Smm	}
1259238825Smm
1260238825Smm	r = set_timefilter_pathname_mbs(a, timetype, as.s);
1261238825Smm	archive_string_free(&as);
1262238825Smm
1263238825Smm	return (r);
1264238825Smm}
1265238825Smm#endif /* _WIN32 && !__CYGWIN__ */
1266238825Smm
1267238825Smm/*
1268238825Smm * Call back funtions for archive_rb.
1269238825Smm */
1270238825Smmstatic int
1271238825Smmcmp_node_mbs(const struct archive_rb_node *n1,
1272238825Smm    const struct archive_rb_node *n2)
1273238825Smm{
1274238825Smm	struct match_file *f1 = (struct match_file *)(uintptr_t)n1;
1275238825Smm	struct match_file *f2 = (struct match_file *)(uintptr_t)n2;
1276238825Smm	const char *p1, *p2;
1277238825Smm
1278238825Smm	archive_mstring_get_mbs(NULL, &(f1->pathname), &p1);
1279238825Smm	archive_mstring_get_mbs(NULL, &(f2->pathname), &p2);
1280238825Smm	if (p1 == NULL)
1281238825Smm		return (1);
1282238825Smm	if (p2 == NULL)
1283238825Smm		return (-1);
1284238825Smm	return (strcmp(p1, p2));
1285238825Smm}
1286238825Smm
1287238825Smmstatic int
1288238825Smmcmp_key_mbs(const struct archive_rb_node *n, const void *key)
1289238825Smm{
1290238825Smm	struct match_file *f = (struct match_file *)(uintptr_t)n;
1291238825Smm	const char *p;
1292238825Smm
1293238825Smm	archive_mstring_get_mbs(NULL, &(f->pathname), &p);
1294238825Smm	if (p == NULL)
1295238825Smm		return (-1);
1296238825Smm	return (strcmp(p, (const char *)key));
1297238825Smm}
1298238825Smm
1299238825Smmstatic int
1300238825Smmcmp_node_wcs(const struct archive_rb_node *n1,
1301238825Smm    const struct archive_rb_node *n2)
1302238825Smm{
1303238825Smm	struct match_file *f1 = (struct match_file *)(uintptr_t)n1;
1304238825Smm	struct match_file *f2 = (struct match_file *)(uintptr_t)n2;
1305238825Smm	const wchar_t *p1, *p2;
1306238825Smm
1307238825Smm	archive_mstring_get_wcs(NULL, &(f1->pathname), &p1);
1308238825Smm	archive_mstring_get_wcs(NULL, &(f2->pathname), &p2);
1309238825Smm	if (p1 == NULL)
1310238825Smm		return (1);
1311238825Smm	if (p2 == NULL)
1312238825Smm		return (-1);
1313238825Smm	return (wcscmp(p1, p2));
1314238825Smm}
1315238825Smm
1316238825Smmstatic int
1317238825Smmcmp_key_wcs(const struct archive_rb_node *n, const void *key)
1318238825Smm{
1319238825Smm	struct match_file *f = (struct match_file *)(uintptr_t)n;
1320238825Smm	const wchar_t *p;
1321238825Smm
1322238825Smm	archive_mstring_get_wcs(NULL, &(f->pathname), &p);
1323238825Smm	if (p == NULL)
1324238825Smm		return (-1);
1325238825Smm	return (wcscmp(p, (const wchar_t *)key));
1326238825Smm}
1327238825Smm
1328238825Smmstatic void
1329238825Smmentry_list_init(struct entry_list *list)
1330238825Smm{
1331238825Smm	list->first = NULL;
1332238825Smm	list->last = &(list->first);
1333238825Smm	list->count = 0;
1334238825Smm}
1335238825Smm
1336238825Smmstatic void
1337238825Smmentry_list_free(struct entry_list *list)
1338238825Smm{
1339238825Smm	struct match_file *p, *q;
1340238825Smm
1341238825Smm	for (p = list->first; p != NULL; ) {
1342238825Smm		q = p;
1343238825Smm		p = p->next;
1344238825Smm		archive_mstring_clean(&(q->pathname));
1345238825Smm		free(q);
1346238825Smm	}
1347238825Smm}
1348238825Smm
1349238825Smmstatic void
1350238825Smmentry_list_add(struct entry_list *list, struct match_file *file)
1351238825Smm{
1352238825Smm	*list->last = file;
1353238825Smm	list->last = &(file->next);
1354238825Smm	list->count++;
1355238825Smm}
1356238825Smm
1357238825Smmstatic int
1358238825Smmadd_entry(struct archive_match *a, int flag,
1359238825Smm    struct archive_entry *entry)
1360238825Smm{
1361238825Smm	struct match_file *f;
1362238825Smm	const void *pathname;
1363238825Smm	int r;
1364238825Smm
1365238825Smm	f = calloc(1, sizeof(*f));
1366238825Smm	if (f == NULL)
1367238825Smm		return (error_nomem(a));
1368238825Smm
1369238825Smm#if defined(_WIN32) && !defined(__CYGWIN__)
1370238825Smm	pathname = archive_entry_pathname_w(entry);
1371238825Smm	if (pathname == NULL) {
1372238825Smm		free(f);
1373238825Smm		archive_set_error(&(a->archive), EINVAL, "pathname is NULL");
1374238825Smm		return (ARCHIVE_FAILED);
1375238825Smm	}
1376238825Smm	archive_mstring_copy_wcs(&(f->pathname), pathname);
1377238825Smm	a->exclusion_tree.rbt_ops = &rb_ops_wcs;
1378238825Smm#else
1379248616Smm	(void)rb_ops_wcs;
1380238825Smm	pathname = archive_entry_pathname(entry);
1381238825Smm	if (pathname == NULL) {
1382238825Smm		free(f);
1383238825Smm		archive_set_error(&(a->archive), EINVAL, "pathname is NULL");
1384238825Smm		return (ARCHIVE_FAILED);
1385238825Smm	}
1386238825Smm	archive_mstring_copy_mbs(&(f->pathname), pathname);
1387238825Smm	a->exclusion_tree.rbt_ops = &rb_ops_mbs;
1388238825Smm#endif
1389238825Smm	f->flag = flag;
1390238825Smm	f->mtime_sec = archive_entry_mtime(entry);
1391238825Smm	f->mtime_nsec = archive_entry_mtime_nsec(entry);
1392238825Smm	f->ctime_sec = archive_entry_ctime(entry);
1393238825Smm	f->ctime_nsec = archive_entry_ctime_nsec(entry);
1394238825Smm	r = __archive_rb_tree_insert_node(&(a->exclusion_tree), &(f->node));
1395238825Smm	if (!r) {
1396238825Smm		struct match_file *f2;
1397238825Smm
1398238825Smm		/* Get the duplicated file. */
1399238825Smm		f2 = (struct match_file *)__archive_rb_tree_find_node(
1400238825Smm			&(a->exclusion_tree), pathname);
1401238825Smm
1402238825Smm		/*
1403238825Smm		 * We always overwrite comparison condision.
1404238825Smm		 * If you do not want to overwrite it, you should not
1405238825Smm		 * call archive_match_exclude_entry(). We cannot know
1406238825Smm		 * what behavior you really expect since overwriting
1407238825Smm		 * condition might be different with the flag.
1408238825Smm		 */
1409238825Smm		if (f2 != NULL) {
1410238825Smm			f2->flag = f->flag;
1411238825Smm			f2->mtime_sec = f->mtime_sec;
1412238825Smm			f2->mtime_nsec = f->mtime_nsec;
1413238825Smm			f2->ctime_sec = f->ctime_sec;
1414238825Smm			f2->ctime_nsec = f->ctime_nsec;
1415238825Smm		}
1416238825Smm		/* Release the duplicated file. */
1417238825Smm		archive_mstring_clean(&(f->pathname));
1418238825Smm		free(f);
1419238825Smm		return (ARCHIVE_OK);
1420238825Smm	}
1421238825Smm	entry_list_add(&(a->exclusion_entry_list), f);
1422238825Smm	a->setflag |= TIME_IS_SET;
1423238825Smm	return (ARCHIVE_OK);
1424238825Smm}
1425238825Smm
1426238825Smm/*
1427238825Smm * Test if entry is excluded by its timestamp.
1428238825Smm */
1429238825Smmstatic int
1430238825Smmtime_excluded(struct archive_match *a, struct archive_entry *entry)
1431238825Smm{
1432238825Smm	struct match_file *f;
1433238825Smm	const void *pathname;
1434238825Smm	time_t sec;
1435238825Smm	long nsec;
1436238825Smm
1437238825Smm	/*
1438238825Smm	 * If this file/dir is excluded by a time comparison, skip it.
1439238825Smm	 */
1440238825Smm	if (a->newer_ctime_filter) {
1441238825Smm		/* If ctime is not set, use mtime instead. */
1442238825Smm		if (archive_entry_ctime_is_set(entry))
1443238825Smm			sec = archive_entry_ctime(entry);
1444238825Smm		else
1445238825Smm			sec = archive_entry_mtime(entry);
1446238825Smm		if (sec < a->newer_ctime_sec)
1447238825Smm			return (1); /* Too old, skip it. */
1448238825Smm		if (sec == a->newer_ctime_sec) {
1449238825Smm			if (archive_entry_ctime_is_set(entry))
1450238825Smm				nsec = archive_entry_ctime_nsec(entry);
1451238825Smm			else
1452238825Smm				nsec = archive_entry_mtime_nsec(entry);
1453238825Smm			if (nsec < a->newer_ctime_nsec)
1454238825Smm				return (1); /* Too old, skip it. */
1455238825Smm			if (nsec == a->newer_ctime_nsec &&
1456238825Smm			    (a->newer_ctime_filter & ARCHIVE_MATCH_EQUAL)
1457238825Smm			      == 0)
1458238825Smm				return (1); /* Equal, skip it. */
1459238825Smm		}
1460238825Smm	}
1461238825Smm	if (a->older_ctime_filter) {
1462238825Smm		/* If ctime is not set, use mtime instead. */
1463238825Smm		if (archive_entry_ctime_is_set(entry))
1464238825Smm			sec = archive_entry_ctime(entry);
1465238825Smm		else
1466238825Smm			sec = archive_entry_mtime(entry);
1467238825Smm		if (sec > a->older_ctime_sec)
1468238825Smm			return (1); /* Too new, skip it. */
1469238825Smm		if (sec == a->older_ctime_sec) {
1470238825Smm			if (archive_entry_ctime_is_set(entry))
1471238825Smm				nsec = archive_entry_ctime_nsec(entry);
1472238825Smm			else
1473238825Smm				nsec = archive_entry_mtime_nsec(entry);
1474238825Smm			if (nsec > a->older_ctime_nsec)
1475238825Smm				return (1); /* Too new, skip it. */
1476238825Smm			if (nsec == a->older_ctime_nsec &&
1477238825Smm			    (a->older_ctime_filter & ARCHIVE_MATCH_EQUAL)
1478238825Smm			      == 0)
1479238825Smm				return (1); /* Eeual, skip it. */
1480238825Smm		}
1481238825Smm	}
1482238825Smm	if (a->newer_mtime_filter) {
1483238825Smm		sec = archive_entry_mtime(entry);
1484238825Smm		if (sec < a->newer_mtime_sec)
1485238825Smm			return (1); /* Too old, skip it. */
1486238825Smm		if (sec == a->newer_mtime_sec) {
1487238825Smm			nsec = archive_entry_mtime_nsec(entry);
1488238825Smm			if (nsec < a->newer_mtime_nsec)
1489238825Smm				return (1); /* Too old, skip it. */
1490238825Smm			if (nsec == a->newer_mtime_nsec &&
1491238825Smm			    (a->newer_mtime_filter & ARCHIVE_MATCH_EQUAL)
1492238825Smm			       == 0)
1493238825Smm				return (1); /* Equal, skip it. */
1494238825Smm		}
1495238825Smm	}
1496238825Smm	if (a->older_mtime_filter) {
1497238825Smm		sec = archive_entry_mtime(entry);
1498238825Smm		if (sec > a->older_mtime_sec)
1499238825Smm			return (1); /* Too new, skip it. */
1500238825Smm		nsec = archive_entry_mtime_nsec(entry);
1501238825Smm		if (sec == a->older_mtime_sec) {
1502238825Smm			if (nsec > a->older_mtime_nsec)
1503238825Smm				return (1); /* Too new, skip it. */
1504238825Smm			if (nsec == a->older_mtime_nsec &&
1505238825Smm			    (a->older_mtime_filter & ARCHIVE_MATCH_EQUAL)
1506238825Smm			       == 0)
1507238825Smm				return (1); /* Equal, skip it. */
1508238825Smm		}
1509238825Smm	}
1510238825Smm
1511238825Smm	/* If there is no excluson list, include the file. */
1512238825Smm	if (a->exclusion_entry_list.count == 0)
1513238825Smm		return (0);
1514238825Smm
1515238825Smm#if defined(_WIN32) && !defined(__CYGWIN__)
1516238825Smm	pathname = archive_entry_pathname_w(entry);
1517238825Smm	a->exclusion_tree.rbt_ops = &rb_ops_wcs;
1518238825Smm#else
1519248616Smm	(void)rb_ops_wcs;
1520238825Smm	pathname = archive_entry_pathname(entry);
1521238825Smm	a->exclusion_tree.rbt_ops = &rb_ops_mbs;
1522238825Smm#endif
1523238825Smm	if (pathname == NULL)
1524238825Smm		return (0);
1525238825Smm
1526238825Smm	f = (struct match_file *)__archive_rb_tree_find_node(
1527238825Smm		&(a->exclusion_tree), pathname);
1528238825Smm	/* If the file wasn't rejected, include it. */
1529238825Smm	if (f == NULL)
1530238825Smm		return (0);
1531238825Smm
1532238825Smm	if (f->flag & ARCHIVE_MATCH_CTIME) {
1533238825Smm		sec = archive_entry_ctime(entry);
1534238825Smm		if (f->ctime_sec > sec) {
1535238825Smm			if (f->flag & ARCHIVE_MATCH_OLDER)
1536238825Smm				return (1);
1537238825Smm		} else if (f->ctime_sec < sec) {
1538238825Smm			if (f->flag & ARCHIVE_MATCH_NEWER)
1539238825Smm				return (1);
1540238825Smm		} else {
1541238825Smm			nsec = archive_entry_ctime_nsec(entry);
1542238825Smm			if (f->ctime_nsec > nsec) {
1543238825Smm				if (f->flag & ARCHIVE_MATCH_OLDER)
1544238825Smm					return (1);
1545238825Smm			} else if (f->ctime_nsec < nsec) {
1546238825Smm				if (f->flag & ARCHIVE_MATCH_NEWER)
1547238825Smm					return (1);
1548238825Smm			} else if (f->flag & ARCHIVE_MATCH_EQUAL)
1549238825Smm				return (1);
1550238825Smm		}
1551238825Smm	}
1552238825Smm	if (f->flag & ARCHIVE_MATCH_MTIME) {
1553238825Smm		sec = archive_entry_mtime(entry);
1554238825Smm		if (f->mtime_sec > sec) {
1555238825Smm			if (f->flag & ARCHIVE_MATCH_OLDER)
1556238825Smm				return (1);
1557238825Smm		} else if (f->mtime_sec < sec) {
1558238825Smm			if (f->flag & ARCHIVE_MATCH_NEWER)
1559238825Smm				return (1);
1560238825Smm		} else {
1561238825Smm			nsec = archive_entry_mtime_nsec(entry);
1562238825Smm			if (f->mtime_nsec > nsec) {
1563238825Smm				if (f->flag & ARCHIVE_MATCH_OLDER)
1564238825Smm					return (1);
1565238825Smm			} else if (f->mtime_nsec < nsec) {
1566238825Smm				if (f->flag & ARCHIVE_MATCH_NEWER)
1567238825Smm					return (1);
1568238825Smm			} else if (f->flag & ARCHIVE_MATCH_EQUAL)
1569238825Smm				return (1);
1570238825Smm		}
1571238825Smm	}
1572238825Smm	return (0);
1573238825Smm}
1574238825Smm
1575238825Smm/*
1576238825Smm * Utility functions to manage inclusion owners
1577238825Smm */
1578238825Smm
1579238825Smmint
1580238825Smmarchive_match_include_uid(struct archive *_a, int64_t uid)
1581238825Smm{
1582238825Smm	struct archive_match *a;
1583238825Smm
1584238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1585238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_uid");
1586238825Smm	a = (struct archive_match *)_a;
1587238825Smm	return (add_owner_id(a, &(a->inclusion_uids), uid));
1588238825Smm}
1589238825Smm
1590238825Smmint
1591238825Smmarchive_match_include_gid(struct archive *_a, int64_t gid)
1592238825Smm{
1593238825Smm	struct archive_match *a;
1594238825Smm
1595238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1596238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_gid");
1597238825Smm	a = (struct archive_match *)_a;
1598238825Smm	return (add_owner_id(a, &(a->inclusion_gids), gid));
1599238825Smm}
1600238825Smm
1601238825Smmint
1602238825Smmarchive_match_include_uname(struct archive *_a, const char *uname)
1603238825Smm{
1604238825Smm	struct archive_match *a;
1605238825Smm
1606238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1607238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_uname");
1608238825Smm	a = (struct archive_match *)_a;
1609238825Smm	return (add_owner_name(a, &(a->inclusion_unames), 1, uname));
1610238825Smm}
1611238825Smm
1612238825Smmint
1613238825Smmarchive_match_include_uname_w(struct archive *_a, const wchar_t *uname)
1614238825Smm{
1615238825Smm	struct archive_match *a;
1616238825Smm
1617238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1618238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_uname_w");
1619238825Smm	a = (struct archive_match *)_a;
1620238825Smm	return (add_owner_name(a, &(a->inclusion_unames), 0, uname));
1621238825Smm}
1622238825Smm
1623238825Smmint
1624238825Smmarchive_match_include_gname(struct archive *_a, const char *gname)
1625238825Smm{
1626238825Smm	struct archive_match *a;
1627238825Smm
1628238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1629238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_gname");
1630238825Smm	a = (struct archive_match *)_a;
1631238825Smm	return (add_owner_name(a, &(a->inclusion_gnames), 1, gname));
1632238825Smm}
1633238825Smm
1634238825Smmint
1635238825Smmarchive_match_include_gname_w(struct archive *_a, const wchar_t *gname)
1636238825Smm{
1637238825Smm	struct archive_match *a;
1638238825Smm
1639238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1640238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_gname_w");
1641238825Smm	a = (struct archive_match *)_a;
1642238825Smm	return (add_owner_name(a, &(a->inclusion_gnames), 0, gname));
1643238825Smm}
1644238825Smm
1645238825Smm/*
1646238825Smm * Test function for owner(uid, gid, uname, gname).
1647238825Smm *
1648238825Smm * Returns 1 if archive entry is excluded.
1649238825Smm * Returns 0 if archive entry is not excluded.
1650238825Smm * Returns <0 if something error happened.
1651238825Smm */
1652238825Smmint
1653238825Smmarchive_match_owner_excluded(struct archive *_a,
1654238825Smm    struct archive_entry *entry)
1655238825Smm{
1656238825Smm	struct archive_match *a;
1657238825Smm
1658238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1659238825Smm	    ARCHIVE_STATE_NEW, "archive_match_id_excluded_ae");
1660238825Smm
1661238825Smm	a = (struct archive_match *)_a;
1662238825Smm	if (entry == NULL) {
1663238825Smm		archive_set_error(&(a->archive), EINVAL, "entry is NULL");
1664238825Smm		return (ARCHIVE_FAILED);
1665238825Smm	}
1666238825Smm
1667238825Smm	/* If we don't have inclusion id set at all, the entry is always
1668238825Smm	 * not excluded. */
1669238825Smm	if ((a->setflag & ID_IS_SET) == 0)
1670238825Smm		return (0);
1671238825Smm	return (owner_excluded(a, entry));
1672238825Smm}
1673238825Smm
1674238825Smmstatic int
1675238825Smmadd_owner_id(struct archive_match *a, struct id_array *ids, int64_t id)
1676238825Smm{
1677238825Smm	unsigned i;
1678238825Smm
1679238825Smm	if (ids->count + 1 >= ids->size) {
1680248616Smm		void *p;
1681248616Smm
1682238825Smm		if (ids->size == 0)
1683238825Smm			ids->size = 8;
1684238825Smm		else
1685238825Smm			ids->size *= 2;
1686248616Smm		p = realloc(ids->ids, sizeof(*ids->ids) * ids->size);
1687248616Smm		if (p == NULL)
1688238825Smm			return (error_nomem(a));
1689248616Smm		ids->ids = (int64_t *)p;
1690238825Smm	}
1691238825Smm
1692238825Smm	/* Find an insert point. */
1693238825Smm	for (i = 0; i < ids->count; i++) {
1694238825Smm		if (ids->ids[i] >= id)
1695238825Smm			break;
1696238825Smm	}
1697238825Smm
1698238825Smm	/* Add oowner id. */
1699238825Smm	if (i == ids->count)
1700238825Smm		ids->ids[ids->count++] = id;
1701238825Smm	else if (ids->ids[i] != id) {
1702238825Smm		memmove(&(ids->ids[i+1]), &(ids->ids[i]),
1703238825Smm		    (ids->count - i) * sizeof(ids->ids[0]));
1704238825Smm		ids->ids[i] = id;
1705238825Smm		ids->count++;
1706238825Smm	}
1707238825Smm	a->setflag |= ID_IS_SET;
1708238825Smm	return (ARCHIVE_OK);
1709238825Smm}
1710238825Smm
1711238825Smmstatic int
1712238825Smmmatch_owner_id(struct id_array *ids, int64_t id)
1713238825Smm{
1714238825Smm	unsigned b, m, t;
1715238825Smm
1716238825Smm	t = 0;
1717248616Smm	b = (unsigned)ids->count;
1718238825Smm	while (t < b) {
1719238825Smm		m = (t + b)>>1;
1720238825Smm		if (ids->ids[m] == id)
1721238825Smm			return (1);
1722238825Smm		if (ids->ids[m] < id)
1723238825Smm			t = m + 1;
1724238825Smm		else
1725238825Smm			b = m;
1726238825Smm	}
1727238825Smm	return (0);
1728238825Smm}
1729238825Smm
1730238825Smmstatic int
1731238825Smmadd_owner_name(struct archive_match *a, struct match_list *list,
1732238825Smm    int mbs, const void *name)
1733238825Smm{
1734238825Smm	struct match *match;
1735238825Smm
1736238825Smm	match = calloc(1, sizeof(*match));
1737238825Smm	if (match == NULL)
1738238825Smm		return (error_nomem(a));
1739238825Smm	if (mbs)
1740238825Smm		archive_mstring_copy_mbs(&(match->pattern), name);
1741238825Smm	else
1742238825Smm		archive_mstring_copy_wcs(&(match->pattern), name);
1743238825Smm	match_list_add(list, match);
1744238825Smm	a->setflag |= ID_IS_SET;
1745238825Smm	return (ARCHIVE_OK);
1746238825Smm}
1747238825Smm
1748238825Smm#if !defined(_WIN32) || defined(__CYGWIN__)
1749238825Smmstatic int
1750238825Smmmatch_owner_name_mbs(struct archive_match *a, struct match_list *list,
1751238825Smm    const char *name)
1752238825Smm{
1753238825Smm	struct match *m;
1754238825Smm	const char *p;
1755238825Smm
1756238825Smm	if (name == NULL || *name == '\0')
1757238825Smm		return (0);
1758238825Smm	for (m = list->first; m; m = m->next) {
1759238825Smm		if (archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p)
1760238825Smm		    < 0 && errno == ENOMEM)
1761238825Smm			return (error_nomem(a));
1762238825Smm		if (p != NULL && strcmp(p, name) == 0) {
1763238825Smm			m->matches++;
1764238825Smm			return (1);
1765238825Smm		}
1766238825Smm	}
1767238825Smm	return (0);
1768238825Smm}
1769238825Smm#else
1770238825Smmstatic int
1771238825Smmmatch_owner_name_wcs(struct archive_match *a, struct match_list *list,
1772238825Smm    const wchar_t *name)
1773238825Smm{
1774238825Smm	struct match *m;
1775238825Smm	const wchar_t *p;
1776238825Smm
1777238825Smm	if (name == NULL || *name == L'\0')
1778238825Smm		return (0);
1779238825Smm	for (m = list->first; m; m = m->next) {
1780238825Smm		if (archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p)
1781238825Smm		    < 0 && errno == ENOMEM)
1782238825Smm			return (error_nomem(a));
1783238825Smm		if (p != NULL && wcscmp(p, name) == 0) {
1784238825Smm			m->matches++;
1785238825Smm			return (1);
1786238825Smm		}
1787238825Smm	}
1788238825Smm	return (0);
1789238825Smm}
1790238825Smm#endif
1791238825Smm
1792238825Smm/*
1793238825Smm * Test if entry is excluded by uid, gid, uname or gname.
1794238825Smm */
1795238825Smmstatic int
1796238825Smmowner_excluded(struct archive_match *a, struct archive_entry *entry)
1797238825Smm{
1798238825Smm	int r;
1799238825Smm
1800238825Smm	if (a->inclusion_uids.count) {
1801238825Smm		if (!match_owner_id(&(a->inclusion_uids),
1802238825Smm		    archive_entry_uid(entry)))
1803238825Smm			return (1);
1804238825Smm	}
1805238825Smm
1806238825Smm	if (a->inclusion_gids.count) {
1807238825Smm		if (!match_owner_id(&(a->inclusion_gids),
1808238825Smm		    archive_entry_gid(entry)))
1809238825Smm			return (1);
1810238825Smm	}
1811238825Smm
1812238825Smm	if (a->inclusion_unames.count) {
1813238825Smm#if defined(_WIN32) && !defined(__CYGWIN__)
1814238825Smm		r = match_owner_name_wcs(a, &(a->inclusion_unames),
1815238825Smm			archive_entry_uname_w(entry));
1816238825Smm#else
1817238825Smm		r = match_owner_name_mbs(a, &(a->inclusion_unames),
1818238825Smm			archive_entry_uname(entry));
1819238825Smm#endif
1820238825Smm		if (!r)
1821238825Smm			return (1);
1822238825Smm		else if (r < 0)
1823238825Smm			return (r);
1824238825Smm	}
1825238825Smm
1826238825Smm	if (a->inclusion_gnames.count) {
1827238825Smm#if defined(_WIN32) && !defined(__CYGWIN__)
1828238825Smm		r = match_owner_name_wcs(a, &(a->inclusion_gnames),
1829238825Smm			archive_entry_gname_w(entry));
1830238825Smm#else
1831238825Smm		r = match_owner_name_mbs(a, &(a->inclusion_gnames),
1832238825Smm			archive_entry_gname(entry));
1833238825Smm#endif
1834238825Smm		if (!r)
1835238825Smm			return (1);
1836238825Smm		else if (r < 0)
1837238825Smm			return (r);
1838238825Smm	}
1839238825Smm	return (0);
1840238825Smm}
1841238825Smm
1842