]> Dogcows Code - chaz/tar/blobdiff - src/tar.c
Merge recent gnulib changes, and remove some lint.
[chaz/tar] / src / tar.c
index 9de90990fb313de67cf55b44c9f41382457fb276..4df7eaa66b0253bb36f9503a3463e8f28b30f1e7 100644 (file)
--- a/src/tar.c
+++ b/src/tar.c
@@ -1,5 +1,8 @@
 /* A tar (tape archiver) program.
-   Copyright (C) 1988, 92,93,94,95,96,97, 1999 Free Software Foundation, Inc.
+
+   Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1999, 2000,
+   2001, 2003, 2004 Free Software Foundation, Inc.
+
    Written by John Gilmore, starting 1985-08-25.
 
    This program is free software; you can redistribute it and/or modify it
 
    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.,
-   59 Place - Suite 330, Boston, MA 02111-1307, USA.  */
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include "system.h"
 
+#include <fnmatch.h>
 #include <getopt.h>
 
+#include <signal.h>
+#if ! defined SIGCHLD && defined SIGCLD
+# define SIGCHLD SIGCLD
+#endif
+
 /* The following causes "common.h" to produce definitions of all the global
    variables, rather than just "extern" declarations of them.  GNU tar does
    depend on the system loader to preset all GLOBAL variables to neutral (or
-   zero) values, explicit initialisation is usually not done.  */
+   zero) values; explicit initialization is usually not done.  */
 #define GLOBAL
 #include "common.h"
 
-#include "xstrtol.h"
-
-time_t get_date ();
+#include <getdate.h>
+#include <localedir.h>
+#include <prepargs.h>
+#include <quotearg.h>
+#include <xstrtol.h>
 
 /* Local declarations.  */
 
+#ifndef DEFAULT_ARCHIVE_FORMAT
+# define DEFAULT_ARCHIVE_FORMAT GNU_FORMAT
+#endif
+
 #ifndef DEFAULT_ARCHIVE
 # define DEFAULT_ARCHIVE "tar.out"
 #endif
@@ -41,17 +56,13 @@ time_t get_date ();
 # define DEFAULT_BLOCKING 20
 #endif
 
-static void usage PARAMS ((int));
 \f
 /* Miscellaneous.  */
 
-/*----------------------------------------------.
-| Doesn't return if stdin already requested.    |
-`----------------------------------------------*/
-
 /* Name of option using stdin.  */
-static const char *stdin_used_by = NULL;
+static const char *stdin_used_by;
 
+/* Doesn't return if stdin already requested.  */
 void
 request_stdin (const char *option)
 {
@@ -62,43 +73,99 @@ request_stdin (const char *option)
   stdin_used_by = option;
 }
 
-/*--------------------------------------------------------.
-| Returns true if and only if the user typed 'y' or 'Y'.  |
-`--------------------------------------------------------*/
-
+/* Returns true if and only if the user typed 'y' or 'Y'.  */
 int
 confirm (const char *message_action, const char *message_name)
 {
-  static FILE *confirm_file = NULL;
+  static FILE *confirm_file;
+  static int confirm_file_EOF;
 
   if (!confirm_file)
     {
       if (archive == 0 || stdin_used_by)
-       confirm_file = fopen (TTY_NAME, "r");
+       {
+         confirm_file = fopen (TTY_NAME, "r");
+         if (! confirm_file)
+           open_fatal (TTY_NAME);
+       }
       else
        {
          request_stdin ("-w");
          confirm_file = stdin;
        }
-
-      if (!confirm_file)
-       FATAL_ERROR ((0, 0, _("Cannot read confirmation from user")));
     }
 
-  fprintf (stdlis, "%s %s?", message_action, message_name);
+  fprintf (stdlis, "%s %s?", message_action, quote (message_name));
   fflush (stdlis);
 
   {
-    int reply = getc (confirm_file);
+    int reply = confirm_file_EOF ? EOF : getc (confirm_file);
     int character;
 
     for (character = reply;
-        character != '\n' && character != EOF;
+        character != '\n';
         character = getc (confirm_file))
-      continue;
+      if (character == EOF)
+       {
+         confirm_file_EOF = 1;
+         fputc ('\n', stdlis);
+         fflush (stdlis);
+         break;
+       }
     return reply == 'y' || reply == 'Y';
   }
 }
+
+static struct fmttab {
+  char const *name;
+  enum archive_format fmt;
+} const fmttab[] = {
+  { "v7",      V7_FORMAT },
+  { "oldgnu",  OLDGNU_FORMAT },
+  { "ustar",   USTAR_FORMAT },
+  { "posix",   POSIX_FORMAT },
+#if 0 /* not fully supported yet */
+  { "star",    STAR_FORMAT },
+#endif
+  { "gnu",     GNU_FORMAT },
+  { NULL,       0 }
+};
+
+static void
+set_archive_format (char const *name)
+{
+  struct fmttab const *p;
+
+  for (p = fmttab; strcmp (p->name, name) != 0; )
+    if (! (++p)->name)
+      USAGE_ERROR ((0, 0, _("%s: Invalid archive format"),
+                   quotearg_colon (name)));
+
+  archive_format = p->fmt;
+}
+
+static const char *
+archive_format_string (enum archive_format fmt)
+{
+  struct fmttab const *p;
+
+  for (p = fmttab; p->name; p++)
+    if (p->fmt == fmt)
+      return p->name;
+  return "unknown?";
+}
+
+#define FORMAT_MASK(n) (1<<(n))
+
+static void
+assert_format(unsigned fmt_mask)
+{
+  if ((FORMAT_MASK(archive_format) & fmt_mask) == 0)
+    USAGE_ERROR ((0, 0,
+                 _("GNU features wanted on incompatible archive format")));
+}
+
+
 \f
 /* Options.  */
 
@@ -106,147 +173,174 @@ confirm (const char *message_action, const char *message_name)
    do it.  For the others, we share the code for the equivalent short
    named option, the name of which is stored in the otherwise-unused `val'
    field of the `struct option'; for long options that have no equivalent
-   short option, we use nongraphic characters as pseudo short option
-   characters, starting at 2 and going upwards.  */
-
-#define BACKUP_OPTION                  2
-#define DELETE_OPTION                  3
-#define EXCLUDE_OPTION                 4
-#define GROUP_OPTION                   5
-#define MODE_OPTION                    6
-#define NEWER_MTIME_OPTION             7
-#define NO_RECURSE_OPTION              8
-#define NULL_OPTION                    9
-#define OWNER_OPTION                   10
-#define POSIX_OPTION                   11
-#define PRESERVE_OPTION                        12
-#define RECORD_SIZE_OPTION             13
-#define RSH_COMMAND_OPTION             14
-#define SUFFIX_OPTION                  15
-#define USE_COMPRESS_PROGRAM_OPTION    16
-#define VOLNO_FILE_OPTION              17
-
-/* Some cleanup is being made in GNU tar long options.  Using old names is
-   allowed for a while, but will also send a warning to stderr.  Take old
-   names out in 1.14, or in summer 1997, whichever happens last.  We use
-   nongraphic characters as pseudo short option characters, starting at 31
-   and going downwards.  */
-
-#define OBSOLETE_ABSOLUTE_NAMES                31
-#define OBSOLETE_BLOCK_COMPRESS                30
-#define OBSOLETE_BLOCKING_FACTOR       29
-#define OBSOLETE_BLOCK_NUMBER          28
-#define OBSOLETE_READ_FULL_RECORDS     27
-#define OBSOLETE_TOUCH                 26
-#define OBSOLETE_VERSION_CONTROL       25
+   short option, we use non-characters as pseudo short options,
+   starting at CHAR_MAX + 1 and going upwards.  */
+
+enum
+{
+  ANCHORED_OPTION = CHAR_MAX + 1,
+  ATIME_PRESERVE_OPTION,
+  BACKUP_OPTION,
+  CHECKPOINT_OPTION,
+  DELETE_OPTION,
+  EXCLUDE_OPTION,
+  FORCE_LOCAL_OPTION,
+  FORMAT_OPTION,
+  GROUP_OPTION,
+  IGNORE_CASE_OPTION,
+  IGNORE_FAILED_READ_OPTION,
+  INDEX_FILE_OPTION,
+  KEEP_NEWER_FILES_OPTION,
+  MODE_OPTION,
+  NEWER_MTIME_OPTION,
+  NO_ANCHORED_OPTION,
+  NO_IGNORE_CASE_OPTION,
+  NO_OVERWRITE_DIR_OPTION,
+  NO_WILDCARDS_OPTION,
+  NO_WILDCARDS_MATCH_SLASH_OPTION,
+  NULL_OPTION,
+  NUMERIC_OWNER_OPTION,
+  OCCURRENCE_OPTION,
+  OVERWRITE_OPTION,
+  OWNER_OPTION,
+  PAX_OPTION,
+  POSIX_OPTION,
+  PRESERVE_OPTION,
+  RECORD_SIZE_OPTION,
+  RECURSIVE_UNLINK_OPTION,
+  REMOVE_FILES_OPTION,
+  RSH_COMMAND_OPTION,
+  SHOW_DEFAULTS_OPTION,
+  SHOW_OMITTED_DIRS_OPTION,
+  STRIP_PATH_OPTION,
+  SUFFIX_OPTION,
+  TOTALS_OPTION,
+  USE_COMPRESS_PROGRAM_OPTION,
+  UTC_OPTION,
+  VOLNO_FILE_OPTION,
+  WILDCARDS_OPTION,
+  WILDCARDS_MATCH_SLASH_OPTION
+};
 
 /* If nonzero, display usage information and exit.  */
-static int show_help = 0;
+static int show_help;
 
 /* If nonzero, print the version on standard output and exit.  */
-static int show_version = 0;
+static int show_version;
 
-struct option long_options[] =
+static struct option long_options[] =
 {
-  {"absolute-names", no_argument, NULL, 'P'},
-  {"absolute-paths", no_argument, NULL, OBSOLETE_ABSOLUTE_NAMES},
-  {"after-date", required_argument, NULL, 'N'},
-  {"append", no_argument, NULL, 'r'},
-  {"atime-preserve", no_argument, &atime_preserve_option, 1},
-  {"backup", optional_argument, NULL, BACKUP_OPTION},
-  {"block-compress", no_argument, NULL, OBSOLETE_BLOCK_COMPRESS},
-  {"block-number", no_argument, NULL, 'R'},
-  {"block-size", required_argument, NULL, OBSOLETE_BLOCKING_FACTOR},
-  {"blocking-factor", required_argument, NULL, 'b'},
-  {"catenate", no_argument, NULL, 'A'},
-  {"checkpoint", no_argument, &checkpoint_option, 1},
-  {"compare", no_argument, NULL, 'd'},
-  {"compress", no_argument, NULL, 'Z'},
-  {"concatenate", no_argument, NULL, 'A'},
-  {"confirmation", no_argument, NULL, 'w'},
+  {"absolute-names", no_argument, 0, 'P'},
+  {"after-date", required_argument, 0, 'N'},
+  {"anchored", no_argument, 0, ANCHORED_OPTION},
+  {"append", no_argument, 0, 'r'},
+  {"atime-preserve", no_argument, 0, ATIME_PRESERVE_OPTION},
+  {"backup", optional_argument, 0, BACKUP_OPTION},
+  {"block-number", no_argument, 0, 'R'},
+  {"blocking-factor", required_argument, 0, 'b'},
+  {"bzip2", no_argument, 0, 'j'},
+  {"catenate", no_argument, 0, 'A'},
+  {"checkpoint", no_argument, 0, CHECKPOINT_OPTION},
+  {"check-links", no_argument, &check_links_option, 1},
+  {"compare", no_argument, 0, 'd'},
+  {"compress", no_argument, 0, 'Z'},
+  {"concatenate", no_argument, 0, 'A'},
+  {"confirmation", no_argument, 0, 'w'},
   /* FIXME: --selective as a synonym for --confirmation?  */
-  {"create", no_argument, NULL, 'c'},
-  {"delete", no_argument, NULL, DELETE_OPTION},
-  {"dereference", no_argument, NULL, 'h'},
-  {"diff", no_argument, NULL, 'd'},
-  {"directory", required_argument, NULL, 'C'},
-  {"ending-file", required_argument, NULL, 'E'},
-  {"exclude", required_argument, NULL, EXCLUDE_OPTION},
-  {"exclude-from", required_argument, NULL, 'X'},
-  {"extract", no_argument, NULL, 'x'},
-  {"file", required_argument, NULL, 'f'},
-  {"files-from", required_argument, NULL, 'T'},
-  {"force-local", no_argument, &force_local_option, 1},
-  {"get", no_argument, NULL, 'x'},
-  {"group", required_argument, NULL, GROUP_OPTION},
-  {"gunzip", no_argument, NULL, 'z'},
-  {"gzip", no_argument, NULL, 'z'},
+  {"create", no_argument, 0, 'c'},
+  {"delete", no_argument, 0, DELETE_OPTION},
+  {"dereference", no_argument, 0, 'h'},
+  {"diff", no_argument, 0, 'd'},
+  {"directory", required_argument, 0, 'C'},
+  {"exclude", required_argument, 0, EXCLUDE_OPTION},
+  {"exclude-from", required_argument, 0, 'X'},
+  {"extract", no_argument, 0, 'x'},
+  {"file", required_argument, 0, 'f'},
+  {"files-from", required_argument, 0, 'T'},
+  {"force-local", no_argument, 0, FORCE_LOCAL_OPTION},
+  {"format", required_argument, 0, FORMAT_OPTION},
+  {"get", no_argument, 0, 'x'},
+  {"group", required_argument, 0, GROUP_OPTION},
+  {"gunzip", no_argument, 0, 'z'},
+  {"gzip", no_argument, 0, 'z'},
   {"help", no_argument, &show_help, 1},
-  {"ignore-failed-read", no_argument, &ignore_failed_read_option, 1},
-  {"ignore-zeros", no_argument, NULL, 'i'},
+  {"ignore-case", no_argument, 0, IGNORE_CASE_OPTION},
+  {"ignore-failed-read", no_argument, 0, IGNORE_FAILED_READ_OPTION},
+  {"ignore-zeros", no_argument, 0, 'i'},
   /* FIXME: --ignore-end as a new name for --ignore-zeros?  */
-  {"incremental", no_argument, NULL, 'G'},
-  {"info-script", required_argument, NULL, 'F'},
-  {"interactive", no_argument, NULL, 'w'},
-  {"keep-old-files", no_argument, NULL, 'k'},
-  {"label", required_argument, NULL, 'V'},
-  {"list", no_argument, NULL, 't'},
-  {"listed-incremental", required_argument, NULL, 'g'},
-  {"mode", required_argument, NULL, MODE_OPTION},
-  {"modification-time", no_argument, NULL, OBSOLETE_TOUCH},
-  {"multi-volume", no_argument, NULL, 'M'},
-  {"new-volume-script", required_argument, NULL, 'F'},
-  {"newer", required_argument, NULL, 'N'},
-  {"newer-mtime", required_argument, NULL, NEWER_MTIME_OPTION},
-  {"null", no_argument, NULL, NULL_OPTION},
-  {"no-recursion", no_argument, NULL, NO_RECURSE_OPTION},
-  {"numeric-owner", no_argument, &numeric_owner_option, 1},
-  {"old-archive", no_argument, NULL, 'o'},
-  {"one-file-system", no_argument, NULL, 'l'},
-  {"owner", required_argument, NULL, OWNER_OPTION},
-  {"portability", no_argument, NULL, 'o'},
-  {"posix", no_argument, NULL, POSIX_OPTION},
-  {"preserve", no_argument, NULL, PRESERVE_OPTION},
-  {"preserve-order", no_argument, NULL, 's'},
-  {"preserve-permissions", no_argument, NULL, 'p'},
-  {"recursive-unlink", no_argument, &recursive_unlink_option, 1},
-  {"read-full-blocks", no_argument, NULL, OBSOLETE_READ_FULL_RECORDS},
-  {"read-full-records", no_argument, NULL, 'B'},
+  {"incremental", no_argument, 0, 'G'},
+  {"index-file", required_argument, 0, INDEX_FILE_OPTION},
+  {"info-script", required_argument, 0, 'F'},
+  {"interactive", no_argument, 0, 'w'},
+  {"keep-newer-files", no_argument, 0, KEEP_NEWER_FILES_OPTION},
+  {"keep-old-files", no_argument, 0, 'k'},
+  {"label", required_argument, 0, 'V'},
+  {"list", no_argument, 0, 't'},
+  {"listed-incremental", required_argument, 0, 'g'},
+  {"mode", required_argument, 0, MODE_OPTION},
+  {"multi-volume", no_argument, 0, 'M'},
+  {"new-volume-script", required_argument, 0, 'F'},
+  {"newer", required_argument, 0, 'N'},
+  {"newer-mtime", required_argument, 0, NEWER_MTIME_OPTION},
+  {"null", no_argument, 0, NULL_OPTION},
+  {"no-anchored", no_argument, 0, NO_ANCHORED_OPTION},
+  {"no-ignore-case", no_argument, 0, NO_IGNORE_CASE_OPTION},
+  {"no-overwrite-dir", no_argument, 0, NO_OVERWRITE_DIR_OPTION},
+  {"no-wildcards", no_argument, 0, NO_WILDCARDS_OPTION},
+  {"no-wildcards-match-slash", no_argument, 0, NO_WILDCARDS_MATCH_SLASH_OPTION},
+  {"no-recursion", no_argument, &recursion_option, 0},
+  {"no-same-owner", no_argument, &same_owner_option, -1},
+  {"no-same-permissions", no_argument, &same_permissions_option, -1},
+  {"numeric-owner", no_argument, 0, NUMERIC_OWNER_OPTION},
+  {"occurrence", optional_argument, 0, OCCURRENCE_OPTION},
+  {"old-archive", no_argument, 0, 'o'},
+  {"one-file-system", no_argument, 0, 'l'},
+  {"overwrite", no_argument, 0, OVERWRITE_OPTION},
+  {"owner", required_argument, 0, OWNER_OPTION},
+  {"pax-option", required_argument, 0, PAX_OPTION},
+  {"portability", no_argument, 0, 'o'},
+  {"posix", no_argument, 0, POSIX_OPTION},
+  {"preserve", no_argument, 0, PRESERVE_OPTION},
+  {"preserve-order", no_argument, 0, 's'},
+  {"preserve-permissions", no_argument, 0, 'p'},
+  {"recursion", no_argument, &recursion_option, FNM_LEADING_DIR},
+  {"recursive-unlink", no_argument, 0, RECURSIVE_UNLINK_OPTION},
+  {"read-full-records", no_argument, 0, 'B'},
   /* FIXME: --partial-blocks might be a synonym for --read-full-records?  */
-  {"record-number", no_argument, NULL, OBSOLETE_BLOCK_NUMBER},
-  {"record-size", required_argument, NULL, RECORD_SIZE_OPTION},
-  {"remove-files", no_argument, &remove_files_option, 1},
-  {"rsh-command", required_argument, NULL, RSH_COMMAND_OPTION},
-  {"same-order", no_argument, NULL, 's'},
+  {"record-size", required_argument, 0, RECORD_SIZE_OPTION},
+  {"remove-files", no_argument, 0, REMOVE_FILES_OPTION},
+  {"rsh-command", required_argument, 0, RSH_COMMAND_OPTION},
+  {"same-order", no_argument, 0, 's'},
   {"same-owner", no_argument, &same_owner_option, 1},
-  {"same-permissions", no_argument, NULL, 'p'},
-  {"show-omitted-dirs", no_argument, &show_omitted_dirs_option, 1},
-  {"sparse", no_argument, NULL, 'S'},
-  {"starting-file", required_argument, NULL, 'K'},
-  {"suffix", required_argument, NULL, SUFFIX_OPTION},
-  {"tape-length", required_argument, NULL, 'L'},
-  {"to-stdout", no_argument, NULL, 'O'},
-  {"totals", no_argument, &totals_option, 1},
-  {"touch", no_argument, NULL, 'm'},
-  {"uncompress", no_argument, NULL, 'Z'},
-  {"ungzip", no_argument, NULL, 'z'},
-  {"unlink-first", no_argument, NULL, 'U'},
-  {"update", no_argument, NULL, 'u'},
-  {"use-compress-program", required_argument, NULL, USE_COMPRESS_PROGRAM_OPTION},
-  {"verbose", no_argument, NULL, 'v'},
-  {"verify", no_argument, NULL, 'W'},
+  {"same-permissions", no_argument, 0, 'p'},
+  {"show-defaults", no_argument, 0, SHOW_DEFAULTS_OPTION},
+  {"show-omitted-dirs", no_argument, 0, SHOW_OMITTED_DIRS_OPTION},
+  {"sparse", no_argument, 0, 'S'},
+  {"starting-file", required_argument, 0, 'K'},
+  {"strip-path", required_argument, 0, STRIP_PATH_OPTION },
+  {"suffix", required_argument, 0, SUFFIX_OPTION},
+  {"tape-length", required_argument, 0, 'L'},
+  {"to-stdout", no_argument, 0, 'O'},
+  {"totals", no_argument, 0, TOTALS_OPTION},
+  {"touch", no_argument, 0, 'm'},
+  {"uncompress", no_argument, 0, 'Z'},
+  {"ungzip", no_argument, 0, 'z'},
+  {"unlink-first", no_argument, 0, 'U'},
+  {"update", no_argument, 0, 'u'},
+  {"utc", no_argument, 0, UTC_OPTION },
+  {"use-compress-program", required_argument, 0, USE_COMPRESS_PROGRAM_OPTION},
+  {"verbose", no_argument, 0, 'v'},
+  {"verify", no_argument, 0, 'W'},
   {"version", no_argument, &show_version, 1},
-  {"version-control", required_argument, NULL, OBSOLETE_VERSION_CONTROL},
-  {"volno-file", required_argument, NULL, VOLNO_FILE_OPTION},
+  {"volno-file", required_argument, 0, VOLNO_FILE_OPTION},
+  {"wildcards", no_argument, 0, WILDCARDS_OPTION},
+  {"wildcards-match-slash", no_argument, 0, WILDCARDS_MATCH_SLASH_OPTION},
 
   {0, 0, 0, 0}
 };
 
-/*---------------------------------------------.
-| Print a usage message and exit with STATUS.  |
-`---------------------------------------------*/
-
-static void
+/* Print a usage message and exit with STATUS.  */
+void
 usage (int status)
 {
   if (status != TAREXIT_SUCCESS)
@@ -258,7 +352,13 @@ usage (int status)
 GNU `tar' saves many files together into a single tape or disk archive, and\n\
 can restore individual files from the archive.\n"),
             stdout);
-      printf (_("\nUsage: %s [OPTION]... [FILE]...\n"), program_name);
+      printf (_("\nUsage: %s [OPTION]... [FILE]...\n\
+\n\
+Examples:\n\
+  %s -cf archive.tar foo bar  # Create archive.tar from files foo and bar.\n\
+  %s -tvf archive.tar         # List all files in archive.tar verbosely.\n\
+  %s -xf archive.tar          # Extract all files from archive.tar.\n"),
+            program_name, program_name, program_name, program_name);
       fputs (_("\
 \n\
 If a long option shows an argument as mandatory, then it is mandatory\n\
@@ -282,14 +382,26 @@ Main operation mode:\n\
 Operation modifiers:\n\
   -W, --verify               attempt to verify the archive after writing it\n\
       --remove-files         remove files after adding them to the archive\n\
-  -k, --keep-old-files       don't overwrite existing files when extracting\n\
+  -k, --keep-old-files       don't replace existing files when extracting\n\
+      --keep-newer-files     don't replace existing files that are newer\n\
+                             than their archive copies\n\
+      --overwrite            overwrite existing files when extracting\n\
+      --no-overwrite-dir     preserve metadata of existing directories\n\
   -U, --unlink-first         remove each file prior to extracting over it\n\
       --recursive-unlink     empty hierarchies prior to extracting directory\n\
   -S, --sparse               handle sparse files efficiently\n\
   -O, --to-stdout            extract files to standard output\n\
   -G, --incremental          handle old GNU-format incremental backup\n\
-  -g, --listed-incremental   handle new GNU-format incremental backup\n\
-      --ignore-failed-read   do not exit with nonzero on unreadable files\n"),
+  -g, --listed-incremental=FILE\n\
+                             handle new GNU-format incremental backup\n\
+      --ignore-failed-read   do not exit with nonzero on unreadable files\n\
+      --occurrence[=NUM]     process only the NUMth occurrence of each file in\n\
+                             the archive. This option is valid only in\n\
+                             conjunction with one of the subcommands --delete,\n\
+                             --diff, --extract or --list and when a list of\n\
+                             files is given either on the command line or\n\
+                             via -T option.\n\
+                             NUM defaults to 1.\n"),
             stdout);
       fputs (_("\
 \n\
@@ -300,8 +412,10 @@ Handling of file attributes:\n\
       --atime-preserve         don't change access times on dumped files\n\
   -m, --modification-time      don't extract file modified time\n\
       --same-owner             try extracting files with the same ownership\n\
+      --no-same-owner          extract files as yourself\n\
       --numeric-owner          always use numbers for user/group names\n\
-  -p, --same-permissions       extract all protection information\n\
+  -p, --same-permissions       extract permissions information\n\
+      --no-same-permissions    do not extract permissions information\n\
       --preserve-permissions   same as -p\n\
   -s, --same-order             sort names to extract to match archive\n\
       --preserve-order         same as -s\n\
@@ -331,10 +445,20 @@ Device blocking:\n\
       fputs (_("\
 \n\
 Archive format selection:\n\
+      --format=FMTNAME               create archive of the given format.\n\
+                                     FMTNAME is one of the following:\n\
+                                     v7        old V7 tar format\n\
+                                     oldgnu    GNU format as per tar <= 1.12\n\
+                                     gnu       GNU tar 1.13 format\n\
+                                     ustar     POSIX 1003.1-1988 (ustar) format\n\
+                                     posix     POSIX 1003.1-2001 (pax) format\n\
+      --old-archive, --portability   same as --format=v7\n\
+      --posix                        same as --format=posix\n\
+  --pax-option keyword[[:]=value][,keyword[[:]=value], ...]\n\
+                                     control pax keywords\n\
   -V, --label=NAME                   create archive with volume name NAME\n\
               PATTERN                at list/extract time, a globbing PATTERN\n\
-  -o, --old-archive, --portability   write a V7 format archive\n\
-      --posix                        write a POSIX conformant archive\n\
+  -j, --bzip2                        filter the archive through bzip2\n\
   -z, --gzip, --ungzip               filter the archive through gzip\n\
   -Z, --compress, --uncompress       filter the archive through compress\n\
       --use-compress-program=PROG    filter through PROG (must accept -d)\n"),
@@ -345,25 +469,34 @@ Local file selection:\n\
   -C, --directory=DIR          change to directory DIR\n\
   -T, --files-from=NAME        get names to extract or create from file NAME\n\
       --null                   -T reads null-terminated names, disable -C\n\
-      --exclude=PATTERN        exclude files, given as a globbing PATTERN\n\
-  -X, --exclude-from=FILE      exclude globbing patterns listed in FILE\n\
+      --exclude=PATTERN        exclude files, given as a PATTERN\n\
+  -X, --exclude-from=FILE      exclude patterns listed in FILE\n\
+      --anchored               exclude patterns match file name start (default)\n\
+      --no-anchored            exclude patterns match after any /\n\
+      --ignore-case            exclusion ignores case\n\
+      --no-ignore-case         exclusion is case sensitive (default)\n\
+      --wildcards              exclude patterns use wildcards (default)\n\
+      --no-wildcards           exclude patterns are plain strings\n\
+      --wildcards-match-slash  exclude pattern wildcards match '/' (default)\n\
+      --no-wildcards-match-slash exclude pattern wildcards do not match '/'\n\
   -P, --absolute-names         don't strip leading `/'s from file names\n\
   -h, --dereference            dump instead the files symlinks point to\n\
       --no-recursion           avoid descending automatically in directories\n\
   -l, --one-file-system        stay in local file system when creating archive\n\
-  -E, --ending-file=NAME       end reading the archive before file NAME\n\
-  -K, --starting-file=NAME     begin at file NAME in the archive\n"),
+  -K, --starting-file=NAME     begin at file NAME in the archive\n\
+      --strip-path=NUM         strip NUM leading components from file names\n\
+                               before extraction\n"),
             stdout);
 #if !MSDOS
       fputs (_("\
-  -N, --newer=DATE             only store files newer than DATE\n\
-      --newer-mtime            compare date and time when data changed only\n\
+  -N, --newer=DATE-OR-FILE     only store files newer than DATE-OR-FILE\n\
+      --newer-mtime=DATE       compare date and time when data changed only\n\
       --after-date=DATE        same as -N\n"),
             stdout);
 #endif
       fputs (_("\
       --backup[=CONTROL]       backup before removal, choose version control\n\
-      --suffix=SUFFIX          backup before removel, override usual suffix\n"),
+      --suffix=SUFFIX          backup before removal, override usual suffix\n"),
             stdout);
       fputs (_("\
 \n\
@@ -372,13 +505,23 @@ Informative output:\n\
       --version         print tar program version number, then exit\n\
   -v, --verbose         verbosely list files processed\n\
       --checkpoint      print directory names while reading the archive\n\
+      --check-links     print a message if not all links are dumped\n\
       --totals          print total bytes written while creating archive\n\
+      --index-file=FILE send verbose output to FILE\n\
+      --utc             print file modification dates in UTC\n\
   -R, --block-number    show block number within archive with each message\n\
   -w, --interactive     ask for confirmation for every action\n\
       --confirmation    same as -w\n"),
             stdout);
       fputs (_("\
 \n\
+Compatibility options:\n\
+  -o                                 when creating, same as --old-archive\n\
+                                     when extracting, same as --no-same-owner\n"),
+             stdout);
+
+      fputs (_("\
+\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\
@@ -388,31 +531,30 @@ The version control may be set with --backup or VERSION_CONTROL, values are:\n\
             stdout);
       printf (_("\
 \n\
-GNU tar cannot read nor produce `--posix' archives.  If POSIXLY_CORRECT\n\
-is set in the environment, GNU extensions are disallowed with `--posix'.\n\
-Support for POSIX is only partially implemented, don't count on it yet.\n\
-ARCHIVE may be FILE, HOST:FILE or USER@HOST:FILE; and FILE may be a file\n\
-or a device.  *This* `tar' defaults to `-f%s -b%d'.\n"),
+ARCHIVE may be FILE, HOST:FILE or USER@HOST:FILE; DATE may be a textual date\n\
+or a file name starting with `/' or `.', in which case the file's date is used.\n\
+*This* `tar' defaults to `--format=%s -f%s -b%d'.\n"),
+             archive_format_string (DEFAULT_ARCHIVE_FORMAT),
              DEFAULT_ARCHIVE, DEFAULT_BLOCKING);
-      fputs (_("\
-\n\
-Report bugs to <tar-bugs@gnu.ai.mit.edu>.\n"),
-              stdout);
+      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
     }
   exit (status);
 }
 
-/*----------------------------.
-| Parse the options for tar.  |
-`----------------------------*/
+/* Parse the options for tar.  */
 
-/* Available option letters are DHIJQY and aejnqy.  Some are reserved:
+/* Available option letters are DEHIJQY and aenqy.  Some are reserved:
 
+   e  exit immediately with a nonzero exit status if unexpected errors occur
+   E  use extended headers (draft POSIX headers, that is)
+   I  same as T (for compatibility with Solaris tar)
+   n  the archive is quickly seekable, so don't worry about random seeks
+   q  stop after extracting the first occurrence of the named file
    y  per-file gzip compression
    Y  per-block gzip compression */
 
 #define OPTION_STRING \
-  "-01234567ABC:E:F:GK:L:MN:OPRST:UV:WX:Zb:cdf:g:hiklmoprstuvwxz"
+  "-01234567ABC:F:GIK:L:MN:OPRST:UV:WX:Zb:cdf:g:hijklmoprstuvwxyz"
 
 static void
 set_subcommand_option (enum subcommand subcommand)
@@ -435,12 +577,16 @@ set_use_compress_program_option (const char *string)
 }
 
 static void
-decode_options (int argc, char *const *argv)
+decode_options (int argc, char **argv)
 {
   int optchar;                 /* option letter */
   int input_files;             /* number of input files */
-  const char *backup_suffix_string;
-  const char *version_control_string = NULL;
+  char const *textual_date_option = 0;
+  char const *backup_suffix_string;
+  char const *version_control_string = 0;
+  int exclude_options = EXCLUDE_WILDCARDS;
+  bool o_option = 0;
+  int pax_option = 0;
 
   /* Set some default option values.  */
 
@@ -448,6 +594,10 @@ decode_options (int argc, char *const *argv)
   archive_format = DEFAULT_FORMAT;
   blocking_factor = DEFAULT_BLOCKING;
   record_size = DEFAULT_BLOCKING * BLOCKSIZE;
+  excluded = new_exclude ();
+  newer_mtime_option.tv_sec = TYPE_MINIMUM (time_t);
+  newer_mtime_option.tv_nsec = -1;
+  recursion_option = FNM_LEADING_DIR;
 
   owner_option = -1;
   group_option = -1;
@@ -475,7 +625,7 @@ decode_options (int argc, char *const *argv)
       /* Allocate a new argument array, and copy program name in it.  */
 
       new_argc = argc - 1 + strlen (argv[1]);
-      new_argv = (char **) xmalloc (new_argc * sizeof (char *));
+      new_argv = xmalloc ((new_argc + 1) * sizeof (char *));
       in = argv;
       out = new_argv;
       *out++ = *in++;
@@ -502,6 +652,7 @@ decode_options (int argc, char *const *argv)
 
       while (in < argv + argc)
        *out++ = *in++;
+      *out = 0;
 
       /* Replace the old option list by the new one.  */
 
@@ -513,8 +664,10 @@ decode_options (int argc, char *const *argv)
 
   input_files = 0;
 
-  while (optchar = getopt_long (argc, argv, OPTION_STRING, long_options, NULL),
-        optchar != EOF)
+  prepend_default_options (getenv ("TAR_OPTIONS"), &argc, &argv);
+
+  while (optchar = getopt_long (argc, argv, OPTION_STRING, long_options, 0),
+        optchar != -1)
     switch (optchar)
       {
       case '?':
@@ -525,7 +678,7 @@ decode_options (int argc, char *const *argv)
 
       case 1:
        /* File name or non-parsed option, because of RETURN_IN_ORDER
-          ordering triggerred by the leading dash in OPTION_STRING.  */
+          ordering triggered by the leading dash in OPTION_STRING.  */
 
        name_add (optarg);
        input_files++;
@@ -535,30 +688,18 @@ decode_options (int argc, char *const *argv)
        set_subcommand_option (CAT_SUBCOMMAND);
        break;
 
-      case OBSOLETE_BLOCK_COMPRESS:
-       WARN ((0, 0, _("Obsolete option, now implied by --blocking-factor")));
-       break;
-
-      case OBSOLETE_BLOCKING_FACTOR:
-       WARN ((0, 0, _("Obsolete option name replaced by --blocking-factor")));
-       /* Fall through.  */
-
       case 'b':
        {
-         long l;
-         if (! (xstrtol (optarg, (char **) 0, 10, &l, "") == LONGINT_OK
-                && l == (blocking_factor = l)
+         uintmax_t u;
+         if (! (xstrtoumax (optarg, 0, 10, &u, "") == LONGINT_OK
+                && u == (blocking_factor = u)
                 && 0 < blocking_factor
-                && l == (record_size = l * (size_t) BLOCKSIZE) / BLOCKSIZE))
-           USAGE_ERROR ((0, 0, _("Invalid blocking factor")));
+                && u == (record_size = u * BLOCKSIZE) / BLOCKSIZE))
+           USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (optarg),
+                         _("Invalid blocking factor")));
        }
        break;
 
-      case OBSOLETE_READ_FULL_RECORDS:
-       WARN ((0, 0,
-              _("Obsolete option name replaced by --read-full-records")));
-       /* Fall through.  */
-
       case 'B':
        /* Try to reblock input records.  For reading 4.2BSD pipes.  */
 
@@ -567,7 +708,7 @@ decode_options (int argc, char *const *argv)
           BSD-derived systems.  This is a consequence of the block/record
           terminology confusion.  */
 
-       read_full_records_option = 1;
+       read_full_records_option = true;
        break;
 
       case 'c':
@@ -583,15 +724,11 @@ decode_options (int argc, char *const *argv)
        set_subcommand_option (DIFF_SUBCOMMAND);
        break;
 
-      case 'E':
-       ending_file_option = optarg;
-       break;
-
       case 'f':
        if (archive_names == allocated_archive_names)
          {
            allocated_archive_names *= 2;
-           archive_name_array = (const char **)
+           archive_name_array =
              xrealloc (archive_name_array,
                        sizeof (const char *) * allocated_archive_names);
          }
@@ -603,11 +740,12 @@ decode_options (int argc, char *const *argv)
           script at the end of each tape.  */
 
        info_script_option = optarg;
-       multi_volume_option = 1;
+       multi_volume_option = true;
        break;
 
       case 'g':
        listed_incremental_option = optarg;
+       after_date_option = true;
        /* Fall through.  */
 
       case 'G':
@@ -615,13 +753,12 @@ decode_options (int argc, char *const *argv)
           directories at the beginning of the archive, and include in each
           directory its contents.  */
 
-       incremental_option = 1;
+       incremental_option = true;
        break;
 
       case 'h':
        /* Follow symbolic links.  */
-
-       dereference_option = 1;
+       dereference_option = true;
        break;
 
       case 'i':
@@ -629,101 +766,115 @@ decode_options (int argc, char *const *argv)
           because Unix tar writes two blocks of zeros, then pads out
           the record with garbage.  */
 
-       ignore_zeros_option = 1;
+       ignore_zeros_option = true;
        break;
 
-      case 'k':
-       /* Don't overwrite existing files.  */
+      case 'I':
+       USAGE_ERROR ((0, 0,
+                     _("Warning: the -I option is not supported;"
+                       " perhaps you meant -j or -T?")));
+       break;
+
+      case 'j':
+       set_use_compress_program_option ("bzip2");
+       break;
 
-       keep_old_files_option = 1;
+      case 'k':
+       /* Don't replace existing files.  */
+       old_files_option = KEEP_OLD_FILES;
        break;
 
       case 'K':
-       starting_file_option = 1;
-       addname (optarg);
+       starting_file_option = true;
+       addname (optarg, 0);
        break;
 
       case 'l':
        /* When dumping directories, don't dump files/subdirectories
           that are on other filesystems.  */
 
-       one_file_system_option = 1;
+       one_file_system_option = true;
        break;
 
       case 'L':
        {
-         unsigned long u;
-         if (xstrtoul (optarg, (char **) 0, 10, &u, "") != LONG_MAX)
-           USAGE_ERROR ((0, 0, _("Invalid tape length")));
-         clear_tarlong (tape_length_option);
-         add_to_tarlong (tape_length_option, u);
-         mult_tarlong (tape_length_option, 1024);
-         multi_volume_option = 1;
+         uintmax_t u;
+         if (xstrtoumax (optarg, 0, 10, &u, "") != LONGINT_OK)
+           USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (optarg),
+                         _("Invalid tape length")));
+         tape_length_option = 1024 * (tarlong) u;
+         multi_volume_option = true;
        }
        break;
 
-      case OBSOLETE_TOUCH:
-       WARN ((0, 0, _("Obsolete option name replaced by --touch")));
-       /* Fall through.  */
-
       case 'm':
-       touch_option = 1;
+       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 = 1;
+       multi_volume_option = true;
        break;
 
 #if !MSDOS
       case 'N':
-       after_date_option = 1;
+       after_date_option = true;
        /* Fall through.  */
 
       case NEWER_MTIME_OPTION:
-       if (newer_mtime_option)
+       if (NEWER_OPTION_INITIALIZED (newer_mtime_option))
          USAGE_ERROR ((0, 0, _("More than one threshold date")));
 
-       newer_mtime_option = get_date (optarg, (voidstar) 0);
-       if (newer_mtime_option == (time_t) -1)
-         USAGE_ERROR ((0, 0, _("Invalid date format `%s'"), optarg));
+       if (FILESYSTEM_PREFIX_LEN (optarg) != 0
+           || ISSLASH (*optarg)
+           || *optarg == '.')
+         {
+           struct stat st;
+           if (deref_stat (dereference_option, optarg, &st) != 0)
+             {
+               stat_error (optarg);
+               USAGE_ERROR ((0, 0, _("Date file not found")));
+             }
+           newer_mtime_option.tv_sec = st.st_mtime;
+           newer_mtime_option.tv_nsec = TIMESPEC_NS (st.st_mtim);
+         }
+       else
+         {
+           if (! get_date (&newer_mtime_option, optarg, NULL))
+             {
+               WARN ((0, 0, _("Substituting %s for unknown date format %s"),
+                      tartime (newer_mtime_option.tv_sec), quote (optarg)));
+               newer_mtime_option.tv_nsec = 0;
+             }
+           else
+             textual_date_option = optarg;
+         }
 
        break;
 #endif /* not MSDOS */
 
       case 'o':
-       if (archive_format == DEFAULT_FORMAT)
-         archive_format = V7_FORMAT;
-       else if (archive_format != V7_FORMAT)
-         USAGE_ERROR ((0, 0, _("Conflicting archive format options")));
+       o_option = true;
        break;
 
       case 'O':
-       to_stdout_option = 1;
+       to_stdout_option = true;
        break;
 
       case 'p':
-       same_permissions_option = 1;
+       same_permissions_option = true;
        break;
 
-      case OBSOLETE_ABSOLUTE_NAMES:
-       WARN ((0, 0, _("Obsolete option name replaced by --absolute-names")));
-       /* Fall through.  */
-
       case 'P':
-       absolute_names_option = 1;
+       absolute_names_option = true;
        break;
 
       case 'r':
        set_subcommand_option (APPEND_SUBCOMMAND);
        break;
 
-      case OBSOLETE_BLOCK_NUMBER:
-       WARN ((0, 0, _("Obsolete option name replaced by --block-number")));
-       /* Fall through.  */
-
       case 'R':
        /* Print block numbers for debugging bad tar archives.  */
 
@@ -732,17 +883,17 @@ decode_options (int argc, char *const *argv)
           BSD-derived systems.  This is a consequence of the block/record
           terminology confusion.  */
 
-       block_number_option = 1;
+       block_number_option = true;
        break;
 
       case 's':
        /* Names to extr are sorted.  */
 
-       same_order_option = 1;
+       same_order_option = true;
        break;
 
       case 'S':
-       sparse_option = 1;
+       sparse_option = true;
        break;
 
       case 't':
@@ -759,7 +910,11 @@ decode_options (int argc, char *const *argv)
        break;
 
       case 'U':
-       unlink_first_option = 1;
+       old_files_option = UNLINK_FIRST_OLD_FILES;
+       break;
+
+      case UTC_OPTION:
+       utc_option = true;
        break;
 
       case 'v':
@@ -771,11 +926,11 @@ decode_options (int argc, char *const *argv)
        break;
 
       case 'w':
-       interactive_option = 1;
+       interactive_option = true;
        break;
 
       case 'W':
-       verify_option = 1;
+       verify_option = true;
        break;
 
       case 'x':
@@ -783,8 +938,19 @@ decode_options (int argc, char *const *argv)
        break;
 
       case 'X':
-       exclude_option = 1;
-       add_exclude_file (optarg);
+       if (add_exclude_file (add_exclude, excluded, optarg,
+                             exclude_options | recursion_option, '\n')
+           != 0)
+         {
+           int e = errno;
+           FATAL_ERROR ((0, e, "%s", quotearg_colon (optarg)));
+         }
+       break;
+
+      case 'y':
+       USAGE_ERROR ((0, 0,
+                     _("Warning: the -y option is not supported;"
+                       " perhaps you meant -j?")));
        break;
 
       case 'z':
@@ -795,12 +961,20 @@ decode_options (int argc, char *const *argv)
        set_use_compress_program_option ("compress");
        break;
 
-      case OBSOLETE_VERSION_CONTROL:
-       WARN ((0, 0, _("Obsolete option name replaced by --backup")));
-       /* Fall through.  */
+      case ANCHORED_OPTION:
+       exclude_options |= EXCLUDE_ANCHORED;
+       break;
+
+      case ATIME_PRESERVE_OPTION:
+       atime_preserve_option = true;
+       break;
+
+      case CHECKPOINT_OPTION:
+       checkpoint_option = true;
+       break;
 
       case BACKUP_OPTION:
-       backup_option = 1;
+       backup_option = true;
        if (optarg)
          version_control_string = optarg;
        break;
@@ -810,8 +984,31 @@ decode_options (int argc, char *const *argv)
        break;
 
       case EXCLUDE_OPTION:
-       exclude_option = 1;
-       add_exclude (optarg);
+       add_exclude (excluded, optarg, exclude_options | recursion_option);
+       break;
+
+      case FORCE_LOCAL_OPTION:
+       force_local_option = true;
+       break;
+
+      case FORMAT_OPTION:
+       set_archive_format (optarg);
+       break;
+
+      case INDEX_FILE_OPTION:
+       index_file_name = optarg;
+       break;
+
+      case IGNORE_CASE_OPTION:
+       exclude_options |= FNM_CASEFOLD;
+       break;
+
+      case IGNORE_FAILED_READ_OPTION:
+       ignore_failed_read_option = true;
+       break;
+
+      case KEEP_NEWER_FILES_OPTION:
+       old_files_option = KEEP_NEWER_FILES;
        break;
 
       case GROUP_OPTION:
@@ -819,11 +1016,12 @@ decode_options (int argc, char *const *argv)
               && gname_to_gid (optarg, &group_option)))
          {
            uintmax_t g;
-           if (xstrtoumax (optarg, (char **) 0, 10, &g, "") == LONGINT_OK
+           if (xstrtoumax (optarg, 0, 10, &g, "") == LONGINT_OK
                && g == (gid_t) g)
              group_option = g;
            else
-             ERROR ((TAREXIT_FAILURE, 0, _("Invalid group given on option")));
+             FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (optarg),
+                           _("%s: Invalid group")));
          }
        break;
 
@@ -832,57 +1030,92 @@ decode_options (int argc, char *const *argv)
          = mode_compile (optarg,
                          MODE_MASK_EQUALS | MODE_MASK_PLUS | MODE_MASK_MINUS);
        if (mode_option == MODE_INVALID)
-         ERROR ((TAREXIT_FAILURE, 0, _("Invalid mode given on option")));
+         FATAL_ERROR ((0, 0, _("Invalid mode given on option")));
        if (mode_option == MODE_MEMORY_EXHAUSTED)
-         ERROR ((TAREXIT_FAILURE, 0, _("Memory exhausted")));
+         xalloc_die ();
+       break;
+
+      case NO_ANCHORED_OPTION:
+       exclude_options &= ~ EXCLUDE_ANCHORED;
+       break;
+
+      case NO_IGNORE_CASE_OPTION:
+       exclude_options &= ~ FNM_CASEFOLD;
+       break;
+
+      case NO_OVERWRITE_DIR_OPTION:
+       old_files_option = NO_OVERWRITE_DIR_OLD_FILES;
        break;
 
-      case NO_RECURSE_OPTION:
-       no_recurse_option = 1;
+      case NO_WILDCARDS_OPTION:
+       exclude_options &= ~ EXCLUDE_WILDCARDS;
+       break;
+
+      case NO_WILDCARDS_MATCH_SLASH_OPTION:
+       exclude_options |= FNM_FILE_NAME;
        break;
 
       case NULL_OPTION:
        filename_terminator = '\0';
        break;
 
+      case NUMERIC_OWNER_OPTION:
+       numeric_owner_option = true;
+       break;
+
+      case OCCURRENCE_OPTION:
+       if (!optarg)
+         occurrence_option = 1;
+       else
+         {
+           uintmax_t u;
+           if (xstrtoumax (optarg, 0, 10, &u, "") == LONGINT_OK)
+             occurrence_option = u;
+           else
+             FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (optarg),
+                           _("Invalid number")));
+         }
+       break;
+
+      case OVERWRITE_OPTION:
+       old_files_option = OVERWRITE_OLD_FILES;
+       break;
+
       case OWNER_OPTION:
        if (! (strlen (optarg) < UNAME_FIELD_SIZE
               && uname_to_uid (optarg, &owner_option)))
          {
            uintmax_t u;
-           if (xstrtoumax (optarg, (char **) 0, 10, &u, "") == LONGINT_OK
+           if (xstrtoumax (optarg, 0, 10, &u, "") == LONGINT_OK
                && u == (uid_t) u)
              owner_option = u;
            else
-             ERROR ((TAREXIT_FAILURE, 0, _("Invalid owner given on option")));
+             FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (optarg),
+                           _("Invalid owner")));
          }
        break;
 
+      case PAX_OPTION:
+       pax_option++;
+       xheader_set_option (optarg);
+       break;
+
       case POSIX_OPTION:
-#if OLDGNU_COMPATIBILITY
-       if (archive_format == DEFAULT_FORMAT)
-         archive_format = GNU_FORMAT;
-       else if (archive_format != GNU_FORMAT)
-         USAGE_ERROR ((0, 0, _("Conflicting archive format options")));
-#else
-       if (archive_format == DEFAULT_FORMAT)
-         archive_format = POSIX_FORMAT;
-       else if (archive_format != POSIX_FORMAT)
-         USAGE_ERROR ((0, 0, _("Conflicting archive format options")));
-#endif
+       set_archive_format ("posix");
        break;
 
       case PRESERVE_OPTION:
-       same_permissions_option = 1;
-       same_order_option = 1;
+       same_permissions_option = true;
+       same_order_option = true;
        break;
 
       case RECORD_SIZE_OPTION:
        {
          uintmax_t u;
-         if (! (xstrtoumax (optarg, (char **) 0, 10, &u, "") == LONG_MAX
+         if (! (xstrtoumax (optarg, 0, 10, &u, "") == LONGINT_OK
                 && u == (size_t) u))
-           USAGE_ERROR ((0, 0, _("Invalid record size")));
+           USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (optarg),
+                         _("Invalid record size")));
          record_size = u;
          if (record_size % BLOCKSIZE != 0)
            USAGE_ERROR ((0, 0, _("Record size must be a multiple of %d."),
@@ -891,23 +1124,60 @@ decode_options (int argc, char *const *argv)
        }
        break;
 
+      case RECURSIVE_UNLINK_OPTION:
+       recursive_unlink_option = true;
+       break;
+
+      case REMOVE_FILES_OPTION:
+       remove_files_option = true;
+       break;
+
       case RSH_COMMAND_OPTION:
        rsh_command_option = optarg;
        break;
 
+      case SHOW_DEFAULTS_OPTION:
+       printf ("--format=%s -f%s -b%d\n",
+               archive_format_string (DEFAULT_ARCHIVE_FORMAT),
+               DEFAULT_ARCHIVE, DEFAULT_BLOCKING);
+       exit(0);
+
+      case STRIP_PATH_OPTION:
+       {
+         uintmax_t u;
+         if (! (xstrtoumax (optarg, 0, 10, &u, "") == LONGINT_OK
+                && u == (size_t) u))
+           USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (optarg),
+                         _("Invalid number of elements")));
+         strip_path_elements = u;
+       }
+       break;
+
       case SUFFIX_OPTION:
-       backup_option = 1;
+       backup_option = true;
        backup_suffix_string = optarg;
        break;
 
-      case VOLNO_FILE_OPTION:
-       volno_file_option = optarg;
+      case TOTALS_OPTION:
+       totals_option = true;
        break;
 
       case USE_COMPRESS_PROGRAM_OPTION:
        set_use_compress_program_option (optarg);
        break;
 
+      case VOLNO_FILE_OPTION:
+       volno_file_option = optarg;
+       break;
+
+      case WILDCARDS_OPTION:
+       exclude_options |= EXCLUDE_WILDCARDS;
+       break;
+
+      case WILDCARDS_MATCH_SLASH_OPTION:
+       exclude_options &= ~ FNM_FILE_NAME;
+       break;
+
       case '0':
       case '1':
       case '2':
@@ -924,7 +1194,7 @@ decode_options (int argc, char *const *argv)
          static char buf[sizeof DEVICE_PREFIX + 10];
          char *cursor;
 
-         density = getopt_long (argc, argv, "lmh", NULL, NULL);
+         density = getopt_long (argc, argv, "lmh", 0, 0);
          strcpy (buf, DEVICE_PREFIX);
          cursor = buf + strlen (buf);
 
@@ -968,14 +1238,11 @@ decode_options (int argc, char *const *argv)
          if (archive_names == allocated_archive_names)
            {
              allocated_archive_names *= 2;
-             archive_name_array = (const char **)
+             archive_name_array =
                xrealloc (archive_name_array,
                          sizeof (const char *) * allocated_archive_names);
            }
-         archive_name_array[archive_names++] = buf;
-
-         /* FIXME: How comes this works for many archives when buf is
-            not xstrdup'ed?  */
+         archive_name_array[archive_names++] = strdup (buf);
        }
        break;
 
@@ -987,23 +1254,49 @@ decode_options (int argc, char *const *argv)
 #endif /* not DEVICE_PREFIX */
       }
 
+  /* Special handling for 'o' option:
+
+     GNU tar used to say "output old format".
+     UNIX98 tar says don't chown files after extracting (we use
+     "--no-same-owner" for this).
+
+     The old GNU tar semantics is retained when used with --create
+     option, otherwise UNIX98 semantics is assumed */
+
+  if (o_option)
+    {
+      if (subcommand_option == CREATE_SUBCOMMAND)
+       {
+         /* GNU Tar <= 1.13 compatibility */
+         set_archive_format ("v7");
+       }
+      else
+       {
+         /* UNIX98 compatibility */
+         same_owner_option = 1;
+       }
+    }
+
+  /* Handle operands after any "--" argument.  */
+  for (; optind < argc; optind++)
+    {
+      name_add (argv[optind]);
+      input_files++;
+    }
+
   /* Process trivial options.  */
 
   if (show_version)
     {
-      printf ("tar (GNU %s) %s\n", PACKAGE, VERSION);
-      fputs (_("\
-\n\
-Copyright (C) 1988, 92,93,94,95,96,97,98, 1999 Free Software Foundation, Inc.\n"),
-            stdout);
-      fputs (_("\
-This is free software; see the source for copying conditions.  There is NO\n\
-warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"),
-            stdout);
-      fputs (_("\
-\n\
-Written by John Gilmore and Jay Fenlason.\n"),
-            stdout);
+      printf ("tar (%s) %s\n%s\n", PACKAGE_NAME, PACKAGE_VERSION,
+             "Copyright (C) 2004 Free Software Foundation, Inc.");
+      puts (_("\
+This program comes with NO WARRANTY, to the extent permitted by law.\n\
+You may redistribute it under the terms of the GNU General Public License;\n\
+see the file named COPYING for details."));
+
+      puts (_("Written by John Gilmore and Jay Fenlason."));
+
       exit (TAREXIT_SUCCESS);
     }
 
@@ -1014,21 +1307,37 @@ Written by John Gilmore and Jay Fenlason.\n"),
 
   if (archive_format == DEFAULT_FORMAT)
     {
-#if OLDGNU_COMPATIBILITY
-      archive_format = OLDGNU_FORMAT;
-#else
-      archive_format = GNU_FORMAT;
-#endif
+      if (pax_option)
+       archive_format = POSIX_FORMAT;
+      else
+       archive_format = DEFAULT_ARCHIVE_FORMAT;
     }
 
-  if (archive_format == GNU_FORMAT && getenv ("POSIXLY_CORRECT"))
-    archive_format = POSIX_FORMAT;
+  if (volume_label_option && subcommand_option == CREATE_SUBCOMMAND)
+    assert_format (FORMAT_MASK (OLDGNU_FORMAT)
+                  | FORMAT_MASK (GNU_FORMAT));
 
-  if ((volume_label_option != NULL
-       || incremental_option || multi_volume_option || sparse_option)
-      && archive_format != OLDGNU_FORMAT && archive_format != GNU_FORMAT)
-    USAGE_ERROR ((0, 0,
-                 _("GNU features wanted on incompatible archive format")));
+
+  if (incremental_option || multi_volume_option)
+    assert_format (FORMAT_MASK (OLDGNU_FORMAT) | FORMAT_MASK (GNU_FORMAT));
+
+  if (sparse_option)
+    assert_format (FORMAT_MASK (OLDGNU_FORMAT)
+                  | FORMAT_MASK (GNU_FORMAT)
+                  | FORMAT_MASK (POSIX_FORMAT));
+
+  if (occurrence_option)
+    {
+      if (!input_files && !files_from_option)
+       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 (archive_names == 0)
     {
@@ -1037,7 +1346,7 @@ Written by John Gilmore and Jay Fenlason.\n"),
 
       archive_names = 1;
       archive_name_array[0] = getenv ("TAPE");
-      if (archive_name_array[0] == NULL)
+      if (! archive_name_array[0])
        archive_name_array[0] = DEFAULT_ARCHIVE;
     }
 
@@ -1045,11 +1354,63 @@ Written by John Gilmore and Jay Fenlason.\n"),
 
   if (archive_names > 1 && !multi_volume_option)
     USAGE_ERROR ((0, 0,
-                 _("Multiple archive files requires `-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")));
+
+  if (volume_label_option)
+    {
+      size_t volume_label_max_len =
+       (sizeof current_header->header.name
+        - 1 /* for trailing '\0' */
+        - (multi_volume_option
+           ? (sizeof " Volume "
+              - 1 /* for null at end of " Volume " */
+              + INT_STRLEN_BOUND (int) /* for volume number */
+              - 1 /* for sign, as 0 <= volno */)
+           : 0));
+      if (volume_label_max_len < strlen (volume_label_option))
+       USAGE_ERROR ((0, 0,
+                     ngettext ("%s: Volume label is too long (limit is %lu byte)",
+                               "%s: Volume label is too long (limit is %lu bytes)",
+                               volume_label_max_len),
+                     quotearg_colon (volume_label_option),
+                     (unsigned long) volume_label_max_len));
+    }
+
+  if (verify_option)
+    {
+      if (multi_volume_option)
+       USAGE_ERROR ((0, 0, _("Cannot verify multi-volume archives")));
+      if (use_compress_program_option)
+       USAGE_ERROR ((0, 0, _("Cannot verify compressed archives")));
+    }
+
+  if (use_compress_program_option)
+    {
+      if (multi_volume_option)
+       USAGE_ERROR ((0, 0, _("Cannot use multi-volume compressed archives")));
+      if (subcommand_option == UPDATE_SUBCOMMAND)
+       USAGE_ERROR ((0, 0, _("Cannot update compressed archives")));
+    }
+
+  /* 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 (pax_option
+      && archive_format != POSIX_FORMAT
+      && (subcommand_option != EXTRACT_SUBCOMMAND
+         || subcommand_option != DIFF_SUBCOMMAND
+         || subcommand_option != LIST_SUBCOMMAND))
+    USAGE_ERROR ((0, 0, _("--pax-option can be used only on POSIX archives")));
 
   /* If ready to unlink hierarchies, so we are for simpler files.  */
   if (recursive_unlink_option)
-    unlink_first_option = 1;
+    old_files_option = UNLINK_FIRST_OLD_FILES;
 
   /* Forbid using -c with no input files whatsoever.  Check that `-f -',
      explicit or implied, is used correctly.  */
@@ -1095,17 +1456,29 @@ Written by John Gilmore and Jay Fenlason.\n"),
 
   if (backup_option)
     backup_type = xget_version ("--backup", version_control_string);
+
+  if (verbose_option && textual_date_option)
+    {
+      /* FIXME: tartime should support nanoseconds, too, so that this
+        comparison doesn't complain about lost nanoseconds.  */
+      char const *treated_as = tartime (newer_mtime_option.tv_sec);
+      if (strcmp (textual_date_option, treated_as) != 0)
+       WARN ((0, 0, _("Treating date `%s' as %s + %ld nanoseconds"),
+              textual_date_option, treated_as, newer_mtime_option.tv_nsec));
+    }
 }
+
 \f
 /* Tar proper.  */
 
-/*-----------------------.
-| Main routine for tar.         |
-`-----------------------*/
-
+/* Main routine for tar.  */
 int
-main (int argc, char *const *argv)
+main (int argc, char **argv)
 {
+#if HAVE_CLOCK_GETTIME
+  if (clock_gettime (CLOCK_REALTIME, &start_timespec) != 0)
+#endif
+    start_time = time (0);
   program_name = argv[0];
   setlocale (LC_ALL, "");
   bindtextdomain (PACKAGE, LOCALEDIR);
@@ -1113,20 +1486,26 @@ main (int argc, char *const *argv)
 
   exit_status = TAREXIT_SUCCESS;
   filename_terminator = '\n';
+  set_quoting_style (0, escape_quoting_style);
 
   /* Pre-allocate a few structures.  */
 
   allocated_archive_names = 10;
-  archive_name_array = (const char **)
+  archive_name_array =
     xmalloc (sizeof (const char *) * allocated_archive_names);
   archive_names = 0;
 
+#ifdef SIGCHLD
+  /* System V fork+wait does not work if SIGCHLD is ignored.  */
+  signal (SIGCHLD, SIG_DFL);
+#endif
+
   init_names ();
 
   /* Decode options.  */
 
   decode_options (argc, argv);
-  name_init (argc, argv);
+  name_init ();
 
   /* Main command execution.  */
 
@@ -1150,9 +1529,6 @@ main (int argc, char *const *argv)
       break;
 
     case CREATE_SUBCOMMAND:
-      if (totals_option)
-       init_total_written ();
-
       create_archive ();
       name_close ();
 
@@ -1163,6 +1539,11 @@ main (int argc, char *const *argv)
     case EXTRACT_SUBCOMMAND:
       extr_init ();
       read_and (extract_archive);
+
+      /* FIXME: should extract_finish () even if an ordinary signal is
+        received.  */
+      extract_finish ();
+
       break;
 
     case LIST_SUBCOMMAND:
@@ -1175,6 +1556,9 @@ main (int argc, char *const *argv)
       break;
     }
 
+  if (check_links_option)
+      check_links ();
+
   if (volno_file_option)
     closeout_volume_number ();
 
@@ -1183,7 +1567,29 @@ main (int argc, char *const *argv)
   free (archive_name_array);
   name_term ();
 
+  if (stdlis != stderr && (ferror (stdlis) || fclose (stdlis) != 0))
+    FATAL_ERROR ((0, 0, _("Error in writing to standard output")));
   if (exit_status == TAREXIT_FAILURE)
     error (0, 0, _("Error exit delayed from previous errors"));
-  exit (exit_status);
+  if (ferror (stderr) || fclose (stderr) != 0)
+    exit_status = TAREXIT_FAILURE;
+  return exit_status;
+}
+
+void
+tar_stat_init (struct tar_stat_info *st)
+{
+  memset (st, 0, sizeof (*st));
+}
+
+void
+tar_stat_destroy (struct tar_stat_info *st)
+{
+  free (st->orig_file_name);
+  free (st->file_name);
+  free (st->link_name);
+  free (st->uname);
+  free (st->gname);
+  free (st->sparse_map);
+  memset (st, 0, sizeof (*st));
 }
This page took 0.065461 seconds and 4 git commands to generate.