Rename C=dir to -C dir, which is what people expect.
This requires some non-trivial surgery to the options parsing. While here, let people who only have getopt() access long options through the -W longopt=value convention.
This commit is contained in:
parent
f05b781e74
commit
87393e623e
@ -136,23 +136,10 @@ As a rule, this argument is only needed when reading from or writing
|
||||
to tape drives, and usually not even then as the default block size of
|
||||
20 records (10240 bytes) is very common.
|
||||
.It Fl C Ar directory
|
||||
Change directories.
|
||||
The directory is changed after the archive
|
||||
is opened, but before any entries are extracted or written.
|
||||
(In particular, it does not affect the interpretation of the
|
||||
.Fl f
|
||||
option.)
|
||||
In create mode, note that
|
||||
.Fl C
|
||||
options are all processed before any files are read.
|
||||
To change directories between files, use
|
||||
.Cm C= Ns Pa dir .
|
||||
.It Cm C= Ns Pa dir
|
||||
(c and r mode only)
|
||||
Change to the specified directory before adding the following files.
|
||||
(Note that this is not an option in the sense of
|
||||
.Xr getopt 3 ,
|
||||
and is therefore processed as the files are processed.)
|
||||
In c and r mode, this changes the directory before adding
|
||||
the following files.
|
||||
In x mode, change directories after opening the archive
|
||||
but before extracting entries from the archive.
|
||||
.It Fl -exclude Ar pattern
|
||||
Do not process files or directories that match the
|
||||
specified pattern.
|
||||
@ -358,11 +345,11 @@ the default tape drive:
|
||||
.Pp
|
||||
In create mode, the list of files and directories to be archived
|
||||
can also include directory change instructions of the form
|
||||
.Cm C= Ns Pa foo/baz
|
||||
.Cm -C Ns Pa foo/baz
|
||||
and archive inclusions of the form
|
||||
.Cm @ Ns Pa archive-file .
|
||||
For example, the command line
|
||||
.Dl Nm Fl c Fl f Pa new.tar Pa foo1 Cm @ Ns Pa old.tgz Cm C= Ns Pa /tmp Pa foo2
|
||||
.Dl Nm Fl c Fl f Pa new.tar Pa foo1 Cm @ Ns Pa old.tgz Cm -C Ns Pa /tmp Pa foo2
|
||||
will create a new archive
|
||||
.Pa new.tar .
|
||||
.Nm
|
||||
@ -560,14 +547,7 @@ options do not.
|
||||
.Pp
|
||||
The
|
||||
.Fl C Pa dir
|
||||
option differs from historic implementations.
|
||||
In order to provide the range of historic behaviors while
|
||||
retaining some consistency with POSIX option-processing
|
||||
conventions, this implementation uses
|
||||
.Fl C Pa dir
|
||||
and
|
||||
.Cm C= Ns Pa dir
|
||||
for the two distinct behaviors.
|
||||
option may differ from historic implementations.
|
||||
.Pp
|
||||
All archive output is written in correctly-sized blocks, even
|
||||
if the output is being compressed.
|
||||
@ -611,11 +591,6 @@ mode with the
|
||||
extension.
|
||||
.Pp
|
||||
To archive a file called
|
||||
.Pa C=foo ,
|
||||
you must specify it as
|
||||
.Pa ./C=foo
|
||||
on the command line.
|
||||
Similarly, to archive a file called
|
||||
.Pa @foo
|
||||
or
|
||||
.Pa -foo
|
||||
|
@ -37,6 +37,15 @@ __FBSDID("$FreeBSD$");
|
||||
#include <fnmatch.h>
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
#include <getopt.h>
|
||||
#else
|
||||
struct option {
|
||||
const char *name;
|
||||
int has_arg;
|
||||
int *flag;
|
||||
int val;
|
||||
};
|
||||
#define no_argument 0
|
||||
#define required_argument 1
|
||||
#endif
|
||||
#include <locale.h>
|
||||
#include <pwd.h>
|
||||
@ -48,6 +57,8 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include "bsdtar.h"
|
||||
|
||||
static int bsdtar_getopt(struct bsdtar *, const char *optstring,
|
||||
const struct option **poption);
|
||||
static void long_help(struct bsdtar *);
|
||||
static void only_mode(struct bsdtar *, char mode, const char *opt,
|
||||
const char *valid);
|
||||
@ -55,21 +66,29 @@ static char ** rewrite_argv(struct bsdtar *,
|
||||
int *argc, char ** src_argv,
|
||||
const char *optstring);
|
||||
|
||||
const char *tar_opts = "Bb:C:cF:f:HhjkLlmnOoPprtT:UuvwX:xyZz";
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
/*
|
||||
* These long options are deliberately not documented. They are
|
||||
* provided only to make life easier for people using GNU tar. The
|
||||
* only long options documented in the manual page are the ones with
|
||||
* no corresponding short option (currently, --exclude, --nodump, and
|
||||
* --fast-read).
|
||||
* The leading '+' here forces the GNU version of getopt() (as well as
|
||||
* both the GNU and BSD versions of getopt_long) to stop at the first
|
||||
* non-option. Otherwise, GNU getopt() permutes the arguments and
|
||||
* screws up -C processing.
|
||||
*/
|
||||
const char *tar_opts = "+Bb:C:cF:f:HhjkLlmnOoPprtT:UuvW:wX:xyZz";
|
||||
|
||||
/*
|
||||
* Most of these long options are deliberately not documented. They
|
||||
* are provided only to make life easier for people who also use GNU tar.
|
||||
* The only long options documented in the manual page are the ones
|
||||
* with no corresponding short option, such as --exclude, --nodump,
|
||||
* and --fast-read.
|
||||
*
|
||||
* XXX TODO: Provide short options for --exclude, --nodump and --fast-read
|
||||
* so that bsdtar is usable on systems that do not have (or do not want
|
||||
* to use) getopt_long().
|
||||
* On systems that lack getopt_long, long options can be specified
|
||||
* using -W longopt and -W longopt=value, e.g. "-W nodump" is the same
|
||||
* as "--nodump" and "-W exclude=pattern" is the same as "--exclude
|
||||
* pattern". This does not rely the GNU getopt() "W;" extension, so
|
||||
* should work correctly on any system with a POSIX-compliant getopt().
|
||||
*/
|
||||
|
||||
/* Fake short equivalents for long options that otherwise lack them. */
|
||||
#define OPTION_EXCLUDE 1
|
||||
#define OPTION_FAST_READ 2
|
||||
#define OPTION_NODUMP 3
|
||||
@ -119,17 +138,17 @@ const struct option tar_longopts[] = {
|
||||
{ "verbose", no_argument, NULL, 'v' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
#endif
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
struct bsdtar *bsdtar, bsdtar_storage;
|
||||
const struct option *option;
|
||||
struct passwd *pwent;
|
||||
int opt;
|
||||
char mode;
|
||||
char buff[16];
|
||||
char possible_help_request;
|
||||
char buff[16];
|
||||
|
||||
/*
|
||||
* Use a pointer for consistency, but stack-allocated storage
|
||||
@ -177,12 +196,7 @@ main(int argc, char **argv)
|
||||
bsdtar->argc = argc;
|
||||
|
||||
/* Process all remaining arguments now. */
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
while ((opt = getopt_long(bsdtar->argc, bsdtar->argv,
|
||||
tar_opts, tar_longopts, NULL)) != -1) {
|
||||
#else
|
||||
while ((opt = getopt(bsdtar->argc, bsdtar->argv, tar_opts)) != -1) {
|
||||
#endif
|
||||
while ((opt = bsdtar_getopt(bsdtar, tar_opts, &option)) != -1) {
|
||||
switch (opt) {
|
||||
case 'B': /* GNU tar */
|
||||
/*
|
||||
@ -194,7 +208,7 @@ main(int argc, char **argv)
|
||||
bsdtar->bytes_per_block = 512 * atoi(optarg);
|
||||
break;
|
||||
case 'C': /* GNU tar */
|
||||
/* XXX How should multiple -C options be handled? */
|
||||
/* Defer first -C until after -f is opened. */
|
||||
bsdtar->start_dir = optarg;
|
||||
break;
|
||||
case 'c': /* SUSv2 */
|
||||
@ -204,11 +218,9 @@ main(int argc, char **argv)
|
||||
opt, mode);
|
||||
mode = opt;
|
||||
break;
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
case OPTION_EXCLUDE: /* GNU tar */
|
||||
exclude(bsdtar, optarg);
|
||||
break;
|
||||
#endif
|
||||
case 'F':
|
||||
bsdtar->create_format = optarg;
|
||||
break;
|
||||
@ -217,11 +229,9 @@ main(int argc, char **argv)
|
||||
if (strcmp(bsdtar->filename, "-") == 0)
|
||||
bsdtar->filename = NULL;
|
||||
break;
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
case OPTION_FAST_READ: /* GNU tar */
|
||||
bsdtar->option_fast_read = 1;
|
||||
break;
|
||||
#endif
|
||||
case 'H': /* BSD convention */
|
||||
bsdtar->symlink_mode = 'H';
|
||||
break;
|
||||
@ -230,17 +240,13 @@ main(int argc, char **argv)
|
||||
/* Hack: -h by itself is the "help" command. */
|
||||
possible_help_request = 1;
|
||||
break;
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
case OPTION_HELP:
|
||||
long_help(bsdtar);
|
||||
exit(0);
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
case OPTION_INCLUDE:
|
||||
include(bsdtar, optarg);
|
||||
break;
|
||||
#endif
|
||||
case 'j': /* GNU tar */
|
||||
if (bsdtar->create_compression != '\0')
|
||||
bsdtar_errc(bsdtar, 1, 0,
|
||||
@ -263,12 +269,9 @@ main(int argc, char **argv)
|
||||
case 'n': /* GNU tar */
|
||||
bsdtar->option_no_subdirs = 1;
|
||||
break;
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
case OPTION_NODUMP: /* star */
|
||||
bsdtar->option_honor_nodump = 1;
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
case OPTION_NO_SAME_PERMISSIONS: /* GNU tar */
|
||||
/*
|
||||
* This is always the default in FreeBSD's
|
||||
@ -277,18 +280,15 @@ main(int argc, char **argv)
|
||||
* command-line option as a no-op.
|
||||
*/
|
||||
break;
|
||||
#endif
|
||||
case 'O': /* GNU tar */
|
||||
bsdtar->option_stdout = 1;
|
||||
break;
|
||||
case 'o': /* SUSv2; note that GNU -o conflicts */
|
||||
bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER;
|
||||
break;
|
||||
#if HAVE_GETOPT_LONG
|
||||
case OPTION_ONE_FILE_SYSTEM: /* -l in GNU tar */
|
||||
bsdtar->option_dont_traverse_mounts = 1;
|
||||
break;
|
||||
#endif
|
||||
#if 0
|
||||
/*
|
||||
* The common BSD -P option is not necessary, since
|
||||
@ -472,7 +472,7 @@ only_mode(struct bsdtar *bsdtar, char mode,
|
||||
* Convert traditional tar arguments into new-style.
|
||||
* For example,
|
||||
* tar tvfb file.tar 32 --exclude FOO
|
||||
* must be converted to
|
||||
* will be converted to
|
||||
* tar -t -v -f file.tar -b 32 --exclude FOO
|
||||
*
|
||||
* This requires building a new argv array. The initial bundled word
|
||||
@ -566,6 +566,8 @@ static const char *long_help_msg[] = {
|
||||
" -F {ustar|pax|cpio|shar} Select archive format\n",
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
" --exclude <pattern> Skip files that match pattern\n",
|
||||
#else
|
||||
" -W exclude=<pattern> Skip files that match pattern\n",
|
||||
#endif
|
||||
" C=<dir> Change to <dir> before processing remaining files\n",
|
||||
" @<archive> Add entries from <archive> to output\n",
|
||||
@ -624,3 +626,70 @@ long_help(struct bsdtar *bsdtar)
|
||||
}
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
static int
|
||||
bsdtar_getopt(struct bsdtar *bsdtar, const char *optstring,
|
||||
const struct option **poption)
|
||||
{
|
||||
char *p, *q;
|
||||
const struct option *option;
|
||||
int opt;
|
||||
int option_index;
|
||||
size_t option_length;
|
||||
|
||||
option_index = -1;
|
||||
*poption = NULL;
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
opt = getopt_long(bsdtar->argc, bsdtar->argv, optstring,
|
||||
tar_longopts, &option_index);
|
||||
if (option_index > -1)
|
||||
*poption = tar_longopts + option_index;
|
||||
#else
|
||||
opt = getopt(bsdtar->argc, bsdtar->argv, optstring);
|
||||
#endif
|
||||
|
||||
/* Support long options through -W longopt=value */
|
||||
if (opt == 'W') {
|
||||
p = optarg;
|
||||
q = strchr(optarg, '=');
|
||||
if (q != NULL) {
|
||||
option_length = q - p;
|
||||
optarg = q + 1;
|
||||
} else {
|
||||
option_length = strlen(p);
|
||||
optarg = NULL;
|
||||
}
|
||||
option = tar_longopts;
|
||||
while (option->name != NULL &&
|
||||
(strlen(option->name) < option_length ||
|
||||
strncmp(p, option->name, option_length) != 0 )) {
|
||||
option++;
|
||||
}
|
||||
|
||||
if (option->name != NULL) {
|
||||
*poption = option;
|
||||
opt = option->val;
|
||||
|
||||
/* Check if there's another match. */
|
||||
option++;
|
||||
while (option->name != NULL &&
|
||||
(strlen(option->name) < option_length ||
|
||||
strncmp(p, option->name, option_length) != 0)) {
|
||||
option++;
|
||||
}
|
||||
if (option->name != NULL)
|
||||
bsdtar_errc(bsdtar, 1, 0,
|
||||
"Ambiguous option %s "
|
||||
"(matches both %s and %s)",
|
||||
p, (*poption)->name, option->name);
|
||||
|
||||
} else {
|
||||
opt = '?';
|
||||
/* TODO: Set up a fake 'struct option' for
|
||||
* error reporting... ? ? ? */
|
||||
}
|
||||
}
|
||||
|
||||
return (opt);
|
||||
}
|
||||
|
@ -358,19 +358,26 @@ write_archive(struct archive *a, struct bsdtar *bsdtar)
|
||||
|
||||
while (*bsdtar->argv) {
|
||||
arg = *bsdtar->argv;
|
||||
if (arg[0] == 'C' && arg[1] == '=') {
|
||||
if (arg[0] == '-' && arg[1] == 'C') {
|
||||
arg += 2;
|
||||
if (*arg == '\0') {
|
||||
bsdtar->argv++;
|
||||
arg = *bsdtar->argv;
|
||||
if (arg == NULL)
|
||||
bsdtar_errc(bsdtar, 1, 0,
|
||||
"Missing argument for -C");
|
||||
}
|
||||
|
||||
/*-
|
||||
* The logic here for C=<dir> attempts to avoid
|
||||
* The logic here for -C <dir> attempts to avoid
|
||||
* chdir() as long as possible. For example:
|
||||
* "C=/foo C=/bar file"
|
||||
* "-C /foo -C /bar file"
|
||||
* needs chdir("/bar") but not chdir("/foo")
|
||||
* "C=/foo C=bar file"
|
||||
* "-C /foo -C bar file"
|
||||
* needs chdir("/foo/bar")
|
||||
* "C=/foo C=bar /file1"
|
||||
* "-C /foo -C bar /file1"
|
||||
* does not need chdir()
|
||||
* "C=/foo C=bar /file1 file2"
|
||||
* "-C /foo -C bar /file1 file2"
|
||||
* needs chdir("/foo/bar") before file2
|
||||
*
|
||||
* The only correct way to handle this is to
|
||||
@ -380,17 +387,17 @@ write_archive(struct archive *a, struct bsdtar *bsdtar)
|
||||
*
|
||||
* I went to all this work so that programs
|
||||
* that build tar command lines don't have to
|
||||
* worry about C= with non-existent
|
||||
* worry about -C with non-existent
|
||||
* directories; such requests will only fail
|
||||
* if the directory must be accessed.
|
||||
*/
|
||||
if (pending_dir && *arg == '/') {
|
||||
/* The C=/foo C=/bar case; dump first one. */
|
||||
/* 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 */
|
||||
/* The -C /foo -C bar case; concatenate */
|
||||
char *old_pending = pending_dir;
|
||||
int old_len = strlen(old_pending);
|
||||
|
||||
@ -409,8 +416,7 @@ write_archive(struct archive *a, struct bsdtar *bsdtar)
|
||||
} else {
|
||||
if (pending_dir &&
|
||||
(*arg != '/' || (*arg == '@' && arg[1] != '/'))) {
|
||||
/* Handle a deferred -C request, see
|
||||
* comments above. */
|
||||
/* Handle a deferred -C */
|
||||
if (chdir(pending_dir))
|
||||
bsdtar_errc(bsdtar, 1, 0,
|
||||
"could not chdir to '%s'\n",
|
||||
|
Loading…
Reference in New Issue
Block a user