]> Dogcows Code - chaz/tar/blobdiff - src/tar.c
Include full-write.h.
[chaz/tar] / src / tar.c
index 5ffb7601c3e132f53637c53aa56c3913ad08f1f5..0095701d68ca9ec836590b0c9369d37b722ec7a9 100644 (file)
--- a/src/tar.c
+++ b/src/tar.c
@@ -1,5 +1,5 @@
 /* A tar (tape archiver) program.
-   Copyright 1988, 92,93,94,95,96,97, 1999 Free Software Foundation, Inc.
+   Copyright 1988,92,93,94,95,96,97,99,2000,2001 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
@@ -18,6 +18,7 @@
 
 #include "system.h"
 
+#include <fnmatch.h>
 #include <getopt.h>
 
 #include <signal.h>
 #define GLOBAL
 #include "common.h"
 
-#include "xstrtol.h"
+#include <localedir.h>
+#include <prepargs.h>
+#include <quotearg.h>
+#include <xstrtol.h>
 
 time_t get_date ();
 
@@ -46,17 +50,14 @@ time_t get_date ();
 # define DEFAULT_BLOCKING 20
 #endif
 
-static void usage PARAMS ((int));
+static void usage PARAMS ((int)) __attribute__ ((noreturn));
 \f
 /* Miscellaneous.  */
 
-/*----------------------------------------------.
-| Doesn't return if stdin already requested.    |
-`----------------------------------------------*/
-
 /* Name of option using stdin.  */
 static const char *stdin_used_by;
 
+/* Doesn't return if stdin already requested.  */
 void
 request_stdin (const char *option)
 {
@@ -67,40 +68,45 @@ 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;
+  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';
   }
 }
@@ -122,8 +128,8 @@ enum
   GROUP_OPTION,
   MODE_OPTION,
   NEWER_MTIME_OPTION,
-  NO_RECURSE_OPTION,
   NULL_OPTION,
+  OVERWRITE_OPTION,
   OWNER_OPTION,
   POSIX_OPTION,
   PRESERVE_OPTION,
@@ -152,7 +158,7 @@ static int show_help;
 /* If nonzero, print the version on standard output and exit.  */
 static int show_version;
 
-struct option long_options[] =
+static struct option long_options[] =
 {
   {"absolute-names", no_argument, 0, 'P'},
   {"absolute-paths", no_argument, 0, OBSOLETE_ABSOLUTE_NAMES},
@@ -164,7 +170,7 @@ struct option long_options[] =
   {"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'},
+  {"bzip2", no_argument, 0, 'j'},
   {"catenate", no_argument, 0, 'A'},
   {"checkpoint", no_argument, &checkpoint_option, 1},
   {"compare", no_argument, 0, 'd'},
@@ -205,10 +211,13 @@ struct option long_options[] =
   {"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-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, &numeric_owner_option, 1},
   {"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},
@@ -248,10 +257,7 @@ struct option long_options[] =
   {0, 0, 0, 0}
 };
 
-/*---------------------------------------------.
-| Print a usage message and exit with STATUS.  |
-`---------------------------------------------*/
-
+/* Print a usage message and exit with STATUS.  */
 static void
 usage (int status)
 {
@@ -264,7 +270,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\
@@ -288,13 +300,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 (_("\
@@ -306,8 +320,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\
@@ -341,7 +357,7 @@ Archive format selection:\n\
               PATTERN                at list/extract time, a globbing PATTERN\n\
   -o, --old-archive, --portability   write a V7 format archive\n\
       --posix                        write a POSIX format archive\n\
-  -I, --bzip2                        filter the archive through bzip2\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"),
@@ -400,25 +416,25 @@ 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 <bug-tar@gnu.org>.\n"),
-              stdout);
+      fputs (_("\nReport bugs to <bug-tar@gnu.org>.\n"), stdout);
     }
   exit (status);
 }
 
-/*----------------------------.
-| Parse the options for tar.  |
-`----------------------------*/
+/* Parse the options for tar.  */
 
-/* Available option letters are DEHJQY 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:F:GIK:L:MN:OPRST:UV:WX:Zb:cdf:g:hiklmoprstuvwxz"
+  "-01234567ABC:F:GK:L:MN:OPRST:UV:WX:Zb:cdf:g:hijklmoprstuvwxz"
 
 static void
 set_subcommand_option (enum subcommand subcommand)
@@ -453,7 +469,7 @@ add_filtered_exclude (struct exclude *dummy, char const *pattern)
 }
 
 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 */
@@ -469,6 +485,7 @@ decode_options (int argc, char *const *argv)
   excluded_with_slash = new_exclude ();
   excluded_without_slash = new_exclude ();
   newer_mtime_option = TYPE_MINIMUM (time_t);
+  recursion_option = FNM_LEADING_DIR;
 
   owner_option = -1;
   group_option = -1;
@@ -534,6 +551,8 @@ decode_options (int argc, char *const *argv)
 
   input_files = 0;
 
+  prepend_default_options (getenv ("TAR_OPTIONS"), &argc, &argv);
+
   while (optchar = getopt_long (argc, argv, OPTION_STRING, long_options, 0),
         optchar != -1)
     switch (optchar)
@@ -571,7 +590,8 @@ decode_options (int argc, char *const *argv)
                 && u == (blocking_factor = u)
                 && 0 < blocking_factor
                 && u == (record_size = u * BLOCKSIZE) / BLOCKSIZE))
-           USAGE_ERROR ((0, 0, _("Invalid blocking factor")));
+           USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (optarg),
+                         _("Invalid blocking factor")));
        }
        break;
 
@@ -650,14 +670,13 @@ decode_options (int argc, char *const *argv)
        ignore_zeros_option = 1;
        break;
 
-      case 'I':
+      case 'j':
        set_use_compress_program_option ("bzip2");
        break;
 
       case 'k':
-       /* Don't overwrite existing files.  */
-
-       keep_old_files_option = 1;
+       /* Don't replace existing files.  */
+       old_files_option = KEEP_OLD_FILES;
        break;
 
       case 'K':
@@ -676,7 +695,8 @@ decode_options (int argc, char *const *argv)
        {
          uintmax_t u;
          if (xstrtoumax (optarg, 0, 10, &u, "") != LONGINT_OK)
-           USAGE_ERROR ((0, 0, _("Invalid tape length")));
+           USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (optarg),
+                         _("Invalid tape length")));
          tape_length_option = 1024 * (tarlong) u;
          multi_volume_option = 1;
        }
@@ -703,12 +723,28 @@ decode_options (int argc, char *const *argv)
        /* Fall through.  */
 
       case NEWER_MTIME_OPTION:
-       if (newer_mtime_option != TYPE_MINIMUM (time_t))
+       if (newer_mtime_option != TYPE_MINIMUM (time_t))
          USAGE_ERROR ((0, 0, _("More than one threshold date")));
 
-       newer_mtime_option = get_date (optarg, 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 = st.st_mtime;
+         }
+       else
+         {
+           newer_mtime_option = get_date (optarg, 0);
+           if (newer_mtime_option == (time_t) -1)
+             WARN ((0, 0, _("Substituting %s for unknown date format %s"),
+                    tartime (newer_mtime_option), quote (optarg)));
+         }
 
        break;
 #endif /* not MSDOS */
@@ -779,7 +815,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':
@@ -804,7 +840,10 @@ decode_options (int argc, char *const *argv)
 
       case 'X':
        if (add_exclude_file (add_filtered_exclude, 0, optarg, '\n') != 0)
-         FATAL_ERROR ((0, errno, "%s", optarg));
+         {
+           int e = errno;
+           FATAL_ERROR ((0, e, "%s", quotearg_colon (optarg)));
+         }
        break;
 
       case 'z':
@@ -842,7 +881,8 @@ decode_options (int argc, char *const *argv)
                && g == (gid_t) g)
              group_option = g;
            else
-             FATAL_ERROR ((0, 0, _("Invalid group given on option")));
+             FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (optarg),
+                           _("%s: Invalid group")));
          }
        break;
 
@@ -853,17 +893,17 @@ decode_options (int argc, char *const *argv)
        if (mode_option == MODE_INVALID)
          FATAL_ERROR ((0, 0, _("Invalid mode given on option")));
        if (mode_option == MODE_MEMORY_EXHAUSTED)
-         FATAL_ERROR ((0, 0, _("Memory exhausted")));
-       break;
-
-      case NO_RECURSE_OPTION:
-       no_recurse_option = 1;
+         xalloc_die ();
        break;
 
       case NULL_OPTION:
        filename_terminator = '\0';
        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)))
@@ -873,7 +913,8 @@ decode_options (int argc, char *const *argv)
                && u == (uid_t) u)
              owner_option = u;
            else
-             FATAL_ERROR ((0, 0, _("Invalid owner given on option")));
+             FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (optarg),
+                           _("Invalid owner")));
          }
        break;
 
@@ -901,7 +942,8 @@ decode_options (int argc, char *const *argv)
          uintmax_t u;
          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."),
@@ -1017,19 +1059,13 @@ decode_options (int argc, char *const *argv)
 
   if (show_version)
     {
-      printf ("tar (GNU %s) %s\n", PACKAGE, VERSION);
-      fputs (_("\
-\n\
-Copyright 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 (GNU %s) %s\n%s\n%s\n%s\n", PACKAGE, VERSION,
+             "Copyright 2001 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);
     }
 
@@ -1078,9 +1114,27 @@ Written by John Gilmore and Jay Fenlason.\n"),
     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,
+                     _("%s: Volume label is too long (limit is %lu bytes)"),
+                     quotearg_colon (volume_label_option),
+                     (unsigned long) volume_label_max_len));
+    }
+
   /* 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.  */
@@ -1130,13 +1184,14 @@ Written by John Gilmore and Jay Fenlason.\n"),
 \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);
@@ -1144,6 +1199,7 @@ 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.  */
 
@@ -1196,6 +1252,7 @@ main (int argc, char *const *argv)
     case EXTRACT_SUBCOMMAND:
       extr_init ();
       read_and (extract_archive);
+      extract_finish ();
       break;
 
     case LIST_SUBCOMMAND:
This page took 0.032624 seconds and 4 git commands to generate.