]> Dogcows Code - chaz/tar/blobdiff - src/list.c
(tar_checksum): New function
[chaz/tar] / src / list.c
index 7c7061a899722b88b5e3492f70b424e510a88850..0d96e8bb67eaef9d9e4ab05552e57c771838b1da 100644 (file)
@@ -1,5 +1,8 @@
 /* List a tar archive, with support routines for reading a tar archive.
-   Copyright 1988,92,93,94,96,97,98,99,2000,2001 Free Software Foundation, Inc.
+
+   Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2000,
+   2001, 2003, 2004 Free Software Foundation, Inc.
+
    Written by John Gilmore, on 1985-08-26.
 
    This program is free software; you can redistribute it and/or modify it
@@ -19,7 +22,7 @@
 /* Define to non-zero for forcing old ctime format instead of ISO format.  */
 #undef USE_OLD_CTIME
 
-#include "system.h"
+#include <system.h>
 #include <quotearg.h>
 
 #include "common.h"
 #define max(a, b) ((a) < (b) ? (b) : (a))
 
 union block *current_header;   /* points to current archive header */
-struct stat current_stat;      /* stat struct corresponding */
 enum archive_format current_format; /* recognized format */
+union block *recent_long_name; /* recent long name header and contents */
+union block *recent_long_link; /* likewise, for long link */
+size_t recent_long_name_blocks;        /* number of blocks in recent_long_name */
+size_t recent_long_link_blocks;        /* likewise, for long link */
 
-static uintmax_t from_header PARAMS ((const char *, size_t, const char *,
-                                     uintmax_t, uintmax_t));
+static uintmax_t from_header (const char *, size_t, const char *,
+                             uintmax_t, uintmax_t);
 
 /* Base 64 digits; see Internet RFC 2045 Table 1.  */
 static char const base_64_digits[64] =
@@ -58,7 +64,7 @@ base64_init (void)
 
 /* Main loop for reading an archive.  */
 void
-read_and (void (*do_something) ())
+read_and (void (*do_something) (void))
 {
   enum read_header status = HEADER_STILL_UNREAD;
   enum read_header prev_status;
@@ -67,13 +73,17 @@ read_and (void (*do_something) ())
   name_gather ();
   open_archive (ACCESS_READ);
 
-  while (1)
+  do
     {
       prev_status = status;
-      status = read_header ();
+      tar_stat_destroy (&current_stat_info);
+      xheader_destroy (&extended_header);
+
+      status = read_header (false);
       switch (status)
        {
        case HEADER_STILL_UNREAD:
+       case HEADER_SUCCESS_EXTENDED:
          abort ();
 
        case HEADER_SUCCESS:
@@ -81,14 +91,19 @@ read_and (void (*do_something) ())
          /* Valid header.  We should decode next field (mode) first.
             Ensure incoming names are null terminated.  */
 
-         if (! name_match (current_file_name)
-             || (newer_mtime_option != TYPE_MINIMUM (time_t)
+         if (! name_match (current_stat_info.file_name)
+             || (NEWER_OPTION_INITIALIZED (newer_mtime_option)
                  /* FIXME: We get mtime now, and again later; this causes
                     duplicate diagnostics if header.mtime is bogus.  */
-                 && ((current_stat.st_mtime
-                      = TIME_FROM_HEADER (current_header->header.mtime))
-                     < newer_mtime_option))
-             || excluded_name (current_file_name))
+                 && ((current_stat_info.stat.st_mtime
+                      = TIME_FROM_HEADER (current_header->header.mtime)),
+#ifdef ST_MTIM_NSEC
+                     /* FIXME: Grab fractional time stamps from
+                        extended header.  */
+                     current_stat_info.stat.st_mtim.ST_MTIM_NSEC = 0,
+#endif
+                     OLDER_STAT_TIME (current_stat_info.stat, m)))
+             || excluded_name (current_stat_info.file_name))
            {
              switch (current_header->header.typeflag)
                {
@@ -96,17 +111,19 @@ read_and (void (*do_something) ())
                case GNUTYPE_MULTIVOL:
                case GNUTYPE_NAMES:
                  break;
-               
+
                case DIRTYPE:
                  if (show_omitted_dirs_option)
                    WARN ((0, 0, _("%s: Omitting"),
-                          quotearg_colon (current_file_name)));
+                          quotearg_colon (current_stat_info.file_name)));
                  /* Fall through.  */
                default:
+                 decode_header (current_header,
+                                &current_stat_info, &current_format, 0);
                  skip_member ();
                  continue;
                }
-             }
+           }
 
          (*do_something) ();
          continue;
@@ -120,10 +137,20 @@ read_and (void (*do_something) ())
            }
 
          set_next_block_after (current_header);
+
+         if (!ignore_zeros_option)
+           {
+             char buf[UINTMAX_STRSIZE_BOUND];
+
+             status = read_header (false);
+             if (status == HEADER_ZERO_BLOCK)
+               break;
+             WARN ((0, 0, _("A lone zero block at %s"),
+                    STRINGIFY_BIGINT (current_block_ordinal (), buf)));
+             break;
+           }
          status = prev_status;
-         if (ignore_zeros_option)
-           continue;
-         break;
+         continue;
 
        case HEADER_END_OF_FILE:
          if (block_number_option)
@@ -146,6 +173,15 @@ read_and (void (*do_something) ())
 
            case HEADER_ZERO_BLOCK:
            case HEADER_SUCCESS:
+             if (block_number_option)
+               {
+                 char buf[UINTMAX_STRSIZE_BOUND];
+                 off_t block_ordinal = current_block_ordinal ();
+                 block_ordinal -= recent_long_name_blocks;
+                 block_ordinal -= recent_long_link_blocks;
+                 fprintf (stdlis, _("block %s: "),
+                          STRINGIFY_BIGINT (block_ordinal, buf));
+               }
              ERROR ((0, 0, _("Skipping to next header")));
              break;
 
@@ -153,11 +189,15 @@ read_and (void (*do_something) ())
            case HEADER_FAILURE:
              /* We are in the middle of a cascade of errors.  */
              break;
+
+           case HEADER_SUCCESS_EXTENDED:
+             abort ();
            }
          continue;
        }
       break;
     }
+  while (!all_names_found (&current_stat_info));
 
   close_archive ();
   names_notfound ();           /* print names not found */
@@ -169,12 +209,9 @@ list_archive (void)
 {
   /* Print the header block.  */
 
+  decode_header (current_header, &current_stat_info, &current_format, 0);
   if (verbose_option)
-    {
-      if (verbose_option > 1)
-       decode_header (current_header, &current_stat, &current_format, 0);
-      print_header ();
-    }
+    print_header (&current_stat_info, -1);
 
   if (incremental_option && current_header->header.typeflag == GNUTYPE_DUMPDIR)
     {
@@ -185,10 +222,10 @@ list_archive (void)
       set_next_block_after (current_header);
       if (multi_volume_option)
        {
-         assign_string (&save_name, current_file_name);
-         save_totsize = current_stat.st_size;
+         assign_string (&save_name, current_stat_info.file_name);
+         save_totsize = current_stat_info.stat.st_size;
        }
-      for (size = current_stat.st_size; size > 0; size -= written)
+      for (size = current_stat_info.stat.st_size; size > 0; size -= written)
        {
          if (multi_volume_option)
            save_sizeleft = size;
@@ -207,7 +244,7 @@ list_archive (void)
                                (data_block->buffer + written - 1));
          if (check != written)
            {
-             write_error_details (current_file_name, check, written);
+             write_error_details (current_stat_info.file_name, check, written);
              skip_file (size - written);
              break;
            }
@@ -221,7 +258,7 @@ list_archive (void)
     }
 
   if (multi_volume_option)
-    assign_string (&save_name, current_file_name);
+    assign_string (&save_name, current_stat_info.file_name);
 
   skip_member ();
 
@@ -229,120 +266,170 @@ list_archive (void)
     assign_string (&save_name, 0);
 }
 
-/* Read a block that's supposed to be a header block.  Return its
-   address in "current_header", and if it is good, the file's size in
-   current_stat.st_size.
-
-   Return 1 for success, 0 if the checksum is bad, EOF on eof, 2 for a
-   block full of zeros (EOF marker).
-
-   You must always set_next_block_after(current_header) to skip past
-   the header which this routine reads.  */
-
+/* Check header checksum */
 /* The standard BSD tar sources create the checksum by adding up the
    bytes in the header as type char.  I think the type char was unsigned
    on the PDP-11, but it's signed on the Next and Sun.  It looks like the
    sources to BSD tar were never changed to compute the checksum
    correctly, so both the Sun and Next add the bytes of the header as
    signed chars.  This doesn't cause a problem until you get a file with
-   a name containing characters with the high bit set.  So read_header
+   a name containing characters with the high bit set.  So tar_checksum
    computes two checksums -- signed and unsigned.  */
 
 enum read_header
-read_header (void)
+tar_checksum (union block *header)
 {
   size_t i;
-  int unsigned_sum;            /* the POSIX one :-) */
-  int signed_sum;              /* the Sun one :-( */
+  int unsigned_sum = 0;                /* the POSIX one :-) */
+  int signed_sum = 0;          /* the Sun one :-( */
   int recorded_sum;
   uintmax_t parsed_sum;
   char *p;
+  
+  p = header->buffer;
+  for (i = sizeof *header; i-- != 0;)
+    {
+      unsigned_sum += (unsigned char) *p;
+      signed_sum += (signed char) (*p++);
+    }
+
+  if (unsigned_sum == 0)
+    return HEADER_ZERO_BLOCK;
+
+  /* Adjust checksum to count the "chksum" field as blanks.  */
+
+  for (i = sizeof header->header.chksum; i-- != 0;)
+    {
+      unsigned_sum -= (unsigned char) header->header.chksum[i];
+      signed_sum -= (signed char) (header->header.chksum[i]);
+    }
+  unsigned_sum += ' ' * sizeof header->header.chksum;
+  signed_sum += ' ' * sizeof header->header.chksum;
+
+  parsed_sum = from_header (header->header.chksum,
+                           sizeof header->header.chksum, 0,
+                           (uintmax_t) 0,
+                           (uintmax_t) TYPE_MAXIMUM (int));
+  if (parsed_sum == (uintmax_t) -1)
+    return HEADER_FAILURE;
+
+  recorded_sum = parsed_sum;
+  
+  if (unsigned_sum != recorded_sum && signed_sum != recorded_sum)
+    return HEADER_FAILURE;
+
+  return HEADER_SUCCESS;
+}
+
+/* Read a block that's supposed to be a header block.  Return its
+   address in "current_header", and if it is good, the file's size in
+   current_stat_info.stat.st_size.
+
+   Return 1 for success, 0 if the checksum is bad, EOF on eof, 2 for a
+   block full of zeros (EOF marker).
+
+   If RAW_EXTENDED_HEADERS is nonzero, do not automagically fold the
+   GNU long name and link headers into later headers.
+
+   You must always set_next_block_after(current_header) to skip past
+   the header which this routine reads.  */
+
+enum read_header
+read_header (bool raw_extended_headers)
+{
+  char *p;
   union block *header;
-  char **longp;
+  union block *header_copy;
   char *bp;
   union block *data_block;
   size_t size, written;
-  static char *next_long_name, *next_long_link;
+  union block *next_long_name = 0;
+  union block *next_long_link = 0;
+  size_t next_long_name_blocks;
+  size_t next_long_link_blocks;
 
   while (1)
     {
+      enum read_header status;
+      
       header = find_next_block ();
       current_header = header;
       if (!header)
        return HEADER_END_OF_FILE;
 
-      unsigned_sum = 0;
-      signed_sum = 0;
-      p = header->buffer;
-      for (i = sizeof *header; i-- != 0;)
-       {
-         unsigned_sum += (unsigned char) *p;
-         signed_sum += (signed char) (*p++);
-       }
-
-      if (unsigned_sum == 0)
-       return HEADER_ZERO_BLOCK;
-
-      /* Adjust checksum to count the "chksum" field as blanks.  */
-
-      for (i = sizeof header->header.chksum; i-- != 0;)
-       {
-         unsigned_sum -= (unsigned char) header->header.chksum[i];
-         signed_sum -= (signed char) (header->header.chksum[i]);
-       }
-      unsigned_sum += ' ' * sizeof header->header.chksum;
-      signed_sum += ' ' * sizeof header->header.chksum;
-
-      parsed_sum = from_header (header->header.chksum,
-                               sizeof header->header.chksum, 0,
-                               (uintmax_t) 0,
-                               (uintmax_t) TYPE_MAXIMUM (int));
-      if (parsed_sum == (uintmax_t) -1)
-       return HEADER_FAILURE;
-
-      recorded_sum = parsed_sum;
-
-      if (unsigned_sum != recorded_sum && signed_sum != recorded_sum)
-       return HEADER_FAILURE;
+      if ((status = tar_checksum (header)) != HEADER_SUCCESS)
+       return status;
 
       /* Good block.  Decode file size and return.  */
 
       if (header->header.typeflag == LNKTYPE)
-       current_stat.st_size = 0;       /* links 0 size on tape */
+       current_stat_info.stat.st_size = 0;     /* links 0 size on tape */
       else
-       current_stat.st_size = OFF_FROM_HEADER (header->header.size);
+       current_stat_info.stat.st_size = OFF_FROM_HEADER (header->header.size);
 
       if (header->header.typeflag == GNUTYPE_LONGNAME
-         || header->header.typeflag == GNUTYPE_LONGLINK)
+         || header->header.typeflag == GNUTYPE_LONGLINK
+         || header->header.typeflag == XHDTYPE
+         || header->header.typeflag == XGLTYPE)
        {
-         longp = ((header->header.typeflag == GNUTYPE_LONGNAME)
-                  ? &next_long_name
-                  : &next_long_link);
-
-         set_next_block_after (header);
-         if (*longp)
-           free (*longp);
-         size = current_stat.st_size;
-         if (size != current_stat.st_size)
-           xalloc_die ();
-         bp = *longp = xmalloc (size);
-
-         for (; size > 0; size -= written)
+         if (raw_extended_headers)
+           return HEADER_SUCCESS_EXTENDED;
+         else if (header->header.typeflag == GNUTYPE_LONGNAME
+                  || header->header.typeflag == GNUTYPE_LONGLINK)
            {
-             data_block = find_next_block ();
-             if (! data_block)
+             size_t name_size = current_stat_info.stat.st_size;
+             size = name_size - name_size % BLOCKSIZE + 2 * BLOCKSIZE;
+             if (name_size != current_stat_info.stat.st_size
+                 || size < name_size)
+               xalloc_die ();
+
+             header_copy = xmalloc (size + 1);
+
+             if (header->header.typeflag == GNUTYPE_LONGNAME)
                {
-                 ERROR ((0, 0, _("Unexpected EOF in archive")));
-                 break;
+                 if (next_long_name)
+                   free (next_long_name);
+                 next_long_name = header_copy;
+                 next_long_name_blocks = size / BLOCKSIZE;
+               }
+             else
+               {
+                 if (next_long_link)
+                   free (next_long_link);
+                 next_long_link = header_copy;
+                 next_long_link_blocks = size / BLOCKSIZE;
                }
-             written = available_space_after (data_block);
-             if (written > size)
-               written = size;
-
-             memcpy (bp, data_block->buffer, written);
-             bp += written;
-             set_next_block_after ((union block *)
-                                   (data_block->buffer + written - 1));
+
+             set_next_block_after (header);
+             *header_copy = *header;
+             bp = header_copy->buffer + BLOCKSIZE;
+
+             for (size -= BLOCKSIZE; size > 0; size -= written)
+               {
+                 data_block = find_next_block ();
+                 if (! data_block)
+                   {
+                     ERROR ((0, 0, _("Unexpected EOF in archive")));
+                     break;
+                   }
+                 written = available_space_after (data_block);
+                 if (written > size)
+                   written = size;
+
+                 memcpy (bp, data_block->buffer, written);
+                 bp += written;
+                 set_next_block_after ((union block *)
+                                       (data_block->buffer + written - 1));
+               }
+
+             *bp = '\0';
+           }
+         else if (header->header.typeflag == XHDTYPE)
+           xheader_read (header, OFF_FROM_HEADER (header->header.size));
+         else if (header->header.typeflag == XGLTYPE)
+           {
+             xheader_read (header, OFF_FROM_HEADER (header->header.size));
+             xheader_decode_global ();
            }
 
          /* Loop!  */
@@ -354,8 +441,16 @@ read_header (void)
          struct posix_header const *h = &current_header->header;
          char namebuf[sizeof h->prefix + 1 + NAME_FIELD_SIZE + 1];
 
-         name = next_long_name;
-         if (! name)
+         if (recent_long_name)
+           free (recent_long_name);
+
+         if (next_long_name)
+           {
+             name = next_long_name->buffer + BLOCKSIZE;
+             recent_long_name = next_long_name;
+             recent_long_name_blocks = next_long_name_blocks;
+           }
+         else
            {
              /* Accept file names as specified by POSIX.1-1996
                  section 10.1.1.  */
@@ -376,33 +471,39 @@ read_header (void)
              memcpy (np, h->name, sizeof h->name);
              np[sizeof h->name] = '\0';
              name = namebuf;
+             recent_long_name = 0;
+             recent_long_name_blocks = 0;
            }
-         assign_string (&current_file_name, name);
-         if (next_long_name)
+         assign_string (&current_stat_info.orig_file_name, name);
+         assign_string (&current_stat_info.file_name, name);
+         current_stat_info.had_trailing_slash = strip_trailing_slashes (current_stat_info.file_name);
+
+         if (recent_long_link)
+           free (recent_long_link);
+
+         if (next_long_link)
            {
-             free (next_long_name);
-             next_long_name = 0;
+             name = next_long_link->buffer + BLOCKSIZE;
+             recent_long_link = next_long_link;
+             recent_long_link_blocks = next_long_link_blocks;
            }
-         
-         name = next_long_link;
-         if (! name)
+         else
            {
              memcpy (namebuf, h->linkname, sizeof h->linkname);
              namebuf[sizeof h->linkname] = '\0';
              name = namebuf;
+             recent_long_link = 0;
+             recent_long_link_blocks = 0;
            }
-         assign_string (&current_link_name, name);
-         if (next_long_link)
-           {
-             free (next_long_link);
-             next_long_link = 0;
-           }
+         assign_string (&current_stat_info.link_name, name);
 
          return HEADER_SUCCESS;
        }
     }
 }
 
+#define ISOCTAL(c) ((c)>='0'&&(c)<='7')
+
 /* Decode things from a file HEADER block into STAT_INFO, also setting
    *FORMAT_POINTER depending on the header block format.  If
    DO_USER_GROUP, decode the user/group information (this is useful
@@ -417,74 +518,108 @@ read_header (void)
    should decode it without uid/gid before calling a routine,
    e.g. print_header, that assumes decoded data.  */
 void
-decode_header (union block *header, struct stat *stat_info,
+decode_header (union block *header, struct tar_stat_info *stat_info,
               enum archive_format *format_pointer, int do_user_group)
 {
   enum archive_format format;
 
   if (strcmp (header->header.magic, TMAGIC) == 0)
-    format = POSIX_FORMAT;
+    {
+      if (header->star_header.prefix[130] == 0
+         && ISOCTAL (header->star_header.atime[0])
+         && header->star_header.atime[11] == ' '
+         && ISOCTAL (header->star_header.ctime[0])
+         && header->star_header.ctime[11] == ' ')
+       format = STAR_FORMAT;
+      else if (extended_header.size)
+       format = POSIX_FORMAT;
+      else
+       format = USTAR_FORMAT;
+    }
   else if (strcmp (header->header.magic, OLDGNU_MAGIC) == 0)
     format = OLDGNU_FORMAT;
   else
     format = V7_FORMAT;
   *format_pointer = format;
 
-  stat_info->st_mode = MODE_FROM_HEADER (header->header.mode);
-  stat_info->st_mtime = TIME_FROM_HEADER (header->header.mtime);
+  stat_info->stat.st_mode = MODE_FROM_HEADER (header->header.mode);
+  stat_info->stat.st_mtime = TIME_FROM_HEADER (header->header.mtime);
+  assign_string (&stat_info->uname,
+                header->header.uname[0] ? header->header.uname : NULL);
+  assign_string (&stat_info->gname,
+                header->header.gname[0] ? header->header.gname : NULL);
+  stat_info->devmajor = MAJOR_FROM_HEADER (header->header.devmajor);
+  stat_info->devminor = MINOR_FROM_HEADER (header->header.devminor);
+
+  stat_info->stat.st_atime = start_time;
+  stat_info->stat.st_ctime = start_time;
 
   if (format == OLDGNU_FORMAT && incremental_option)
     {
-      stat_info->st_atime = TIME_FROM_HEADER (header->oldgnu_header.atime);
-      stat_info->st_ctime = TIME_FROM_HEADER (header->oldgnu_header.ctime);
+      stat_info->stat.st_atime = TIME_FROM_HEADER (header->oldgnu_header.atime);
+      stat_info->stat.st_ctime = TIME_FROM_HEADER (header->oldgnu_header.ctime);
     }
 
   if (format == V7_FORMAT)
     {
-      stat_info->st_uid = UID_FROM_HEADER (header->header.uid);
-      stat_info->st_gid = GID_FROM_HEADER (header->header.gid);
-      stat_info->st_rdev = 0;
+      stat_info->stat.st_uid = UID_FROM_HEADER (header->header.uid);
+      stat_info->stat.st_gid = GID_FROM_HEADER (header->header.gid);
+      stat_info->stat.st_rdev = 0;
     }
   else
     {
+
+      if (format == STAR_FORMAT)
+       {
+         stat_info->stat.st_atime = TIME_FROM_HEADER (header->star_header.atime);
+         stat_info->stat.st_ctime = TIME_FROM_HEADER (header->star_header.ctime);
+       }
+
       if (do_user_group)
        {
          /* FIXME: Decide if this should somewhat depend on -p.  */
 
          if (numeric_owner_option
              || !*header->header.uname
-             || !uname_to_uid (header->header.uname, &stat_info->st_uid))
-           stat_info->st_uid = UID_FROM_HEADER (header->header.uid);
+             || !uname_to_uid (header->header.uname, &stat_info->stat.st_uid))
+           stat_info->stat.st_uid = UID_FROM_HEADER (header->header.uid);
 
          if (numeric_owner_option
              || !*header->header.gname
-             || !gname_to_gid (header->header.gname, &stat_info->st_gid))
-           stat_info->st_gid = GID_FROM_HEADER (header->header.gid);
+             || !gname_to_gid (header->header.gname, &stat_info->stat.st_gid))
+           stat_info->stat.st_gid = GID_FROM_HEADER (header->header.gid);
        }
+
       switch (header->header.typeflag)
        {
        case BLKTYPE:
-         stat_info->st_rdev
-           = makedev (MAJOR_FROM_HEADER (header->header.devmajor),
-                      MINOR_FROM_HEADER (header->header.devminor));
-         break;
-
        case CHRTYPE:
-         stat_info->st_rdev
-           = makedev (MAJOR_FROM_HEADER (header->header.devmajor),
-                      MINOR_FROM_HEADER (header->header.devminor));
+         stat_info->stat.st_rdev = makedev (stat_info->devmajor,
+                                            stat_info->devminor);
          break;
 
        default:
-         stat_info->st_rdev = 0;
+         stat_info->stat.st_rdev = 0;
        }
     }
+
+  stat_info->archive_file_size = stat_info->stat.st_size;
+  xheader_decode (stat_info);
+
+  if (sparse_member_p (stat_info))
+    {
+      sparse_fixup_header (stat_info);
+      stat_info->is_sparse = true;
+    }
+  else
+    stat_info->is_sparse = false;
 }
 
 /* Convert buffer at WHERE0 of size DIGS from external format to
    uintmax_t.  The data is of type TYPE.  The buffer must represent a
    value in the range -MINUS_MINVAL through MAXVAL.  DIGS must be
-   positive.  */
+   positive.  Return -1 on error, diagnosing the error if TYPE is
+   nonzero.  */
 static uintmax_t
 from_header (char const *where0, size_t digs, char const *type,
             uintmax_t minus_minval, uintmax_t maxval)
@@ -534,7 +669,7 @@ from_header (char const *where0, size_t digs, char const *type,
          nonzero digit is 1, we can't recover the original value
          reliably; so do this only if the digit is 2 or more.  This
          catches the common case of 32-bit negative time stamps.  */
-      if ((overflow || maxval < value) && '2' <= *where1)
+      if ((overflow || maxval < value) && '2' <= *where1 && type)
        {
          /* Compute the negative of the input value, assuming two's
             complement.  */
@@ -566,79 +701,76 @@ from_header (char const *where0, size_t digs, char const *type,
 
       if (overflow)
        {
-         ERROR ((0, 0,
-                 _("Archive octal value %.*s is out of %s range"),
-                 (int) (where - where1), where1, type));
+         if (type)
+           ERROR ((0, 0,
+                   _("Archive octal value %.*s is out of %s range"),
+                   (int) (where - where1), where1, type));
          return -1;
        }
     }
-  else if (type)
+  else if (*where == '-' || *where == '+')
     {
-      /* The following forms cannot appear as checksums, so we don't
-        check for them if TYPE is null.  */
-
-      if (*where == '-' || *where == '+')
+      /* Parse base-64 output produced only by tar test versions
+        1.13.6 (1999-08-11) through 1.13.11 (1999-08-23).
+        Support for this will be withdrawn in future releases.  */
+      int dig;
+      static int warned_once;
+      if (! warned_once)
        {
-         /* Parse base-64 output produced only by tar test versions
-            1.13.6 (1999-08-11) through 1.13.11 (1999-08-23).
-            Support for this will be withdrawn in future releases.  */
-         int dig;
-         static int warned_once;
-         if (! warned_once)
-           {
-             warned_once = 1;
-             WARN ((0, 0,
-                    _("Archive contains obsolescent base-64 headers")));
-           }
-         negative = *where++ == '-';
-         while (where != lim
-                && (dig = base64_map[(unsigned char) *where]) < 64)
+         warned_once = 1;
+         WARN ((0, 0,
+                _("Archive contains obsolescent base-64 headers")));
+       }
+      negative = *where++ == '-';
+      while (where != lim
+            && (dig = base64_map[(unsigned char) *where]) < 64)
+       {
+         if (value << LG_64 >> LG_64 != value)
            {
-             if (value << LG_64 >> LG_64 != value)
-               {
-                 char *string = alloca (digs + 1);
-                 memcpy (string, where0, digs);
-                 string[digs] = '\0';
-                 ERROR ((0, 0,
-                         _("Archive signed base-64 string %s is out of %s range"),
-                         quote (string), type));
-                 return -1;
-               }
-             value = (value << LG_64) | dig;
-             where++;
+             char *string = alloca (digs + 1);
+             memcpy (string, where0, digs);
+             string[digs] = '\0';
+             if (type)
+               ERROR ((0, 0,
+                       _("Archive signed base-64 string %s is out of %s range"),
+                       quote (string), type));
+             return -1;
            }
+         value = (value << LG_64) | dig;
+         where++;
        }
-      else if (*where == '\200' /* positive base-256 */
-              || *where == '\377' /* negative base-256 */)
+    }
+  else if (*where == '\200' /* positive base-256 */
+          || *where == '\377' /* negative base-256 */)
+    {
+      /* Parse base-256 output.  A nonnegative number N is
+        represented as (256**DIGS)/2 + N; a negative number -N is
+        represented as (256**DIGS) - N, i.e. as two's complement.
+        The representation guarantees that the leading bit is
+        always on, so that we don't confuse this format with the
+        others (assuming ASCII bytes of 8 bits or more).  */
+      int signbit = *where & (1 << (LG_256 - 2));
+      uintmax_t topbits = (((uintmax_t) - signbit)
+                          << (CHAR_BIT * sizeof (uintmax_t)
+                              - LG_256 - (LG_256 - 2)));
+      value = (*where++ & ((1 << (LG_256 - 2)) - 1)) - signbit;
+      for (;;)
        {
-         /* Parse base-256 output.  A nonnegative number N is
-            represented as (256**DIGS)/2 + N; a negative number -N is
-            represented as (256**DIGS) - N, i.e. as two's complement.
-            The representation guarantees that the leading bit is
-            always on, so that we don't confuse this format with the
-            others (assuming ASCII bytes of 8 bits or more).  */
-         int signbit = *where & (1 << (LG_256 - 2));
-         uintmax_t topbits = (((uintmax_t) - signbit)
-                              << (CHAR_BIT * sizeof (uintmax_t)
-                                  - LG_256 - (LG_256 - 2)));
-         value = (*where++ & ((1 << (LG_256 - 2)) - 1)) - signbit;
-         for (;;)
+         value = (value << LG_256) + (unsigned char) *where++;
+         if (where == lim)
+           break;
+         if (((value << LG_256 >> LG_256) | topbits) != value)
            {
-             value = (value << LG_256) + (unsigned char) *where++;
-             if (where == lim)
-               break;
-             if (((value << LG_256 >> LG_256) | topbits) != value)
-               {
-                 ERROR ((0, 0,
-                         _("Archive base-256 value is out of %s range"),
-                         type));
-                 return -1;
-               }
+             if (type)
+               ERROR ((0, 0,
+                       _("Archive base-256 value is out of %s range"),
+                       type));
+             return -1;
            }
-         negative = signbit;
-         if (negative)
-           value = -value;
        }
+      negative = signbit;
+      if (negative)
+       value = -value;
     }
 
   if (where != lim && *where && !ISSPACE ((unsigned char) *where))
@@ -679,7 +811,7 @@ from_header (char const *where0, size_t digs, char const *type,
        *--value_string = '-';
       if (minus_minval)
        *--minval_string = '-';
-      ERROR ((0, 0, _("Archive value %s is out of %s range %s..%s"),
+      ERROR ((0, 0, _("Archive value %s is out of %s range %s.%s"),
              value_string, type,
              minval_string, STRINGIFY_BIGINT (maxval, maxval_buf)));
     }
@@ -744,7 +876,7 @@ off_from_header (const char *p, size_t s)
 size_t
 size_from_header (const char *p, size_t s)
 {
-  return from_header (p, s, "size_t", (uintmax_t) 0, 
+  return from_header (p, s, "size_t", (uintmax_t) 0,
                      (uintmax_t) TYPE_MAXIMUM (size_t));
 }
 
@@ -807,11 +939,11 @@ tartime (time_t t)
 #else
   /* Use ISO 8610 format.  See:
      http://www.cl.cam.ac.uk/~mgk25/iso-time.html  */
-  struct tm *tm = localtime (&t);
+  struct tm *tm = utc_option ? gmtime (&t) : localtime (&t);
   if (tm)
     {
-      sprintf (buffer, "%04d-%02d-%02d %02d:%02d:%02d",
-              tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+      sprintf (buffer, "%04ld-%02d-%02d %02d:%02d:%02d",
+              tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
               tm->tm_hour, tm->tm_min, tm->tm_sec);
       return buffer;
     }
@@ -841,7 +973,7 @@ tartime (time_t t)
 
 
 /* FIXME: Note that print_header uses the globals HEAD, HSTAT, and
-   HEAD_STANDARD, which must be set up in advance.  Not very clean...  */
+   HEAD_STANDARD, which must be set up in advance.  Not very clean..  */
 
 /* UGSWIDTH starts with 18, so with user and group names <= 8 chars, the
    columns never shift during the listing.  */
@@ -856,10 +988,12 @@ static int ugswidth = UGSWIDTH;   /* maximum width encountered so far */
 #endif
 
 void
-print_header (void)
+print_header (struct tar_stat_info *st, off_t block_ordinal)
 {
   char modes[11];
   char const *time_stamp;
+  char *temp_name = st->orig_file_name ? st->orig_file_name : st->file_name;
+
   /* These hold formatted ints.  */
   char uform[UINTMAX_STRSIZE_BOUND], gform[UINTMAX_STRSIZE_BOUND];
   char *user, *group;
@@ -871,14 +1005,18 @@ print_header (void)
   if (block_number_option)
     {
       char buf[UINTMAX_STRSIZE_BOUND];
+      if (block_ordinal < 0)
+       block_ordinal = current_block_ordinal ();
+      block_ordinal -= recent_long_name_blocks;
+      block_ordinal -= recent_long_link_blocks;
       fprintf (stdlis, _("block %s: "),
-              STRINGIFY_BIGINT (current_block_ordinal (), buf));
+              STRINGIFY_BIGINT (block_ordinal, buf));
     }
 
   if (verbose_option <= 1)
     {
       /* Just the fax, mam.  */
-      fprintf (stdlis, "%s\n", quotearg (current_file_name));
+      fprintf (stdlis, "%s\n", quotearg (temp_name));
     }
   else
     {
@@ -901,17 +1039,20 @@ print_header (void)
 
        case GNUTYPE_LONGNAME:
        case GNUTYPE_LONGLINK:
+         modes[0] = 'L';
          ERROR ((0, 0, _("Visible longname error")));
          break;
 
        case GNUTYPE_SPARSE:
        case REGTYPE:
        case AREGTYPE:
-       case LNKTYPE:
          modes[0] = '-';
-         if (current_file_name[strlen (current_file_name) - 1] == '/')
+         if (temp_name[strlen (temp_name) - 1] == '/')
            modes[0] = 'd';
          break;
+       case LNKTYPE:
+         modes[0] = 'h';
+         break;
        case GNUTYPE_DUMPDIR:
          modes[0] = 'd';
          break;
@@ -935,29 +1076,57 @@ print_header (void)
          break;
        }
 
-      decode_mode (current_stat.st_mode, modes + 1);
+      decode_mode (st->stat.st_mode, modes + 1);
 
       /* Time stamp.  */
 
-      time_stamp = tartime (current_stat.st_mtime);
+      time_stamp = tartime (st->stat.st_mtime);
 
       /* User and group names.  */
 
-      if (*current_header->header.uname && current_format != V7_FORMAT
+      if (st->uname && current_format != V7_FORMAT
          && !numeric_owner_option)
-       user = current_header->header.uname;
+       user = st->uname;
       else
-       user = STRINGIFY_BIGINT (UINTMAX_FROM_HEADER
-                                (current_header->header.uid),
-                                uform);
+       {
+         /* Try parsing it as an unsigned integer first, and as a
+            uid_t if that fails.  This method can list positive user
+            ids that are too large to fit in a uid_t.  */
+         uintmax_t u = from_header (current_header->header.uid,
+                                    sizeof current_header->header.uid, 0,
+                                    (uintmax_t) 0,
+                                    (uintmax_t) TYPE_MAXIMUM (uintmax_t));
+         if (u != -1)
+           user = STRINGIFY_BIGINT (u, uform);
+         else
+           {
+             sprintf (uform, "%ld",
+                      (long) UID_FROM_HEADER (current_header->header.uid));
+             user = uform;
+           }
+       }
 
-      if (*current_header->header.gname && current_format != V7_FORMAT
+      if (st->gname && current_format != V7_FORMAT
          && !numeric_owner_option)
-       group = current_header->header.gname;
+       group = st->gname;
       else
-       group = STRINGIFY_BIGINT (UINTMAX_FROM_HEADER
-                                 (current_header->header.gid),
-                                 gform);
+       {
+         /* Try parsing it as an unsigned integer first, and as a
+            gid_t if that fails.  This method can list positive group
+            ids that are too large to fit in a gid_t.  */
+         uintmax_t g = from_header (current_header->header.gid,
+                                    sizeof current_header->header.gid, 0,
+                                    (uintmax_t) 0,
+                                    (uintmax_t) TYPE_MAXIMUM (uintmax_t));
+         if (g != -1)
+           group = STRINGIFY_BIGINT (g, gform);
+         else
+           {
+             sprintf (gform, "%ld",
+                      (long) GID_FROM_HEADER (current_header->header.gid));
+             group = gform;
+           }
+       }
 
       /* Format the file size or major/minor device numbers.  */
 
@@ -966,20 +1135,15 @@ print_header (void)
        case CHRTYPE:
        case BLKTYPE:
          strcpy (size,
-                 STRINGIFY_BIGINT (major (current_stat.st_rdev), uintbuf));
+                 STRINGIFY_BIGINT (major (st->stat.st_rdev), uintbuf));
          strcat (size, ",");
          strcat (size,
-                 STRINGIFY_BIGINT (minor (current_stat.st_rdev), uintbuf));
-         break;
-       case GNUTYPE_SPARSE:
-         strcpy (size,
-                 STRINGIFY_BIGINT
-                 (UINTMAX_FROM_HEADER (current_header
-                                       ->oldgnu_header.realsize),
-                  uintbuf));
+                 STRINGIFY_BIGINT (minor (st->stat.st_rdev), uintbuf));
          break;
+
        default:
-         strcpy (size, STRINGIFY_BIGINT (current_stat.st_size, uintbuf));
+         /* st->stat.st_size keeps stored file size */
+         strcpy (size, STRINGIFY_BIGINT (st->stat.st_size, uintbuf));
          break;
        }
 
@@ -992,16 +1156,16 @@ print_header (void)
       fprintf (stdlis, "%s %s/%s %*s%s %s",
               modes, user, group, ugswidth - pad, "", size, time_stamp);
 
-      fprintf (stdlis, " %s", quotearg (current_file_name));
+      fprintf (stdlis, " %s", quotearg (temp_name));
 
       switch (current_header->header.typeflag)
        {
        case SYMTYPE:
-         fprintf (stdlis, " -> %s\n", quotearg (current_link_name));
+         fprintf (stdlis, " -> %s\n", quotearg (st->link_name));
          break;
 
        case LNKTYPE:
-         fprintf (stdlis, _(" link to %s\n"), quotearg (current_link_name));
+         fprintf (stdlis, _(" link to %s\n"), quotearg (st->link_name));
          break;
 
        default:
@@ -1026,6 +1190,14 @@ print_header (void)
          putc ('\n', stdlis);
          break;
 
+       case GNUTYPE_LONGLINK:
+         fprintf (stdlis, _("--Long Link--\n"));
+         break;
+
+       case GNUTYPE_LONGNAME:
+         fprintf (stdlis, _("--Long Name--\n"));
+         break;
+
        case GNUTYPE_VOLHDR:
          fprintf (stdlis, _("--Volume Header--\n"));
          break;
@@ -1048,7 +1220,7 @@ print_header (void)
 
 /* Print a similar line when we make a directory automatically.  */
 void
-print_for_mkdir (char *pathname, int length, mode_t mode)
+print_for_mkdir (char *dirname, int length, mode_t mode)
 {
   char modes[11];
 
@@ -1067,7 +1239,7 @@ print_for_mkdir (char *pathname, int length, mode_t mode)
        }
 
       fprintf (stdlis, "%s %*s %.*s\n", modes, ugswidth + DATEWIDTH,
-              _("Creating directory:"), length, quotearg (pathname));
+              _("Creating directory:"), length, quotearg (dirname));
     }
 }
 
@@ -1083,6 +1255,19 @@ skip_file (off_t size)
       save_sizeleft = size;
     }
 
+  if (seekable_archive)
+    {
+      off_t nblk = seek_archive (size);
+      if (nblk >= 0)
+       {
+         size -= nblk * BLOCKSIZE;
+         if (multi_volume_option) /* Argh.. */
+           save_sizeleft -= nblk * BLOCKSIZE;
+       }
+      else
+       seekable_archive = false;
+    }
+  
   while (size > 0)
     {
       x = find_next_block ();
@@ -1096,26 +1281,18 @@ skip_file (off_t size)
     }
 }
 
-/* Skip the current member in the archive.  */
+/* Skip the current member in the archive.
+   NOTE: Current header must be decoded before calling this function. */
 void
 skip_member (void)
 {
   char save_typeflag = current_header->header.typeflag;
   set_next_block_after (current_header);
+  
+  assign_string (&save_name, current_stat_info.file_name);
 
-  if (current_header->oldgnu_header.isextended)
-    {
-      union block *exhdr;
-      do
-       {
-         exhdr = find_next_block ();
-         if (!exhdr)
-           FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
-         set_next_block_after (exhdr);
-       }
-      while (exhdr->sparse_header.isextended);
-    }
-
-  if (save_typeflag != DIRTYPE)
-    skip_file (current_stat.st_size);
+  if (current_stat_info.is_sparse)
+    sparse_skip_file (&current_stat_info);
+  else if (save_typeflag != DIRTYPE)
+    skip_file (current_stat_info.stat.st_size);
 }
This page took 0.047837 seconds and 4 git commands to generate.