+ /* Seek back to the beginning of this record and start writing there. */
+
+ position -= record_size;
+ if (position < 0)
+ position = 0;
+ if (rmtlseek (archive, position, SEEK_SET) != position)
+ {
+ /* Lseek failed. Try a different method. */
+
+ WARN ((0, 0,
+ _("Cannot backspace archive file; it may be unreadable without -i")));
+
+ /* Replace the first part of the record with NULs. */
+
+ if (record_start->buffer != output_start)
+ memset (record_start->buffer, 0,
+ output_start - record_start->buffer);
+ }
+ }
+}
+
+off_t
+seek_archive (off_t size)
+{
+ off_t start = current_block_ordinal ();
+ off_t offset;
+ off_t nrec, nblk;
+ off_t skipped = (blocking_factor - (current_block - record_start))
+ * BLOCKSIZE;
+
+ if (size <= skipped)
+ return 0;
+
+ /* Compute number of records to skip */
+ nrec = (size - skipped) / record_size;
+ if (nrec == 0)
+ return 0;
+ offset = rmtlseek (archive, nrec * record_size, SEEK_CUR);
+ if (offset < 0)
+ return offset;
+
+ if (offset % record_size)
+ FATAL_ERROR ((0, 0, _("rmtlseek not stopped at a record boundary")));
+
+ /* Convert to number of records */
+ offset /= BLOCKSIZE;
+ /* Compute number of skipped blocks */
+ nblk = offset - start;
+
+ /* Update buffering info */
+ records_read += nblk / blocking_factor;
+ record_start_block = offset - blocking_factor;
+ current_block = record_end;
+
+ return nblk;
+}
+
+/* Close the archive file. */
+void
+close_archive (void)
+{
+ if (time_to_start_writing || access_mode == ACCESS_WRITE)
+ {
+ flush_archive ();
+ if (current_block > record_start)
+ flush_archive ();
+ }
+
+ compute_duration ();
+ if (verify_option)
+ verify_volume ();
+
+ if (rmtclose (archive) != 0)
+ close_error (*archive_name_cursor);
+
+ sys_wait_for_child (child_pid, hit_eof);
+
+ tar_stat_destroy (¤t_stat_info);
+ free (record_buffer[0]);
+ free (record_buffer[1]);
+ bufmap_free (NULL);
+}
+
+/* Called to initialize the global volume number. */
+void
+init_volume_number (void)
+{
+ FILE *file = fopen (volno_file_option, "r");
+
+ if (file)
+ {
+ if (fscanf (file, "%d", &global_volno) != 1
+ || global_volno < 0)
+ FATAL_ERROR ((0, 0, _("%s: contains invalid volume number"),
+ quotearg_colon (volno_file_option)));
+ if (ferror (file))
+ read_error (volno_file_option);
+ if (fclose (file) != 0)
+ close_error (volno_file_option);
+ }
+ else if (errno != ENOENT)
+ open_error (volno_file_option);
+}
+
+/* Called to write out the closing global volume number. */
+void
+closeout_volume_number (void)
+{
+ FILE *file = fopen (volno_file_option, "w");
+
+ if (file)
+ {
+ fprintf (file, "%d\n", global_volno);
+ if (ferror (file))
+ write_error (volno_file_option);
+ if (fclose (file) != 0)
+ close_error (volno_file_option);
+ }
+ else
+ open_error (volno_file_option);
+}
+
+\f
+static void
+increase_volume_number (void)
+{
+ global_volno++;
+ if (global_volno < 0)
+ FATAL_ERROR ((0, 0, _("Volume number overflow")));
+ volno++;
+}
+
+static void
+change_tape_menu (FILE *read_file)
+{
+ char *input_buffer = NULL;
+ size_t size = 0;
+ bool stop = false;
+
+ while (!stop)
+ {
+ fputc ('\007', stderr);
+ fprintf (stderr,
+ _("Prepare volume #%d for %s and hit return: "),
+ global_volno + 1, quote (*archive_name_cursor));
+ fflush (stderr);
+
+ if (getline (&input_buffer, &size, read_file) <= 0)
+ {
+ WARN ((0, 0, _("EOF where user reply was expected")));
+
+ if (subcommand_option != EXTRACT_SUBCOMMAND
+ && subcommand_option != LIST_SUBCOMMAND
+ && subcommand_option != DIFF_SUBCOMMAND)
+ WARN ((0, 0, _("WARNING: Archive is incomplete")));
+
+ fatal_exit ();
+ }
+
+ if (input_buffer[0] == '\n'
+ || input_buffer[0] == 'y'
+ || input_buffer[0] == 'Y')
+ break;
+
+ switch (input_buffer[0])
+ {
+ case '?':
+ {
+ fprintf (stderr, _("\
+ n name Give a new file name for the next (and subsequent) volume(s)\n\
+ q Abort tar\n\
+ y or newline Continue operation\n"));
+ if (!restrict_option)
+ fprintf (stderr, _(" ! Spawn a subshell\n"));
+ fprintf (stderr, _(" ? Print this list\n"));
+ }
+ break;
+
+ case 'q':
+ /* Quit. */
+
+ WARN ((0, 0, _("No new volume; exiting.\n")));
+
+ if (subcommand_option != EXTRACT_SUBCOMMAND
+ && subcommand_option != LIST_SUBCOMMAND
+ && subcommand_option != DIFF_SUBCOMMAND)
+ WARN ((0, 0, _("WARNING: Archive is incomplete")));
+
+ fatal_exit ();
+
+ case 'n':
+ /* Get new file name. */
+
+ {
+ char *name;
+ char *cursor;
+
+ for (name = input_buffer + 1;
+ *name == ' ' || *name == '\t';
+ name++)
+ ;
+
+ for (cursor = name; *cursor && *cursor != '\n'; cursor++)
+ ;
+ *cursor = '\0';
+
+ if (name[0])
+ {
+ /* FIXME: the following allocation is never reclaimed. */
+ *archive_name_cursor = xstrdup (name);
+ stop = true;
+ }
+ else
+ fprintf (stderr, "%s",
+ _("File name not specified. Try again.\n"));
+ }
+ break;
+
+ case '!':
+ if (!restrict_option)
+ {
+ sys_spawn_shell ();
+ break;
+ }
+ /* FALL THROUGH */
+
+ default:
+ fprintf (stderr, _("Invalid input. Type ? for help.\n"));
+ }
+ }
+ free (input_buffer);
+}
+
+/* We've hit the end of the old volume. Close it and open the next one.
+ Return nonzero on success.
+*/
+static bool
+new_volume (enum access_mode mode)
+{
+ static FILE *read_file;
+ static int looped;
+ int prompt;
+
+ if (!read_file && !info_script_option)
+ /* FIXME: if fopen is used, it will never be closed. */
+ read_file = archive == STDIN_FILENO ? fopen (TTY_NAME, "r") : stdin;
+
+ if (now_verifying)
+ return false;
+ if (verify_option)
+ verify_volume ();
+
+ assign_string (&volume_label, NULL);
+ assign_string (&continued_file_name, NULL);
+ continued_file_size = continued_file_offset = 0;
+ current_block = record_start;
+
+ if (rmtclose (archive) != 0)
+ close_error (*archive_name_cursor);
+
+ archive_name_cursor++;
+ if (archive_name_cursor == archive_name_array + archive_names)
+ {
+ archive_name_cursor = archive_name_array;
+ looped = 1;
+ }
+ prompt = looped;
+
+ tryagain:
+ if (prompt)
+ {
+ /* We have to prompt from now on. */
+
+ if (info_script_option)
+ {
+ if (volno_file_option)
+ closeout_volume_number ();
+ if (sys_exec_info_script (archive_name_cursor, global_volno+1))
+ FATAL_ERROR ((0, 0, _("%s command failed"),
+ quote (info_script_option)));
+ }