/* List a tar archive, with support routines for reading a tar archive.
Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2000,
- 2001, 2003, 2004, 2005 Free Software Foundation, Inc.
+ 2001, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
Written by John Gilmore, on 1985-08-26.
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
-/* Define to non-zero for forcing old ctime format instead of ISO format. */
-#undef USE_OLD_CTIME
-
#include <system.h>
+#include <inttostr.h>
#include <quotearg.h>
#include "common.h"
{
enum read_header status = HEADER_STILL_UNREAD;
enum read_header prev_status;
+ struct timespec mtime;
base64_init ();
name_gather ();
|| (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_info.stat.st_mtime
+ && ((mtime.tv_sec
= 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)))
+ mtime.tv_nsec = 0,
+ current_stat_info.mtime = mtime,
+ OLDER_TAR_STAT_TIME (current_stat_info, m)))
|| excluded_name (current_stat_info.file_name))
{
switch (current_header->header.typeflag)
void
list_archive (void)
{
+ off_t block_ordinal = current_block_ordinal ();
/* Print the header block. */
decode_header (current_header, ¤t_stat_info, ¤t_format, 0);
if (verbose_option)
- print_header (¤t_stat_info, -1);
+ print_header (¤t_stat_info, block_ordinal);
- if (incremental_option && current_header->header.typeflag == GNUTYPE_DUMPDIR)
+ if (incremental_option)
{
- off_t size;
- size_t written, check;
- union block *data_block;
-
- set_next_block_after (current_header);
- if (multi_volume_option)
+ if (verbose_option > 2)
{
- assign_string (&save_name, current_stat_info.orig_file_name);
- save_totsize = current_stat_info.stat.st_size;
- }
- for (size = current_stat_info.stat.st_size; size > 0; size -= written)
- {
- if (multi_volume_option)
- save_sizeleft = size;
- data_block = find_next_block ();
- if (!data_block)
- {
- ERROR ((0, 0, _("Unexpected EOF in archive")));
- break; /* FIXME: What happens, then? */
- }
- written = available_space_after (data_block);
- if (written > size)
- written = size;
- errno = 0;
- check = fwrite (data_block->buffer, sizeof (char), written, stdlis);
- set_next_block_after ((union block *)
- (data_block->buffer + written - 1));
- if (check != written)
- {
- write_error_details (current_stat_info.file_name, check, written);
- skip_file (size - written);
- break;
- }
+ if (is_dumpdir (¤t_stat_info))
+ list_dumpdir (current_stat_info.dumpdir,
+ dumpdir_size (current_stat_info.dumpdir));
}
- if (multi_volume_option)
- assign_string (&save_name, 0);
- fputc ('\n', stdlis);
- fflush (stdlis);
- return;
-
}
- if (multi_volume_option)
- assign_string (&save_name, current_stat_info.orig_file_name);
-
skip_member ();
-
- if (multi_volume_option)
- assign_string (&save_name, 0);
}
/* Check header checksum */
{
xheader_read (header, OFF_FROM_HEADER (header->header.size));
xheader_decode_global ();
+ xheader_destroy (&extended_header);
}
/* Loop! */
*format_pointer = format;
stat_info->stat.st_mode = MODE_FROM_HEADER (header->header.mode);
- stat_info->stat.st_mtime = TIME_FROM_HEADER (header->header.mtime);
+ stat_info->mtime.tv_sec = TIME_FROM_HEADER (header->header.mtime);
+ stat_info->mtime.tv_nsec = 0;
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->stat.st_atime = TIME_FROM_HEADER (header->oldgnu_header.atime);
- stat_info->stat.st_ctime = TIME_FROM_HEADER (header->oldgnu_header.ctime);
+ stat_info->atime.tv_sec = TIME_FROM_HEADER (header->oldgnu_header.atime);
+ stat_info->ctime.tv_sec = TIME_FROM_HEADER (header->oldgnu_header.ctime);
+ stat_info->atime.tv_nsec = stat_info->ctime.tv_nsec = 0;
+ }
+ else if (format == STAR_FORMAT)
+ {
+ stat_info->atime.tv_sec = TIME_FROM_HEADER (header->star_header.atime);
+ stat_info->ctime.tv_sec = TIME_FROM_HEADER (header->star_header.ctime);
+ stat_info->atime.tv_nsec = stat_info->ctime.tv_nsec = 0;
}
+ else
+ stat_info->atime = stat_info->ctime = start_time;
if (format == V7_FORMAT)
{
}
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. */
{
case BLKTYPE:
case CHRTYPE:
- stat_info->stat.st_rdev = makedev (stat_info->devmajor,
- stat_info->devminor);
+ stat_info->stat.st_rdev =
+ makedev (MAJOR_FROM_HEADER (header->header.devmajor),
+ MINOR_FROM_HEADER (header->header.devminor));
break;
default:
stat_info->is_sparse = true;
}
else
- stat_info->is_sparse = false;
+ {
+ stat_info->is_sparse = false;
+ if (((current_format == GNU_FORMAT
+ || current_format == OLDGNU_FORMAT)
+ && current_header->header.typeflag == GNUTYPE_DUMPDIR)
+ || stat_info->dumpdir)
+ stat_info->is_dumpdir = true;
+ }
}
/* Convert buffer at WHERE0 of size DIGS from external format to
/* Return a printable representation of T. The result points to
static storage that can be reused in the next call to this
- function, to ctime, or to asctime. */
+ function, to ctime, or to asctime. If FULL_TIME, then output the
+ time stamp to its full resolution; otherwise, just output it to
+ 1-minute resolution. */
char const *
-tartime (time_t t)
+tartime (struct timespec t, bool full_time)
{
+ enum { fraclen = sizeof ".FFFFFFFFF" - 1 };
static char buffer[max (UINTMAX_STRSIZE_BOUND + 1,
- INT_STRLEN_BOUND (int) + 16)];
+ INT_STRLEN_BOUND (int) + 16)
+ + fraclen];
+ struct tm *tm;
+ time_t s = t.tv_sec;
+ int ns = t.tv_nsec;
+ bool negative = s < 0;
char *p;
-#if USE_OLD_CTIME
- p = ctime (&t);
- if (p)
+ if (negative && ns != 0)
{
- char const *time_stamp = p + 4;
- for (p += 16; p[3] != '\n'; p++)
- p[0] = p[3];
- p[0] = '\0';
- return time_stamp;
+ s++;
+ ns = 1000000000 - ns;
}
-#else
- /* Use ISO 8610 format. See:
- http://www.cl.cam.ac.uk/~mgk25/iso-time.html */
- struct tm *tm = utc_option ? gmtime (&t) : localtime (&t);
+
+ tm = utc_option ? gmtime (&s) : localtime (&s);
if (tm)
{
- 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);
+ if (full_time)
+ {
+ 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);
+ code_ns_fraction (ns, buffer + strlen (buffer));
+ }
+ else
+ sprintf (buffer, "%04ld-%02d-%02d %02d:%02d",
+ tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min);
return buffer;
}
-#endif
/* The time stamp cannot be broken down, most likely because it
is out of range. Convert it as an integer,
right-adjusted in a field with the same width as the usual
- 19-byte 4-year ISO time format. */
- p = stringify_uintmax_t_backwards (t < 0 ? - (uintmax_t) t : (uintmax_t) t,
- buffer + sizeof buffer);
- if (t < 0)
+ 4-year ISO time format. */
+ p = umaxtostr (negative ? - (uintmax_t) s : s,
+ buffer + sizeof buffer - UINTMAX_STRSIZE_BOUND - fraclen);
+ if (negative)
*--p = '-';
- while (buffer + sizeof buffer - 19 - 1 < p)
+ while ((buffer + sizeof buffer - sizeof "YYYY-MM-DD HH:MM"
+ + (full_time ? sizeof ":SS.FFFFFFFFF" - 1 : 0))
+ < p)
*--p = ' ';
+ if (full_time)
+ code_ns_fraction (ns, buffer + sizeof buffer - 1 - fraclen);
return p;
}
/* FIXME: Note that print_header uses the globals HEAD, HSTAT, and
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. */
-#define UGSWIDTH 18
-static int ugswidth = UGSWIDTH; /* maximum width encountered so far */
+/* Width of "user/group size", with initial value chosen
+ heuristically. This grows as needed, though this may cause some
+ stairstepping in the output. Make it too small and the output will
+ almost always look ragged. Make it too large and the output will
+ be spaced out too far. */
+static int ugswidth = 19;
-/* DATEWIDTH is the number of columns taken by the date and time fields. */
-#if USE_OLD_CDATE
-# define DATEWIDTH 19
-#else
-# define DATEWIDTH 18
-#endif
+/* Width of printed time stamps. It grows if longer time stamps are
+ found (typically, those with nanosecond resolution). Like
+ USGWIDTH, some stairstepping may occur. */
+static int datewidth = sizeof "YYYY-MM-DD HH:MM" - 1;
void
print_header (struct tar_stat_info *st, off_t block_ordinal)
{
char modes[11];
char const *time_stamp;
+ int time_stamp_len;
char *temp_name = st->orig_file_name ? st->orig_file_name : st->file_name;
/* These hold formatted ints. */
/* holds formatted size or major,minor */
char uintbuf[UINTMAX_STRSIZE_BOUND];
int pad;
+ int sizelen;
+
+ if (test_label_option && current_header->header.typeflag != GNUTYPE_VOLHDR)
+ return;
+
+ if (show_stored_names_option)
+ {
+ switch (subcommand_option)
+ {
+ case CAT_SUBCOMMAND:
+ case UPDATE_SUBCOMMAND:
+ case APPEND_SUBCOMMAND:
+ case CREATE_SUBCOMMAND:
+ temp_name = st->file_name ? st->file_name : st->orig_file_name;
+ break;
+
+ default:
+ temp_name = st->orig_file_name ? st->orig_file_name : st->file_name;
+ }
+ }
+ else
+ temp_name = st->orig_file_name ? st->orig_file_name : st->file_name;
if (block_number_option)
{
/* Time stamp. */
- time_stamp = tartime (st->stat.st_mtime);
+ time_stamp = tartime (st->mtime, false);
+ time_stamp_len = strlen (time_stamp);
+ if (datewidth < time_stamp_len)
+ datewidth = time_stamp_len;
/* User and group names. */
/* Figure out padding and print the whole line. */
- pad = strlen (user) + strlen (group) + strlen (size) + 1;
+ sizelen = strlen (size);
+ pad = strlen (user) + 1 + strlen (group) + 1 + sizelen;
if (pad > ugswidth)
ugswidth = pad;
- fprintf (stdlis, "%s %s/%s %*s%s %s",
- modes, user, group, ugswidth - pad, "", size, time_stamp);
+ fprintf (stdlis, "%s %s/%s %*s %-*s",
+ modes, user, group, ugswidth - pad + sizelen, size,
+ datewidth, time_stamp);
fprintf (stdlis, " %s", quotearg (temp_name));
STRINGIFY_BIGINT (current_block_ordinal (), buf));
}
- fprintf (stdlis, "%s %*s %.*s\n", modes, ugswidth + DATEWIDTH,
+ fprintf (stdlis, "%s %*s %.*s\n", modes, ugswidth + 1 + datewidth,
_("Creating directory:"), length, quotearg (dirname));
}
}
{
union block *x;
- if (multi_volume_option)
- {
- save_totsize = size;
- save_sizeleft = size;
- }
+ /* FIXME: Make sure mv_begin is always called before it */
if (seekable_archive)
{
off_t nblk = seek_archive (size);
if (nblk >= 0)
- {
- size -= nblk * BLOCKSIZE;
- if (multi_volume_option) /* Argh.. */
- save_sizeleft -= nblk * BLOCKSIZE;
- }
+ size -= nblk * BLOCKSIZE;
else
seekable_archive = false;
}
+ mv_size_left (size);
+
while (size > 0)
{
x = find_next_block ();
set_next_block_after (x);
size -= BLOCKSIZE;
- if (multi_volume_option)
- save_sizeleft -= BLOCKSIZE;
+ mv_size_left (size);
}
}
void
skip_member (void)
{
- char save_typeflag = current_header->header.typeflag;
- set_next_block_after (current_header);
+ if (!current_stat_info.skipped)
+ {
+ char save_typeflag = current_header->header.typeflag;
+ set_next_block_after (current_header);
- assign_string (&save_name, current_stat_info.orig_file_name);
+ mv_begin (¤t_stat_info);
- if (current_stat_info.is_sparse)
- sparse_skip_file (¤t_stat_info);
- else if (save_typeflag != DIRTYPE)
- skip_file (current_stat_info.stat.st_size);
+ if (current_stat_info.is_sparse)
+ sparse_skip_file (¤t_stat_info);
+ else if (save_typeflag != DIRTYPE)
+ skip_file (current_stat_info.stat.st_size);
+
+ mv_end ();
+ }
}