]> Dogcows Code - chaz/tar/blobdiff - src/tar.c
Update copyright year.
[chaz/tar] / src / tar.c
index 24b0f90bf72097c17f9dd5da1ca734397968d49c..5415bbee8e950e30655a2e1b2d2afb1053cbf6c6 100644 (file)
--- a/src/tar.c
+++ b/src/tar.c
@@ -1,5 +1,5 @@
 /* A tar (tape archiver) program.
-   Copyright (C) 1988, 92, 93, 94, 95, 96, 97 Free Software Foundation, Inc.
+   Copyright 1988, 92,93,94,95,96,97,99, 2000 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 <getopt.h>
 
+#include <signal.h>
+#if ! defined SIGCHLD && defined SIGCLD
+# define SIGCHLD SIGCLD
+#endif
+
+#include <time.h>
+#ifndef time
+time_t time ();
+#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 "backupfile.h"
-enum backup_type get_version ();
-
-/* FIXME: We should use a conversion routine that does reasonable error
-   checking -- atoi doesn't.  For now, punt.  */
-#define intconv        atoi
+#include "quotearg.h"
+#include "xstrtol.h"
 
 time_t get_date ();
 
@@ -46,49 +52,16 @@ time_t get_date ();
 # define DEFAULT_BLOCKING 20
 #endif
 
-static void usage PARAMS ((int));
+static void usage PARAMS ((int)) __attribute__ ((noreturn));
 \f
 /* Miscellaneous.  */
 
-/*------------------------------------------------------------------------.
-| Check if STRING is the decimal representation of number, and return its |
-| value.  If not a decimal number, return -1.                            |
-`------------------------------------------------------------------------*/
-
-static int
-check_decimal (const char *string)
-{
-  int value = -1;
-
-  while (*string)
-    switch (*string)
-      {
-      case '0':
-      case '1':
-      case '2':
-      case '3':
-      case '4':
-      case '5':
-      case '6':
-      case '7':
-      case '8':
-      case '9':
-       value = value < 0 ? *string - '0' : 10 * value + *string - '0';
-       string++;
-       break;
-
-      default:
-       return -1;
-      }
-    return value;
-}
-
 /*----------------------------------------------.
 | 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;
 
 void
 request_stdin (const char *option)
@@ -107,33 +80,41 @@ request_stdin (const char *option)
 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';
   }
 }
@@ -144,137 +125,143 @@ 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
+{
+  BACKUP_OPTION = CHAR_MAX + 1,
+  DELETE_OPTION,
+  EXCLUDE_OPTION,
+  GROUP_OPTION,
+  MODE_OPTION,
+  NEWER_MTIME_OPTION,
+  NO_RECURSE_OPTION,
+  NULL_OPTION,
+  OVERWRITE_OPTION,
+  OWNER_OPTION,
+  POSIX_OPTION,
+  PRESERVE_OPTION,
+  RECORD_SIZE_OPTION,
+  RSH_COMMAND_OPTION,
+  SUFFIX_OPTION,
+  USE_COMPRESS_PROGRAM_OPTION,
+  VOLNO_FILE_OPTION,
+
+  /* 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.  */
+
+  OBSOLETE_ABSOLUTE_NAMES,
+  OBSOLETE_BLOCK_COMPRESS,
+  OBSOLETE_BLOCKING_FACTOR,
+  OBSOLETE_BLOCK_NUMBER,
+  OBSOLETE_READ_FULL_RECORDS,
+  OBSOLETE_TOUCH,
+  OBSOLETE_VERSION_CONTROL
+};
 
 /* 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[] =
 {
-  {"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'},
+  {"absolute-names", no_argument, 0, 'P'},
+  {"absolute-paths", no_argument, 0, OBSOLETE_ABSOLUTE_NAMES},
+  {"after-date", required_argument, 0, 'N'},
+  {"append", no_argument, 0, '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'},
+  {"backup", optional_argument, 0, BACKUP_OPTION},
+  {"block-compress", no_argument, 0, OBSOLETE_BLOCK_COMPRESS},
+  {"block-number", no_argument, 0, 'R'},
+  {"block-size", required_argument, 0, OBSOLETE_BLOCKING_FACTOR},
+  {"blocking-factor", required_argument, 0, 'b'},
+  {"bzip2", no_argument, 0, 'I'},
+  {"catenate", no_argument, 0, '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'},
+  {"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'},
-  {"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'},
+  {"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, &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'},
+  {"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-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},
+  {"incremental", no_argument, 0, 'G'},
+  {"info-script", required_argument, 0, 'F'},
+  {"interactive", no_argument, 0, 'w'},
+  {"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},
+  {"modification-time", no_argument, 0, OBSOLETE_TOUCH},
+  {"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-recursion", no_argument, 0, NO_RECURSE_OPTION},
+  {"no-same-owner", no_argument, &same_owner_option, -1},
+  {"no-same-permissions", no_argument, &same_permissions_option, -1},
   {"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'},
+  {"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},
+  {"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'},
   {"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'},
+  {"read-full-blocks", no_argument, 0, OBSOLETE_READ_FULL_RECORDS},
+  {"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},
+  {"record-number", no_argument, 0, OBSOLETE_BLOCK_NUMBER},
+  {"record-size", required_argument, 0, 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'},
+  {"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'},
+  {"same-permissions", no_argument, 0, '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'},
+  {"sparse", no_argument, 0, 'S'},
+  {"starting-file", required_argument, 0, 'K'},
+  {"suffix", required_argument, 0, SUFFIX_OPTION},
+  {"tape-length", required_argument, 0, 'L'},
+  {"to-stdout", no_argument, 0, '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'},
+  {"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'},
+  {"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},
+  {"version-control", required_argument, 0, OBSOLETE_VERSION_CONTROL},
+  {"volno-file", required_argument, 0, VOLNO_FILE_OPTION},
 
   {0, 0, 0, 0}
 };
@@ -295,7 +282,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\
@@ -319,13 +312,15 @@ 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\
+      --overwrite            overwrite existing files when extracting\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\
+  -g, --listed-incremental=FILE\n\
+                             handle new GNU-format incremental backup\n\
       --ignore-failed-read   do not exit with nonzero on unreadable files\n"),
             stdout);
       fputs (_("\
@@ -337,8 +332,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\
@@ -371,7 +368,8 @@ Archive format selection:\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\
+      --posix                        write a POSIX format archive\n\
+  -I, --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"),
@@ -399,7 +397,7 @@ Local file selection:\n\
 #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\
@@ -430,10 +428,7 @@ 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"),
              DEFAULT_ARCHIVE, DEFAULT_BLOCKING);
-      fputs (_("\
-\n\
-Report bugs to <tar-bugs@gnu.ai.mit.edu>.\n"),
-              stdout);
+      fputs (_("\nReport bugs to <bug-tar@gnu.org>.\n"), stdout);
     }
   exit (status);
 }
@@ -442,13 +437,13 @@ Report bugs to <tar-bugs@gnu.ai.mit.edu>.\n"),
 | Parse the options for tar.  |
 `----------------------------*/
 
-/* Available option letters are DEHIJQY and aejnqy.  Some are reserved:
+/* Available option letters are DEHJQY and aejnqy.  Some are reserved:
 
    y  per-file gzip compression
    Y  per-block gzip compression */
 
 #define OPTION_STRING \
-  "-01234567ABC:F:GK:L:MN:OPRST:UV:WX:Zb:cdf:g:hiklmoprstuvwxz"
+  "-01234567ABC:F:GIK:L:MN:OPRST:UV:WX:Zb:cdf:g:hiklmoprstuvwxz"
 
 static void
 set_subcommand_option (enum subcommand subcommand)
@@ -470,13 +465,25 @@ set_use_compress_program_option (const char *string)
   use_compress_program_option = string;
 }
 
+/* Ignore DUMMY (which will always be null in practice), and add
+   PATTERN to the proper set of patterns to be excluded -- either
+   patterns with slashes, or patterns without.  */
+static void
+add_filtered_exclude (struct exclude *dummy, char const *pattern)
+{
+  add_exclude ((strchr (pattern, '/')
+               ? excluded_with_slash
+               : excluded_without_slash),
+              pattern);
+}
+
 static void
 decode_options (int argc, char *const *argv)
 {
   int optchar;                 /* option letter */
   int input_files;             /* number of input files */
   const char *backup_suffix_string;
-  const char *version_control_string;
+  const char *version_control_string = 0;
 
   /* Set some default option values.  */
 
@@ -484,12 +491,14 @@ decode_options (int argc, char *const *argv)
   archive_format = DEFAULT_FORMAT;
   blocking_factor = DEFAULT_BLOCKING;
   record_size = DEFAULT_BLOCKING * BLOCKSIZE;
+  excluded_with_slash = new_exclude ();
+  excluded_without_slash = new_exclude ();
+  newer_mtime_option = TYPE_MINIMUM (time_t);
 
   owner_option = -1;
   group_option = -1;
 
   backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
-  version_control_string = getenv ("VERSION_CONTROL");
 
   /* Convert old-style tar call by exploding option element and rearranging
      options accordingly.  */
@@ -512,7 +521,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 * sizeof (char *));
       in = argv;
       out = new_argv;
       *out++ = *in++;
@@ -526,11 +535,13 @@ decode_options (int argc, char *const *argv)
          *out++ = xstrdup (buffer);
          cursor = strchr (OPTION_STRING, *letter);
          if (cursor && cursor[1] == ':')
-           if (in < argv + argc)
-             *out++ = *in++;
-           else
-             USAGE_ERROR ((0, 0, _("Old option `%c' requires an argument."),
-                           *letter));
+           {
+             if (in < argv + argc)
+               *out++ = *in++;
+             else
+               USAGE_ERROR ((0, 0, _("Old option `%c' requires an argument."),
+                             *letter));
+           }
        }
 
       /* Copy all remaining options.  */
@@ -548,8 +559,8 @@ decode_options (int argc, char *const *argv)
 
   input_files = 0;
 
-  while (optchar = getopt_long (argc, argv, OPTION_STRING, long_options, NULL),
-        optchar != EOF)
+  while (optchar = getopt_long (argc, argv, OPTION_STRING, long_options, 0),
+        optchar != -1)
     switch (optchar)
       {
       case '?':
@@ -560,7 +571,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++;
@@ -579,8 +590,14 @@ decode_options (int argc, char *const *argv)
        /* Fall through.  */
 
       case 'b':
-       blocking_factor = intconv (optarg);
-       record_size = blocking_factor * BLOCKSIZE;
+       {
+         uintmax_t u;
+         if (! (xstrtoumax (optarg, 0, 10, &u, "") == LONGINT_OK
+                && u == (blocking_factor = u)
+                && 0 < blocking_factor
+                && u == (record_size = u * BLOCKSIZE) / BLOCKSIZE))
+           USAGE_ERROR ((0, 0, _("Invalid blocking factor")));
+       }
        break;
 
       case OBSOLETE_READ_FULL_RECORDS:
@@ -616,7 +633,7 @@ 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);
          }
@@ -633,6 +650,7 @@ decode_options (int argc, char *const *argv)
 
       case 'g':
        listed_incremental_option = optarg;
+       after_date_option = 1;
        /* Fall through.  */
 
       case 'G':
@@ -657,15 +675,18 @@ decode_options (int argc, char *const *argv)
        ignore_zeros_option = 1;
        break;
 
-      case 'k':
-       /* Don't overwrite existing files.  */
+      case 'I':
+       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);
+       addname (optarg, 0);
        break;
 
       case 'l':
@@ -676,10 +697,13 @@ decode_options (int argc, char *const *argv)
        break;
 
       case 'L':
-       clear_tarlong (tape_length_option);
-       add_to_tarlong (tape_length_option, intconv (optarg));
-       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, _("Invalid tape length")));
+         tape_length_option = 1024 * (tarlong) u;
+         multi_volume_option = 1;
+       }
        break;
 
       case OBSOLETE_TOUCH:
@@ -703,12 +727,13 @@ decode_options (int argc, char *const *argv)
        /* Fall through.  */
 
       case NEWER_MTIME_OPTION:
-       if (newer_mtime_option)
+       if (newer_mtime_option != TYPE_MINIMUM (time_t))
          USAGE_ERROR ((0, 0, _("More than one threshold date")));
 
-       newer_mtime_option = get_date (optarg, (voidstar) 0);
+       newer_mtime_option = get_date (optarg, 0);
        if (newer_mtime_option == (time_t) -1)
-         USAGE_ERROR ((0, 0, _("Invalid date format `%s'"), optarg));
+         WARN ((0, 0, _("Substituting %s for unknown date format %s"),
+                tartime (newer_mtime_option), quote (optarg)));
 
        break;
 #endif /* not MSDOS */
@@ -779,7 +804,7 @@ decode_options (int argc, char *const *argv)
        break;
 
       case 'U':
-       unlink_first_option = 1;
+       old_files_option = UNLINK_FIRST_OLD_FILES;
        break;
 
       case 'v':
@@ -803,8 +828,11 @@ decode_options (int argc, char *const *argv)
        break;
 
       case 'X':
-       exclude_option = 1;
-       add_exclude_file (optarg);
+       if (add_exclude_file (add_filtered_exclude, 0, optarg, '\n') != 0)
+         {
+           int e = errno;
+           FATAL_ERROR ((0, e, "%s", quotearg_colon (optarg)));
+         }
        break;
 
       case 'z':
@@ -830,16 +858,20 @@ decode_options (int argc, char *const *argv)
        break;
 
       case EXCLUDE_OPTION:
-       exclude_option = 1;
-       add_exclude (optarg);
+       add_filtered_exclude (0, optarg);
        break;
 
       case GROUP_OPTION:
-       if (!gname_to_gid (optarg, &group_option))
-         if (!check_decimal (optarg) >= 0)
-           ERROR ((TAREXIT_FAILURE, 0, _("Invalid group given on option")));
-         else
-           group_option = check_decimal (optarg);
+       if (! (strlen (optarg) < GNAME_FIELD_SIZE
+              && gname_to_gid (optarg, &group_option)))
+         {
+           uintmax_t g;
+           if (xstrtoumax (optarg, 0, 10, &g, "") == LONGINT_OK
+               && g == (gid_t) g)
+             group_option = g;
+           else
+             FATAL_ERROR ((0, 0, _("Invalid group given on option")));
+         }
        break;
 
       case MODE_OPTION:
@@ -847,9 +879,9 @@ 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_RECURSE_OPTION:
@@ -860,12 +892,21 @@ decode_options (int argc, char *const *argv)
        filename_terminator = '\0';
        break;
 
+      case OVERWRITE_OPTION:
+       old_files_option = OVERWRITE_OLD_FILES;
+       break;
+
       case OWNER_OPTION:
-       if (!uname_to_uid (optarg, &owner_option))
-         if (!check_decimal (optarg) >= 0)
-           ERROR ((TAREXIT_FAILURE, 0, _("Invalid owner given on option")));
-         else
-           owner_option = check_decimal (optarg);
+       if (! (strlen (optarg) < UNAME_FIELD_SIZE
+              && uname_to_uid (optarg, &owner_option)))
+         {
+           uintmax_t u;
+           if (xstrtoumax (optarg, 0, 10, &u, "") == LONGINT_OK
+               && u == (uid_t) u)
+             owner_option = u;
+           else
+             FATAL_ERROR ((0, 0, _("Invalid owner given on option")));
+         }
        break;
 
       case POSIX_OPTION:
@@ -888,11 +929,17 @@ decode_options (int argc, char *const *argv)
        break;
 
       case RECORD_SIZE_OPTION:
-       record_size = intconv (optarg);
-       if (record_size % BLOCKSIZE != 0)
-         USAGE_ERROR ((0, 0, _("Record size must be a multiple of %d."),
-                       BLOCKSIZE));
-       blocking_factor = record_size / BLOCKSIZE;
+       {
+         uintmax_t u;
+         if (! (xstrtoumax (optarg, 0, 10, &u, "") == LONGINT_OK
+                && u == (size_t) u))
+           USAGE_ERROR ((0, 0, _("Invalid record size")));
+         record_size = u;
+         if (record_size % BLOCKSIZE != 0)
+           USAGE_ERROR ((0, 0, _("Record size must be a multiple of %d."),
+                         BLOCKSIZE));
+         blocking_factor = record_size / BLOCKSIZE;
+       }
        break;
 
       case RSH_COMMAND_OPTION:
@@ -928,7 +975,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);
 
@@ -972,7 +1019,7 @@ 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);
            }
@@ -991,23 +1038,24 @@ decode_options (int argc, char *const *argv)
 #endif /* not DEVICE_PREFIX */
       }
 
+  /* 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 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 (GNU %s) %s\n%s\n%s\n%s\n", PACKAGE, VERSION,
+             "Copyright 2000 Free Software Foundation, Inc.",
+             _("\
+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."),
+             _("Written by John Gilmore and Jay Fenlason."));
       exit (TAREXIT_SUCCESS);
     }
 
@@ -1028,7 +1076,7 @@ Written by John Gilmore and Jay Fenlason.\n"),
   if (archive_format == GNU_FORMAT && getenv ("POSIXLY_CORRECT"))
     archive_format = POSIX_FORMAT;
 
-  if ((volume_label_option != NULL
+  if ((volume_label_option
        || incremental_option || multi_volume_option || sparse_option)
       && archive_format != OLDGNU_FORMAT && archive_format != GNU_FORMAT)
     USAGE_ERROR ((0, 0,
@@ -1041,7 +1089,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;
     }
 
@@ -1051,9 +1099,14 @@ Written by John Gilmore and Jay Fenlason.\n"),
     USAGE_ERROR ((0, 0,
                  _("Multiple archive files requires `-M' option")));
 
+  if (listed_incremental_option
+      && newer_mtime_option != TYPE_MINIMUM (time_t))
+    USAGE_ERROR ((0, 0,
+                 _("Cannot combine --listed-incremental with --newer")));
+
   /* 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.  */
@@ -1079,12 +1132,13 @@ Written by John Gilmore and Jay Fenlason.\n"),
     case CAT_SUBCOMMAND:
     case UPDATE_SUBCOMMAND:
     case APPEND_SUBCOMMAND:
+    case DELETE_SUBCOMMAND:
       for (archive_name_cursor = archive_name_array;
           archive_name_cursor < archive_name_array + archive_names;
           archive_name_cursor++)
        if (!strcmp (*archive_name_cursor, "-"))
          USAGE_ERROR ((0, 0,
-                       _("Options `-Aru' are incompatible with `-f -'")));
+                       _("Options `-Aru' and `--delete' are incompatible with `-f -'")));
 
     default:
       break;
@@ -1098,7 +1152,7 @@ Written by John Gilmore and Jay Fenlason.\n"),
     simple_backup_suffix = xstrdup (backup_suffix_string);
 
   if (backup_option)
-    backup_type = get_version (version_control_string);
+    backup_type = xget_version ("--backup", version_control_string);
 }
 \f
 /* Tar proper.  */
@@ -1110,6 +1164,10 @@ Written by John Gilmore and Jay Fenlason.\n"),
 int
 main (int argc, char *const *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);
@@ -1117,14 +1175,20 @@ 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.  */
@@ -1154,9 +1218,6 @@ main (int argc, char *const *argv)
       break;
 
     case CREATE_SUBCOMMAND:
-      if (totals_option)
-       init_total_written ();
-
       create_archive ();
       name_close ();
 
@@ -1187,6 +1248,8 @@ main (int argc, char *const *argv)
   free (archive_name_array);
   name_term ();
 
+  if (stdlis == stdout && (ferror (stdout) || fclose (stdout) != 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);
This page took 0.048063 seconds and 4 git commands to generate.