+/*--------------------------------------------------------------------.
+| Attempt repairing what went wrong with the extraction. Delete an |
+| already existing file or create missing intermediate directories. |
+| Return nonzero if we somewhat increased our chances at a successful |
+| extraction. errno is properly restored on zero return. |
+`--------------------------------------------------------------------*/
+
+static int
+maybe_recoverable (char *file_name)
+{
+ switch (errno)
+ {
+ case EEXIST:
+ /* Attempt deleting an existing file. However, with -k or -U, just stay
+ quiet. */
+
+ if (keep_old_files_option || unlink_first_option)
+ return 0;
+
+ return remove_any_file (file_name, 0);
+
+ case ENOENT:
+ /* Attempt creating missing intermediate directories. */
+
+ return make_directories (file_name);
+
+ default:
+ /* Just say we can't do anything about it... */
+
+ return 0;
+ }
+}
+
+/*---.
+| ? |
+`---*/
+
+static void
+extract_sparse_file (int fd, off_t *sizeleft, off_t totalsize, char *name)
+{
+ int sparse_ind = 0;
+ size_t written;
+ ssize_t count;
+
+ /* assuming sizeleft is initially totalsize */
+
+ while (*sizeleft > 0)
+ {
+ union block *data_block = find_next_block ();
+ if (! data_block)
+ {
+ ERROR ((0, 0, _("Unexpected EOF on archive file")));
+ return;
+ }
+ if (lseek (fd, sparsearray[sparse_ind].offset, SEEK_SET) < 0)
+ {
+ char buf[UINTMAX_STRSIZE_BOUND];
+ ERROR ((0, errno, _("%s: lseek error at byte %s"),
+ STRINGIFY_BIGINT (sparsearray[sparse_ind].offset, buf),
+ name));
+ return;
+ }
+ written = sparsearray[sparse_ind++].numbytes;
+ while (written > BLOCKSIZE)
+ {
+ count = full_write (fd, data_block->buffer, BLOCKSIZE);
+ if (count < 0)
+ ERROR ((0, errno, _("%s: Could not write to file"), name));
+ written -= count;
+ *sizeleft -= count;
+ set_next_block_after (data_block);
+ data_block = find_next_block ();
+ if (! data_block)
+ {
+ ERROR ((0, 0, _("Unexpected EOF on archive file")));
+ return;
+ }
+ }
+
+ count = full_write (fd, data_block->buffer, written);
+
+ if (count < 0)
+ ERROR ((0, errno, _("%s: Could not write to file"), name));
+ else if (count != written)
+ {
+ char buf1[UINTMAX_STRSIZE_BOUND];
+ char buf2[UINTMAX_STRSIZE_BOUND];
+ ERROR ((0, 0, _("%s: Could only write %s of %s bytes"),
+ name,
+ STRINGIFY_BIGINT (totalsize - *sizeleft, buf1),
+ STRINGIFY_BIGINT (totalsize, buf2)));
+ skip_file (*sizeleft);
+ }
+
+ written -= count;
+ *sizeleft -= count;
+ set_next_block_after (data_block);
+ }
+
+ free (sparsearray);
+}
+
+/*----------------------------------.
+| Extract a file from the archive. |
+`----------------------------------*/