svn commit: r184761 - head/usr.bin/tar

Tim Kientzle kientzle at FreeBSD.org
Fri Nov 7 20:43:24 PST 2008


Author: kientzle
Date: Sat Nov  8 04:43:24 2008
New Revision: 184761
URL: http://svn.freebsd.org/changeset/base/184761

Log:
  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

Added:
  head/usr.bin/tar/cmdline.c   (contents, props changed)
Modified:
  head/usr.bin/tar/Makefile
  head/usr.bin/tar/bsdtar.1
  head/usr.bin/tar/bsdtar.c
  head/usr.bin/tar/bsdtar.h
  head/usr.bin/tar/config_freebsd.h

Modified: head/usr.bin/tar/Makefile
==============================================================================
--- head/usr.bin/tar/Makefile	Sat Nov  8 02:05:41 2008	(r184760)
+++ head/usr.bin/tar/Makefile	Sat Nov  8 04:43:24 2008	(r184761)
@@ -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_fr
 CFLAGS+=	-I${.CURDIR}
 SYMLINKS=	bsdtar ${BINDIR}/tar
 MLINKS=	bsdtar.1 tar.1
+DEBUG_FLAGS=-g
 
 .PHONY: check test
 check test: $(PROG) bsdtar.1.gz

Modified: head/usr.bin/tar/bsdtar.1
==============================================================================
--- head/usr.bin/tar/bsdtar.1	Sat Nov  8 02:05:41 2008	(r184760)
+++ head/usr.bin/tar/bsdtar.1	Sat Nov  8 04:43:24 2008	(r184761)
@@ -144,21 +144,21 @@ In c and r mode, this changes the direct
 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
 .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

Modified: head/usr.bin/tar/bsdtar.c
==============================================================================
--- head/usr.bin/tar/bsdtar.c	Sat Nov  8 02:05:41 2008	(r184760)
+++ head/usr.bin/tar/bsdtar.c	Sat Nov  8 04:43:24 2008	(r184761)
@@ -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 c
 }
 
 
-/*-
- * 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);
-}

Modified: head/usr.bin/tar/bsdtar.h
==============================================================================
--- head/usr.bin/tar/bsdtar.h	Sat Nov  8 02:05:41 2008	(r184760)
+++ head/usr.bin/tar/bsdtar.h	Sat Nov  8 04:43:24 2008	(r184761)
@@ -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 *);

Added: head/usr.bin/tar/cmdline.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/usr.bin/tar/cmdline.c	Sat Nov  8 04:43:24 2008	(r184761)
@@ -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;

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-all mailing list