From 2e75ccba07cbf0b2aaf2d472050735824a1969e1 Mon Sep 17 00:00:00 2001 From: kientzle Date: Sun, 27 Jun 2004 06:29:03 +0000 Subject: 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. --- usr.bin/tar/bsdtar.c | 32 +++++++++---------- usr.bin/tar/bsdtar.h | 12 +++++-- usr.bin/tar/matching.c | 56 ++++++++++---------------------- usr.bin/tar/read.c | 3 ++ usr.bin/tar/util.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++ usr.bin/tar/write.c | 70 ++++++++++++++++++---------------------- 6 files changed, 161 insertions(+), 98 deletions(-) (limited to 'usr.bin') diff --git a/usr.bin/tar/bsdtar.c b/usr.bin/tar/bsdtar.c index 17672f9..2564571 100644 --- a/usr.bin/tar/bsdtar.c +++ b/usr.bin/tar/bsdtar.c @@ -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"); } diff --git a/usr.bin/tar/bsdtar.h b/usr.bin/tar/bsdtar.h index 3c6746c..79ec482 100644 --- a/usr.bin/tar/bsdtar.h +++ b/usr.bin/tar/bsdtar.h @@ -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); diff --git a/usr.bin/tar/matching.c b/usr.bin/tar/matching.c index da5417b..d73c5e5 100644 --- a/usr.bin/tar/matching.c +++ b/usr.bin/tar/matching.c @@ -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 diff --git a/usr.bin/tar/read.c b/usr.bin/tar/read.c index d6532f3..b66ffed 100644 --- a/usr.bin/tar/read.c +++ b/usr.bin/tar/read.c @@ -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); diff --git a/usr.bin/tar/util.c b/usr.bin/tar/util.c index dc1dd4b..d0ea39e 100644 --- a/usr.bin/tar/util.c +++ b/usr.bin/tar/util.c @@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$"); #include /* Linux doesn't define mode_t, etc. in sys/stat.h. */ #include #include +#include #include #include #include @@ -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); +} diff --git a/usr.bin/tar/write.c b/usr.bin/tar/write.c index 196f922..dc2ddc0 100644 --- a/usr.bin/tar/write.c +++ b/usr.bin/tar/write.c @@ -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; - 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); - } - } + 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"); +} - if (strcmp(bsdtar->names_from_file, "-") != 0) - fclose(f); +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 + write_heirarchy(bsdtar, bsdtar->archive, line); + return (0); } /* -- cgit v1.1