summaryrefslogtreecommitdiffstats
path: root/usr.bin/tar
diff options
context:
space:
mode:
authorkientzle <kientzle@FreeBSD.org>2004-08-08 05:50:10 +0000
committerkientzle <kientzle@FreeBSD.org>2004-08-08 05:50:10 +0000
commit03cc09682ba372ec8e35d35378cd1c083a7da553 (patch)
tree347ff888c2045d99c708ebb1540bf8a514cf38eb /usr.bin/tar
parent86f0a3c9714e65b3d1ebb7094277a133b819f3bc (diff)
downloadFreeBSD-src-03cc09682ba372ec8e35d35378cd1c083a7da553.zip
FreeBSD-src-03cc09682ba372ec8e35d35378cd1c083a7da553.tar.gz
Move the smart chdir logic into a couple of utility functions in util.c.
Then use them to provide consistent -C support throughout the program. Thanks to: Christoph Mallon
Diffstat (limited to 'usr.bin/tar')
-rw-r--r--usr.bin/tar/bsdtar.c3
-rw-r--r--usr.bin/tar/bsdtar.h4
-rw-r--r--usr.bin/tar/read.c5
-rw-r--r--usr.bin/tar/util.c58
-rw-r--r--usr.bin/tar/write.c81
5 files changed, 66 insertions, 85 deletions
diff --git a/usr.bin/tar/bsdtar.c b/usr.bin/tar/bsdtar.c
index 38cc25b..296eb2c 100644
--- a/usr.bin/tar/bsdtar.c
+++ b/usr.bin/tar/bsdtar.c
@@ -236,8 +236,7 @@ main(int argc, char **argv)
bsdtar->bytes_per_block = 512 * t;
break;
case 'C': /* GNU tar */
- /* Defer first -C until after -f is opened. */
- bsdtar->start_dir = optarg;
+ set_chdir(bsdtar, optarg);
break;
case 'c': /* SUSv2 */
set_mode(bsdtar, opt);
diff --git a/usr.bin/tar/bsdtar.h b/usr.bin/tar/bsdtar.h
index 188061c..387c040 100644
--- a/usr.bin/tar/bsdtar.h
+++ b/usr.bin/tar/bsdtar.h
@@ -46,7 +46,7 @@ struct bsdtar {
/* Options */
const char *filename; /* -f filename */
const char *create_format; /* -F format */
- const char *start_dir; /* -C dir */
+ char *pending_chdir; /* -C dir */
const char *names_from_file; /* -T file */
int bytes_per_block; /* -b block_size */
int verbose; /* -v */
@@ -100,6 +100,7 @@ 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 do_chdir(struct bsdtar *);
int exclude(struct bsdtar *, const char *pattern);
int exclude_from_file(struct bsdtar *, const char *pathname);
int excluded(struct bsdtar *, const char *pathname);
@@ -108,6 +109,7 @@ 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 set_chdir(struct bsdtar *, const char *newdir);
void tar_mode_c(struct bsdtar *bsdtar);
void tar_mode_r(struct bsdtar *bsdtar);
void tar_mode_t(struct bsdtar *bsdtar);
diff --git a/usr.bin/tar/read.c b/usr.bin/tar/read.c
index 51cf047..11eb644 100644
--- a/usr.bin/tar/read.c
+++ b/usr.bin/tar/read.c
@@ -89,10 +89,7 @@ read_archive(struct bsdtar *bsdtar, char mode)
bsdtar_errc(bsdtar, 1, 0, "Error opening archive: %s",
archive_error_string(a));
- if (bsdtar->start_dir != NULL && chdir(bsdtar->start_dir))
- bsdtar_errc(bsdtar, 1, errno,
- "chdir(%s) failed", bsdtar->start_dir);
-
+ do_chdir(bsdtar);
for (;;) {
/* Support --fast-read option */
if (bsdtar->option_fast_read &&
diff --git a/usr.bin/tar/util.c b/usr.bin/tar/util.c
index 48e30c2..5071fe9 100644
--- a/usr.bin/tar/util.c
+++ b/usr.bin/tar/util.c
@@ -322,3 +322,61 @@ process_lines(struct bsdtar *bsdtar, const char *pathname,
fclose(f);
return (ret);
}
+
+/*-
+ * The logic here for -C <dir> attempts to avoid
+ * chdir() as long as possible. For example:
+ * "-C /foo -C /bar file" needs chdir("/bar") but not chdir("/foo")
+ * "-C /foo -C bar file" needs chdir("/foo/bar")
+ * "-C /foo -C bar /file1" does not need chdir()
+ * "-C /foo -C bar /file1 file2" needs chdir("/foo/bar") before file2
+ *
+ * The only correct way to handle this is to record a "pending" chdir
+ * request and combine multiple requests intelligently until we
+ * need to process a non-absolute file. set_chdir() adds the new dir
+ * to the pending list; do_chdir() actually executes any pending chdir.
+ *
+ * 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.
+ */
+void
+set_chdir(struct bsdtar *bsdtar, const char *newdir)
+{
+ if (newdir[0] == '/') {
+ /* The -C /foo -C /bar case; dump first one. */
+ free(bsdtar->pending_chdir);
+ bsdtar->pending_chdir = NULL;
+ }
+ if (bsdtar->pending_chdir == NULL)
+ /* Easy case: no previously-saved dir. */
+ bsdtar->pending_chdir = strdup(newdir);
+ else {
+ /* The -C /foo -C bar case; concatenate */
+ char *old_pending = bsdtar->pending_chdir;
+ size_t old_len = strlen(old_pending);
+ bsdtar->pending_chdir = malloc(old_len + strlen(newdir) + 2);
+ if (old_pending[old_len - 1] == '/')
+ old_pending[old_len - 1] = '\0';
+ if (bsdtar->pending_chdir != NULL)
+ sprintf(bsdtar->pending_chdir, "%s/%s",
+ old_pending, newdir);
+ free(old_pending);
+ }
+ if (bsdtar->pending_chdir == NULL)
+ bsdtar_errc(bsdtar, 1, errno, "No memory");
+}
+
+void
+do_chdir(struct bsdtar *bsdtar)
+{
+ if (bsdtar->pending_chdir == NULL)
+ return;
+
+ if (chdir(bsdtar->pending_chdir) != 0) {
+ bsdtar_errc(bsdtar, 1, 0, "could not chdir to '%s'\n",
+ bsdtar->pending_chdir);
+ }
+ free(bsdtar->pending_chdir);
+ bsdtar->pending_chdir = NULL;
+}
diff --git a/usr.bin/tar/write.c b/usr.bin/tar/write.c
index dc12055..e8d4ca0 100644
--- a/usr.bin/tar/write.c
+++ b/usr.bin/tar/write.c
@@ -367,17 +367,6 @@ static void
write_archive(struct archive *a, struct bsdtar *bsdtar)
{
const char *arg;
- char *pending_dir;
-
- pending_dir = NULL;
-
- if (bsdtar->start_dir != NULL && chdir(bsdtar->start_dir)) {
- bsdtar_warnc(bsdtar, errno,
- "chdir(%s) failed", bsdtar->start_dir);
- bsdtar->return_value = 1;
- return;
- }
-
if (bsdtar->names_from_file != NULL)
archive_names_from_file(bsdtar, a);
@@ -396,74 +385,10 @@ write_archive(struct archive *a, struct bsdtar *bsdtar)
return;
}
}
-
- /*-
- * The logic here for -C <dir> attempts to avoid
- * chdir() as long as possible. For example:
- * "-C /foo -C /bar file"
- * needs chdir("/bar") but not chdir("/foo")
- * "-C /foo -C bar file"
- * needs chdir("/foo/bar")
- * "-C /foo -C bar /file1"
- * does not need chdir()
- * "-C /foo -C bar /file1 file2"
- * needs chdir("/foo/bar") before file2
- *
- * The only correct way to handle this is to
- * record a "pending" chdir request and only
- * execute the real chdir when a non-absolute
- * filename is seen on the command line.
- *
- * I went to all this work so that 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.
- */
- if (pending_dir != NULL && *arg == '/') {
- /* The -C /foo -C /bar case; dump first one. */
- free(pending_dir);
- pending_dir = NULL;
- }
- if (pending_dir) {
- /* The -C /foo -C bar case; concatenate */
- char *old_pending = pending_dir;
- size_t old_len = strlen(old_pending);
-
- pending_dir =
- malloc(old_len + 1 + strlen(arg));
- if (pending_dir == NULL)
- bsdtar_errc(bsdtar, 1, errno,
- "No Memory");
- strcpy(pending_dir, old_pending);
- free(old_pending);
- if (pending_dir[old_len - 1] != '/') {
- pending_dir[old_len] = '/';
- old_len ++;
- }
- strcpy(pending_dir + old_len, arg);
- } else {
- /* Easy case: no previously-saved dir. */
- pending_dir = strdup(arg);
- if (pending_dir == NULL)
- bsdtar_errc(bsdtar, 1, errno,
- "No Memory");
- }
+ set_chdir(bsdtar, arg);
} else {
- if (pending_dir != NULL &&
- (*arg != '/' || (*arg == '@' && arg[1] != '/'))) {
- /* Handle a deferred -C */
- if (chdir(pending_dir) != 0) {
- bsdtar_warnc(bsdtar, 0,
- "could not chdir to '%s'\n",
- pending_dir);
- bsdtar->return_value = 1;
- return;
- }
- free(pending_dir);
- pending_dir = NULL;
- }
-
+ if (*arg != '/' || (arg[0] == '@' && arg[1] != '/'))
+ do_chdir(bsdtar); /* Handle a deferred -C */
if (*arg == '@') {
if (append_archive(bsdtar, a, arg + 1) != 0)
break;
OpenPOWER on IntegriCloud