In standard C, how do you reliably check whether all output that was written to a standard I/O stream was successfully saved to disk?
The C standard states that fclose
will return 0 on success, or EOF if "any errors were detected".
But does that mean "any errors were detected during the fclose
call"? Or does it mean "any errors were detected since the last call to clearerr
"?
In other words, is it sufficient for a program to just check the return value of fclose
, or is it also necessary to check ferror
? Are there any implementations where, if ferror
returns non-zero, a subsequent call to fclose
might return 0?
The same goes for fflush
: is it always the case that if fflush
returns 0, a subsequent call to ferror
will also return 0, and if fflush
returns EOF, a subsequent call to ferror
will return non-zero? Are there any implementations for which this is not the case?
(Of course, I'm not considering power outages, gremlins, etc. Yes, a program that requires guaranteed durability should use fsync
, but that's outside the scope of standard C.)
The ferror() function tests for an error in reading from or writing to the given stream . If an error occurs, the error indicator for the stream remains set until you close stream , call the rewind() function, or call the clearerr() function.
The ferror() function returns a non-zero value if there is an error associated with fptr . If there is no error associated with fptr , the function returns zero.
Return value If no error has occurred on stream , ferror returns 0. Otherwise, it returns a nonzero value. If stream is NULL , ferror invokes the invalid parameter handler, as described in Parameter validation. If execution is allowed to continue, this function sets errno to EINVAL and returns 0.
In the absence of a better answer:
the Standard tells me that ferror()
returns the state of the 'error indicator' which is defined to be part of the stream state.
the Standard tells me a lot about when the 'error indicator' is set, but nothing about when it may be cleared -- except that clearerr()
and rewind()
are defined to clear it.
the Standard tells me nothing about what any function (other than ferror()
) is expected to do if the 'error indicator' is set when the function is called.
Your questions seem to be based on the possibility that the 'error indicator' is set as soon as there is an error and only cleared when explicitly cleared (it is "latched"). In which case:
ferror()
would tell you that an error has occurred in some stdio function, since the fopen()
(or the most recent clearerr()
or rewind()
).
I don't think the Standard requires that, but it doesn't say it may not.
fclose()
might return an error (a) if one occurs while closing, or (b) if the 'error indicator' was already set.
And, if so, a successful fclose()
would mean that all was and has been well, since the fopen()
(or the most recent clearerr()
or rewind()
).
I don't think the Standard requires that, but it doesn't say it may not.
Where the Standard does not explicitly require something and does not explicitly rule something out, we have ourselves a cat which is neither alive nor dead.
In short, I don't think the Standard answers any of your questions, one way or another.
A conservative reading of the standard would be:
to check for errors immediately after every stdio function call, and proceed accordingly.
in general, after a read or write error, giving up and closing the stream is the obvious response.
fclose()
may return the current error (again), or a new error or no error at all. Where an error is to be returned I would return the original read/write error. Where errors are to be reported I would report both the original read/write error and any error returned by fclose()
.
if the decision is to keep on inputting/outputting, the 'error indicator' may (or may not) affect further functions, and may (or may not) be cleared by most further functions...
... so it is probably best to clearerr(), to avoid any possible confusion.
However, I am almost convinced that fgetc()
must either:
a. give up immediately if 'error indicator' is already set (and set errno
, again)
or:
b. clear 'error indicator' -- for if not, what does it mean if fgetc()
then succeeds and returns EOF
?
The same is true of the other get and put char and wide-char. Not quite so true of all the other functions, where the return value is not ambiguous.
to not expect ferror()
to tell you anything about errors which may have happened before the most recent stdio function call.
It is clear that ferror()
is useful for those few functions where the error return is the same as an EOF
or WEOF
return. (And feof()
is useful if it is possible that a character with the value EOF
or WEOF
may appear !).
It is less clear whether ferror()
and the 'error indicator' are useful for anything else.
FWIW: what I found in the Standard
The Standard says (§7.21.7.1) that for fgetc()
:
- If the end-of-file indicator for the input stream pointed to by stream is not set and a next character is present, the
fgetc
function obtains that character as an unsigned char converted to an int and advances the associated file position indicator for the stream (if defined).Returns
- If the end-of-file indicator for the stream is set, or if the stream is at end-of-file, the end-of-file indicator for the stream is set and the
fgetc
function returnsEOF
. Otherwise, thefgetc
function returns the next character from the input stream pointed to by stream. If a read error occurs, the error indicator for the stream is set and thefgetc
function returnsEOF
293).293) An end-of-file and a read error can be distinguished by use of the
feof
andferror
functions.
I note that this is very clear on what it should do if the 'end-of-file indicator' is set when the function is called. In contrast, it does not say, one way or another, what fgetc()
should do if the 'error indicator' is already set:
should fgetc()
fail immediately ?
If so, should it set errno
to the same value as when the 'error indicator' was first set ?
However, if the 'error indicator' were previously set by (say) EINTR
, it would make no sense to take any notice of it.
otherwise:
should fgetc()
clear the 'error indicator' if this call succeeds ?
If not, then 'error indicator' could be deemed to be a "latched" state, indicating that at some time since it was last cleared, an error has occurred.
Again, if the 'error indicator' were previously set by (say) EINTR
, it would make no sense to leave it set. Mind you, C knows nothing of EINTR
... so the implementation is free to do different things with the 'error indicator', depending on how it was set.
AND, if fgetc()
just happens to fetch a character with value EOF
or just happens to hit actual EOF, then NOT clearing the 'error indicator' would be a mistake !
The Standard says (§7.21.7.3) that for fputc()
:
- The
fputc
function writes the character specified byc
(converted to an unsigned char) to the output stream ...Returns
- The
fputc
function returns the character written. If a write error occurs, the error indicator for the stream is set andfputc
returnsEOF
.
And again, this does not specify what fputc()
should do if the 'error indicator' is already set.
And the same applies to fgetwc()
and fputwc()
.
Every other input/output function is defined to work "as if" they are repeated fgetc()
, fputc()
, fgetwc()
and fputwc()`.
The Standard says (§7.21.10.3) that for ferror()
:
- The
ferror
function tests the error indicator for the stream pointed to by stream.Returns
- The
ferror
function returns nonzero if and only if the error indicator is set for stream.
That's it. Footnote 293 above is the most concrete guide we have for how ferror()
and the 'error indicator' should be used.
fflush()
(§7.21.5.2), fseek()
(§7.21.9.2) and fsetpos()
(§7.21.9.3) are all defined to set the 'error indicator' in the event of an error.
rewind()
(§7.21.9.5) and clearerr()
(§7.21.10.1) are defined to clear the 'error indicator'.
I found no other references to the 'error indicator'.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With