bool already_exists = false;
mode_t new_mask;
int ifd, ofd;
- int n;
- char buf[16 * 1024];
struct stat st;
#endif
record_unwind_protect_int (close_file_unwind, ofd);
+ off_t oldsize = 0, newsize = 0;
+
if (already_exists)
{
struct stat out_st;
if (st.st_dev == out_st.st_dev && st.st_ino == out_st.st_ino)
report_file_errno ("Input and output files are the same",
list2 (file, newname), 0);
- if (ftruncate (ofd, 0) != 0)
- report_file_error ("Truncating output file", newname);
+ if (S_ISREG (out_st.st_mode))
+ oldsize = out_st.st_size;
}
immediate_quit = 1;
QUIT;
- while ((n = emacs_read (ifd, buf, sizeof buf)) > 0)
- if (emacs_write_sig (ofd, buf, n) != n)
- report_file_error ("Write error", newname);
+ while (true)
+ {
+ char buf[MAX_ALLOCA];
+ ptrdiff_t n = emacs_read (ifd, buf, sizeof buf);
+ if (n < 0)
+ report_file_error ("Read error", file);
+ if (n == 0)
+ break;
+ if (emacs_write_sig (ofd, buf, n) != n)
+ report_file_error ("Write error", newname);
+ newsize += n;
+ }
+
+ /* Truncate any existing output file after writing the data. This
+ is more likely to work than truncation before writing, if the
+ file system is out of space or the user is over disk quota. */
+ if (newsize < oldsize && ftruncate (ofd, newsize) != 0)
+ report_file_error ("Truncating output file", newname);
+
immediate_quit = 0;
#ifndef MSDOS