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:
M | src/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");