]> Dogcows Code - chaz/tar/blobdiff - src/delete.c
Fix bugs in handling the --remove-files option.
[chaz/tar] / src / delete.c
index 4cb73b3c95453fb0893921a8c60b928212910823..a67993cb9be91e92b2d5677622ddde372099cf41 100644 (file)
@@ -1,9 +1,11 @@
 /* Delete entries from a tar archive.
 /* Delete entries from a tar archive.
-   Copyright (C) 1988, 1992, 1994, 1996, 1997 Free Software Foundation, Inc.
+
+   Copyright (C) 1988, 1992, 1994, 1996, 1997, 2000, 2001, 2003, 2004,
+   2005, 2006 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
 
    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
+   Free Software Foundation; either version 3, or (at your option) any later
    version.
 
    This program is distributed in the hope that it will be useful, but
    version.
 
    This program is distributed in the hope that it will be useful, but
 
    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.,
 
    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"
+   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
 
 
-#define STDIN 0
-#define STDOUT 1
+#include <system.h>
+#include <system-ioctl.h>
 
 #include "common.h"
 
 #include "common.h"
-#include "rmt.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;
+static union block *new_record;
+static int new_blocks;
+static bool acting_as_filter;
 
 
-/* FIXME: This module should not directly handle the following three
-   variables, instead, this should be done in buffer.c only.  */
+/* FIXME: This module should not directly handle the following
+   variables, instead, the interface should be cleaned up.  */
 extern union block *record_start;
 extern union block *record_end;
 extern union block *current_block;
 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!                                                               |
-`-------------------------------------------------------------------------*/
-
+extern union block *recent_long_name;
+extern union block *recent_long_link;
+extern off_t records_read;
+
+/* The number of records skipped at the start of the archive, when
+   passing over members that are not deleted.  */
+off_t records_skipped;
+
+/* Move archive descriptor by COUNT records worth.  If COUNT is
+   positive we move forward, else we move negative.  If it's a tape,
+   MTIOCTOP had better work.  If it's something else, we try to seek
+   on it.  If we can't seek, we lose!  */
 static void
 static void
-move_archive (int count)
+move_archive (off_t count)
 {
 {
+  if (count == 0)
+    return;
+
 #ifdef MTIOCTOP
   {
     struct mtop operation;
 #ifdef MTIOCTOP
   {
     struct mtop operation;
-    int status;
 
 
-    if (count > 0)
-      {
-       operation.mt_op = MTFSR;
-       operation.mt_count = count;
-      }
-    else
+    if (count < 0
+       ? (operation.mt_op = MTBSR,
+          operation.mt_count = -count,
+          operation.mt_count == -count)
+       : (operation.mt_op = MTFSR,
+          operation.mt_count = count,
+          operation.mt_count == count))
       {
       {
-       operation.mt_op = MTBSR;
-       operation.mt_count = -count;
-      }
-
-    if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
-       status >= 0)
-      return;
+       if (0 <= rmtioctl (archive, MTIOCTOP, (char *) &operation))
+         return;
 
 
-    if (errno == EIO)
-      if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
-         status >= 0)
-      return;
+       if (errno == EIO
+           && 0 <= rmtioctl (archive, MTIOCTOP, (char *) &operation))
+         return;
+      }
   }
 #endif /* MTIOCTOP */
 
   {
   }
 #endif /* MTIOCTOP */
 
   {
-    off_t position = rmtlseek (archive, 0L, 1);
-
-    position += record_size * count;
+    off_t position0 = rmtlseek (archive, (off_t) 0, SEEK_CUR);
+    off_t increment = record_size * (off_t) count;
+    off_t position = position0 + increment;
 
 
-    if (rmtlseek (archive, position, 0) != position)
-      FATAL_ERROR ((0, 0, _("Could not re-position archive file")));
+    if (increment / count != record_size
+       || (position < position0) != (increment < 0)
+       || (position = position < 0 ? 0 : position,
+           rmtlseek (archive, position, SEEK_SET) != position))
+      seek_error_details (archive_name_array[0], position);
 
     return;
   }
 }
 
 
     return;
   }
 }
 
-/*----------------------------------------------------------------.
-| Write out the record which has been filled.  If MOVE_BACK_FLAG, |
-| backspace to where we started.                                  |
-`----------------------------------------------------------------*/
-
+/* 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)
 {
 static void
 write_record (int move_back_flag)
 {
-  save_record = record_start;
+  union block *save_record = record_start;
   record_start = new_record;
 
   record_start = new_record;
 
-  if (archive == STDIN)
+  if (acting_as_filter)
     {
     {
-      archive = STDOUT;
+      archive = STDOUT_FILENO;
       flush_write ();
       flush_write ();
-      archive = STDIN;
+      archive = STDIN_FILENO;
     }
   else
     {
     }
   else
     {
-      move_archive (-(records_read + 1));
+      move_archive ((records_written + records_skipped) - records_read);
       flush_write ();
     }
 
       flush_write ();
     }
 
@@ -113,19 +113,39 @@ write_record (int move_back_flag)
     {
       /* Move the tape head back to where we were.  */
 
     {
       /* Move the tape head back to where we were.  */
 
-      if (archive != STDIN)
-       move_archive (records_read);
-
-      records_read--;
+      if (! acting_as_filter)
+       move_archive (records_read - (records_written + records_skipped));
     }
 
     }
 
-  blocks_needed = blocking_factor;
   new_blocks = 0;
 }
 
   new_blocks = 0;
 }
 
-/*---.
-| ?  |
-`---*/
+static void
+write_recent_blocks (union block *h, size_t blocks)
+{
+  size_t i;
+  for (i = 0; i < blocks; i++)
+    {
+      new_record[new_blocks++] = h[i];
+      if (new_blocks == blocking_factor)
+       write_record (1);
+    }
+}
+
+static void
+write_recent_bytes (char *data, size_t bytes)
+{
+  size_t blocks = bytes / BLOCKSIZE;
+  size_t rest = bytes - blocks * BLOCKSIZE;
+
+  write_recent_blocks ((union block *)data, blocks);
+  memcpy (new_record[new_blocks].buffer, data + blocks * BLOCKSIZE, rest);
+  if (rest < BLOCKSIZE)
+    memset (new_record[new_blocks].buffer + rest, 0, BLOCKSIZE - rest);
+  new_blocks++;
+  if (new_blocks == blocking_factor)
+    write_record (1);
+}
 
 void
 delete_archive_members (void)
 
 void
 delete_archive_members (void)
@@ -135,16 +155,17 @@ delete_archive_members (void)
 
   /* FIXME: Should clean the routine before cleaning these variables :-( */
   struct name *name;
 
   /* FIXME: Should clean the routine before cleaning these variables :-( */
   struct name *name;
-  int blocks_to_skip = 0;
-  int blocks_to_keep = 0;
+  off_t blocks_to_skip = 0;
+  off_t blocks_to_keep = 0;
   int kept_blocks_in_record;
 
   name_gather ();
   open_archive (ACCESS_UPDATE);
   int kept_blocks_in_record;
 
   name_gather ();
   open_archive (ACCESS_UPDATE);
+  acting_as_filter = strcmp (archive_name_array[0], "-") == 0;
 
 
-  while (logical_status == HEADER_STILL_UNREAD)
+  do
     {
     {
-      enum read_header status = read_header ();
+      enum read_header status = read_header (true);
 
       switch (status)
        {
 
       switch (status)
        {
@@ -152,19 +173,30 @@ delete_archive_members (void)
          abort ();
 
        case HEADER_SUCCESS:
          abort ();
 
        case HEADER_SUCCESS:
-         if (name = name_scan (current_file_name), !name)
+         if ((name = name_scan (current_stat_info.file_name)) == NULL)
            {
            {
-             set_next_block_after (current_header);
-             if (current_header->oldgnu_header.isextended)
-               skip_extended_headers ();
-             skip_file ((long) (current_stat.st_size));
+             skip_member ();
              break;
            }
              break;
            }
-         name->found = 1;
-         logical_status = HEADER_SUCCESS;
+         name->found_count++;
+         if (!ISFOUND(name))
+           {
+             skip_member ();
+             break;
+           }
+
+         /* Fall through.  */
+       case HEADER_SUCCESS_EXTENDED:
+         logical_status = status;
          break;
 
        case HEADER_ZERO_BLOCK:
          break;
 
        case HEADER_ZERO_BLOCK:
+         if (ignore_zeros_option)
+           {
+             set_next_block_after (current_header);
+             break;
+           }
+         /* Fall through.  */
        case HEADER_END_OF_FILE:
          logical_status = HEADER_END_OF_FILE;
          break;
        case HEADER_END_OF_FILE:
          logical_status = HEADER_END_OF_FILE;
          break;
@@ -178,6 +210,7 @@ delete_archive_members (void)
              /* Fall through.  */
 
            case HEADER_SUCCESS:
              /* Fall through.  */
 
            case HEADER_SUCCESS:
+           case HEADER_SUCCESS_EXTENDED:
            case HEADER_ZERO_BLOCK:
              ERROR ((0, 0, _("Skipping to next header")));
              /* Fall through.  */
            case HEADER_ZERO_BLOCK:
              ERROR ((0, 0, _("Skipping to next header")));
              /* Fall through.  */
@@ -193,144 +226,165 @@ delete_archive_members (void)
 
       previous_status = status;
     }
 
       previous_status = status;
     }
+  while (logical_status == HEADER_STILL_UNREAD);
 
 
-  if (logical_status != HEADER_SUCCESS)
-    {
-      write_eot ();
-      close_archive ();
-      names_notfound ();
-      return;
-    }
+  records_skipped = records_read - 1;
+  new_record = xmalloc (record_size);
 
 
-  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)
+  if (logical_status == HEADER_SUCCESS
+      || logical_status == HEADER_SUCCESS_EXTENDED)
     {
     {
-      enum read_header status;
+      write_archive_to_stdout = false;
 
 
-      /* Fill in a record.  */
+      /* Save away blocks before this one in this record.  */
 
 
-      if (current_block == record_end)
-       {
-         flush_archive ();
-         records_read++;
-       }
-      status = read_header ();
+      new_blocks = current_block - record_start;
+      if (new_blocks)
+       memcpy (new_record, record_start, new_blocks * BLOCKSIZE);
 
 
-      if (status == HEADER_ZERO_BLOCK && ignore_zeros_option)
+      if (logical_status == HEADER_SUCCESS)
        {
        {
-         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;
+         /* FIXME: Pheew!  This is crufty code!  */
+         logical_status = HEADER_STILL_UNREAD;
+         goto flush_file;
        }
 
        }
 
-      if (status == HEADER_FAILURE)
+      /* 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)
        {
        {
-         ERROR ((0, 0, _("Deleting non-header from archive")));
-         set_next_block_after (current_header);
-         continue;
-       }
+         enum read_header status;
 
 
-      /* Found another header.  */
+         /* Fill in a record.  */
 
 
-      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;
+         if (current_block == record_end)
+           flush_archive ();
+         status = read_header (false);
 
 
-         while (record_end - current_block <= blocks_to_skip)
+         xheader_decode (&current_stat_info);
+
+         if (status == HEADER_ZERO_BLOCK && ignore_zeros_option)
            {
            {
-             blocks_to_skip -= (record_end - current_block);
-             flush_archive ();
-             records_read++;
+             set_next_block_after (current_header);
+             continue;
+           }
+         if (status == HEADER_END_OF_FILE || status == HEADER_ZERO_BLOCK)
+           {
+             logical_status = HEADER_END_OF_FILE;
+             break;
            }
            }
-         current_block += blocks_to_skip;
-         blocks_to_skip = 0;
-         continue;
-       }
 
 
-      /* Copy header.  */
+         if (status == HEADER_FAILURE)
+           {
+             ERROR ((0, 0, _("Deleting non-header from archive")));
+             set_next_block_after (current_header);
+             continue;
+           }
 
 
-      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);
+         /* Found another header.  */
+
+         if ((name = name_scan (current_stat_info.file_name)) != NULL)
+           {
+             name->found_count++;
+             if (ISFOUND(name))
+               {
+               flush_file:
+                 set_next_block_after (current_header);
+                 blocks_to_skip = (current_stat_info.stat.st_size
+                                   + BLOCKSIZE - 1) / BLOCKSIZE;
+                 
+                 while (record_end - current_block <= blocks_to_skip)
+                   {
+                     blocks_to_skip -= (record_end - current_block);
+                     flush_archive ();
+                   }
+                 current_block += blocks_to_skip;
+                 blocks_to_skip = 0;
+                 continue;
+               }
+           }
+         /* Copy header.  */
+
+         if (current_stat_info.xhdr.size)
+           {
+             write_recent_bytes (current_stat_info.xhdr.buffer,
+                                 current_stat_info.xhdr.size);
+           }
+         else
+           {
+             write_recent_blocks (recent_long_name, recent_long_name_blocks);
+             write_recent_blocks (recent_long_link, recent_long_link_blocks);
+           }
+         new_record[new_blocks] = *current_header;
+         new_blocks++;
+         blocks_to_keep
+           = (current_stat_info.stat.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
+         set_next_block_after (current_header);
+         if (new_blocks == blocking_factor)
+           write_record (1);
 
 
-      /* Copy data.  */
+         /* 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;
+         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)
+         while (blocks_to_keep)
+           {
+             int count;
+
+             if (current_block == record_end)
+               {
+                 flush_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 (blocking_factor - new_blocks < count)
+               count = blocking_factor - new_blocks;
+
+             if (! count)
+               abort ();
+
+             memcpy (new_record + new_blocks, current_block, count * BLOCKSIZE);
+             new_blocks += count;
+             current_block += count;
+             blocks_to_keep -= count;
+             kept_blocks_in_record -= count;
+
+             if (new_blocks == blocking_factor)
+               write_record (1);
+           }
+       }
+
+      if (logical_status == HEADER_END_OF_FILE)
        {
        {
-         int count;
+         /* Write the end of tape.  FIXME: we can't use write_eot here,
+            as it gets confused when the input is at end of file.  */
 
 
-         if (current_block == record_end)
+         int total_zero_blocks = 0;
+
+         do
            {
            {
-             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;
+             int zero_blocks = blocking_factor - new_blocks;
+             memset (new_record + new_blocks, 0, BLOCKSIZE * zero_blocks);
+             total_zero_blocks += zero_blocks;
+             write_record (total_zero_blocks < 2);
            }
            }
-         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);
+         while (total_zero_blocks < 2);
+       }
+
+      if (! acting_as_filter && ! _isrmt (archive))
+       {
+         if (sys_truncate (archive))
+           truncate_warn (archive_name_array[0]);
        }
     }
        }
     }
+  free (new_record);
 
 
-  write_eot ();
   close_archive ();
   names_notfound ();
 }
   close_archive ();
   names_notfound ();
 }
This page took 0.031647 seconds and 4 git commands to generate.