re-mark 1.29b-2 as not yet uploaded (merge madness!)
[debian/tar] / src / tar.c
index da124196ad48b5f74a209e0ae2fff475e2da480f..ba24c4345859ac88a576c5f3856257069572b30d 100644 (file)
--- a/src/tar.c
+++ b/src/tar.c
@@ -1,7 +1,7 @@
 /* A tar (tape archiver) program.
 
-   Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1999, 2000,
-   2001, 2003, 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
+   Copyright 1988, 1992-1997, 1999-2001, 2003-2007, 2012-2016 Free
+   Software Foundation, Inc.
 
    Written by John Gilmore, starting 1985-08-25.
 
@@ -16,8 +16,7 @@
    Public License for more details.
 
    You should have received a copy of the GNU General Public License along
-   with this program; if not, write to the Free Software Foundation, Inc.,
-   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+   with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include <system.h>
 
 #include <closeout.h>
 #include <configmake.h>
 #include <exitfail.h>
-#include <getdate.h>
+#include <parse-datetime.h>
 #include <rmt.h>
 #include <rmt-command.h>
-#include <prepargs.h>
+#include <wordsplit.h>
+#include <sysexits.h>
 #include <quotearg.h>
 #include <version-etc.h>
 #include <xstrtol.h>
 #include <stdopen.h>
 #include <priv-set.h>
+#include <savedir.h>
 
 /* Local declarations.  */
 
 # define DEFAULT_BLOCKING 20
 #endif
 
+/* Print a message if not all links are dumped */
+static int check_links_option;
+
+/* Number of allocated tape drive names.  */
+static size_t allocated_archive_names;
+
 \f
 /* Miscellaneous.  */
 
@@ -78,7 +85,7 @@ void
 request_stdin (const char *option)
 {
   if (stdin_used_by)
-    USAGE_ERROR ((0, 0, _("Options `-%s' and `-%s' both want standard input"),
+    USAGE_ERROR ((0, 0, _("Options '%s' and '%s' both want standard input"),
                  stdin_used_by, option));
 
   stdin_used_by = option;
@@ -161,6 +168,14 @@ set_archive_format (char const *name)
   archive_format = p->fmt;
 }
 
+static void
+set_xattr_option (int value)
+{
+  if (value == 1)
+    set_archive_format ("posix");
+  xattrs_option = value;
+}
+
 const char *
 archive_format_string (enum archive_format fmt)
 {
@@ -220,12 +235,12 @@ subcommand_string (enum subcommand c)
   abort ();
 }
 
-void
-tar_list_quoting_styles (struct obstack *stk, char *prefix)
+static void
+tar_list_quoting_styles (struct obstack *stk, char const *prefix)
 {
   int i;
   size_t prefixlen = strlen (prefix);
-  
+
   for (i = 0; quoting_style_args[i]; i++)
     {
       obstack_grow (stk, prefix, prefixlen);
@@ -235,7 +250,7 @@ tar_list_quoting_styles (struct obstack *stk, char *prefix)
     }
 }
 
-void
+static void
 tar_set_quoting_style (char *arg)
 {
   int i;
@@ -247,7 +262,7 @@ tar_set_quoting_style (char *arg)
        return;
       }
   FATAL_ERROR ((0, 0,
-               _("Unknown quoting style `%s'. Try `%s --quoting-style=help' to get a list."), arg, program_invocation_short_name));
+               _("Unknown quoting style '%s'. Try '%s --quoting-style=help' to get a list."), arg, program_name));
 }
 
 \f
@@ -255,30 +270,24 @@ tar_set_quoting_style (char *arg)
 
 enum
 {
-  ANCHORED_OPTION = CHAR_MAX + 1,
+  ACLS_OPTION = CHAR_MAX + 1,
   ATIME_PRESERVE_OPTION,
   BACKUP_OPTION,
   CHECK_DEVICE_OPTION,
   CHECKPOINT_OPTION,
   CHECKPOINT_ACTION_OPTION,
+  CLAMP_MTIME_OPTION,
   DELAY_DIRECTORY_RESTORE_OPTION,
   HARD_DEREFERENCE_OPTION,
   DELETE_OPTION,
-  EXCLUDE_BACKUPS_OPTION,
-  EXCLUDE_CACHES_OPTION,
-  EXCLUDE_CACHES_UNDER_OPTION,
-  EXCLUDE_CACHES_ALL_OPTION,
-  EXCLUDE_OPTION,
-  EXCLUDE_TAG_OPTION,
-  EXCLUDE_TAG_UNDER_OPTION,
-  EXCLUDE_TAG_ALL_OPTION,
-  EXCLUDE_VCS_OPTION,
   FORCE_LOCAL_OPTION,
+  FULL_TIME_OPTION,
   GROUP_OPTION,
-  IGNORE_CASE_OPTION,
+  GROUP_MAP_OPTION,
   IGNORE_COMMAND_ERROR_OPTION,
   IGNORE_FAILED_READ_OPTION,
   INDEX_FILE_OPTION,
+  KEEP_DIRECTORY_SYMLINK_OPTION,
   KEEP_NEWER_FILES_OPTION,
   LEVEL_OPTION,
   LZIP_OPTION,
@@ -287,46 +296,46 @@ enum
   MODE_OPTION,
   MTIME_OPTION,
   NEWER_MTIME_OPTION,
-  NO_ANCHORED_OPTION,
+  NO_ACLS_OPTION,
   NO_AUTO_COMPRESS_OPTION,
   NO_CHECK_DEVICE_OPTION,
   NO_DELAY_DIRECTORY_RESTORE_OPTION,
-  NO_IGNORE_CASE_OPTION,
   NO_IGNORE_COMMAND_ERROR_OPTION,
-  NO_NULL_OPTION,
   NO_OVERWRITE_DIR_OPTION,
   NO_QUOTE_CHARS_OPTION,
-  NO_RECURSION_OPTION,
   NO_SAME_OWNER_OPTION,
   NO_SAME_PERMISSIONS_OPTION,
   NO_SEEK_OPTION,
-  NO_UNQUOTE_OPTION,
-  NO_WILDCARDS_MATCH_SLASH_OPTION,
-  NO_WILDCARDS_OPTION,
-  NULL_OPTION,
+  NO_SELINUX_CONTEXT_OPTION,
+  NO_XATTR_OPTION,
   NUMERIC_OWNER_OPTION,
   OCCURRENCE_OPTION,
   OLD_ARCHIVE_OPTION,
   ONE_FILE_SYSTEM_OPTION,
+  ONE_TOP_LEVEL_OPTION,
   OVERWRITE_DIR_OPTION,
   OVERWRITE_OPTION,
   OWNER_OPTION,
+  OWNER_MAP_OPTION,
   PAX_OPTION,
   POSIX_OPTION,
-  PRESERVE_OPTION,
   QUOTE_CHARS_OPTION,
   QUOTING_STYLE_OPTION,
   RECORD_SIZE_OPTION,
-  RECURSION_OPTION,
   RECURSIVE_UNLINK_OPTION,
   REMOVE_FILES_OPTION,
   RESTRICT_OPTION,
   RMT_COMMAND_OPTION,
   RSH_COMMAND_OPTION,
   SAME_OWNER_OPTION,
+  SELINUX_CONTEXT_OPTION,
   SHOW_DEFAULTS_OPTION,
   SHOW_OMITTED_DIRS_OPTION,
+  SHOW_SNAPSHOT_FIELD_RANGES_OPTION,
   SHOW_TRANSFORMED_NAMES_OPTION,
+  SKIP_OLD_FILES_OPTION,
+  SORT_OPTION,
+  HOLE_DETECTION_OPTION,
   SPARSE_VERSION_OPTION,
   STRIP_COMPONENTS_OPTION,
   SUFFIX_OPTION,
@@ -334,18 +343,16 @@ enum
   TOTALS_OPTION,
   TO_COMMAND_OPTION,
   TRANSFORM_OPTION,
-  UNQUOTE_OPTION,
   UTC_OPTION,
   VOLNO_FILE_OPTION,
-  WARNING_OPTION, 
-  WILDCARDS_MATCH_SLASH_OPTION,
-  WILDCARDS_OPTION
+  WARNING_OPTION,
+  XATTR_OPTION,
+  XATTR_EXCLUDE,
+  XATTR_INCLUDE
 };
 
-const char *argp_program_version = "tar (" PACKAGE_NAME ") " VERSION;
-const char *argp_program_bug_address = "<" PACKAGE_BUGREPORT ">";
 static char const doc[] = N_("\
-GNU `tar' saves many files together into a single tape or disk archive, \
+GNU 'tar' saves many files together into a single tape or disk archive, \
 and can restore individual files from the archive.\n\
 \n\
 Examples:\n\
@@ -353,7 +360,7 @@ Examples:\n\
   tar -tvf archive.tar         # List all files in archive.tar verbosely.\n\
   tar -xf archive.tar          # Extract all files from archive.tar.\n")
 "\v"
-N_("The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
+N_("The backup suffix is '~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
 The version control may be set with --backup or VERSION_CONTROL, values are:\n\n\
   none, off       never make backups\n\
   t, numbered     make numbered backups\n\
@@ -414,6 +421,8 @@ static struct argp_option options[] = {
 
   {"sparse", 'S', 0, 0,
    N_("handle sparse files efficiently"), GRID+1 },
+  {"hole-detection", HOLE_DETECTION_OPTION, N_("TYPE"), 0,
+   N_("technique to detect holes"), GRID+1 },
   {"sparse-version", SPARSE_VERSION_OPTION, N_("MAJOR[.MINOR]"), 0,
    N_("set version of the sparse format to use (implies --sparse)"), GRID+1},
   {"incremental", 'G', 0, 0,
@@ -451,7 +460,11 @@ static struct argp_option options[] = {
   {"remove-files", REMOVE_FILES_OPTION, 0, 0,
    N_("remove files after adding them to the archive"), GRID+1 },
   {"keep-old-files", 'k', 0, 0,
-   N_("don't replace existing files when extracting"), GRID+1 },
+   N_("don't replace existing files when extracting, "
+      "treat them as errors"), GRID+1 },
+  {"skip-old-files", SKIP_OLD_FILES_OPTION, 0, 0,
+   N_("don't replace existing files when extracting, silently skip over them"),
+   GRID+1 },
   {"keep-newer-files", KEEP_NEWER_FILES_OPTION, 0, 0,
    N_("don't replace existing files that are newer than their archive copies"), GRID+1 },
   {"overwrite", OVERWRITE_OPTION, 0, 0,
@@ -465,6 +478,12 @@ static struct argp_option options[] = {
   {"overwrite-dir", OVERWRITE_DIR_OPTION, 0, 0,
    N_("overwrite metadata of existing directories when extracting (default)"),
    GRID+1 },
+  {"keep-directory-symlink", KEEP_DIRECTORY_SYMLINK_OPTION, 0, 0,
+   N_("preserve existing symlinks to directories when extracting"),
+   GRID+1 },
+  {"one-top-level", ONE_TOP_LEVEL_OPTION, N_("DIR"), OPTION_ARG_OPTIONAL,
+   N_("create a subdirectory to avoid having loose files extracted"),
+   GRID+1 },
 #undef GRID
 
 #define GRID 40
@@ -489,8 +508,14 @@ static struct argp_option options[] = {
    N_("force NAME as owner for added files"), GRID+1 },
   {"group", GROUP_OPTION, N_("NAME"), 0,
    N_("force NAME as group for added files"), GRID+1 },
+  {"owner-map", OWNER_MAP_OPTION, N_("FILE"), 0,
+   N_("use FILE to map file owner UIDs and names"), GRID+1 },
+  {"group-map", GROUP_MAP_OPTION, N_("FILE"), 0,
+   N_("use FILE to map file owner GIDs and names"), GRID+1 },
   {"mtime", MTIME_OPTION, N_("DATE-OR-FILE"), 0,
    N_("set mtime for added files from DATE-OR-FILE"), GRID+1 },
+  {"clamp-mtime", CLAMP_MTIME_OPTION, 0, 0,
+   N_("only set time when the file is more recent than what was given with --mtime"), GRID+1 },
   {"mode", MODE_OPTION, N_("CHANGES"), 0,
    N_("force (symbolic) mode CHANGES for added files"), GRID+1 },
   {"atime-preserve", ATIME_PRESERVE_OPTION,
@@ -513,15 +538,43 @@ static struct argp_option options[] = {
   {"no-same-permissions", NO_SAME_PERMISSIONS_OPTION, 0, 0,
    N_("apply the user's umask when extracting permissions from the archive (default for ordinary users)"), GRID+1 },
   {"preserve-order", 's', 0, 0,
-   N_("sort names to extract to match archive"), GRID+1 },
+   N_("member arguments are listed in the same order as the "
+      "files in the archive"), GRID+1 },
   {"same-order", 0, 0, OPTION_ALIAS, NULL, GRID+1 },
-  {"preserve", PRESERVE_OPTION, 0, 0,
-   N_("same as both -p and -s"), GRID+1 },
   {"delay-directory-restore", DELAY_DIRECTORY_RESTORE_OPTION, 0, 0,
    N_("delay setting modification times and permissions of extracted"
       " directories until the end of extraction"), GRID+1 },
   {"no-delay-directory-restore", NO_DELAY_DIRECTORY_RESTORE_OPTION, 0, 0,
    N_("cancel the effect of --delay-directory-restore option"), GRID+1 },
+  {"sort", SORT_OPTION, N_("ORDER"), 0,
+#if D_INO_IN_DIRENT
+   N_("directory sorting order: none (default), name or inode"
+#else
+   N_("directory sorting order: none (default) or name"
+#endif
+     ), GRID+1 },
+#undef GRID
+
+#define GRID 55
+  {NULL, 0, NULL, 0,
+   N_("Handling of extended file attributes:"), GRID },
+
+  {"xattrs", XATTR_OPTION, 0, 0,
+   N_("Enable extended attributes support"), GRID+1 },
+  {"no-xattrs", NO_XATTR_OPTION, 0, 0,
+   N_("Disable extended attributes support"), GRID+1 },
+  {"xattrs-include", XATTR_INCLUDE, N_("MASK"), 0,
+   N_("specify the include pattern for xattr keys"), GRID+1 },
+  {"xattrs-exclude", XATTR_EXCLUDE, N_("MASK"), 0,
+   N_("specify the exclude pattern for xattr keys"), GRID+1 },
+  {"selinux", SELINUX_CONTEXT_OPTION, 0, 0,
+   N_("Enable the SELinux context support"), GRID+1 },
+  {"no-selinux", NO_SELINUX_CONTEXT_OPTION, 0, 0,
+   N_("Disable the SELinux context support"), GRID+1 },
+  {"acls", ACLS_OPTION, 0, 0,
+   N_("Enable the POSIX ACLs support"), GRID+1 },
+  {"no-acls", NO_ACLS_OPTION, 0, 0,
+   N_("Disable the POSIX ACLs support"), GRID+1 },
 #undef GRID
 
 #define GRID 60
@@ -537,7 +590,7 @@ static struct argp_option options[] = {
   {"rsh-command", RSH_COMMAND_OPTION, N_("COMMAND"), 0,
    N_("use remote COMMAND instead of rsh"), GRID+1 },
 #ifdef DEVICE_PREFIX
-  {"-[0-7][lmh]", 0, NULL, OPTION_DOC, /* It is OK, since `name' will never be
+  {"-[0-7][lmh]", 0, NULL, OPTION_DOC, /* It is OK, since 'name' will never be
                                          translated */
    N_("specify drive and density"), GRID+1 },
 #endif
@@ -630,62 +683,20 @@ static struct argp_option options[] = {
   {"lzop", LZOP_OPTION, 0, 0, NULL, GRID+1 },
   {"xz", 'J', 0, 0, NULL, GRID+1 },
 #undef GRID
-  
+
 #define GRID 100
   {NULL, 0, NULL, 0,
    N_("Local file selection:"), GRID },
-
-  {"add-file", ARGP_KEY_ARG, N_("FILE"), 0,
-   N_("add given FILE to the archive (useful if its name starts with a dash)"), GRID+1 },
-  {"directory", 'C', N_("DIR"), 0,
-   N_("change to directory DIR"), GRID+1 },
-  {"files-from", 'T', N_("FILE"), 0,
-   N_("get names to extract or create from FILE"), GRID+1 },
-  {"null", NULL_OPTION, 0, 0,
-   N_("-T reads null-terminated names, disable -C"), GRID+1 },
-  {"no-null", NO_NULL_OPTION, 0, 0,
-   N_("disable the effect of the previous --null option"), GRID+1 },
-  {"unquote", UNQUOTE_OPTION, 0, 0,
-   N_("unquote filenames read with -T (default)"), GRID+1 },
-  {"no-unquote", NO_UNQUOTE_OPTION, 0, 0,
-   N_("do not unquote filenames read with -T"), GRID+1 },
-  {"exclude", EXCLUDE_OPTION, N_("PATTERN"), 0,
-   N_("exclude files, given as a PATTERN"), GRID+1 },
-  {"exclude-from", 'X', N_("FILE"), 0,
-   N_("exclude patterns listed in FILE"), GRID+1 },
-  {"exclude-caches", EXCLUDE_CACHES_OPTION, 0, 0,
-   N_("exclude contents of directories containing CACHEDIR.TAG, "
-      "except for the tag file itself"), GRID+1 },
-  {"exclude-caches-under", EXCLUDE_CACHES_UNDER_OPTION, 0, 0,
-   N_("exclude everything under directories containing CACHEDIR.TAG"),
-   GRID+1 },
-  {"exclude-caches-all", EXCLUDE_CACHES_ALL_OPTION, 0, 0,
-   N_("exclude directories containing CACHEDIR.TAG"), GRID+1 },
-  {"exclude-tag", EXCLUDE_TAG_OPTION, N_("FILE"), 0,
-   N_("exclude contents of directories containing FILE, except"
-      " for FILE itself"), GRID+1 },
-  {"exclude-tag-under", EXCLUDE_TAG_UNDER_OPTION, N_("FILE"), 0,
-   N_("exclude everything under directories containing FILE"), GRID+1 },
-  {"exclude-tag-all", EXCLUDE_TAG_ALL_OPTION, N_("FILE"), 0,
-   N_("exclude directories containing FILE"), GRID+1 },
-  {"exclude-vcs", EXCLUDE_VCS_OPTION, NULL, 0,
-   N_("exclude version control system directories"), GRID+1 },
-  {"exclude-backups", EXCLUDE_BACKUPS_OPTION, NULL, 0,
-   N_("exclude backup and lock files"), GRID+1 },
-  {"no-recursion", NO_RECURSION_OPTION, 0, 0,
-   N_("avoid descending automatically in directories"), GRID+1 },
   {"one-file-system", ONE_FILE_SYSTEM_OPTION, 0, 0,
    N_("stay in local file system when creating archive"), GRID+1 },
-  {"recursion", RECURSION_OPTION, 0, 0,
-   N_("recurse into directories (default)"), GRID+1 },
   {"absolute-names", 'P', 0, 0,
-   N_("don't strip leading `/'s from file names"), GRID+1 },
+   N_("don't strip leading '/'s from file names"), GRID+1 },
   {"dereference", 'h', 0, 0,
    N_("follow symlinks; archive and dump the files they point to"), GRID+1 },
   {"hard-dereference", HARD_DEREFERENCE_OPTION, 0, 0,
    N_("follow hard links; archive and dump the files they refer to"), GRID+1 },
   {"starting-file", 'K', N_("MEMBER-NAME"), 0,
-   N_("begin at member MEMBER-NAME in the archive"), GRID+1 },
+   N_("begin at member MEMBER-NAME when reading the archive"), GRID+1 },
   {"newer", 'N', N_("DATE-OR-FILE"), 0,
    N_("only store files newer than DATE-OR-FILE"), GRID+1 },
   {"after-date", 0, 0, OPTION_ALIAS, NULL, GRID+1 },
@@ -708,28 +719,6 @@ static struct argp_option options[] = {
   {"xform", 0, 0, OPTION_ALIAS, NULL, GRID+1 },
 #undef GRID
 
-#define GRID 120
-  {NULL, 0, NULL, 0,
-   N_("File name matching options (affect both exclude and include patterns):"),
-   GRID },
-  {"ignore-case", IGNORE_CASE_OPTION, 0, 0,
-   N_("ignore case"), GRID+1 },
-  {"anchored", ANCHORED_OPTION, 0, 0,
-   N_("patterns match file name start"), GRID+1 },
-  {"no-anchored", NO_ANCHORED_OPTION, 0, 0,
-   N_("patterns match after any `/' (default for exclusion)"), GRID+1 },
-  {"no-ignore-case", NO_IGNORE_CASE_OPTION, 0, 0,
-   N_("case sensitive matching (default)"), GRID+1 },
-  {"wildcards", WILDCARDS_OPTION, 0, 0,
-   N_("use wildcards (default for exclusion)"), GRID+1 },
-  {"no-wildcards", NO_WILDCARDS_OPTION, 0, 0,
-   N_("verbatim string matching"), GRID+1 },
-  {"no-wildcards-match-slash", NO_WILDCARDS_MATCH_SLASH_OPTION, 0, 0,
-   N_("wildcards do not match `/'"), GRID+1 },
-  {"wildcards-match-slash", WILDCARDS_MATCH_SLASH_OPTION, 0, 0,
-   N_("wildcards match `/' (default for exclusion)"), GRID+1 },
-#undef GRID
-
 #define GRID 130
   {NULL, 0, NULL, 0,
    N_("Informative output:"), GRID },
@@ -752,7 +741,9 @@ static struct argp_option options[] = {
       "Allowed signals are: SIGHUP, SIGQUIT, SIGINT, SIGUSR1 and SIGUSR2; "
       "the names without SIG prefix are also accepted"), GRID+1 },
   {"utc", UTC_OPTION, 0, 0,
-   N_("print file modification dates in UTC"), GRID+1 },
+   N_("print file modification times in UTC"), GRID+1 },
+  {"full-time", FULL_TIME_OPTION, 0, 0,
+   N_("print file time to its full resolution"), GRID+1 },
   {"index-file", INDEX_FILE_OPTION, N_("FILE"), 0,
    N_("send verbose output to FILE"), GRID+1 },
   {"block-number", 'R', 0, 0,
@@ -762,6 +753,8 @@ static struct argp_option options[] = {
   {"confirmation", 0, 0, OPTION_ALIAS, NULL, GRID+1 },
   {"show-defaults", SHOW_DEFAULTS_OPTION, 0, 0,
    N_("show tar defaults"), GRID+1 },
+  {"show-snapshot-field-ranges", SHOW_SNAPSHOT_FIELD_RANGES_OPTION, 0, 0,
+   N_("show valid ranges for snapshot-file fields"), GRID+1 },
   {"show-omitted-dirs", SHOW_OMITTED_DIRS_OPTION, 0, 0,
    N_("when listing or extracting, list each directory that does not match search criteria"), GRID+1 },
   {"show-transformed-names", SHOW_TRANSFORMED_NAMES_OPTION, 0, 0,
@@ -809,25 +802,13 @@ static enum atime_preserve const atime_preserve_types[] =
    (minus 1 for NULL guard) */
 ARGMATCH_VERIFY (atime_preserve_args, atime_preserve_types);
 
-/* Wildcard matching settings */
-enum wildcards
-  {
-    default_wildcards, /* For exclusion == enable_wildcards,
-                         for inclusion == disable_wildcards */
-    disable_wildcards,
-    enable_wildcards
-  };
-
 struct tar_args        /* Variables used during option parsing */
 {
+  struct option_locus *loc;
+
   struct textual_date *textual_date; /* Keeps the arguments to --newer-mtime
                                        and/or --date option if they are
                                        textual dates */
-  enum wildcards wildcards;        /* Wildcard settings (--wildcards/
-                                     --no-wildcards) */
-  int matching_flags;              /* exclude_fnmatch options */
-  int include_anchored;            /* Pattern anchoring options used for
-                                     file inclusion */
   bool o_option;                   /* True if -o option was given */
   bool pax_option;                 /* True if --pax-option was given */
   char const *backup_suffix_string;   /* --suffix option argument */
@@ -837,66 +818,6 @@ struct tar_args        /* Variables used during option parsing */
                                      be attempted when creating archives */
 };
 
-\f
-#define MAKE_EXCL_OPTIONS(args) \
- ((((args)->wildcards != disable_wildcards) ? EXCLUDE_WILDCARDS : 0) \
-  | (args)->matching_flags \
-  | recursion_option)
-
-#define MAKE_INCL_OPTIONS(args) \
- ((((args)->wildcards == enable_wildcards) ? EXCLUDE_WILDCARDS : 0) \
-  | (args)->include_anchored \
-  | (args)->matching_flags \
-  | recursion_option)
-
-static char const * const vcs_file_table[] = {
-  /* CVS: */
-  "CVS",
-  ".cvsignore",
-  /* RCS: */
-  "RCS",
-  /* SCCS: */
-  "SCCS",
-  /* SVN: */
-  ".svn",
-  /* git: */
-  ".git",
-  ".gitignore",
-  /* Arch: */
-  ".arch-ids",
-  "{arch}",
-  "=RELEASE-ID",
-  "=meta-update",
-  "=update",
-  /* Bazaar */
-  ".bzr",
-  ".bzrignore",
-  ".bzrtags",
-  /* Mercurial */
-  ".hg",
-  ".hgignore",
-  ".hgtags",
-  /* darcs */
-  "_darcs",
-  NULL
-};
-
-static char const * const backup_file_table[] = {
-  ".#*",
-  "*~",
-  "#*#",
-  NULL
-};
-
-void
-add_exclude_array (char const * const * fv)
-{
-  int i;
-
-  for (i = 0; fv[i]; i++)
-    add_exclude (excluded, fv[i], 0);
-}
-
 \f
 static char *
 format_default_settings (void)
@@ -907,8 +828,8 @@ format_default_settings (void)
            " --rsh-command=%s"
 #endif
            ,
-           archive_format_string (DEFAULT_ARCHIVE_FORMAT), 
-           DEFAULT_ARCHIVE, DEFAULT_BLOCKING,              
+           archive_format_string (DEFAULT_ARCHIVE_FORMAT),
+           DEFAULT_ARCHIVE, DEFAULT_BLOCKING,
            quoting_style_args[DEFAULT_QUOTING_STYLE],
            DEFAULT_RMT_COMMAND
 #ifdef REMOTE_SHELL
@@ -916,7 +837,93 @@ format_default_settings (void)
 #endif
            );
 }
+\f
+static void
+option_conflict_error (const char *a, const char *b)
+{
+  /* TRANSLATORS: Both %s in this statement are replaced with
+     option names. */
+  USAGE_ERROR ((0, 0, _("'%s' cannot be used with '%s'"), a, b));
+}
+\f
+/* Classes of options that can conflict: */
+enum option_class
+  {
+    OC_COMPRESS,                 /* Compress options: -JjZz, -I, etc. */
+    OC_OCCURRENCE,               /* --occurrence */
+    OC_LISTED_INCREMENTAL,       /* --listed-incremental */
+    OC_NEWER,                    /* --newer, --newer-mtime, --after-date */
+    OC_VERIFY,                   /* --verify */
+    OC_STARTING_FILE,            /* --starting-file */
+    OC_SAME_ORDER,               /* --same-order */
+    OC_ONE_TOP_LEVEL,            /* --one-top-level */
+    OC_ABSOLUTE_NAMES,           /* --absolute-names */
+    OC_OLD_FILES,                /* --keep-old-files, --overwrite, etc. */
+    OC_MAX
+  };
+
+/* Table of locations of potentially conflicting options.  Two options can
+   conflict only if they procede from the command line.  Otherwise, options
+   in command line silently override those defined in TAR_OPTIONS. */
+static struct option_locus *option_class[OC_MAX];
+
+/* Save location of an option of class ID.  Return location of a previous
+   occurrence of an option of that class, or NULL. */
+static struct option_locus *
+optloc_save (unsigned int id, struct option_locus *loc)
+{
+  struct option_locus *optloc;
+  char *p;
+  size_t s;
+
+  if (id >= sizeof (option_class) / sizeof (option_class[0]))
+    abort ();
+  s = sizeof (*loc);
+  if (loc->name)
+    s += strlen (loc->name) + 1;
+  optloc = xmalloc (s);
+  if (loc->name)
+    {
+      p = (char*) optloc + sizeof (*loc);
+      strcpy (p, loc->name);
+      optloc->name = p;
+    }
+  else
+    optloc->name = NULL;
+  optloc->source = loc->source;
+  optloc->line = loc->line;
+  optloc->prev = option_class[id];
+  option_class[id] = optloc;
+  return optloc->prev;
+}
+
+/* Return location of a recent option of class ID */
+static struct option_locus *
+optloc_lookup (int id)
+{
+  return option_class[id];
+}
+
+/* Return true if the latest occurrence of option ID was in the command line */
+static int
+option_set_in_cl (int id)
+{
+  struct option_locus *loc = optloc_lookup (id);
+  if (!loc)
+    return 0;
+  return loc->source == OPTS_COMMAND_LINE;
+}
 
+/* Compare two option locations */
+static int
+optloc_eq (struct option_locus *a, struct option_locus *b)
+{
+  if (a->source != b->source)
+    return 0;
+  if (a->source == OPTS_COMMAND_LINE)
+    return 1;
+  return strcmp (a->name, b->name) == 0;
+}
 \f
 static void
 set_subcommand_option (enum subcommand subcommand)
@@ -924,22 +931,24 @@ set_subcommand_option (enum subcommand subcommand)
   if (subcommand_option != UNKNOWN_SUBCOMMAND
       && subcommand_option != subcommand)
     USAGE_ERROR ((0, 0,
-                 _("You may not specify more than one `-Acdtrux' or `--test-label' option")));
-  
+                 _("You may not specify more than one '-Acdtrux', '--delete' or  '--test-label' option")));
+
   subcommand_option = subcommand;
 }
 
 static void
-set_use_compress_program_option (const char *string)
+set_use_compress_program_option (const char *string, struct option_locus *loc)
 {
+  struct option_locus *p = optloc_save (OC_COMPRESS, loc);
   if (use_compress_program_option
-      && strcmp (use_compress_program_option, string) != 0)
+      && strcmp (use_compress_program_option, string) != 0
+      && p->source == OPTS_COMMAND_LINE)
     USAGE_ERROR ((0, 0, _("Conflicting compression options")));
-  
+
   use_compress_program_option = string;
 }
 \f
-static RETSIGTYPE
+static void
 sigstat (int signo)
 {
   compute_duration ();
@@ -953,24 +962,27 @@ static void
 stat_on_signal (int signo)
 {
 #ifdef HAVE_SIGACTION
+# ifndef SA_RESTART
+#  define SA_RESTART 0
+# endif
   struct sigaction act;
   act.sa_handler = sigstat;
   sigemptyset (&act.sa_mask);
-  act.sa_flags = 0;
+  act.sa_flags = SA_RESTART;
   sigaction (signo, &act, NULL);
 #else
   signal (signo, sigstat);
 #endif
 }
 
-void
+static void
 set_stat_signal (const char *name)
 {
   static struct sigtab
   {
-    char *name;
+    char const *name;
     int signo;
-  } sigtab[] = {
+  } const sigtab[] = {
     { "SIGUSR1", SIGUSR1 },
     { "USR1", SIGUSR1 },
     { "SIGUSR2", SIGUSR2 },
@@ -982,7 +994,7 @@ set_stat_signal (const char *name)
     { "SIGQUIT", SIGQUIT },
     { "QUIT", SIGQUIT }
   };
-  struct sigtab *p;
+  struct sigtab const *p;
 
   for (p = sigtab; p < sigtab + sizeof (sigtab) / sizeof (sigtab[0]); p++)
     if (strcmp (p->name, name) == 0)
@@ -1011,7 +1023,7 @@ get_date_or_file (struct tar_args *args, const char *option,
       || *str == '.')
     {
       struct stat st;
-      if (deref_stat (dereference_option, str, &st) != 0)
+      if (stat (str, &st) != 0)
        {
          stat_error (str);
          USAGE_ERROR ((0, 0, _("Date sample file not found")));
@@ -1020,7 +1032,7 @@ get_date_or_file (struct tar_args *args, const char *option,
     }
   else
     {
-      if (! get_date (ts, str, NULL))
+      if (! parse_datetime (ts, str, NULL))
        {
          WARN ((0, 0, _("Substituting %s for unknown date format %s"),
                 tartime (*ts, false), quote (str)));
@@ -1051,7 +1063,7 @@ report_textual_dates (struct tar_args *args)
        {
          char const *treated_as = tartime (p->ts, true);
          if (strcmp (p->date, treated_as) != 0)
-           WARN ((0, 0, _("Option %s: Treating date `%s' as %s"),
+           WARN ((0, 0, _("Option %s: Treating date '%s' as %s"),
                   p->option, p->date, treated_as));
        }
       free (p->date);
@@ -1061,193 +1073,22 @@ report_textual_dates (struct tar_args *args)
 }
 
 \f
-
-/* Either NL or NUL, as decided by the --null option.  */
-static char filename_terminator;
-
-enum read_file_list_state  /* Result of reading file name from the list file */
-  {
-    file_list_success,     /* OK, name read successfully */
-    file_list_end,         /* End of list file */
-    file_list_zero,        /* Zero separator encountered where it should not */
-    file_list_skip         /* Empty (zero-length) entry encountered, skip it */
-  };
-
-/* Read from FP a sequence of characters up to TERM and put them
-   into STK.
- */
-static enum read_file_list_state
-read_name_from_file (FILE *fp, struct obstack *stk, int term)
-{
-  int c;
-  size_t counter = 0;
-
-  for (c = getc (fp); c != EOF && c != term; c = getc (fp))
-    {
-      if (c == 0)
-       {
-         /* We have read a zero separator. The file possibly is
-            zero-separated */
-         return file_list_zero;
-       }
-      obstack_1grow (stk, c);
-      counter++;
-    }
-  
-  if (counter == 0 && c != EOF)
-    return file_list_skip;
-
-  obstack_1grow (stk, 0);
-
-  return (counter == 0 && c == EOF) ? file_list_end : file_list_success;
-}
-
-\f
-static bool files_from_option;  /* When set, tar will not refuse to create
-                                  empty archives */
-static struct obstack argv_stk; /* Storage for additional command line options
-                                  read using -T option */
-
-/* Prevent recursive inclusion of the same file */
-struct file_id_list
-{
-  struct file_id_list *next;
-  ino_t ino;
-  dev_t dev;
-};
-
-static struct file_id_list *file_id_list;
-
-static void
-add_file_id (const char *filename)
-{
-  struct file_id_list *p;
-  struct stat st;
-
-  if (stat (filename, &st))
-    stat_fatal (filename);
-  for (p = file_id_list; p; p = p->next)
-    if (p->ino == st.st_ino && p->dev == st.st_dev)
-      {
-       FATAL_ERROR ((0, 0, _("%s: file list already read"),
-                     quotearg_colon (filename)));
-      }
-  p = xmalloc (sizeof *p);
-  p->next = file_id_list;
-  p->ino = st.st_ino;
-  p->dev = st.st_dev;
-  file_id_list = p;
-}
-
 /* Default density numbers for [0-9][lmh] device specifications */
 
-#ifndef LOW_DENSITY_NUM
-# define LOW_DENSITY_NUM 0
-#endif
+#if defined DEVICE_PREFIX && !defined DENSITY_LETTER
+# ifndef LOW_DENSITY_NUM
+#  define LOW_DENSITY_NUM 0
+# endif
 
-#ifndef MID_DENSITY_NUM
-# define MID_DENSITY_NUM 8
-#endif
+# ifndef MID_DENSITY_NUM
+#  define MID_DENSITY_NUM 8
+# endif
 
-#ifndef HIGH_DENSITY_NUM
-# define HIGH_DENSITY_NUM 16
+# ifndef HIGH_DENSITY_NUM
+#  define HIGH_DENSITY_NUM 16
+# endif
 #endif
 
-static void
-update_argv (const char *filename, struct argp_state *state)
-{
-  FILE *fp;
-  size_t count = 0, i;
-  char *start, *p;
-  char **new_argv;
-  size_t new_argc;
-  bool is_stdin = false;
-  enum read_file_list_state read_state;
-  int term = filename_terminator;
-  
-  if (!strcmp (filename, "-"))
-    {
-      is_stdin = true;
-      request_stdin ("-T");
-      fp = stdin;
-    }
-  else
-    {
-      add_file_id (filename);
-      if ((fp = fopen (filename, "r")) == NULL)
-       open_fatal (filename);
-    }
-  
-  while ((read_state = read_name_from_file (fp, &argv_stk, term))
-        != file_list_end)
-    {
-      switch (read_state)
-       {
-       case file_list_success:
-         count++;
-         break;
-         
-       case file_list_end: /* won't happen, just to pacify gcc */
-         break;
-         
-       case file_list_zero:
-         {
-           size_t size;
-           
-           WARNOPT (WARN_FILENAME_WITH_NULS,
-                    (0, 0, N_("%s: file name read contains nul character"),
-                     quotearg_colon (filename)));
-           
-           /* Prepare new stack contents */
-           size = obstack_object_size (&argv_stk);
-           p = obstack_finish (&argv_stk);
-           for (; size > 0; size--, p++)
-             if (*p)
-               obstack_1grow (&argv_stk, *p);
-             else
-               obstack_1grow (&argv_stk, '\n');
-           obstack_1grow (&argv_stk, 0);
-           count = 1;
-           /* Read rest of files using new filename terminator */
-           term = 0;
-           break;
-         }
-         
-       case file_list_skip:
-         break;
-       }
-    }
-  
-  if (!is_stdin)
-    fclose (fp);
-  
-  if (count == 0)
-    return;
-  
-  start = obstack_finish (&argv_stk);
-  
-  if (term == 0)
-    for (p = start; *p; p += strlen (p) + 1)
-      if (p[0] == '-')
-       count++;
-  
-  new_argc = state->argc + count;
-  new_argv = xmalloc (sizeof (state->argv[0]) * (new_argc + 1));
-  memcpy (new_argv, state->argv, sizeof (state->argv[0]) * (state->argc + 1));
-  state->argv = new_argv;
-  memmove (&state->argv[state->next + count], &state->argv[state->next],
-          (state->argc - state->next + 1) * sizeof (state->argv[0]));
-  
-  state->argc = new_argc;
-  
-  for (i = state->next, p = start; *p; p += strlen (p) + 1, i++)
-    {
-      if (term == 0 && p[0] == '-')
-       state->argv[i++] = "--add-file";
-      state->argv[i] = p;
-    }
-}
-
 \f
 static char *
 tar_help_filter (int key, const char *text, void *input)
@@ -1260,15 +1101,15 @@ tar_help_filter (int key, const char *text, void *input)
     default:
       s = (char*) text;
       break;
-      
+
     case 'j':
       s = xasprintf (_("filter the archive through %s"), BZIP2_PROGRAM);
       break;
-      
+
     case 'z':
       s = xasprintf (_("filter the archive through %s"), GZIP_PROGRAM);
       break;
-      
+
     case 'Z':
       s = xasprintf (_("filter the archive through %s"), COMPRESS_PROGRAM);
       break;
@@ -1276,29 +1117,36 @@ tar_help_filter (int key, const char *text, void *input)
     case LZIP_OPTION:
       s = xasprintf (_("filter the archive through %s"), LZIP_PROGRAM);
       break;
-      
+
     case LZMA_OPTION:
       s = xasprintf (_("filter the archive through %s"), LZMA_PROGRAM);
       break;
-      
+
+    case LZOP_OPTION:
+      s = xasprintf (_("filter the archive through %s"), LZOP_PROGRAM);
+
     case 'J':
       s = xasprintf (_("filter the archive through %s"), XZ_PROGRAM);
       break;
-      
+
     case ARGP_KEY_HELP_EXTRA:
-      obstack_init (&stk);
-      s = _("Valid arguments for the --quoting-style option are:");
-      obstack_grow (&stk, s, strlen (s));
-      obstack_grow (&stk, "\n\n", 2);
-      tar_list_quoting_styles (&stk, "  ");
-      s = _("\n*This* tar defaults to:\n");
-      obstack_grow (&stk, s, strlen (s));
-      s = format_default_settings ();
-      obstack_grow (&stk, s, strlen (s));
-      obstack_1grow (&stk, '\n');
-      obstack_1grow (&stk, 0);
-      s = xstrdup (obstack_finish (&stk));
-      obstack_free (&stk, NULL);
+      {
+       const char *tstr;
+
+       obstack_init (&stk);
+       tstr = _("Valid arguments for the --quoting-style option are:");
+       obstack_grow (&stk, tstr, strlen (tstr));
+       obstack_grow (&stk, "\n\n", 2);
+       tar_list_quoting_styles (&stk, "  ");
+       tstr = _("\n*This* tar defaults to:\n");
+       obstack_grow (&stk, tstr, strlen (tstr));
+       s = format_default_settings ();
+       obstack_grow (&stk, s, strlen (s));
+       obstack_1grow (&stk, '\n');
+       obstack_1grow (&stk, 0);
+       s = xstrdup (obstack_finish (&stk));
+       obstack_free (&stk, NULL);
+      }
     }
   return s;
 }
@@ -1308,7 +1156,7 @@ expand_pax_option (struct tar_args *targs, const char *arg)
 {
   struct obstack stk;
   char *res;
-  
+
   obstack_init (&stk);
   while (*arg)
     {
@@ -1329,8 +1177,8 @@ expand_pax_option (struct tar_args *targs, const char *arg)
              tmp[len-2] = 0;
              if (get_date_or_file (targs, "--pax-option", tmp, &ts) == 0)
                {
-                 char buf[UINTMAX_STRSIZE_BOUND], *s;
-                 s = umaxtostr (ts.tv_sec, buf);
+                 char buf[TIMESPEC_STRSIZE_BOUND];
+                 char const *s = code_timespec (ts, buf);
                  obstack_grow (&stk, s, strlen (s));
                }
              else
@@ -1356,6 +1204,113 @@ expand_pax_option (struct tar_args *targs, const char *arg)
   return res;
 }
 
+\f
+static uintmax_t
+parse_owner_group (char *arg, uintmax_t field_max, char const **name_option)
+{
+  uintmax_t u = UINTMAX_MAX;
+  char *end;
+  char const *name = 0;
+  char const *invalid_num = 0;
+  char *colon = strchr (arg, ':');
+
+  if (colon)
+    {
+      char const *num = colon + 1;
+      *colon = '\0';
+      if (*arg)
+       name = arg;
+      if (num && (! (xstrtoumax (num, &end, 10, &u, "") == LONGINT_OK
+                    && u <= field_max)))
+       invalid_num = num;
+    }
+  else
+    {
+      uintmax_t u1;
+      switch ('0' <= *arg && *arg <= '9'
+             ? xstrtoumax (arg, &end, 10, &u1, "")
+             : LONGINT_INVALID)
+       {
+       default:
+         name = arg;
+         break;
+
+       case LONGINT_OK:
+         if (u1 <= field_max)
+           {
+             u = u1;
+             break;
+           }
+         /* Fall through.  */
+       case LONGINT_OVERFLOW:
+         invalid_num = arg;
+         break;
+       }
+    }
+
+  if (invalid_num)
+    FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (invalid_num),
+                 _("Invalid owner or group ID")));
+  if (name)
+    *name_option = name;
+  return u;
+}
+
+#define TAR_SIZE_SUFFIXES "bBcGgkKMmPTtw"
+
+static char const *const sort_mode_arg[] = {
+  "none",
+  "name",
+#if D_INO_IN_DIRENT
+  "inode",
+#endif
+  NULL
+};
+
+static int sort_mode_flag[] = {
+    SAVEDIR_SORT_NONE,
+    SAVEDIR_SORT_NAME,
+#if D_INO_IN_DIRENT
+    SAVEDIR_SORT_INODE
+#endif
+};
+
+ARGMATCH_VERIFY (sort_mode_arg, sort_mode_flag);
+
+static char const *const hole_detection_args[] =
+{
+  "raw", "seek", NULL
+};
+
+static int const hole_detection_types[] =
+{
+  HOLE_DETECTION_RAW, HOLE_DETECTION_SEEK
+};
+
+ARGMATCH_VERIFY (hole_detection_args, hole_detection_types);
+
+\f
+static void
+set_old_files_option (int code, struct option_locus *loc)
+{
+  struct option_locus *prev;
+  static char const *const code_to_opt[] = {
+    "--overwrite-dir",
+    "--no-overwrite-dir",
+    "--overwrite",
+    "--unlink-first",
+    "--keep-old-files",
+    "--skip-old-files",
+    "--keep-newer-files"
+  };
+
+  prev = optloc_save (OC_OLD_FILES, loc);
+  if (prev && optloc_eq (loc, prev) && code != old_files_option)
+    option_conflict_error (code_to_opt[code], code_to_opt[old_files_option]);
+
+  old_files_option = code;
+}
+\f
 \f
 static error_t
 parse_opt (int key, char *arg, struct argp_state *state)
@@ -1366,22 +1321,22 @@ parse_opt (int key, char *arg, struct argp_state *state)
     {
     case ARGP_KEY_ARG:
       /* File name or non-parsed option, because of ARGP_IN_ORDER */
-      name_add_name (arg, MAKE_INCL_OPTIONS (args));
+      name_add_name (arg);
       args->input_files = true;
       break;
-      
+
     case 'A':
       set_subcommand_option (CAT_SUBCOMMAND);
       break;
-      
+
     case 'a':
       args->compress_autodetect = true;
       break;
-      
+
     case NO_AUTO_COMPRESS_OPTION:
       args->compress_autodetect = false;
       break;
-      
+
     case 'b':
       {
        uintmax_t u;
@@ -1393,116 +1348,133 @@ parse_opt (int key, char *arg, struct argp_state *state)
                        _("Invalid blocking factor")));
       }
       break;
-      
+
     case 'B':
       /* Try to reblock input records.  For reading 4.2BSD pipes.  */
-      
+
       /* It would surely make sense to exchange -B and -R, but it seems
         that -B has been used for a long while in Sun tar and most
         BSD-derived systems.  This is a consequence of the block/record
         terminology confusion.  */
-      
+
       read_full_records_option = true;
       break;
-      
+
     case 'c':
       set_subcommand_option (CREATE_SUBCOMMAND);
       break;
-      
-    case 'C':
-      name_add_dir (arg);
+
+    case CLAMP_MTIME_OPTION:
+      set_mtime_option = CLAMP_MTIME;
       break;
-      
+
     case 'd':
       set_subcommand_option (DIFF_SUBCOMMAND);
       break;
-      
+
     case 'f':
       if (archive_names == allocated_archive_names)
        archive_name_array = x2nrealloc (archive_name_array,
                                         &allocated_archive_names,
                                         sizeof (archive_name_array[0]));
-      
+
       archive_name_array[archive_names++] = arg;
       break;
-      
+
     case 'F':
       /* Since -F is only useful with -M, make it implied.  Run this
         script at the end of each tape.  */
-      
+
       info_script_option = arg;
       multi_volume_option = true;
       break;
-      
+
+    case FULL_TIME_OPTION:
+      full_time_option = true;
+      break;
+
     case 'g':
+      optloc_save (OC_LISTED_INCREMENTAL, args->loc);
       listed_incremental_option = arg;
       after_date_option = true;
       /* Fall through.  */
-      
+
     case 'G':
       /* We are making an incremental dump (FIXME: are we?); save
         directories at the beginning of the archive, and include in each
         directory its contents.  */
-      
+
       incremental_option = true;
       break;
-      
+
     case 'h':
       /* Follow symbolic links.  */
       dereference_option = true;
       break;
-      
+
     case HARD_DEREFERENCE_OPTION:
       hard_dereference_option = true;
       break;
-      
+
     case 'i':
       /* Ignore zero blocks (eofs).  This can't be the default,
         because Unix tar writes two blocks of zeros, then pads out
         the record with garbage.  */
-      
+
       ignore_zeros_option = true;
       break;
-      
+
     case 'j':
-      set_use_compress_program_option (BZIP2_PROGRAM);
+      set_use_compress_program_option (BZIP2_PROGRAM, args->loc);
       break;
-      
+
     case 'J':
-      set_use_compress_program_option (XZ_PROGRAM);
+      set_use_compress_program_option (XZ_PROGRAM, args->loc);
       break;
-      
+
     case 'k':
       /* Don't replace existing files.  */
-      old_files_option = KEEP_OLD_FILES;
+      set_old_files_option (KEEP_OLD_FILES, args->loc);
       break;
-      
+
     case 'K':
+      optloc_save (OC_STARTING_FILE, args->loc);
       starting_file_option = true;
       addname (arg, 0, true, NULL);
       break;
-      
+
     case ONE_FILE_SYSTEM_OPTION:
       /* When dumping directories, don't dump files/subdirectories
         that are on other filesystems. */
       one_file_system_option = true;
       break;
-      
-    case 'l':
-      check_links_option = 1;
+
+    case ONE_TOP_LEVEL_OPTION:
+      optloc_save (OC_ONE_TOP_LEVEL, args->loc);
+      one_top_level_option = true;
+      one_top_level_dir = arg;
       break;
-      
+
+    case 'l':
+      check_links_option = 1;
+      break;
+
     case 'L':
       {
        uintmax_t u;
-       if (xstrtoumax (arg, 0, 10, &u, "") != LONGINT_OK)
+       char *p;
+
+       if (xstrtoumax (arg, &p, 10, &u, TAR_SIZE_SUFFIXES) != LONGINT_OK)
          USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (arg),
                        _("Invalid tape length")));
-       tape_length_option = 1024 * (tarlong) u;
+       if (p > arg && !strchr (TAR_SIZE_SUFFIXES, p[-1]))
+         tape_length_option = 1024 * (tarlong) u;
+       else
+         tape_length_option = (tarlong) u;
        multi_volume_option = true;
       }
       break;
-      
+
     case LEVEL_OPTION:
       {
        char *p;
@@ -1511,96 +1483,109 @@ parse_opt (int key, char *arg, struct argp_state *state)
          USAGE_ERROR ((0, 0, _("Invalid incremental level value")));
       }
       break;
-      
+
     case LZIP_OPTION:
-      set_use_compress_program_option (LZIP_PROGRAM);
+      set_use_compress_program_option (LZIP_PROGRAM, args->loc);
       break;
-      
+
     case LZMA_OPTION:
-      set_use_compress_program_option (LZMA_PROGRAM);
+      set_use_compress_program_option (LZMA_PROGRAM, args->loc);
       break;
-      
+
     case LZOP_OPTION:
-      set_use_compress_program_option (LZOP_PROGRAM);
+      set_use_compress_program_option (LZOP_PROGRAM, args->loc);
       break;
-      
+
     case 'm':
       touch_option = true;
       break;
-      
+
     case 'M':
       /* Make multivolume archive: when we can't write any more into
         the archive, re-open it, and continue writing.  */
-      
+
       multi_volume_option = true;
       break;
-      
+
     case MTIME_OPTION:
       get_date_or_file (args, "--mtime", arg, &mtime_option);
-      set_mtime_option = true;
+      if (set_mtime_option == USE_FILE_MTIME)
+        set_mtime_option = FORCE_MTIME;
       break;
-      
+
     case 'n':
       seek_option = 1;
       break;
-      
+
     case NO_SEEK_OPTION:
       seek_option = 0;
       break;
-      
+
     case 'N':
       after_date_option = true;
       /* Fall through.  */
-      
+
     case NEWER_MTIME_OPTION:
-      if (NEWER_OPTION_INITIALIZED (newer_mtime_option))
+      if (TIME_OPTION_INITIALIZED (newer_mtime_option))
        USAGE_ERROR ((0, 0, _("More than one threshold date")));
       get_date_or_file (args,
                        key == NEWER_MTIME_OPTION ? "--newer-mtime"
                        : "--after-date", arg, &newer_mtime_option);
+      optloc_save (OC_NEWER, args->loc);
       break;
-      
+
     case 'o':
       args->o_option = true;
       break;
-      
+
     case 'O':
       to_stdout_option = true;
       break;
-      
+
     case 'p':
       same_permissions_option = true;
       break;
-      
+
     case 'P':
+      optloc_save (OC_ABSOLUTE_NAMES, args->loc);
       absolute_names_option = true;
       break;
-      
+
     case 'r':
       set_subcommand_option (APPEND_SUBCOMMAND);
       break;
-      
+
     case 'R':
       /* Print block numbers for debugging bad tar archives.  */
-      
+
       /* It would surely make sense to exchange -B and -R, but it seems
         that -B has been used for a long while in Sun tar and most
         BSD-derived systems.  This is a consequence of the block/record
         terminology confusion.  */
-      
+
       block_number_option = true;
       break;
-      
+
     case 's':
       /* Names to extract are sorted.  */
-      
+      optloc_save (OC_SAME_ORDER, args->loc);
       same_order_option = true;
       break;
-      
+
     case 'S':
       sparse_option = true;
       break;
-      
+
+    case SKIP_OLD_FILES_OPTION:
+      set_old_files_option (SKIP_OLD_FILES, args->loc);
+      break;
+
+    case HOLE_DETECTION_OPTION:
+      hole_detection = XARGMATCH ("--hole-detection", arg,
+                                 hole_detection_args, hole_detection_types);
+      sparse_option = true;
+      break;
+
     case SPARSE_VERSION_OPTION:
       sparse_option = true;
       {
@@ -1616,79 +1601,58 @@ parse_opt (int key, char *arg, struct argp_state *state)
          }
       }
       break;
-      
+
     case 't':
       set_subcommand_option (LIST_SUBCOMMAND);
       verbose_option++;
       break;
-      
+
     case TEST_LABEL_OPTION:
       set_subcommand_option (TEST_LABEL_SUBCOMMAND);
       break;
-      
-    case 'T':
-      update_argv (arg, state);
-      /* Indicate we've been given -T option. This is for backward
-        compatibility only, so that `tar cfT archive /dev/null will
-        succeed */
-      files_from_option = true;
-      break;
-      
+
     case 'u':
       set_subcommand_option (UPDATE_SUBCOMMAND);
       break;
-      
+
     case 'U':
-      old_files_option = UNLINK_FIRST_OLD_FILES;
+      set_old_files_option (UNLINK_FIRST_OLD_FILES, args->loc);
       break;
-      
+
     case UTC_OPTION:
       utc_option = true;
       break;
-      
+
     case 'v':
       verbose_option++;
       warning_option |= WARN_VERBOSE_WARNINGS;
       break;
-      
+
     case 'V':
       volume_label_option = arg;
       break;
-      
+
     case 'w':
       interactive_option = true;
       break;
-      
+
     case 'W':
+      optloc_save (OC_VERIFY, args->loc);
       verify_option = true;
       break;
-      
+
     case 'x':
       set_subcommand_option (EXTRACT_SUBCOMMAND);
       break;
-      
-    case 'X':
-      if (add_exclude_file (add_exclude, excluded, arg,
-                           MAKE_EXCL_OPTIONS (args), '\n')
-         != 0)
-       {
-         int e = errno;
-         FATAL_ERROR ((0, e, "%s", quotearg_colon (arg)));
-       }
-      break;
-      
+
     case 'z':
-      set_use_compress_program_option (GZIP_PROGRAM);
+      set_use_compress_program_option (GZIP_PROGRAM, args->loc);
       break;
-      
+
     case 'Z':
-      set_use_compress_program_option (COMPRESS_PROGRAM);
-      break;
-      
-    case ANCHORED_OPTION:
-      args->matching_flags |= EXCLUDE_ANCHORED;
+      set_use_compress_program_option (COMPRESS_PROGRAM, args->loc);
       break;
-      
+
     case ATIME_PRESERVE_OPTION:
       atime_preserve_option =
        (arg
@@ -1700,20 +1664,20 @@ parse_opt (int key, char *arg, struct argp_state *state)
                      _("--atime-preserve='system' is not supported"
                        " on this platform")));
       break;
-      
+
     case CHECK_DEVICE_OPTION:
       check_device_option = true;
       break;
-      
+
     case NO_CHECK_DEVICE_OPTION:
       check_device_option = false;
       break;
-      
+
     case CHECKPOINT_OPTION:
       if (arg)
        {
          char *p;
-         
+
          if (*arg == '.')
            {
              checkpoint_compile_action (".");
@@ -1727,108 +1691,74 @@ parse_opt (int key, char *arg, struct argp_state *state)
       else
        checkpoint_option = DEFAULT_CHECKPOINT;
       break;
-      
+
     case CHECKPOINT_ACTION_OPTION:
       checkpoint_compile_action (arg);
       break;
-      
+
     case BACKUP_OPTION:
       backup_option = true;
       if (arg)
        args->version_control_string = arg;
       break;
-      
+
     case DELAY_DIRECTORY_RESTORE_OPTION:
       delay_directory_restore_option = true;
       break;
-      
+
     case NO_DELAY_DIRECTORY_RESTORE_OPTION:
       delay_directory_restore_option = false;
       break;
-      
+
     case DELETE_OPTION:
       set_subcommand_option (DELETE_SUBCOMMAND);
       break;
-      
-    case EXCLUDE_BACKUPS_OPTION:
-      add_exclude_array (backup_file_table);
-      break;
-      
-    case EXCLUDE_OPTION:
-      add_exclude (excluded, arg, MAKE_EXCL_OPTIONS (args));
-      break;
-      
-    case EXCLUDE_CACHES_OPTION:
-      add_exclusion_tag ("CACHEDIR.TAG", exclusion_tag_contents,
-                        cachedir_file_p);
-      break;
-      
-    case EXCLUDE_CACHES_UNDER_OPTION:
-      add_exclusion_tag ("CACHEDIR.TAG", exclusion_tag_under,
-                        cachedir_file_p);
-      break;
-      
-    case EXCLUDE_CACHES_ALL_OPTION:
-      add_exclusion_tag ("CACHEDIR.TAG", exclusion_tag_all,
-                        cachedir_file_p);
-      break;
-      
-    case EXCLUDE_TAG_OPTION:
-      add_exclusion_tag (arg, exclusion_tag_contents, NULL);
-      break;
-      
-    case EXCLUDE_TAG_UNDER_OPTION:
-      add_exclusion_tag (arg, exclusion_tag_under, NULL);
-      break;
-      
-    case EXCLUDE_TAG_ALL_OPTION:
-      add_exclusion_tag (arg, exclusion_tag_all, NULL);
-      break;
-      
-    case EXCLUDE_VCS_OPTION:
-      add_exclude_array (vcs_file_table);
-      break;
-      
+
     case FORCE_LOCAL_OPTION:
       force_local_option = true;
       break;
-      
+
     case 'H':
       set_archive_format (arg);
       break;
-      
+
     case INDEX_FILE_OPTION:
       index_file_name = arg;
       break;
-      
-    case IGNORE_CASE_OPTION:
-      args->matching_flags |= FNM_CASEFOLD;
-      break;
-      
+
     case IGNORE_COMMAND_ERROR_OPTION:
       ignore_command_error_option = true;
       break;
-      
+
     case IGNORE_FAILED_READ_OPTION:
       ignore_failed_read_option = true;
       break;
-      
+
+    case KEEP_DIRECTORY_SYMLINK_OPTION:
+      keep_directory_symlink_option = true;
+      break;
+
     case KEEP_NEWER_FILES_OPTION:
-      old_files_option = KEEP_NEWER_FILES;
+      set_old_files_option (KEEP_NEWER_FILES, args->loc);
       break;
-      
+
     case GROUP_OPTION:
-      if (! (strlen (arg) < GNAME_FIELD_SIZE
-            && gname_to_gid (arg, &group_option)))
-       {
-         uintmax_t g;
-         if (xstrtoumax (arg, 0, 10, &g, "") == LONGINT_OK
-             && g == (gid_t) g)
-           group_option = g;
-         else
-           FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (arg),
-                         _("Invalid group")));
-       }
+      {
+       uintmax_t u = parse_owner_group (arg, TYPE_MAXIMUM (gid_t),
+                                        &group_name_option);
+       if (u == UINTMAX_MAX)
+         {
+           group_option = -1;
+           if (group_name_option)
+             gname_to_gid (group_name_option, &group_option);
+         }
+       else
+         group_option = u;
+      }
+      break;
+
+    case GROUP_MAP_OPTION:
+      group_map_read (arg);
       break;
       
     case MODE_OPTION:
@@ -1838,50 +1768,26 @@ parse_opt (int key, char *arg, struct argp_state *state)
       initial_umask = umask (0);
       umask (initial_umask);
       break;
-      
-    case NO_ANCHORED_OPTION:
-      args->include_anchored = 0; /* Clear the default for comman line args */
-      args->matching_flags &= ~ EXCLUDE_ANCHORED;
-      break;
-      
-    case NO_IGNORE_CASE_OPTION:
-      args->matching_flags &= ~ FNM_CASEFOLD;
-      break;
-      
+
     case NO_IGNORE_COMMAND_ERROR_OPTION:
       ignore_command_error_option = false;
       break;
-      
+
     case NO_OVERWRITE_DIR_OPTION:
-      old_files_option = NO_OVERWRITE_DIR_OLD_FILES;
+      set_old_files_option (NO_OVERWRITE_DIR_OLD_FILES, args->loc);
       break;
-      
+
     case NO_QUOTE_CHARS_OPTION:
       for (;*arg; arg++)
        set_char_quoting (NULL, *arg, 0);
       break;
-      
-    case NO_WILDCARDS_OPTION:
-      args->wildcards = disable_wildcards;
-      break;
-      
-    case NO_WILDCARDS_MATCH_SLASH_OPTION:
-      args->matching_flags |= FNM_FILE_NAME;
-      break;
-      
-    case NULL_OPTION:
-      filename_terminator = '\0';
-      break;
-      
-    case NO_NULL_OPTION:
-      filename_terminator = '\n';
-      break;
-      
+
     case NUMERIC_OWNER_OPTION:
       numeric_owner_option = true;
       break;
-      
+
     case OCCURRENCE_OPTION:
+      optloc_save (OC_OCCURRENCE, args->loc);
       if (!arg)
        occurrence_option = 1;
       else
@@ -1894,38 +1800,47 @@ parse_opt (int key, char *arg, struct argp_state *state)
                          _("Invalid number")));
        }
       break;
-      
+
+    case OLD_ARCHIVE_OPTION:
+      set_archive_format ("v7");
+      break;
+
     case OVERWRITE_DIR_OPTION:
-      old_files_option = DEFAULT_OLD_FILES;
+      set_old_files_option (DEFAULT_OLD_FILES, args->loc);
       break;
-      
+
     case OVERWRITE_OPTION:
-      old_files_option = OVERWRITE_OLD_FILES;
+      set_old_files_option (OVERWRITE_OLD_FILES, args->loc);
       break;
-      
+
     case OWNER_OPTION:
-      if (! (strlen (arg) < UNAME_FIELD_SIZE
-            && uname_to_uid (arg, &owner_option)))
-       {
-         uintmax_t u;
-         if (xstrtoumax (arg, 0, 10, &u, "") == LONGINT_OK
-             && u == (uid_t) u)
-           owner_option = u;
-         else
-           FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (arg),
-                         _("Invalid owner")));
-       }
+      {
+       uintmax_t u = parse_owner_group (arg, TYPE_MAXIMUM (uid_t),
+                                        &owner_name_option);
+       if (u == UINTMAX_MAX)
+         {
+           owner_option = -1;
+           if (owner_name_option)
+             uname_to_uid (owner_name_option, &owner_option);
+         }
+       else
+         owner_option = u;
+      }
+      break;
+
+    case OWNER_MAP_OPTION:
+      owner_map_read (arg);
       break;
       
     case QUOTE_CHARS_OPTION:
       for (;*arg; arg++)
        set_char_quoting (NULL, *arg, 1);
       break;
-      
+
     case QUOTING_STYLE_OPTION:
       tar_set_quoting_style (arg);
       break;
-      
+
     case PAX_OPTION:
       {
        char *tmp = expand_pax_option (args, arg);
@@ -1934,23 +1849,16 @@ parse_opt (int key, char *arg, struct argp_state *state)
        free (tmp);
       }
       break;
-      
+
     case POSIX_OPTION:
       set_archive_format ("posix");
       break;
-      
-    case PRESERVE_OPTION:
-      /* FIXME: What it is good for? */
-      same_permissions_option = true;
-      same_order_option = true;
-      WARN ((0, 0, _("The --preserve option is deprecated, "
-                    "use --preserve-permissions --preserve-order instead")));
-      break;
-      
+
     case RECORD_SIZE_OPTION:
       {
        uintmax_t u;
-       if (! (xstrtoumax (arg, 0, 10, &u, "") == LONGINT_OK
+
+       if (! (xstrtoumax (arg, NULL, 10, &u, TAR_SIZE_SUFFIXES) == LONGINT_OK
               && u == (size_t) u))
          USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (arg),
                        _("Invalid record size")));
@@ -1961,27 +1869,27 @@ parse_opt (int key, char *arg, struct argp_state *state)
        blocking_factor = record_size / BLOCKSIZE;
       }
       break;
-      
+
     case RECURSIVE_UNLINK_OPTION:
       recursive_unlink_option = true;
       break;
-      
+
     case REMOVE_FILES_OPTION:
       remove_files_option = true;
       break;
-      
+
     case RESTRICT_OPTION:
       restrict_option = true;
       break;
-      
+
     case RMT_COMMAND_OPTION:
       rmt_command = arg;
       break;
-      
+
     case RSH_COMMAND_OPTION:
       rsh_command_option = arg;
       break;
-      
+
     case SHOW_DEFAULTS_OPTION:
       {
        char *s = format_default_settings ();
@@ -1990,7 +1898,12 @@ parse_opt (int key, char *arg, struct argp_state *state)
        free (s);
        exit (0);
       }
-      
+
+    case SHOW_SNAPSHOT_FIELD_RANGES_OPTION:
+      show_snapshot_field_ranges ();
+      close_stdout ();
+      exit (0);
+
     case STRIP_COMPONENTS_OPTION:
       {
        uintmax_t u;
@@ -2010,6 +1923,11 @@ parse_opt (int key, char *arg, struct argp_state *state)
       show_transformed_names_option = true;
       break;
 
+    case SORT_OPTION:
+      savedir_sort_order = XARGMATCH ("--sort", arg,
+                                     sort_mode_arg, sort_mode_flag);
+      break;
+
     case SUFFIX_OPTION:
       backup_option = true;
       args->backup_suffix_string = arg;
@@ -2033,53 +1951,61 @@ parse_opt (int key, char *arg, struct argp_state *state)
       break;
 
     case 'I':
-      set_use_compress_program_option (arg);
+      set_use_compress_program_option (arg, args->loc);
       break;
 
     case VOLNO_FILE_OPTION:
       volno_file_option = arg;
       break;
 
-    case WILDCARDS_OPTION:
-      args->wildcards = enable_wildcards;
+    case NO_SAME_OWNER_OPTION:
+      same_owner_option = -1;
       break;
 
-    case WILDCARDS_MATCH_SLASH_OPTION:
-      args->matching_flags &= ~ FNM_FILE_NAME;
+    case NO_SAME_PERMISSIONS_OPTION:
+      same_permissions_option = -1;
       break;
 
-    case NO_RECURSION_OPTION:
-      recursion_option = 0;
+    case ACLS_OPTION:
+      set_archive_format ("posix");
+      acls_option = 1;
       break;
 
-    case NO_SAME_OWNER_OPTION:
-      same_owner_option = -1;
+    case NO_ACLS_OPTION:
+      acls_option = -1;
       break;
 
-    case NO_SAME_PERMISSIONS_OPTION:
-      same_permissions_option = -1;
+    case SELINUX_CONTEXT_OPTION:
+      set_archive_format ("posix");
+      selinux_context_option = 1;
       break;
 
-    case RECURSION_OPTION:
-      recursion_option = FNM_LEADING_DIR;
+    case NO_SELINUX_CONTEXT_OPTION:
+      selinux_context_option = -1;
       break;
 
-    case SAME_OWNER_OPTION:
-      same_owner_option = 1;
+    case XATTR_OPTION:
+      set_xattr_option (1);
+      break;
+
+    case NO_XATTR_OPTION:
+      set_xattr_option (-1);
       break;
 
-    case UNQUOTE_OPTION:
-      unquote_option = true;
+    case XATTR_INCLUDE:
+    case XATTR_EXCLUDE:
+      set_xattr_option (1);
+      xattrs_mask_add (arg, (key == XATTR_INCLUDE));
       break;
 
-    case NO_UNQUOTE_OPTION:
-      unquote_option = false;
+    case SAME_OWNER_OPTION:
+      same_owner_option = 1;
       break;
 
     case WARNING_OPTION:
       set_warning_option (arg);
       break;
-      
+
     case '0':
     case '1':
     case '2':
@@ -2123,7 +2049,7 @@ parse_opt (int key, char *arg, struct argp_state *state)
            break;
 
          default:
-           argp_error (state, _("Unknown density: `%c'"), arg[0]);
+           argp_error (state, _("Unknown density: '%c'"), arg[0]);
          }
        sprintf (cursor, "%d", device);
 
@@ -2140,22 +2066,32 @@ parse_opt (int key, char *arg, struct argp_state *state)
 #else /* not DEVICE_PREFIX */
 
       argp_error (state,
-                 _("Options `-[0-7][lmh]' not supported by *this* tar"));
+                 _("Options '-[0-7][lmh]' not supported by *this* tar"));
 
 #endif /* not DEVICE_PREFIX */
 
+    case ARGP_KEY_ERROR:
+      if (args->loc->source == OPTS_FILE)
+       error (0, 0, _("%s:%lu: location of the error"), args->loc->name,
+              (unsigned long) args->loc->line);
+      else if (args->loc->source == OPTS_ENVIRON)
+       error (0, 0, _("error parsing %s"), args->loc->name);
+      exit (EX_USAGE);
+
     default:
       return ARGP_ERR_UNKNOWN;
     }
   return 0;
 }
 
+extern struct argp_child names_argp_children[];
+
 static struct argp argp = {
   options,
   parse_opt,
   N_("[FILE]..."),
   doc,
-  NULL,
+  names_argp_children,
   tar_help_filter,
   NULL
 };
@@ -2170,8 +2106,8 @@ usage (int status)
 
 /* Parse the options for tar.  */
 
-static struct argp_option *
-find_argp_option (struct argp_option *o, int letter)
+static struct argp_option const *
+find_argp_option_key (struct argp_option const *o, int key)
 {
   for (;
        !(o->name == NULL
@@ -2179,58 +2115,141 @@ find_argp_option (struct argp_option *o, int letter)
         && o->arg == 0
         && o->flags == 0
         && o->doc == NULL); o++)
-    if (o->key == letter)
+    if (o->key == key)
       return o;
   return NULL;
 }
 
+static struct argp_option const *
+find_argp_option (struct argp *ap, int key)
+{
+  struct argp_option const *p = NULL;
+  struct argp_child const *child;
+  
+  p = find_argp_option_key (ap->options, key);
+  if (!p && ap->children)
+    {
+      for (child = ap->children; child->argp; child++)
+       {
+         p = find_argp_option_key (child->argp->options, key);
+         if (p)
+           break;
+       }
+    }
+  return p;
+}
+  
 static const char *tar_authors[] = {
   "John Gilmore",
   "Jay Fenlason",
   NULL
 };
+\f
+/* Subcommand classes */
+#define SUBCL_READ    0x01   /* subcommand reads from the archive */
+#define SUBCL_WRITE   0x02   /* subcommand writes to the archive */
+#define SUBCL_UPDATE  0x04   /* subcommand updates existing archive */
+#define SUBCL_TEST    0x08   /* subcommand tests archive header or meta-info */
+#define SUBCL_OCCUR   0x10   /* subcommand allows the use of the occurrence
+                               option */
+
+static int subcommand_class[] = {
+  /* UNKNOWN_SUBCOMMAND */     0,
+  /* APPEND_SUBCOMMAND  */     SUBCL_WRITE|SUBCL_UPDATE,
+  /* CAT_SUBCOMMAND     */     SUBCL_WRITE,
+  /* CREATE_SUBCOMMAND  */     SUBCL_WRITE,
+  /* DELETE_SUBCOMMAND  */     SUBCL_WRITE|SUBCL_UPDATE|SUBCL_OCCUR,
+  /* DIFF_SUBCOMMAND    */     SUBCL_READ|SUBCL_OCCUR,
+  /* EXTRACT_SUBCOMMAND */     SUBCL_READ|SUBCL_OCCUR,
+  /* LIST_SUBCOMMAND    */     SUBCL_READ|SUBCL_OCCUR,
+  /* UPDATE_SUBCOMMAND  */     SUBCL_WRITE|SUBCL_UPDATE,
+  /* TEST_LABEL_SUBCOMMAND */  SUBCL_TEST
+};
+
+/* Return t if the subcommand_option is in class(es) f */
+#define IS_SUBCOMMAND_CLASS(f) (subcommand_class[subcommand_option] & (f))
+
+static struct tar_args args;
+
+void
+more_options (int argc, char **argv, struct option_locus *loc)
+{
+  int idx;
+
+  args.loc = loc;
+  if (argp_parse (&argp, argc, argv, ARGP_IN_ORDER|ARGP_NO_EXIT, &idx, &args))
+    abort (); /* shouldn't happen */
+  if (loc->source == OPTS_ENVIRON && args.input_files)
+    USAGE_ERROR ((0, 0, _("non-option arguments in %s"), loc->name));
+}
+
+static void
+parse_default_options (void)
+{
+  char *opts = getenv ("TAR_OPTIONS");
+  struct wordsplit ws;
+  struct option_locus loc = { OPTS_ENVIRON, "TAR_OPTIONS", 0, 0 };
+
+  if (!opts)
+    return;
+
+  ws.ws_offs = 1;
+  if (wordsplit (opts, &ws, WRDSF_DEFFLAGS|WRDSF_DOOFFS))
+    FATAL_ERROR ((0, 0, _("cannot split TAR_OPTIONS: %s"),
+                 wordsplit_strerror (&ws)));
+  if (ws.ws_wordc)
+    {
+      ws.ws_wordv[0] = (char*) program_name;
+      more_options (ws.ws_offs + ws.ws_wordc, ws.ws_wordv, &loc);
+    }
+
+  wordsplit_free (&ws);
+}
 
 static void
 decode_options (int argc, char **argv)
 {
   int idx;
-  struct tar_args args;
+  struct option_locus loc = { OPTS_COMMAND_LINE, 0, 0, 0 };
 
   argp_version_setup ("tar", tar_authors);
-  
+
   /* Set some default option values.  */
   args.textual_date = NULL;
-  args.wildcards = default_wildcards;
-  args.matching_flags = 0;
-  args.include_anchored = EXCLUDE_ANCHORED;
   args.o_option = false;
   args.pax_option = false;
   args.backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
   args.version_control_string = 0;
   args.input_files = false;
   args.compress_autodetect = false;
-  
+
   subcommand_option = UNKNOWN_SUBCOMMAND;
   archive_format = DEFAULT_FORMAT;
   blocking_factor = DEFAULT_BLOCKING;
   record_size = DEFAULT_BLOCKING * BLOCKSIZE;
   excluded = new_exclude ();
+  hole_detection = HOLE_DETECTION_DEFAULT;
+
   newer_mtime_option.tv_sec = TYPE_MINIMUM (time_t);
   newer_mtime_option.tv_nsec = -1;
+  mtime_option.tv_sec = TYPE_MINIMUM (time_t);
+  mtime_option.tv_nsec = -1;
   recursion_option = FNM_LEADING_DIR;
   unquote_option = true;
   tar_sparse_major = 1;
   tar_sparse_minor = 0;
 
-  owner_option = -1;
-  group_option = -1;
+  savedir_sort_order = SAVEDIR_SORT_NONE;
+
+  owner_option = -1; owner_name_option = NULL;
+  group_option = -1; group_name_option = NULL;
 
   check_device_option = true;
 
   incremental_level = -1;
 
   seek_option = -1;
-  
+
   /* Convert old-style tar call by exploding option element and rearranging
      options accordingly.  */
 
@@ -2261,17 +2280,17 @@ decode_options (int argc, char **argv)
 
       for (letter = *in++; *letter; letter++)
        {
-         struct argp_option *opt;
+         struct argp_option const *opt;
 
          buffer[1] = *letter;
          *out++ = xstrdup (buffer);
-         opt = find_argp_option (options, *letter);
+         opt = find_argp_option (&argp, *letter);
          if (opt && opt->arg)
            {
              if (in < argv + argc)
                *out++ = *in++;
              else
-               USAGE_ERROR ((0, 0, _("Old option `%c' requires an argument."),
+               USAGE_ERROR ((0, 0, _("Old option '%c' requires an argument."),
                              *letter));
            }
        }
@@ -2289,13 +2308,12 @@ decode_options (int argc, char **argv)
     }
 
   /* Parse all options and non-options as they appear.  */
+  parse_default_options ();
 
-  prepend_default_options (getenv ("TAR_OPTIONS"), &argc, &argv);
-
+  args.loc = &loc;
   if (argp_parse (&argp, argc, argv, ARGP_IN_ORDER, &idx, &args))
     exit (TAREXIT_FAILURE);
 
-
   /* Special handling for 'o' option:
 
      GNU tar used to say "output old format".
@@ -2322,14 +2340,10 @@ decode_options (int argc, char **argv)
   /* Handle operands after any "--" argument.  */
   for (; idx < argc; idx++)
     {
-      name_add_name (argv[idx], MAKE_INCL_OPTIONS (&args));
+      name_add_name (argv[idx]);
       args.input_files = true;
     }
 
-  /* Warn about implicit use of the wildcards in command line arguments.
-     See TODO */
-  warn_regex_usage = args.wildcards == default_wildcards;
-
   /* Derive option values and check option consistency.  */
 
   if (archive_format == DEFAULT_FORMAT)
@@ -2353,12 +2367,14 @@ decode_options (int argc, char **argv)
       if (!args.input_files)
        USAGE_ERROR ((0, 0,
                      _("--occurrence is meaningless without a file list")));
-      if (subcommand_option != DELETE_SUBCOMMAND
-         && subcommand_option != DIFF_SUBCOMMAND
-         && subcommand_option != EXTRACT_SUBCOMMAND
-         && subcommand_option != LIST_SUBCOMMAND)
-           USAGE_ERROR ((0, 0,
-                         _("--occurrence cannot be used in the requested operation mode")));
+      if (!IS_SUBCOMMAND_CLASS (SUBCL_OCCUR))
+       {
+         if (option_set_in_cl (OC_OCCURRENCE))
+           option_conflict_error ("--occurrence",
+                                  subcommand_string (subcommand_option));
+         else
+           occurrence_option = 0;
+       }
     }
 
   if (archive_names == 0)
@@ -2372,20 +2388,29 @@ decode_options (int argc, char **argv)
        archive_name_array[0] = DEFAULT_ARCHIVE;
     }
 
-  /* Allow multiple archives only with `-M'.  */
+  /* Allow multiple archives only with '-M'.  */
 
   if (archive_names > 1 && !multi_volume_option)
     USAGE_ERROR ((0, 0,
-                 _("Multiple archive files require `-M' option")));
+                 _("Multiple archive files require '-M' option")));
 
   if (listed_incremental_option
-      && NEWER_OPTION_INITIALIZED (newer_mtime_option))
-    USAGE_ERROR ((0, 0,
-                 _("Cannot combine --listed-incremental with --newer")));
+      && TIME_OPTION_INITIALIZED (newer_mtime_option))
+    {
+      struct option_locus *listed_loc = optloc_lookup (OC_LISTED_INCREMENTAL);
+      struct option_locus *newer_loc = optloc_lookup (OC_NEWER);
+      if (optloc_eq (listed_loc, newer_loc))
+       option_conflict_error ("--listed-incremental", "--newer");
+      else if (listed_loc->source == OPTS_COMMAND_LINE)
+       listed_incremental_option = NULL;
+      else
+       memset (&newer_mtime_option, 0, sizeof (newer_mtime_option));
+    }
+
   if (incremental_level != -1 && !listed_incremental_option)
     WARN ((0, 0,
           _("--level is meaningless without --listed-incremental")));
-  
+
   if (volume_label_option)
     {
       if (archive_format == GNU_FORMAT || archive_format == OLDGNU_FORMAT)
@@ -2417,35 +2442,127 @@ decode_options (int argc, char **argv)
        USAGE_ERROR ((0, 0, _("Cannot verify multi-volume archives")));
       if (use_compress_program_option)
        USAGE_ERROR ((0, 0, _("Cannot verify compressed archives")));
+      if (!IS_SUBCOMMAND_CLASS (SUBCL_WRITE))
+       {
+         if (option_set_in_cl (OC_VERIFY))
+           option_conflict_error ("--verify",
+                                  subcommand_string (subcommand_option));
+         else
+           verify_option = false;
+       }
     }
 
   if (use_compress_program_option)
     {
       if (multi_volume_option)
        USAGE_ERROR ((0, 0, _("Cannot use multi-volume compressed archives")));
-      if (subcommand_option == UPDATE_SUBCOMMAND
-         || subcommand_option == APPEND_SUBCOMMAND
-         || subcommand_option == DELETE_SUBCOMMAND)
+      if (IS_SUBCOMMAND_CLASS (SUBCL_UPDATE))
        USAGE_ERROR ((0, 0, _("Cannot update compressed archives")));
       if (subcommand_option == CAT_SUBCOMMAND)
        USAGE_ERROR ((0, 0, _("Cannot concatenate compressed archives")));
     }
 
+  if (set_mtime_option == CLAMP_MTIME)
+    {
+      if (!TIME_OPTION_INITIALIZED (mtime_option))
+       USAGE_ERROR ((0, 0,
+                     _("--clamp-mtime needs a date specified using --mtime")));
+    }
+
   /* It is no harm to use --pax-option on non-pax archives in archive
      reading mode. It may even be useful, since it allows to override
      file attributes from tar headers. Therefore I allow such usage.
      --gray */
   if (args.pax_option
       && archive_format != POSIX_FORMAT
-      && (subcommand_option != EXTRACT_SUBCOMMAND
-         || subcommand_option != DIFF_SUBCOMMAND
-         || subcommand_option != LIST_SUBCOMMAND))
+      && !IS_SUBCOMMAND_CLASS (SUBCL_READ))
     USAGE_ERROR ((0, 0, _("--pax-option can be used only on POSIX archives")));
 
+  /* star creates non-POSIX typed archives with xattr support, so allow the
+     extra headers when reading */
+  if ((acls_option > 0)
+      && archive_format != POSIX_FORMAT
+      && !IS_SUBCOMMAND_CLASS (SUBCL_READ))
+    USAGE_ERROR ((0, 0, _("--acls can be used only on POSIX archives")));
+
+  if ((selinux_context_option > 0)
+      && archive_format != POSIX_FORMAT
+      && !IS_SUBCOMMAND_CLASS (SUBCL_READ))
+    USAGE_ERROR ((0, 0, _("--selinux can be used only on POSIX archives")));
+
+  if ((xattrs_option > 0)
+      && archive_format != POSIX_FORMAT
+      && !IS_SUBCOMMAND_CLASS (SUBCL_READ))
+    USAGE_ERROR ((0, 0, _("--xattrs can be used only on POSIX archives")));
+
+  if (starting_file_option && !IS_SUBCOMMAND_CLASS (SUBCL_READ))
+    {
+      if (option_set_in_cl (OC_STARTING_FILE))
+       option_conflict_error ("--starting-file",
+                              subcommand_string (subcommand_option));
+      else
+       starting_file_option = false;
+    }
+
+  if (same_order_option && !IS_SUBCOMMAND_CLASS (SUBCL_READ))
+    {
+      if (option_set_in_cl (OC_SAME_ORDER))
+       option_conflict_error ("--same-order",
+                              subcommand_string (subcommand_option));
+      else
+       same_order_option = false;
+    }
+
+  if (one_top_level_option)
+    {
+      char *base;
+
+      if (absolute_names_option)
+       {
+         struct option_locus *one_top_level_loc =
+           optloc_lookup (OC_ONE_TOP_LEVEL);
+         struct option_locus *absolute_names_loc =
+           optloc_lookup (OC_ABSOLUTE_NAMES);
+
+         if (optloc_eq (one_top_level_loc, absolute_names_loc))
+           option_conflict_error ("--one-top-level", "--absolute-names");
+         else if (one_top_level_loc->source == OPTS_COMMAND_LINE)
+           absolute_names_option = false;
+         else
+           one_top_level_option = false;
+       }
+
+      if (one_top_level_option && !one_top_level_dir)
+       {
+         /* If the user wants to guarantee that everything is under one
+            directory, determine its name now and let it be created later.  */
+         base = base_name (archive_name_array[0]);
+         one_top_level_dir = strip_compression_suffix (base);
+         free (base);
+
+         if (!one_top_level_dir)
+           USAGE_ERROR ((0, 0,
+                         _("Cannot deduce top-level directory name; "
+                           "please set it explicitly with --one-top-level=DIR")));
+       }
+    }
+
   /* If ready to unlink hierarchies, so we are for simpler files.  */
   if (recursive_unlink_option)
     old_files_option = UNLINK_FIRST_OLD_FILES;
 
+  /* Flags for accessing files to be read from or copied into.  POSIX says
+     O_NONBLOCK has unspecified effect on most types of files, but in
+     practice it never harms and sometimes helps.  */
+  {
+    int base_open_flags =
+      (O_BINARY | O_CLOEXEC | O_NOCTTY | O_NONBLOCK
+       | (dereference_option ? 0 : O_NOFOLLOW)
+       | (atime_preserve_option == system_atime_preserve ? O_NOATIME : 0));
+    open_read_flags = O_RDONLY | base_open_flags;
+    open_searchdir_flags = O_SEARCH | O_DIRECTORY | base_open_flags;
+  }
+  fstatat_flags = dereference_option ? 0 : AT_SYMLINK_NOFOLLOW;
 
   if (subcommand_option == TEST_LABEL_SUBCOMMAND)
     {
@@ -2461,10 +2578,20 @@ decode_options (int argc, char **argv)
     USAGE_ERROR ((0, 0, _("Volume length cannot be less than record size")));
 
   if (same_order_option && listed_incremental_option)
-    USAGE_ERROR ((0, 0, _("--preserve-order is not compatible with "
-                         "--listed-incremental")));
-  
-  /* Forbid using -c with no input files whatsoever.  Check that `-f -',
+    {
+      struct option_locus *preserve_order_loc = optloc_lookup (OC_SAME_ORDER);
+      struct option_locus *listed_incremental_loc =
+       optloc_lookup (OC_LISTED_INCREMENTAL);
+
+      if (optloc_eq (preserve_order_loc, listed_incremental_loc))
+       option_conflict_error ("--preserve-order", "--listed-incremental");
+      else if (preserve_order_loc->source == OPTS_COMMAND_LINE)
+       listed_incremental_option = false;
+      else
+       same_order_option = false;
+    }
+
+  /* Forbid using -c with no input files whatsoever.  Check that '-f -',
      explicit or implied, is used correctly.  */
 
   switch (subcommand_option)
@@ -2475,8 +2602,8 @@ decode_options (int argc, char **argv)
                      _("Cowardly refusing to create an empty archive")));
       if (args.compress_autodetect && archive_names
          && strcmp (archive_name_array[0], "-"))
-       set_comression_program_by_suffix (archive_name_array[0],
-                                         use_compress_program_option);
+       set_compression_program_by_suffix (archive_name_array[0],
+                                          use_compress_program_option);
       break;
 
     case EXTRACT_SUBCOMMAND:
@@ -2498,7 +2625,7 @@ decode_options (int argc, char **argv)
           archive_name_cursor++)
        if (!strcmp (*archive_name_cursor, "-"))
          USAGE_ERROR ((0, 0,
-                       _("Options `-Aru' are incompatible with `-f -'")));
+                       _("Options '-Aru' are incompatible with '-f -'")));
 
     default:
       break;
@@ -2509,7 +2636,7 @@ decode_options (int argc, char **argv)
     {
       stdlis = fopen (index_file_name, "w");
       if (! stdlis)
-       open_error (index_file_name);
+       open_fatal (index_file_name);
     }
   else
     stdlis = to_stdout_option ? stderr : stdout;
@@ -2531,7 +2658,7 @@ decode_options (int argc, char **argv)
     }
 
   checkpoint_finish_compile ();
-  
+
   report_textual_dates (&args);
 }
 
@@ -2551,7 +2678,8 @@ main (int argc, char **argv)
 
   exit_failure = TAREXIT_FAILURE;
   exit_status = TAREXIT_SUCCESS;
-  filename_terminator = '\n';
+  error_hook = checkpoint_flush_actions;
+
   set_quoting_style (0, DEFAULT_QUOTING_STYLE);
 
   /* Make sure we have first three descriptors available */
@@ -2564,16 +2692,12 @@ main (int argc, char **argv)
     xmalloc (sizeof (const char *) * allocated_archive_names);
   archive_names = 0;
 
-  obstack_init (&argv_stk);
-
-  /* Ensure default behavior for some signals */
-  signal (SIGPIPE, SIG_IGN);
   /* System V fork+wait does not work if SIGCHLD is ignored.  */
   signal (SIGCHLD, SIG_DFL);
 
   /* Try to disable the ability to unlink a directory.  */
   priv_set_remove_linkdir ();
-  
+
   /* Decode options.  */
 
   decode_options (argc, argv);
@@ -2589,7 +2713,7 @@ main (int argc, char **argv)
     {
     case UNKNOWN_SUBCOMMAND:
       USAGE_ERROR ((0, 0,
-                   _("You must specify one of the `-Acdtrux' or `--test-label'  options")));
+                   _("You must specify one of the '-Acdtrux', '--delete' or '--test-label' options")));
 
     case CAT_SUBCOMMAND:
     case UPDATE_SUBCOMMAND:
@@ -2628,6 +2752,8 @@ main (int argc, char **argv)
       test_archive_label ();
     }
 
+  checkpoint_finish ();
+
   if (totals_option)
     print_total_stats ();
 
@@ -2640,6 +2766,7 @@ main (int argc, char **argv)
   /* Dispose of allocated memory, and return.  */
 
   free (archive_name_array);
+  xattrs_clear_setup ();
   name_term ();
 
   if (exit_status == TAREXIT_FAILURE)
@@ -2659,17 +2786,44 @@ tar_stat_init (struct tar_stat_info *st)
   memset (st, 0, sizeof (*st));
 }
 
+/* Close the stream or file descriptor associated with ST, and remove
+   all traces of it from ST.  Return true if successful, false (with a
+   diagnostic) otherwise.  */
+bool
+tar_stat_close (struct tar_stat_info *st)
+{
+  int status = (st->dirstream ? closedir (st->dirstream)
+               : 0 < st->fd ? close (st->fd)
+               : 0);
+  st->dirstream = 0;
+  st->fd = 0;
+
+  if (status == 0)
+    return true;
+  else
+    {
+      close_diag (st->orig_file_name);
+      return false;
+    }
+}
+
 void
 tar_stat_destroy (struct tar_stat_info *st)
 {
+  tar_stat_close (st);
+  xheader_xattr_free (st->xattr_map, st->xattr_map_size);
   free (st->orig_file_name);
   free (st->file_name);
   free (st->link_name);
   free (st->uname);
   free (st->gname);
+  free (st->cntx_name);
+  free (st->acls_a_ptr);
+  free (st->acls_d_ptr);
   free (st->sparse_map);
   free (st->dumpdir);
   xheader_destroy (&st->xhdr);
+  info_free_exclist (st);
   memset (st, 0, sizeof (*st));
 }