Augment the -T handling:
* Add --null option (sort #defines here) * Add process_lines function to util.c that reads newline-terminated or null-terminated lines (with self-sizing buffers, etc) and iteratively invokes a provided function. Use this to dramatically simplify: -T handling for -c, --exclude-from-file, and --include-from-file. * Add -T handling to -x (via include_from_file) Hopefully, this will fix the openoffice port and a couple of others that rely on -T and --null.
This commit is contained in:
parent
30d140332e
commit
d3d1a208cb
@ -91,11 +91,12 @@ const char *tar_opts = "+Bb:C:cF:f:HhjkLlmnOoPprtT:UuvW:wX:xyZz";
|
||||
/* Fake short equivalents for long options that otherwise lack them. */
|
||||
#define OPTION_EXCLUDE 1
|
||||
#define OPTION_FAST_READ 2
|
||||
#define OPTION_NODUMP 3
|
||||
#define OPTION_HELP 4
|
||||
#define OPTION_INCLUDE 5
|
||||
#define OPTION_ONE_FILE_SYSTEM 6
|
||||
#define OPTION_NO_SAME_PERMISSIONS 7
|
||||
#define OPTION_HELP 3
|
||||
#define OPTION_INCLUDE 4
|
||||
#define OPTION_NODUMP 5
|
||||
#define OPTION_NO_SAME_PERMISSIONS 6
|
||||
#define OPTION_NULL 7
|
||||
#define OPTION_ONE_FILE_SYSTEM 8
|
||||
|
||||
const struct option tar_longopts[] = {
|
||||
{ "absolute-paths", no_argument, NULL, 'P' },
|
||||
@ -127,6 +128,7 @@ const struct option tar_longopts[] = {
|
||||
{ "norecurse", no_argument, NULL, 'n' },
|
||||
{ "no-same-owner", no_argument, NULL, 'o' },
|
||||
{ "no-same-permissions",no_argument, NULL, OPTION_NO_SAME_PERMISSIONS },
|
||||
{ "null", no_argument, NULL, OPTION_NULL },
|
||||
{ "one-file-system", no_argument, NULL, OPTION_ONE_FILE_SYSTEM },
|
||||
{ "preserve-permissions", no_argument, NULL, 'p' },
|
||||
{ "read-full-blocks", no_argument, NULL, 'B' },
|
||||
@ -199,10 +201,7 @@ main(int argc, char **argv)
|
||||
while ((opt = bsdtar_getopt(bsdtar, tar_opts, &option)) != -1) {
|
||||
switch (opt) {
|
||||
case 'B': /* GNU tar */
|
||||
/*
|
||||
* bsdtar is stream-based internally, so this
|
||||
* option has no effect. Just ignore it.
|
||||
*/
|
||||
/* libarchive doesn't need this; just ignore it. */
|
||||
break;
|
||||
case 'b': /* SUSv2 */
|
||||
bsdtar->bytes_per_block = 512 * atoi(optarg);
|
||||
@ -280,6 +279,9 @@ main(int argc, char **argv)
|
||||
* command-line option as a no-op.
|
||||
*/
|
||||
break;
|
||||
case OPTION_NULL: /* GNU tar */
|
||||
bsdtar->option_null++;
|
||||
break;
|
||||
case 'O': /* GNU tar */
|
||||
bsdtar->option_stdout = 1;
|
||||
break;
|
||||
@ -314,6 +316,9 @@ main(int argc, char **argv)
|
||||
opt, mode);
|
||||
mode = opt;
|
||||
break;
|
||||
case 'T': /* GNU tar */
|
||||
bsdtar->names_from_file = optarg;
|
||||
break;
|
||||
case 't': /* SUSv2 */
|
||||
if (mode != '\0')
|
||||
bsdtar_errc(bsdtar, 1, 0,
|
||||
@ -322,9 +327,6 @@ main(int argc, char **argv)
|
||||
mode = opt;
|
||||
bsdtar->verbose++;
|
||||
break;
|
||||
case 'T': /* GNU tar */
|
||||
bsdtar->names_from_file = optarg;
|
||||
break;
|
||||
case 'U': /* GNU tar */
|
||||
bsdtar->extract_flags |= ARCHIVE_EXTRACT_UNLINK;
|
||||
bsdtar->option_unlink_first = 1;
|
||||
@ -366,7 +368,7 @@ main(int argc, char **argv)
|
||||
bsdtar->create_compression);
|
||||
bsdtar->create_compression = opt;
|
||||
break;
|
||||
case 'z': /* GNU tar, star */
|
||||
case 'z': /* GNU tar, star, many others */
|
||||
if (bsdtar->create_compression != '\0')
|
||||
bsdtar_errc(bsdtar, 1, 0,
|
||||
"Can't specify both -%c and -%c", opt,
|
||||
@ -418,10 +420,8 @@ main(int argc, char **argv)
|
||||
}
|
||||
if (bsdtar->create_format != NULL)
|
||||
only_mode(bsdtar, mode, "-F", "c");
|
||||
if (bsdtar->names_from_file != NULL)
|
||||
only_mode(bsdtar, mode, "-T", "cru");
|
||||
if (bsdtar->symlink_mode != '\0') {
|
||||
strcpy(buff, "-X");
|
||||
strcpy(buff, "-?");
|
||||
buff[1] = bsdtar->symlink_mode;
|
||||
only_mode(bsdtar, mode, buff, "cru");
|
||||
}
|
||||
|
@ -59,6 +59,7 @@ struct bsdtar {
|
||||
char option_honor_nodump; /* --nodump */
|
||||
char option_interactive; /* -w */
|
||||
char option_no_subdirs; /* -d */
|
||||
char option_null; /* --null */
|
||||
char option_stdout; /* -p */
|
||||
char option_unlink_first; /* -U */
|
||||
char option_warn_links; /* -l */
|
||||
@ -67,6 +68,7 @@ struct bsdtar {
|
||||
int fd;
|
||||
|
||||
/* Miscellaneous state information */
|
||||
struct archive *archive;
|
||||
const char *progname;
|
||||
int argc;
|
||||
char **argv;
|
||||
@ -76,6 +78,7 @@ struct bsdtar {
|
||||
uid_t user_uid; /* UID running this program */
|
||||
int return_value; /* Value returned by main() */
|
||||
char warned_lead_slash; /* Already displayed warning */
|
||||
char next_line_is_dir; /* Used for -C parsing in -cT */
|
||||
|
||||
/*
|
||||
* Data for various subsystems. Full definitions are located in
|
||||
@ -94,10 +97,13 @@ void bsdtar_errc(struct bsdtar *, int _eval, int _code,
|
||||
void bsdtar_strmode(struct archive_entry *entry, char *bp);
|
||||
void bsdtar_warnc(struct bsdtar *, int _code, const char *fmt, ...);
|
||||
void cleanup_exclusions(struct bsdtar *);
|
||||
void exclude(struct bsdtar *, const char *pattern);
|
||||
void exclude_from_file(struct bsdtar *, const char *pathname);
|
||||
int exclude(struct bsdtar *, const char *pattern);
|
||||
int exclude_from_file(struct bsdtar *, const char *pathname);
|
||||
int excluded(struct bsdtar *, const char *pathname);
|
||||
void include(struct bsdtar *, const char *pattern);
|
||||
int include(struct bsdtar *, const char *pattern);
|
||||
int include_from_file(struct bsdtar *, const char *pathname);
|
||||
int process_lines(struct bsdtar *bsdtar, const char *pathname,
|
||||
int (*process)(struct bsdtar *, const char *));
|
||||
void safe_fprintf(FILE *, const char *fmt, ...);
|
||||
void tar_mode_c(struct bsdtar *bsdtar);
|
||||
void tar_mode_r(struct bsdtar *bsdtar);
|
||||
|
@ -56,17 +56,17 @@ static int match_exclusion(struct match *, const char *pathname);
|
||||
static int match_inclusion(struct match *, const char *pathname);
|
||||
|
||||
/*
|
||||
* The matching logic here needs to be re-thought. I started
|
||||
* out to try to mimic gtar's matching logic, but found it wasn't
|
||||
* really consistent. In particular 'tar -t' and 'tar -x' interpret
|
||||
* patterns on the command line as anchored, but --exclude doesn't.
|
||||
* The matching logic here needs to be re-thought. I started out to
|
||||
* try to mimic gtar's matching logic, but it's not entirely
|
||||
* consistent. In particular 'tar -t' and 'tar -x' interpret patterns
|
||||
* on the command line as anchored, but --exclude doesn't.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Utility functions to manage exclusion/inclusion patterns
|
||||
*/
|
||||
|
||||
void
|
||||
int
|
||||
exclude(struct bsdtar *bsdtar, const char *pattern)
|
||||
{
|
||||
struct matching *matching;
|
||||
@ -76,47 +76,16 @@ exclude(struct bsdtar *bsdtar, const char *pattern)
|
||||
matching = bsdtar->matching;
|
||||
add_pattern(bsdtar, &(matching->exclusions), pattern);
|
||||
matching->exclusions_count++;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read lines from file and exclude() each one. This uses
|
||||
* a self-sizing buffer to handle arbitrarily-long lines.
|
||||
*/
|
||||
void
|
||||
int
|
||||
exclude_from_file(struct bsdtar *bsdtar, const char *pathname)
|
||||
{
|
||||
FILE *f;
|
||||
char *buff;
|
||||
size_t buff_length, line_length;
|
||||
|
||||
f = fopen(pathname, "r");
|
||||
if (f == NULL)
|
||||
bsdtar_errc(bsdtar, 1, errno, "Couldn't open %s", pathname);
|
||||
buff_length = 256;
|
||||
buff = malloc(buff_length);
|
||||
if (buff == NULL)
|
||||
bsdtar_errc(bsdtar, 1, ENOMEM, "Can't read %s", pathname);
|
||||
while(fgets(buff, buff_length, f) != NULL) {
|
||||
line_length = strlen(buff);
|
||||
while (buff[line_length - 1] != '\n') {
|
||||
buff = realloc(buff, buff_length *= 2);
|
||||
if (buff == NULL)
|
||||
bsdtar_errc(bsdtar, 1, ENOMEM,
|
||||
"Line too long in %s", pathname);
|
||||
if (fgets(buff + line_length,
|
||||
buff_length - line_length, f) == NULL)
|
||||
bsdtar_errc(bsdtar, 1, 0,
|
||||
"Bad input line in %s", pathname);
|
||||
line_length = strlen(buff);
|
||||
}
|
||||
buff[line_length - 1] = '\0';
|
||||
exclude(bsdtar, buff);
|
||||
}
|
||||
free(buff);
|
||||
fclose(f);
|
||||
return (process_lines(bsdtar, pathname, &exclude));
|
||||
}
|
||||
|
||||
void
|
||||
int
|
||||
include(struct bsdtar *bsdtar, const char *pattern)
|
||||
{
|
||||
struct matching *matching;
|
||||
@ -127,6 +96,13 @@ include(struct bsdtar *bsdtar, const char *pattern)
|
||||
add_pattern(bsdtar, &(matching->inclusions), pattern);
|
||||
matching->inclusions_count++;
|
||||
matching->inclusions_unmatched_count++;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
include_from_file(struct bsdtar *bsdtar, const char *pathname)
|
||||
{
|
||||
return (process_lines(bsdtar, pathname, &include));
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -77,6 +77,9 @@ read_archive(struct bsdtar *bsdtar, char mode)
|
||||
bsdtar->argv++;
|
||||
}
|
||||
|
||||
if (bsdtar->names_from_file != NULL)
|
||||
include_from_file(bsdtar, bsdtar->names_from_file);
|
||||
|
||||
a = archive_read_new();
|
||||
archive_read_support_compression_all(a);
|
||||
archive_read_support_format_all(a);
|
||||
|
@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/types.h> /* Linux doesn't define mode_t, etc. in sys/stat.h. */
|
||||
#include <archive_entry.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -234,3 +235,88 @@ bsdtar_strmode(struct archive_entry *entry, char *bp)
|
||||
if (archive_entry_acl_count(entry, ARCHIVE_ENTRY_ACL_TYPE_ACCESS))
|
||||
bp[10] = '+';
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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, 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(bsdtar, 1, errno, "Couldn't open %s", pathname);
|
||||
buff_length = 8192;
|
||||
buff = malloc(buff_length);
|
||||
if (buff == NULL)
|
||||
bsdtar_errc(bsdtar, 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(bsdtar, 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. */
|
||||
p = realloc(buff, buff_length *= 2);
|
||||
if (buff == NULL)
|
||||
bsdtar_errc(bsdtar, 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);
|
||||
}
|
||||
|
@ -103,6 +103,8 @@ static int append_archive(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 void create_cleanup(struct bsdtar *);
|
||||
static void free_cache(struct name_cache *cache);
|
||||
static const char * lookup_gname(struct bsdtar *bsdtar, gid_t gid);
|
||||
@ -447,50 +449,40 @@ write_archive(struct archive *a, struct bsdtar *bsdtar)
|
||||
create_cleanup(bsdtar);
|
||||
}
|
||||
|
||||
/* Archive names specified in file. */
|
||||
/*
|
||||
* Archive names specified in file.
|
||||
*
|
||||
* Unless --null was specified, a line containing exactly "-C" will
|
||||
* cause the next line to be a directory to pass to chdir(). If
|
||||
* --null is specified, then a line "-C" is just another filename.
|
||||
*/
|
||||
void
|
||||
archive_names_from_file(struct bsdtar *bsdtar, struct archive *a)
|
||||
{
|
||||
FILE *f;
|
||||
char buff[1024];
|
||||
int l;
|
||||
bsdtar->archive = a;
|
||||
|
||||
if (strcmp(bsdtar->names_from_file, "-") == 0)
|
||||
f = stdin;
|
||||
bsdtar->next_line_is_dir = 0;
|
||||
process_lines(bsdtar, bsdtar->names_from_file,
|
||||
archive_names_from_file_helper);
|
||||
if (bsdtar->next_line_is_dir)
|
||||
bsdtar_errc(bsdtar, 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) {
|
||||
if (chdir(line) != 0)
|
||||
bsdtar_errc(bsdtar, 1, errno,
|
||||
"chdir(%s) failed", line);
|
||||
bsdtar->next_line_is_dir = 0;
|
||||
} else if (!bsdtar->option_null && strcmp(line, "-C") == 0)
|
||||
bsdtar->next_line_is_dir = 1;
|
||||
else
|
||||
f = fopen(bsdtar->names_from_file, "r");
|
||||
|
||||
while (fgets(buff, sizeof(buff), f) != NULL) {
|
||||
l = strlen(buff);
|
||||
if (buff[l-1] == '\n')
|
||||
buff[l-1] = '\0';
|
||||
|
||||
/*
|
||||
* This -C processing hack is really quite ugly, but
|
||||
* gtar does it this way and pkg_create depends on it,
|
||||
* so I guess I'm stuck having to implement it.
|
||||
*
|
||||
* Someday, pkg_create will just use libarchive directly
|
||||
* and this will just be a bad memory.
|
||||
*/
|
||||
if (strcmp(buff, "-C") == 0) {
|
||||
if (fgets(buff, sizeof(buff), f) == NULL)
|
||||
bsdtar_errc(bsdtar, 1, errno,
|
||||
"Unexpected end of filename list; "
|
||||
"directory expected after -C");
|
||||
l = strlen(buff);
|
||||
if (buff[l-1] == '\n')
|
||||
buff[l-1] = '\0';
|
||||
if (chdir(buff))
|
||||
bsdtar_errc(bsdtar, 1, errno,
|
||||
"chdir(%s) failed", buff);
|
||||
} else {
|
||||
write_heirarchy(bsdtar, a, buff);
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(bsdtar->names_from_file, "-") != 0)
|
||||
fclose(f);
|
||||
write_heirarchy(bsdtar, bsdtar->archive, line);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user