find matched archive_entry objects. This is based on libarchive_fe/matching.[ch]
I added serveral interfaces for wchar_t, and matching file stamps and owners.
I also plan to use that at archive_read_disk instead of
archive_read_disk_set_name_filter_callback function, which I recently added.
SVN-Revision: 4162
libarchive/archive_entry_stat.c \
libarchive/archive_entry_strmode.c \
libarchive/archive_entry_xattr.c \
+ libarchive/archive_matching.c \
libarchive/archive_options.c \
libarchive/archive_options_private.h \
+ libarchive/archive_pathmatch.c \
+ libarchive/archive_pathmatch.h \
libarchive/archive_platform.h \
libarchive/archive_ppmd_private.h \
libarchive/archive_ppmd7.c \
libarchive/test/test_archive_api_feature.c \
libarchive/test/test_archive_clear_error.c \
libarchive/test/test_archive_crypto.c \
+ libarchive/test/test_archive_matching_owner.c \
+ libarchive/test/test_archive_matching_path.c \
+ libarchive/test/test_archive_matching_time.c \
libarchive/test/test_archive_read_close_twice.c \
libarchive/test/test_archive_read_close_twice_open_fd.c \
libarchive/test/test_archive_read_close_twice_open_filename.c \
archive_entry_stat.c
archive_entry_strmode.c
archive_entry_xattr.c
+ archive_matching.c
archive_options.c
archive_options_private.h
+ archive_pathmatch.c
+ archive_pathmatch.h
archive_platform.h
archive_ppmd_private.h
archive_ppmd7.c
struct archive *src);
__LA_DECL int archive_file_count(struct archive *);
+/*
+ * ARCHIVE_MATCHING API
+ */
+__LA_DECL struct archive *archive_matching_new(void);
+__LA_DECL int archive_matching_free(struct archive *);
+
+/*
+ * Test if archive_entry is excluded.
+ * This is a convenience function. This is the same as calling all
+ * archive_matching_path_excluded_ae, archive_matching_time_excluded_ae
+ * and archive_matching_owner_excluded_ae.
+ */
+__LA_DECL int archive_matching_excluded_ae(struct archive *,
+ struct archive_entry *);
+
+/*
+ * Test if filename is excluded. The conditions are set by following functions.
+ */
+__LA_DECL int archive_matching_path_excluded(struct archive *,
+ const char *_filename);
+__LA_DECL int archive_matching_path_excluded_w(struct archive *,
+ const wchar_t *_filename);
+__LA_DECL int archive_matching_path_excluded_ae(struct archive *,
+ struct archive_entry *);
+/* Add exclusion fielname pattern. */
+__LA_DECL int archive_matching_exclude_pattern(struct archive *,
+ const char *);
+__LA_DECL int archive_matching_exclude_pattern_w(struct archive *,
+ const wchar_t *);
+/* Add inclusion fielname pattern. */
+__LA_DECL int archive_matching_include_pattern(struct archive *,
+ const char *);
+__LA_DECL int archive_matching_include_pattern_w(struct archive *,
+ const wchar_t *);
+/*
+ * How to get statistic information for inclusion patterns.
+ */
+/* Return the amount number of unmatched inclusion patterns. */
+__LA_DECL int archive_matching_path_unmatched_inclusions(
+ struct archive *);
+/* Return the pattern of unmatched inclusion with ARCHIVE_OK.
+ * Return ARCHIVE_EOF if there is no inclusion pattern. */
+__LA_DECL int archive_matching_path_unmatched_inclusions_next(
+ struct archive *, const char **);
+__LA_DECL int archive_matching_path_unmatched_inclusions_next_w(
+ struct archive *, const wchar_t **);
+
+/*
+ * Test if a file is excluded by its time stamp.
+ * The conditions are set by following functions.
+ */
+__LA_DECL int archive_matching_time_excluded_ae(struct archive *,
+ struct archive_entry *);
+/* Set inclusion file times. */
+__LA_DECL int archive_matching_newer_mtime(struct archive *,
+ time_t _sec, long _nsec);
+__LA_DECL int archive_matching_newer_mtime_than(struct archive *,
+ const char *_pathname);
+__LA_DECL int archive_matching_newer_mtime_than_w(struct archive *,
+ const wchar_t *_pathname);
+__LA_DECL int archive_matching_newer_ctime(struct archive *,
+ time_t _sec, long _nsec);
+__LA_DECL int archive_matching_newer_ctime_than(struct archive *,
+ const char *_pathname);
+__LA_DECL int archive_matching_newer_ctime_than_w(struct archive *,
+ const wchar_t *_pathname);
+__LA_DECL int archive_matching_older_mtime(struct archive *,
+ time_t _sec, long _nsec);
+__LA_DECL int archive_matching_older_mtime_than(struct archive *,
+ const char *_pathname);
+__LA_DECL int archive_matching_older_mtime_than_w(struct archive *,
+ const wchar_t *_pathname);
+__LA_DECL int archive_matching_older_ctime(struct archive *,
+ time_t _sec, long _nsec);
+__LA_DECL int archive_matching_older_ctime_than(struct archive *,
+ const char *_pathname);
+__LA_DECL int archive_matching_older_ctime_than_w(struct archive *,
+ const wchar_t *_pathname);
+/* Add inclusion file times with its filename. */
+__LA_DECL int archive_matching_pathname_newer_mtime(
+ struct archive *, const char *_pathname,
+ time_t _sec, long _nsec);
+__LA_DECL int archive_matching_pathname_newer_mtime_w(
+ struct archive *, const wchar_t *_pathname,
+ time_t _sec, long _nsec);
+__LA_DECL int archive_matching_pathname_newer_mtime_ae(
+ struct archive *, struct archive_entry *);
+
+/*
+ * Test if a file is excluded by its uid ,gid, uname or gname.
+ * The conditions are set by following functions.
+ */
+__LA_DECL int archive_matching_owner_excluded_ae(struct archive *,
+ struct archive_entry *);
+/* Add inclusion uid, gid, uname and gname. */
+__LA_DECL int archive_matching_include_uid(struct archive *,
+ int64_t);
+__LA_DECL int archive_matching_include_gid(struct archive *,
+ int64_t);
+__LA_DECL int archive_matching_include_uname(struct archive *,
+ const char *);
+__LA_DECL int archive_matching_include_uname_w(struct archive *,
+ const wchar_t *);
+__LA_DECL int archive_matching_include_gname(struct archive *,
+ const char *);
+__LA_DECL int archive_matching_include_gname_w(struct archive *,
+ const wchar_t *);
+
#ifdef __cplusplus
}
#endif
case ARCHIVE_READ_MAGIC: return ("archive_read");
case ARCHIVE_WRITE_DISK_MAGIC: return ("archive_write_disk");
case ARCHIVE_READ_DISK_MAGIC: return ("archive_read_disk");
+ case ARCHIVE_MATCHING_MAGIC: return ("archive_matching");
default: return NULL;
}
}
--- /dev/null
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_entry.h"
+#include "archive_pathmatch.h"
+#include "archive_rb.h"
+#include "archive_string.h"
+
+struct match {
+ struct match *next;
+ int matches;
+ struct archive_mstring pattern;
+};
+
+struct match_list {
+ struct match *first;
+ struct match **last;
+ int count;
+ int unmatched_count;
+ struct match *unmatched_next;
+ int unmatched_eof;
+};
+
+struct newer_file {
+ struct archive_rb_node node;
+ struct newer_file *next;
+ struct archive_mstring pathname;
+ time_t mtime_sec;
+ long mtime_nsec;
+};
+
+struct newer_file_list {
+ struct newer_file *first;
+ struct newer_file **last;
+ int count;
+};
+
+struct id_array {
+ size_t size;/* Allocated size */
+ size_t count;
+ int64_t *ids;
+};
+
+#define PATTERN_IS_SET 1
+#define TIME_IS_SET 2
+#define ID_IS_SET 4
+
+struct archive_matching {
+ struct archive archive;
+
+ /* exclusion/inclusion set flag. */
+ int setflag;
+
+ /*
+ * Matching filename patterns.
+ */
+ struct match_list exclusions;
+ struct match_list inclusions;
+
+ /*
+ * Matching time stamps.
+ */
+ int newer_mtime_filter;
+ time_t newer_mtime_sec;
+ long newer_mtime_nsec;
+ int newer_ctime_filter;
+ time_t newer_ctime_sec;
+ long newer_ctime_nsec;
+ int older_mtime_filter;
+ time_t older_mtime_sec;
+ long older_mtime_nsec;
+ int older_ctime_filter;
+ time_t older_ctime_sec;
+ long older_ctime_nsec;
+ /*
+ * Matching time stamps with its filename.
+ */
+ struct archive_rb_tree newer_tree;
+ struct newer_file_list newer_list;
+
+ /*
+ * Matching file owners.
+ */
+ struct id_array inclusion_uids;
+ struct id_array inclusion_gids;
+ struct match_list inclusion_unames;
+ struct match_list inclusion_gnames;
+};
+
+static int add_newer_mtime_pathname(struct archive_matching *, int,
+ const void *, time_t sec, long nsec);
+static int add_owner_id(struct archive_matching *, struct id_array *,
+ int64_t);
+static int add_owner_name(struct archive_matching *, struct match_list *,
+ int, const void *);
+static int add_pattern_mbs(struct archive_matching *, struct match_list *,
+ const char *);
+static int add_pattern_wcs(struct archive_matching *, struct match_list *,
+ const wchar_t *);
+static int cmp_key_mbs(const struct archive_rb_node *, const void *);
+static int cmp_key_wcs(const struct archive_rb_node *, const void *);
+static int cmp_node_mbs(const struct archive_rb_node *,
+ const struct archive_rb_node *);
+static int cmp_node_wcs(const struct archive_rb_node *,
+ const struct archive_rb_node *);
+static int error_nomem(struct archive_matching *);
+static int get_filetime_mbs(struct archive_matching *, const char *,
+ int, time_t *, long *);
+static int get_filetime_wcs(struct archive_matching *, const wchar_t *,
+ int, time_t *, long *);
+static void match_list_add(struct match_list *, struct match *);
+static void match_list_free(struct match_list *);
+static void match_list_init(struct match_list *);
+static int match_list_unmatched_inclusions_next(struct archive_matching *,
+ struct match_list *, int, const void **);
+static int match_owner_id(struct id_array *, int64_t);
+#if !defined(_WIN32) || defined(__CYGWIN__)
+static int match_owner_name_mbs(struct archive_matching *,
+ struct match_list *, const char *);
+#else
+static int match_owner_name_wcs(struct archive_matching *,
+ struct match_list *, const wchar_t *);
+#endif
+static int match_path_exclusion(struct archive_matching *,
+ struct match *, int, const void *);
+static int match_path_inclusion(struct archive_matching *,
+ struct match *, int, const void *);
+static void newer_file_list_add(struct newer_file_list *,
+ struct newer_file *);
+static void newer_file_list_free(struct newer_file_list *);
+static void newer_file_list_init(struct newer_file_list *);
+static int owner_excluded(struct archive_matching *,
+ struct archive_entry *);
+static int path_excluded(struct archive_matching *, int, const void *);
+static int time_excluded(struct archive_matching *,
+ struct archive_entry *);
+
+static const struct archive_rb_tree_ops rb_ops_mbs = {
+ cmp_node_mbs, cmp_key_mbs
+};
+
+static const struct archive_rb_tree_ops rb_ops_wcs = {
+ cmp_node_wcs, cmp_key_wcs
+};
+
+/*
+ * The matching logic here needs to be re-thought. I started out to
+ * try to mimic gtar's matching logic, but it's not entirely
+ * consistent. In particular 'tar -t' and 'tar -x' interpret patterns
+ * on the command line as anchored, but --exclude doesn't.
+ */
+
+static int
+error_nomem(struct archive_matching *a)
+{
+ archive_set_error(&(a->archive), ENOMEM, "No memory");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+}
+
+struct archive *
+archive_matching_new(void)
+{
+ struct archive_matching *a;
+
+ a = (struct archive_matching *)calloc(1, sizeof(*a));
+ if (a == NULL)
+ return (NULL);
+ a->archive.magic = ARCHIVE_MATCHING_MAGIC;
+ a->archive.state = ARCHIVE_STATE_NEW;
+ match_list_init(&(a->inclusions));
+ match_list_init(&(a->exclusions));
+ __archive_rb_tree_init(&(a->newer_tree), &rb_ops_mbs);
+ newer_file_list_init(&(a->newer_list));
+ match_list_init(&(a->inclusion_unames));
+ match_list_init(&(a->inclusion_gnames));
+ return (&(a->archive));
+}
+
+int
+archive_matching_free(struct archive *_a)
+{
+ struct archive_matching *a;
+
+ if (_a == NULL)
+ return (ARCHIVE_OK);
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_matching_free");
+ a = (struct archive_matching *)_a;
+ match_list_free(&(a->inclusions));
+ match_list_free(&(a->exclusions));
+ newer_file_list_free(&(a->newer_list));
+ free(a->inclusion_uids.ids);
+ free(a->inclusion_gids.ids);
+ match_list_free(&(a->inclusion_unames));
+ match_list_free(&(a->inclusion_gnames));
+ free(a);
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Convenience function to perform all exclusion tests.
+ *
+ * Returns 1 if archive entry is excluded.
+ * Returns 0 if archive entry is not excluded.
+ * Returns <0 if something error happened.
+ */
+int
+archive_matching_excluded_ae(struct archive *_a, struct archive_entry *entry)
+{
+ struct archive_matching *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_excluded_ae");
+
+ a = (struct archive_matching *)_a;
+ if (entry == NULL) {
+ archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+ return (ARCHIVE_FAILED);
+ }
+
+ r = 0;
+ if (a->setflag & PATTERN_IS_SET) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ r = path_excluded(a, 0, archive_entry_pathname_w(entry));
+#else
+ r = path_excluded(a, 1, archive_entry_pathname(entry));
+#endif
+ if (r != 0)
+ return (r);
+ }
+
+ if (a->setflag & TIME_IS_SET) {
+ r = time_excluded(a, entry);
+ if (r != 0)
+ return (r);
+ }
+
+ if (a->setflag & ID_IS_SET)
+ r = owner_excluded(a, entry);
+ return (r);
+}
+
+/*
+ * Utility functions to manage exclusion/inclusion patterns
+ */
+
+int
+archive_matching_exclude_pattern(struct archive *_a, const char *pattern)
+{
+ struct archive_matching *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_exclude_pattern");
+ a = (struct archive_matching *)_a;
+
+ if (pattern == NULL || *pattern == '\0') {
+ archive_set_error(&(a->archive), EINVAL, "pattern is empty");
+ return (ARCHIVE_FAILED);
+ }
+ if ((r = add_pattern_mbs(a, &(a->exclusions), pattern)) != ARCHIVE_OK)
+ return (r);
+ return (ARCHIVE_OK);
+}
+
+int
+archive_matching_exclude_pattern_w(struct archive *_a, const wchar_t *pattern)
+{
+ struct archive_matching *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_exclude_pattern_w");
+ a = (struct archive_matching *)_a;
+
+ if (pattern == NULL || *pattern == L'\0') {
+ archive_set_error(&(a->archive), EINVAL, "pattern is empty");
+ return (ARCHIVE_FAILED);
+ }
+ if ((r = add_pattern_wcs(a, &(a->exclusions), pattern)) != ARCHIVE_OK)
+ return (r);
+ return (ARCHIVE_OK);
+}
+
+int
+archive_matching_include_pattern(struct archive *_a, const char *pattern)
+{
+ struct archive_matching *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_include_pattern");
+ a = (struct archive_matching *)_a;
+
+ if (pattern == NULL || *pattern == '\0') {
+ archive_set_error(&(a->archive), EINVAL, "pattern is empty");
+ return (ARCHIVE_FAILED);
+ }
+ if ((r = add_pattern_mbs(a, &(a->inclusions), pattern)) != ARCHIVE_OK)
+ return (r);
+ return (ARCHIVE_OK);
+}
+
+int
+archive_matching_include_pattern_w(struct archive *_a, const wchar_t *pattern)
+{
+ struct archive_matching *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_include_pattern_w");
+ a = (struct archive_matching *)_a;
+
+ if (pattern == NULL || *pattern == L'\0') {
+ archive_set_error(&(a->archive), EINVAL, "pattern is empty");
+ return (ARCHIVE_FAILED);
+ }
+ if ((r = add_pattern_wcs(a, &(a->inclusions), pattern)) != ARCHIVE_OK)
+ return (r);
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Test functions for pathname patterns.
+ *
+ * Returns 1 if archive entry is excluded.
+ * Returns 0 if archive entry is not excluded.
+ * Returns <0 if something error happened.
+ */
+int
+archive_matching_path_excluded(struct archive *_a, const char *pathname)
+{
+ struct archive_matching *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_path_excluded");
+
+ if (pathname == NULL || *pathname == L'\0')
+ return (0);
+ a = (struct archive_matching *)_a;
+
+ /* If we don't have exclusion/inclusion pattern set at all,
+ * the pathname is always not excluded. */
+ if ((a->setflag & PATTERN_IS_SET) == 0)
+ return (0);
+ return (path_excluded(a, 1, pathname));
+}
+
+int
+archive_matching_path_excluded_w(struct archive *_a, const wchar_t *pathname)
+{
+ struct archive_matching *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_path_excluded_w");
+
+ if (pathname == NULL || *pathname == L'\0')
+ return (0);
+ a = (struct archive_matching *)_a;
+
+ /* If we don't have exclusion/inclusion pattern set at all,
+ * the pathname is always not excluded. */
+ if ((a->setflag & PATTERN_IS_SET) == 0)
+ return (0);
+ return (path_excluded(a, 0, pathname));
+}
+
+int
+archive_matching_path_excluded_ae(struct archive *_a,
+ struct archive_entry *entry)
+{
+ struct archive_matching *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_path_excluded_ae");
+
+ a = (struct archive_matching *)_a;
+ if (entry == NULL) {
+ archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* If we don't have exclusion/inclusion pattern set at all,
+ * the entry is always not excluded. */
+ if ((a->setflag & PATTERN_IS_SET) == 0)
+ return (0);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ return (path_excluded(a, 0, archive_entry_pathname_w(entry)));
+#else
+ return (path_excluded(a, 1, archive_entry_pathname(entry)));
+#endif
+}
+
+/*
+ * Utilty functions to get statistic information for inclusion patterns.
+ */
+int
+archive_matching_path_unmatched_inclusions(struct archive *_a)
+{
+ struct archive_matching *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_unmatched_inclusions");
+ a = (struct archive_matching *)_a;
+
+ return (a->inclusions.unmatched_count);
+}
+
+int
+archive_matching_path_unmatched_inclusions_next(struct archive *_a,
+ const char **_p)
+{
+ struct archive_matching *a;
+ const void *v;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_unmatched_inclusions_next");
+ a = (struct archive_matching *)_a;
+
+ r = match_list_unmatched_inclusions_next(a, &(a->inclusions), 1, &v);
+ *_p = (const char *)v;
+ return (r);
+}
+
+int
+archive_matching_path_unmatched_inclusions_next_w(struct archive *_a,
+ const wchar_t **_p)
+{
+ struct archive_matching *a;
+ const void *v;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_unmatched_inclusions_next_w");
+ a = (struct archive_matching *)_a;
+
+ r = match_list_unmatched_inclusions_next(a, &(a->inclusions), 0, &v);
+ *_p = (const wchar_t *)v;
+ return (r);
+}
+
+static int
+add_pattern_mbs(struct archive_matching *a, struct match_list *list,
+ const char *pattern)
+{
+ struct match *match;
+ size_t len;
+
+ match = calloc(1, sizeof(*match));
+ if (match == NULL)
+ return (error_nomem(a));
+ /* Both "foo/" and "foo" should match "foo/bar". */
+ len = strlen(pattern);
+ if (len && pattern[len - 1] == '/')
+ --len;
+ archive_mstring_copy_mbs_len(&(match->pattern), pattern, len);
+ match_list_add(list, match);
+ a->setflag |= PATTERN_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+static int
+add_pattern_wcs(struct archive_matching *a, struct match_list *list,
+ const wchar_t *pattern)
+{
+ struct match *match;
+ size_t len;
+
+ match = calloc(1, sizeof(*match));
+ if (match == NULL)
+ return (error_nomem(a));
+ /* Both "foo/" and "foo" should match "foo/bar". */
+ len = wcslen(pattern);
+ if (len && pattern[len - 1] == L'/')
+ --len;
+ archive_mstring_copy_wcs_len(&(match->pattern), pattern, len);
+ match_list_add(list, match);
+ a->setflag |= PATTERN_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+static int
+path_excluded(struct archive_matching *a, int mbs, const void *pathname)
+{
+ struct match *match;
+ struct match *matched;
+ int r;
+
+ if (a == NULL)
+ return (0);
+
+ /* Mark off any unmatched inclusions. */
+ /* In particular, if a filename does appear in the archive and
+ * is explicitly included and excluded, then we don't report
+ * it as missing even though we don't extract it.
+ */
+ matched = NULL;
+ for (match = a->inclusions.first; match != NULL;
+ match = match->next){
+ if (match->matches == 0
+ && (r = match_path_inclusion(a, match, mbs, pathname))) {
+ if (r < 0)
+ return (r);
+ a->inclusions.unmatched_count--;
+ match->matches++;
+ matched = match;
+ }
+ }
+
+ /* Exclusions take priority */
+ for (match = a->exclusions.first; match != NULL;
+ match = match->next){
+ r = match_path_exclusion(a, match, mbs, pathname);
+ if (r)
+ return (r);
+ }
+
+ /* It's not excluded and we found an inclusion above, so it's
+ * included. */
+ if (matched != NULL)
+ return (0);
+
+
+ /* We didn't find an unmatched inclusion, check the remaining ones. */
+ for (match = a->inclusions.first; match != NULL;
+ match = match->next){
+ /* We looked at previously-unmatched inclusions already. */
+ if (match->matches > 0
+ && (r = match_path_inclusion(a, match, mbs, pathname))) {
+ if (r < 0)
+ return (r);
+ match->matches++;
+ return (0);
+ }
+ }
+
+ /* If there were inclusions, default is to exclude. */
+ if (a->inclusions.first != NULL)
+ return (1);
+
+ /* No explicit inclusions, default is to match. */
+ return (0);
+}
+
+/*
+ * This is a little odd, but it matches the default behavior of
+ * gtar. In particular, 'a*b' will match 'foo/a1111/222b/bar'
+ *
+ */
+static int
+match_path_exclusion(struct archive_matching *a, struct match *m,
+ int mbs, const void *pn)
+{
+ int flag = PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END;
+ int r;
+
+ if (mbs) {
+ const char *p;
+ r = archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p);
+ if (r == 0)
+ return (archive_pathmatch(p, (const char *)pn, flag));
+ } else {
+ const wchar_t *p;
+ r = archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p);
+ if (r == 0)
+ return (archive_pathmatch_w(p, (const wchar_t *)pn,
+ flag));
+ }
+ if (errno == ENOMEM)
+ return (error_nomem(a));
+ return (0);
+}
+
+/*
+ * Again, mimic gtar: inclusions are always anchored (have to match
+ * the beginning of the path) even though exclusions are not anchored.
+ */
+static int
+match_path_inclusion(struct archive_matching *a, struct match *m,
+ int mbs, const void *pn)
+{
+ int flag = PATHMATCH_NO_ANCHOR_END;
+ int r;
+
+ if (mbs) {
+ const char *p;
+ r = archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p);
+ if (r == 0)
+ return (archive_pathmatch(p, (const char *)pn, flag));
+ } else {
+ const wchar_t *p;
+ r = archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p);
+ if (r == 0)
+ return (archive_pathmatch_w(p, (const wchar_t *)pn,
+ flag));
+ }
+ if (errno == ENOMEM)
+ return (error_nomem(a));
+ return (0);
+}
+
+static void
+match_list_init(struct match_list *list)
+{
+ list->first = NULL;
+ list->last = &(list->first);
+ list->count = 0;
+}
+
+static void
+match_list_free(struct match_list *list)
+{
+ struct match *p, *q;
+
+ for (p = list->first; p != NULL; ) {
+ q = p;
+ p = p->next;
+ archive_mstring_clean(&(q->pattern));
+ free(q);
+ }
+}
+
+static void
+match_list_add(struct match_list *list, struct match *m)
+{
+ *list->last = m;
+ list->last = &(m->next);
+ list->count++;
+ list->unmatched_count++;
+}
+
+static int
+match_list_unmatched_inclusions_next(struct archive_matching *a,
+ struct match_list *list, int mbs, const void **vp)
+{
+ struct match *m;
+
+ *vp = NULL;
+ if (list->unmatched_eof) {
+ list->unmatched_eof = 0;
+ return (ARCHIVE_EOF);
+ }
+ if (list->unmatched_next == NULL) {
+ if (list->unmatched_count == 0)
+ return (ARCHIVE_EOF);
+ list->unmatched_next = list->first;
+ }
+
+ for (m = list->unmatched_next; m != NULL; m = m->next) {
+ int r;
+
+ if (m->matches)
+ continue;
+ if (mbs) {
+ const char *p;
+ r = archive_mstring_get_mbs(&(a->archive),
+ &(m->pattern), &p);
+ if (r < 0 && errno == ENOMEM)
+ return (error_nomem(a));
+ if (p == NULL)
+ p = "";
+ *vp = p;
+ } else {
+ const wchar_t *p;
+ r = archive_mstring_get_wcs(&(a->archive),
+ &(m->pattern), &p);
+ if (r < 0 && errno == ENOMEM)
+ return (error_nomem(a));
+ if (p == NULL)
+ p = L"";
+ *vp = p;
+ }
+ list->unmatched_next = m->next;
+ if (list->unmatched_next == NULL)
+ /* To return EOF next time. */
+ list->unmatched_eof = 1;
+ return (ARCHIVE_OK);
+ }
+ list->unmatched_next = NULL;
+ return (ARCHIVE_EOF);
+}
+
+/*
+ * Utility functions to manage inclusion timestamps.
+ */
+
+int
+archive_matching_newer_mtime(struct archive *_a, time_t sec, long nsec)
+{
+ struct archive_matching *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_newer_mtime");
+ a = (struct archive_matching *)_a;
+
+ a->newer_mtime_filter = 1;
+ a->newer_mtime_sec = sec;
+ a->newer_mtime_nsec = nsec;
+ a->setflag |= TIME_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_matching_newer_mtime_than(struct archive *_a, const char *pathname)
+{
+ struct archive_matching *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_newer_mtime_than");
+ a = (struct archive_matching *)_a;
+
+ if (pathname == NULL || *pathname == '\0') {
+ archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+ return (ARCHIVE_FAILED);
+ }
+ r = get_filetime_mbs(a, pathname, 0, &(a->newer_mtime_sec),
+ &(a->newer_mtime_nsec));
+ if (r != ARCHIVE_OK)
+ return (r);
+ a->newer_mtime_filter = 1;
+ a->setflag |= TIME_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_matching_newer_mtime_than_w(struct archive *_a, const wchar_t *pathname)
+{
+ struct archive_matching *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_newer_mtime_than_w");
+ a = (struct archive_matching *)_a;
+
+ if (pathname == NULL || *pathname == L'\0') {
+ archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+ return (ARCHIVE_FAILED);
+ }
+ r = get_filetime_wcs(a, pathname, 0, &(a->newer_mtime_sec),
+ &(a->newer_mtime_nsec));
+ if (r != ARCHIVE_OK)
+ return (r);
+ a->newer_mtime_filter = 1;
+ a->setflag |= TIME_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_matching_newer_ctime(struct archive *_a, time_t sec, long nsec)
+{
+ struct archive_matching *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_newer_ctime");
+ a = (struct archive_matching *)_a;
+
+ a->newer_ctime_filter = 1;
+ a->newer_ctime_sec = sec;
+ a->newer_ctime_nsec = nsec;
+ a->setflag |= TIME_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_matching_newer_ctime_than(struct archive *_a,
+ const char *pathname)
+{
+ struct archive_matching *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_newer_ctime_than");
+ a = (struct archive_matching *)_a;
+
+ if (pathname == NULL || *pathname == '\0') {
+ archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+ return (ARCHIVE_FAILED);
+ }
+ r = get_filetime_mbs(a, pathname, 0, &(a->newer_ctime_sec),
+ &(a->newer_ctime_nsec));
+ if (r != ARCHIVE_OK)
+ return (r);
+ a->newer_ctime_filter = 1;
+ a->setflag |= TIME_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_matching_newer_ctime_than_w(struct archive *_a, const wchar_t *pathname)
+{
+ struct archive_matching *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_newer_ctime_than_w");
+ a = (struct archive_matching *)_a;
+
+ if (pathname == NULL || *pathname == L'\0') {
+ archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+ return (ARCHIVE_FAILED);
+ }
+ r = get_filetime_wcs(a, pathname, 0, &(a->newer_ctime_sec),
+ &(a->newer_ctime_nsec));
+ if (r != ARCHIVE_OK)
+ return (r);
+ a->newer_ctime_filter = 1;
+ a->setflag |= TIME_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_matching_older_mtime(struct archive *_a, time_t sec, long nsec)
+{
+ struct archive_matching *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_older_mtime");
+ a = (struct archive_matching *)_a;
+
+ a->older_mtime_filter = 1;
+ a->older_mtime_sec = sec;
+ a->older_mtime_nsec = nsec;
+ a->setflag |= TIME_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_matching_older_mtime_than(struct archive *_a, const char *pathname)
+{
+ struct archive_matching *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_older_mtime_than");
+ a = (struct archive_matching *)_a;
+
+ if (pathname == NULL || *pathname == '\0') {
+ archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+ return (ARCHIVE_FAILED);
+ }
+ r = get_filetime_mbs(a, pathname, 0, &(a->older_mtime_sec),
+ &(a->older_mtime_nsec));
+ if (r != ARCHIVE_OK)
+ return (r);
+ a->older_mtime_filter = 1;
+ a->setflag |= TIME_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_matching_older_mtime_than_w(struct archive *_a, const wchar_t *pathname)
+{
+ struct archive_matching *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_older_mtime_than_w");
+ a = (struct archive_matching *)_a;
+
+ if (pathname == NULL || *pathname == L'\0') {
+ archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+ return (ARCHIVE_FAILED);
+ }
+ r = get_filetime_wcs(a, pathname, 0, &(a->older_mtime_sec),
+ &(a->older_mtime_nsec));
+ if (r != ARCHIVE_OK)
+ return (r);
+ a->older_mtime_filter = 1;
+ a->setflag |= TIME_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_matching_older_ctime(struct archive *_a, time_t sec, long nsec)
+{
+ struct archive_matching *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_older_ctime");
+ a = (struct archive_matching *)_a;
+
+ a->older_ctime_filter = 1;
+ a->older_ctime_sec = sec;
+ a->older_ctime_nsec = nsec;
+ a->setflag |= TIME_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_matching_older_ctime_than(struct archive *_a, const char *pathname)
+{
+ struct archive_matching *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_older_ctime_than");
+ a = (struct archive_matching *)_a;
+
+ if (pathname == NULL || *pathname == '\0') {
+ archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+ return (ARCHIVE_FAILED);
+ }
+ r = get_filetime_mbs(a, pathname, 0, &(a->older_ctime_sec),
+ &(a->older_ctime_nsec));
+ if (r != ARCHIVE_OK)
+ return (r);
+ a->older_ctime_filter = 1;
+ a->setflag |= TIME_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_matching_older_ctime_than_w(struct archive *_a, const wchar_t *pathname)
+{
+ struct archive_matching *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_older_ctime_than_w");
+ a = (struct archive_matching *)_a;
+
+ if (pathname == NULL || *pathname == '\0') {
+ archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+ return (ARCHIVE_FAILED);
+ }
+ r = get_filetime_wcs(a, pathname, 0, &(a->older_ctime_sec),
+ &(a->older_ctime_nsec));
+ if (r != ARCHIVE_OK)
+ return (r);
+ a->older_ctime_filter = 1;
+ a->setflag |= TIME_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_matching_pathname_newer_mtime(struct archive *_a,
+ const char *pathname, time_t sec, long nsec)
+{
+ struct archive_matching *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_add_newer_mtime_pathname");
+ a = (struct archive_matching *)_a;
+
+ if (pathname == NULL || *pathname == '\0') {
+ archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+ return (ARCHIVE_FAILED);
+ }
+ a->newer_tree.rbt_ops = &rb_ops_mbs;
+ return (add_newer_mtime_pathname(a, 1, pathname, sec, nsec));
+}
+
+int
+archive_matching_pathname_newer_mtime_w(struct archive *_a,
+ const wchar_t *pathname, time_t sec, long nsec)
+{
+ struct archive_matching *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_add_newer_mtime_pathname_w");
+ a = (struct archive_matching *)_a;
+
+ if (pathname == NULL || *pathname == '\0') {
+ archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+ return (ARCHIVE_FAILED);
+ }
+ a->newer_tree.rbt_ops = &rb_ops_wcs;
+ return (add_newer_mtime_pathname(a, 0, pathname, sec, nsec));
+}
+
+int
+archive_matching_pathname_newer_mtime_ae(struct archive *_a,
+ struct archive_entry *entry)
+{
+ struct archive_matching *a;
+ const void *pathname;
+ int mbs;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_add_newer_mtime_ae");
+ a = (struct archive_matching *)_a;
+
+ if (entry == NULL) {
+ archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+ return (ARCHIVE_FAILED);
+ }
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ a->newer_tree.rbt_ops = &rb_ops_wcs;
+ pathname = archive_entry_pathname_w(entry);
+ mbs = 0;
+#else
+ a->newer_tree.rbt_ops = &rb_ops_mbs;
+ pathname = archive_entry_pathname(entry);
+ mbs = 1;
+#endif
+ if (pathname == NULL) {
+ archive_set_error(&(a->archive), EINVAL, "pathname is NULL");
+ return (ARCHIVE_FAILED);
+ }
+ return (add_newer_mtime_pathname(a, mbs, pathname,
+ archive_entry_mtime(entry), archive_entry_mtime_nsec(entry)));
+}
+
+/*
+ * Test function for time stamps.
+ *
+ * Returns 1 if archive entry is excluded.
+ * Returns 0 if archive entry is not excluded.
+ * Returns <0 if something error happened.
+ */
+int
+archive_matching_time_excluded_ae(struct archive *_a,
+ struct archive_entry *entry)
+{
+ struct archive_matching *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_time_excluded_ae");
+
+ a = (struct archive_matching *)_a;
+ if (entry == NULL) {
+ archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* If we don't have inclusion time set at all, the entry is always
+ * not excluded. */
+ if ((a->setflag & TIME_IS_SET) == 0)
+ return (0);
+ return (time_excluded(a, entry));
+}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#define EPOC_TIME (116444736000000000ui64)
+#else
+static int
+get_time(struct archive_matching *a, struct stat *st, int is_ctime,
+ time_t *time, long *ns)
+{
+ struct archive_entry *ae;
+
+ ae = archive_entry_new();
+ if (ae == NULL)
+ return (error_nomem(a));
+ archive_entry_copy_stat(ae, st);
+ if (is_ctime) {
+ *time = archive_entry_ctime(ae);
+ *ns = archive_entry_ctime_nsec(ae);
+ } else {
+ *time = archive_entry_mtime(ae);
+ *ns = archive_entry_mtime_nsec(ae);
+ }
+ archive_entry_free(ae);
+ return (ARCHIVE_OK);
+}
+#endif
+
+static int
+get_filetime_mbs(struct archive_matching *a, const char *path,
+ int is_ctime, time_t *time, long *ns)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* NOTE: stat() on Windows cannot handle nano seconds. */
+ HANDLE h;
+ WIN32_FIND_DATA d;
+ ULARGE_INTEGER utc;
+
+ h = FindFirstFileA(path, &d);
+ if (h == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&(a->archive), errno,
+ "Failed to FindFirstFileA");
+ return (ARCHIVE_FAILED);
+ }
+ FindClose(h);
+ if (is_ctime) {
+ utc.HighPart = d.ftCreationTime.dwHighDateTime;
+ utc.LowPart = d.ftCreationTime.dwLowDateTime;
+ } else {
+ utc.HighPart = d.ftLastWriteTime.dwHighDateTime;
+ utc.LowPart = d.ftLastWriteTime.dwLowDateTime;
+ }
+ if (utc.QuadPart >= EPOC_TIME) {
+ utc.QuadPart -= EPOC_TIME;
+ *time = (time_t)(utc.QuadPart / 10000000);
+ *ns = (long)(utc.QuadPart % 10000000) * 100;
+ } else {
+ *time = 0;
+ *ns = 0;
+ }
+ return (ARCHIVE_OK);
+#else
+ struct stat st;
+
+ if (stat(path, &st) != 0) {
+ archive_set_error(&(a->archive), errno, "Failed to stat()");
+ return (ARCHIVE_FAILED);
+ }
+ return (get_time(a, &st, is_ctime, time, ns));
+#endif
+}
+
+static int
+get_filetime_wcs(struct archive_matching *a, const wchar_t *path,
+ int is_ctime, time_t *time, long *ns)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ HANDLE h;
+ WIN32_FIND_DATAW d;
+ ULARGE_INTEGER utc;
+
+ h = FindFirstFileW(path, &d);
+ if (h == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&(a->archive), errno,
+ "Failed to FindFirstFile");
+ return (ARCHIVE_FAILED);
+ }
+ FindClose(h);
+ if (is_ctime) {
+ utc.HighPart = d.ftCreationTime.dwHighDateTime;
+ utc.LowPart = d.ftCreationTime.dwLowDateTime;
+ } else {
+ utc.HighPart = d.ftLastWriteTime.dwHighDateTime;
+ utc.LowPart = d.ftLastWriteTime.dwLowDateTime;
+ }
+ if (utc.QuadPart >= EPOC_TIME) {
+ utc.QuadPart -= EPOC_TIME;
+ *time = (time_t)(utc.QuadPart / 10000000);
+ *ns = (long)(utc.QuadPart % 10000000) * 100;
+ } else {
+ *time = 0;
+ *ns = 0;
+ }
+ return (ARCHIVE_OK);
+#else
+ struct stat st;
+ struct archive_string as;
+
+ archive_string_init(&as);
+ if (archive_string_append_from_wcs(&as, path, wcslen(path)) < 0) {
+ if (errno == ENOMEM)
+ return (error_nomem(a));
+ archive_set_error(&(a->archive), -1,
+ "Failed to convert WCS to MBS");
+ return (ARCHIVE_FAILED);
+ }
+ if (stat(as.s, &st) != 0) {
+ archive_set_error(&(a->archive), errno, "Failed to stat()");
+ archive_string_free(&as);
+ return (ARCHIVE_FAILED);
+ }
+ archive_string_free(&as);
+ return (get_time(a, &st, is_ctime, time, ns));
+#endif
+}
+
+static int
+cmp_node_mbs(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ struct newer_file *f1 = (struct newer_file *)n1;
+ struct newer_file *f2 = (struct newer_file *)n2;
+ const char *p1, *p2;
+
+ archive_mstring_get_mbs(NULL, &(f1->pathname), &p1);
+ archive_mstring_get_mbs(NULL, &(f2->pathname), &p2);
+ if (p1 == NULL)
+ return (1);
+ if (p2 == NULL)
+ return (-1);
+ return (strcmp(p1, p2));
+}
+
+static int
+cmp_key_mbs(const struct archive_rb_node *n, const void *key)
+{
+ struct newer_file *f = (struct newer_file *)n;
+ const char *p;
+
+ archive_mstring_get_mbs(NULL, &(f->pathname), &p);
+ if (p == NULL)
+ return (-1);
+ return (strcmp(p, (const char *)key));
+}
+
+static int
+cmp_node_wcs(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ struct newer_file *f1 = (struct newer_file *)n1;
+ struct newer_file *f2 = (struct newer_file *)n2;
+ const wchar_t *p1, *p2;
+
+ archive_mstring_get_wcs(NULL, &(f1->pathname), &p1);
+ archive_mstring_get_wcs(NULL, &(f2->pathname), &p2);
+ if (p1 == NULL)
+ return (1);
+ if (p2 == NULL)
+ return (-1);
+ return (wcscmp(p1, p2));
+}
+
+static int
+cmp_key_wcs(const struct archive_rb_node *n, const void *key)
+{
+ struct newer_file *f = (struct newer_file *)n;
+ const wchar_t *p;
+
+ archive_mstring_get_wcs(NULL, &(f->pathname), &p);
+ if (p == NULL)
+ return (-1);
+ return (wcscmp(p, (const wchar_t *)key));
+}
+
+static void
+newer_file_list_init(struct newer_file_list *list)
+{
+ list->first = NULL;
+ list->last = &(list->first);
+ list->count = 0;
+}
+
+static void
+newer_file_list_free(struct newer_file_list *list)
+{
+ struct newer_file *p, *q;
+
+ for (p = list->first; p != NULL; ) {
+ q = p;
+ p = p->next;
+ archive_mstring_clean(&(q->pathname));
+ free(q);
+ }
+}
+
+static void
+newer_file_list_add(struct newer_file_list *list, struct newer_file *file)
+{
+ *list->last = file;
+ list->last = &(file->next);
+ list->count++;
+}
+
+static int
+add_newer_mtime_pathname(struct archive_matching *a, int mbs,
+ const void *pathname, time_t sec, long nsec)
+{
+ struct newer_file *f;
+ int r;
+
+ f = calloc(1, sizeof(*f));
+ if (f == NULL)
+ return (error_nomem(a));
+ if (mbs)
+ archive_mstring_copy_mbs(&(f->pathname), pathname);
+ else
+ archive_mstring_copy_wcs(&(f->pathname), pathname);
+ f->mtime_sec = sec;
+ f->mtime_nsec = nsec;
+ r = __archive_rb_tree_insert_node(&(a->newer_tree), &(f->node));
+ if (!r) {
+ struct newer_file *f2;
+
+ /* Get the duplicated file. */
+ f2 = (struct newer_file *)__archive_rb_tree_find_node(
+ &(a->newer_tree), pathname);
+
+ /* Overwrite mtime condision if it is newer than. */
+ if (f2 != NULL && ((f2->mtime_sec < f->mtime_sec) ||
+ (f2->mtime_sec == f->mtime_sec &&
+ f2->mtime_nsec < f->mtime_nsec))) {
+ f2->mtime_sec = f->mtime_sec;
+ f2->mtime_nsec = f->mtime_nsec;
+ /* Release the duplicated file. */
+ archive_mstring_clean(&(f->pathname));
+ free(f);
+ return (ARCHIVE_OK);
+ }
+ }
+ newer_file_list_add(&(a->newer_list), f);
+ a->setflag |= TIME_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+static int
+time_excluded(struct archive_matching *a, struct archive_entry *entry)
+{
+ struct newer_file *f;
+ const void *pathname;
+ time_t sec;
+ long nsec;
+
+ /*
+ * If this file/dir is excluded by a time comparison, skip it.
+ */
+ if (a->newer_ctime_filter) {
+ sec = archive_entry_ctime(entry);
+ if (sec < a->newer_ctime_sec)
+ return (1); /* Too old, skip it. */
+ nsec = archive_entry_ctime_nsec(entry);
+ if (sec == a->newer_ctime_sec
+ && nsec <= a->newer_ctime_nsec)
+ return (1); /* Too old, skip it. */
+ }
+ if (a->older_ctime_filter) {
+ sec = archive_entry_ctime(entry);
+ if (sec > a->older_ctime_sec)
+ return (1); /* Too new, skip it. */
+ nsec = archive_entry_ctime_nsec(entry);
+ if (sec == a->older_ctime_sec
+ && nsec >= a->older_ctime_nsec)
+ return (1); /* Too new, skip it. */
+ }
+ if (a->newer_mtime_filter) {
+ sec = archive_entry_mtime(entry);
+ if (sec < a->newer_mtime_sec)
+ return (1); /* Too old, skip it. */
+ nsec = archive_entry_mtime_nsec(entry);
+ if (sec == a->newer_mtime_sec
+ && nsec <= a->newer_mtime_nsec)
+ return (1); /* Too old, skip it. */
+ }
+ if (a->older_mtime_filter) {
+ sec = archive_entry_mtime(entry);
+ if (sec > a->older_mtime_sec)
+ return (1); /* Too new, skip it. */
+ nsec = archive_entry_mtime_nsec(entry);
+ if (sec == a->older_mtime_sec
+ && nsec >= a->older_mtime_nsec)
+ return (1); /* Too new, skip it. */
+ }
+
+ /* If there is no incluson list, include the file. */
+ if (a->newer_list.count == 0)
+ return (0);
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ pathname = archive_entry_pathname_w(entry);
+ a->newer_tree.rbt_ops = &rb_ops_wcs;
+#else
+ pathname = archive_entry_pathname(entry);
+ a->newer_tree.rbt_ops = &rb_ops_mbs;
+#endif
+ if (pathname == NULL)
+ return (0);
+
+ f = (struct newer_file *)__archive_rb_tree_find_node(
+ &(a->newer_tree), pathname);
+ /* If the file wasn't rejected, include it. */
+ if (f == NULL)
+ return (0);
+
+ sec = archive_entry_mtime(entry);
+ if (f->mtime_sec < sec)
+ return (0);
+ nsec = archive_entry_mtime_nsec(entry);
+ return (f->mtime_sec > sec || f->mtime_nsec >= nsec);
+}
+
+/*
+ * Utility functions to manage inclusion owners
+ */
+
+int
+archive_matching_include_uid(struct archive *_a, int64_t uid)
+{
+ struct archive_matching *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_include_uid");
+ a = (struct archive_matching *)_a;
+ return (add_owner_id(a, &(a->inclusion_uids), uid));
+}
+
+int
+archive_matching_include_gid(struct archive *_a, int64_t gid)
+{
+ struct archive_matching *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_include_gid");
+ a = (struct archive_matching *)_a;
+ return (add_owner_id(a, &(a->inclusion_gids), gid));
+}
+
+int
+archive_matching_include_uname(struct archive *_a, const char *uname)
+{
+ struct archive_matching *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_include_uname");
+ a = (struct archive_matching *)_a;
+ return (add_owner_name(a, &(a->inclusion_unames), 1, uname));
+}
+
+int
+archive_matching_include_uname_w(struct archive *_a, const wchar_t *uname)
+{
+ struct archive_matching *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_include_uname_w");
+ a = (struct archive_matching *)_a;
+ return (add_owner_name(a, &(a->inclusion_unames), 0, uname));
+}
+
+int
+archive_matching_include_gname(struct archive *_a, const char *gname)
+{
+ struct archive_matching *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_include_gname");
+ a = (struct archive_matching *)_a;
+ return (add_owner_name(a, &(a->inclusion_gnames), 1, gname));
+}
+
+int
+archive_matching_include_gname_w(struct archive *_a, const wchar_t *gname)
+{
+ struct archive_matching *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_include_gname_w");
+ a = (struct archive_matching *)_a;
+ return (add_owner_name(a, &(a->inclusion_gnames), 0, gname));
+}
+
+/*
+ * Test function for owner(uid, gid, uname, gname).
+ *
+ * Returns 1 if archive entry is excluded.
+ * Returns 0 if archive entry is not excluded.
+ * Returns <0 if something error happened.
+ */
+int
+archive_matching_owner_excluded_ae(struct archive *_a,
+ struct archive_entry *entry)
+{
+ struct archive_matching *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCHING_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_matching_id_excluded_ae");
+
+ a = (struct archive_matching *)_a;
+ if (entry == NULL) {
+ archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* If we don't have inclusion id set at all, the entry is always
+ * not excluded. */
+ if ((a->setflag & ID_IS_SET) == 0)
+ return (0);
+ return (owner_excluded(a, entry));
+}
+
+static int
+add_owner_id(struct archive_matching *a, struct id_array *ids, int64_t id)
+{
+ if (ids->count + 1 >= ids->size) {
+ if (ids->size == 0)
+ ids->size = 8;
+ else
+ ids->size *= 2;
+ ids->ids = realloc(ids->ids, sizeof(*ids->ids) * ids->size);
+ if (ids->ids == NULL)
+ return (error_nomem(a));
+ }
+ /*
+ * TODO: sort id list.
+ */
+ ids->ids[ids->count++] = id;
+ a->setflag |= ID_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+static int
+match_owner_id(struct id_array *ids, int64_t id)
+{
+ int i;
+
+ for (i = 0; i < (int)ids->count; i++) {
+ if (ids->ids[i] == id)
+ return (1);
+ }
+ return (0);
+}
+
+static int
+add_owner_name(struct archive_matching *a, struct match_list *list,
+ int mbs, const void *name)
+{
+ struct match *match;
+
+ match = calloc(1, sizeof(*match));
+ if (match == NULL)
+ return (error_nomem(a));
+ if (mbs)
+ archive_mstring_copy_mbs(&(match->pattern), name);
+ else
+ archive_mstring_copy_wcs(&(match->pattern), name);
+ match_list_add(list, match);
+ a->setflag |= ID_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+#if !defined(_WIN32) || defined(__CYGWIN__)
+static int
+match_owner_name_mbs(struct archive_matching *a, struct match_list *list,
+ const char *name)
+{
+ struct match *m;
+ const char *p;
+
+ if (name == NULL || *name == '\0')
+ return (0);
+ for (m = list->first; m; m = m->next) {
+ if (archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p)
+ < 0 && errno == ENOMEM)
+ return (error_nomem(a));
+ if (p != NULL && strcmp(p, name) == 0) {
+ m->matches++;
+ return (1);
+ }
+ }
+ return (0);
+}
+#else
+static int
+match_owner_name_wcs(struct archive_matching *a, struct match_list *list,
+ const wchar_t *name)
+{
+ struct match *m;
+ const wchar_t *p;
+
+ if (name == NULL || *name == L'\0')
+ return (0);
+ for (m = list->first; m; m = m->next) {
+ if (archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p)
+ < 0 && errno == ENOMEM)
+ return (error_nomem(a));
+ if (p != NULL && wcscmp(p, name) == 0) {
+ m->matches++;
+ return (1);
+ }
+ }
+ return (0);
+}
+#endif
+
+static int
+owner_excluded(struct archive_matching *a, struct archive_entry *entry)
+{
+ int r;
+
+ if (a->inclusion_uids.count) {
+ if (!match_owner_id(&(a->inclusion_uids),
+ archive_entry_uid(entry)))
+ return (1);
+ }
+
+ if (a->inclusion_gids.count) {
+ if (!match_owner_id(&(a->inclusion_gids),
+ archive_entry_gid(entry)))
+ return (1);
+ }
+
+ if (a->inclusion_unames.count) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ r = match_owner_name_wcs(a, &(a->inclusion_unames),
+ archive_entry_uname_w(entry));
+#else
+ r = match_owner_name_mbs(a, &(a->inclusion_unames),
+ archive_entry_uname(entry));
+#endif
+ if (!r)
+ return (1);
+ else if (r < 0)
+ return (r);
+ }
+
+ if (a->inclusion_gnames.count) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ r = match_owner_name_wcs(a, &(a->inclusion_gnames),
+ archive_entry_gname_w(entry));
+#else
+ r = match_owner_name_mbs(a, &(a->inclusion_gnames),
+ archive_entry_gname(entry));
+#endif
+ if (!r)
+ return (1);
+ else if (r < 0)
+ return (r);
+ }
+ return (0);
+}
+
--- /dev/null
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
+
+#include "archive_pathmatch.h"
+
+/*
+ * Check whether a character 'c' is matched by a list specification [...]:
+ * * Leading '!' or '^' negates the class.
+ * * <char>-<char> is a range of characters
+ * * \<char> removes any special meaning for <char>
+ *
+ * Some interesting boundary cases:
+ * a-d-e is one range (a-d) followed by two single characters - and e.
+ * \a-\d is same as a-d
+ * a\-d is three single characters: a, d, -
+ * Trailing - is not special (so [a-] is two characters a and -).
+ * Initial - is not special ([a-] is same as [-a] is same as [\\-a])
+ * This function never sees a trailing \.
+ * [] always fails
+ * [!] always succeeds
+ */
+static int
+pm_list(const char *start, const char *end, const char c, int flags)
+{
+ const char *p = start;
+ char rangeStart = '\0', nextRangeStart;
+ int match = 1, nomatch = 0;
+
+ /* This will be used soon... */
+ (void)flags; /* UNUSED */
+
+ /* If this is a negated class, return success for nomatch. */
+ if ((*p == '!' || *p == '^') && p < end) {
+ match = 0;
+ nomatch = 1;
+ ++p;
+ }
+
+ while (p < end) {
+ nextRangeStart = '\0';
+ switch (*p) {
+ case '-':
+ /* Trailing or initial '-' is not special. */
+ if ((rangeStart == '\0') || (p == end - 1)) {
+ if (*p == c)
+ return (match);
+ } else {
+ char rangeEnd = *++p;
+ if (rangeEnd == '\\')
+ rangeEnd = *++p;
+ if ((rangeStart <= c) && (c <= rangeEnd))
+ return (match);
+ }
+ break;
+ case '\\':
+ ++p;
+ /* Fall through */
+ default:
+ if (*p == c)
+ return (match);
+ nextRangeStart = *p; /* Possible start of range. */
+ }
+ rangeStart = nextRangeStart;
+ ++p;
+ }
+ return (nomatch);
+}
+
+static int
+pm_list_w(const wchar_t *start, const wchar_t *end, const wchar_t c, int flags)
+{
+ const wchar_t *p = start;
+ wchar_t rangeStart = L'\0', nextRangeStart;
+ int match = 1, nomatch = 0;
+
+ /* This will be used soon... */
+ (void)flags; /* UNUSED */
+
+ /* If this is a negated class, return success for nomatch. */
+ if ((*p == L'!' || *p == L'^') && p < end) {
+ match = 0;
+ nomatch = 1;
+ ++p;
+ }
+
+ while (p < end) {
+ nextRangeStart = L'\0';
+ switch (*p) {
+ case L'-':
+ /* Trailing or initial '-' is not special. */
+ if ((rangeStart == L'\0') || (p == end - 1)) {
+ if (*p == c)
+ return (match);
+ } else {
+ char rangeEnd = *++p;
+ if (rangeEnd == L'\\')
+ rangeEnd = *++p;
+ if ((rangeStart <= c) && (c <= rangeEnd))
+ return (match);
+ }
+ break;
+ case L'\\':
+ ++p;
+ /* Fall through */
+ default:
+ if (*p == c)
+ return (match);
+ nextRangeStart = *p; /* Possible start of range. */
+ }
+ rangeStart = nextRangeStart;
+ ++p;
+ }
+ return (nomatch);
+}
+
+/*
+ * If s is pointing to "./", ".//", "./././" or the like, skip it.
+ */
+static const char *
+pm_slashskip(const char *s) {
+ while ((*s == '/')
+ || (s[0] == '.' && s[1] == '/')
+ || (s[0] == '.' && s[1] == '\0'))
+ ++s;
+ return (s);
+}
+
+static const wchar_t *
+pm_slashskip_w(const wchar_t *s) {
+ while ((*s == L'/')
+ || (s[0] == L'.' && s[1] == L'/')
+ || (s[0] == L'.' && s[1] == L'\0'))
+ ++s;
+ return (s);
+}
+
+static int
+pm(const char *p, const char *s, int flags)
+{
+ const char *end;
+
+ /*
+ * Ignore leading './', './/', '././', etc.
+ */
+ if (s[0] == '.' && s[1] == '/')
+ s = pm_slashskip(s + 1);
+ if (p[0] == '.' && p[1] == '/')
+ p = pm_slashskip(p + 1);
+
+ for (;;) {
+ switch (*p) {
+ case '\0':
+ if (s[0] == '/') {
+ if (flags & PATHMATCH_NO_ANCHOR_END)
+ return (1);
+ /* "dir" == "dir/" == "dir/." */
+ s = pm_slashskip(s);
+ }
+ return (*s == '\0');
+ case '?':
+ /* ? always succeeds, unless we hit end of 's' */
+ if (*s == '\0')
+ return (0);
+ break;
+ case '*':
+ /* "*" == "**" == "***" ... */
+ while (*p == '*')
+ ++p;
+ /* Trailing '*' always succeeds. */
+ if (*p == '\0')
+ return (1);
+ while (*s) {
+ if (archive_pathmatch(p, s, flags))
+ return (1);
+ ++s;
+ }
+ return (0);
+ case '[':
+ /*
+ * Find the end of the [...] character class,
+ * ignoring \] that might occur within the class.
+ */
+ end = p + 1;
+ while (*end != '\0' && *end != ']') {
+ if (*end == '\\' && end[1] != '\0')
+ ++end;
+ ++end;
+ }
+ if (*end == ']') {
+ /* We found [...], try to match it. */
+ if (!pm_list(p + 1, end, *s, flags))
+ return (0);
+ p = end; /* Jump to trailing ']' char. */
+ break;
+ } else
+ /* No final ']', so just match '['. */
+ if (*p != *s)
+ return (0);
+ break;
+ case '\\':
+ /* Trailing '\\' matches itself. */
+ if (p[1] == '\0') {
+ if (*s != '\\')
+ return (0);
+ } else {
+ ++p;
+ if (*p != *s)
+ return (0);
+ }
+ break;
+ case '/':
+ if (*s != '/' && *s != '\0')
+ return (0);
+ /* Note: pattern "/\./" won't match "/";
+ * pm_slashskip() correctly stops at backslash. */
+ p = pm_slashskip(p);
+ s = pm_slashskip(s);
+ if (*p == '\0' && (flags & PATHMATCH_NO_ANCHOR_END))
+ return (1);
+ --p; /* Counteract the increment below. */
+ --s;
+ break;
+ case '$':
+ /* '$' is special only at end of pattern and only
+ * if PATHMATCH_NO_ANCHOR_END is specified. */
+ if (p[1] == '\0' && (flags & PATHMATCH_NO_ANCHOR_END)){
+ /* "dir" == "dir/" == "dir/." */
+ return (*pm_slashskip(s) == '\0');
+ }
+ /* Otherwise, '$' is not special. */
+ /* FALL THROUGH */
+ default:
+ if (*p != *s)
+ return (0);
+ break;
+ }
+ ++p;
+ ++s;
+ }
+}
+
+static int
+pm_w(const wchar_t *p, const wchar_t *s, int flags)
+{
+ const wchar_t *end;
+
+ /*
+ * Ignore leading './', './/', '././', etc.
+ */
+ if (s[0] == L'.' && s[1] == L'/')
+ s = pm_slashskip_w(s + 1);
+ if (p[0] == L'.' && p[1] == L'/')
+ p = pm_slashskip_w(p + 1);
+
+ for (;;) {
+ switch (*p) {
+ case L'\0':
+ if (s[0] == L'/') {
+ if (flags & PATHMATCH_NO_ANCHOR_END)
+ return (1);
+ /* "dir" == "dir/" == "dir/." */
+ s = pm_slashskip_w(s);
+ }
+ return (*s == L'\0');
+ case L'?':
+ /* ? always succeeds, unless we hit end of 's' */
+ if (*s == L'\0')
+ return (0);
+ break;
+ case L'*':
+ /* "*" == "**" == "***" ... */
+ while (*p == L'*')
+ ++p;
+ /* Trailing '*' always succeeds. */
+ if (*p == L'\0')
+ return (1);
+ while (*s) {
+ if (archive_pathmatch_w(p, s, flags))
+ return (1);
+ ++s;
+ }
+ return (0);
+ case L'[':
+ /*
+ * Find the end of the [...] character class,
+ * ignoring \] that might occur within the class.
+ */
+ end = p + 1;
+ while (*end != L'\0' && *end != L']') {
+ if (*end == L'\\' && end[1] != L'\0')
+ ++end;
+ ++end;
+ }
+ if (*end == L']') {
+ /* We found [...], try to match it. */
+ if (!pm_list_w(p + 1, end, *s, flags))
+ return (0);
+ p = end; /* Jump to trailing ']' char. */
+ break;
+ } else
+ /* No final ']', so just match '['. */
+ if (*p != *s)
+ return (0);
+ break;
+ case L'\\':
+ /* Trailing '\\' matches itself. */
+ if (p[1] == L'\0') {
+ if (*s != L'\\')
+ return (0);
+ } else {
+ ++p;
+ if (*p != *s)
+ return (0);
+ }
+ break;
+ case L'/':
+ if (*s != L'/' && *s != L'\0')
+ return (0);
+ /* Note: pattern "/\./" won't match "/";
+ * pm_slashskip() correctly stops at backslash. */
+ p = pm_slashskip_w(p);
+ s = pm_slashskip_w(s);
+ if (*p == L'\0' && (flags & PATHMATCH_NO_ANCHOR_END))
+ return (1);
+ --p; /* Counteract the increment below. */
+ --s;
+ break;
+ case L'$':
+ /* '$' is special only at end of pattern and only
+ * if PATHMATCH_NO_ANCHOR_END is specified. */
+ if (p[1] == L'\0' && (flags & PATHMATCH_NO_ANCHOR_END)){
+ /* "dir" == "dir/" == "dir/." */
+ return (*pm_slashskip_w(s) == L'\0');
+ }
+ /* Otherwise, '$' is not special. */
+ /* FALL THROUGH */
+ default:
+ if (*p != *s)
+ return (0);
+ break;
+ }
+ ++p;
+ ++s;
+ }
+}
+
+/* Main entry point. */
+int
+__archive_pathmatch(const char *p, const char *s, int flags)
+{
+ /* Empty pattern only matches the empty string. */
+ if (p == NULL || *p == '\0')
+ return (s == NULL || *s == '\0');
+
+ /* Leading '^' anchors the start of the pattern. */
+ if (*p == '^') {
+ ++p;
+ flags &= ~PATHMATCH_NO_ANCHOR_START;
+ }
+
+ if (*p == '/' && *s != '/')
+ return (0);
+
+ /* Certain patterns and file names anchor implicitly. */
+ if (*p == '*' || *p == '/' || *p == '/') {
+ while (*p == '/')
+ ++p;
+ while (*s == '/')
+ ++s;
+ return (pm(p, s, flags));
+ }
+
+ /* If start is unanchored, try to match start of each path element. */
+ if (flags & PATHMATCH_NO_ANCHOR_START) {
+ for ( ; s != NULL; s = strchr(s, '/')) {
+ if (*s == '/')
+ s++;
+ if (pm(p, s, flags))
+ return (1);
+ }
+ return (0);
+ }
+
+ /* Default: Match from beginning. */
+ return (pm(p, s, flags));
+}
+
+int
+__archive_pathmatch_w(const wchar_t *p, const wchar_t *s, int flags)
+{
+ /* Empty pattern only matches the empty string. */
+ if (p == NULL || *p == L'\0')
+ return (s == NULL || *s == L'\0');
+
+ /* Leading '^' anchors the start of the pattern. */
+ if (*p == L'^') {
+ ++p;
+ flags &= ~PATHMATCH_NO_ANCHOR_START;
+ }
+
+ if (*p == L'/' && *s != L'/')
+ return (0);
+
+ /* Certain patterns and file names anchor implicitly. */
+ if (*p == L'*' || *p == L'/' || *p == L'/') {
+ while (*p == L'/')
+ ++p;
+ while (*s == L'/')
+ ++s;
+ return (pm_w(p, s, flags));
+ }
+
+ /* If start is unanchored, try to match start of each path element. */
+ if (flags & PATHMATCH_NO_ANCHOR_START) {
+ for ( ; s != NULL; s = wcschr(s, L'/')) {
+ if (*s == L'/')
+ s++;
+ if (pm_w(p, s, flags))
+ return (1);
+ }
+ return (0);
+ }
+
+ /* Default: Match from beginning. */
+ return (pm_w(p, s, flags));
+}
--- /dev/null
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endi
+
+#ifndef ARCHIVE_PATHMATCH_H
+#define ARCHIVE_PATHMATCH_H
+
+/* Don't anchor at beginning unless the pattern starts with "^" */
+#define PATHMATCH_NO_ANCHOR_START 1
+/* Don't anchor at end unless the pattern ends with "$" */
+#define PATHMATCH_NO_ANCHOR_END 2
+
+/* Note that "^" and "$" are not special unless you set the corresponding
+ * flag above. */
+
+int __archive_pathmatch(const char *p, const char *s, int flags);
+int __archive_pathmatch_w(const wchar_t *p, const wchar_t *s, int flags);
+
+#define archive_pathmatch(p, s, f) __archive_pathmatch(p, s, f)
+#define archive_pathmatch_w(p, s, f) __archive_pathmatch_w(p, s, f)
+
+#endif
#define ARCHIVE_READ_MAGIC (0xdeb0c5U)
#define ARCHIVE_WRITE_DISK_MAGIC (0xc001b0c5U)
#define ARCHIVE_READ_DISK_MAGIC (0xbadb0c5U)
+#define ARCHIVE_MATCHING_MAGIC (0xcad11c9U)
#define ARCHIVE_STATE_NEW 1U
#define ARCHIVE_STATE_HEADER 2U
test_archive_api_feature.c
test_archive_clear_error.c
test_archive_crypto.c
+ test_archive_matching_owner.c
+ test_archive_matching_path.c
+ test_archive_matching_time.c
test_archive_read_close_twice.c
test_archive_read_close_twice_open_fd.c
test_archive_read_close_twice_open_filename.c
--- /dev/null
+/*-
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "test.h"
+__FBSDID("$FreeBSD$");
+
+static void
+test_uid(void)
+{
+ struct archive_entry *ae;
+ struct archive *m;
+
+ if (!assert((m = archive_matching_new()) != NULL))
+ return;
+ if (!assert((ae = archive_entry_new()) != NULL)) {
+ archive_matching_free(m);
+ return;
+ }
+
+ assertEqualIntA(m, 0, archive_matching_include_uid(m, 1000));
+ assertEqualIntA(m, 0, archive_matching_include_uid(m, 1002));
+
+ archive_entry_set_uid(ae, 0);
+ failure("uid 0 should be excluded");
+ assertEqualInt(1, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+ archive_entry_set_uid(ae, 1000);
+ failure("uid 1000 should not be excluded");
+ assertEqualInt(0, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+ archive_entry_set_uid(ae, 1001);
+ failure("uid 1001 should be excluded");
+ assertEqualInt(1, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+ archive_entry_set_uid(ae, 1002);
+ failure("uid 1002 should not be excluded");
+ assertEqualInt(0, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+ archive_entry_set_uid(ae, 1003);
+ failure("uid 1003 should be excluded");
+ assertEqualInt(1, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Clean up. */
+ archive_entry_free(ae);
+ archive_matching_free(m);
+}
+
+static void
+test_gid(void)
+{
+ struct archive_entry *ae;
+ struct archive *m;
+
+ if (!assert((m = archive_matching_new()) != NULL))
+ return;
+ if (!assert((ae = archive_entry_new()) != NULL)) {
+ archive_matching_free(m);
+ return;
+ }
+
+ assertEqualIntA(m, 0, archive_matching_include_gid(m, 1000));
+ assertEqualIntA(m, 0, archive_matching_include_gid(m, 1002));
+
+ archive_entry_set_gid(ae, 0);
+ failure("uid 0 should be excluded");
+ assertEqualInt(1, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+ archive_entry_set_gid(ae, 1000);
+ failure("uid 1000 should not be excluded");
+ assertEqualInt(0, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+ archive_entry_set_gid(ae, 1001);
+ failure("uid 1001 should be excluded");
+ assertEqualInt(1, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+ archive_entry_set_gid(ae, 1002);
+ failure("uid 1002 should not be excluded");
+ assertEqualInt(0, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+ archive_entry_set_gid(ae, 1003);
+ failure("uid 1003 should be excluded");
+ assertEqualInt(1, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Clean up. */
+ archive_entry_free(ae);
+ archive_matching_free(m);
+}
+
+static void
+test_uname_mbs(void)
+{
+ struct archive_entry *ae;
+ struct archive *m;
+
+ if (!assert((m = archive_matching_new()) != NULL))
+ return;
+ if (!assert((ae = archive_entry_new()) != NULL)) {
+ archive_matching_free(m);
+ return;
+ }
+
+ assertEqualIntA(m, 0, archive_matching_include_uname(m, "foo"));
+ assertEqualIntA(m, 0, archive_matching_include_uname(m, "bar"));
+
+ archive_entry_copy_uname(ae, "unknown");
+ failure("User 'unknown' should be excluded");
+ assertEqualInt(1, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+ archive_entry_copy_uname(ae, "foo");
+ failure("User 'foo' should not be excluded");
+ assertEqualInt(0, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+ archive_entry_copy_uname(ae, "foo1");
+ failure("User 'foo1' should be excluded");
+ assertEqualInt(1, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+ archive_entry_copy_uname(ae, "bar");
+ failure("User 'bar' should not be excluded");
+ assertEqualInt(0, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+ archive_entry_copy_uname(ae, "bar1");
+ failure("User 'bar1' should be excluded");
+ assertEqualInt(1, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Clean up. */
+ archive_entry_free(ae);
+ archive_matching_free(m);
+}
+
+static void
+test_uname_wcs(void)
+{
+ struct archive_entry *ae;
+ struct archive *m;
+
+ if (!assert((m = archive_matching_new()) != NULL))
+ return;
+ if (!assert((ae = archive_entry_new()) != NULL)) {
+ archive_matching_free(m);
+ return;
+ }
+
+ assertEqualIntA(m, 0, archive_matching_include_uname_w(m, L"foo"));
+ assertEqualIntA(m, 0, archive_matching_include_uname_w(m, L"bar"));
+
+ archive_entry_copy_uname_w(ae, L"unknown");
+ failure("User 'unknown' should be excluded");
+ assertEqualInt(1, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+ archive_entry_copy_uname_w(ae, L"foo");
+ failure("User 'foo' should not be excluded");
+ assertEqualInt(0, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+ archive_entry_copy_uname_w(ae, L"foo1");
+ failure("User 'foo1' should be excluded");
+ assertEqualInt(1, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+ archive_entry_copy_uname_w(ae, L"bar");
+ failure("User 'bar' should not be excluded");
+ assertEqualInt(0, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+ archive_entry_copy_uname_w(ae, L"bar1");
+ failure("User 'bar1' should be excluded");
+ assertEqualInt(1, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Clean up. */
+ archive_entry_free(ae);
+ archive_matching_free(m);
+}
+
+static void
+test_gname_mbs(void)
+{
+ struct archive_entry *ae;
+ struct archive *m;
+
+ if (!assert((m = archive_matching_new()) != NULL))
+ return;
+ if (!assert((ae = archive_entry_new()) != NULL)) {
+ archive_matching_free(m);
+ return;
+ }
+
+ assertEqualIntA(m, 0, archive_matching_include_gname(m, "foo"));
+ assertEqualIntA(m, 0, archive_matching_include_gname(m, "bar"));
+
+ archive_entry_copy_gname(ae, "unknown");
+ failure("Group 'unknown' should be excluded");
+ assertEqualInt(1, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+ archive_entry_copy_gname(ae, "foo");
+ failure("Group 'foo' should not be excluded");
+ assertEqualInt(0, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+ archive_entry_copy_gname(ae, "foo1");
+ failure("Group 'foo1' should be excluded");
+ assertEqualInt(1, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+ archive_entry_copy_gname(ae, "bar");
+ failure("Group 'bar' should not be excluded");
+ assertEqualInt(0, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+ archive_entry_copy_gname(ae, "bar1");
+ failure("Group 'bar1' should be excluded");
+ assertEqualInt(1, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Clean up. */
+ archive_entry_free(ae);
+ archive_matching_free(m);
+}
+
+static void
+test_gname_wcs(void)
+{
+ struct archive_entry *ae;
+ struct archive *m;
+
+ if (!assert((m = archive_matching_new()) != NULL))
+ return;
+ if (!assert((ae = archive_entry_new()) != NULL)) {
+ archive_matching_free(m);
+ return;
+ }
+
+ assertEqualIntA(m, 0, archive_matching_include_gname_w(m, L"foo"));
+ assertEqualIntA(m, 0, archive_matching_include_gname_w(m, L"bar"));
+
+ archive_entry_copy_gname_w(ae, L"unknown");
+ failure("Group 'unknown' should be excluded");
+ assertEqualInt(1, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+ archive_entry_copy_gname_w(ae, L"foo");
+ failure("Group 'foo' should not be excluded");
+ assertEqualInt(0, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+ archive_entry_copy_gname_w(ae, L"foo1");
+ failure("Group 'foo1' should be excluded");
+ assertEqualInt(1, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+ archive_entry_copy_gname_w(ae, L"bar");
+ failure("Group 'bar' should not be excluded");
+ assertEqualInt(0, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+ archive_entry_copy_gname_w(ae, L"bar1");
+ failure("Group 'bar1' should be excluded");
+ assertEqualInt(1, archive_matching_owner_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Clean up. */
+ archive_entry_free(ae);
+ archive_matching_free(m);
+}
+
+DEFINE_TEST(test_archive_matching_owner)
+{
+ test_uid();
+ test_gid();
+ test_uname_mbs();
+ test_uname_wcs();
+ test_gname_mbs();
+ test_gname_wcs();
+}
--- /dev/null
+/*-
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "test.h"
+__FBSDID("$FreeBSD$");
+
+static void
+test_exclusion_mbs(void)
+{
+ struct archive_entry *ae;
+ struct archive *m;
+
+ if (!assert((m = archive_matching_new()) != NULL))
+ return;
+ if (!assert((ae = archive_entry_new()) != NULL)) {
+ archive_matching_free(m);
+ return;
+ }
+
+ /* Test for pattern "^aa*" */
+ assertEqualIntA(m, 0, archive_matching_exclude_pattern(m, "^aa*"));
+
+ /* Test with 'aa1234', which should be excluded. */
+ failure("'aa1234' should be excluded");
+ assertEqualInt(1, archive_matching_path_excluded(m, "aa1234"));
+ failure("'aa1234' should be excluded");
+ assertEqualInt(1, archive_matching_path_excluded_w(m, L"aa1234"));
+ archive_entry_copy_pathname(ae, "aa1234");
+ failure("'aa1234' should be excluded");
+ assertEqualInt(1, archive_matching_path_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Test with 'a1234', which should not be excluded. */
+ failure("'a1234' should not be excluded");
+ assertEqualInt(0, archive_matching_path_excluded(m, "a1234"));
+ failure("'a1234' should not be excluded");
+ assertEqualInt(0, archive_matching_path_excluded_w(m, L"a1234"));
+ archive_entry_copy_pathname(ae, "a1234");
+ failure("'a1234' should not be excluded");
+ assertEqualInt(0, archive_matching_path_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+
+ /* Clean up. */
+ archive_entry_free(ae);
+ archive_matching_free(m);
+}
+
+static void
+test_exclusion_wcs(void)
+{
+ struct archive_entry *ae;
+ struct archive *m;
+
+ if (!assert((m = archive_matching_new()) != NULL))
+ return;
+ if (!assert((ae = archive_entry_new()) != NULL)) {
+ archive_matching_free(m);
+ return;
+ }
+
+ /* Test for pattern "^aa*" */
+ assertEqualIntA(m, 0, archive_matching_exclude_pattern_w(m, L"^aa*"));
+
+ /* Test with 'aa1234', which should be excluded. */
+ failure("'aa1234' should be excluded");
+ assertEqualInt(1, archive_matching_path_excluded(m, "aa1234"));
+ failure("'aa1234' should be excluded");
+ assertEqualInt(1, archive_matching_path_excluded_w(m, L"aa1234"));
+ archive_entry_copy_pathname(ae, "aa1234");
+ failure("'aa1234' should be excluded");
+ assertEqualInt(1, archive_matching_path_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Test with 'a1234', which should not be excluded. */
+ failure("'a1234' should not be excluded");
+ assertEqualInt(0, archive_matching_path_excluded(m, "a1234"));
+ failure("'a1234' should not be excluded");
+ assertEqualInt(0, archive_matching_path_excluded_w(m, L"a1234"));
+ archive_entry_copy_pathname(ae, "a1234");
+ failure("'a1234' should not be excluded");
+ assertEqualInt(0, archive_matching_path_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+
+ /* Clean up. */
+ archive_entry_free(ae);
+ archive_matching_free(m);
+}
+
+static void
+test_inclusion_mbs(void)
+{
+ struct archive_entry *ae;
+ struct archive *m;
+ const char *mp;
+
+ if (!assert((m = archive_matching_new()) != NULL))
+ return;
+ if (!assert((ae = archive_entry_new()) != NULL)) {
+ archive_matching_free(m);
+ return;
+ }
+
+ /* Test for pattern "^aa*" */
+ assertEqualIntA(m, 0, archive_matching_include_pattern(m, "^aa*"));
+
+ /* Test with 'aa1234', which should not be excluded. */
+ failure("'aa1234' should not be excluded");
+ assertEqualInt(0, archive_matching_path_excluded(m, "aa1234"));
+ failure("'aa1234' should not be excluded");
+ assertEqualInt(0, archive_matching_path_excluded_w(m, L"aa1234"));
+ archive_entry_copy_pathname(ae, "aa1234");
+ failure("'aa1234' should not be excluded");
+ assertEqualInt(0, archive_matching_path_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+
+ /* Test with 'a1234', which should be excluded. */
+ failure("'a1234' should be excluded");
+ assertEqualInt(1, archive_matching_path_excluded(m, "a1234"));
+ failure("'a1234' should be excluded");
+ assertEqualInt(1, archive_matching_path_excluded_w(m, L"a1234"));
+ archive_entry_copy_pathname(ae, "a1234");
+ failure("'a1234' should be excluded");
+ assertEqualInt(1, archive_matching_path_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Verify unmatched_inclusions. */
+ assertEqualInt(0, archive_matching_path_unmatched_inclusions(m));
+ assertEqualIntA(m, ARCHIVE_EOF,
+ archive_matching_path_unmatched_inclusions_next(m, &mp));
+
+ /* Clean up. */
+ archive_entry_free(ae);
+ archive_matching_free(m);
+}
+
+static void
+test_inclusion_wcs(void)
+{
+ struct archive_entry *ae;
+ struct archive *m;
+ const char *mp;
+
+ if (!assert((m = archive_matching_new()) != NULL))
+ return;
+ if (!assert((ae = archive_entry_new()) != NULL)) {
+ archive_matching_free(m);
+ return;
+ }
+
+ /* Test for pattern "^aa*" */
+ assertEqualIntA(m, 0, archive_matching_include_pattern_w(m, L"^aa*"));
+
+ /* Test with 'aa1234', which should not be excluded. */
+ failure("'aa1234' should not be excluded");
+ assertEqualInt(0, archive_matching_path_excluded(m, "aa1234"));
+ failure("'aa1234' should not be excluded");
+ assertEqualInt(0, archive_matching_path_excluded_w(m, L"aa1234"));
+ archive_entry_copy_pathname(ae, "aa1234");
+ failure("'aa1234' should not be excluded");
+ assertEqualInt(0, archive_matching_path_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+
+ /* Test with 'a1234', which should be excluded. */
+ failure("'a1234' should be excluded");
+ assertEqualInt(1, archive_matching_path_excluded(m, "a1234"));
+ failure("'a1234' should be excluded");
+ assertEqualInt(1, archive_matching_path_excluded_w(m, L"a1234"));
+ archive_entry_copy_pathname(ae, "a1234");
+ failure("'a1234' should be excluded");
+ assertEqualInt(1, archive_matching_path_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Verify unmatched_inclusions. */
+ assertEqualInt(0, archive_matching_path_unmatched_inclusions(m));
+ assertEqualIntA(m, ARCHIVE_EOF,
+ archive_matching_path_unmatched_inclusions_next(m, &mp));
+
+ /* Clean up. */
+ archive_entry_free(ae);
+ archive_matching_free(m);
+}
+
+static void
+test_exclusion_and_inclusion(void)
+{
+ struct archive_entry *ae;
+ struct archive *m;
+ const char *mp;
+ const wchar_t *wp;
+
+ if (!assert((m = archive_matching_new()) != NULL))
+ return;
+ if (!assert((ae = archive_entry_new()) != NULL)) {
+ archive_matching_free(m);
+ return;
+ }
+
+ assertEqualIntA(m, 0, archive_matching_exclude_pattern(m, "^aaa*"));
+ assertEqualIntA(m, 0, archive_matching_include_pattern_w(m, L"^aa*"));
+ assertEqualIntA(m, 0, archive_matching_include_pattern(m, "^a1*"));
+
+ /* Test with 'aa1234', which should not be excluded. */
+ failure("'aa1234' should not be excluded");
+ assertEqualInt(0, archive_matching_path_excluded(m, "aa1234"));
+ failure("'aa1234' should not be excluded");
+ assertEqualInt(0, archive_matching_path_excluded_w(m, L"aa1234"));
+ archive_entry_copy_pathname(ae, "aa1234");
+ failure("'aa1234' should not be excluded");
+ assertEqualInt(0, archive_matching_path_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+
+ /* Test with 'aaa1234', which should be excluded. */
+ failure("'aaa1234' should be excluded");
+ assertEqualInt(1, archive_matching_path_excluded(m, "aaa1234"));
+ failure("'aaa1234' should be excluded");
+ assertEqualInt(1, archive_matching_path_excluded_w(m, L"aaa1234"));
+ archive_entry_copy_pathname(ae, "aaa1234");
+ failure("'aaa1234' should be excluded");
+ assertEqualInt(1, archive_matching_path_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Verify unmatched_inclusions. */
+ assertEqualInt(1, archive_matching_path_unmatched_inclusions(m));
+ /* Verify unmatched inclusion patterns. */
+ assertEqualIntA(m, ARCHIVE_OK,
+ archive_matching_path_unmatched_inclusions_next(m, &mp));
+ assertEqualString("^a1*", mp);
+ assertEqualIntA(m, ARCHIVE_EOF,
+ archive_matching_path_unmatched_inclusions_next(m, &mp));
+ /* Verify unmatched inclusion patterns again in Wide-Char. */
+ assertEqualIntA(m, ARCHIVE_OK,
+ archive_matching_path_unmatched_inclusions_next_w(m, &wp));
+ assertEqualWString(L"^a1*", wp);
+ assertEqualIntA(m, ARCHIVE_EOF,
+ archive_matching_path_unmatched_inclusions_next_w(m, &wp));
+
+ /* Clean up. */
+ archive_entry_free(ae);
+ archive_matching_free(m);
+}
+
+DEFINE_TEST(test_archive_matching_path)
+{
+ test_exclusion_mbs();
+ test_exclusion_wcs();
+ test_inclusion_mbs();
+ test_inclusion_wcs();
+ test_exclusion_and_inclusion();
+}
--- /dev/null
+/*-
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "test.h"
+__FBSDID("$FreeBSD$");
+
+static void
+test_newer_time(void)
+{
+ struct archive_entry *ae;
+ struct archive *m;
+
+ if (!assert((m = archive_matching_new()) != NULL))
+ return;
+ if (!assert((ae = archive_entry_new()) != NULL)) {
+ archive_matching_free(m);
+ return;
+ }
+
+ assertEqualIntA(m, 0, archive_matching_newer_mtime(m, 7880, 0));
+ assertEqualIntA(m, 0, archive_matching_newer_ctime(m, 7880, 0));
+
+ archive_entry_copy_pathname(ae, "file1");
+ archive_entry_set_mtime(ae, 7880, 0);
+ archive_entry_set_ctime(ae, 7880, 0);
+ failure("Both Its mtime and ctime should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+ archive_entry_set_mtime(ae, 7879, 999);
+ archive_entry_set_ctime(ae, 7879, 999);
+ failure("Both Its mtime and ctime should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ archive_entry_set_mtime(ae, 7881, 0);
+ archive_entry_set_ctime(ae, 7881, 0);
+ failure("Both Its mtime and ctime should not be excluded");
+ assertEqualInt(0, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+
+ archive_entry_set_mtime(ae, 7880, 1);
+ archive_entry_set_ctime(ae, 7880, 0);
+ failure("Its mtime should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ archive_entry_set_mtime(ae, 7880, 0);
+ archive_entry_set_ctime(ae, 7880, 1);
+ failure("Its ctime should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Clean up. */
+ archive_entry_free(ae);
+ archive_matching_free(m);
+}
+
+static void
+test_newer_than_file_mbs(void)
+{
+ struct archive *a;
+ struct archive_entry *ae;
+ struct archive *m;
+
+ if (!assert((m = archive_matching_new()) != NULL))
+ return;
+ if (!assert((ae = archive_entry_new()) != NULL)) {
+ archive_matching_free(m);
+ return;
+ }
+ if (!assert((a = archive_read_disk_new()) != NULL)) {
+ archive_matching_free(m);
+ archive_entry_free(ae);
+ return;
+ }
+
+ assertMakeDir("test_newer_than_file_mbs", 0777);
+ assertChdir("test_newer_than_file_mbs");
+
+ assertMakeFile("old", 0666, "old");
+ sleepUntilAfter(time(NULL));
+ assertMakeFile("mid", 0666, "mid");
+ sleepUntilAfter(time(NULL));
+ assertMakeFile("new", 0666, "new");
+
+ /*
+ * Test 'newer mtime than'.
+ */
+ assertEqualIntA(m, 0, archive_matching_newer_mtime_than(m, "mid"));
+
+ /* Verify 'old' file. */
+ archive_entry_copy_pathname(ae, "old");
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_entry_from_file(a, ae, -1, NULL));
+ failure("It should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Verify 'mid' file. */
+ archive_entry_clear(ae);
+ archive_entry_copy_pathname(ae, "mid");
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_entry_from_file(a, ae, -1, NULL));
+ failure("It should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Verify 'new' file. */
+ archive_entry_clear(ae);
+ archive_entry_copy_pathname(ae, "new");
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_entry_from_file(a, ae, -1, NULL));
+ failure("It should not be excluded");
+ assertEqualInt(0, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+
+ /*
+ * Test 'newer ctime than'.
+ */
+ archive_matching_free(m);
+ if (!assert((m = archive_matching_new()) != NULL)) {
+ assertChdir("..");
+ archive_entry_free(ae);
+ archive_read_free(a);
+ return;
+ }
+ assertEqualIntA(m, 0, archive_matching_newer_ctime_than(m, "mid"));
+
+ /* Verify 'old' file. */
+ archive_entry_clear(ae);
+ archive_entry_copy_pathname(ae, "old");
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_entry_from_file(a, ae, -1, NULL));
+ failure("It should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Verify 'mid' file. */
+ archive_entry_clear(ae);
+ archive_entry_copy_pathname(ae, "mid");
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_entry_from_file(a, ae, -1, NULL));
+ failure("It should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Verify 'new' file. */
+ archive_entry_clear(ae);
+ archive_entry_copy_pathname(ae, "new");
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_entry_from_file(a, ae, -1, NULL));
+ failure("It should not be excluded");
+ assertEqualInt(0, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+
+ assertChdir("..");
+ /* Clean up. */
+ archive_read_free(a);
+ archive_entry_free(ae);
+ archive_matching_free(m);
+}
+
+static void
+test_newer_than_file_wcs(void)
+{
+ struct archive *a;
+ struct archive_entry *ae;
+ struct archive *m;
+
+ if (!assert((m = archive_matching_new()) != NULL))
+ return;
+ if (!assert((ae = archive_entry_new()) != NULL)) {
+ archive_matching_free(m);
+ return;
+ }
+ if (!assert((a = archive_read_disk_new()) != NULL)) {
+ archive_matching_free(m);
+ archive_entry_free(ae);
+ return;
+ }
+
+ assertMakeDir("test_newer_than_file_wcs", 0777);
+ assertChdir("test_newer_than_file_wcs");
+
+ assertMakeFile("old", 0666, "old");
+ sleepUntilAfter(time(NULL));
+ assertMakeFile("mid", 0666, "mid");
+ sleepUntilAfter(time(NULL));
+ assertMakeFile("new", 0666, "new");
+
+ /*
+ * Test 'newer mtime than'.
+ */
+ assertEqualIntA(m, 0, archive_matching_newer_mtime_than_w(m, L"mid"));
+
+ /* Verify 'old' file. */
+ archive_entry_copy_pathname(ae, "old");
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_entry_from_file(a, ae, -1, NULL));
+ failure("It should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Verify 'mid' file. */
+ archive_entry_clear(ae);
+ archive_entry_copy_pathname(ae, "mid");
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_entry_from_file(a, ae, -1, NULL));
+ failure("It should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Verify 'new' file. */
+ archive_entry_clear(ae);
+ archive_entry_copy_pathname(ae, "new");
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_entry_from_file(a, ae, -1, NULL));
+ failure("It should not be excluded");
+ assertEqualInt(0, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+
+ /*
+ * Test 'newer ctime than'.
+ */
+ archive_matching_free(m);
+ if (!assert((m = archive_matching_new()) != NULL)) {
+ assertChdir("..");
+ archive_entry_free(ae);
+ archive_read_free(a);
+ return;
+ }
+ assertEqualIntA(m, 0, archive_matching_newer_ctime_than_w(m, L"mid"));
+
+ /* Verify 'old' file. */
+ archive_entry_clear(ae);
+ archive_entry_copy_pathname(ae, "old");
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_entry_from_file(a, ae, -1, NULL));
+ failure("It should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Verify 'mid' file. */
+ archive_entry_clear(ae);
+ archive_entry_copy_pathname(ae, "mid");
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_entry_from_file(a, ae, -1, NULL));
+ failure("It should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Verify 'new' file. */
+ archive_entry_clear(ae);
+ archive_entry_copy_pathname(ae, "new");
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_entry_from_file(a, ae, -1, NULL));
+ failure("It should not be excluded");
+ assertEqualInt(0, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+
+ assertChdir("..");
+ /* Clean up. */
+ archive_read_free(a);
+ archive_entry_free(ae);
+ archive_matching_free(m);
+}
+
+static void
+test_older_time(void)
+{
+ struct archive_entry *ae;
+ struct archive *m;
+
+ if (!assert((m = archive_matching_new()) != NULL))
+ return;
+ if (!assert((ae = archive_entry_new()) != NULL)) {
+ archive_matching_free(m);
+ return;
+ }
+
+ assertEqualIntA(m, 0, archive_matching_older_mtime(m, 7880, 0));
+ assertEqualIntA(m, 0, archive_matching_older_ctime(m, 7880, 0));
+
+ archive_entry_copy_pathname(ae, "file1");
+ archive_entry_set_mtime(ae, 7880, 0);
+ archive_entry_set_ctime(ae, 7880, 0);
+ failure("Both Its mtime and ctime should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+ archive_entry_set_mtime(ae, 7879, 999);
+ archive_entry_set_ctime(ae, 7879, 999);
+ failure("Both Its mtime and ctime should not be excluded");
+ assertEqualInt(0, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+
+ archive_entry_set_mtime(ae, 7881, 0);
+ archive_entry_set_ctime(ae, 7881, 0);
+ failure("Both Its mtime and ctime should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ archive_entry_set_mtime(ae, 7880, 1);
+ archive_entry_set_ctime(ae, 7879, 0);
+ failure("Its mtime should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ archive_entry_set_mtime(ae, 7879, 0);
+ archive_entry_set_ctime(ae, 7880, 1);
+ failure("Its ctime should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Clean up. */
+ archive_entry_free(ae);
+ archive_matching_free(m);
+}
+
+static void
+test_older_than_file_mbs(void)
+{
+ struct archive *a;
+ struct archive_entry *ae;
+ struct archive *m;
+
+ if (!assert((m = archive_matching_new()) != NULL))
+ return;
+ if (!assert((ae = archive_entry_new()) != NULL)) {
+ archive_matching_free(m);
+ return;
+ }
+ if (!assert((a = archive_read_disk_new()) != NULL)) {
+ archive_matching_free(m);
+ archive_entry_free(ae);
+ return;
+ }
+
+ assertMakeDir("test_older_than_file_mbs", 0777);
+ assertChdir("test_older_than_file_mbs");
+
+ assertMakeFile("old", 0666, "old");
+ sleepUntilAfter(time(NULL));
+ assertMakeFile("mid", 0666, "mid");
+ sleepUntilAfter(time(NULL));
+ assertMakeFile("new", 0666, "new");
+
+ /*
+ * Test 'older mtime than'.
+ */
+ assertEqualIntA(m, 0, archive_matching_older_mtime_than(m, "mid"));
+
+ /* Verify 'old' file. */
+ archive_entry_copy_pathname(ae, "old");
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_entry_from_file(a, ae, -1, NULL));
+ failure("It should not be excluded");
+ assertEqualInt(0, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+
+ /* Verify 'mid' file. */
+ archive_entry_clear(ae);
+ archive_entry_copy_pathname(ae, "mid");
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_entry_from_file(a, ae, -1, NULL));
+ failure("It should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Verify 'new' file. */
+ archive_entry_clear(ae);
+ archive_entry_copy_pathname(ae, "new");
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_entry_from_file(a, ae, -1, NULL));
+ failure("It should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /*
+ * Test 'older ctime than'.
+ */
+ archive_matching_free(m);
+ if (!assert((m = archive_matching_new()) != NULL)) {
+ assertChdir("..");
+ archive_entry_free(ae);
+ archive_read_free(a);
+ return;
+ }
+ assertEqualIntA(m, 0, archive_matching_older_ctime_than(m, "mid"));
+
+ /* Verify 'old' file. */
+ archive_entry_clear(ae);
+ archive_entry_copy_pathname(ae, "old");
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_entry_from_file(a, ae, -1, NULL));
+ failure("It should not be excluded");
+ assertEqualInt(0, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+
+ /* Verify 'mid' file. */
+ archive_entry_clear(ae);
+ archive_entry_copy_pathname(ae, "mid");
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_entry_from_file(a, ae, -1, NULL));
+ failure("It should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Verify 'new' file. */
+ archive_entry_clear(ae);
+ archive_entry_copy_pathname(ae, "new");
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_entry_from_file(a, ae, -1, NULL));
+ failure("It should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ assertChdir("..");
+ /* Clean up. */
+ archive_read_free(a);
+ archive_entry_free(ae);
+ archive_matching_free(m);
+}
+
+static void
+test_older_than_file_wcs(void)
+{
+ struct archive *a;
+ struct archive_entry *ae;
+ struct archive *m;
+
+ if (!assert((m = archive_matching_new()) != NULL))
+ return;
+ if (!assert((ae = archive_entry_new()) != NULL)) {
+ archive_matching_free(m);
+ return;
+ }
+ if (!assert((a = archive_read_disk_new()) != NULL)) {
+ archive_matching_free(m);
+ archive_entry_free(ae);
+ return;
+ }
+
+ assertMakeDir("test_older_than_file_wcs", 0777);
+ assertChdir("test_older_than_file_wcs");
+
+ assertMakeFile("old", 0666, "old");
+ sleepUntilAfter(time(NULL));
+ assertMakeFile("mid", 0666, "mid");
+ sleepUntilAfter(time(NULL));
+ assertMakeFile("new", 0666, "new");
+
+ /*
+ * Test 'older mtime than'.
+ */
+ assertEqualIntA(m, 0, archive_matching_older_mtime_than_w(m, L"mid"));
+
+ /* Verify 'old' file. */
+ archive_entry_copy_pathname(ae, "old");
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_entry_from_file(a, ae, -1, NULL));
+ failure("It should not be excluded");
+ assertEqualInt(0, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+
+ /* Verify 'mid' file. */
+ archive_entry_clear(ae);
+ archive_entry_copy_pathname(ae, "mid");
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_entry_from_file(a, ae, -1, NULL));
+ failure("It should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Verify 'new' file. */
+ archive_entry_clear(ae);
+ archive_entry_copy_pathname(ae, "new");
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_entry_from_file(a, ae, -1, NULL));
+ failure("It should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /*
+ * Test 'older ctime than'.
+ */
+ archive_matching_free(m);
+ if (!assert((m = archive_matching_new()) != NULL)) {
+ assertChdir("..");
+ archive_entry_free(ae);
+ archive_read_free(a);
+ return;
+ }
+ assertEqualIntA(m, 0, archive_matching_older_ctime_than_w(m, L"mid"));
+
+ /* Verify 'old' file. */
+ archive_entry_clear(ae);
+ archive_entry_copy_pathname(ae, "old");
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_entry_from_file(a, ae, -1, NULL));
+ failure("It should not be excluded");
+ assertEqualInt(0, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+
+ /* Verify 'mid' file. */
+ archive_entry_clear(ae);
+ archive_entry_copy_pathname(ae, "mid");
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_entry_from_file(a, ae, -1, NULL));
+ failure("It should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ /* Verify 'new' file. */
+ archive_entry_clear(ae);
+ archive_entry_copy_pathname(ae, "new");
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_disk_entry_from_file(a, ae, -1, NULL));
+ failure("It should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ assertChdir("..");
+ /* Clean up. */
+ archive_read_free(a);
+ archive_entry_free(ae);
+ archive_matching_free(m);
+}
+
+static void
+excluded(struct archive *m)
+{
+ struct archive_entry *ae;
+
+ if (!assert((ae = archive_entry_new()) != NULL))
+ return;
+
+ archive_entry_copy_pathname(ae, "file1");
+ archive_entry_set_mtime(ae, 7879, 999);
+ failure("It should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+ archive_entry_set_mtime(ae, 7880, 0);
+ failure("It should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+ archive_entry_set_mtime(ae, 7880, 1);
+ failure("It should not be excluded");
+ assertEqualInt(0, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+
+ archive_entry_copy_pathname(ae, "file2");
+ archive_entry_set_mtime(ae, 7879, 999);
+ failure("It should not be excluded");
+ assertEqualInt(0, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+ archive_entry_set_mtime(ae, 7880, 0);
+ failure("It should not be excluded");
+ assertEqualInt(0, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+ archive_entry_set_mtime(ae, 7880, 1);
+ failure("It should not be excluded");
+ assertEqualInt(0, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+
+ archive_entry_copy_pathname(ae, "file3");
+ archive_entry_set_mtime(ae, 7879, 999);
+ failure("It should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+ archive_entry_set_mtime(ae, 7880, 0);
+ failure("It should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+ archive_entry_set_mtime(ae, 7880, 1);
+ failure("It should be excluded");
+ assertEqualInt(1, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(1, archive_matching_excluded_ae(m, ae));
+
+ archive_entry_copy_pathname(ae, "file4");
+ archive_entry_set_mtime(ae, 7879, 999);
+ failure("It should not be excluded");
+ assertEqualInt(0, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+ archive_entry_set_mtime(ae, 7880, 0);
+ failure("It should not be excluded");
+ assertEqualInt(0, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+ archive_entry_set_mtime(ae, 7880, 1);
+ failure("It should not be excluded");
+ assertEqualInt(0, archive_matching_time_excluded_ae(m, ae));
+ assertEqualInt(0, archive_matching_excluded_ae(m, ae));
+
+
+ /* Clean up. */
+ archive_entry_free(ae);
+}
+
+static void
+test_newer_pathname_mbs(void)
+{
+ struct archive *m;
+
+ if (!assert((m = archive_matching_new()) != NULL))
+ return;
+
+ assertEqualIntA(m, 0,
+ archive_matching_pathname_newer_mtime(m, "file1", 7880, 0));
+ assertEqualIntA(m, 0,
+ archive_matching_pathname_newer_mtime(m, "file2", 1, 0));
+ assertEqualIntA(m, 0,
+ archive_matching_pathname_newer_mtime(m, "file3", 99999, 0));
+
+ excluded(m);
+
+ /* Clean up. */
+ archive_matching_free(m);
+}
+
+static void
+test_newer_pathname_wcs(void)
+{
+ struct archive *m;
+
+ if (!assert((m = archive_matching_new()) != NULL))
+ return;
+
+ assertEqualIntA(m, 0,
+ archive_matching_pathname_newer_mtime_w(m, L"file1", 7880, 0));
+ assertEqualIntA(m, 0,
+ archive_matching_pathname_newer_mtime_w(m, L"file2", 1, 0));
+ assertEqualIntA(m, 0,
+ archive_matching_pathname_newer_mtime_w(m, L"file3", 99999, 0));
+
+ excluded(m);
+
+ /* Clean up. */
+ archive_matching_free(m);
+}
+
+static void
+test_newer_archive_entry(void)
+{
+ struct archive_entry *ae;
+ struct archive *m;
+
+ if (!assert((m = archive_matching_new()) != NULL))
+ return;
+ if (!assert((ae = archive_entry_new()) != NULL)) {
+ archive_matching_free(m);
+ return;
+ }
+
+ archive_entry_copy_pathname(ae, "file1");
+ archive_entry_set_mtime(ae, 7880, 0);
+ assertEqualIntA(m, 0, archive_matching_pathname_newer_mtime_ae(m, ae));
+ archive_entry_copy_pathname(ae, "file2");
+ archive_entry_set_mtime(ae, 1, 0);
+ assertEqualIntA(m, 0, archive_matching_pathname_newer_mtime_ae(m, ae));
+ archive_entry_copy_pathname(ae, "file3");
+ archive_entry_set_mtime(ae, 99999, 0);
+ assertEqualIntA(m, 0, archive_matching_pathname_newer_mtime_ae(m, ae));
+
+ excluded(m);
+
+ /* Clean up. */
+ archive_entry_free(ae);
+ archive_matching_free(m);
+}
+
+DEFINE_TEST(test_archive_matching_time)
+{
+ test_newer_time();
+ test_newer_than_file_mbs();
+ test_newer_than_file_wcs();
+ test_older_time();
+ test_older_than_file_mbs();
+ test_older_than_file_wcs();
+ test_newer_pathname_mbs();
+ test_newer_pathname_wcs();
+ test_newer_archive_entry();
+}