summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Gruenbacher <agruen@linbit.com>2012-04-17 11:56:08 +0200
committerAndreas Gruenbacher <agruen@linbit.com>2012-04-17 16:48:19 +0200
commit3b4824fb338c91f4c3a755084e35fef157163f8c (patch)
tree9941aed265936f902c1cbf6f7fb872cabf0e66e6
parent73184a17e237f0004b41a3bf1492a7f0ef8f1a7a (diff)
downloadpatch-3b4824fb338c91f4c3a755084e35fef157163f8c.tar.gz
Do not delete files immediately
Fixes the bug that more than one numbered backup would be created when a patch file deletes and recreates a file. * bootstrap.conf (gnulib_modules): Add linked-list and xlist modules. * src/util.h (file_id_type): Add DELETE_LATER and OVERWRITTEN types. (create_backup, set_file_attributes): Update prototype. (insert_file_id): Add prototype. * src/util.c (insert_file_id): Export. (set_file_attributes, create_backup_copy): Make the st argument const. (create_backup): Pass in to_st instead of returning it from create_backup(). This obsoletes the to_errno argument. (move_file): Determine to_st here and pass it to create_backup(). Remember when a file is overwritten. * src/patch.c (output_file): Add to_st parameter. Remember files to delete instead of deleting them immediately. Pass from-st to create_backup(). (file_to_delete): New struct. (init_files_to_delete, delete_file_later, delete_files): New functions. (main): Use init_files_to_delete() and delete_files(). Pass to_st to output_file() where we already have it. * src/pch.c (intuit_diff_type): Assume that files which are marked for deletion don't exist.
-rw-r--r--bootstrap.conf2
-rw-r--r--m4/.gitignore1
-rw-r--r--src/patch.c98
-rw-r--r--src/pch.c2
-rw-r--r--src/util.c33
-rw-r--r--src/util.h7
6 files changed, 105 insertions, 38 deletions
diff --git a/bootstrap.conf b/bootstrap.conf
index 7024323..a549668 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -37,6 +37,7 @@ hash
ignore-value
largefile
lchmod
+linked-list
lstat
maintainer-makefile
malloc
@@ -66,6 +67,7 @@ update-copyright
utimens
verror
xalloc
+xlist
"
gnulib_tool_option_extras='--symlink --makefile-name=gnulib.mk'
diff --git a/m4/.gitignore b/m4/.gitignore
index 45e6333..b112934 100644
--- a/m4/.gitignore
+++ b/m4/.gitignore
@@ -244,3 +244,4 @@ xsize.m4
xstrndup.m4
xvasprintf.m4
/close.m4
+/gl_list.m4
diff --git a/src/patch.c b/src/patch.c
index 3bd7472..09ae41c 100644
--- a/src/patch.c
+++ b/src/patch.c
@@ -31,6 +31,8 @@
#include <util.h>
#include <version.h>
#include <xalloc.h>
+#include <gl_linked_list.h>
+#include <gl_xlist.h>
/* procedures */
@@ -54,7 +56,10 @@ static void abort_hunk_context (bool, bool);
static void abort_hunk_unified (bool, bool);
static void output_file (char const *, int *, const struct stat *, char const *,
- mode_t, bool);
+ const struct stat *, mode_t, bool);
+
+static void init_files_to_delete (void);
+static void delete_files (void);
#ifdef ENABLE_MERGE
static bool merge;
@@ -153,6 +158,8 @@ main (int argc, char **argv)
backup_type = get_version (version_control_context, version_control);
init_backup_hash_table ();
+ init_files_to_delete ();
+
init_output (&outstate);
if (outfile)
outstate.ofp = open_outfile (outfile);
@@ -442,7 +449,9 @@ main (int argc, char **argv)
|| S_ISLNK (file_type)))
{
if (! dry_run)
- output_file (NULL, NULL, NULL, outname, file_type | 0, backup);
+ output_file (NULL, NULL, NULL, outname,
+ (inname == outname) ? &instat : NULL,
+ file_type | 0, backup);
}
else
{
@@ -502,13 +511,15 @@ main (int argc, char **argv)
}
output_file (TMPOUTNAME, &TMPOUTNAME_needs_removal,
- &outst, outname, mode, backup);
+ &outst, outname, NULL, mode, backup);
if (pch_rename ())
- output_file (NULL, NULL, NULL, inname, mode, backup);
+ output_file (NULL, NULL, NULL, inname, &instat,
+ mode, backup);
}
else
- output_file (outname, NULL, &outst, NULL, file_type | 0, backup);
+ output_file (outname, NULL, &outst, NULL, NULL,
+ file_type | 0, backup);
}
}
}
@@ -574,6 +585,7 @@ main (int argc, char **argv)
}
if (outstate.ofp && (ferror (outstate.ofp) || fclose (outstate.ofp) != 0))
write_fatal ();
+ delete_files ();
cleanup ();
if (somefailed)
exit (1);
@@ -1608,26 +1620,82 @@ similar (char const *a, size_t alen, char const *b, size_t blen)
}
}
+/* Deferred deletion of files. */
+
+struct file_to_delete {
+ char *name;
+ struct stat st;
+ bool backup;
+};
+
+static gl_list_t files_to_delete;
+
+static void
+init_files_to_delete (void)
+{
+ files_to_delete = gl_list_create_empty (GL_LINKED_LIST, NULL, NULL, NULL, true);
+}
+
+static void
+delete_file_later (const char *name, const struct stat *st, bool backup)
+{
+ struct file_to_delete *file_to_delete;
+ struct stat st_tmp;
+
+ if (! st)
+ {
+ if (lstat (name, &st_tmp) != 0)
+ pfatal ("Can't get file attributes of %s %s", "file", name);
+ st = &st_tmp;
+ }
+ file_to_delete = xmalloc (sizeof *file_to_delete);
+ file_to_delete->name = xstrdup (name);
+ file_to_delete->st = *st;
+ file_to_delete->backup = backup;
+ gl_list_add_last (files_to_delete, file_to_delete);
+ insert_file_id (st, DELETE_LATER);
+}
+
+static void
+delete_files (void)
+{
+ gl_list_iterator_t iter;
+ const void *elt;
+
+ iter = gl_list_iterator (files_to_delete);
+ while (gl_list_iterator_next (&iter, &elt, NULL))
+ {
+ const struct file_to_delete *file_to_delete = elt;
+
+ if (lookup_file_id (&file_to_delete->st) == DELETE_LATER)
+ {
+ mode_t mode = file_to_delete->st.st_mode;
+
+ if (verbosity == VERBOSE)
+ say ("Removing %s %s\n",
+ S_ISLNK (mode) ? "symbolic link" : "file",
+ quotearg (file_to_delete->name));
+ move_file (0, 0, 0, file_to_delete->name, mode,
+ file_to_delete->backup);
+ removedirs (file_to_delete->name);
+ }
+ }
+ gl_list_iterator_free (&iter);
+}
+
/* Putting output files into place and removing them. */
static void
output_file (char const *from, int *from_needs_removal,
const struct stat *from_st, char const *to,
- mode_t mode, bool backup)
+ const struct stat *to_st, mode_t mode, bool backup)
{
if (from == NULL)
- {
- if (verbosity == VERBOSE)
- say ("Removing %s %s\n",
- S_ISLNK (mode) ? "symbolic link" : "file",
- quotearg (to));
- move_file (0, 0, 0, to, mode, backup);
- removedirs (to);
- }
+ delete_file_later (to, to_st, backup);
else if (to == NULL)
{
if (backup)
- create_backup (from, 0, 0, true, false);
+ create_backup (from, from_st, true, false);
}
else
{
diff --git a/src/pch.c b/src/pch.c
index a1e2839..ab4ebc6 100644
--- a/src/pch.c
+++ b/src/pch.c
@@ -869,6 +869,8 @@ intuit_diff_type (bool need_header, mode_t *p_file_type)
}
else if (lstat (p_name[i], &st[i]) != 0)
stat_errno[i] = errno;
+ else if (lookup_file_id (&st[i]) == DELETE_LATER)
+ stat_errno[i] = ENOENT;
else
{
stat_errno[i] = 0;
diff --git a/src/util.c b/src/util.c
index acd8d1b..bb1e7b1 100644
--- a/src/util.c
+++ b/src/util.c
@@ -96,7 +96,7 @@ init_backup_hash_table (void)
/* Insert a file with status ST and type TYPE into the hash table.
The type of an existing entry can be changed by re-inserting it. */
-static void
+void
insert_file_id (struct stat const *st, enum file_id_type type)
{
file_id *p;
@@ -193,7 +193,7 @@ copy_attr (char const *src_path, char const *dst_path)
void
set_file_attributes (char const *to, enum file_attributes attr,
- char const *from, struct stat *st, mode_t mode,
+ char const *from, const struct stat *st, mode_t mode,
struct timespec *new_time)
{
if (attr & FA_TIMES)
@@ -260,7 +260,7 @@ set_file_attributes (char const *to, enum file_attributes attr,
}
static void
-create_backup_copy (char const *from, char const *to, struct stat *st,
+create_backup_copy (char const *from, char const *to, const struct stat *st,
bool to_dir_known_to_exist, bool remember_backup)
{
struct stat backup_st;
@@ -272,12 +272,9 @@ create_backup_copy (char const *from, char const *to, struct stat *st,
}
void
-create_backup (char const *to, struct stat *to_st, int *to_errno,
- bool leave_original, bool remember_backup)
+create_backup (char const *to, const struct stat *to_st, bool leave_original,
+ bool remember_backup)
{
- struct stat tmp_st;
- int tmp_errno;
-
/* When the input to patch modifies the same file more than once, patch only
backs up the initial version of each file.
@@ -294,18 +291,11 @@ create_backup (char const *to, struct stat *to_st, int *to_errno,
deletes and later recreates a file with numbered backups, two numbered
backups will be created. */
- if (! to_st || ! to_errno)
- {
- to_st = &tmp_st;
- to_errno = &tmp_errno;
- }
- *to_errno = lstat (to, to_st) == 0 ? 0 : errno;
-
- if (! to_errno && ! (S_ISREG (to_st->st_mode) || S_ISLNK (to_st->st_mode)))
+ if (to_st && ! (S_ISREG (to_st->st_mode) || S_ISLNK (to_st->st_mode)))
fatal ("File %s is not a %s -- refusing to create backup",
to, S_ISLNK (to_st->st_mode) ? "symbolic link" : "regular file");
- if (! *to_errno && lookup_file_id (to_st) == CREATED)
+ if (to_st && lookup_file_id (to_st) == CREATED)
{
if (debug & 4)
say ("File %s already seen\n", quotearg (to));
@@ -354,7 +344,7 @@ create_backup (char const *to, struct stat *to_st, int *to_errno,
xalloc_die ();
}
- if (*to_errno)
+ if (! to_st)
{
struct stat backup_st;
int fd;
@@ -435,10 +425,13 @@ move_file (char const *from, int *from_needs_removal,
char const *to, mode_t mode, bool backup)
{
struct stat to_st;
- int to_errno = -1;
+ int to_errno;
+ to_errno = lstat (to, &to_st) == 0 ? 0 : errno;
if (backup)
- create_backup (to, &to_st, &to_errno, false, from == NULL);
+ create_backup (to, to_errno ? NULL : &to_st, false, from == NULL);
+ if (! to_errno)
+ insert_file_id (&to_st, OVERWRITTEN);
if (from)
{
diff --git a/src/util.h b/src/util.h
index c61a834..97212ed 100644
--- a/src/util.h
+++ b/src/util.h
@@ -27,7 +27,7 @@
Add one for the sign. */
#define LINENUM_LENGTH_BOUND (sizeof (lin) * CHAR_BIT / 3 + 1)
-enum file_id_type { UNKNOWN, CREATED };
+enum file_id_type { UNKNOWN, CREATED, DELETE_LATER, OVERWRITTEN };
XTERN enum backup_type backup_type;
@@ -57,13 +57,14 @@ void ignore_signals (void);
void init_backup_hash_table (void);
void init_time (void);
void xalloc_die (void) __attribute__ ((noreturn));
-void create_backup (char const *, struct stat *, int *, bool, bool);
+void create_backup (char const *, const struct stat *, bool, bool);
void move_file (char const *, int *, struct stat const *, char const *, mode_t, bool);
void read_fatal (void) __attribute__ ((noreturn));
void remove_prefix (char *, size_t);
void removedirs (char const *);
void set_signals (bool);
void write_fatal (void) __attribute__ ((noreturn));
+void insert_file_id (struct stat const *, enum file_id_type);
enum file_id_type lookup_file_id (struct stat const *);
enum file_attributes {
@@ -74,7 +75,7 @@ enum file_attributes {
};
void set_file_attributes (char const *, enum file_attributes, char const *,
- struct stat *, mode_t, struct timespec *);
+ const struct stat *, mode_t, struct timespec *);
static inline char const * _GL_ATTRIBUTE_PURE
skip_spaces (char const *str)