diff options
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | tar/Makefile | 2 | ||||
-rw-r--r-- | tar/bsdtar.1 | 38 | ||||
-rw-r--r-- | tar/bsdtar.c | 362 | ||||
-rw-r--r-- | tar/bsdtar.h | 29 | ||||
-rw-r--r-- | tar/cmdline.c | 376 | ||||
-rw-r--r-- | tar/config_freebsd.h | 1 |
7 files changed, 454 insertions, 355 deletions
diff --git a/Makefile.am b/Makefile.am index ae4fa32d..cccdc33c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -273,6 +273,7 @@ bsdtar_SOURCES= \ tar/bsdtar.c \ tar/bsdtar.h \ tar/bsdtar_platform.h \ + tar/cmdline.c \ tar/getdate.y \ tar/matching.c \ tar/read.c \ diff --git a/tar/Makefile b/tar/Makefile index 67f2b047..0996faac 100644 --- a/tar/Makefile +++ b/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 diff --git a/tar/bsdtar.1 b/tar/bsdtar.1 index a6015dc6..7c7b0527 100644 --- a/tar/bsdtar.1 +++ b/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,7 +345,7 @@ 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 ) +.It Fl -strip-components Ar count (x and t mode only) Remove the specified number of leading path elements. Pathnames with fewer elements will be silently skipped. @@ -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/tar/bsdtar.c b/tar/bsdtar.c index 40ddd78a..2f2e1048 100644 --- a/tar/bsdtar.c +++ b/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: src/usr.bin/tar/bsdtar.c,v 1.92 2008/08/22 01:22:55 kientzle #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/tar/bsdtar.h b/tar/bsdtar.h index fb256d8d..d3bf854c 100644 --- a/tar/bsdtar.h +++ b/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, ...) __LA_DEAD; +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/tar/cmdline.c b/tar/cmdline.c new file mode 100644 index 00000000..09818a31 --- /dev/null +++ b/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/tar/config_freebsd.h b/tar/config_freebsd.h index 25aceea7..4686cd83 100644 --- a/tar/config_freebsd.h +++ b/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 |