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.
This commit is contained in:
parent
e3debd74cb
commit
a405ab8bd4
@ -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 \
|
||||
|
@ -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
|
||||
|
@ -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, ...);
|
||||
|
||||
|
@ -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
|
||||
|
171
usr.bin/tar/line_reader.c
Normal file
171
usr.bin/tar/line_reader.c
Normal file
@ -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);
|
||||
}
|
37
usr.bin/tar/line_reader.h
Normal file
37
usr.bin/tar/line_reader.h
Normal file
@ -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
|
@ -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;
|
||||
for (p = matching->inclusions; p != NULL; p = p->next) {
|
||||
if (p->matches == 0)
|
||||
bsdtar_warnc(0, "%s: %s", p->pattern, msg);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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 */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
46
usr.bin/tar/matching.h
Normal file
46
usr.bin/tar/matching.h
Normal file
@ -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
|
255
usr.bin/tar/pathmatch.c
Normal file
255
usr.bin/tar/pathmatch.c
Normal file
@ -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));
|
||||
}
|
42
usr.bin/tar/pathmatch.h
Normal file
42
usr.bin/tar/pathmatch.h
Normal file
@ -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
|
@ -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') {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
/*
|
||||
|
Loading…
x
Reference in New Issue
Block a user