summaryrefslogtreecommitdiffstats
path: root/usr.bin/tar
diff options
context:
space:
mode:
authorkientzle <kientzle@FreeBSD.org>2008-11-08 04:43:24 +0000
committerkientzle <kientzle@FreeBSD.org>2008-11-08 04:43:24 +0000
commitc1676b587be226dbc880e81381ba9c7307276a0c (patch)
tree9eda7943e2409228a0abd608585047d5ddf3d319 /usr.bin/tar
parentddff5a26cd0e67ce67516526cce12e7d90e70f7f (diff)
downloadFreeBSD-src-c1676b587be226dbc880e81381ba9c7307276a0c.zip
FreeBSD-src-c1676b587be226dbc880e81381ba9c7307276a0c.tar.gz
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
Diffstat (limited to 'usr.bin/tar')
-rw-r--r--usr.bin/tar/Makefile3
-rw-r--r--usr.bin/tar/bsdtar.140
-rw-r--r--usr.bin/tar/bsdtar.c362
-rw-r--r--usr.bin/tar/bsdtar.h29
-rw-r--r--usr.bin/tar/cmdline.c376
-rw-r--r--usr.bin/tar/config_freebsd.h1
6 files changed, 455 insertions, 356 deletions
diff --git a/usr.bin/tar/Makefile b/usr.bin/tar/Makefile
index 1dc4388..a334b9c 100644
--- a/usr.bin/tar/Makefile
+++ b/usr.bin/tar/Makefile
@@ -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
diff --git a/usr.bin/tar/bsdtar.1 b/usr.bin/tar/bsdtar.1
index 02b301d..39a5376 100644
--- a/usr.bin/tar/bsdtar.1
+++ b/usr.bin/tar/bsdtar.1
@@ -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
diff --git a/usr.bin/tar/bsdtar.c b/usr.bin/tar/bsdtar.c
index d935e3f..24aaad7 100644
--- a/usr.bin/tar/bsdtar.c
+++ b/usr.bin/tar/bsdtar.c
@@ -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);
-}
diff --git a/usr.bin/tar/bsdtar.h b/usr.bin/tar/bsdtar.h
index eee53a9..3a4f549 100644
--- a/usr.bin/tar/bsdtar.h
+++ b/usr.bin/tar/bsdtar.h
@@ -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 *);
diff --git a/usr.bin/tar/cmdline.c b/usr.bin/tar/cmdline.c
new file mode 100644
index 0000000..09818a3
--- /dev/null
+++ b/usr.bin/tar/cmdline.c
@@ -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);
+}
diff --git a/usr.bin/tar/config_freebsd.h b/usr.bin/tar/config_freebsd.h
index a3dabc5..96cd5bc 100644
--- a/usr.bin/tar/config_freebsd.h
+++ b/usr.bin/tar/config_freebsd.h
@@ -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
OpenPOWER on IntegriCloud