summaryrefslogtreecommitdiffstats
path: root/usr.bin/tar
diff options
context:
space:
mode:
authorkientzle <kientzle@FreeBSD.org>2010-02-07 02:00:26 +0000
committerkientzle <kientzle@FreeBSD.org>2010-02-07 02:00:26 +0000
commita405ab8bd4b54c290a83c59ee78dfd001568834d (patch)
tree9c540137fcbc6850f0fd47cab1694094ae3b6824 /usr.bin/tar
parente3debd74cb13f99f183614845c9e80b401a4939a (diff)
downloadFreeBSD-src-a405ab8bd4b54c290a83c59ee78dfd001568834d.zip
FreeBSD-src-a405ab8bd4b54c290a83c59ee78dfd001568834d.tar.gz
Merge a bunch of refactoring from Joerg Sonnenberger to
isolate common code used by tar and cpio (and useful to other libarchive clients). The functions here are prefixed with "lafe" (libarchive front-end) to indicate their use.
Diffstat (limited to 'usr.bin/tar')
-rw-r--r--usr.bin/tar/Makefile2
-rw-r--r--usr.bin/tar/bsdtar.c9
-rw-r--r--usr.bin/tar/bsdtar.h15
-rw-r--r--usr.bin/tar/config_freebsd.h3
-rw-r--r--usr.bin/tar/line_reader.c171
-rw-r--r--usr.bin/tar/line_reader.h37
-rw-r--r--usr.bin/tar/matching.c357
-rw-r--r--usr.bin/tar/matching.h46
-rw-r--r--usr.bin/tar/pathmatch.c255
-rw-r--r--usr.bin/tar/pathmatch.h42
-rw-r--r--usr.bin/tar/read.c18
-rw-r--r--usr.bin/tar/util.c153
-rw-r--r--usr.bin/tar/write.c49
13 files changed, 724 insertions, 433 deletions
diff --git a/usr.bin/tar/Makefile b/usr.bin/tar/Makefile
index b2eb4c2..29abcc5 100644
--- a/usr.bin/tar/Makefile
+++ b/usr.bin/tar/Makefile
@@ -7,7 +7,9 @@ SRCS= bsdtar.c \
cmdline.c \
err.c \
getdate.c \
+ line_reader.c \
matching.c \
+ pathmatch.c \
read.c \
subst.c \
tree.c \
diff --git a/usr.bin/tar/bsdtar.c b/usr.bin/tar/bsdtar.c
index 0c4640e..c858ba3 100644
--- a/usr.bin/tar/bsdtar.c
+++ b/usr.bin/tar/bsdtar.c
@@ -66,6 +66,7 @@ __FBSDID("$FreeBSD$");
#include "bsdtar.h"
#include "err.h"
+#include "matching.h"
/*
* Per POSIX.1-1988, tar defaults to reading/writing archives to/from
@@ -248,7 +249,7 @@ main(int argc, char **argv)
bsdtar->option_chroot = 1;
break;
case OPTION_EXCLUDE: /* GNU tar */
- if (exclude(bsdtar, bsdtar->optarg))
+ if (lafe_exclude(&bsdtar->matching, bsdtar->optarg))
bsdtar_errc(1, 0,
"Couldn't exclude %s\n", bsdtar->optarg);
break;
@@ -294,7 +295,7 @@ main(int argc, char **argv)
* noone else needs this to filter entries
* when transforming archives.
*/
- if (include(bsdtar, bsdtar->optarg))
+ if (lafe_include(&bsdtar->matching, bsdtar->optarg))
bsdtar_errc(1, 0,
"Failed to add %s to inclusion list",
bsdtar->optarg);
@@ -484,7 +485,7 @@ main(int argc, char **argv)
bsdtar->option_interactive = 1;
break;
case 'X': /* GNU tar */
- if (exclude_from_file(bsdtar, bsdtar->optarg))
+ if (lafe_exclude_from_file(&bsdtar->matching, bsdtar->optarg))
bsdtar_errc(1, 0,
"failed to process exclusions from file %s",
bsdtar->optarg);
@@ -607,7 +608,7 @@ main(int argc, char **argv)
break;
}
- cleanup_exclusions(bsdtar);
+ lafe_cleanup_exclusions(&bsdtar->matching);
#if HAVE_REGEX_H
cleanup_substitution(bsdtar);
#endif
diff --git a/usr.bin/tar/bsdtar.h b/usr.bin/tar/bsdtar.h
index 2e5972b..919156a 100644
--- a/usr.bin/tar/bsdtar.h
+++ b/usr.bin/tar/bsdtar.h
@@ -28,6 +28,8 @@
#include "bsdtar_platform.h"
#include <stdio.h>
+#include "matching.h"
+
#define DEFAULT_BYTES_PER_BLOCK (20*512)
/*
@@ -77,7 +79,6 @@ struct bsdtar {
int fd;
/* Miscellaneous state information */
- struct archive *archive;
int argc;
char **argv;
const char *optarg;
@@ -97,7 +98,7 @@ struct bsdtar {
struct archive_dir *archive_dir; /* for write.c */
struct name_cache *gname_cache; /* for write.c */
char *buff; /* for write.c */
- struct matching *matching; /* for matching.c */
+ struct lafe_matching *matching; /* for matching.c */
struct security *security; /* for read.c */
struct name_cache *uname_cache; /* for write.c */
struct siginfo_data *siginfo; /* for siginfo.c */
@@ -134,18 +135,10 @@ enum {
};
int bsdtar_getopt(struct bsdtar *);
-void cleanup_exclusions(struct bsdtar *);
void do_chdir(struct bsdtar *);
int edit_pathname(struct bsdtar *, struct archive_entry *);
-int exclude(struct bsdtar *, const char *pattern);
-int exclude_from_file(struct bsdtar *, const char *pathname);
-int excluded(struct bsdtar *, const char *pathname);
-int include(struct bsdtar *, const char *pattern);
-int include_from_file(struct bsdtar *, const char *pathname);
int need_report(void);
int pathcmp(const char *a, const char *b);
-int process_lines(struct bsdtar *bsdtar, const char *pathname,
- int (*process)(struct bsdtar *, const char *));
void safe_fprintf(FILE *, const char *fmt, ...);
void set_chdir(struct bsdtar *, const char *newdir);
const char *tar_i64toa(int64_t);
@@ -154,8 +147,6 @@ void tar_mode_r(struct bsdtar *bsdtar);
void tar_mode_t(struct bsdtar *bsdtar);
void tar_mode_u(struct bsdtar *bsdtar);
void tar_mode_x(struct bsdtar *bsdtar);
-int unmatched_inclusions(struct bsdtar *bsdtar);
-int unmatched_inclusions_warn(struct bsdtar *bsdtar, const char *msg);
void usage(void);
int yes(const char *fmt, ...);
diff --git a/usr.bin/tar/config_freebsd.h b/usr.bin/tar/config_freebsd.h
index d3f213c..965fe857 100644
--- a/usr.bin/tar/config_freebsd.h
+++ b/usr.bin/tar/config_freebsd.h
@@ -37,9 +37,6 @@
#undef HAVE_EXT2FS_EXT2_FS_H
#define HAVE_FCHDIR 1
#define HAVE_FCNTL_H 1
-#define HAVE_FNMATCH 1
-#define HAVE_FNMATCH_H 1
-#define HAVE_FNM_LEADING_DIR 1
#define HAVE_GRP_H 1
#define HAVE_LANGINFO_H 1
#define HAVE_LIBARCHIVE 1
diff --git a/usr.bin/tar/line_reader.c b/usr.bin/tar/line_reader.c
new file mode 100644
index 0000000..c9df1b0
--- /dev/null
+++ b/usr.bin/tar/line_reader.c
@@ -0,0 +1,171 @@
+/*-
+ * Copyright (c) 2008 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 "bsdtar_platform.h"
+__FBSDID("$FreeBSD$");
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "err.h"
+#include "line_reader.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__BORLANDC__)
+#define strdup _strdup
+#endif
+
+/*
+ * Read lines from file and do something with each one. If option_null
+ * is set, lines are terminated with zero bytes; otherwise, they're
+ * terminated with newlines.
+ *
+ * This uses a self-sizing buffer to handle arbitrarily-long lines.
+ */
+struct lafe_line_reader {
+ FILE *f;
+ char *buff, *buff_end, *line_start, *line_end, *p;
+ char *pathname;
+ size_t buff_length;
+ int nullSeparator; /* Lines separated by null, not CR/CRLF/etc. */
+ int ret;
+};
+
+struct lafe_line_reader *
+lafe_line_reader(const char *pathname, int nullSeparator)
+{
+ struct lafe_line_reader *lr;
+
+ lr = calloc(1, sizeof(*lr));
+ if (lr == NULL)
+ bsdtar_errc(1, ENOMEM, "Can't open %s", pathname);
+
+ lr->nullSeparator = nullSeparator;
+ lr->pathname = strdup(pathname);
+
+ if (strcmp(pathname, "-") == 0)
+ lr->f = stdin;
+ else
+ lr->f = fopen(pathname, "r");
+ if (lr->f == NULL)
+ bsdtar_errc(1, errno, "Couldn't open %s", pathname);
+ lr->buff_length = 8192;
+ lr->buff = malloc(lr->buff_length);
+ if (lr->buff == NULL)
+ bsdtar_errc(1, ENOMEM, "Can't read %s", pathname);
+ lr->line_start = lr->line_end = lr->buff_end = lr->buff;
+
+ return (lr);
+}
+
+const char *
+lafe_line_reader_next(struct lafe_line_reader *lr)
+{
+ size_t bytes_wanted, bytes_read, new_buff_size;
+ char *line_start, *p;
+
+ for (;;) {
+ /* If there's a line in the buffer, return it immediately. */
+ while (lr->line_end < lr->buff_end) {
+ if (lr->nullSeparator) {
+ if (*lr->line_end == '\0') {
+ line_start = lr->line_start;
+ lr->line_start = lr->line_end + 1;
+ lr->line_end = lr->line_start;
+ return (line_start);
+ }
+ } else if (*lr->line_end == '\x0a' || *lr->line_end == '\x0d') {
+ *lr->line_end = '\0';
+ line_start = lr->line_start;
+ lr->line_start = lr->line_end + 1;
+ lr->line_end = lr->line_start;
+ if (line_start[0] != '\0')
+ return (line_start);
+ }
+ lr->line_end++;
+ }
+
+ /* If we're at end-of-file, process the final data. */
+ if (lr->f == NULL) {
+ /* If there's more text, return one last line. */
+ if (lr->line_end > lr->line_start) {
+ *lr->line_end = '\0';
+ line_start = lr->line_start;
+ lr->line_start = lr->line_end + 1;
+ lr->line_end = lr->line_start;
+ return (line_start);
+ }
+ /* Otherwise, we're done. */
+ return (NULL);
+ }
+
+ /* Buffer only has part of a line. */
+ if (lr->line_start > lr->buff) {
+ /* Move a leftover fractional line to the beginning. */
+ memmove(lr->buff, lr->line_start,
+ lr->buff_end - lr->line_start);
+ lr->buff_end -= lr->line_start - lr->buff;
+ lr->line_end -= lr->line_start - lr->buff;
+ lr->line_start = lr->buff;
+ } else {
+ /* Line is too big; enlarge the buffer. */
+ new_buff_size = lr->buff_length * 2;
+ if (new_buff_size <= lr->buff_length)
+ bsdtar_errc(1, ENOMEM,
+ "Line too long in %s", lr->pathname);
+ lr->buff_length = new_buff_size;
+ p = realloc(lr->buff, new_buff_size);
+ if (p == NULL)
+ bsdtar_errc(1, ENOMEM,
+ "Line too long in %s", lr->pathname);
+ lr->buff_end = p + (lr->buff_end - lr->buff);
+ lr->line_end = p + (lr->line_end - lr->buff);
+ lr->line_start = lr->buff = p;
+ }
+
+ /* Get some more data into the buffer. */
+ bytes_wanted = lr->buff + lr->buff_length - lr->buff_end;
+ bytes_read = fread(lr->buff_end, 1, bytes_wanted, lr->f);
+ lr->buff_end += bytes_read;
+
+ if (ferror(lr->f))
+ bsdtar_errc(1, errno, "Can't read %s", lr->pathname);
+ if (feof(lr->f)) {
+ if (lr->f != stdin)
+ fclose(lr->f);
+ lr->f = NULL;
+ }
+ }
+}
+
+void
+lafe_line_reader_free(struct lafe_line_reader *lr)
+{
+ free(lr->buff);
+ free(lr->pathname);
+ free(lr);
+}
diff --git a/usr.bin/tar/line_reader.h b/usr.bin/tar/line_reader.h
new file mode 100644
index 0000000..e4c3729
--- /dev/null
+++ b/usr.bin/tar/line_reader.h
@@ -0,0 +1,37 @@
+/*-
+ * Copyright (c) 2009 Joerg Sonnenberger
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef LAFE_LINE_READER_H
+#define LAFE_LINE_READER_H
+
+struct lafe_line_reader;
+
+struct lafe_line_reader *lafe_line_reader(const char *, int nullSeparator);
+const char *lafe_line_reader_next(struct lafe_line_reader *);
+void lafe_line_reader_free(struct lafe_line_reader *);
+
+#endif
diff --git a/usr.bin/tar/matching.c b/usr.bin/tar/matching.c
index c5fe094..184d29a 100644
--- a/usr.bin/tar/matching.c
+++ b/usr.bin/tar/matching.c
@@ -36,8 +36,10 @@ __FBSDID("$FreeBSD$");
#include <string.h>
#endif
-#include "bsdtar.h"
#include "err.h"
+#include "line_reader.h"
+#include "matching.h"
+#include "pathmatch.h"
struct match {
struct match *next;
@@ -45,7 +47,7 @@ struct match {
char pattern[1];
};
-struct matching {
+struct lafe_matching {
struct match *exclusions;
int exclusions_count;
struct match *inclusions;
@@ -53,14 +55,10 @@ struct matching {
int inclusions_unmatched_count;
};
-
-static void add_pattern(struct match **list,
- const char *pattern);
-static int bsdtar_fnmatch(const char *p, const char *s);
-static void initialize_matching(struct bsdtar *);
+static void add_pattern(struct match **list, const char *pattern);
+static void initialize_matching(struct lafe_matching **);
static int match_exclusion(struct match *, const char *pathname);
static int match_inclusion(struct match *, const char *pathname);
-static int pathmatch(const char *p, const char *s);
/*
* The matching logic here needs to be re-thought. I started out to
@@ -74,55 +72,74 @@ static int pathmatch(const char *p, const char *s);
*/
int
-exclude(struct bsdtar *bsdtar, const char *pattern)
+lafe_exclude(struct lafe_matching **matching, const char *pattern)
{
- struct matching *matching;
- if (bsdtar->matching == NULL)
- initialize_matching(bsdtar);
- matching = bsdtar->matching;
- add_pattern(&(matching->exclusions), pattern);
- matching->exclusions_count++;
+ if (*matching == NULL)
+ initialize_matching(matching);
+ add_pattern(&((*matching)->exclusions), pattern);
+ (*matching)->exclusions_count++;
return (0);
}
int
-exclude_from_file(struct bsdtar *bsdtar, const char *pathname)
+lafe_exclude_from_file(struct lafe_matching **matching, const char *pathname)
{
- return (process_lines(bsdtar, pathname, &exclude));
+ struct lafe_line_reader *lr;
+ const char *p;
+ int ret = 0;
+
+ lr = lafe_line_reader(pathname, '\n');
+ while ((p = lafe_line_reader_next(lr)) != NULL) {
+ if (lafe_exclude(matching, p) != 0)
+ ret = -1;
+ }
+ lafe_line_reader_free(lr);
+ return (ret);
}
int
-include(struct bsdtar *bsdtar, const char *pattern)
+lafe_include(struct lafe_matching **matching, const char *pattern)
{
- struct matching *matching;
-
- if (bsdtar->matching == NULL)
- initialize_matching(bsdtar);
- matching = bsdtar->matching;
- add_pattern(&(matching->inclusions), pattern);
- matching->inclusions_count++;
- matching->inclusions_unmatched_count++;
+
+ if (*matching == NULL)
+ initialize_matching(matching);
+ add_pattern(&((*matching)->inclusions), pattern);
+ (*matching)->inclusions_count++;
+ (*matching)->inclusions_unmatched_count++;
return (0);
}
int
-include_from_file(struct bsdtar *bsdtar, const char *pathname)
+lafe_include_from_file(struct lafe_matching **matching, const char *pathname,
+ int nullSeparator)
{
- return (process_lines(bsdtar, pathname, &include));
+ struct lafe_line_reader *lr;
+ const char *p;
+ int ret = 0;
+
+ lr = lafe_line_reader(pathname, nullSeparator);
+ while ((p = lafe_line_reader_next(lr)) != NULL) {
+ if (lafe_include(matching, p) != 0)
+ ret = -1;
+ }
+ lafe_line_reader_free(lr);
+ return (ret);
}
static void
add_pattern(struct match **list, const char *pattern)
{
struct match *match;
+ size_t len;
- match = malloc(sizeof(*match) + strlen(pattern) + 1);
+ len = strlen(pattern);
+ match = malloc(sizeof(*match) + len + 1);
if (match == NULL)
bsdtar_errc(1, errno, "Out of memory");
strcpy(match->pattern, pattern);
/* Both "foo/" and "foo" should match "foo/bar". */
- if (match->pattern[strlen(match->pattern)-1] == '/')
+ if (len && match->pattern[len - 1] == '/')
match->pattern[strlen(match->pattern)-1] = '\0';
match->next = *list;
*list = match;
@@ -131,13 +148,11 @@ add_pattern(struct match **list, const char *pattern)
int
-excluded(struct bsdtar *bsdtar, const char *pathname)
+lafe_excluded(struct lafe_matching *matching, const char *pathname)
{
- struct matching *matching;
struct match *match;
struct match *matched;
- matching = bsdtar->matching;
if (matching == NULL)
return (0);
@@ -192,288 +207,74 @@ excluded(struct bsdtar *bsdtar, const char *pathname)
static int
match_exclusion(struct match *match, const char *pathname)
{
- const char *p;
-
- if (*match->pattern == '*' || *match->pattern == '/')
- return (pathmatch(match->pattern, pathname) == 0);
-
- for (p = pathname; p != NULL; p = strchr(p, '/')) {
- if (*p == '/')
- p++;
- if (pathmatch(match->pattern, p) == 0)
- return (1);
- }
- return (0);
+ return (lafe_pathmatch(match->pattern,
+ pathname,
+ PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END));
}
/*
* Again, mimic gtar: inclusions are always anchored (have to match
* the beginning of the path) even though exclusions are not anchored.
*/
-int
+static int
match_inclusion(struct match *match, const char *pathname)
{
- return (pathmatch(match->pattern, pathname) == 0);
+ return (lafe_pathmatch(match->pattern, pathname, PATHMATCH_NO_ANCHOR_END));
}
void
-cleanup_exclusions(struct bsdtar *bsdtar)
+lafe_cleanup_exclusions(struct lafe_matching **matching)
{
struct match *p, *q;
- if (bsdtar->matching) {
- p = bsdtar->matching->inclusions;
- while (p != NULL) {
- q = p;
- p = p->next;
- free(q);
- }
- p = bsdtar->matching->exclusions;
- while (p != NULL) {
- q = p;
- p = p->next;
- free(q);
- }
- free(bsdtar->matching);
+ if (*matching == NULL)
+ return;
+
+ for (p = (*matching)->inclusions; p != NULL; ) {
+ q = p;
+ p = p->next;
+ free(q);
}
+
+ for (p = (*matching)->exclusions; p != NULL; ) {
+ q = p;
+ p = p->next;
+ free(q);
+ }
+
+ free(*matching);
+ *matching = NULL;
}
static void
-initialize_matching(struct bsdtar *bsdtar)
+initialize_matching(struct lafe_matching **matching)
{
- bsdtar->matching = malloc(sizeof(*bsdtar->matching));
- if (bsdtar->matching == NULL)
+ *matching = calloc(sizeof(**matching), 1);
+ if (*matching == NULL)
bsdtar_errc(1, errno, "No memory");
- memset(bsdtar->matching, 0, sizeof(*bsdtar->matching));
}
int
-unmatched_inclusions(struct bsdtar *bsdtar)
+lafe_unmatched_inclusions(struct lafe_matching *matching)
{
- struct matching *matching;
- matching = bsdtar->matching;
if (matching == NULL)
return (0);
return (matching->inclusions_unmatched_count);
}
-
int
-unmatched_inclusions_warn(struct bsdtar *bsdtar, const char *msg)
+lafe_unmatched_inclusions_warn(struct lafe_matching *matching, const char *msg)
{
- struct matching *matching;
struct match *p;
- matching = bsdtar->matching;
if (matching == NULL)
return (0);
- p = matching->inclusions;
- while (p != NULL) {
- if (p->matches == 0) {
- bsdtar->return_value = 1;
- bsdtar_warnc(0, "%s: %s",
- p->pattern, msg);
- }
- p = p->next;
- }
- return (matching->inclusions_unmatched_count);
-}
-
-/*
- * TODO: Extend this so that the following matches work:
- * "foo//bar" == "foo/bar"
- * "foo/./bar" == "foo/bar"
- * "./foo" == "foo"
- *
- * The POSIX fnmatch() function doesn't handle any of these, but
- * all are common situations that arise when paths are generated within
- * large scripts. E.g., the following is quite common:
- * MYPATH=foo/ TARGET=$MYPATH/bar
- * It may be worthwhile to edit such paths at write time as well,
- * especially when such editing may avoid the need for long pathname
- * extensions.
- */
-static int
-pathmatch(const char *pattern, const char *string)
-{
- /*
- * Strip leading "./" or ".//" so that, e.g.,
- * "foo" matches "./foo". In particular, this
- * opens up an optimization for the writer to
- * elide leading "./".
- */
- if (pattern[0] == '.' && pattern[1] == '/') {
- pattern += 2;
- while (pattern[0] == '/')
- ++pattern;
+ for (p = matching->inclusions; p != NULL; p = p->next) {
+ if (p->matches == 0)
+ bsdtar_warnc(0, "%s: %s", p->pattern, msg);
}
- if (string[0] == '.' && string[1] == '/') {
- string += 2;
- while (string[0] == '/')
- ++string;
- }
- return (bsdtar_fnmatch(pattern, string));
-}
-
-#if defined(HAVE_FNMATCH) && defined(HAVE_FNM_LEADING_DIR)
-
-/* Use system fnmatch() if it suits our needs. */
-/* On Linux, _GNU_SOURCE must be defined to get FNM_LEADING_DIR. */
-#define _GNU_SOURCE
-#include <fnmatch.h>
-static int
-bsdtar_fnmatch(const char *pattern, const char *string)
-{
- return (fnmatch(pattern, string, FNM_LEADING_DIR));
-}
-
-#else
-/*
- * The following was hacked from BSD C library
- * code: src/lib/libc/gen/fnmatch.c,v 1.15 2002/02/01
- *
- * In particular, most of the flags were ripped out: this always
- * behaves like FNM_LEADING_DIR is set and other flags specified
- * by POSIX are unset.
- *
- * Normally, I would not conditionally compile something like this: If
- * I have to support it anyway, everyone may as well use it. ;-)
- * However, the full POSIX spec for fnmatch() includes a lot of
- * advanced character handling that I'm not ready to put in here, so
- * it's probably best if people use a local version when it's available.
- */
-
-/*
- * Copyright (c) 1989, 1993, 1994
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Guido van Rossum.
- *
- * 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.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
- */
-
-static int
-bsdtar_fnmatch(const char *pattern, const char *string)
-{
- const char *saved_pattern;
- int negate, matched;
- char c;
-
- for (;;) {
- switch (c = *pattern++) {
- case '\0':
- if (*string == '/' || *string == '\0')
- return (0);
- return (1);
- case '?':
- if (*string == '\0')
- return (1);
- ++string;
- break;
- case '*':
- c = *pattern;
- /* Collapse multiple stars. */
- while (c == '*')
- c = *++pattern;
-
- /* Optimize for pattern with * at end. */
- if (c == '\0')
- return (0);
-
- /* General case, use recursion. */
- while (*string != '\0') {
- if (!bsdtar_fnmatch(pattern, string))
- return (0);
- ++string;
- }
- return (1);
- case '[':
- if (*string == '\0')
- return (1);
- saved_pattern = pattern;
- if (*pattern == '!' || *pattern == '^') {
- negate = 1;
- ++pattern;
- } else
- negate = 0;
- matched = 0;
- c = *pattern++;
- do {
- if (c == '\\')
- c = *pattern++;
- if (c == '\0') {
- pattern = saved_pattern;
- c = '[';
- goto norm;
- }
- if (*pattern == '-') {
- char c2 = *(pattern + 1);
- if (c2 == '\0') {
- pattern = saved_pattern;
- c = '[';
- goto norm;
- }
- if (c2 == ']') {
- /* [a-] is not a range. */
- if (c == *string
- || '-' == *string)
- matched = 1;
- pattern ++;
- } else {
- if (c <= *string
- && *string <= c2)
- matched = 1;
- pattern += 2;
- }
- } else if (c == *string)
- matched = 1;
- c = *pattern++;
- } while (c != ']');
- if (matched == negate)
- return (1);
- ++string;
- break;
- case '\\':
- if ((c = *pattern++) == '\0') {
- c = '\\';
- --pattern;
- }
- /* FALLTHROUGH */
- default:
- norm:
- if (c != *string)
- return (1);
- string++;
- break;
- }
- }
- /* NOTREACHED */
+ return (matching->inclusions_unmatched_count);
}
-
-#endif
diff --git a/usr.bin/tar/matching.h b/usr.bin/tar/matching.h
new file mode 100644
index 0000000..f4edebd
--- /dev/null
+++ b/usr.bin/tar/matching.h
@@ -0,0 +1,46 @@
+/*-
+ * 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 MATCHING_H
+#define MATCHING_H
+
+struct lafe_matching;
+
+int lafe_exclude(struct lafe_matching **matching, const char *pattern);
+int lafe_exclude_from_file(struct lafe_matching **matching,
+ const char *pathname);
+int lafe_include(struct lafe_matching **matching, const char *pattern);
+int lafe_include_from_file(struct lafe_matching **matching,
+ const char *pathname, int nullSeparator);
+
+int lafe_excluded(struct lafe_matching *, const char *pathname);
+void lafe_cleanup_exclusions(struct lafe_matching **);
+int lafe_unmatched_inclusions(struct lafe_matching *);
+int lafe_unmatched_inclusions_warn(struct lafe_matching *, const char *msg);
+
+#endif
diff --git a/usr.bin/tar/pathmatch.c b/usr.bin/tar/pathmatch.c
new file mode 100644
index 0000000..8b43138
--- /dev/null
+++ b/usr.bin/tar/pathmatch.c
@@ -0,0 +1,255 @@
+/*-
+ * 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 "bsdtar_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "pathmatch.h"
+
+/*
+ * Check whether a character 'c' is matched by a list specification [...]:
+ * * Leading '!' 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 < 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);
+}
+
+/*
+ * 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 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 succeds, 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 (lafe_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;
+ }
+}
+
+/* Main entry point. */
+int
+lafe_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));
+}
diff --git a/usr.bin/tar/pathmatch.h b/usr.bin/tar/pathmatch.h
new file mode 100644
index 0000000..a92f3ae
--- /dev/null
+++ b/usr.bin/tar/pathmatch.h
@@ -0,0 +1,42 @@
+/*-
+ * 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 LAFE_PATHMATCH_H
+#define LAFE_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 lafe_pathmatch(const char *p, const char *s, int flags);
+
+#endif
diff --git a/usr.bin/tar/read.c b/usr.bin/tar/read.c
index ab66f89..c0e5bcb 100644
--- a/usr.bin/tar/read.c
+++ b/usr.bin/tar/read.c
@@ -48,6 +48,9 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
@@ -79,7 +82,8 @@ void
tar_mode_t(struct bsdtar *bsdtar)
{
read_archive(bsdtar, 't');
- unmatched_inclusions_warn(bsdtar, "Not found in archive");
+ if (lafe_unmatched_inclusions_warn(bsdtar->matching, "Not found in archive") != 0)
+ bsdtar->return_value = 1;
}
void
@@ -87,7 +91,8 @@ tar_mode_x(struct bsdtar *bsdtar)
{
read_archive(bsdtar, 'x');
- unmatched_inclusions_warn(bsdtar, "Not found in archive");
+ if (lafe_unmatched_inclusions_warn(bsdtar->matching, "Not found in archive") != 0)
+ bsdtar->return_value = 1;
}
static void
@@ -135,12 +140,13 @@ read_archive(struct bsdtar *bsdtar, char mode)
int r;
while (*bsdtar->argv) {
- include(bsdtar, *bsdtar->argv);
+ lafe_include(&bsdtar->matching, *bsdtar->argv);
bsdtar->argv++;
}
if (bsdtar->names_from_file != NULL)
- include_from_file(bsdtar, bsdtar->names_from_file);
+ lafe_include_from_file(&bsdtar->matching,
+ bsdtar->names_from_file, bsdtar->option_null);
a = archive_read_new();
if (bsdtar->compress_program != NULL)
@@ -179,7 +185,7 @@ read_archive(struct bsdtar *bsdtar, char mode)
for (;;) {
/* Support --fast-read option */
if (bsdtar->option_fast_read &&
- unmatched_inclusions(bsdtar) == 0)
+ lafe_unmatched_inclusions(bsdtar->matching) == 0)
break;
r = archive_read_next_header(a, &entry);
@@ -233,7 +239,7 @@ read_archive(struct bsdtar *bsdtar, char mode)
* rewrite, there would be no way to exclude foo1/bar
* while allowing foo2/bar.)
*/
- if (excluded(bsdtar, archive_entry_pathname(entry)))
+ if (lafe_excluded(bsdtar->matching, archive_entry_pathname(entry)))
continue; /* Excluded by a pattern test. */
if (mode == 't') {
diff --git a/usr.bin/tar/util.c b/usr.bin/tar/util.c
index b3df5f0..858d472 100644
--- a/usr.bin/tar/util.c
+++ b/usr.bin/tar/util.c
@@ -36,9 +36,15 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
@@ -59,6 +65,10 @@ __FBSDID("$FreeBSD$");
static size_t bsdtar_expand_char(char *, size_t, char);
static const char *strip_components(const char *path, int elements);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#define read _read
+#endif
+
/* TODO: Hack up a version of mbtowc for platforms with no wide
* character support at all. I think the following might suffice,
* but it needs careful testing.
@@ -87,7 +97,7 @@ safe_fprintf(FILE *f, const char *fmt, ...)
char *fmtbuff_heap; /* If fmtbuff_stack is too small, we use malloc */
char *fmtbuff; /* Pointer to fmtbuff_stack or fmtbuff_heap. */
int fmtbuff_length;
- int length;
+ int length, n;
va_list ap;
const char *p;
unsigned i;
@@ -124,14 +134,13 @@ safe_fprintf(FILE *f, const char *fmt, ...)
/* Note: mbrtowc() has a cleaner API, but mbtowc() seems a bit
* more portable, so we use that here instead. */
- mbtowc(NULL, NULL, 0); /* Reset the shift state. */
+ n = mbtowc(NULL, NULL, 1); /* Reset the shift state. */
/* Write data, expanding unprintable characters. */
p = fmtbuff;
i = 0;
try_wc = 1;
while (*p != '\0') {
- int n;
/* Convert to wide char, test if the wide
* char is printable in the current locale. */
@@ -144,24 +153,24 @@ safe_fprintf(FILE *f, const char *fmt, ...)
} else {
/* Not printable, format the bytes. */
while (n-- > 0)
- i += bsdtar_expand_char(
+ i += (unsigned)bsdtar_expand_char(
outbuff, i, *p++);
}
} else {
/* After any conversion failure, don't bother
* trying to convert the rest. */
- i += bsdtar_expand_char(outbuff, i, *p++);
+ i += (unsigned)bsdtar_expand_char(outbuff, i, *p++);
try_wc = 0;
}
/* If our output buffer is full, dump it and keep going. */
if (i > (sizeof(outbuff) - 20)) {
- outbuff[i++] = '\0';
+ outbuff[i] = '\0';
fprintf(f, "%s", outbuff);
i = 0;
}
}
- outbuff[i++] = '\0';
+ outbuff[i] = '\0';
fprintf(f, "%s", outbuff);
/* If we allocated a heap-based formatting buffer, free it now. */
@@ -237,95 +246,6 @@ yes(const char *fmt, ...)
return (0);
}
-/*
- * Read lines from file and do something with each one. If option_null
- * is set, lines are terminated with zero bytes; otherwise, they're
- * terminated with newlines.
- *
- * This uses a self-sizing buffer to handle arbitrarily-long lines.
- * If the "process" function returns non-zero for any line, this
- * function will return non-zero after attempting to process all
- * remaining lines.
- */
-int
-process_lines(struct bsdtar *bsdtar, const char *pathname,
- int (*process)(struct bsdtar *, const char *))
-{
- FILE *f;
- char *buff, *buff_end, *line_start, *line_end, *p;
- size_t buff_length, new_buff_length, bytes_read, bytes_wanted;
- int separator;
- int ret;
-
- separator = bsdtar->option_null ? '\0' : '\n';
- ret = 0;
-
- if (strcmp(pathname, "-") == 0)
- f = stdin;
- else
- f = fopen(pathname, "r");
- if (f == NULL)
- bsdtar_errc(1, errno, "Couldn't open %s", pathname);
- buff_length = 8192;
- buff = malloc(buff_length);
- if (buff == NULL)
- bsdtar_errc(1, ENOMEM, "Can't read %s", pathname);
- line_start = line_end = buff_end = buff;
- for (;;) {
- /* Get some more data into the buffer. */
- bytes_wanted = buff + buff_length - buff_end;
- bytes_read = fread(buff_end, 1, bytes_wanted, f);
- buff_end += bytes_read;
- /* Process all complete lines in the buffer. */
- while (line_end < buff_end) {
- if (*line_end == separator) {
- *line_end = '\0';
- if ((*process)(bsdtar, line_start) != 0)
- ret = -1;
- line_start = line_end + 1;
- line_end = line_start;
- } else
- line_end++;
- }
- if (feof(f))
- break;
- if (ferror(f))
- bsdtar_errc(1, errno,
- "Can't read %s", pathname);
- if (line_start > buff) {
- /* Move a leftover fractional line to the beginning. */
- memmove(buff, line_start, buff_end - line_start);
- buff_end -= line_start - buff;
- line_end -= line_start - buff;
- line_start = buff;
- } else {
- /* Line is too big; enlarge the buffer. */
- new_buff_length = buff_length * 2;
- if (new_buff_length <= buff_length)
- bsdtar_errc(1, ENOMEM,
- "Line too long in %s", pathname);
- buff_length = new_buff_length;
- p = realloc(buff, buff_length);
- if (p == NULL)
- bsdtar_errc(1, ENOMEM,
- "Line too long in %s", pathname);
- buff_end = p + (buff_end - buff);
- line_end = p + (line_end - buff);
- line_start = buff = p;
- }
- }
- /* At end-of-file, handle the final line. */
- if (line_end > line_start) {
- *line_end = '\0';
- if ((*process)(bsdtar, line_start) != 0)
- ret = -1;
- }
- free(buff);
- if (f != stdin)
- fclose(f);
- return (ret);
-}
-
/*-
* The logic here for -C <dir> attempts to avoid
* chdir() as long as possible. For example:
@@ -342,6 +262,8 @@ process_lines(struct bsdtar *bsdtar, const char *pathname,
* This way, programs that build tar command lines don't have to worry
* about -C with non-existent directories; such requests will only
* fail if the directory must be accessed.
+ *
+ * TODO: Make this handle Windows paths correctly.
*/
void
set_chdir(struct bsdtar *bsdtar, const char *newdir)
@@ -384,16 +306,17 @@ do_chdir(struct bsdtar *bsdtar)
bsdtar->pending_chdir = NULL;
}
-const char *
-strip_components(const char *path, int elements)
+static const char *
+strip_components(const char *p, int elements)
{
- const char *p = path;
-
+ /* Skip as many elements as necessary. */
while (elements > 0) {
switch (*p++) {
case '/':
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ case '\\': /* Support \ path sep on Windows ONLY. */
+#endif
elements--;
- path = p;
break;
case '\0':
/* Path is too short, skip it. */
@@ -401,12 +324,25 @@ strip_components(const char *path, int elements)
}
}
- while (*path == '/')
- ++path;
- if (*path == '\0')
- return (NULL);
-
- return (path);
+ /* Skip any / characters. This handles short paths that have
+ * additional / termination. This also handles the case where
+ * the logic above stops in the middle of a duplicate //
+ * sequence (which would otherwise get converted to an
+ * absolute path). */
+ for (;;) {
+ switch (*p) {
+ case '/':
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ case '\\': /* Support \ path sep on Windows ONLY. */
+#endif
+ ++p;
+ break;
+ case '\0':
+ return (NULL);
+ default:
+ return (p);
+ }
+ }
}
/*
@@ -590,6 +526,9 @@ tar_i64toa(int64_t n0)
* TODO: Publish the path normalization routines in libarchive so
* that bsdtar can normalize paths and use fast strcmp() instead
* of this.
+ *
+ * Note: This is currently only used within write.c, so should
+ * not handle \ path separators.
*/
int
diff --git a/usr.bin/tar/write.c b/usr.bin/tar/write.c
index f7861bc..31005eb 100644
--- a/usr.bin/tar/write.c
+++ b/usr.bin/tar/write.c
@@ -47,6 +47,9 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_GRP_H
#include <grp.h>
#endif
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
@@ -67,6 +70,9 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
@@ -80,6 +86,7 @@ __FBSDID("$FreeBSD$");
#include "bsdtar.h"
#include "err.h"
+#include "line_reader.h"
#include "tree.h"
/* Size of buffer for holding file data prior to writing. */
@@ -119,8 +126,6 @@ static int append_archive_filename(struct bsdtar *,
struct archive *, const char *fname);
static void archive_names_from_file(struct bsdtar *bsdtar,
struct archive *a);
-static int archive_names_from_file_helper(struct bsdtar *bsdtar,
- const char *line);
static int copy_file_data(struct bsdtar *, struct archive *a,
struct archive *ina, struct archive_entry *);
static int new_enough(struct bsdtar *, const char *path,
@@ -514,33 +519,31 @@ cleanup:
static void
archive_names_from_file(struct bsdtar *bsdtar, struct archive *a)
{
- bsdtar->archive = a;
+ struct lafe_line_reader *lr;
+ const char *line;
bsdtar->next_line_is_dir = 0;
- process_lines(bsdtar, bsdtar->names_from_file,
- archive_names_from_file_helper);
+
+ lr = lafe_line_reader(bsdtar->names_from_file, bsdtar->option_null);
+ while ((line = lafe_line_reader_next(lr)) != NULL) {
+ if (bsdtar->next_line_is_dir) {
+ set_chdir(bsdtar, line);
+ bsdtar->next_line_is_dir = 0;
+ } else if (!bsdtar->option_null && strcmp(line, "-C") == 0)
+ bsdtar->next_line_is_dir = 1;
+ else {
+ if (*line != '/')
+ do_chdir(bsdtar); /* Handle a deferred -C */
+ write_hierarchy(bsdtar, a, line);
+ }
+ }
+ lafe_line_reader_free(lr);
if (bsdtar->next_line_is_dir)
bsdtar_errc(1, errno,
"Unexpected end of filename list; "
"directory expected after -C");
}
-static int
-archive_names_from_file_helper(struct bsdtar *bsdtar, const char *line)
-{
- if (bsdtar->next_line_is_dir) {
- set_chdir(bsdtar, line);
- bsdtar->next_line_is_dir = 0;
- } else if (!bsdtar->option_null && strcmp(line, "-C") == 0)
- bsdtar->next_line_is_dir = 1;
- else {
- if (*line != '/')
- do_chdir(bsdtar); /* Handle a deferred -C */
- write_hierarchy(bsdtar, bsdtar->archive, line);
- }
- return (0);
-}
-
/*
* Copy from specified archive to current archive. Returns non-zero
* for write errors (which force us to terminate the entire archiving
@@ -589,7 +592,7 @@ append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina)
if (!new_enough(bsdtar, archive_entry_pathname(in_entry),
archive_entry_stat(in_entry)))
continue;
- if (excluded(bsdtar, archive_entry_pathname(in_entry)))
+ if (lafe_excluded(bsdtar->matching, archive_entry_pathname(in_entry)))
continue;
if (bsdtar->option_interactive &&
!yes("copy '%s'", archive_entry_pathname(in_entry)))
@@ -699,7 +702,7 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path)
* If this file/dir is excluded by a filename
* pattern, skip it.
*/
- if (excluded(bsdtar, name))
+ if (lafe_excluded(bsdtar->matching, name))
continue;
/*
OpenPOWER on IntegriCloud