summaryrefslogtreecommitdiffstats
path: root/usr.bin
diff options
context:
space:
mode:
authorkientzle <kientzle@FreeBSD.org>2004-06-27 06:29:03 +0000
committerkientzle <kientzle@FreeBSD.org>2004-06-27 06:29:03 +0000
commit2e75ccba07cbf0b2aaf2d472050735824a1969e1 (patch)
tree006eea9aabde7acb96386741b5e6ecea4bc5e129 /usr.bin
parentcb03971cc7b58053c0bf29edc032bef8ece262e8 (diff)
downloadFreeBSD-src-2e75ccba07cbf0b2aaf2d472050735824a1969e1.zip
FreeBSD-src-2e75ccba07cbf0b2aaf2d472050735824a1969e1.tar.gz
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.
Diffstat (limited to 'usr.bin')
-rw-r--r--usr.bin/tar/bsdtar.c32
-rw-r--r--usr.bin/tar/bsdtar.h12
-rw-r--r--usr.bin/tar/matching.c56
-rw-r--r--usr.bin/tar/read.c3
-rw-r--r--usr.bin/tar/util.c86
-rw-r--r--usr.bin/tar/write.c70
6 files changed, 161 insertions, 98 deletions
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 <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);
+}
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);
}
/*
OpenPOWER on IntegriCloud