--- /dev/null
+/* Delete entries from a tar archive.
+ Copyright (C) 1988, 1992, 1994, 1996, 1997 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any later
+ version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "system.h"
+
+#define STDIN 0
+#define STDOUT 1
+
+#include "common.h"
+#include "rmt.h"
+
+static union block *new_record = NULL;
+static union block *save_record = NULL;
+static int records_read = 0;
+static int new_blocks = 0;
+static int blocks_needed = 0;
+
+/* FIXME: This module should not directly handle the following three
+ variables, instead, this should be done in buffer.c only. */
+extern union block *record_start;
+extern union block *record_end;
+extern union block *current_block;
+
+/*-------------------------------------------------------------------------.
+| Move archive descriptor by COUNT records worth. If COUNT is positive we |
+| move forward, else we move negative. If its a tape, MTIOCTOP had better |
+| work. If its something else, we try to seek on it. If we can't seek, |
+| we loose! |
+`-------------------------------------------------------------------------*/
+
+static void
+move_archive (int count)
+{
+#ifdef MTIOCTOP
+ {
+ struct mtop operation;
+ int status;
+
+ if (count > 0)
+ {
+ operation.mt_op = MTFSR;
+ operation.mt_count = count;
+ }
+ else
+ {
+ operation.mt_op = MTBSR;
+ operation.mt_count = -count;
+ }
+
+ if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
+ status >= 0)
+ return;
+
+ if (errno == EIO)
+ if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
+ status >= 0)
+ return;
+ }
+#endif /* MTIOCTOP */
+
+ {
+ off_t position = rmtlseek (archive, 0L, 1);
+
+ position += record_size * count;
+
+ if (rmtlseek (archive, position, 0) != position)
+ FATAL_ERROR ((0, 0, _("Could not re-position archive file")));
+
+ return;
+ }
+}
+
+/*----------------------------------------------------------------.
+| Write out the record which has been filled. If MOVE_BACK_FLAG, |
+| backspace to where we started. |
+`----------------------------------------------------------------*/
+
+static void
+write_record (int move_back_flag)
+{
+ save_record = record_start;
+ record_start = new_record;
+
+ if (archive == STDIN)
+ {
+ archive = STDOUT;
+ flush_write ();
+ archive = STDIN;
+ }
+ else
+ {
+ move_archive (-(records_read + 1));
+ flush_write ();
+ }
+
+ record_start = save_record;
+
+ if (move_back_flag)
+ {
+ /* Move the tape head back to where we were. */
+
+ if (archive != STDIN)
+ move_archive (records_read);
+
+ records_read--;
+ }
+
+ blocks_needed = blocking_factor;
+ new_blocks = 0;
+}
+
+/*---.
+| ? |
+`---*/
+
+void
+delete_archive_members (void)
+{
+ enum read_header logical_status = HEADER_STILL_UNREAD;
+ enum read_header previous_status = HEADER_STILL_UNREAD;
+
+ /* FIXME: Should clean the routine before cleaning these variables :-( */
+ struct name *name;
+ int blocks_to_skip = 0;
+ int blocks_to_keep = 0;
+ int kept_blocks_in_record;
+
+ name_gather ();
+ open_archive (ACCESS_UPDATE);
+
+ while (logical_status == HEADER_STILL_UNREAD)
+ {
+ enum read_header status = read_header ();
+
+ switch (status)
+ {
+ case HEADER_STILL_UNREAD:
+ abort ();
+
+ case HEADER_SUCCESS:
+ if (name = name_scan (current_file_name), !name)
+ {
+ set_next_block_after (current_header);
+ if (current_header->oldgnu_header.isextended)
+ skip_extended_headers ();
+ skip_file ((long) (current_stat.st_size));
+ break;
+ }
+ name->found = 1;
+ logical_status = HEADER_SUCCESS;
+ break;
+
+ case HEADER_ZERO_BLOCK:
+ case HEADER_END_OF_FILE:
+ logical_status = HEADER_END_OF_FILE;
+ break;
+
+ case HEADER_FAILURE:
+ set_next_block_after (current_header);
+ switch (previous_status)
+ {
+ case HEADER_STILL_UNREAD:
+ WARN ((0, 0, _("This does not look like a tar archive")));
+ /* Fall through. */
+
+ case HEADER_SUCCESS:
+ case HEADER_ZERO_BLOCK:
+ ERROR ((0, 0, _("Skipping to next header")));
+ /* Fall through. */
+
+ case HEADER_FAILURE:
+ break;
+
+ case HEADER_END_OF_FILE:
+ abort ();
+ }
+ break;
+ }
+
+ previous_status = status;
+ }
+
+ if (logical_status != HEADER_SUCCESS)
+ {
+ write_eot ();
+ close_archive ();
+ names_notfound ();
+ return;
+ }
+
+ write_archive_to_stdout = 0;
+ new_record = (union block *) xmalloc ((size_t) record_size);
+
+ /* Save away blocks before this one in this record. */
+
+ new_blocks = current_block - record_start;
+ blocks_needed = blocking_factor - new_blocks;
+ if (new_blocks)
+ memcpy ((void *) new_record, (void *) record_start,
+ (size_t) (new_blocks * BLOCKSIZE));
+
+#if 0
+ /* FIXME: Old code, before the goto was inserted. To be redesigned. */
+ set_next_block_after (current_header);
+ if (current_header->oldgnu_header.isextended)
+ skip_extended_headers ();
+ skip_file ((long) (current_stat.st_size));
+#endif
+ logical_status = HEADER_STILL_UNREAD;
+ goto flush_file;
+
+ /* FIXME: Solaris 2.4 Sun cc (the ANSI one, not the old K&R) says:
+ "delete.c", line 223: warning: loop not entered at top
+ Reported by Bruno Haible. */
+ while (1)
+ {
+ enum read_header status;
+
+ /* Fill in a record. */
+
+ if (current_block == record_end)
+ {
+ flush_archive ();
+ records_read++;
+ }
+ status = read_header ();
+
+ if (status == HEADER_ZERO_BLOCK && ignore_zeros_option)
+ {
+ set_next_block_after (current_header);
+ continue;
+ }
+ if (status == HEADER_END_OF_FILE || status == HEADER_ZERO_BLOCK)
+ {
+ logical_status = HEADER_END_OF_FILE;
+ memset (new_record[new_blocks].buffer, 0,
+ (size_t) (BLOCKSIZE * blocks_needed));
+ new_blocks += blocks_needed;
+ blocks_needed = 0;
+ write_record (0);
+ break;
+ }
+
+ if (status == HEADER_FAILURE)
+ {
+ ERROR ((0, 0, _("Deleting non-header from archive")));
+ set_next_block_after (current_header);
+ continue;
+ }
+
+ /* Found another header. */
+
+ if (name = name_scan (current_file_name), name)
+ {
+ name->found = 1;
+ flush_file:
+ set_next_block_after (current_header);
+ blocks_to_skip = (current_stat.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
+
+ while (record_end - current_block <= blocks_to_skip)
+ {
+ blocks_to_skip -= (record_end - current_block);
+ flush_archive ();
+ records_read++;
+ }
+ current_block += blocks_to_skip;
+ blocks_to_skip = 0;
+ continue;
+ }
+
+ /* Copy header. */
+
+ new_record[new_blocks] = *current_header;
+ new_blocks++;
+ blocks_needed--;
+ blocks_to_keep
+ = (current_stat.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
+ set_next_block_after (current_header);
+ if (blocks_needed == 0)
+ write_record (1);
+
+ /* Copy data. */
+
+ kept_blocks_in_record = record_end - current_block;
+ if (kept_blocks_in_record > blocks_to_keep)
+ kept_blocks_in_record = blocks_to_keep;
+
+ while (blocks_to_keep)
+ {
+ int count;
+
+ if (current_block == record_end)
+ {
+ flush_read ();
+ records_read++;
+ current_block = record_start;
+ kept_blocks_in_record = blocking_factor;
+ if (kept_blocks_in_record > blocks_to_keep)
+ kept_blocks_in_record = blocks_to_keep;
+ }
+ count = kept_blocks_in_record;
+ if (count > blocks_needed)
+ count = blocks_needed;
+
+ memcpy ((void *) (new_record + new_blocks),
+ (void *) current_block,
+ (size_t) (count * BLOCKSIZE));
+ new_blocks += count;
+ blocks_needed -= count;
+ current_block += count;
+ blocks_to_keep -= count;
+ kept_blocks_in_record -= count;
+
+ if (blocks_needed == 0)
+ write_record (1);
+ }
+ }
+
+ write_eot ();
+ close_archive ();
+ names_notfound ();
+}