nano

nano with my custom patches
git clone git://bsandro.tech/nano
Log | Files | Refs | README | LICENSE

commit ed76a045ae4308e4faefceb8a53ea4a62b877c5f
parent 62ffc221aa4dda2f128e8be6855338105751dbae
Author: Michalis Kokologiannakis <mixaskok@gmail.com>
Date:   Mon, 13 Jul 2020 11:31:33 +0300

tweaks: move the backup code to a separate function

This change moves the code responsible for backup creation to a new,
separate function, backup_file().  This function returns a boolean
indicating whether saving the buffer should proceed or not.

Signed-off-by: Michalis Kokologiannakis <michalis@mpi-sws.org>

Diffstat:
Msrc/files.c | 166++++++++++++++++++++++++++++++++++++++++++-------------------------------------
1 file changed, 89 insertions(+), 77 deletions(-)

diff --git a/src/files.c b/src/files.c @@ -1588,62 +1588,15 @@ int sync_file(FILE *thefile) return 0; } -/* Write the current buffer to disk. If thefile isn't NULL, we write to a - * temporary file that is already open. If tmp is TRUE (when spell checking - * or emergency dumping, for example), we set the umask to disallow anyone else - * from accessing the file, and don't print out how many lines we wrote on the - * status bar. If method is APPEND or PREPEND, it means we will be appending - * or prepending instead of overwriting the given file. If fullbuffer is TRUE - * and when writing normally, we set the current filename and stat info. - * Return TRUE on success, and FALSE otherwise. */ -bool write_file(const char *name, FILE *thefile, bool tmp, - kind_of_writing_type method, bool fullbuffer) -{ #ifndef NANO_TINY - bool is_existing_file; - /* Becomes TRUE when the file is non-temporary and exists. */ - struct stat st; - /* The status fields filled in by statting the file. */ - char *backupname = NULL; - /* The name of the backup file, in case we make one. */ +/* Create a backup of an existing file. If the user did not request backups, + * make a temporary one (trying first in the directory of the original file, + * then in the user's home directory). Return TRUE if the save can proceed. */ +bool backup_file(char *realname, char **backupname) +{ bool second_attempt = FALSE; /* Whether a normal backup failed and we are resorting to a failsafe. */ -#endif - char *realname = real_dir_from_tilde(name); - /* The filename after tilde expansion. */ - char *tempname = NULL; - /* The name of the temporary file we use when prepending. */ - linestruct *line = openfile->filetop; - /* An iterator for moving through the lines of the buffer. */ - size_t lineswritten = 0; - /* The number of lines written, for feedback on the status bar. */ - bool retval = FALSE; - /* The return value, to become TRUE when writing has succeeded. */ -#ifdef ENABLE_OPERATINGDIR - /* If we're writing a temporary file, we're probably going outside - * the operating directory, so skip the operating directory test. */ - if (!tmp && outside_of_confinement(realname, FALSE)) { - statusline(ALERT, _("Can't write outside of %s"), operating_dir); - goto cleanup_and_exit; - } -#endif -#ifndef NANO_TINY - /* Check whether the file (at the end of the symlink) exists. */ - is_existing_file = (!tmp) && (stat(realname, &st) != -1); - - /* If we haven't statted this file before (say, the user just specified - * it interactively), stat and save the value now, or else we will chase - * null pointers when we do modtime checks and such during backup. */ - if (openfile->statinfo == NULL && is_existing_file) - stat_with_alloc(realname, &openfile->statinfo); - - /* When the user requested a backup, we do this only if the file exists and - * isn't temporary AND the file has not been modified by someone else since - * we opened it (or we are appending/prepending or writing a selection). */ - if (is_existing_file && openfile->statinfo && - (openfile->statinfo->st_mtime == st.st_mtime || - method != OVERWRITE || openfile->mark)) { static struct timespec filetime[2]; int backup_cflags, backup_fd, verdict; FILE *original = NULL, *backup_file = NULL; @@ -1658,14 +1611,14 @@ bool write_file(const char *name, FILE *thefile, bool tmp, * by appending a tilde to the original file name. Otherwise, * we create a numbered backup in the specified directory. */ if (!ISSET(MAKE_BACKUP)) { - backupname = charalloc(strlen(realname) + 8); - sprintf(backupname, "%s~XXXXXX", realname); + *backupname = charalloc(strlen(realname) + 8); + sprintf(*backupname, "%s~XXXXXX", realname); - backup_fd = mkstemp(backupname); + backup_fd = mkstemp(*backupname); goto try_backup; } else if (backup_dir == NULL) { - backupname = charalloc(strlen(realname) + 2); - sprintf(backupname, "%s~", realname); + *backupname = charalloc(strlen(realname) + 2); + sprintf(*backupname, "%s~", realname); } else { char *backuptemp = get_full_path(realname); @@ -1679,24 +1632,24 @@ bool write_file(const char *name, FILE *thefile, bool tmp, } else backuptemp = copy_of(tail(realname)); - backupname = charalloc(strlen(backup_dir) + strlen(backuptemp) + 1); - sprintf(backupname, "%s%s", backup_dir, backuptemp); + *backupname = charalloc(strlen(backup_dir) + strlen(backuptemp) + 1); + sprintf(*backupname, "%s%s", backup_dir, backuptemp); free(backuptemp); - backuptemp = get_next_filename(backupname, "~"); - free(backupname); - backupname = backuptemp; + backuptemp = get_next_filename(*backupname, "~"); + free(*backupname); + *backupname = backuptemp; /* If all numbered backup names are taken, the user must * be fond of backups. Thus, without one, do not go on. */ - if (*backupname == '\0') { + if (**backupname == '\0') { statusline(ALERT, _("Too many existing backup files")); - goto cleanup_and_exit; + return FALSE; } } /* Now first try to delete an existing backup file. */ - if (unlink(backupname) < 0 && errno != ENOENT && !ISSET(INSECURE_BACKUP)) + if (unlink(*backupname) < 0 && errno != ENOENT && !ISSET(INSECURE_BACKUP)) goto backup_error; if (ISSET(INSECURE_BACKUP)) @@ -1705,7 +1658,7 @@ bool write_file(const char *name, FILE *thefile, bool tmp, backup_cflags = O_WRONLY | O_CREAT | O_EXCL; /* Create the backup file (or truncate the existing one). */ - backup_fd = open(backupname, backup_cflags, S_IRUSR|S_IWUSR); + backup_fd = open(*backupname, backup_cflags, S_IRUSR|S_IWUSR); try_backup: if (backup_fd >= 0) @@ -1731,7 +1684,7 @@ bool write_file(const char *name, FILE *thefile, bool tmp, if (original == NULL) { statusline(ALERT, _("Error reading %s: %s"), realname, strerror(errno)); fclose(backup_file); - goto save_the_file; + return TRUE; } /* Copy the existing file to the backup. */ @@ -1740,7 +1693,7 @@ bool write_file(const char *name, FILE *thefile, bool tmp, if (verdict < 0) { fclose(backup_file); statusline(ALERT, _("Error reading %s: %s"), realname, strerror(errno)); - goto cleanup_and_exit; + return FALSE; } else if (verdict > 0) { fclose(backup_file); goto backup_error; @@ -1756,20 +1709,20 @@ bool write_file(const char *name, FILE *thefile, bool tmp, IGNORE_CALL_RESULT(futimens(backup_fd, filetime)); if (fclose(backup_file) == 0) - goto save_the_file; + return TRUE; backup_error: get_homedir(); /* If the first attempt of copying the file failed, try again to HOME. */ if (!second_attempt && homedir) { - unlink(backupname); - free(backupname); + unlink(*backupname); + free(*backupname); - backupname = charalloc(strlen(homedir) + strlen(tail(realname)) + 9); - sprintf(backupname, "%s/%s~XXXXXX", homedir, tail(realname)); + *backupname = charalloc(strlen(homedir) + strlen(tail(realname)) + 9); + sprintf(*backupname, "%s/%s~XXXXXX", homedir, tail(realname)); - backup_fd = mkstemp(backupname); + backup_fd = mkstemp(*backupname); backup_file = NULL; if (ISSET(MAKE_BACKUP)) { @@ -1787,12 +1740,71 @@ bool write_file(const char *name, FILE *thefile, bool tmp, warn_and_briefly_pause(_("Cannot make backup")); if (!user_wants_to_proceed()) { statusline(HUSH, _("Cannot write backup %s: %s"), - backupname, strerror(errno)); - goto cleanup_and_exit; + *backupname, strerror(errno)); + return FALSE; } + return TRUE; +} +#endif /* !NANO_TINY */ + +/* Write the current buffer to disk. If thefile isn't NULL, we write to a + * temporary file that is already open. If tmp is TRUE (when spell checking + * or emergency dumping, for example), we set the umask to disallow anyone else + * from accessing the file, and don't print out how many lines we wrote on the + * status bar. If method is APPEND or PREPEND, it means we will be appending + * or prepending instead of overwriting the given file. If fullbuffer is TRUE + * and when writing normally, we set the current filename and stat info. + * Return TRUE on success, and FALSE otherwise. */ +bool write_file(const char *name, FILE *thefile, bool tmp, + kind_of_writing_type method, bool fullbuffer) +{ +#ifndef NANO_TINY + bool is_existing_file; + /* Becomes TRUE when the file is non-temporary and exists. */ + struct stat st; + /* The status fields filled in by statting the file. */ + char *backupname = NULL; + /* The name of the backup file, in case we make one. */ +#endif + char *realname = real_dir_from_tilde(name); + /* The filename after tilde expansion. */ + char *tempname = NULL; + /* The name of the temporary file we use when prepending. */ + linestruct *line = openfile->filetop; + /* An iterator for moving through the lines of the buffer. */ + size_t lineswritten = 0; + /* The number of lines written, for feedback on the status bar. */ + bool retval = FALSE; + /* The return value, to become TRUE when writing has succeeded. */ + +#ifdef ENABLE_OPERATINGDIR + /* If we're writing a temporary file, we're probably going outside + * the operating directory, so skip the operating directory test. */ + if (!tmp && outside_of_confinement(realname, FALSE)) { + statusline(ALERT, _("Can't write outside of %s"), operating_dir); + goto cleanup_and_exit; + } +#endif +#ifndef NANO_TINY + /* Check whether the file (at the end of the symlink) exists. */ + is_existing_file = (!tmp) && (stat(realname, &st) != -1); + + /* If we haven't statted this file before (say, the user just specified + * it interactively), stat and save the value now, or else we will chase + * null pointers when we do modtime checks and such during backup. */ + if (openfile->statinfo == NULL && is_existing_file) + stat_with_alloc(realname, &openfile->statinfo); + + /* When the user requested a backup, we do this only if the file exists and + * isn't temporary AND the file has not been modified by someone else since + * we opened it (or we are appending/prepending or writing a selection). */ + if (is_existing_file && openfile->statinfo && + (openfile->statinfo->st_mtime == st.st_mtime || + method != OVERWRITE || openfile->mark)) { + if (!backup_file(realname, &backupname)) + goto cleanup_and_exit; } - save_the_file: /* When prepending, first copy the existing file to a temporary file. */ if (method == PREPEND) { FILE *source = fopen(realname, "rb");