diff options
| author | Paul Eggert | 2012-11-03 11:54:17 -0700 |
|---|---|---|
| committer | Paul Eggert | 2012-11-03 11:54:17 -0700 |
| commit | 7ccfb720b477df05042729e0e300bae5922f5120 (patch) | |
| tree | d5bfded71b48dfefbc4e3334c0ae586de29ab7bc /lib | |
| parent | 376a8e83bb3438f77dadf2d9910ef7baabcc82c2 (diff) | |
| download | emacs-7ccfb720b477df05042729e0e300bae5922f5120.tar.gz emacs-7ccfb720b477df05042729e0e300bae5922f5120.zip | |
Fix data-loss with --batch.
* admin/merge-gnulib (GNULIB_MODULES): Add close-stream.
* lib/close-stream.c, lib/close-stream.h, lib/fpending.c
* lib/fpending.h, m4/close-stream.m4, m4/fpending.m4:
New files, from gnulib.
* lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate.
* src/emacs.c: Include <close-stream.h>.
(close_output_streams): New function.
(main): Pass it to atexit, so that Emacs closes stdout and stderr
and handles errors appropriately.
(Fkill_emacs): Don't worry about flushing, as close_output_stream
does that now.
Fixes: debbugs:9574
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/close-stream.c | 78 | ||||
| -rw-r--r-- | lib/close-stream.h | 2 | ||||
| -rw-r--r-- | lib/fpending.c | 30 | ||||
| -rw-r--r-- | lib/fpending.h | 30 | ||||
| -rw-r--r-- | lib/gnulib.mk | 19 |
5 files changed, 158 insertions, 1 deletions
diff --git a/lib/close-stream.c b/lib/close-stream.c new file mode 100644 index 00000000000..04fa5ece09d --- /dev/null +++ b/lib/close-stream.c | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | /* Close a stream, with nicer error checking than fclose's. | ||
| 2 | |||
| 3 | Copyright (C) 1998-2002, 2004, 2006-2012 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | This program is free software: you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 3 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | ||
| 17 | |||
| 18 | #include <config.h> | ||
| 19 | |||
| 20 | #include "close-stream.h" | ||
| 21 | |||
| 22 | #include <errno.h> | ||
| 23 | #include <stdbool.h> | ||
| 24 | |||
| 25 | #include "fpending.h" | ||
| 26 | |||
| 27 | #if USE_UNLOCKED_IO | ||
| 28 | # include "unlocked-io.h" | ||
| 29 | #endif | ||
| 30 | |||
| 31 | /* Close STREAM. Return 0 if successful, EOF (setting errno) | ||
| 32 | otherwise. A failure might set errno to 0 if the error number | ||
| 33 | cannot be determined. | ||
| 34 | |||
| 35 | A failure with errno set to EPIPE may or may not indicate an error | ||
| 36 | situation worth signaling to the user. See the documentation of the | ||
| 37 | close_stdout_set_ignore_EPIPE function for details. | ||
| 38 | |||
| 39 | If a program writes *anything* to STREAM, that program should close | ||
| 40 | STREAM and make sure that it succeeds before exiting. Otherwise, | ||
| 41 | suppose that you go to the extreme of checking the return status | ||
| 42 | of every function that does an explicit write to STREAM. The last | ||
| 43 | printf can succeed in writing to the internal stream buffer, and yet | ||
| 44 | the fclose(STREAM) could still fail (due e.g., to a disk full error) | ||
| 45 | when it tries to write out that buffered data. Thus, you would be | ||
| 46 | left with an incomplete output file and the offending program would | ||
| 47 | exit successfully. Even calling fflush is not always sufficient, | ||
| 48 | since some file systems (NFS and CODA) buffer written/flushed data | ||
| 49 | until an actual close call. | ||
| 50 | |||
| 51 | Besides, it's wasteful to check the return value from every call | ||
| 52 | that writes to STREAM -- just let the internal stream state record | ||
| 53 | the failure. That's what the ferror test is checking below. */ | ||
| 54 | |||
| 55 | int | ||
| 56 | close_stream (FILE *stream) | ||
| 57 | { | ||
| 58 | const bool some_pending = (__fpending (stream) != 0); | ||
| 59 | const bool prev_fail = (ferror (stream) != 0); | ||
| 60 | const bool fclose_fail = (fclose (stream) != 0); | ||
| 61 | |||
| 62 | /* Return an error indication if there was a previous failure or if | ||
| 63 | fclose failed, with one exception: ignore an fclose failure if | ||
| 64 | there was no previous error, no data remains to be flushed, and | ||
| 65 | fclose failed with EBADF. That can happen when a program like cp | ||
| 66 | is invoked like this 'cp a b >&-' (i.e., with standard output | ||
| 67 | closed) and doesn't generate any output (hence no previous error | ||
| 68 | and nothing to be flushed). */ | ||
| 69 | |||
| 70 | if (prev_fail || (fclose_fail && (some_pending || errno != EBADF))) | ||
| 71 | { | ||
| 72 | if (! fclose_fail) | ||
| 73 | errno = 0; | ||
| 74 | return EOF; | ||
| 75 | } | ||
| 76 | |||
| 77 | return 0; | ||
| 78 | } | ||
diff --git a/lib/close-stream.h b/lib/close-stream.h new file mode 100644 index 00000000000..be3d4196b06 --- /dev/null +++ b/lib/close-stream.h | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | #include <stdio.h> | ||
| 2 | int close_stream (FILE *stream); | ||
diff --git a/lib/fpending.c b/lib/fpending.c new file mode 100644 index 00000000000..2591d534377 --- /dev/null +++ b/lib/fpending.c | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | /* fpending.c -- return the number of pending output bytes on a stream | ||
| 2 | Copyright (C) 2000, 2004, 2006-2007, 2009-2012 Free Software Foundation, | ||
| 3 | Inc. | ||
| 4 | |||
| 5 | This program is free software: you can redistribute it and/or modify | ||
| 6 | it under the terms of the GNU General Public License as published by | ||
| 7 | the Free Software Foundation; either version 3 of the License, or | ||
| 8 | (at your option) any later version. | ||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, | ||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | GNU General Public License for more details. | ||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License | ||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | ||
| 17 | |||
| 18 | /* Written by Jim Meyering. */ | ||
| 19 | |||
| 20 | #include <config.h> | ||
| 21 | |||
| 22 | #include "fpending.h" | ||
| 23 | |||
| 24 | /* Return the number of pending (aka buffered, unflushed) | ||
| 25 | bytes on the stream, FP, that is open for writing. */ | ||
| 26 | size_t | ||
| 27 | __fpending (FILE *fp) | ||
| 28 | { | ||
| 29 | return PENDING_OUTPUT_N_BYTES; | ||
| 30 | } | ||
diff --git a/lib/fpending.h b/lib/fpending.h new file mode 100644 index 00000000000..0365287ba76 --- /dev/null +++ b/lib/fpending.h | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | /* Declare __fpending. | ||
| 2 | |||
| 3 | Copyright (C) 2000, 2003, 2005-2006, 2009-2012 Free Software Foundation, | ||
| 4 | Inc. | ||
| 5 | |||
| 6 | This program is free software: you can redistribute it and/or modify | ||
| 7 | it under the terms of the GNU General Public License as published by | ||
| 8 | the Free Software Foundation; either version 3 of the License, or | ||
| 9 | (at your option) any later version. | ||
| 10 | |||
| 11 | This program is distributed in the hope that it will be useful, | ||
| 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | GNU General Public License for more details. | ||
| 15 | |||
| 16 | You should have received a copy of the GNU General Public License | ||
| 17 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 18 | |||
| 19 | Written by Jim Meyering. */ | ||
| 20 | |||
| 21 | #include <stddef.h> | ||
| 22 | #include <stdio.h> | ||
| 23 | |||
| 24 | #if HAVE_DECL___FPENDING | ||
| 25 | # if HAVE_STDIO_EXT_H | ||
| 26 | # include <stdio_ext.h> | ||
| 27 | # endif | ||
| 28 | #else | ||
| 29 | size_t __fpending (FILE *); | ||
| 30 | #endif | ||
diff --git a/lib/gnulib.mk b/lib/gnulib.mk index 23749331a83..324e5cb78fd 100644 --- a/lib/gnulib.mk +++ b/lib/gnulib.mk | |||
| @@ -21,7 +21,7 @@ | |||
| 21 | # the same distribution terms as the rest of that program. | 21 | # the same distribution terms as the rest of that program. |
| 22 | # | 22 | # |
| 23 | # Generated by gnulib-tool. | 23 | # Generated by gnulib-tool. |
| 24 | # Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=errno --avoid=fcntl --avoid=fcntl-h --avoid=fstat --avoid=msvc-inval --avoid=msvc-nothrow --avoid=raise --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo filemode getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings mktime pselect pthread_sigmask readlink socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub utimens warnings | 24 | # Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=errno --avoid=fcntl --avoid=fcntl-h --avoid=fstat --avoid=msvc-inval --avoid=msvc-nothrow --avoid=raise --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo filemode getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings mktime pselect pthread_sigmask readlink socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub utimens warnings |
| 25 | 25 | ||
| 26 | 26 | ||
| 27 | MOSTLYCLEANFILES += core *.stackdump | 27 | MOSTLYCLEANFILES += core *.stackdump |
| @@ -84,6 +84,14 @@ EXTRA_DIST += careadlinkat.h | |||
| 84 | 84 | ||
| 85 | ## end gnulib module careadlinkat | 85 | ## end gnulib module careadlinkat |
| 86 | 86 | ||
| 87 | ## begin gnulib module close-stream | ||
| 88 | |||
| 89 | libgnu_a_SOURCES += close-stream.c | ||
| 90 | |||
| 91 | EXTRA_DIST += close-stream.h | ||
| 92 | |||
| 93 | ## end gnulib module close-stream | ||
| 94 | |||
| 87 | ## begin gnulib module crypto/md5 | 95 | ## begin gnulib module crypto/md5 |
| 88 | 96 | ||
| 89 | libgnu_a_SOURCES += md5.c | 97 | libgnu_a_SOURCES += md5.c |
| @@ -183,6 +191,15 @@ EXTRA_DIST += filemode.h | |||
| 183 | 191 | ||
| 184 | ## end gnulib module filemode | 192 | ## end gnulib module filemode |
| 185 | 193 | ||
| 194 | ## begin gnulib module fpending | ||
| 195 | |||
| 196 | |||
| 197 | EXTRA_DIST += fpending.c fpending.h | ||
| 198 | |||
| 199 | EXTRA_libgnu_a_SOURCES += fpending.c | ||
| 200 | |||
| 201 | ## end gnulib module fpending | ||
| 202 | |||
| 186 | ## begin gnulib module getloadavg | 203 | ## begin gnulib module getloadavg |
| 187 | 204 | ||
| 188 | 205 | ||