diff --git a/ChangeLog b/ChangeLog index a8ea47df..2efbbd4c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -40,6 +40,12 @@ CVS code - necessary. Changes to get_next_filename(), write_file(), die(), usage(), nano.1, nanorc.5, nanorc.sample, and nano.texi. (DLR, suggested by James Collings) + - Rework the file-writing routines so that they can work + properly with already-opened files, such as the + mkstemp()-created files used by the spell-checking code. + Changes to safe_tempnam() (renamed safe_tempfile()), + write_file(), write_marked(), die(), do_spell(), and + do_exit(). (DLR) - cut.c: cut_line() - Set placewewant properly after cutting a line, to avoid a diff --git a/src/files.c b/src/files.c index cd99fd16..2c02cfa4 100644 --- a/src/files.c +++ b/src/files.c @@ -1134,15 +1134,18 @@ char *check_writable_directory(const char *path) return full_path; } -/* This function acts like a call to tempnam(NULL, "nano."). The - * difference is that the number of calls is not limited by TMP_MAX. - * Instead we use mkstemp(). */ -char *safe_tempnam(void) +/* This function calls mkstemp(($TMPDIR|P_tmpdir|/tmp/)"nano.XXXXXX"). + * On success, it returns the malloc()ed filename and corresponding FILE + * stream, opened in "w+b" mode. On error, it returns NULL for the + * filename and leaves the FILE stream unchanged. */ +char *safe_tempfile(FILE **f) { char *full_tempdir = NULL; const char *TMPDIR_env; int filedesc; + assert(f != NULL); + /* If $TMPDIR is set and non-empty, set tempdir to it, run it * through get_full_path(), and save the result in full_tempdir. * Otherwise, leave full_tempdir set to NULL. */ @@ -1163,17 +1166,14 @@ char *safe_tempnam(void) strcat(full_tempdir, "nano.XXXXXX"); filedesc = mkstemp(full_tempdir); - /* If mkstemp() succeeded, close the resulting file, delete it - * (since it'll be 0 bytes long), and return the filename. */ - if (filedesc != -1) { - close(filedesc); - unlink(full_tempdir); - return full_tempdir; + if (filedesc != -1) + *f = fdopen(filedesc, "w+b"); + else { + free(full_tempdir); + full_tempdir = NULL; } - free(full_tempdir); - - return NULL; + return full_tempdir; } #endif /* !DISABLE_SPELLER */ @@ -1304,10 +1304,11 @@ int copy_file(FILE *inn, FILE *out) return retval; } -/* Write a file out. If tmp is FALSE, we set the umask to disallow - * anyone else from accessing the file, we don't set the global variable - * filename to its name, and we don't print out how many lines we wrote - * on the statusbar. +/* Write a file out. If f_open isn't NULL, we assume that it is a + * stream associated with the file, and we don't try to open it + * ourselves. If tmp is TRUE, we set the umask to disallow anyone else + * from accessing the file, we don't set the global variable filename to + * its name, and we don't print out how many lines we wrote on the statusbar. * * tmp means we are writing a temporary file in a secure fashion. We * use it when spell checking or dumping the file on an error. @@ -1318,9 +1319,9 @@ int copy_file(FILE *inn, FILE *out) * nonamechange means don't change the current filename. It is ignored * if tmp is FALSE or if we're appending/prepending. * - * Return -1 on error, 1 on success. */ -int write_file(const char *name, bool tmp, int append, bool - nonamechange) + * Return 0 on success or -1 on error. */ +int write_file(const char *name, FILE *f_open, bool tmp, int append, + bool nonamechange) { int retval = -1; /* Instead of returning in this function, you should always @@ -1344,7 +1345,7 @@ int write_file(const char *name, bool tmp, int append, bool /* The status fields filled in by lstat(). */ char *realname; /* name after tilde expansion. */ - FILE *f; + FILE *f = NULL; /* The actual file, realname, we are writing to. */ char *tempname = NULL; /* The temp file name we write to on prepend. */ @@ -1354,6 +1355,9 @@ int write_file(const char *name, bool tmp, int append, bool if (name[0] == '\0') return -1; + if (f_open != NULL) + f = f_open; + if (!tmp) titlebar(NULL); @@ -1370,8 +1374,8 @@ int write_file(const char *name, bool tmp, int append, bool anyexists = (lstat(realname, &lst) != -1); - /* If the temp file exists, give up. */ - if (tmp && anyexists) + /* If the temp file exists and isn't already open, give up. */ + if (tmp && anyexists && f_open == NULL) goto cleanup_and_exit; /* If NOFOLLOW_SYMLINKS is set, it doesn't make sense to prepend or @@ -1404,13 +1408,15 @@ int write_file(const char *name, bool tmp, int append, bool filetime.actime = originalfilestat.st_atime; filetime.modtime = originalfilestat.st_mtime; - /* Open the original file to copy to the backup. */ - f = fopen(realname, "rb"); + if (f_open == NULL) { + /* Open the original file to copy to the backup. */ + f = fopen(realname, "rb"); - if (f == NULL) { - statusbar(_("Error reading %s: %s"), realname, - strerror(errno)); - goto cleanup_and_exit; + if (f == NULL) { + statusbar(_("Error reading %s: %s"), realname, + strerror(errno)); + goto cleanup_and_exit; + } } /* If backup_dir is set, we set backupname to @@ -1510,52 +1516,44 @@ int write_file(const char *name, bool tmp, int append, bool goto cleanup_and_exit; } - original_umask = umask(0); - umask(original_umask); + if (f_open == NULL) { + original_umask = umask(0); + umask(original_umask); - /* If we create a temp file, we don't let anyone else access it. We - * create a temp file if tmp is TRUE or if we're prepending. */ - if (tmp || append == 2) - umask(S_IRWXG | S_IRWXO); + /* If we create a temp file, we don't let anyone else access it. + * We create a temp file if tmp is TRUE or if we're + * prepending. */ + if (tmp || append == 2) + umask(S_IRWXG | S_IRWXO); + } /* If we're prepending, copy the file to a temp file. */ if (append == 2) { int fd_source; FILE *f_source = NULL; - tempname = charalloc(strlen(realname) + 8); - strcpy(tempname, realname); - strcat(tempname, ".XXXXXX"); - fd = mkstemp(tempname); - f = NULL; + tempname = safe_tempfile(&f); - if (fd != -1) { - f = fdopen(fd, "wb"); - if (f == NULL) - close(fd); - } - - if (f == NULL) { - statusbar(_("Error writing %s: %s"), tempname, + if (tempname == NULL) { + statusbar(_("Prepending to %s failed: %s"), realname, strerror(errno)); - unlink(tempname); goto cleanup_and_exit; } - fd_source = open(realname, O_RDONLY | O_CREAT); + if (f_open == NULL) { + fd_source = open(realname, O_RDONLY | O_CREAT); - if (fd_source != -1) { - f_source = fdopen(fd_source, "rb"); - if (f_source == NULL) - close(fd_source); - } - - if (f_source == NULL) { - statusbar(_("Error reading %s: %s"), realname, - strerror(errno)); - fclose(f); - unlink(tempname); - goto cleanup_and_exit; + if (fd_source != -1) { + f_source = fdopen(fd_source, "rb"); + if (f_source == NULL) { + statusbar(_("Error reading %s: %s"), realname, + strerror(errno)); + close(fd_source); + fclose(f); + unlink(tempname); + goto cleanup_and_exit; + } + } } if (copy_file(f_source, f) != 0) { @@ -1566,31 +1564,36 @@ int write_file(const char *name, bool tmp, int append, bool } } - /* Now open the file in place. Use O_EXCL if tmp is TRUE. This is - * copied from joe, because wiggy says so *shrug*. */ - fd = open(realname, O_WRONLY | O_CREAT | - ((append == 1) ? O_APPEND : (tmp ? O_EXCL : O_TRUNC)), - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + if (f_open == NULL) { + /* Now open the file in place. Use O_EXCL if tmp is TRUE. This + * is copied from joe, because wiggy says so *shrug*. */ + fd = open(realname, O_WRONLY | O_CREAT | + ((append == 1) ? O_APPEND : (tmp ? O_EXCL : O_TRUNC)), + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | + S_IWOTH); - /* Set the umask back to the user's original value. */ - umask(original_umask); + /* Set the umask back to the user's original value. */ + umask(original_umask); - /* If we couldn't open the file, give up. */ - if (fd == -1) { - statusbar(_("Error writing %s: %s"), realname, strerror(errno)); + /* If we couldn't open the file, give up. */ + if (fd == -1) { + statusbar(_("Error writing %s: %s"), realname, + strerror(errno)); - /* tempname has been set only if we're prepending. */ - if (tempname != NULL) - unlink(tempname); - goto cleanup_and_exit; - } + /* tempname has been set only if we're prepending. */ + if (tempname != NULL) + unlink(tempname); + goto cleanup_and_exit; + } - f = fdopen(fd, (append == 1) ? "ab" : "wb"); + f = fdopen(fd, (append == 1) ? "ab" : "wb"); - if (f == NULL) { - statusbar(_("Error writing %s: %s"), realname, strerror(errno)); - close(fd); - goto cleanup_and_exit; + if (f == NULL) { + statusbar(_("Error writing %s: %s"), realname, + strerror(errno)); + close(fd); + goto cleanup_and_exit; + } } /* There might not be a magicline. There won't be when writing out @@ -1692,7 +1695,7 @@ int write_file(const char *name, bool tmp, int append, bool titlebar(NULL); } - retval = 1; + retval = 0; cleanup_and_exit: free(realname); @@ -1704,11 +1707,11 @@ int write_file(const char *name, bool tmp, int append, bool #ifndef NANO_SMALL /* Write a marked selection from a file out. First, set fileage and * filebot as the top and bottom of the mark, respectively. Then call - * write_file() with the values of name, temp, and append, and with - * nonamechange set to TRUE so that we don't change the current + * write_file() with the values of name, f_open, temp, and append, and + * with nonamechange set to TRUE so that we don't change the current * filename. Finally, set fileage and filebot back to their old values * and return. */ -int write_marked(const char *name, bool tmp, int append) +int write_marked(const char *name, FILE *f_open, bool tmp, int append) { int retval = -1; bool old_modified = ISSET(MODIFIED); @@ -1731,7 +1734,7 @@ int write_marked(const char *name, bool tmp, int append) if (added_magicline) new_magicline(); - retval = write_file(name, tmp, append, TRUE); + retval = write_file(name, f_open, tmp, append, TRUE); /* If we added a magicline, remove it now. */ if (added_magicline) @@ -1761,10 +1764,10 @@ int do_writeout(bool exiting) currshortcut = writefile_list; if (exiting && filename[0] != '\0' && ISSET(TEMP_FILE)) { - retval = write_file(filename, FALSE, 0, FALSE); + retval = write_file(filename, NULL, FALSE, 0, FALSE); /* Write succeeded. */ - if (retval == 1) + if (retval == 0) return retval; } @@ -1907,10 +1910,10 @@ int do_writeout(bool exiting) * disabled since it allows reading from or writing to files * not specified on the command line. */ if (!ISSET(RESTRICTED) && !exiting && ISSET(MARK_ISSET)) - retval = write_marked(answer, FALSE, append); + retval = write_marked(answer, NULL, FALSE, append); else #endif /* !NANO_SMALL */ - retval = write_file(answer, FALSE, append, FALSE); + retval = write_file(answer, NULL, FALSE, append, FALSE); #ifdef ENABLE_MULTIBUFFER /* If we're not about to exit, update the current entry in diff --git a/src/nano.c b/src/nano.c index dfe28cbd..fc823078 100644 --- a/src/nano.c +++ b/src/nano.c @@ -171,7 +171,7 @@ void die_save_file(const char *die_filename) retval = get_next_filename(die_filename, ".save"); if (retval[0] != '\0') - failed = (write_file(retval, TRUE, FALSE, TRUE) == -1); + failed = (write_file(retval, NULL, TRUE, FALSE, TRUE) == -1); if (!failed) fprintf(stderr, _("\nBuffer written to %s\n"), retval); @@ -2368,7 +2368,8 @@ const char *do_alt_speller(char *tempfile_name) void do_spell(void) { int i; - char *temp = safe_tempnam(); + FILE *temp_file; + char *temp = safe_tempfile(&temp_file); const char *spell_msg; if (temp == NULL) { @@ -2378,10 +2379,10 @@ void do_spell(void) #ifndef NANO_SMALL if (ISSET(MARK_ISSET)) - i = write_marked(temp, TRUE, FALSE); + i = write_marked(temp, temp_file, TRUE, FALSE); else #endif - i = write_file(temp, TRUE, FALSE, FALSE); + i = write_file(temp, temp_file, TRUE, FALSE, FALSE); if (i == -1) { statusbar(_("Error writing temp file: %s"), strerror(errno)); @@ -3368,7 +3369,7 @@ void do_exit(void) dump_buffer(fileage); #endif - if (i == 0 || (i == 1 && do_writeout(TRUE) > 0)) { + if (i == 0 || (i == 1 && do_writeout(TRUE) == 0)) { #ifdef ENABLE_MULTIBUFFER /* Exit only if there are no more open file buffers. */ if (!close_open_file()) diff --git a/src/proto.h b/src/proto.h index b6f42e12..77aca662 100644 --- a/src/proto.h +++ b/src/proto.h @@ -280,7 +280,7 @@ char *get_full_path(const char *origpath); #endif #ifndef DISABLE_SPELLER char *check_writable_directory(const char *path); -char *safe_tempnam(void); +char *safe_tempfile(FILE **f); #endif #ifndef DISABLE_OPERATINGDIR void init_operating_dir(void); @@ -290,10 +290,10 @@ bool check_operating_dir(const char *currpath, bool allow_tabcomp); void init_backup_dir(void); #endif int copy_file(FILE *inn, FILE *out); -int write_file(const char *name, bool tmp, int append, bool - nonamechange); +int write_file(const char *name, FILE *f_open, bool tmp, int append, + bool nonamechange); #ifndef NANO_SMALL -int write_marked(const char *name, bool tmp, int append); +int write_marked(const char *name, FILE *f_open, bool tmp, int append); #endif int do_writeout(bool exiting); void do_writeout_void(void);