+static char *
+decode_xform (char *file_name, void *data)
+{
+ int type = *(int*)data;
+
+ switch (type)
+ {
+ case XFORM_SYMLINK:
+ /* FIXME: It is not quite clear how and to which extent are the symbolic
+ links subject to filename transformation. In the absence of another
+ solution, symbolic links are exempt from component stripping and
+ name suffix normalization, but subject to filename transformation
+ proper. */
+ return file_name;
+
+ case XFORM_LINK:
+ file_name = safer_name_suffix (file_name, true, absolute_names_option);
+ break;
+
+ case XFORM_REGFILE:
+ file_name = safer_name_suffix (file_name, false, absolute_names_option);
+ break;
+ }
+
+ if (strip_name_components)
+ {
+ size_t prefix_len = stripped_prefix_len (file_name,
+ strip_name_components);
+ if (prefix_len == (size_t) -1)
+ prefix_len = strlen (file_name);
+ file_name += prefix_len;
+ }
+ return file_name;
+}
+
+static bool
+transform_member_name (char **pinput, int type)
+{
+ return transform_name_fp (pinput, type, decode_xform, &type);
+}
+
+#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
+ for extraction, but waste time when merely listing).
+
+ read_header() has already decoded the checksum and length, so we don't.
+
+ This routine should *not* be called twice for the same block, since
+ the two calls might use different DO_USER_GROUP values and thus
+ might end up with different uid/gid for the two calls. If anybody
+ wants the uid/gid they should decode it first, and other callers
+ 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 tar_stat_info *stat_info,
+ enum archive_format *format_pointer, int do_user_group)
+{
+ enum archive_format format;
+ unsigned hbits; /* high bits of the file mode. */
+ mode_t mode = MODE_FROM_HEADER (header->header.mode, &hbits);
+
+ if (strcmp (header->header.magic, TMAGIC) == 0)
+ {
+ 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 (stat_info->xhdr.size)
+ format = POSIX_FORMAT;
+ else
+ format = USTAR_FORMAT;
+ }
+ else if (strcmp (header->header.magic, OLDGNU_MAGIC) == 0)
+ format = hbits ? OLDGNU_FORMAT : GNU_FORMAT;
+ else
+ format = V7_FORMAT;
+ *format_pointer = format;
+
+ stat_info->stat.st_mode = mode;
+ 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);
+
+ if (format == OLDGNU_FORMAT && incremental_option)
+ {
+ 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)
+ {
+ 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 (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->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->stat.st_gid))
+ stat_info->stat.st_gid = GID_FROM_HEADER (header->header.gid);
+ }
+
+ switch (header->header.typeflag)
+ {
+ case BLKTYPE:
+ case CHRTYPE:
+ stat_info->stat.st_rdev =
+ makedev (MAJOR_FROM_HEADER (header->header.devmajor),
+ MINOR_FROM_HEADER (header->header.devminor));
+ break;
+
+ default:
+ 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;
+ if (((current_format == GNU_FORMAT
+ || current_format == OLDGNU_FORMAT)
+ && current_header->header.typeflag == GNUTYPE_DUMPDIR)
+ || stat_info->dumpdir)
+ stat_info->is_dumpdir = true;
+ }
+
+ transform_member_name (&stat_info->file_name, XFORM_REGFILE);
+ switch (header->header.typeflag)
+ {
+ case SYMTYPE:
+ transform_member_name (&stat_info->link_name, XFORM_SYMLINK);
+ break;
+
+ case LNKTYPE:
+ transform_member_name (&stat_info->link_name, XFORM_LINK);
+ }
+}