+/* Finish off a filled-in header block and write it out. We also
+ print the file name and/or full info if verbose is on. If BLOCK_ORDINAL
+ is not negative, is the block ordinal of the first record for this
+ file, which may be a preceding long name or long link record. */
+void
+finish_header (union block *header, off_t block_ordinal)
+{
+ size_t i;
+ int sum;
+ char *p;
+
+ /* Note: It is important to do this before the call to write_extended(),
+ so that the actual ustar header is printed */
+ if (verbose_option
+ && header->header.typeflag != GNUTYPE_LONGLINK
+ && header->header.typeflag != GNUTYPE_LONGNAME
+ && header->header.typeflag != XHDTYPE
+ && header->header.typeflag != XGLTYPE)
+ {
+ /* These globals are parameters to print_header, sigh. */
+
+ current_header = header;
+ /* current_stat_info is already set up. */
+ current_format = archive_format;
+ print_header (block_ordinal);
+ }
+
+ header = write_extended (header, XHDTYPE);
+
+ memcpy (header->header.chksum, CHKBLANKS, sizeof header->header.chksum);
+
+ sum = 0;
+ p = header->buffer;
+ for (i = sizeof *header; i-- != 0; )
+ /* We can't use unsigned char here because of old compilers, e.g. V7. */
+ sum += 0xFF & *p++;
+
+ /* Fill in the checksum field. It's formatted differently from the
+ other fields: it has [6] digits, a null, then a space -- rather than
+ digits, then a null. We use to_chars.
+ The final space is already there, from
+ checksumming, and to_chars doesn't modify it.
+
+ This is a fast way to do:
+
+ sprintf(header->header.chksum, "%6o", sum); */
+
+ uintmax_to_chars ((uintmax_t) sum, header->header.chksum, 7);
+
+ set_next_block_after (header);
+}
+\f
+/* Sparse file processing. */
+
+/* Takes a blockful of data and basically cruises through it to see if
+ it's made *entirely* of zeros, returning a 0 the instant it finds
+ something that is a nonzero, i.e., useful data. */
+static int
+zero_block_p (char *buffer)
+{
+ int counter;
+
+ for (counter = 0; counter < BLOCKSIZE; counter++)
+ if (buffer[counter] != '\0')
+ return 0;
+ return 1;
+}
+
+void
+init_sparsearray (void)
+{
+ if (! sp_array_size)
+ sp_array_size = SPARSES_IN_OLDGNU_HEADER;
+ sparsearray = xmalloc (sp_array_size * sizeof *sparsearray);
+}
+
+static off_t
+find_new_file_size (int sparses)
+{
+ int i;
+ off_t s = 0;
+ for (i = 0; i < sparses; i++)
+ s += sparsearray[i].numbytes;
+ return s;
+}
+
+/* Make one pass over the file NAME, studying where any non-zero data
+ is, that is, how far into the file each instance of data is, and
+ how many bytes are there. Save this information in the
+ sparsearray, which will later be translated into header
+ information. */
+
+/* There is little point in trimming small amounts of null data at the head
+ and tail of blocks, only avoid dumping full null blocks. */
+
+/* FIXME: this routine might accept bits of algorithmic cleanup, it is
+ too kludgey for my taste... */
+
+static int
+deal_with_sparse (char *name, union block *header)
+{
+ size_t numbytes = 0;
+ off_t offset = 0;
+ int file;
+ int sparses = 0;
+ ssize_t count;
+ char buffer[BLOCKSIZE];
+
+ if (archive_format == OLDGNU_FORMAT)
+ header->oldgnu_header.isextended = 0;
+
+ if (file = open (name, O_RDONLY), file < 0)
+ /* This problem will be caught later on, so just return. */
+ return 0;
+
+ init_sparsearray ();
+ clear_buffer (buffer);
+
+ for (;;)
+ {
+ /* Realloc the scratch area as necessary. FIXME: should reallocate
+ only at beginning of a new instance of non-zero data. */
+
+ if (sp_array_size <= sparses)