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


git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@2571 35c25a1d-7b9e-4130-9fde-d3aeb78583b8
master
David Lawrence Ramsey 2005-05-31 04:28:15 +00:00
parent bec2f20da0
commit 5e068c6031
4 changed files with 110 additions and 100 deletions

View File

@ -40,6 +40,12 @@ CVS code -
necessary. Changes to get_next_filename(), write_file(), necessary. Changes to get_next_filename(), write_file(),
die(), usage(), nano.1, nanorc.5, nanorc.sample, and die(), usage(), nano.1, nanorc.5, nanorc.sample, and
nano.texi. (DLR, suggested by James Collings) 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.c:
cut_line() cut_line()
- Set placewewant properly after cutting a line, to avoid a - Set placewewant properly after cutting a line, to avoid a

View File

@ -1134,15 +1134,18 @@ char *check_writable_directory(const char *path)
return full_path; return full_path;
} }
/* This function acts like a call to tempnam(NULL, "nano."). The /* This function calls mkstemp(($TMPDIR|P_tmpdir|/tmp/)"nano.XXXXXX").
* difference is that the number of calls is not limited by TMP_MAX. * On success, it returns the malloc()ed filename and corresponding FILE
* Instead we use mkstemp(). */ * stream, opened in "w+b" mode. On error, it returns NULL for the
char *safe_tempnam(void) * filename and leaves the FILE stream unchanged. */
char *safe_tempfile(FILE **f)
{ {
char *full_tempdir = NULL; char *full_tempdir = NULL;
const char *TMPDIR_env; const char *TMPDIR_env;
int filedesc; int filedesc;
assert(f != NULL);
/* If $TMPDIR is set and non-empty, set tempdir to it, run it /* If $TMPDIR is set and non-empty, set tempdir to it, run it
* through get_full_path(), and save the result in full_tempdir. * through get_full_path(), and save the result in full_tempdir.
* Otherwise, leave full_tempdir set to NULL. */ * Otherwise, leave full_tempdir set to NULL. */
@ -1163,17 +1166,14 @@ char *safe_tempnam(void)
strcat(full_tempdir, "nano.XXXXXX"); strcat(full_tempdir, "nano.XXXXXX");
filedesc = mkstemp(full_tempdir); filedesc = mkstemp(full_tempdir);
/* If mkstemp() succeeded, close the resulting file, delete it if (filedesc != -1)
* (since it'll be 0 bytes long), and return the filename. */ *f = fdopen(filedesc, "w+b");
if (filedesc != -1) { else {
close(filedesc); free(full_tempdir);
unlink(full_tempdir); full_tempdir = NULL;
return full_tempdir;
} }
free(full_tempdir); return full_tempdir;
return NULL;
} }
#endif /* !DISABLE_SPELLER */ #endif /* !DISABLE_SPELLER */
@ -1304,10 +1304,11 @@ int copy_file(FILE *inn, FILE *out)
return retval; return retval;
} }
/* Write a file out. If tmp is FALSE, we set the umask to disallow /* Write a file out. If f_open isn't NULL, we assume that it is a
* anyone else from accessing the file, we don't set the global variable * stream associated with the file, and we don't try to open it
* filename to its name, and we don't print out how many lines we wrote * ourselves. If tmp is TRUE, we set the umask to disallow anyone else
* on the statusbar. * 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 * 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. * 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 * nonamechange means don't change the current filename. It is ignored
* if tmp is FALSE or if we're appending/prepending. * if tmp is FALSE or if we're appending/prepending.
* *
* Return -1 on error, 1 on success. */ * Return 0 on success or -1 on error. */
int write_file(const char *name, bool tmp, int append, bool int write_file(const char *name, FILE *f_open, bool tmp, int append,
nonamechange) bool nonamechange)
{ {
int retval = -1; int retval = -1;
/* Instead of returning in this function, you should always /* 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(). */ /* The status fields filled in by lstat(). */
char *realname; char *realname;
/* name after tilde expansion. */ /* name after tilde expansion. */
FILE *f; FILE *f = NULL;
/* The actual file, realname, we are writing to. */ /* The actual file, realname, we are writing to. */
char *tempname = NULL; char *tempname = NULL;
/* The temp file name we write to on prepend. */ /* 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') if (name[0] == '\0')
return -1; return -1;
if (f_open != NULL)
f = f_open;
if (!tmp) if (!tmp)
titlebar(NULL); titlebar(NULL);
@ -1370,8 +1374,8 @@ int write_file(const char *name, bool tmp, int append, bool
anyexists = (lstat(realname, &lst) != -1); anyexists = (lstat(realname, &lst) != -1);
/* If the temp file exists, give up. */ /* If the temp file exists and isn't already open, give up. */
if (tmp && anyexists) if (tmp && anyexists && f_open == NULL)
goto cleanup_and_exit; goto cleanup_and_exit;
/* If NOFOLLOW_SYMLINKS is set, it doesn't make sense to prepend or /* If NOFOLLOW_SYMLINKS is set, it doesn't make sense to prepend or
@ -1404,6 +1408,7 @@ int write_file(const char *name, bool tmp, int append, bool
filetime.actime = originalfilestat.st_atime; filetime.actime = originalfilestat.st_atime;
filetime.modtime = originalfilestat.st_mtime; filetime.modtime = originalfilestat.st_mtime;
if (f_open == NULL) {
/* Open the original file to copy to the backup. */ /* Open the original file to copy to the backup. */
f = fopen(realname, "rb"); f = fopen(realname, "rb");
@ -1412,6 +1417,7 @@ int write_file(const char *name, bool tmp, int append, bool
strerror(errno)); strerror(errno));
goto cleanup_and_exit; goto cleanup_and_exit;
} }
}
/* If backup_dir is set, we set backupname to /* If backup_dir is set, we set backupname to
* backup_dir/backupname~[.number], where backupname is the * backup_dir/backupname~[.number], where backupname is the
@ -1510,53 +1516,45 @@ int write_file(const char *name, bool tmp, int append, bool
goto cleanup_and_exit; goto cleanup_and_exit;
} }
if (f_open == NULL) {
original_umask = umask(0); original_umask = umask(0);
umask(original_umask); umask(original_umask);
/* If we create a temp file, we don't let anyone else access it. We /* If we create a temp file, we don't let anyone else access it.
* create a temp file if tmp is TRUE or if we're prepending. */ * We create a temp file if tmp is TRUE or if we're
* prepending. */
if (tmp || append == 2) if (tmp || append == 2)
umask(S_IRWXG | S_IRWXO); umask(S_IRWXG | S_IRWXO);
}
/* If we're prepending, copy the file to a temp file. */ /* If we're prepending, copy the file to a temp file. */
if (append == 2) { if (append == 2) {
int fd_source; int fd_source;
FILE *f_source = NULL; FILE *f_source = NULL;
tempname = charalloc(strlen(realname) + 8); tempname = safe_tempfile(&f);
strcpy(tempname, realname);
strcat(tempname, ".XXXXXX");
fd = mkstemp(tempname);
f = NULL;
if (fd != -1) { if (tempname == NULL) {
f = fdopen(fd, "wb"); statusbar(_("Prepending to %s failed: %s"), realname,
if (f == NULL)
close(fd);
}
if (f == NULL) {
statusbar(_("Error writing %s: %s"), tempname,
strerror(errno)); strerror(errno));
unlink(tempname);
goto cleanup_and_exit; goto cleanup_and_exit;
} }
if (f_open == NULL) {
fd_source = open(realname, O_RDONLY | O_CREAT); fd_source = open(realname, O_RDONLY | O_CREAT);
if (fd_source != -1) { if (fd_source != -1) {
f_source = fdopen(fd_source, "rb"); f_source = fdopen(fd_source, "rb");
if (f_source == NULL)
close(fd_source);
}
if (f_source == NULL) { if (f_source == NULL) {
statusbar(_("Error reading %s: %s"), realname, statusbar(_("Error reading %s: %s"), realname,
strerror(errno)); strerror(errno));
close(fd_source);
fclose(f); fclose(f);
unlink(tempname); unlink(tempname);
goto cleanup_and_exit; goto cleanup_and_exit;
} }
}
}
if (copy_file(f_source, f) != 0) { if (copy_file(f_source, f) != 0) {
statusbar(_("Error writing %s: %s"), tempname, statusbar(_("Error writing %s: %s"), tempname,
@ -1566,18 +1564,21 @@ 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 if (f_open == NULL) {
* copied from joe, because wiggy says so *shrug*. */ /* 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 | fd = open(realname, O_WRONLY | O_CREAT |
((append == 1) ? O_APPEND : (tmp ? O_EXCL : O_TRUNC)), ((append == 1) ? O_APPEND : (tmp ? O_EXCL : O_TRUNC)),
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH |
S_IWOTH);
/* Set the umask back to the user's original value. */ /* Set the umask back to the user's original value. */
umask(original_umask); umask(original_umask);
/* If we couldn't open the file, give up. */ /* If we couldn't open the file, give up. */
if (fd == -1) { if (fd == -1) {
statusbar(_("Error writing %s: %s"), realname, strerror(errno)); statusbar(_("Error writing %s: %s"), realname,
strerror(errno));
/* tempname has been set only if we're prepending. */ /* tempname has been set only if we're prepending. */
if (tempname != NULL) if (tempname != NULL)
@ -1588,10 +1589,12 @@ int write_file(const char *name, bool tmp, int append, bool
f = fdopen(fd, (append == 1) ? "ab" : "wb"); f = fdopen(fd, (append == 1) ? "ab" : "wb");
if (f == NULL) { if (f == NULL) {
statusbar(_("Error writing %s: %s"), realname, strerror(errno)); statusbar(_("Error writing %s: %s"), realname,
strerror(errno));
close(fd); close(fd);
goto cleanup_and_exit; goto cleanup_and_exit;
} }
}
/* There might not be a magicline. There won't be when writing out /* There might not be a magicline. There won't be when writing out
* a selection. */ * a selection. */
@ -1692,7 +1695,7 @@ int write_file(const char *name, bool tmp, int append, bool
titlebar(NULL); titlebar(NULL);
} }
retval = 1; retval = 0;
cleanup_and_exit: cleanup_and_exit:
free(realname); free(realname);
@ -1704,11 +1707,11 @@ int write_file(const char *name, bool tmp, int append, bool
#ifndef NANO_SMALL #ifndef NANO_SMALL
/* Write a marked selection from a file out. First, set fileage and /* Write a marked selection from a file out. First, set fileage and
* filebot as the top and bottom of the mark, respectively. Then call * filebot as the top and bottom of the mark, respectively. Then call
* write_file() with the values of name, temp, and append, and with * write_file() with the values of name, f_open, temp, and append, and
* nonamechange set to TRUE so that we don't change the current * with nonamechange set to TRUE so that we don't change the current
* filename. Finally, set fileage and filebot back to their old values * filename. Finally, set fileage and filebot back to their old values
* and return. */ * 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; int retval = -1;
bool old_modified = ISSET(MODIFIED); bool old_modified = ISSET(MODIFIED);
@ -1731,7 +1734,7 @@ int write_marked(const char *name, bool tmp, int append)
if (added_magicline) if (added_magicline)
new_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 we added a magicline, remove it now. */
if (added_magicline) if (added_magicline)
@ -1761,10 +1764,10 @@ int do_writeout(bool exiting)
currshortcut = writefile_list; currshortcut = writefile_list;
if (exiting && filename[0] != '\0' && ISSET(TEMP_FILE)) { 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. */ /* Write succeeded. */
if (retval == 1) if (retval == 0)
return retval; return retval;
} }
@ -1907,10 +1910,10 @@ int do_writeout(bool exiting)
* disabled since it allows reading from or writing to files * disabled since it allows reading from or writing to files
* not specified on the command line. */ * not specified on the command line. */
if (!ISSET(RESTRICTED) && !exiting && ISSET(MARK_ISSET)) if (!ISSET(RESTRICTED) && !exiting && ISSET(MARK_ISSET))
retval = write_marked(answer, FALSE, append); retval = write_marked(answer, NULL, FALSE, append);
else else
#endif /* !NANO_SMALL */ #endif /* !NANO_SMALL */
retval = write_file(answer, FALSE, append, FALSE); retval = write_file(answer, NULL, FALSE, append, FALSE);
#ifdef ENABLE_MULTIBUFFER #ifdef ENABLE_MULTIBUFFER
/* If we're not about to exit, update the current entry in /* If we're not about to exit, update the current entry in

View File

@ -171,7 +171,7 @@ void die_save_file(const char *die_filename)
retval = get_next_filename(die_filename, ".save"); retval = get_next_filename(die_filename, ".save");
if (retval[0] != '\0') if (retval[0] != '\0')
failed = (write_file(retval, TRUE, FALSE, TRUE) == -1); failed = (write_file(retval, NULL, TRUE, FALSE, TRUE) == -1);
if (!failed) if (!failed)
fprintf(stderr, _("\nBuffer written to %s\n"), retval); fprintf(stderr, _("\nBuffer written to %s\n"), retval);
@ -2368,7 +2368,8 @@ const char *do_alt_speller(char *tempfile_name)
void do_spell(void) void do_spell(void)
{ {
int i; int i;
char *temp = safe_tempnam(); FILE *temp_file;
char *temp = safe_tempfile(&temp_file);
const char *spell_msg; const char *spell_msg;
if (temp == NULL) { if (temp == NULL) {
@ -2378,10 +2379,10 @@ void do_spell(void)
#ifndef NANO_SMALL #ifndef NANO_SMALL
if (ISSET(MARK_ISSET)) if (ISSET(MARK_ISSET))
i = write_marked(temp, TRUE, FALSE); i = write_marked(temp, temp_file, TRUE, FALSE);
else else
#endif #endif
i = write_file(temp, TRUE, FALSE, FALSE); i = write_file(temp, temp_file, TRUE, FALSE, FALSE);
if (i == -1) { if (i == -1) {
statusbar(_("Error writing temp file: %s"), strerror(errno)); statusbar(_("Error writing temp file: %s"), strerror(errno));
@ -3368,7 +3369,7 @@ void do_exit(void)
dump_buffer(fileage); dump_buffer(fileage);
#endif #endif
if (i == 0 || (i == 1 && do_writeout(TRUE) > 0)) { if (i == 0 || (i == 1 && do_writeout(TRUE) == 0)) {
#ifdef ENABLE_MULTIBUFFER #ifdef ENABLE_MULTIBUFFER
/* Exit only if there are no more open file buffers. */ /* Exit only if there are no more open file buffers. */
if (!close_open_file()) if (!close_open_file())

View File

@ -280,7 +280,7 @@ char *get_full_path(const char *origpath);
#endif #endif
#ifndef DISABLE_SPELLER #ifndef DISABLE_SPELLER
char *check_writable_directory(const char *path); char *check_writable_directory(const char *path);
char *safe_tempnam(void); char *safe_tempfile(FILE **f);
#endif #endif
#ifndef DISABLE_OPERATINGDIR #ifndef DISABLE_OPERATINGDIR
void init_operating_dir(void); void init_operating_dir(void);
@ -290,10 +290,10 @@ bool check_operating_dir(const char *currpath, bool allow_tabcomp);
void init_backup_dir(void); void init_backup_dir(void);
#endif #endif
int copy_file(FILE *inn, FILE *out); int copy_file(FILE *inn, FILE *out);
int write_file(const char *name, bool tmp, int append, bool int write_file(const char *name, FILE *f_open, bool tmp, int append,
nonamechange); bool nonamechange);
#ifndef NANO_SMALL #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 #endif
int do_writeout(bool exiting); int do_writeout(bool exiting);
void do_writeout_void(void); void do_writeout_void(void);