diff options
author | Andreas Gruenbacher <agruen@linbit.com> | 2012-04-17 11:56:08 +0200 |
---|---|---|
committer | Andreas Gruenbacher <agruen@linbit.com> | 2012-04-17 16:48:19 +0200 |
commit | 3b4824fb338c91f4c3a755084e35fef157163f8c (patch) | |
tree | 9941aed265936f902c1cbf6f7fb872cabf0e66e6 | |
parent | 73184a17e237f0004b41a3bf1492a7f0ef8f1a7a (diff) | |
download | patch-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.conf | 2 | ||||
-rw-r--r-- | m4/.gitignore | 1 | ||||
-rw-r--r-- | src/patch.c | 98 | ||||
-rw-r--r-- | src/pch.c | 2 | ||||
-rw-r--r-- | src/util.c | 33 | ||||
-rw-r--r-- | src/util.h | 7 |
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 { @@ -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; @@ -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) { @@ -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) |