Browse Source

GNU tar 1.12

Paul Eggert 28 years ago
parent
commit
3e3d9b7e39
1 changed files with 336 additions and 0 deletions
  1. 336 0
      src/delete.c

+ 336 - 0
src/delete.c

@@ -0,0 +1,336 @@
+/* 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 ();
+}