New command-line parser for bsdtar.
This replaces the getopt()/getopt_long() wrapper, the old-style argument rewriter and the associated configuration glue with a more straightforward custom command parser. In particular, this ensures that bsdtar will have consistent option parsing on every platform, regardless of whether the platform supports getopt_long(). MFC after: 30 days
This commit is contained in:
parent
ddff5a26cd
commit
c1676b587b
@ -2,7 +2,7 @@
|
||||
|
||||
PROG= bsdtar
|
||||
BSDTAR_VERSION_STRING=2.5.5
|
||||
SRCS= bsdtar.c getdate.y matching.c read.c siginfo.c subst.c tree.c util.c write.c
|
||||
SRCS= bsdtar.c cmdline.c getdate.y matching.c read.c siginfo.c subst.c tree.c util.c write.c
|
||||
WARNS?= 5
|
||||
DPADD= ${LIBARCHIVE} ${LIBBZ2} ${LIBZ}
|
||||
LDADD= -larchive -lbz2 -lz
|
||||
@ -11,6 +11,7 @@ CFLAGS+= -DPLATFORM_CONFIG_H=\"config_freebsd.h\"
|
||||
CFLAGS+= -I${.CURDIR}
|
||||
SYMLINKS= bsdtar ${BINDIR}/tar
|
||||
MLINKS= bsdtar.1 tar.1
|
||||
DEBUG_FLAGS=-g
|
||||
|
||||
.PHONY: check test
|
||||
check test: $(PROG) bsdtar.1.gz
|
||||
|
@ -144,21 +144,21 @@ 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 -check-links ( Fl W Cm check-links )
|
||||
.It Fl -check-links
|
||||
(c and r modes only)
|
||||
Issue a warning message unless all links to each file are archived.
|
||||
.It Fl -chroot ( Fl W Cm chroot )
|
||||
.It Fl -chroot
|
||||
(x mode only)
|
||||
.Fn chroot
|
||||
to the current directory after processing any
|
||||
.Fl C
|
||||
options and before extracting any files.
|
||||
.It Fl -exclude Ar pattern ( Fl W Cm exclude Ns = Ns Ar pattern )
|
||||
.It Fl -exclude Ar pattern
|
||||
Do not process files or directories that match the
|
||||
specified pattern.
|
||||
Note that exclusions take precedence over patterns or filenames
|
||||
specified on the command line.
|
||||
.It Fl -format Ar format ( Fl W Cm format Ns = Ns Ar format )
|
||||
.It Fl -format Ar format
|
||||
(c, r, u mode only)
|
||||
Use the specified format for the created archive.
|
||||
Supported formats include
|
||||
@ -193,7 +193,7 @@ Synonym for
|
||||
.It Fl I
|
||||
Synonym for
|
||||
.Fl T .
|
||||
.It Fl -include Ar pattern ( Fl W Cm include Ns = Ns Ar pattern )
|
||||
.It Fl -include Ar pattern
|
||||
Process only files or directories that match the specified pattern.
|
||||
Note that exclusions specified with
|
||||
.Fl -exclude
|
||||
@ -225,7 +225,7 @@ automatically when reading archives.
|
||||
Do not overwrite existing files.
|
||||
In particular, if a file appears more than once in an archive,
|
||||
later copies will not overwrite earlier copies.
|
||||
.It Fl -keep-newer-files ( Fl W Cm keep-newer-files )
|
||||
.It Fl -keep-newer-files
|
||||
(x mode only)
|
||||
Do not overwrite existing files that are newer than the
|
||||
versions appearing in the archive being extracted.
|
||||
@ -245,28 +245,28 @@ By default, the modification time is set to the time stored in the archive.
|
||||
.It Fl n
|
||||
(c, r, u modes only)
|
||||
Do not recursively archive the contents of directories.
|
||||
.It Fl -newer Ar date ( Fl W Cm newer Ns = Ns Ar date )
|
||||
.It Fl -newer Ar date
|
||||
(c, r, u modes only)
|
||||
Only include files and directories newer than the specified date.
|
||||
This compares ctime entries.
|
||||
.It Fl -newer-mtime Ar date ( Fl W Cm newer-mtime Ns = Ns Ar date )
|
||||
.It Fl -newer-mtime Ar date
|
||||
(c, r, u modes only)
|
||||
Like
|
||||
.Fl -newer ,
|
||||
except it compares mtime entries instead of ctime entries.
|
||||
.It Fl -newer-than Pa file ( Fl W Cm newer-than Ns = Ns Pa file )
|
||||
.It Fl -newer-than Pa file
|
||||
(c, r, u modes only)
|
||||
Only include files and directories newer than the specified file.
|
||||
This compares ctime entries.
|
||||
.It Fl -newer-mtime-than Pa file ( Fl W Cm newer-mtime-than Ns = Ns Pa file )
|
||||
.It Fl -newer-mtime-than Pa file
|
||||
(c, r, u modes only)
|
||||
Like
|
||||
.Fl -newer-than ,
|
||||
except it compares mtime entries instead of ctime entries.
|
||||
.It Fl -nodump ( Fl W Cm nodump )
|
||||
.It Fl -nodump
|
||||
(c and r modes only)
|
||||
Honor the nodump file flag by skipping this file.
|
||||
.It Fl -null ( Fl W Cm null )
|
||||
.It Fl -null
|
||||
(use with
|
||||
.Fl I ,
|
||||
.Fl T ,
|
||||
@ -302,7 +302,7 @@ the archive will be discarded.
|
||||
(c, r, u mode)
|
||||
A synonym for
|
||||
.Fl -format Ar ustar
|
||||
.It Fl -one-file-system ( Fl W Cm one-file-system )
|
||||
.It Fl -one-file-system
|
||||
(c, r, and u modes)
|
||||
Do not cross mount points.
|
||||
.It Fl P
|
||||
@ -345,8 +345,8 @@ Extract files as sparse files.
|
||||
For every block on disk, check first if it contains only NULL bytes and seek
|
||||
over it otherwise.
|
||||
This works similiar to the conv=sparse option of dd.
|
||||
.It Fl -strip-components Ar count ( Fl W Cm strip-components Ns = Ns Ar count )
|
||||
(x and t mode only)
|
||||
.It Fl -strip-components Ar count
|
||||
(x mode only)
|
||||
Remove the specified number of leading path elements.
|
||||
Pathnames with fewer elements will be silently skipped.
|
||||
Note that the pathname is edited after checking inclusion/exclusion patterns
|
||||
@ -418,16 +418,6 @@ Print version of
|
||||
and
|
||||
.Nm libarchive ,
|
||||
and exit.
|
||||
.It Fl W Ar longopt=value
|
||||
Long options (preceded by
|
||||
.Fl - )
|
||||
are only supported directly on systems that have the
|
||||
.Xr getopt_long 3
|
||||
function.
|
||||
The
|
||||
.Fl W
|
||||
option can be used to access long options on systems that
|
||||
do not support this function.
|
||||
.It Fl w
|
||||
Ask for confirmation for every action.
|
||||
.It Fl X Ar filename
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2007 Tim Kientzle
|
||||
* Copyright (c) 2003-2008 Tim Kientzle
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -38,18 +38,6 @@ __FBSDID("$FreeBSD$");
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#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
|
||||
#ifdef HAVE_LANGINFO_H
|
||||
#include <langinfo.h>
|
||||
#endif
|
||||
@ -78,14 +66,6 @@ struct option {
|
||||
|
||||
#include "bsdtar.h"
|
||||
|
||||
#if !HAVE_DECL_OPTARG
|
||||
extern int optarg;
|
||||
#endif
|
||||
|
||||
#if !HAVE_DECL_OPTIND
|
||||
extern int optind;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Per POSIX.1-1988, tar defaults to reading/writing archives to/from
|
||||
* the default tape device for the system. Pick something reasonable here.
|
||||
@ -101,133 +81,12 @@ extern int optind;
|
||||
/* External function to parse a date/time string (from getdate.y) */
|
||||
time_t get_date(const char *);
|
||||
|
||||
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 *, const char *opt,
|
||||
const char *valid);
|
||||
static char ** rewrite_argv(struct bsdtar *,
|
||||
int *argc, char ** src_argv,
|
||||
const char *optstring);
|
||||
static void set_mode(struct bsdtar *, char opt);
|
||||
static void version(void);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
static const char *tar_opts = "+Bb:C:cf:HhI:jkLlmnOoPpqrts:ST: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.
|
||||
*
|
||||
* 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. */
|
||||
enum {
|
||||
OPTION_CHECK_LINKS = 1,
|
||||
OPTION_CHROOT,
|
||||
OPTION_EXCLUDE,
|
||||
OPTION_FORMAT,
|
||||
OPTION_HELP,
|
||||
OPTION_INCLUDE,
|
||||
OPTION_KEEP_NEWER_FILES,
|
||||
OPTION_NEWER_CTIME,
|
||||
OPTION_NEWER_CTIME_THAN,
|
||||
OPTION_NEWER_MTIME,
|
||||
OPTION_NEWER_MTIME_THAN,
|
||||
OPTION_NODUMP,
|
||||
OPTION_NO_SAME_OWNER,
|
||||
OPTION_NO_SAME_PERMISSIONS,
|
||||
OPTION_NULL,
|
||||
OPTION_NUMERIC_OWNER,
|
||||
OPTION_ONE_FILE_SYSTEM,
|
||||
OPTION_POSIX,
|
||||
OPTION_STRIP_COMPONENTS,
|
||||
OPTION_TOTALS,
|
||||
OPTION_USE_COMPRESS_PROGRAM,
|
||||
OPTION_VERSION
|
||||
};
|
||||
|
||||
/*
|
||||
* If you add anything, be very careful to keep this list properly
|
||||
* sorted, as the -W logic relies on it.
|
||||
*/
|
||||
static const struct option tar_longopts[] = {
|
||||
{ "absolute-paths", no_argument, NULL, 'P' },
|
||||
{ "append", no_argument, NULL, 'r' },
|
||||
{ "block-size", required_argument, NULL, 'b' },
|
||||
{ "bunzip2", no_argument, NULL, 'j' },
|
||||
{ "bzip", no_argument, NULL, 'j' },
|
||||
{ "bzip2", no_argument, NULL, 'j' },
|
||||
{ "cd", required_argument, NULL, 'C' },
|
||||
{ "check-links", no_argument, NULL, OPTION_CHECK_LINKS },
|
||||
{ "chroot", no_argument, NULL, OPTION_CHROOT },
|
||||
{ "compress", no_argument, NULL, 'Z' },
|
||||
{ "confirmation", no_argument, NULL, 'w' },
|
||||
{ "create", no_argument, NULL, 'c' },
|
||||
{ "dereference", no_argument, NULL, 'L' },
|
||||
{ "directory", required_argument, NULL, 'C' },
|
||||
{ "exclude", required_argument, NULL, OPTION_EXCLUDE },
|
||||
{ "exclude-from", required_argument, NULL, 'X' },
|
||||
{ "extract", no_argument, NULL, 'x' },
|
||||
{ "fast-read", no_argument, NULL, 'q' },
|
||||
{ "file", required_argument, NULL, 'f' },
|
||||
{ "files-from", required_argument, NULL, 'T' },
|
||||
{ "format", required_argument, NULL, OPTION_FORMAT },
|
||||
{ "gunzip", no_argument, NULL, 'z' },
|
||||
{ "gzip", no_argument, NULL, 'z' },
|
||||
{ "help", no_argument, NULL, OPTION_HELP },
|
||||
{ "include", required_argument, NULL, OPTION_INCLUDE },
|
||||
{ "interactive", no_argument, NULL, 'w' },
|
||||
{ "insecure", no_argument, NULL, 'P' },
|
||||
{ "keep-newer-files", no_argument, NULL, OPTION_KEEP_NEWER_FILES },
|
||||
{ "keep-old-files", no_argument, NULL, 'k' },
|
||||
{ "list", no_argument, NULL, 't' },
|
||||
{ "modification-time", no_argument, NULL, 'm' },
|
||||
{ "newer", required_argument, NULL, OPTION_NEWER_CTIME },
|
||||
{ "newer-ctime", required_argument, NULL, OPTION_NEWER_CTIME },
|
||||
{ "newer-ctime-than", required_argument, NULL, OPTION_NEWER_CTIME_THAN },
|
||||
{ "newer-mtime", required_argument, NULL, OPTION_NEWER_MTIME },
|
||||
{ "newer-mtime-than", required_argument, NULL, OPTION_NEWER_MTIME_THAN },
|
||||
{ "newer-than", required_argument, NULL, OPTION_NEWER_CTIME_THAN },
|
||||
{ "nodump", no_argument, NULL, OPTION_NODUMP },
|
||||
{ "norecurse", no_argument, NULL, 'n' },
|
||||
{ "no-recursion", no_argument, NULL, 'n' },
|
||||
{ "no-same-owner", no_argument, NULL, OPTION_NO_SAME_OWNER },
|
||||
{ "no-same-permissions",no_argument, NULL, OPTION_NO_SAME_PERMISSIONS },
|
||||
{ "null", no_argument, NULL, OPTION_NULL },
|
||||
{ "numeric-owner", no_argument, NULL, OPTION_NUMERIC_OWNER },
|
||||
{ "one-file-system", no_argument, NULL, OPTION_ONE_FILE_SYSTEM },
|
||||
{ "posix", no_argument, NULL, OPTION_POSIX },
|
||||
{ "preserve-permissions", no_argument, NULL, 'p' },
|
||||
{ "read-full-blocks", no_argument, NULL, 'B' },
|
||||
{ "same-permissions", no_argument, NULL, 'p' },
|
||||
{ "strip-components", required_argument, NULL, OPTION_STRIP_COMPONENTS },
|
||||
{ "to-stdout", no_argument, NULL, 'O' },
|
||||
{ "totals", no_argument, NULL, OPTION_TOTALS },
|
||||
{ "uncompress", no_argument, NULL, 'Z' },
|
||||
{ "unlink", no_argument, NULL, 'U' },
|
||||
{ "unlink-first", no_argument, NULL, 'U' },
|
||||
{ "update", no_argument, NULL, 'u' },
|
||||
{ "use-compress-program",
|
||||
required_argument, NULL, OPTION_USE_COMPRESS_PROGRAM },
|
||||
{ "verbose", no_argument, NULL, 'v' },
|
||||
{ "version", no_argument, NULL, OPTION_VERSION },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
/* A basic set of security flags to request from libarchive. */
|
||||
#define SECURITY \
|
||||
(ARCHIVE_EXTRACT_SECURE_SYMLINKS \
|
||||
@ -237,7 +96,6 @@ int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
struct bsdtar *bsdtar, bsdtar_storage;
|
||||
const struct option *option;
|
||||
int opt, t;
|
||||
char option_o;
|
||||
char possible_help_request;
|
||||
@ -295,33 +153,29 @@ main(int argc, char **argv)
|
||||
bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS;
|
||||
}
|
||||
|
||||
/* Rewrite traditional-style tar arguments, if used. */
|
||||
argv = rewrite_argv(bsdtar, &argc, argv, tar_opts);
|
||||
|
||||
bsdtar->argv = argv;
|
||||
bsdtar->argc = argc;
|
||||
|
||||
/* Process all remaining arguments now. */
|
||||
/*
|
||||
* Comments following each option indicate where that option
|
||||
* originated: SUSv2, POSIX, GNU tar, star, etc. If there's
|
||||
* no such comment, then I don't know of anyone else who
|
||||
* implements that option.
|
||||
*/
|
||||
while ((opt = bsdtar_getopt(bsdtar, tar_opts, &option)) != -1) {
|
||||
while ((opt = bsdtar_getopt(bsdtar)) != -1) {
|
||||
switch (opt) {
|
||||
case 'B': /* GNU tar */
|
||||
/* libarchive doesn't need this; just ignore it. */
|
||||
break;
|
||||
case 'b': /* SUSv2 */
|
||||
t = atoi(optarg);
|
||||
t = atoi(bsdtar->optarg);
|
||||
if (t <= 0 || t > 1024)
|
||||
bsdtar_errc(bsdtar, 1, 0,
|
||||
"Argument to -b is out of range (1..1024)");
|
||||
bsdtar->bytes_per_block = 512 * t;
|
||||
break;
|
||||
case 'C': /* GNU tar */
|
||||
set_chdir(bsdtar, optarg);
|
||||
set_chdir(bsdtar, bsdtar->optarg);
|
||||
break;
|
||||
case 'c': /* SUSv2 */
|
||||
set_mode(bsdtar, opt);
|
||||
@ -333,15 +187,15 @@ main(int argc, char **argv)
|
||||
bsdtar->option_chroot = 1;
|
||||
break;
|
||||
case OPTION_EXCLUDE: /* GNU tar */
|
||||
if (exclude(bsdtar, optarg))
|
||||
if (exclude(bsdtar, bsdtar->optarg))
|
||||
bsdtar_errc(bsdtar, 1, 0,
|
||||
"Couldn't exclude %s\n", optarg);
|
||||
"Couldn't exclude %s\n", bsdtar->optarg);
|
||||
break;
|
||||
case OPTION_FORMAT: /* GNU tar, others */
|
||||
bsdtar->create_format = optarg;
|
||||
bsdtar->create_format = bsdtar->optarg;
|
||||
break;
|
||||
case 'f': /* SUSv2 */
|
||||
bsdtar->filename = optarg;
|
||||
bsdtar->filename = bsdtar->optarg;
|
||||
if (strcmp(bsdtar->filename, "-") == 0)
|
||||
bsdtar->filename = NULL;
|
||||
break;
|
||||
@ -368,7 +222,7 @@ main(int argc, char **argv)
|
||||
* permissions without having to create those
|
||||
* permissions on disk.
|
||||
*/
|
||||
bsdtar->names_from_file = optarg;
|
||||
bsdtar->names_from_file = bsdtar->optarg;
|
||||
break;
|
||||
case OPTION_INCLUDE:
|
||||
/*
|
||||
@ -376,10 +230,10 @@ main(int argc, char **argv)
|
||||
* noone else needs this to filter entries
|
||||
* when transforming archives.
|
||||
*/
|
||||
if (include(bsdtar, optarg))
|
||||
if (include(bsdtar, bsdtar->optarg))
|
||||
bsdtar_errc(bsdtar, 1, 0,
|
||||
"Failed to add %s to inclusion list",
|
||||
optarg);
|
||||
bsdtar->optarg);
|
||||
break;
|
||||
case 'j': /* GNU tar */
|
||||
#if HAVE_LIBBZ2
|
||||
@ -389,7 +243,8 @@ main(int argc, char **argv)
|
||||
bsdtar->create_compression);
|
||||
bsdtar->create_compression = opt;
|
||||
#else
|
||||
bsdtar_warnc(bsdtar, 0, "-j compression not supported by this version of bsdtar");
|
||||
bsdtar_warnc(bsdtar, 0,
|
||||
"bzip2 compression not supported by this version of bsdtar");
|
||||
usage(bsdtar);
|
||||
#endif
|
||||
break;
|
||||
@ -420,28 +275,28 @@ main(int argc, char **argv)
|
||||
* TODO: Add corresponding "older" options to reverse these.
|
||||
*/
|
||||
case OPTION_NEWER_CTIME: /* GNU tar */
|
||||
bsdtar->newer_ctime_sec = get_date(optarg);
|
||||
bsdtar->newer_ctime_sec = get_date(bsdtar->optarg);
|
||||
break;
|
||||
case OPTION_NEWER_CTIME_THAN:
|
||||
{
|
||||
struct stat st;
|
||||
if (stat(optarg, &st) != 0)
|
||||
if (stat(bsdtar->optarg, &st) != 0)
|
||||
bsdtar_errc(bsdtar, 1, 0,
|
||||
"Can't open file %s", optarg);
|
||||
"Can't open file %s", bsdtar->optarg);
|
||||
bsdtar->newer_ctime_sec = st.st_ctime;
|
||||
bsdtar->newer_ctime_nsec =
|
||||
ARCHIVE_STAT_CTIME_NANOS(&st);
|
||||
}
|
||||
break;
|
||||
case OPTION_NEWER_MTIME: /* GNU tar */
|
||||
bsdtar->newer_mtime_sec = get_date(optarg);
|
||||
bsdtar->newer_mtime_sec = get_date(bsdtar->optarg);
|
||||
break;
|
||||
case OPTION_NEWER_MTIME_THAN:
|
||||
{
|
||||
struct stat st;
|
||||
if (stat(optarg, &st) != 0)
|
||||
if (stat(bsdtar->optarg, &st) != 0)
|
||||
bsdtar_errc(bsdtar, 1, 0,
|
||||
"Can't open file %s", optarg);
|
||||
"Can't open file %s", bsdtar->optarg);
|
||||
bsdtar->newer_mtime_sec = st.st_mtime;
|
||||
bsdtar->newer_mtime_nsec =
|
||||
ARCHIVE_STAT_MTIME_NANOS(&st);
|
||||
@ -509,17 +364,18 @@ main(int argc, char **argv)
|
||||
break;
|
||||
case 's': /* NetBSD pax-as-tar */
|
||||
#if HAVE_REGEX_H
|
||||
add_substitution(bsdtar, optarg);
|
||||
add_substitution(bsdtar, bsdtar->optarg);
|
||||
#else
|
||||
bsdtar_warnc(bsdtar, 0, "-s is not supported by this version of bsdtar");
|
||||
bsdtar_warnc(bsdtar, 0,
|
||||
"-s is not supported by this version of bsdtar");
|
||||
usage(bsdtar);
|
||||
#endif
|
||||
break;
|
||||
case OPTION_STRIP_COMPONENTS: /* GNU tar 1.15 */
|
||||
bsdtar->strip_components = atoi(optarg);
|
||||
bsdtar->strip_components = atoi(bsdtar->optarg);
|
||||
break;
|
||||
case 'T': /* GNU tar */
|
||||
bsdtar->names_from_file = optarg;
|
||||
bsdtar->names_from_file = bsdtar->optarg;
|
||||
break;
|
||||
case 't': /* SUSv2 */
|
||||
set_mode(bsdtar, opt);
|
||||
@ -544,19 +400,19 @@ main(int argc, char **argv)
|
||||
#if 0
|
||||
/*
|
||||
* The -W longopt feature is handled inside of
|
||||
* bsdtar_getop(), so -W is not available here.
|
||||
* bsdtar_getopt(), so -W is not available here.
|
||||
*/
|
||||
case 'W': /* Obscure, but useful GNU convention. */
|
||||
case 'W': /* Obscure GNU convention. */
|
||||
break;
|
||||
#endif
|
||||
case 'w': /* SUSv2 */
|
||||
bsdtar->option_interactive = 1;
|
||||
break;
|
||||
case 'X': /* GNU tar */
|
||||
if (exclude_from_file(bsdtar, optarg))
|
||||
if (exclude_from_file(bsdtar, bsdtar->optarg))
|
||||
bsdtar_errc(bsdtar, 1, 0,
|
||||
"failed to process exclusions from file %s",
|
||||
optarg);
|
||||
bsdtar->optarg);
|
||||
break;
|
||||
case 'x': /* SUSv2 */
|
||||
set_mode(bsdtar, opt);
|
||||
@ -569,7 +425,8 @@ main(int argc, char **argv)
|
||||
bsdtar->create_compression);
|
||||
bsdtar->create_compression = opt;
|
||||
#else
|
||||
bsdtar_warnc(bsdtar, 0, "-y compression not supported by this version of bsdtar");
|
||||
bsdtar_warnc(bsdtar, 0,
|
||||
"bzip2 compression not supported by this version of bsdtar");
|
||||
usage(bsdtar);
|
||||
#endif
|
||||
break;
|
||||
@ -588,12 +445,13 @@ main(int argc, char **argv)
|
||||
bsdtar->create_compression);
|
||||
bsdtar->create_compression = opt;
|
||||
#else
|
||||
bsdtar_warnc(bsdtar, 0, "-z compression not supported by this version of bsdtar");
|
||||
bsdtar_warnc(bsdtar, 0,
|
||||
"gzip compression not supported by this version of bsdtar");
|
||||
usage(bsdtar);
|
||||
#endif
|
||||
break;
|
||||
case OPTION_USE_COMPRESS_PROGRAM:
|
||||
bsdtar->compress_program = optarg;
|
||||
bsdtar->compress_program = bsdtar->optarg;
|
||||
break;
|
||||
default:
|
||||
usage(bsdtar);
|
||||
@ -668,9 +526,6 @@ main(int argc, char **argv)
|
||||
if (bsdtar->strip_components != 0)
|
||||
only_mode(bsdtar, "--strip-components", "xt");
|
||||
|
||||
bsdtar->argc -= optind;
|
||||
bsdtar->argv += optind;
|
||||
|
||||
switch(bsdtar->mode) {
|
||||
case 'c':
|
||||
tar_mode_c(bsdtar);
|
||||
@ -722,72 +577,6 @@ only_mode(struct bsdtar *bsdtar, const char *opt, const char *valid_modes)
|
||||
}
|
||||
|
||||
|
||||
/*-
|
||||
* Convert traditional tar arguments into new-style.
|
||||
* For example,
|
||||
* tar tvfb file.tar 32 --exclude FOO
|
||||
* 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
|
||||
* gets expanded into a new string that looks like "-t\0-v\0-f\0-b\0".
|
||||
* The new argv array has pointers into this string intermingled with
|
||||
* pointers to the existing arguments. Arguments are moved to
|
||||
* immediately follow their options.
|
||||
*
|
||||
* The optstring argument here is the same one passed to getopt(3).
|
||||
* It is used to determine which option letters have trailing arguments.
|
||||
*/
|
||||
char **
|
||||
rewrite_argv(struct bsdtar *bsdtar, int *argc, char **src_argv,
|
||||
const char *optstring)
|
||||
{
|
||||
char **new_argv, **dest_argv;
|
||||
const char *p;
|
||||
char *src, *dest;
|
||||
|
||||
if (src_argv[0] == NULL || src_argv[1] == NULL ||
|
||||
src_argv[1][0] == '-' || src_argv[1][0] == '\0')
|
||||
return (src_argv);
|
||||
|
||||
*argc += strlen(src_argv[1]) - 1;
|
||||
new_argv = malloc((*argc + 1) * sizeof(new_argv[0]));
|
||||
if (new_argv == NULL)
|
||||
bsdtar_errc(bsdtar, 1, errno, "No Memory");
|
||||
|
||||
dest_argv = new_argv;
|
||||
*dest_argv++ = *src_argv++;
|
||||
|
||||
dest = malloc(strlen(*src_argv) * 3);
|
||||
if (dest == NULL)
|
||||
bsdtar_errc(bsdtar, 1, errno, "No memory");
|
||||
for (src = *src_argv++; *src != '\0'; src++) {
|
||||
*dest_argv++ = dest;
|
||||
*dest++ = '-';
|
||||
*dest++ = *src;
|
||||
*dest++ = '\0';
|
||||
/* If option takes an argument, insert that into the list. */
|
||||
for (p = optstring; p != NULL && *p != '\0'; p++) {
|
||||
if (*p != *src)
|
||||
continue;
|
||||
if (p[1] != ':') /* No arg required, done. */
|
||||
break;
|
||||
if (*src_argv == NULL) /* No arg available? Error. */
|
||||
bsdtar_errc(bsdtar, 1, 0,
|
||||
"Option %c requires an argument",
|
||||
*src);
|
||||
*dest_argv++ = *src_argv++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy remaining arguments, including trailing NULL. */
|
||||
while ((*dest_argv++ = *src_argv++) != NULL)
|
||||
;
|
||||
|
||||
return (new_argv);
|
||||
}
|
||||
|
||||
void
|
||||
usage(struct bsdtar *bsdtar)
|
||||
{
|
||||
@ -799,11 +588,7 @@ usage(struct bsdtar *bsdtar)
|
||||
fprintf(stderr, " List: %s -tf <archive-filename>\n", p);
|
||||
fprintf(stderr, " Extract: %s -xf <archive-filename>\n", p);
|
||||
fprintf(stderr, " Create: %s -cf <archive-filename> [filenames...]\n", p);
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
fprintf(stderr, " Help: %s --help\n", p);
|
||||
#else
|
||||
fprintf(stderr, " Help: %s -h\n", p);
|
||||
#endif
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -828,11 +613,7 @@ static const char *long_help_msg =
|
||||
" <file>, <dir> add these items to archive\n"
|
||||
" -z, -j Compress archive with gzip/bzip2\n"
|
||||
" --format {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"
|
||||
"List: %p -t [options] [<patterns>]\n"
|
||||
@ -880,80 +661,3 @@ long_help(struct bsdtar *bsdtar)
|
||||
}
|
||||
version();
|
||||
}
|
||||
|
||||
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 = (size_t)(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;
|
||||
|
||||
/* If the first match was exact, we're done. */
|
||||
if (strncmp(p, option->name, strlen(option->name)) == 0) {
|
||||
while (option->name != NULL)
|
||||
option++;
|
||||
} else {
|
||||
/* 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);
|
||||
|
||||
if ((*poption)->has_arg == required_argument
|
||||
&& optarg == NULL)
|
||||
bsdtar_errc(bsdtar, 1, 0,
|
||||
"Option \"%s\" requires argument", p);
|
||||
} else {
|
||||
opt = '?';
|
||||
/* TODO: Set up a fake 'struct option' for
|
||||
* error reporting... ? ? ? */
|
||||
}
|
||||
}
|
||||
|
||||
return (opt);
|
||||
}
|
||||
|
@ -80,6 +80,7 @@ struct bsdtar {
|
||||
const char *progname;
|
||||
int argc;
|
||||
char **argv;
|
||||
const char *optarg;
|
||||
size_t gs_width; /* For 'list_item' in read.c */
|
||||
size_t u_width; /* for 'list_item' in read.c */
|
||||
uid_t user_uid; /* UID running this program */
|
||||
@ -102,8 +103,36 @@ struct bsdtar {
|
||||
struct substitution *substitution; /* for subst.c */
|
||||
};
|
||||
|
||||
/* Fake short equivalents for long options that otherwise lack them. */
|
||||
enum {
|
||||
OPTION_CHECK_LINKS = 1,
|
||||
OPTION_CHROOT,
|
||||
OPTION_EXCLUDE,
|
||||
OPTION_FORMAT,
|
||||
OPTION_HELP,
|
||||
OPTION_INCLUDE,
|
||||
OPTION_KEEP_NEWER_FILES,
|
||||
OPTION_NEWER_CTIME,
|
||||
OPTION_NEWER_CTIME_THAN,
|
||||
OPTION_NEWER_MTIME,
|
||||
OPTION_NEWER_MTIME_THAN,
|
||||
OPTION_NODUMP,
|
||||
OPTION_NO_SAME_OWNER,
|
||||
OPTION_NO_SAME_PERMISSIONS,
|
||||
OPTION_NULL,
|
||||
OPTION_NUMERIC_OWNER,
|
||||
OPTION_ONE_FILE_SYSTEM,
|
||||
OPTION_POSIX,
|
||||
OPTION_STRIP_COMPONENTS,
|
||||
OPTION_TOTALS,
|
||||
OPTION_USE_COMPRESS_PROGRAM,
|
||||
OPTION_VERSION
|
||||
};
|
||||
|
||||
|
||||
void bsdtar_errc(struct bsdtar *, int _eval, int _code,
|
||||
const char *fmt, ...) __dead2;
|
||||
int bsdtar_getopt(struct bsdtar *);
|
||||
void bsdtar_warnc(struct bsdtar *, int _code, const char *fmt, ...);
|
||||
void cleanup_exclusions(struct bsdtar *);
|
||||
void do_chdir(struct bsdtar *);
|
||||
|
376
usr.bin/tar/cmdline.c
Normal file
376
usr.bin/tar/cmdline.c
Normal file
@ -0,0 +1,376 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2008 Tim Kientzle
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Command line parser for tar.
|
||||
*/
|
||||
|
||||
#include "bsdtar_platform.h"
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "bsdtar.h"
|
||||
|
||||
/*
|
||||
* Short options for tar. Please keep this sorted.
|
||||
*/
|
||||
static const char *short_options
|
||||
= "Bb:C:cf:HhI:jkLlmnOoPpqrSs:T:tUuvW:wX:xyZz";
|
||||
|
||||
/*
|
||||
* Long options for tar. Please keep this list sorted.
|
||||
*
|
||||
* The symbolic names for options that lack a short equivalent are
|
||||
* defined in bsdtar.h. Also note that so far I've found no need
|
||||
* to support optional arguments to long options. That would be
|
||||
* a small change to the code below.
|
||||
*/
|
||||
|
||||
static struct option {
|
||||
const char *name;
|
||||
int required; /* 1 if this option requires an argument. */
|
||||
int equivalent; /* Equivalent short option. */
|
||||
} tar_longopts[] = {
|
||||
{ "absolute-paths", 0, 'P' },
|
||||
{ "append", 0, 'r' },
|
||||
{ "block-size", 1, 'b' },
|
||||
{ "bunzip2", 0, 'j' },
|
||||
{ "bzip", 0, 'j' },
|
||||
{ "bzip2", 0, 'j' },
|
||||
{ "cd", 1, 'C' },
|
||||
{ "check-links", 0, OPTION_CHECK_LINKS },
|
||||
{ "chroot", 0, OPTION_CHROOT },
|
||||
{ "compress", 0, 'Z' },
|
||||
{ "confirmation", 0, 'w' },
|
||||
{ "create", 0, 'c' },
|
||||
{ "dereference", 0, 'L' },
|
||||
{ "directory", 1, 'C' },
|
||||
{ "exclude", 1, OPTION_EXCLUDE },
|
||||
{ "exclude-from", 1, 'X' },
|
||||
{ "extract", 0, 'x' },
|
||||
{ "fast-read", 0, 'q' },
|
||||
{ "file", 1, 'f' },
|
||||
{ "files-from", 1, 'T' },
|
||||
{ "format", 1, OPTION_FORMAT },
|
||||
{ "gunzip", 0, 'z' },
|
||||
{ "gzip", 0, 'z' },
|
||||
{ "help", 0, OPTION_HELP },
|
||||
{ "include", 1, OPTION_INCLUDE },
|
||||
{ "interactive", 0, 'w' },
|
||||
{ "insecure", 0, 'P' },
|
||||
{ "keep-newer-files", 0, OPTION_KEEP_NEWER_FILES },
|
||||
{ "keep-old-files", 0, 'k' },
|
||||
{ "list", 0, 't' },
|
||||
{ "modification-time", 0, 'm' },
|
||||
{ "newer", 1, OPTION_NEWER_CTIME },
|
||||
{ "newer-ctime", 1, OPTION_NEWER_CTIME },
|
||||
{ "newer-ctime-than", 1, OPTION_NEWER_CTIME_THAN },
|
||||
{ "newer-mtime", 1, OPTION_NEWER_MTIME },
|
||||
{ "newer-mtime-than", 1, OPTION_NEWER_MTIME_THAN },
|
||||
{ "newer-than", 1, OPTION_NEWER_CTIME_THAN },
|
||||
{ "nodump", 0, OPTION_NODUMP },
|
||||
{ "norecurse", 0, 'n' },
|
||||
{ "no-recursion", 0, 'n' },
|
||||
{ "no-same-owner", 0, OPTION_NO_SAME_OWNER },
|
||||
{ "no-same-permissions", 0, OPTION_NO_SAME_PERMISSIONS },
|
||||
{ "null", 0, OPTION_NULL },
|
||||
{ "numeric-owner", 0, OPTION_NUMERIC_OWNER },
|
||||
{ "one-file-system", 0, OPTION_ONE_FILE_SYSTEM },
|
||||
{ "posix", 0, OPTION_POSIX },
|
||||
{ "preserve-permissions", 0, 'p' },
|
||||
{ "read-full-blocks", 0, 'B' },
|
||||
{ "same-permissions", 0, 'p' },
|
||||
{ "strip-components", 1, OPTION_STRIP_COMPONENTS },
|
||||
{ "to-stdout", 0, 'O' },
|
||||
{ "totals", 0, OPTION_TOTALS },
|
||||
{ "uncompress", 0, 'Z' },
|
||||
{ "unlink", 0, 'U' },
|
||||
{ "unlink-first", 0, 'U' },
|
||||
{ "update", 0, 'u' },
|
||||
{ "use-compress-program", 1, OPTION_USE_COMPRESS_PROGRAM },
|
||||
{ "verbose", 0, 'v' },
|
||||
{ "version", 0, OPTION_VERSION },
|
||||
{ NULL, 0, 0 }
|
||||
};
|
||||
|
||||
/*
|
||||
* This getopt implementation has two key features that common
|
||||
* getopt_long() implementations lack. Apart from those, it's a
|
||||
* straightforward option parser, considerably simplified by not
|
||||
* needing to support the wealth of exotic getopt_long() features. It
|
||||
* has, of course, been shamelessly tailored for bsdtar. (If you're
|
||||
* looking for a generic getopt_long() implementation for your
|
||||
* project, I recommend Gregory Pietsch's public domain getopt_long()
|
||||
* implementation.) The two additional features are:
|
||||
*
|
||||
* Old-style tar arguments: The original tar implementation treated
|
||||
* the first argument word as a list of single-character option
|
||||
* letters. All arguments follow as separate words. For example,
|
||||
* tar xbf 32 /dev/tape
|
||||
* Here, the "xbf" is three option letters, "32" is the argument for
|
||||
* "b" and "/dev/tape" is the argument for "f". We support this usage
|
||||
* if the first command-line argument does not begin with '-'. We
|
||||
* also allow regular short and long options to follow, e.g.,
|
||||
* tar xbf 32 /dev/tape -P --format=pax
|
||||
*
|
||||
* -W long options: There's an obscure GNU convention (only rarely
|
||||
* supported even there) that allows "-W option=argument" as an
|
||||
* alternative way to support long options. This was supported in
|
||||
* early bsdtar as a way to access long options on platforms that did
|
||||
* not support getopt_long() and is preserved here for backwards
|
||||
* compatibility. (Of course, if I'd started with a custom
|
||||
* command-line parser from the beginning, I would have had normal
|
||||
* long option support on every platform so that hack wouldn't have
|
||||
* been necessary. Oh, well. Some mistakes you just have to live
|
||||
* with.)
|
||||
*
|
||||
* TODO: We should be able to use this to pull files and intermingled
|
||||
* options (such as -C) from the command line in write mode. That
|
||||
* will require a little rethinking of the argument handling in
|
||||
* bsdtar.c.
|
||||
*
|
||||
* TODO: If we want to support arbitrary command-line options from -T
|
||||
* input (as GNU tar does), we may need to extend this to handle option
|
||||
* words from sources other than argv/arc. I'm not really sure if I
|
||||
* like that feature of GNU tar, so it's certainly not a priority.
|
||||
*/
|
||||
|
||||
int
|
||||
bsdtar_getopt(struct bsdtar *bsdtar)
|
||||
{
|
||||
enum { state_start = 0, state_old_tar, state_next_word,
|
||||
state_short, state_long };
|
||||
static int state = state_start;
|
||||
static char *opt_word;
|
||||
|
||||
const struct option *popt, *match = NULL, *match2 = NULL;
|
||||
const char *p, *long_prefix = "--";
|
||||
size_t optlength;
|
||||
int opt = '?';
|
||||
int required = 0;
|
||||
|
||||
bsdtar->optarg = NULL;
|
||||
|
||||
/* First time through, initialize everything. */
|
||||
if (state == state_start) {
|
||||
/* Skip program name. */
|
||||
++bsdtar->argv;
|
||||
--bsdtar->argc;
|
||||
if (*bsdtar->argv == NULL)
|
||||
return (-1);
|
||||
/* Decide between "new style" and "old style" arguments. */
|
||||
if (bsdtar->argv[0][0] == '-') {
|
||||
state = state_next_word;
|
||||
} else {
|
||||
state = state_old_tar;
|
||||
opt_word = *bsdtar->argv++;
|
||||
--bsdtar->argc;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We're parsing old-style tar arguments
|
||||
*/
|
||||
if (state == state_old_tar) {
|
||||
/* Get the next option character. */
|
||||
opt = *opt_word++;
|
||||
if (opt == '\0') {
|
||||
/* New-style args can follow old-style. */
|
||||
state = state_next_word;
|
||||
} else {
|
||||
/* See if it takes an argument. */
|
||||
p = strchr(short_options, opt);
|
||||
if (p == NULL)
|
||||
return ('?');
|
||||
if (p[1] == ':') {
|
||||
bsdtar->optarg = *bsdtar->argv;
|
||||
if (bsdtar->optarg == NULL) {
|
||||
bsdtar_warnc(bsdtar, 0,
|
||||
"Option %c requires an argument",
|
||||
opt);
|
||||
return ('?');
|
||||
}
|
||||
++bsdtar->argv;
|
||||
--bsdtar->argc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We're ready to look at the next word in argv.
|
||||
*/
|
||||
if (state == state_next_word) {
|
||||
/* No more arguments, so no more options. */
|
||||
if (bsdtar->argv[0] == NULL)
|
||||
return (-1);
|
||||
/* Doesn't start with '-', so no more options. */
|
||||
if (bsdtar->argv[0][0] != '-')
|
||||
return (-1);
|
||||
/* "--" marks end of options; consume it and return. */
|
||||
if (strcmp(bsdtar->argv[0], "--") == 0) {
|
||||
++bsdtar->argv;
|
||||
--bsdtar->argc;
|
||||
return (-1);
|
||||
}
|
||||
/* Get next word for parsing. */
|
||||
opt_word = *bsdtar->argv++;
|
||||
--bsdtar->argc;
|
||||
if (opt_word[1] == '-') {
|
||||
/* Set up long option parser. */
|
||||
state = state_long;
|
||||
opt_word += 2; /* Skip leading '--' */
|
||||
} else {
|
||||
/* Set up short option parser. */
|
||||
state = state_short;
|
||||
++opt_word; /* Skip leading '-' */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We're parsing a group of POSIX-style single-character options.
|
||||
*/
|
||||
if (state == state_short) {
|
||||
/* Peel next option off of a group of short options. */
|
||||
opt = *opt_word++;
|
||||
if (opt == '\0') {
|
||||
/* End of this group; recurse to get next option. */
|
||||
state = state_next_word;
|
||||
return bsdtar_getopt(bsdtar);
|
||||
}
|
||||
|
||||
/* Does this option take an argument? */
|
||||
p = strchr(short_options, opt);
|
||||
if (p == NULL)
|
||||
return ('?');
|
||||
if (p[1] == ':')
|
||||
required = 1;
|
||||
|
||||
/* If it takes an argument, parse that. */
|
||||
if (required) {
|
||||
/* If arg is run-in, opt_word already points to it. */
|
||||
if (opt_word[0] == '\0') {
|
||||
/* Otherwise, pick up the next word. */
|
||||
opt_word = *bsdtar->argv;
|
||||
if (opt_word == NULL) {
|
||||
bsdtar_warnc(bsdtar, 0,
|
||||
"Option -%c requires an argument",
|
||||
opt);
|
||||
return ('?');
|
||||
}
|
||||
++bsdtar->argv;
|
||||
--bsdtar->argc;
|
||||
}
|
||||
if (opt == 'W') {
|
||||
state = state_long;
|
||||
long_prefix = "-W "; /* For clearer errors. */
|
||||
} else {
|
||||
state = state_next_word;
|
||||
bsdtar->optarg = opt_word;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* We're reading a long option, including -W long=arg convention. */
|
||||
if (state == state_long) {
|
||||
/* After this long option, we'll be starting a new word. */
|
||||
state = state_next_word;
|
||||
|
||||
/* Option name ends at '=' if there is one. */
|
||||
p = strchr(opt_word, '=');
|
||||
if (p != NULL) {
|
||||
optlength = (size_t)(p - opt_word);
|
||||
bsdtar->optarg = (char *)(uintptr_t)(p + 1);
|
||||
} else {
|
||||
optlength = strlen(opt_word);
|
||||
}
|
||||
|
||||
/* Search the table for an unambiguous match. */
|
||||
for (popt = tar_longopts; popt->name != NULL; popt++) {
|
||||
/* Short-circuit if first chars don't match. */
|
||||
if (popt->name[0] != opt_word[0])
|
||||
continue;
|
||||
/* If option is a prefix of name in table, record it.*/
|
||||
if (strncmp(opt_word, popt->name, optlength) == 0) {
|
||||
match2 = match; /* Record up to two matches. */
|
||||
match = popt;
|
||||
/* If it's an exact match, we're done. */
|
||||
if (strlen(popt->name) == optlength) {
|
||||
match2 = NULL; /* Forget the others. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Fail if there wasn't a unique match. */
|
||||
if (match == NULL) {
|
||||
bsdtar_warnc(bsdtar, 0,
|
||||
"Option %s%s is not supported",
|
||||
long_prefix, opt_word);
|
||||
return ('?');
|
||||
}
|
||||
if (match2 != NULL) {
|
||||
bsdtar_warnc(bsdtar, 0,
|
||||
"Ambiguous option %s%s (matches --%s and --%s)",
|
||||
long_prefix, opt_word, match->name, match2->name);
|
||||
return ('?');
|
||||
}
|
||||
|
||||
/* We've found a unique match; does it need an argument? */
|
||||
if (match->required) {
|
||||
/* Argument required: get next word if necessary. */
|
||||
if (bsdtar->optarg == NULL) {
|
||||
bsdtar->optarg = *bsdtar->argv;
|
||||
if (bsdtar->optarg == NULL) {
|
||||
bsdtar_warnc(bsdtar, 0,
|
||||
"Option %s%s requires an argument",
|
||||
long_prefix, match->name);
|
||||
return ('?');
|
||||
}
|
||||
++bsdtar->argv;
|
||||
--bsdtar->argc;
|
||||
}
|
||||
} else {
|
||||
/* Argument forbidden: fail if there is one. */
|
||||
if (bsdtar->optarg != NULL) {
|
||||
bsdtar_warnc(bsdtar, 0,
|
||||
"Option %s%s does not allow an argument",
|
||||
long_prefix, match->name);
|
||||
return ('?');
|
||||
}
|
||||
}
|
||||
return (match->equivalent);
|
||||
}
|
||||
|
||||
return (opt);
|
||||
}
|
@ -52,7 +52,6 @@
|
||||
#define HAVE_FNMATCH_H 1
|
||||
#define HAVE_FNM_LEADING_DIR 1
|
||||
#define HAVE_FTRUNCATE 1
|
||||
#define HAVE_GETOPT_LONG 1
|
||||
#undef HAVE_GETXATTR
|
||||
#define HAVE_GRP_H 1
|
||||
#define HAVE_INTTYPES_H 1
|
||||
|
Loading…
Reference in New Issue
Block a user