archive_match.c revision 305188
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"
43299529Smm#include "archive_getdate.h"
44238825Smm#include "archive_pathmatch.h"
45238825Smm#include "archive_rb.h"
46238825Smm#include "archive_string.h"
47238825Smm
48238825Smmstruct match {
49238825Smm	struct match		*next;
50238825Smm	int			 matches;
51238825Smm	struct archive_mstring	 pattern;
52238825Smm};
53238825Smm
54238825Smmstruct match_list {
55238825Smm	struct match		*first;
56238825Smm	struct match		**last;
57238825Smm	int			 count;
58238825Smm	int			 unmatched_count;
59238825Smm	struct match		*unmatched_next;
60238825Smm	int			 unmatched_eof;
61238825Smm};
62238825Smm
63238825Smmstruct match_file {
64238825Smm	struct archive_rb_node	 node;
65238825Smm	struct match_file	*next;
66238825Smm	struct archive_mstring	 pathname;
67238825Smm	int			 flag;
68238825Smm	time_t			 mtime_sec;
69238825Smm	long			 mtime_nsec;
70238825Smm	time_t			 ctime_sec;
71238825Smm	long			 ctime_nsec;
72238825Smm};
73238825Smm
74238825Smmstruct entry_list {
75238825Smm	struct match_file	*first;
76238825Smm	struct match_file	**last;
77238825Smm	int			 count;
78238825Smm};
79238825Smm
80238825Smmstruct id_array {
81238825Smm	size_t			 size;/* Allocated size */
82238825Smm	size_t			 count;
83238825Smm	int64_t			*ids;
84238825Smm};
85238825Smm
86238825Smm#define PATTERN_IS_SET		1
87238825Smm#define TIME_IS_SET		2
88238825Smm#define ID_IS_SET		4
89238825Smm
90238825Smmstruct archive_match {
91238825Smm	struct archive		 archive;
92238825Smm
93238825Smm	/* exclusion/inclusion set flag. */
94238825Smm	int			 setflag;
95238825Smm
96238825Smm	/*
97238825Smm	 * Matching filename patterns.
98238825Smm	 */
99238825Smm	struct match_list	 exclusions;
100238825Smm	struct match_list	 inclusions;
101238825Smm
102238825Smm	/*
103238825Smm	 * Matching time stamps.
104238825Smm	 */
105238825Smm	time_t			 now;
106238825Smm	int			 newer_mtime_filter;
107238825Smm	time_t			 newer_mtime_sec;
108238825Smm	long			 newer_mtime_nsec;
109238825Smm	int			 newer_ctime_filter;
110238825Smm	time_t			 newer_ctime_sec;
111238825Smm	long			 newer_ctime_nsec;
112238825Smm	int			 older_mtime_filter;
113238825Smm	time_t			 older_mtime_sec;
114238825Smm	long			 older_mtime_nsec;
115238825Smm	int			 older_ctime_filter;
116238825Smm	time_t			 older_ctime_sec;
117238825Smm	long			 older_ctime_nsec;
118238825Smm	/*
119238825Smm	 * Matching time stamps with its filename.
120238825Smm	 */
121238825Smm	struct archive_rb_tree	 exclusion_tree;
122238825Smm	struct entry_list 	 exclusion_entry_list;
123238825Smm
124238825Smm	/*
125238825Smm	 * Matching file owners.
126238825Smm	 */
127238825Smm	struct id_array 	 inclusion_uids;
128238825Smm	struct id_array 	 inclusion_gids;
129238825Smm	struct match_list	 inclusion_unames;
130238825Smm	struct match_list	 inclusion_gnames;
131238825Smm};
132238825Smm
133238825Smmstatic int	add_pattern_from_file(struct archive_match *,
134238825Smm		    struct match_list *, int, const void *, int);
135238825Smmstatic int	add_entry(struct archive_match *, int,
136238825Smm		    struct archive_entry *);
137238825Smmstatic int	add_owner_id(struct archive_match *, struct id_array *,
138238825Smm		    int64_t);
139238825Smmstatic int	add_owner_name(struct archive_match *, struct match_list *,
140238825Smm		    int, const void *);
141238825Smmstatic int	add_pattern_mbs(struct archive_match *, struct match_list *,
142238825Smm		    const char *);
143238825Smmstatic int	add_pattern_wcs(struct archive_match *, struct match_list *,
144238825Smm		    const wchar_t *);
145238825Smmstatic int	cmp_key_mbs(const struct archive_rb_node *, const void *);
146238825Smmstatic int	cmp_key_wcs(const struct archive_rb_node *, const void *);
147238825Smmstatic int	cmp_node_mbs(const struct archive_rb_node *,
148238825Smm		    const struct archive_rb_node *);
149238825Smmstatic int	cmp_node_wcs(const struct archive_rb_node *,
150238825Smm		    const struct archive_rb_node *);
151238825Smmstatic void	entry_list_add(struct entry_list *, struct match_file *);
152238825Smmstatic void	entry_list_free(struct entry_list *);
153238825Smmstatic void	entry_list_init(struct entry_list *);
154238825Smmstatic int	error_nomem(struct archive_match *);
155238825Smmstatic void	match_list_add(struct match_list *, struct match *);
156238825Smmstatic void	match_list_free(struct match_list *);
157238825Smmstatic void	match_list_init(struct match_list *);
158238825Smmstatic int	match_list_unmatched_inclusions_next(struct archive_match *,
159238825Smm		    struct match_list *, int, const void **);
160238825Smmstatic int	match_owner_id(struct id_array *, int64_t);
161238825Smm#if !defined(_WIN32) || defined(__CYGWIN__)
162238825Smmstatic int	match_owner_name_mbs(struct archive_match *,
163238825Smm		    struct match_list *, const char *);
164238825Smm#else
165238825Smmstatic int	match_owner_name_wcs(struct archive_match *,
166238825Smm		    struct match_list *, const wchar_t *);
167238825Smm#endif
168238825Smmstatic int	match_path_exclusion(struct archive_match *,
169238825Smm		    struct match *, int, const void *);
170238825Smmstatic int	match_path_inclusion(struct archive_match *,
171238825Smm		    struct match *, int, const void *);
172238825Smmstatic int	owner_excluded(struct archive_match *,
173238825Smm		    struct archive_entry *);
174238825Smmstatic int	path_excluded(struct archive_match *, int, const void *);
175238825Smmstatic int	set_timefilter(struct archive_match *, int, time_t, long,
176238825Smm		    time_t, long);
177238825Smmstatic int	set_timefilter_pathname_mbs(struct archive_match *,
178238825Smm		    int, const char *);
179238825Smmstatic int	set_timefilter_pathname_wcs(struct archive_match *,
180238825Smm		    int, const wchar_t *);
181238825Smmstatic int	set_timefilter_date(struct archive_match *, int, const char *);
182238825Smmstatic int	set_timefilter_date_w(struct archive_match *, int,
183238825Smm		    const wchar_t *);
184238825Smmstatic int	time_excluded(struct archive_match *,
185238825Smm		    struct archive_entry *);
186238825Smmstatic int	validate_time_flag(struct archive *, int, const char *);
187238825Smm
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);
583299529Smm	r = archive_read_support_format_empty(ar);
584238825Smm	if (r != ARCHIVE_OK) {
585238825Smm		archive_copy_error(&(a->archive), ar);
586238825Smm		archive_read_free(ar);
587238825Smm		return (r);
588238825Smm	}
589238825Smm	if (mbs)
590238825Smm		r = archive_read_open_filename(ar, pathname, 512*20);
591238825Smm	else
592238825Smm		r = archive_read_open_filename_w(ar, pathname, 512*20);
593238825Smm	if (r != ARCHIVE_OK) {
594238825Smm		archive_copy_error(&(a->archive), ar);
595238825Smm		archive_read_free(ar);
596238825Smm		return (r);
597238825Smm	}
598238825Smm	r = archive_read_next_header(ar, &ae);
599238825Smm	if (r != ARCHIVE_OK) {
600238825Smm		archive_read_free(ar);
601299529Smm		if (r == ARCHIVE_EOF) {
602299529Smm			return (ARCHIVE_OK);
603299529Smm		} else {
604299529Smm			archive_copy_error(&(a->archive), ar);
605299529Smm			return (r);
606299529Smm		}
607238825Smm	}
608238825Smm
609238825Smm	archive_string_init(&as);
610238825Smm
611238825Smm	while ((r = archive_read_data_block(ar, &buff, &size, &offset))
612238825Smm	    == ARCHIVE_OK) {
613238825Smm		const char *b = (const char *)buff;
614238825Smm
615238825Smm		while (size) {
616238825Smm			const char *s = (const char *)b;
617238825Smm			size_t length = 0;
618238825Smm			int found_separator = 0;
619238825Smm
620238825Smm			while (length < size) {
621238825Smm				if (nullSeparator) {
622238825Smm					if (*b == '\0') {
623238825Smm						found_separator = 1;
624238825Smm						break;
625238825Smm					}
626238825Smm				} else {
627238825Smm			            	if (*b == 0x0d || *b == 0x0a) {
628238825Smm						found_separator = 1;
629238825Smm						break;
630238825Smm					}
631238825Smm				}
632238825Smm				b++;
633238825Smm				length++;
634238825Smm			}
635238825Smm			if (!found_separator) {
636238825Smm				archive_strncat(&as, s, length);
637238825Smm				/* Read next data block. */
638238825Smm				break;
639238825Smm			}
640238825Smm			b++;
641238825Smm			size -= length + 1;
642238825Smm			archive_strncat(&as, s, length);
643238825Smm
644238825Smm			/* If the line is not empty, add the pattern. */
645238825Smm			if (archive_strlen(&as) > 0) {
646238825Smm				/* Add pattern. */
647238825Smm				r = add_pattern_mbs(a, mlist, as.s);
648238825Smm				if (r != ARCHIVE_OK) {
649238825Smm					archive_read_free(ar);
650238825Smm					archive_string_free(&as);
651238825Smm					return (r);
652238825Smm				}
653238825Smm				archive_string_empty(&as);
654238825Smm			}
655238825Smm		}
656238825Smm	}
657238825Smm
658305188Smm	/* If an error occurred, report it immediately. */
659238825Smm	if (r < ARCHIVE_OK) {
660238825Smm		archive_copy_error(&(a->archive), ar);
661238825Smm		archive_read_free(ar);
662238825Smm		archive_string_free(&as);
663238825Smm		return (r);
664238825Smm	}
665238825Smm
666238825Smm	/* If the line is not empty, add the pattern. */
667238825Smm	if (r == ARCHIVE_EOF && archive_strlen(&as) > 0) {
668238825Smm		/* Add pattern. */
669238825Smm		r = add_pattern_mbs(a, mlist, as.s);
670238825Smm		if (r != ARCHIVE_OK) {
671238825Smm			archive_read_free(ar);
672238825Smm			archive_string_free(&as);
673238825Smm			return (r);
674238825Smm		}
675238825Smm	}
676238825Smm	archive_read_free(ar);
677238825Smm	archive_string_free(&as);
678238825Smm	return (ARCHIVE_OK);
679238825Smm}
680238825Smm
681238825Smm/*
682238825Smm * Test if pathname is excluded by inclusion/exclusion patterns.
683238825Smm */
684238825Smmstatic int
685238825Smmpath_excluded(struct archive_match *a, int mbs, const void *pathname)
686238825Smm{
687238825Smm	struct match *match;
688238825Smm	struct match *matched;
689238825Smm	int r;
690238825Smm
691238825Smm	if (a == NULL)
692238825Smm		return (0);
693238825Smm
694238825Smm	/* Mark off any unmatched inclusions. */
695238825Smm	/* In particular, if a filename does appear in the archive and
696238825Smm	 * is explicitly included and excluded, then we don't report
697238825Smm	 * it as missing even though we don't extract it.
698238825Smm	 */
699238825Smm	matched = NULL;
700238825Smm	for (match = a->inclusions.first; match != NULL;
701238825Smm	    match = match->next){
702238825Smm		if (match->matches == 0 &&
703238825Smm		    (r = match_path_inclusion(a, match, mbs, pathname)) != 0) {
704238825Smm			if (r < 0)
705238825Smm				return (r);
706238825Smm			a->inclusions.unmatched_count--;
707238825Smm			match->matches++;
708238825Smm			matched = match;
709238825Smm		}
710238825Smm	}
711238825Smm
712238825Smm	/* Exclusions take priority */
713238825Smm	for (match = a->exclusions.first; match != NULL;
714238825Smm	    match = match->next){
715238825Smm		r = match_path_exclusion(a, match, mbs, pathname);
716238825Smm		if (r)
717238825Smm			return (r);
718238825Smm	}
719238825Smm
720238825Smm	/* It's not excluded and we found an inclusion above, so it's
721238825Smm	 * included. */
722238825Smm	if (matched != NULL)
723238825Smm		return (0);
724238825Smm
725238825Smm
726238825Smm	/* We didn't find an unmatched inclusion, check the remaining ones. */
727238825Smm	for (match = a->inclusions.first; match != NULL;
728238825Smm	    match = match->next){
729238825Smm		/* We looked at previously-unmatched inclusions already. */
730238825Smm		if (match->matches > 0 &&
731238825Smm		    (r = match_path_inclusion(a, match, mbs, pathname)) != 0) {
732238825Smm			if (r < 0)
733238825Smm				return (r);
734238825Smm			match->matches++;
735238825Smm			return (0);
736238825Smm		}
737238825Smm	}
738238825Smm
739238825Smm	/* If there were inclusions, default is to exclude. */
740238825Smm	if (a->inclusions.first != NULL)
741238825Smm	    return (1);
742238825Smm
743238825Smm	/* No explicit inclusions, default is to match. */
744238825Smm	return (0);
745238825Smm}
746238825Smm
747238825Smm/*
748238825Smm * This is a little odd, but it matches the default behavior of
749238825Smm * gtar.  In particular, 'a*b' will match 'foo/a1111/222b/bar'
750238825Smm *
751238825Smm */
752238825Smmstatic int
753238825Smmmatch_path_exclusion(struct archive_match *a, struct match *m,
754238825Smm    int mbs, const void *pn)
755238825Smm{
756238825Smm	int flag = PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END;
757238825Smm	int r;
758238825Smm
759238825Smm	if (mbs) {
760238825Smm		const char *p;
761238825Smm		r = archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p);
762238825Smm		if (r == 0)
763238825Smm			return (archive_pathmatch(p, (const char *)pn, flag));
764238825Smm	} else {
765238825Smm		const wchar_t *p;
766238825Smm		r = archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p);
767238825Smm		if (r == 0)
768238825Smm			return (archive_pathmatch_w(p, (const wchar_t *)pn,
769238825Smm				flag));
770238825Smm	}
771238825Smm	if (errno == ENOMEM)
772238825Smm		return (error_nomem(a));
773238825Smm	return (0);
774238825Smm}
775238825Smm
776238825Smm/*
777238825Smm * Again, mimic gtar:  inclusions are always anchored (have to match
778238825Smm * the beginning of the path) even though exclusions are not anchored.
779238825Smm */
780238825Smmstatic int
781238825Smmmatch_path_inclusion(struct archive_match *a, struct match *m,
782238825Smm    int mbs, const void *pn)
783238825Smm{
784238825Smm	int flag = PATHMATCH_NO_ANCHOR_END;
785238825Smm	int r;
786238825Smm
787238825Smm	if (mbs) {
788238825Smm		const char *p;
789238825Smm		r = archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p);
790238825Smm		if (r == 0)
791238825Smm			return (archive_pathmatch(p, (const char *)pn, flag));
792238825Smm	} else {
793238825Smm		const wchar_t *p;
794238825Smm		r = archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p);
795238825Smm		if (r == 0)
796238825Smm			return (archive_pathmatch_w(p, (const wchar_t *)pn,
797238825Smm				flag));
798238825Smm	}
799238825Smm	if (errno == ENOMEM)
800238825Smm		return (error_nomem(a));
801238825Smm	return (0);
802238825Smm}
803238825Smm
804238825Smmstatic void
805238825Smmmatch_list_init(struct match_list *list)
806238825Smm{
807238825Smm	list->first = NULL;
808238825Smm	list->last = &(list->first);
809238825Smm	list->count = 0;
810238825Smm}
811238825Smm
812238825Smmstatic void
813238825Smmmatch_list_free(struct match_list *list)
814238825Smm{
815238825Smm	struct match *p, *q;
816238825Smm
817238825Smm	for (p = list->first; p != NULL; ) {
818238825Smm		q = p;
819238825Smm		p = p->next;
820238825Smm		archive_mstring_clean(&(q->pattern));
821238825Smm		free(q);
822238825Smm	}
823238825Smm}
824238825Smm
825238825Smmstatic void
826238825Smmmatch_list_add(struct match_list *list, struct match *m)
827238825Smm{
828238825Smm	*list->last = m;
829238825Smm	list->last = &(m->next);
830238825Smm	list->count++;
831238825Smm	list->unmatched_count++;
832238825Smm}
833238825Smm
834238825Smmstatic int
835238825Smmmatch_list_unmatched_inclusions_next(struct archive_match *a,
836238825Smm    struct match_list *list, int mbs, const void **vp)
837238825Smm{
838238825Smm	struct match *m;
839238825Smm
840238825Smm	*vp = NULL;
841238825Smm	if (list->unmatched_eof) {
842238825Smm		list->unmatched_eof = 0;
843238825Smm		return (ARCHIVE_EOF);
844238825Smm	}
845238825Smm	if (list->unmatched_next == NULL) {
846238825Smm		if (list->unmatched_count == 0)
847238825Smm			return (ARCHIVE_EOF);
848238825Smm		list->unmatched_next = list->first;
849238825Smm	}
850238825Smm
851238825Smm	for (m = list->unmatched_next; m != NULL; m = m->next) {
852238825Smm		int r;
853238825Smm
854238825Smm		if (m->matches)
855238825Smm			continue;
856238825Smm		if (mbs) {
857238825Smm			const char *p;
858238825Smm			r = archive_mstring_get_mbs(&(a->archive),
859238825Smm				&(m->pattern), &p);
860238825Smm			if (r < 0 && errno == ENOMEM)
861238825Smm				return (error_nomem(a));
862238825Smm			if (p == NULL)
863238825Smm				p = "";
864238825Smm			*vp = p;
865238825Smm		} else {
866238825Smm			const wchar_t *p;
867238825Smm			r = archive_mstring_get_wcs(&(a->archive),
868238825Smm				&(m->pattern), &p);
869238825Smm			if (r < 0 && errno == ENOMEM)
870238825Smm				return (error_nomem(a));
871238825Smm			if (p == NULL)
872238825Smm				p = L"";
873238825Smm			*vp = p;
874238825Smm		}
875238825Smm		list->unmatched_next = m->next;
876238825Smm		if (list->unmatched_next == NULL)
877238825Smm			/* To return EOF next time. */
878238825Smm			list->unmatched_eof = 1;
879238825Smm		return (ARCHIVE_OK);
880238825Smm	}
881238825Smm	list->unmatched_next = NULL;
882238825Smm	return (ARCHIVE_EOF);
883238825Smm}
884238825Smm
885238825Smm/*
886238825Smm * Utility functions to manage inclusion timestamps.
887238825Smm */
888238825Smmint
889238825Smmarchive_match_include_time(struct archive *_a, int flag, time_t sec,
890238825Smm    long nsec)
891238825Smm{
892238825Smm	int r;
893238825Smm
894238825Smm	r = validate_time_flag(_a, flag, "archive_match_include_time");
895238825Smm	if (r != ARCHIVE_OK)
896238825Smm		return (r);
897238825Smm	return set_timefilter((struct archive_match *)_a, flag,
898238825Smm			sec, nsec, sec, nsec);
899238825Smm}
900238825Smm
901238825Smmint
902238825Smmarchive_match_include_date(struct archive *_a, int flag,
903238825Smm    const char *datestr)
904238825Smm{
905238825Smm	int r;
906238825Smm
907238825Smm	r = validate_time_flag(_a, flag, "archive_match_include_date");
908238825Smm	if (r != ARCHIVE_OK)
909238825Smm		return (r);
910238825Smm	return set_timefilter_date((struct archive_match *)_a, flag, datestr);
911238825Smm}
912238825Smm
913238825Smmint
914238825Smmarchive_match_include_date_w(struct archive *_a, int flag,
915238825Smm    const wchar_t *datestr)
916238825Smm{
917238825Smm	int r;
918238825Smm
919238825Smm	r = validate_time_flag(_a, flag, "archive_match_include_date_w");
920238825Smm	if (r != ARCHIVE_OK)
921238825Smm		return (r);
922238825Smm
923238825Smm	return set_timefilter_date_w((struct archive_match *)_a, flag, datestr);
924238825Smm}
925238825Smm
926238825Smmint
927238825Smmarchive_match_include_file_time(struct archive *_a, int flag,
928238825Smm    const char *pathname)
929238825Smm{
930238825Smm	int r;
931238825Smm
932238825Smm	r = validate_time_flag(_a, flag, "archive_match_include_file_time");
933238825Smm	if (r != ARCHIVE_OK)
934238825Smm		return (r);
935238825Smm	return set_timefilter_pathname_mbs((struct archive_match *)_a,
936238825Smm			flag, pathname);
937238825Smm}
938238825Smm
939238825Smmint
940238825Smmarchive_match_include_file_time_w(struct archive *_a, int flag,
941238825Smm    const wchar_t *pathname)
942238825Smm{
943238825Smm	int r;
944238825Smm
945238825Smm	r = validate_time_flag(_a, flag, "archive_match_include_file_time_w");
946238825Smm	if (r != ARCHIVE_OK)
947238825Smm		return (r);
948238825Smm	return set_timefilter_pathname_wcs((struct archive_match *)_a,
949238825Smm			flag, pathname);
950238825Smm}
951238825Smm
952238825Smmint
953238825Smmarchive_match_exclude_entry(struct archive *_a, int flag,
954238825Smm    struct archive_entry *entry)
955238825Smm{
956238825Smm	struct archive_match *a;
957238825Smm	int r;
958238825Smm
959238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
960238825Smm	    ARCHIVE_STATE_NEW, "archive_match_time_include_entry");
961238825Smm	a = (struct archive_match *)_a;
962238825Smm
963238825Smm	if (entry == NULL) {
964238825Smm		archive_set_error(&(a->archive), EINVAL, "entry is NULL");
965238825Smm		return (ARCHIVE_FAILED);
966238825Smm	}
967238825Smm	r = validate_time_flag(_a, flag, "archive_match_exclude_entry");
968238825Smm	if (r != ARCHIVE_OK)
969238825Smm		return (r);
970238825Smm	return (add_entry(a, flag, entry));
971238825Smm}
972238825Smm
973238825Smm/*
974238825Smm * Test function for time stamps.
975238825Smm *
976238825Smm * Returns 1 if archive entry is excluded.
977238825Smm * Returns 0 if archive entry is not excluded.
978238825Smm * Returns <0 if something error happened.
979238825Smm */
980238825Smmint
981238825Smmarchive_match_time_excluded(struct archive *_a,
982238825Smm    struct archive_entry *entry)
983238825Smm{
984238825Smm	struct archive_match *a;
985238825Smm
986238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
987238825Smm	    ARCHIVE_STATE_NEW, "archive_match_time_excluded_ae");
988238825Smm
989238825Smm	a = (struct archive_match *)_a;
990238825Smm	if (entry == NULL) {
991238825Smm		archive_set_error(&(a->archive), EINVAL, "entry is NULL");
992238825Smm		return (ARCHIVE_FAILED);
993238825Smm	}
994238825Smm
995238825Smm	/* If we don't have inclusion time set at all, the entry is always
996238825Smm	 * not excluded. */
997238825Smm	if ((a->setflag & TIME_IS_SET) == 0)
998238825Smm		return (0);
999238825Smm	return (time_excluded(a, entry));
1000238825Smm}
1001238825Smm
1002238825Smmstatic int
1003238825Smmvalidate_time_flag(struct archive *_a, int flag, const char *_fn)
1004238825Smm{
1005238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1006238825Smm	    ARCHIVE_STATE_NEW, _fn);
1007238825Smm
1008238825Smm	/* Check a type of time. */
1009238825Smm	if (flag &
1010238825Smm	   ((~(ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_CTIME)) & 0xff00)) {
1011238825Smm		archive_set_error(_a, EINVAL, "Invalid time flag");
1012238825Smm		return (ARCHIVE_FAILED);
1013238825Smm	}
1014238825Smm	if ((flag & (ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_CTIME)) == 0) {
1015238825Smm		archive_set_error(_a, EINVAL, "No time flag");
1016238825Smm		return (ARCHIVE_FAILED);
1017238825Smm	}
1018238825Smm
1019238825Smm	/* Check a type of comparison. */
1020238825Smm	if (flag &
1021238825Smm	   ((~(ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER
1022238825Smm			| ARCHIVE_MATCH_EQUAL)) & 0x00ff)) {
1023238825Smm		archive_set_error(_a, EINVAL, "Invalid comparison flag");
1024238825Smm		return (ARCHIVE_FAILED);
1025238825Smm	}
1026238825Smm	if ((flag & (ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER
1027238825Smm	    | ARCHIVE_MATCH_EQUAL)) == 0) {
1028238825Smm		archive_set_error(_a, EINVAL, "No comparison flag");
1029238825Smm		return (ARCHIVE_FAILED);
1030238825Smm	}
1031238825Smm
1032238825Smm	return (ARCHIVE_OK);
1033238825Smm}
1034238825Smm
1035238825Smm#define JUST_EQUAL(t) (((t) &  (ARCHIVE_MATCH_EQUAL |\
1036238825Smm	ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER)) == ARCHIVE_MATCH_EQUAL)
1037238825Smmstatic int
1038238825Smmset_timefilter(struct archive_match *a, int timetype,
1039238825Smm    time_t mtime_sec, long mtime_nsec, time_t ctime_sec, long ctime_nsec)
1040238825Smm{
1041238825Smm	if (timetype & ARCHIVE_MATCH_MTIME) {
1042238825Smm		if ((timetype & ARCHIVE_MATCH_NEWER) || JUST_EQUAL(timetype)) {
1043238825Smm			a->newer_mtime_filter = timetype;
1044238825Smm			a->newer_mtime_sec = mtime_sec;
1045238825Smm			a->newer_mtime_nsec = mtime_nsec;
1046238825Smm			a->setflag |= TIME_IS_SET;
1047238825Smm		}
1048238825Smm		if ((timetype & ARCHIVE_MATCH_OLDER) || JUST_EQUAL(timetype)) {
1049238825Smm			a->older_mtime_filter = timetype;
1050238825Smm			a->older_mtime_sec = mtime_sec;
1051238825Smm			a->older_mtime_nsec = mtime_nsec;
1052238825Smm			a->setflag |= TIME_IS_SET;
1053238825Smm		}
1054238825Smm	}
1055238825Smm	if (timetype & ARCHIVE_MATCH_CTIME) {
1056238825Smm		if ((timetype & ARCHIVE_MATCH_NEWER) || JUST_EQUAL(timetype)) {
1057238825Smm			a->newer_ctime_filter = timetype;
1058238825Smm			a->newer_ctime_sec = ctime_sec;
1059238825Smm			a->newer_ctime_nsec = ctime_nsec;
1060238825Smm			a->setflag |= TIME_IS_SET;
1061238825Smm		}
1062238825Smm		if ((timetype & ARCHIVE_MATCH_OLDER) || JUST_EQUAL(timetype)) {
1063238825Smm			a->older_ctime_filter = timetype;
1064238825Smm			a->older_ctime_sec = ctime_sec;
1065238825Smm			a->older_ctime_nsec = ctime_nsec;
1066238825Smm			a->setflag |= TIME_IS_SET;
1067238825Smm		}
1068238825Smm	}
1069238825Smm	return (ARCHIVE_OK);
1070238825Smm}
1071238825Smm
1072238825Smmstatic int
1073238825Smmset_timefilter_date(struct archive_match *a, int timetype, const char *datestr)
1074238825Smm{
1075238825Smm	time_t t;
1076238825Smm
1077238825Smm	if (datestr == NULL || *datestr == '\0') {
1078238825Smm		archive_set_error(&(a->archive), EINVAL, "date is empty");
1079238825Smm		return (ARCHIVE_FAILED);
1080238825Smm	}
1081238825Smm	t = get_date(a->now, datestr);
1082238825Smm	if (t == (time_t)-1) {
1083238825Smm		archive_set_error(&(a->archive), EINVAL, "invalid date string");
1084238825Smm		return (ARCHIVE_FAILED);
1085238825Smm	}
1086238825Smm	return set_timefilter(a, timetype, t, 0, t, 0);
1087238825Smm}
1088238825Smm
1089238825Smmstatic int
1090238825Smmset_timefilter_date_w(struct archive_match *a, int timetype,
1091238825Smm    const wchar_t *datestr)
1092238825Smm{
1093238825Smm	struct archive_string as;
1094238825Smm	time_t t;
1095238825Smm
1096238825Smm	if (datestr == NULL || *datestr == L'\0') {
1097238825Smm		archive_set_error(&(a->archive), EINVAL, "date is empty");
1098238825Smm		return (ARCHIVE_FAILED);
1099238825Smm	}
1100238825Smm
1101238825Smm	archive_string_init(&as);
1102238825Smm	if (archive_string_append_from_wcs(&as, datestr, wcslen(datestr)) < 0) {
1103238825Smm		archive_string_free(&as);
1104238825Smm		if (errno == ENOMEM)
1105238825Smm			return (error_nomem(a));
1106238825Smm		archive_set_error(&(a->archive), -1,
1107238825Smm		    "Failed to convert WCS to MBS");
1108238825Smm		return (ARCHIVE_FAILED);
1109238825Smm	}
1110238825Smm	t = get_date(a->now, as.s);
1111238825Smm	archive_string_free(&as);
1112238825Smm	if (t == (time_t)-1) {
1113238825Smm		archive_set_error(&(a->archive), EINVAL, "invalid date string");
1114238825Smm		return (ARCHIVE_FAILED);
1115238825Smm	}
1116238825Smm	return set_timefilter(a, timetype, t, 0, t, 0);
1117238825Smm}
1118238825Smm
1119238825Smm#if defined(_WIN32) && !defined(__CYGWIN__)
1120238825Smm#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
1121238825Smmstatic int
1122238825Smmset_timefilter_find_data(struct archive_match *a, int timetype,
1123238825Smm    DWORD ftLastWriteTime_dwHighDateTime, DWORD ftLastWriteTime_dwLowDateTime,
1124238825Smm    DWORD ftCreationTime_dwHighDateTime, DWORD ftCreationTime_dwLowDateTime)
1125238825Smm{
1126238825Smm	ULARGE_INTEGER utc;
1127238825Smm	time_t ctime_sec, mtime_sec;
1128238825Smm	long ctime_ns, mtime_ns;
1129238825Smm
1130238825Smm	utc.HighPart = ftCreationTime_dwHighDateTime;
1131238825Smm	utc.LowPart = ftCreationTime_dwLowDateTime;
1132238825Smm	if (utc.QuadPart >= EPOC_TIME) {
1133238825Smm		utc.QuadPart -= EPOC_TIME;
1134238825Smm		ctime_sec = (time_t)(utc.QuadPart / 10000000);
1135238825Smm		ctime_ns = (long)(utc.QuadPart % 10000000) * 100;
1136238825Smm	} else {
1137238825Smm		ctime_sec = 0;
1138238825Smm		ctime_ns = 0;
1139238825Smm	}
1140238825Smm	utc.HighPart = ftLastWriteTime_dwHighDateTime;
1141238825Smm	utc.LowPart = ftLastWriteTime_dwLowDateTime;
1142238825Smm	if (utc.QuadPart >= EPOC_TIME) {
1143238825Smm		utc.QuadPart -= EPOC_TIME;
1144238825Smm		mtime_sec = (time_t)(utc.QuadPart / 10000000);
1145238825Smm		mtime_ns = (long)(utc.QuadPart % 10000000) * 100;
1146238825Smm	} else {
1147238825Smm		mtime_sec = 0;
1148238825Smm		mtime_ns = 0;
1149238825Smm	}
1150238825Smm	return set_timefilter(a, timetype,
1151238825Smm			mtime_sec, mtime_ns, ctime_sec, ctime_ns);
1152238825Smm}
1153238825Smm
1154238825Smmstatic int
1155238825Smmset_timefilter_pathname_mbs(struct archive_match *a, int timetype,
1156238825Smm    const char *path)
1157238825Smm{
1158238825Smm	/* NOTE: stat() on Windows cannot handle nano seconds. */
1159238825Smm	HANDLE h;
1160299529Smm	WIN32_FIND_DATAA d;
1161238825Smm
1162238825Smm	if (path == NULL || *path == '\0') {
1163238825Smm		archive_set_error(&(a->archive), EINVAL, "pathname is empty");
1164238825Smm		return (ARCHIVE_FAILED);
1165238825Smm	}
1166238825Smm	h = FindFirstFileA(path, &d);
1167238825Smm	if (h == INVALID_HANDLE_VALUE) {
1168238825Smm		la_dosmaperr(GetLastError());
1169238825Smm		archive_set_error(&(a->archive), errno,
1170238825Smm		    "Failed to FindFirstFileA");
1171238825Smm		return (ARCHIVE_FAILED);
1172238825Smm	}
1173238825Smm	FindClose(h);
1174238825Smm	return set_timefilter_find_data(a, timetype,
1175238825Smm	    d.ftLastWriteTime.dwHighDateTime, d.ftLastWriteTime.dwLowDateTime,
1176238825Smm	    d.ftCreationTime.dwHighDateTime, d.ftCreationTime.dwLowDateTime);
1177238825Smm}
1178238825Smm
1179238825Smmstatic int
1180238825Smmset_timefilter_pathname_wcs(struct archive_match *a, int timetype,
1181238825Smm    const wchar_t *path)
1182238825Smm{
1183238825Smm	HANDLE h;
1184238825Smm	WIN32_FIND_DATAW d;
1185238825Smm
1186238825Smm	if (path == NULL || *path == L'\0') {
1187238825Smm		archive_set_error(&(a->archive), EINVAL, "pathname is empty");
1188238825Smm		return (ARCHIVE_FAILED);
1189238825Smm	}
1190238825Smm	h = FindFirstFileW(path, &d);
1191238825Smm	if (h == INVALID_HANDLE_VALUE) {
1192238825Smm		la_dosmaperr(GetLastError());
1193238825Smm		archive_set_error(&(a->archive), errno,
1194238825Smm		    "Failed to FindFirstFile");
1195238825Smm		return (ARCHIVE_FAILED);
1196238825Smm	}
1197238825Smm	FindClose(h);
1198238825Smm	return set_timefilter_find_data(a, timetype,
1199238825Smm	    d.ftLastWriteTime.dwHighDateTime, d.ftLastWriteTime.dwLowDateTime,
1200238825Smm	    d.ftCreationTime.dwHighDateTime, d.ftCreationTime.dwLowDateTime);
1201238825Smm}
1202238825Smm
1203238825Smm#else /* _WIN32 && !__CYGWIN__ */
1204238825Smm
1205238825Smmstatic int
1206238825Smmset_timefilter_stat(struct archive_match *a, int timetype, struct stat *st)
1207238825Smm{
1208238825Smm	struct archive_entry *ae;
1209238825Smm	time_t ctime_sec, mtime_sec;
1210238825Smm	long ctime_ns, mtime_ns;
1211238825Smm
1212238825Smm	ae = archive_entry_new();
1213238825Smm	if (ae == NULL)
1214238825Smm		return (error_nomem(a));
1215238825Smm	archive_entry_copy_stat(ae, st);
1216238825Smm	ctime_sec = archive_entry_ctime(ae);
1217238825Smm	ctime_ns = archive_entry_ctime_nsec(ae);
1218238825Smm	mtime_sec = archive_entry_mtime(ae);
1219238825Smm	mtime_ns = archive_entry_mtime_nsec(ae);
1220238825Smm	archive_entry_free(ae);
1221238825Smm	return set_timefilter(a, timetype, mtime_sec, mtime_ns,
1222238825Smm			ctime_sec, ctime_ns);
1223238825Smm}
1224238825Smm
1225238825Smmstatic int
1226238825Smmset_timefilter_pathname_mbs(struct archive_match *a, int timetype,
1227238825Smm    const char *path)
1228238825Smm{
1229238825Smm	struct stat st;
1230238825Smm
1231238825Smm	if (path == NULL || *path == '\0') {
1232238825Smm		archive_set_error(&(a->archive), EINVAL, "pathname is empty");
1233238825Smm		return (ARCHIVE_FAILED);
1234238825Smm	}
1235238825Smm	if (stat(path, &st) != 0) {
1236238825Smm		archive_set_error(&(a->archive), errno, "Failed to stat()");
1237238825Smm		return (ARCHIVE_FAILED);
1238238825Smm	}
1239238825Smm	return (set_timefilter_stat(a, timetype, &st));
1240238825Smm}
1241238825Smm
1242238825Smmstatic int
1243238825Smmset_timefilter_pathname_wcs(struct archive_match *a, int timetype,
1244238825Smm    const wchar_t *path)
1245238825Smm{
1246238825Smm	struct archive_string as;
1247238825Smm	int r;
1248238825Smm
1249238825Smm	if (path == NULL || *path == L'\0') {
1250238825Smm		archive_set_error(&(a->archive), EINVAL, "pathname is empty");
1251238825Smm		return (ARCHIVE_FAILED);
1252238825Smm	}
1253238825Smm
1254238825Smm	/* Convert WCS filename to MBS filename. */
1255238825Smm	archive_string_init(&as);
1256238825Smm	if (archive_string_append_from_wcs(&as, path, wcslen(path)) < 0) {
1257238825Smm		archive_string_free(&as);
1258238825Smm		if (errno == ENOMEM)
1259238825Smm			return (error_nomem(a));
1260238825Smm		archive_set_error(&(a->archive), -1,
1261238825Smm		    "Failed to convert WCS to MBS");
1262238825Smm		return (ARCHIVE_FAILED);
1263238825Smm	}
1264238825Smm
1265238825Smm	r = set_timefilter_pathname_mbs(a, timetype, as.s);
1266238825Smm	archive_string_free(&as);
1267238825Smm
1268238825Smm	return (r);
1269238825Smm}
1270238825Smm#endif /* _WIN32 && !__CYGWIN__ */
1271238825Smm
1272238825Smm/*
1273238825Smm * Call back funtions for archive_rb.
1274238825Smm */
1275238825Smmstatic int
1276238825Smmcmp_node_mbs(const struct archive_rb_node *n1,
1277238825Smm    const struct archive_rb_node *n2)
1278238825Smm{
1279238825Smm	struct match_file *f1 = (struct match_file *)(uintptr_t)n1;
1280238825Smm	struct match_file *f2 = (struct match_file *)(uintptr_t)n2;
1281238825Smm	const char *p1, *p2;
1282238825Smm
1283238825Smm	archive_mstring_get_mbs(NULL, &(f1->pathname), &p1);
1284238825Smm	archive_mstring_get_mbs(NULL, &(f2->pathname), &p2);
1285238825Smm	if (p1 == NULL)
1286238825Smm		return (1);
1287238825Smm	if (p2 == NULL)
1288238825Smm		return (-1);
1289238825Smm	return (strcmp(p1, p2));
1290238825Smm}
1291238825Smm
1292238825Smmstatic int
1293238825Smmcmp_key_mbs(const struct archive_rb_node *n, const void *key)
1294238825Smm{
1295238825Smm	struct match_file *f = (struct match_file *)(uintptr_t)n;
1296238825Smm	const char *p;
1297238825Smm
1298238825Smm	archive_mstring_get_mbs(NULL, &(f->pathname), &p);
1299238825Smm	if (p == NULL)
1300238825Smm		return (-1);
1301238825Smm	return (strcmp(p, (const char *)key));
1302238825Smm}
1303238825Smm
1304238825Smmstatic int
1305238825Smmcmp_node_wcs(const struct archive_rb_node *n1,
1306238825Smm    const struct archive_rb_node *n2)
1307238825Smm{
1308238825Smm	struct match_file *f1 = (struct match_file *)(uintptr_t)n1;
1309238825Smm	struct match_file *f2 = (struct match_file *)(uintptr_t)n2;
1310238825Smm	const wchar_t *p1, *p2;
1311238825Smm
1312238825Smm	archive_mstring_get_wcs(NULL, &(f1->pathname), &p1);
1313238825Smm	archive_mstring_get_wcs(NULL, &(f2->pathname), &p2);
1314238825Smm	if (p1 == NULL)
1315238825Smm		return (1);
1316238825Smm	if (p2 == NULL)
1317238825Smm		return (-1);
1318238825Smm	return (wcscmp(p1, p2));
1319238825Smm}
1320238825Smm
1321238825Smmstatic int
1322238825Smmcmp_key_wcs(const struct archive_rb_node *n, const void *key)
1323238825Smm{
1324238825Smm	struct match_file *f = (struct match_file *)(uintptr_t)n;
1325238825Smm	const wchar_t *p;
1326238825Smm
1327238825Smm	archive_mstring_get_wcs(NULL, &(f->pathname), &p);
1328238825Smm	if (p == NULL)
1329238825Smm		return (-1);
1330238825Smm	return (wcscmp(p, (const wchar_t *)key));
1331238825Smm}
1332238825Smm
1333238825Smmstatic void
1334238825Smmentry_list_init(struct entry_list *list)
1335238825Smm{
1336238825Smm	list->first = NULL;
1337238825Smm	list->last = &(list->first);
1338238825Smm	list->count = 0;
1339238825Smm}
1340238825Smm
1341238825Smmstatic void
1342238825Smmentry_list_free(struct entry_list *list)
1343238825Smm{
1344238825Smm	struct match_file *p, *q;
1345238825Smm
1346238825Smm	for (p = list->first; p != NULL; ) {
1347238825Smm		q = p;
1348238825Smm		p = p->next;
1349238825Smm		archive_mstring_clean(&(q->pathname));
1350238825Smm		free(q);
1351238825Smm	}
1352238825Smm}
1353238825Smm
1354238825Smmstatic void
1355238825Smmentry_list_add(struct entry_list *list, struct match_file *file)
1356238825Smm{
1357238825Smm	*list->last = file;
1358238825Smm	list->last = &(file->next);
1359238825Smm	list->count++;
1360238825Smm}
1361238825Smm
1362238825Smmstatic int
1363238825Smmadd_entry(struct archive_match *a, int flag,
1364238825Smm    struct archive_entry *entry)
1365238825Smm{
1366238825Smm	struct match_file *f;
1367238825Smm	const void *pathname;
1368238825Smm	int r;
1369238825Smm
1370238825Smm	f = calloc(1, sizeof(*f));
1371238825Smm	if (f == NULL)
1372238825Smm		return (error_nomem(a));
1373238825Smm
1374238825Smm#if defined(_WIN32) && !defined(__CYGWIN__)
1375238825Smm	pathname = archive_entry_pathname_w(entry);
1376238825Smm	if (pathname == NULL) {
1377238825Smm		free(f);
1378238825Smm		archive_set_error(&(a->archive), EINVAL, "pathname is NULL");
1379238825Smm		return (ARCHIVE_FAILED);
1380238825Smm	}
1381238825Smm	archive_mstring_copy_wcs(&(f->pathname), pathname);
1382238825Smm	a->exclusion_tree.rbt_ops = &rb_ops_wcs;
1383238825Smm#else
1384248616Smm	(void)rb_ops_wcs;
1385238825Smm	pathname = archive_entry_pathname(entry);
1386238825Smm	if (pathname == NULL) {
1387238825Smm		free(f);
1388238825Smm		archive_set_error(&(a->archive), EINVAL, "pathname is NULL");
1389238825Smm		return (ARCHIVE_FAILED);
1390238825Smm	}
1391238825Smm	archive_mstring_copy_mbs(&(f->pathname), pathname);
1392238825Smm	a->exclusion_tree.rbt_ops = &rb_ops_mbs;
1393238825Smm#endif
1394238825Smm	f->flag = flag;
1395238825Smm	f->mtime_sec = archive_entry_mtime(entry);
1396238825Smm	f->mtime_nsec = archive_entry_mtime_nsec(entry);
1397238825Smm	f->ctime_sec = archive_entry_ctime(entry);
1398238825Smm	f->ctime_nsec = archive_entry_ctime_nsec(entry);
1399238825Smm	r = __archive_rb_tree_insert_node(&(a->exclusion_tree), &(f->node));
1400238825Smm	if (!r) {
1401238825Smm		struct match_file *f2;
1402238825Smm
1403238825Smm		/* Get the duplicated file. */
1404238825Smm		f2 = (struct match_file *)__archive_rb_tree_find_node(
1405238825Smm			&(a->exclusion_tree), pathname);
1406238825Smm
1407238825Smm		/*
1408238825Smm		 * We always overwrite comparison condision.
1409238825Smm		 * If you do not want to overwrite it, you should not
1410238825Smm		 * call archive_match_exclude_entry(). We cannot know
1411238825Smm		 * what behavior you really expect since overwriting
1412238825Smm		 * condition might be different with the flag.
1413238825Smm		 */
1414238825Smm		if (f2 != NULL) {
1415238825Smm			f2->flag = f->flag;
1416238825Smm			f2->mtime_sec = f->mtime_sec;
1417238825Smm			f2->mtime_nsec = f->mtime_nsec;
1418238825Smm			f2->ctime_sec = f->ctime_sec;
1419238825Smm			f2->ctime_nsec = f->ctime_nsec;
1420238825Smm		}
1421238825Smm		/* Release the duplicated file. */
1422238825Smm		archive_mstring_clean(&(f->pathname));
1423238825Smm		free(f);
1424238825Smm		return (ARCHIVE_OK);
1425238825Smm	}
1426238825Smm	entry_list_add(&(a->exclusion_entry_list), f);
1427238825Smm	a->setflag |= TIME_IS_SET;
1428238825Smm	return (ARCHIVE_OK);
1429238825Smm}
1430238825Smm
1431238825Smm/*
1432238825Smm * Test if entry is excluded by its timestamp.
1433238825Smm */
1434238825Smmstatic int
1435238825Smmtime_excluded(struct archive_match *a, struct archive_entry *entry)
1436238825Smm{
1437238825Smm	struct match_file *f;
1438238825Smm	const void *pathname;
1439238825Smm	time_t sec;
1440238825Smm	long nsec;
1441238825Smm
1442238825Smm	/*
1443238825Smm	 * If this file/dir is excluded by a time comparison, skip it.
1444238825Smm	 */
1445238825Smm	if (a->newer_ctime_filter) {
1446238825Smm		/* If ctime is not set, use mtime instead. */
1447238825Smm		if (archive_entry_ctime_is_set(entry))
1448238825Smm			sec = archive_entry_ctime(entry);
1449238825Smm		else
1450238825Smm			sec = archive_entry_mtime(entry);
1451238825Smm		if (sec < a->newer_ctime_sec)
1452238825Smm			return (1); /* Too old, skip it. */
1453238825Smm		if (sec == a->newer_ctime_sec) {
1454238825Smm			if (archive_entry_ctime_is_set(entry))
1455238825Smm				nsec = archive_entry_ctime_nsec(entry);
1456238825Smm			else
1457238825Smm				nsec = archive_entry_mtime_nsec(entry);
1458238825Smm			if (nsec < a->newer_ctime_nsec)
1459238825Smm				return (1); /* Too old, skip it. */
1460238825Smm			if (nsec == a->newer_ctime_nsec &&
1461238825Smm			    (a->newer_ctime_filter & ARCHIVE_MATCH_EQUAL)
1462238825Smm			      == 0)
1463238825Smm				return (1); /* Equal, skip it. */
1464238825Smm		}
1465238825Smm	}
1466238825Smm	if (a->older_ctime_filter) {
1467238825Smm		/* If ctime is not set, use mtime instead. */
1468238825Smm		if (archive_entry_ctime_is_set(entry))
1469238825Smm			sec = archive_entry_ctime(entry);
1470238825Smm		else
1471238825Smm			sec = archive_entry_mtime(entry);
1472238825Smm		if (sec > a->older_ctime_sec)
1473238825Smm			return (1); /* Too new, skip it. */
1474238825Smm		if (sec == a->older_ctime_sec) {
1475238825Smm			if (archive_entry_ctime_is_set(entry))
1476238825Smm				nsec = archive_entry_ctime_nsec(entry);
1477238825Smm			else
1478238825Smm				nsec = archive_entry_mtime_nsec(entry);
1479238825Smm			if (nsec > a->older_ctime_nsec)
1480238825Smm				return (1); /* Too new, skip it. */
1481238825Smm			if (nsec == a->older_ctime_nsec &&
1482238825Smm			    (a->older_ctime_filter & ARCHIVE_MATCH_EQUAL)
1483238825Smm			      == 0)
1484238825Smm				return (1); /* Eeual, skip it. */
1485238825Smm		}
1486238825Smm	}
1487238825Smm	if (a->newer_mtime_filter) {
1488238825Smm		sec = archive_entry_mtime(entry);
1489238825Smm		if (sec < a->newer_mtime_sec)
1490238825Smm			return (1); /* Too old, skip it. */
1491238825Smm		if (sec == a->newer_mtime_sec) {
1492238825Smm			nsec = archive_entry_mtime_nsec(entry);
1493238825Smm			if (nsec < a->newer_mtime_nsec)
1494238825Smm				return (1); /* Too old, skip it. */
1495238825Smm			if (nsec == a->newer_mtime_nsec &&
1496238825Smm			    (a->newer_mtime_filter & ARCHIVE_MATCH_EQUAL)
1497238825Smm			       == 0)
1498238825Smm				return (1); /* Equal, skip it. */
1499238825Smm		}
1500238825Smm	}
1501238825Smm	if (a->older_mtime_filter) {
1502238825Smm		sec = archive_entry_mtime(entry);
1503238825Smm		if (sec > a->older_mtime_sec)
1504238825Smm			return (1); /* Too new, skip it. */
1505238825Smm		nsec = archive_entry_mtime_nsec(entry);
1506238825Smm		if (sec == a->older_mtime_sec) {
1507238825Smm			if (nsec > a->older_mtime_nsec)
1508238825Smm				return (1); /* Too new, skip it. */
1509238825Smm			if (nsec == a->older_mtime_nsec &&
1510238825Smm			    (a->older_mtime_filter & ARCHIVE_MATCH_EQUAL)
1511238825Smm			       == 0)
1512238825Smm				return (1); /* Equal, skip it. */
1513238825Smm		}
1514238825Smm	}
1515238825Smm
1516238825Smm	/* If there is no excluson list, include the file. */
1517238825Smm	if (a->exclusion_entry_list.count == 0)
1518238825Smm		return (0);
1519238825Smm
1520238825Smm#if defined(_WIN32) && !defined(__CYGWIN__)
1521238825Smm	pathname = archive_entry_pathname_w(entry);
1522238825Smm	a->exclusion_tree.rbt_ops = &rb_ops_wcs;
1523238825Smm#else
1524248616Smm	(void)rb_ops_wcs;
1525238825Smm	pathname = archive_entry_pathname(entry);
1526238825Smm	a->exclusion_tree.rbt_ops = &rb_ops_mbs;
1527238825Smm#endif
1528238825Smm	if (pathname == NULL)
1529238825Smm		return (0);
1530238825Smm
1531238825Smm	f = (struct match_file *)__archive_rb_tree_find_node(
1532238825Smm		&(a->exclusion_tree), pathname);
1533238825Smm	/* If the file wasn't rejected, include it. */
1534238825Smm	if (f == NULL)
1535238825Smm		return (0);
1536238825Smm
1537238825Smm	if (f->flag & ARCHIVE_MATCH_CTIME) {
1538238825Smm		sec = archive_entry_ctime(entry);
1539238825Smm		if (f->ctime_sec > sec) {
1540238825Smm			if (f->flag & ARCHIVE_MATCH_OLDER)
1541238825Smm				return (1);
1542238825Smm		} else if (f->ctime_sec < sec) {
1543238825Smm			if (f->flag & ARCHIVE_MATCH_NEWER)
1544238825Smm				return (1);
1545238825Smm		} else {
1546238825Smm			nsec = archive_entry_ctime_nsec(entry);
1547238825Smm			if (f->ctime_nsec > nsec) {
1548238825Smm				if (f->flag & ARCHIVE_MATCH_OLDER)
1549238825Smm					return (1);
1550238825Smm			} else if (f->ctime_nsec < nsec) {
1551238825Smm				if (f->flag & ARCHIVE_MATCH_NEWER)
1552238825Smm					return (1);
1553238825Smm			} else if (f->flag & ARCHIVE_MATCH_EQUAL)
1554238825Smm				return (1);
1555238825Smm		}
1556238825Smm	}
1557238825Smm	if (f->flag & ARCHIVE_MATCH_MTIME) {
1558238825Smm		sec = archive_entry_mtime(entry);
1559238825Smm		if (f->mtime_sec > sec) {
1560238825Smm			if (f->flag & ARCHIVE_MATCH_OLDER)
1561238825Smm				return (1);
1562238825Smm		} else if (f->mtime_sec < sec) {
1563238825Smm			if (f->flag & ARCHIVE_MATCH_NEWER)
1564238825Smm				return (1);
1565238825Smm		} else {
1566238825Smm			nsec = archive_entry_mtime_nsec(entry);
1567238825Smm			if (f->mtime_nsec > nsec) {
1568238825Smm				if (f->flag & ARCHIVE_MATCH_OLDER)
1569238825Smm					return (1);
1570238825Smm			} else if (f->mtime_nsec < nsec) {
1571238825Smm				if (f->flag & ARCHIVE_MATCH_NEWER)
1572238825Smm					return (1);
1573238825Smm			} else if (f->flag & ARCHIVE_MATCH_EQUAL)
1574238825Smm				return (1);
1575238825Smm		}
1576238825Smm	}
1577238825Smm	return (0);
1578238825Smm}
1579238825Smm
1580238825Smm/*
1581238825Smm * Utility functions to manage inclusion owners
1582238825Smm */
1583238825Smm
1584238825Smmint
1585238825Smmarchive_match_include_uid(struct archive *_a, int64_t uid)
1586238825Smm{
1587238825Smm	struct archive_match *a;
1588238825Smm
1589238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1590238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_uid");
1591238825Smm	a = (struct archive_match *)_a;
1592238825Smm	return (add_owner_id(a, &(a->inclusion_uids), uid));
1593238825Smm}
1594238825Smm
1595238825Smmint
1596238825Smmarchive_match_include_gid(struct archive *_a, int64_t gid)
1597238825Smm{
1598238825Smm	struct archive_match *a;
1599238825Smm
1600238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1601238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_gid");
1602238825Smm	a = (struct archive_match *)_a;
1603238825Smm	return (add_owner_id(a, &(a->inclusion_gids), gid));
1604238825Smm}
1605238825Smm
1606238825Smmint
1607238825Smmarchive_match_include_uname(struct archive *_a, const char *uname)
1608238825Smm{
1609238825Smm	struct archive_match *a;
1610238825Smm
1611238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1612238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_uname");
1613238825Smm	a = (struct archive_match *)_a;
1614238825Smm	return (add_owner_name(a, &(a->inclusion_unames), 1, uname));
1615238825Smm}
1616238825Smm
1617238825Smmint
1618238825Smmarchive_match_include_uname_w(struct archive *_a, const wchar_t *uname)
1619238825Smm{
1620238825Smm	struct archive_match *a;
1621238825Smm
1622238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1623238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_uname_w");
1624238825Smm	a = (struct archive_match *)_a;
1625238825Smm	return (add_owner_name(a, &(a->inclusion_unames), 0, uname));
1626238825Smm}
1627238825Smm
1628238825Smmint
1629238825Smmarchive_match_include_gname(struct archive *_a, const char *gname)
1630238825Smm{
1631238825Smm	struct archive_match *a;
1632238825Smm
1633238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1634238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_gname");
1635238825Smm	a = (struct archive_match *)_a;
1636238825Smm	return (add_owner_name(a, &(a->inclusion_gnames), 1, gname));
1637238825Smm}
1638238825Smm
1639238825Smmint
1640238825Smmarchive_match_include_gname_w(struct archive *_a, const wchar_t *gname)
1641238825Smm{
1642238825Smm	struct archive_match *a;
1643238825Smm
1644238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1645238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_gname_w");
1646238825Smm	a = (struct archive_match *)_a;
1647238825Smm	return (add_owner_name(a, &(a->inclusion_gnames), 0, gname));
1648238825Smm}
1649238825Smm
1650238825Smm/*
1651238825Smm * Test function for owner(uid, gid, uname, gname).
1652238825Smm *
1653238825Smm * Returns 1 if archive entry is excluded.
1654238825Smm * Returns 0 if archive entry is not excluded.
1655238825Smm * Returns <0 if something error happened.
1656238825Smm */
1657238825Smmint
1658238825Smmarchive_match_owner_excluded(struct archive *_a,
1659238825Smm    struct archive_entry *entry)
1660238825Smm{
1661238825Smm	struct archive_match *a;
1662238825Smm
1663238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1664238825Smm	    ARCHIVE_STATE_NEW, "archive_match_id_excluded_ae");
1665238825Smm
1666238825Smm	a = (struct archive_match *)_a;
1667238825Smm	if (entry == NULL) {
1668238825Smm		archive_set_error(&(a->archive), EINVAL, "entry is NULL");
1669238825Smm		return (ARCHIVE_FAILED);
1670238825Smm	}
1671238825Smm
1672238825Smm	/* If we don't have inclusion id set at all, the entry is always
1673238825Smm	 * not excluded. */
1674238825Smm	if ((a->setflag & ID_IS_SET) == 0)
1675238825Smm		return (0);
1676238825Smm	return (owner_excluded(a, entry));
1677238825Smm}
1678238825Smm
1679238825Smmstatic int
1680238825Smmadd_owner_id(struct archive_match *a, struct id_array *ids, int64_t id)
1681238825Smm{
1682238825Smm	unsigned i;
1683238825Smm
1684238825Smm	if (ids->count + 1 >= ids->size) {
1685248616Smm		void *p;
1686248616Smm
1687238825Smm		if (ids->size == 0)
1688238825Smm			ids->size = 8;
1689238825Smm		else
1690238825Smm			ids->size *= 2;
1691248616Smm		p = realloc(ids->ids, sizeof(*ids->ids) * ids->size);
1692248616Smm		if (p == NULL)
1693238825Smm			return (error_nomem(a));
1694248616Smm		ids->ids = (int64_t *)p;
1695238825Smm	}
1696238825Smm
1697238825Smm	/* Find an insert point. */
1698238825Smm	for (i = 0; i < ids->count; i++) {
1699238825Smm		if (ids->ids[i] >= id)
1700238825Smm			break;
1701238825Smm	}
1702238825Smm
1703238825Smm	/* Add oowner id. */
1704238825Smm	if (i == ids->count)
1705238825Smm		ids->ids[ids->count++] = id;
1706238825Smm	else if (ids->ids[i] != id) {
1707238825Smm		memmove(&(ids->ids[i+1]), &(ids->ids[i]),
1708238825Smm		    (ids->count - i) * sizeof(ids->ids[0]));
1709238825Smm		ids->ids[i] = id;
1710238825Smm		ids->count++;
1711238825Smm	}
1712238825Smm	a->setflag |= ID_IS_SET;
1713238825Smm	return (ARCHIVE_OK);
1714238825Smm}
1715238825Smm
1716238825Smmstatic int
1717238825Smmmatch_owner_id(struct id_array *ids, int64_t id)
1718238825Smm{
1719238825Smm	unsigned b, m, t;
1720238825Smm
1721238825Smm	t = 0;
1722248616Smm	b = (unsigned)ids->count;
1723238825Smm	while (t < b) {
1724238825Smm		m = (t + b)>>1;
1725238825Smm		if (ids->ids[m] == id)
1726238825Smm			return (1);
1727238825Smm		if (ids->ids[m] < id)
1728238825Smm			t = m + 1;
1729238825Smm		else
1730238825Smm			b = m;
1731238825Smm	}
1732238825Smm	return (0);
1733238825Smm}
1734238825Smm
1735238825Smmstatic int
1736238825Smmadd_owner_name(struct archive_match *a, struct match_list *list,
1737238825Smm    int mbs, const void *name)
1738238825Smm{
1739238825Smm	struct match *match;
1740238825Smm
1741238825Smm	match = calloc(1, sizeof(*match));
1742238825Smm	if (match == NULL)
1743238825Smm		return (error_nomem(a));
1744238825Smm	if (mbs)
1745238825Smm		archive_mstring_copy_mbs(&(match->pattern), name);
1746238825Smm	else
1747238825Smm		archive_mstring_copy_wcs(&(match->pattern), name);
1748238825Smm	match_list_add(list, match);
1749238825Smm	a->setflag |= ID_IS_SET;
1750238825Smm	return (ARCHIVE_OK);
1751238825Smm}
1752238825Smm
1753238825Smm#if !defined(_WIN32) || defined(__CYGWIN__)
1754238825Smmstatic int
1755238825Smmmatch_owner_name_mbs(struct archive_match *a, struct match_list *list,
1756238825Smm    const char *name)
1757238825Smm{
1758238825Smm	struct match *m;
1759238825Smm	const char *p;
1760238825Smm
1761238825Smm	if (name == NULL || *name == '\0')
1762238825Smm		return (0);
1763238825Smm	for (m = list->first; m; m = m->next) {
1764238825Smm		if (archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p)
1765238825Smm		    < 0 && errno == ENOMEM)
1766238825Smm			return (error_nomem(a));
1767238825Smm		if (p != NULL && strcmp(p, name) == 0) {
1768238825Smm			m->matches++;
1769238825Smm			return (1);
1770238825Smm		}
1771238825Smm	}
1772238825Smm	return (0);
1773238825Smm}
1774238825Smm#else
1775238825Smmstatic int
1776238825Smmmatch_owner_name_wcs(struct archive_match *a, struct match_list *list,
1777238825Smm    const wchar_t *name)
1778238825Smm{
1779238825Smm	struct match *m;
1780238825Smm	const wchar_t *p;
1781238825Smm
1782238825Smm	if (name == NULL || *name == L'\0')
1783238825Smm		return (0);
1784238825Smm	for (m = list->first; m; m = m->next) {
1785238825Smm		if (archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p)
1786238825Smm		    < 0 && errno == ENOMEM)
1787238825Smm			return (error_nomem(a));
1788238825Smm		if (p != NULL && wcscmp(p, name) == 0) {
1789238825Smm			m->matches++;
1790238825Smm			return (1);
1791238825Smm		}
1792238825Smm	}
1793238825Smm	return (0);
1794238825Smm}
1795238825Smm#endif
1796238825Smm
1797238825Smm/*
1798238825Smm * Test if entry is excluded by uid, gid, uname or gname.
1799238825Smm */
1800238825Smmstatic int
1801238825Smmowner_excluded(struct archive_match *a, struct archive_entry *entry)
1802238825Smm{
1803238825Smm	int r;
1804238825Smm
1805238825Smm	if (a->inclusion_uids.count) {
1806238825Smm		if (!match_owner_id(&(a->inclusion_uids),
1807238825Smm		    archive_entry_uid(entry)))
1808238825Smm			return (1);
1809238825Smm	}
1810238825Smm
1811238825Smm	if (a->inclusion_gids.count) {
1812238825Smm		if (!match_owner_id(&(a->inclusion_gids),
1813238825Smm		    archive_entry_gid(entry)))
1814238825Smm			return (1);
1815238825Smm	}
1816238825Smm
1817238825Smm	if (a->inclusion_unames.count) {
1818238825Smm#if defined(_WIN32) && !defined(__CYGWIN__)
1819238825Smm		r = match_owner_name_wcs(a, &(a->inclusion_unames),
1820238825Smm			archive_entry_uname_w(entry));
1821238825Smm#else
1822238825Smm		r = match_owner_name_mbs(a, &(a->inclusion_unames),
1823238825Smm			archive_entry_uname(entry));
1824238825Smm#endif
1825238825Smm		if (!r)
1826238825Smm			return (1);
1827238825Smm		else if (r < 0)
1828238825Smm			return (r);
1829238825Smm	}
1830238825Smm
1831238825Smm	if (a->inclusion_gnames.count) {
1832238825Smm#if defined(_WIN32) && !defined(__CYGWIN__)
1833238825Smm		r = match_owner_name_wcs(a, &(a->inclusion_gnames),
1834238825Smm			archive_entry_gname_w(entry));
1835238825Smm#else
1836238825Smm		r = match_owner_name_mbs(a, &(a->inclusion_gnames),
1837238825Smm			archive_entry_gname(entry));
1838238825Smm#endif
1839238825Smm		if (!r)
1840238825Smm			return (1);
1841238825Smm		else if (r < 0)
1842238825Smm			return (r);
1843238825Smm	}
1844238825Smm	return (0);
1845238825Smm}
1846238825Smm
1847