+/*-------------------------------------------------------------------------.
+| 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. |
+`-------------------------------------------------------------------------*/
+
+void
+finish_header (union block *header)
+{
+ int i, sum;
+ char *p;
+
+ 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, a space, then a null. We use to_oct then write the null in
+ over to_oct's space. The final space is already there, from
+ checksumming, and to_oct doesn't modify it.
+
+ This is a fast way to do:
+
+ sprintf(header->header.chksum, "%6o", sum); */
+
+ to_oct ((long) sum, 8, header->header.chksum);
+ header->header.chksum[6] = '\0'; /* zap the space */
+
+ set_next_block_after (header);
+
+ if (verbose_option
+ && header->header.typeflag != GNUTYPE_LONGLINK
+ && header->header.typeflag != GNUTYPE_LONGNAME)
+ {
+ /* These globals are parameters to print_header, sigh. */
+
+ current_header = header;
+ /* current_stat is already set up. */
+ current_format = archive_format;
+ print_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;
+}
+
+/*---.
+| ? |
+`---*/
+
+static void
+init_sparsearray (void)
+{
+ int counter;
+
+ sp_array_size = 10;
+
+ /* Make room for our scratch space -- initially is 10 elts long. */
+
+ sparsearray = (struct sp_array *)
+ xmalloc (sp_array_size * sizeof (struct sp_array));
+ for (counter = 0; counter < sp_array_size; counter++)
+ {
+ sparsearray[counter].offset = 0;
+ sparsearray[counter].numbytes = 0;
+ }
+}
+
+/*---.
+| ? |
+`---*/
+
+static void
+find_new_file_size (int *filesize, int highest_index)
+{
+ int counter;
+
+ *filesize = 0;
+ for (counter = 0;
+ sparsearray[counter].numbytes && counter <= highest_index;
+ counter++)
+ *filesize += sparsearray[counter].numbytes;
+}
+
+/*-----------------------------------------------------------------------.
+| 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)
+{
+ long numbytes = 0;
+ long offset = 0;
+ int file;
+ int sparse_index = 0;
+ int 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);
+
+ while (count = read (file, buffer, sizeof buffer), count != 0)
+ {
+ /* Realloc the scratch area as necessary. FIXME: should reallocate
+ only at beginning of a new instance of non-zero data. */
+
+ if (sparse_index > sp_array_size - 1)
+ {
+
+ sparsearray = (struct sp_array *)
+ xrealloc (sparsearray,
+ 2 * sp_array_size * sizeof (struct sp_array));
+ sp_array_size *= 2;
+ }
+
+ /* Process one block. */
+
+ if (count == sizeof buffer)
+
+ if (zero_block_p (buffer))
+ {
+ if (numbytes)
+ {
+ sparsearray[sparse_index++].numbytes = numbytes;
+ numbytes = 0;
+ }
+ }
+ else
+ {
+ if (!numbytes)
+ sparsearray[sparse_index].offset = offset;
+ numbytes += count;
+ }
+
+ else
+
+ /* Since count < sizeof buffer, we have the last bit of the file. */
+
+ if (!zero_block_p (buffer))
+ {
+ if (!numbytes)
+ sparsearray[sparse_index].offset = offset;
+ numbytes += count;
+ }
+ else
+ /* The next two lines are suggested by Andreas Degert, who says
+ they are required for trailing full blocks to be written to the
+ archive, when all zeroed. Yet, it seems to me that the case
+ does not apply. Further, at restore time, the file is not as
+ sparse as it should. So, some serious cleanup is *also* needed
+ in this area. Just one more... :-(. FIXME. */
+ if (numbytes)
+ numbytes += count;
+
+ /* Prepare for next block. */
+
+ offset += count;
+ /* FIXME: do not clear unless necessary. */
+ clear_buffer (buffer);
+ }
+
+ if (numbytes)
+ sparsearray[sparse_index++].numbytes = numbytes;
+ else
+ {
+ sparsearray[sparse_index].offset = offset - 1;
+ sparsearray[sparse_index++].numbytes = 1;
+ }
+
+ close (file);
+ return sparse_index - 1;
+}
+
+/*---.
+| ? |
+`---*/
+
+static int
+finish_sparse_file (int file, long *sizeleft, long fullsize, char *name)
+{
+ union block *start;
+ int bufsize;
+ int sparse_index = 0;
+ int count;
+ long pos;
+ long nwritten = 0;
+
+ while (*sizeleft > 0)
+ {
+ start = find_next_block ();
+ memset (start->buffer, 0, BLOCKSIZE);
+ bufsize = sparsearray[sparse_index].numbytes;
+ if (!bufsize)
+ {
+ /* We blew it, maybe. */
+
+ ERROR ((0, 0, _("Wrote %ld of %ld bytes to file %s"),
+ fullsize - *sizeleft, fullsize, name));
+ break;
+ }
+ pos = lseek (file, sparsearray[sparse_index++].offset, 0);
+
+ /* If the number of bytes to be written here exceeds the size of
+ the temporary buffer, do it in steps. */
+
+ while (bufsize > BLOCKSIZE)
+ {
+#if 0
+ if (amount_read)
+ {
+ count = read (file, start->buffer + amount_read,
+ BLOCKSIZE - amount_read);
+ bufsize -= BLOCKSIZE - amount_read;
+ amount_read = 0;
+ set_next_block_after (start);
+ start = find_next_block ();
+ memset (start->buffer, 0, BLOCKSIZE);
+ }
+#endif
+ /* Store the data. */
+
+ count = read (file, start->buffer, BLOCKSIZE);
+ if (count < 0)
+ {
+ ERROR ((0, errno, _("\
+Read error at byte %ld, reading %d bytes, in file %s"),
+ fullsize - *sizeleft, bufsize, name));
+ return 1;
+ }
+ bufsize -= count;
+ *sizeleft -= count;
+ set_next_block_after (start);
+ nwritten += BLOCKSIZE; /* FIXME: ??? */
+ start = find_next_block ();
+ memset (start->buffer, 0, BLOCKSIZE);
+ }
+
+ {
+ char buffer[BLOCKSIZE];
+
+ clear_buffer (buffer);
+ count = read (file, buffer, (size_t) bufsize);
+ memcpy (start->buffer, buffer, BLOCKSIZE);
+ }
+
+ if (count < 0)
+ {
+ ERROR ((0, errno,
+ _("Read error at byte %ld, reading %d bytes, in file %s"),
+ fullsize - *sizeleft, bufsize, name));
+ return 1;
+ }
+#if 0
+ if (amount_read >= BLOCKSIZE)
+ {
+ amount_read = 0;
+ set_next_block_after (start + (count - 1) / BLOCKSIZE);
+ if (count != bufsize)
+ {
+ ERROR ((0, 0,
+ _("File %s shrunk by %d bytes, padding with zeros"),
+ name, sizeleft));
+ return 1;
+ }
+ start = find_next_block ();