diff options
Diffstat (limited to 'src/util.c')
-rw-r--r-- | src/util.c | 202 |
1 files changed, 111 insertions, 91 deletions
@@ -1,4 +1,4 @@ -/* utility functions for `patch' */ +/* utility functions for 'patch' */ /* Copyright (C) 1986 Larry Wall @@ -57,6 +57,8 @@ typedef struct { dev_t dev; ino_t ino; + enum file_id_type type; + bool queued_output; } file_id; /* Return an index for ENTRY into a hash table of size TABLE_SIZE. */ @@ -92,10 +94,8 @@ init_backup_hash_table (void) xalloc_die (); } -/* Insert a file with status ST into the hash table. */ - -static void -insert_file (struct stat const *st) +static file_id * +__insert_file_id (struct stat const *st, enum file_id_type type) { file_id *p; static file_id *next_slot; @@ -104,23 +104,62 @@ insert_file (struct stat const *st) next_slot = xmalloc (sizeof *next_slot); next_slot->dev = st->st_dev; next_slot->ino = st->st_ino; + next_slot->queued_output = false; p = hash_insert (file_id_table, next_slot); if (!p) xalloc_die (); if (p == next_slot) next_slot = NULL; + p->type = type; + return p; } -/* Has the file identified by ST already been inserted into the hash - table? */ - -bool -file_already_seen (struct stat const *st) +static file_id * +__lookup_file_id (struct stat const *st) { file_id f; + f.dev = st->st_dev; f.ino = st->st_ino; - return hash_lookup (file_id_table, &f) != 0; + return hash_lookup (file_id_table, &f); +} + +/* 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. */ + +void +insert_file_id (struct stat const *st, enum file_id_type type) +{ + __insert_file_id (st, type); +} + +/* Has the file identified by ST already been inserted into the hash + table, and what type does it have? */ + +enum file_id_type +lookup_file_id (struct stat const *st) +{ + file_id *p = __lookup_file_id (st); + + return p ? p->type : UNKNOWN; +} + +void +set_queued_output (struct stat const *st, bool queued_output) +{ + file_id *p = __lookup_file_id (st); + + if (! p) + p = __insert_file_id (st, UNKNOWN); + p->queued_output = queued_output; +} + +bool +has_queued_output (struct stat const *st) +{ + file_id *p = __lookup_file_id (st); + + return p && p->queued_output; } static bool _GL_ATTRIBUTE_PURE @@ -140,10 +179,13 @@ copy_attr_error (struct error_context *ctx, char const *fmt, ...) int err = errno; va_list ap; - /* use verror module to print error message */ - va_start (ap, fmt); - verror (0, err, fmt, ap); - va_end (ap); + if (err != ENOSYS && err != ENOTSUP && err != EPERM) + { + /* use verror module to print error message */ + va_start (ap, fmt); + verror (0, err, fmt, ap); + va_end (ap); + } } static char const * @@ -188,7 +230,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) @@ -235,9 +277,9 @@ set_file_attributes (char const *to, enum file_attributes attr, quotearg (to)); } if (attr & FA_XATTRS) - if (copy_attr (from, to)) + if (copy_attr (from, to) != 0 + && errno != ENOSYS && errno != ENOTSUP && errno != EPERM) fatal_exit (0); - /* FIXME: There may be other attributes to preserve. */ if (attr & FA_MODE) { #if 0 && defined HAVE_LCHMOD @@ -255,24 +297,16 @@ set_file_attributes (char const *to, enum file_attributes attr, } static void -create_backup_copy (char const *from, char const *to, struct stat *st, - bool to_dir_known_to_exist, bool remember_backup) +create_backup_copy (char const *from, char const *to, const struct stat *st, + bool to_dir_known_to_exist) { - struct stat backup_st; - copy_file (from, to, remember_backup ? &backup_st : NULL, 0, st->st_mode, - to_dir_known_to_exist); - if (remember_backup) - insert_file (&backup_st); + copy_file (from, to, NULL, 0, st->st_mode, to_dir_known_to_exist); set_file_attributes (to, FA_TIMES | FA_IDS | FA_MODE, from, st, st->st_mode, NULL); } 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) { - 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. @@ -281,26 +315,16 @@ create_backup (char const *to, struct stat *to_st, int *to_errno, up; files already known have already been backed up before, and are skipped. - When a patch deletes a file, this leaves patch without such a "sentinel" - file. In that case, patch remembers the *backup file* instead; when a - patch creates a file, patch checks if the *backup file* is already known. - - This strategy is not fully compatible with numbered backups: when a patch - deletes and later recreates a file with numbered backups, two numbered - backups will be created. */ + When a patch tries to delete a file, in order to not break the above + logic, we merely remember which file to delete. After the entire patch + file has been read, we delete all files marked for deletion which have not + been recreated in the meantime. */ - 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 && file_already_seen (to_st)) + if (to_st && lookup_file_id (to_st) == CREATED) { if (debug & 4) say ("File %s already seen\n", quotearg (to)); @@ -349,40 +373,27 @@ 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; - if (lstat (bakname, &backup_st) == 0 - && file_already_seen (&backup_st)) - { - if (debug & 4) - say ("File %s already seen\n", quotearg (to)); - } - else - { - if (debug & 4) - say ("Creating empty file %s\n", quotearg (bakname)); + if (debug & 4) + say ("Creating empty file %s\n", quotearg (bakname)); - try_makedirs_errno = ENOENT; - unlink (bakname); - while ((fd = creat (bakname, 0666)) < 0) - { - if (errno != try_makedirs_errno) - pfatal ("Can't create file %s", quotearg (bakname)); - makedirs (bakname); - try_makedirs_errno = 0; - } - if (remember_backup && fstat (fd, &backup_st) == 0) - insert_file (&backup_st); - if (close (fd) != 0) - pfatal ("Can't close file %s", quotearg (bakname)); + try_makedirs_errno = ENOENT; + unlink (bakname); + while ((fd = creat (bakname, 0666)) < 0) + { + if (errno != try_makedirs_errno) + pfatal ("Can't create file %s", quotearg (bakname)); + makedirs (bakname); + try_makedirs_errno = 0; } + if (close (fd) != 0) + pfatal ("Can't close file %s", quotearg (bakname)); } else if (leave_original) - create_backup_copy (to, bakname, to_st, try_makedirs_errno == 0, - remember_backup); + create_backup_copy (to, bakname, to_st, try_makedirs_errno == 0); else { if (debug & 4) @@ -398,8 +409,7 @@ create_backup (char const *to, struct stat *to_st, int *to_errno, else if (errno == EXDEV) { create_backup_copy (to, bakname, to_st, - try_makedirs_errno == 0, - remember_backup); + try_makedirs_errno == 0); unlink (to); break; } @@ -407,8 +417,6 @@ create_backup (char const *to, struct stat *to_st, int *to_errno, pfatal ("Can't rename file %s to %s", quotearg_n (0, to), quotearg_n (1, bakname)); } - if (remember_backup) - insert_file (to_st); } free (bakname); } @@ -425,15 +433,18 @@ create_backup (char const *to, struct stat *to_st, int *to_errno, Back up TO if BACKUP is true. */ void -move_file (char const *from, int *from_needs_removal, +move_file (char const *from, bool *from_needs_removal, struct stat const *fromst, char const *to, mode_t mode, bool backup) { struct stat to_st; - int to_errno = -1; + int to_errno; + to_errno = stat_file (to, &to_st); if (backup) - create_backup (to, &to_st, &to_errno, false, from == NULL); + create_backup (to, to_errno ? NULL : &to_st, false); + if (! to_errno) + insert_file_id (&to_st, OVERWRITTEN); if (from) { @@ -469,7 +480,7 @@ move_file (char const *from, int *from_needs_removal, free (buffer); if (lstat (to, &to_st) != 0) pfatal ("Can't get file attributes of %s %s", "symbolic link", to); - insert_file (&to_st); + insert_file_id (&to_st, CREATED); } else { @@ -501,7 +512,7 @@ move_file (char const *from, int *from_needs_removal, pfatal ("Can't remove file %s", quotearg (to)); } copy_file (from, to, &tost, 0, mode, to_dir_known_to_exist); - insert_file (&tost); + insert_file_id (&tost, CREATED); return; } @@ -510,13 +521,14 @@ move_file (char const *from, int *from_needs_removal, } rename_succeeded: - insert_file (fromst); + insert_file_id (fromst, CREATED); /* Do not clear *FROM_NEEDS_REMOVAL if it's possible that the rename returned zero because FROM and TO are hard links to the same file. */ - if (0 < to_errno - || (to_errno == 0 && to_st.st_nlink <= 1)) - *from_needs_removal = 0; + if ((0 < to_errno + || (to_errno == 0 && to_st.st_nlink <= 1)) + && from_needs_removal) + *from_needs_removal = false; } } else if (! backup) @@ -811,7 +823,7 @@ version_get (char const *filename, char const *cs, bool exists, bool readonly, if (dry_run) { if (! exists) - fatal ("can't do dry run on nonexistent version-controlled file %s; invoke `%s' and try again", + fatal ("can't do dry run on nonexistent version-controlled file %s; invoke '%s' and try again", quotearg (filename), getbuf); } else @@ -977,7 +989,7 @@ ask (char const *format, ...) /* If standard output is not a tty, don't bother opening /dev/tty, since it's unlikely that stdout will be seen by the tty user. The isatty test also works around a bug in GNU Emacs 19.34 under Linux - which makes a call-process `patch' hang when it reads from /dev/tty. + which makes a call-process 'patch' hang when it reads from /dev/tty. POSIX.1-2001 XCU line 26599 requires that we read /dev/tty, though. */ ttyfd = (posixly_correct || isatty (STDOUT_FILENO) @@ -1251,7 +1263,7 @@ replace_slashes (char *filename) } /* Make sure we'll have the directories to create a file. - Ignore the last element of `filename'. */ + Ignore the last element of 'filename'. */ static void makedirs (char const *name) @@ -1422,7 +1434,7 @@ strip_leading_slashes (char *name, int strip_leading) /* Make filenames more reasonable. */ void -fetchname (char const *at, int strip_leading, bool maybe_quoted, char **pname, +fetchname (char const *at, int strip_leading, char **pname, char **ptimestr, struct timespec *pstamp) { char *name; @@ -1438,7 +1450,7 @@ fetchname (char const *at, int strip_leading, bool maybe_quoted, char **pname, if (debug & 128) say ("fetchname %s %d\n", at, strip_leading); - if (maybe_quoted && *at == '"') + if (*at == '"') { name = parse_c_string (at, &t); if (! name) @@ -1640,3 +1652,11 @@ make_tempfile (char const **name, char letter, char const *real_name, return fd; } } + +int stat_file (char const *filename, struct stat *st) +{ + int (*xstat)(char const *, struct stat *) = + follow_symlinks ? stat : lstat; + + return xstat (filename, st) == 0 ? 0 : errno; +} |