diff options
author | kientzle <kientzle@FreeBSD.org> | 2004-08-08 05:50:10 +0000 |
---|---|---|
committer | kientzle <kientzle@FreeBSD.org> | 2004-08-08 05:50:10 +0000 |
commit | 03cc09682ba372ec8e35d35378cd1c083a7da553 (patch) | |
tree | 347ff888c2045d99c708ebb1540bf8a514cf38eb /usr.bin/tar | |
parent | 86f0a3c9714e65b3d1ebb7094277a133b819f3bc (diff) | |
download | FreeBSD-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.c | 3 | ||||
-rw-r--r-- | usr.bin/tar/bsdtar.h | 4 | ||||
-rw-r--r-- | usr.bin/tar/read.c | 5 | ||||
-rw-r--r-- | usr.bin/tar/util.c | 58 | ||||
-rw-r--r-- | usr.bin/tar/write.c | 81 |
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; |