mirror of
https://git.FreeBSD.org/src.git
synced 2025-01-20 15:43:16 +00:00
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:
parent
2de562f7a1
commit
991f1d87bb
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=133310
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 &&
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user