+ 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;
+
+ /* Good block. Decode file size and return. */
+
+ if (header->header.typeflag == LNKTYPE)
+ current_stat_info.stat.st_size = 0; /* links 0 size on tape */
+ else
+ 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 == XHDTYPE
+ || header->header.typeflag == XGLTYPE)
+ {
+ if (raw_extended_headers)
+ return HEADER_SUCCESS_EXTENDED;
+ else if (header->header.typeflag == GNUTYPE_LONGNAME
+ || header->header.typeflag == GNUTYPE_LONGLINK)
+ {
+ 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)
+ {
+ 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;
+ }
+
+ 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! */
+
+ }
+ else
+ {
+ char const *name;
+ struct posix_header const *h = ¤t_header->header;
+ char namebuf[sizeof h->prefix + 1 + NAME_FIELD_SIZE + 1];
+
+ 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. */
+ char *np = namebuf;
+
+ if (h->prefix[0] && strcmp (h->magic, TMAGIC) == 0)
+ {
+ memcpy (np, h->prefix, sizeof h->prefix);
+ np[sizeof h->prefix] = '\0';
+ np += strlen (np);
+ *np++ = '/';
+
+ /* Prevent later references to current_header from
+ mistakenly treating this as an old GNU header.
+ This assignment invalidates h->prefix. */
+ current_header->oldgnu_header.isextended = 0;
+ }
+ 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 (¤t_stat_info.orig_file_name, name);
+ assign_string (¤t_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)
+ {
+ name = next_long_link->buffer + BLOCKSIZE;
+ recent_long_link = next_long_link;
+ recent_long_link_blocks = next_long_link_blocks;
+ }
+ 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 (¤t_stat_info.link_name, name);
+
+ return HEADER_SUCCESS;
+ }