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
This commit is contained in:
Tim Kientzle 2004-08-08 05:50:10 +00:00
parent 2de562f7a1
commit 991f1d87bb
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=133310
5 changed files with 66 additions and 85 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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 &&

View File

@ -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;
}

View File

@ -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;