How a stream error indicator affects following input code?
Each stream has "an error indicator that records whether a read/write error has occurred".
It is set, usually rarely, by various functions: fgetc(), fflush(), fseek(), ...
.
It is cleared by various functions: rewind(), clearerr(), fopen(), ...
.
int ferror(FILE *stream)
reports the state.
The
ferror
function returns nonzero if and only if the error indicator is set forstream
.
In this case, certainly an input error just occurred.
if (!ferror(istream))
int ch = fgetc(istream);
if (ch == EOF && ferror(istream))
puts("Input error just occurred");
Exploring fgetc()
deeper, fgetc()
does not return EOF
because the error indicator was set, but because "If a read error occurs" or end-of-file related reasons1. Usually once an error occurs (e. g. parity error on a serial stream), code does not continue reading without clearing the error, yet consider what happens when it does continue.
I see 8 situations: the error indicator set/clear prior to fgetc()
, fgetc()
returns EOF
or not, and a following ferror()
could be true or not.
int e1 = !!ferror(istream);
int eof = fgetc(istream) == EOF;
int e2 = !!ferror(istream);
Assuming no UB, are 5 of the 8 possible and not the 3 unexpected ones? especially is valid input possible with error indicator set? 2
e1 eof e2
0 0 0 Normal reading of valid data
0 0 1 Unexpected
0 1 0 End-of-file
0 1 1 Input error
1 0 0 Unexpected
1 0 1 Normal reading of valid data with error indicator set!
1 1 0 Unexpected
1 1 1 Input error or end-of-file
With the error indicator set prior to an input operation, things become complicated and clearing it beforehand simplifies code. Yet that prevents error indicator accumulation.
If codes does not clear the error indicator before hand and wants to detect if a line of input had a rare input error, it seems to make sense to test !feof()
and not ferror()
to detect.
Is checking ferror()
potentially misleading? or have I missed something about the error indicator?
char buf[80];
for (int i=0; i<(80-1); i++)
int ch = fgetc(stdin);
if (ch == EOF)
if (ferror(stdin))
puts("Input error or (Prior input error and end of file occurred)"); // ambiguous
if (feof(stdin))
puts("End of file occurred");
else
puts("Input error occurred"); // ferror() test not needed
i = 0; // ignore prior input
break;
if (ch == 'n') break;
buf[i++] = ch;
buf[i] = 0;
Similar questions
File operations with error indicator set
. This unanswered one centers on accumulating error indicator without testing fgetc()
return value (answers venture off into errno
and making a user error flag) and this one is trying to simply disambiguate a fgetc()
.
fgetc(): Is it enough to just check EOF? does not address error indicator set prior to fgetc()
.
Similar issues apply to output and I/O streams, yet this question focuses on input streams.
1int fgetc(FILE *stream)
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, the fgetc function returns the next character from the input stream pointed to bystream
. If a read error occurs, the error indicator for the stream is set and thefgetc
function returnsEOF
. C11dr §7.21.7.1 2
2 On cases 0-1-0, 1-1-1. Seems if UCHAR_MAX == UINT_MAX
, a unsigned char
, could be returned and equate to EOF
and not be due to end-of-file nor input error.
c error-handling language-lawyer
|
show 7 more comments
Each stream has "an error indicator that records whether a read/write error has occurred".
It is set, usually rarely, by various functions: fgetc(), fflush(), fseek(), ...
.
It is cleared by various functions: rewind(), clearerr(), fopen(), ...
.
int ferror(FILE *stream)
reports the state.
The
ferror
function returns nonzero if and only if the error indicator is set forstream
.
In this case, certainly an input error just occurred.
if (!ferror(istream))
int ch = fgetc(istream);
if (ch == EOF && ferror(istream))
puts("Input error just occurred");
Exploring fgetc()
deeper, fgetc()
does not return EOF
because the error indicator was set, but because "If a read error occurs" or end-of-file related reasons1. Usually once an error occurs (e. g. parity error on a serial stream), code does not continue reading without clearing the error, yet consider what happens when it does continue.
I see 8 situations: the error indicator set/clear prior to fgetc()
, fgetc()
returns EOF
or not, and a following ferror()
could be true or not.
int e1 = !!ferror(istream);
int eof = fgetc(istream) == EOF;
int e2 = !!ferror(istream);
Assuming no UB, are 5 of the 8 possible and not the 3 unexpected ones? especially is valid input possible with error indicator set? 2
e1 eof e2
0 0 0 Normal reading of valid data
0 0 1 Unexpected
0 1 0 End-of-file
0 1 1 Input error
1 0 0 Unexpected
1 0 1 Normal reading of valid data with error indicator set!
1 1 0 Unexpected
1 1 1 Input error or end-of-file
With the error indicator set prior to an input operation, things become complicated and clearing it beforehand simplifies code. Yet that prevents error indicator accumulation.
If codes does not clear the error indicator before hand and wants to detect if a line of input had a rare input error, it seems to make sense to test !feof()
and not ferror()
to detect.
Is checking ferror()
potentially misleading? or have I missed something about the error indicator?
char buf[80];
for (int i=0; i<(80-1); i++)
int ch = fgetc(stdin);
if (ch == EOF)
if (ferror(stdin))
puts("Input error or (Prior input error and end of file occurred)"); // ambiguous
if (feof(stdin))
puts("End of file occurred");
else
puts("Input error occurred"); // ferror() test not needed
i = 0; // ignore prior input
break;
if (ch == 'n') break;
buf[i++] = ch;
buf[i] = 0;
Similar questions
File operations with error indicator set
. This unanswered one centers on accumulating error indicator without testing fgetc()
return value (answers venture off into errno
and making a user error flag) and this one is trying to simply disambiguate a fgetc()
.
fgetc(): Is it enough to just check EOF? does not address error indicator set prior to fgetc()
.
Similar issues apply to output and I/O streams, yet this question focuses on input streams.
1int fgetc(FILE *stream)
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, the fgetc function returns the next character from the input stream pointed to bystream
. If a read error occurs, the error indicator for the stream is set and thefgetc
function returnsEOF
. C11dr §7.21.7.1 2
2 On cases 0-1-0, 1-1-1. Seems if UCHAR_MAX == UINT_MAX
, a unsigned char
, could be returned and equate to EOF
and not be due to end-of-file nor input error.
c error-handling language-lawyer
Are you looking for a standards-based answer (perhaps language-lawyer), or a practical answer?
– Nominal Animal
Nov 13 '18 at 2:08
@NominalAnimal Closer to standards-based than only practical as I am trying to divine the error indicator mysteries to solve: whenfgetc()
returnsEOF
, is that due to a recent error, end-of-file, some wideunsigned char
or something else? LL added.
– chux
Nov 13 '18 at 2:16
FWIW, with glibc it is always either due to detecting a new end-of-file or error condition, since thefgetc()
/getc()
/getchar()
functions never examine the error flag.
– Nominal Animal
Nov 13 '18 at 2:30
@NominalAnimal Does glibc also returnEOF
on existing end-of-file flag and not just a new end-of-file?
– chux
Nov 13 '18 at 2:35
1
The behavior of glibcfgetc
was changed in version 2.28, see sourceware.org/ml/libc-alpha/2018-08/msg00003.html and sourceware.org/bugzilla/show_bug.cgi?id=1190 . We sure thought 7.21.7p2,3 mandated "sticky EOF" -- the change is described as "correct[ing] a long-standing C99 conformance bug". (However, the EOF indicator and the error indicator are two separate bits.)
– zwol
Nov 14 '18 at 17:35
|
show 7 more comments
Each stream has "an error indicator that records whether a read/write error has occurred".
It is set, usually rarely, by various functions: fgetc(), fflush(), fseek(), ...
.
It is cleared by various functions: rewind(), clearerr(), fopen(), ...
.
int ferror(FILE *stream)
reports the state.
The
ferror
function returns nonzero if and only if the error indicator is set forstream
.
In this case, certainly an input error just occurred.
if (!ferror(istream))
int ch = fgetc(istream);
if (ch == EOF && ferror(istream))
puts("Input error just occurred");
Exploring fgetc()
deeper, fgetc()
does not return EOF
because the error indicator was set, but because "If a read error occurs" or end-of-file related reasons1. Usually once an error occurs (e. g. parity error on a serial stream), code does not continue reading without clearing the error, yet consider what happens when it does continue.
I see 8 situations: the error indicator set/clear prior to fgetc()
, fgetc()
returns EOF
or not, and a following ferror()
could be true or not.
int e1 = !!ferror(istream);
int eof = fgetc(istream) == EOF;
int e2 = !!ferror(istream);
Assuming no UB, are 5 of the 8 possible and not the 3 unexpected ones? especially is valid input possible with error indicator set? 2
e1 eof e2
0 0 0 Normal reading of valid data
0 0 1 Unexpected
0 1 0 End-of-file
0 1 1 Input error
1 0 0 Unexpected
1 0 1 Normal reading of valid data with error indicator set!
1 1 0 Unexpected
1 1 1 Input error or end-of-file
With the error indicator set prior to an input operation, things become complicated and clearing it beforehand simplifies code. Yet that prevents error indicator accumulation.
If codes does not clear the error indicator before hand and wants to detect if a line of input had a rare input error, it seems to make sense to test !feof()
and not ferror()
to detect.
Is checking ferror()
potentially misleading? or have I missed something about the error indicator?
char buf[80];
for (int i=0; i<(80-1); i++)
int ch = fgetc(stdin);
if (ch == EOF)
if (ferror(stdin))
puts("Input error or (Prior input error and end of file occurred)"); // ambiguous
if (feof(stdin))
puts("End of file occurred");
else
puts("Input error occurred"); // ferror() test not needed
i = 0; // ignore prior input
break;
if (ch == 'n') break;
buf[i++] = ch;
buf[i] = 0;
Similar questions
File operations with error indicator set
. This unanswered one centers on accumulating error indicator without testing fgetc()
return value (answers venture off into errno
and making a user error flag) and this one is trying to simply disambiguate a fgetc()
.
fgetc(): Is it enough to just check EOF? does not address error indicator set prior to fgetc()
.
Similar issues apply to output and I/O streams, yet this question focuses on input streams.
1int fgetc(FILE *stream)
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, the fgetc function returns the next character from the input stream pointed to bystream
. If a read error occurs, the error indicator for the stream is set and thefgetc
function returnsEOF
. C11dr §7.21.7.1 2
2 On cases 0-1-0, 1-1-1. Seems if UCHAR_MAX == UINT_MAX
, a unsigned char
, could be returned and equate to EOF
and not be due to end-of-file nor input error.
c error-handling language-lawyer
Each stream has "an error indicator that records whether a read/write error has occurred".
It is set, usually rarely, by various functions: fgetc(), fflush(), fseek(), ...
.
It is cleared by various functions: rewind(), clearerr(), fopen(), ...
.
int ferror(FILE *stream)
reports the state.
The
ferror
function returns nonzero if and only if the error indicator is set forstream
.
In this case, certainly an input error just occurred.
if (!ferror(istream))
int ch = fgetc(istream);
if (ch == EOF && ferror(istream))
puts("Input error just occurred");
Exploring fgetc()
deeper, fgetc()
does not return EOF
because the error indicator was set, but because "If a read error occurs" or end-of-file related reasons1. Usually once an error occurs (e. g. parity error on a serial stream), code does not continue reading without clearing the error, yet consider what happens when it does continue.
I see 8 situations: the error indicator set/clear prior to fgetc()
, fgetc()
returns EOF
or not, and a following ferror()
could be true or not.
int e1 = !!ferror(istream);
int eof = fgetc(istream) == EOF;
int e2 = !!ferror(istream);
Assuming no UB, are 5 of the 8 possible and not the 3 unexpected ones? especially is valid input possible with error indicator set? 2
e1 eof e2
0 0 0 Normal reading of valid data
0 0 1 Unexpected
0 1 0 End-of-file
0 1 1 Input error
1 0 0 Unexpected
1 0 1 Normal reading of valid data with error indicator set!
1 1 0 Unexpected
1 1 1 Input error or end-of-file
With the error indicator set prior to an input operation, things become complicated and clearing it beforehand simplifies code. Yet that prevents error indicator accumulation.
If codes does not clear the error indicator before hand and wants to detect if a line of input had a rare input error, it seems to make sense to test !feof()
and not ferror()
to detect.
Is checking ferror()
potentially misleading? or have I missed something about the error indicator?
char buf[80];
for (int i=0; i<(80-1); i++)
int ch = fgetc(stdin);
if (ch == EOF)
if (ferror(stdin))
puts("Input error or (Prior input error and end of file occurred)"); // ambiguous
if (feof(stdin))
puts("End of file occurred");
else
puts("Input error occurred"); // ferror() test not needed
i = 0; // ignore prior input
break;
if (ch == 'n') break;
buf[i++] = ch;
buf[i] = 0;
Similar questions
File operations with error indicator set
. This unanswered one centers on accumulating error indicator without testing fgetc()
return value (answers venture off into errno
and making a user error flag) and this one is trying to simply disambiguate a fgetc()
.
fgetc(): Is it enough to just check EOF? does not address error indicator set prior to fgetc()
.
Similar issues apply to output and I/O streams, yet this question focuses on input streams.
1int fgetc(FILE *stream)
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, the fgetc function returns the next character from the input stream pointed to bystream
. If a read error occurs, the error indicator for the stream is set and thefgetc
function returnsEOF
. C11dr §7.21.7.1 2
2 On cases 0-1-0, 1-1-1. Seems if UCHAR_MAX == UINT_MAX
, a unsigned char
, could be returned and equate to EOF
and not be due to end-of-file nor input error.
c error-handling language-lawyer
c error-handling language-lawyer
edited Nov 13 '18 at 2:16
chux
asked Nov 13 '18 at 1:53
chuxchux
81k870148
81k870148
Are you looking for a standards-based answer (perhaps language-lawyer), or a practical answer?
– Nominal Animal
Nov 13 '18 at 2:08
@NominalAnimal Closer to standards-based than only practical as I am trying to divine the error indicator mysteries to solve: whenfgetc()
returnsEOF
, is that due to a recent error, end-of-file, some wideunsigned char
or something else? LL added.
– chux
Nov 13 '18 at 2:16
FWIW, with glibc it is always either due to detecting a new end-of-file or error condition, since thefgetc()
/getc()
/getchar()
functions never examine the error flag.
– Nominal Animal
Nov 13 '18 at 2:30
@NominalAnimal Does glibc also returnEOF
on existing end-of-file flag and not just a new end-of-file?
– chux
Nov 13 '18 at 2:35
1
The behavior of glibcfgetc
was changed in version 2.28, see sourceware.org/ml/libc-alpha/2018-08/msg00003.html and sourceware.org/bugzilla/show_bug.cgi?id=1190 . We sure thought 7.21.7p2,3 mandated "sticky EOF" -- the change is described as "correct[ing] a long-standing C99 conformance bug". (However, the EOF indicator and the error indicator are two separate bits.)
– zwol
Nov 14 '18 at 17:35
|
show 7 more comments
Are you looking for a standards-based answer (perhaps language-lawyer), or a practical answer?
– Nominal Animal
Nov 13 '18 at 2:08
@NominalAnimal Closer to standards-based than only practical as I am trying to divine the error indicator mysteries to solve: whenfgetc()
returnsEOF
, is that due to a recent error, end-of-file, some wideunsigned char
or something else? LL added.
– chux
Nov 13 '18 at 2:16
FWIW, with glibc it is always either due to detecting a new end-of-file or error condition, since thefgetc()
/getc()
/getchar()
functions never examine the error flag.
– Nominal Animal
Nov 13 '18 at 2:30
@NominalAnimal Does glibc also returnEOF
on existing end-of-file flag and not just a new end-of-file?
– chux
Nov 13 '18 at 2:35
1
The behavior of glibcfgetc
was changed in version 2.28, see sourceware.org/ml/libc-alpha/2018-08/msg00003.html and sourceware.org/bugzilla/show_bug.cgi?id=1190 . We sure thought 7.21.7p2,3 mandated "sticky EOF" -- the change is described as "correct[ing] a long-standing C99 conformance bug". (However, the EOF indicator and the error indicator are two separate bits.)
– zwol
Nov 14 '18 at 17:35
Are you looking for a standards-based answer (perhaps language-lawyer), or a practical answer?
– Nominal Animal
Nov 13 '18 at 2:08
Are you looking for a standards-based answer (perhaps language-lawyer), or a practical answer?
– Nominal Animal
Nov 13 '18 at 2:08
@NominalAnimal Closer to standards-based than only practical as I am trying to divine the error indicator mysteries to solve: when
fgetc()
returns EOF
, is that due to a recent error, end-of-file, some wide unsigned char
or something else? LL added.– chux
Nov 13 '18 at 2:16
@NominalAnimal Closer to standards-based than only practical as I am trying to divine the error indicator mysteries to solve: when
fgetc()
returns EOF
, is that due to a recent error, end-of-file, some wide unsigned char
or something else? LL added.– chux
Nov 13 '18 at 2:16
FWIW, with glibc it is always either due to detecting a new end-of-file or error condition, since the
fgetc()
/getc()
/getchar()
functions never examine the error flag.– Nominal Animal
Nov 13 '18 at 2:30
FWIW, with glibc it is always either due to detecting a new end-of-file or error condition, since the
fgetc()
/getc()
/getchar()
functions never examine the error flag.– Nominal Animal
Nov 13 '18 at 2:30
@NominalAnimal Does glibc also return
EOF
on existing end-of-file flag and not just a new end-of-file?– chux
Nov 13 '18 at 2:35
@NominalAnimal Does glibc also return
EOF
on existing end-of-file flag and not just a new end-of-file?– chux
Nov 13 '18 at 2:35
1
1
The behavior of glibc
fgetc
was changed in version 2.28, see sourceware.org/ml/libc-alpha/2018-08/msg00003.html and sourceware.org/bugzilla/show_bug.cgi?id=1190 . We sure thought 7.21.7p2,3 mandated "sticky EOF" -- the change is described as "correct[ing] a long-standing C99 conformance bug". (However, the EOF indicator and the error indicator are two separate bits.)– zwol
Nov 14 '18 at 17:35
The behavior of glibc
fgetc
was changed in version 2.28, see sourceware.org/ml/libc-alpha/2018-08/msg00003.html and sourceware.org/bugzilla/show_bug.cgi?id=1190 . We sure thought 7.21.7p2,3 mandated "sticky EOF" -- the change is described as "correct[ing] a long-standing C99 conformance bug". (However, the EOF indicator and the error indicator are two separate bits.)– zwol
Nov 14 '18 at 17:35
|
show 7 more comments
4 Answers
4
active
oldest
votes
Assuming no UB, are 5 of the 8 possible and not the 3 unexpected ones?
especially is valid input possible with error indicator set?
Speaking specifically to the provisions of the standard, I'm inclined to agree with your analysis:
Few functions are specified to clear the error indicator of a stream, and
fgetc()
is not one of them. More generally, none of them are data-transfer functions. Therefore, if the error indicator is set for a stream before that stream is presented tofgetc()
for reading, then it should still be set when that function returns, all other considerations notwithstanding. That covers these cases:*1 0 0 Unexpected
1 1 0 Unexpected
1 1 1 Input error or end-of-fileIt also covers this case with respect to the expected value of the error indicator, though it does not speak to whether it can actually happen:
1 0 1 Normal reading of valid data with error indicator set!
fgetc()
is specified to returnEOF
in every situation in which it is specified to set the end-of-file indicator on a stream. Therefore, iffgetc()
returns anything other thanEOF
then it will not, on that call, have set the stream's error (or end-of-file) indicator. That covers these cases:0 0 0 Normal reading of valid data
0 0 1 UnexpectedOn the other hand, if
fgetc()
does returnEOF
then either the stream's end-of-file indicator or its error indicator should afterward be found set. But the standard distinguishes between these cases, and specifies that the user can distinguish them via thefeof()
andferror()
functions. That covers these cases:*0 1 0 End-of-file
0 1 1 Input errorFinally, I concur that none of the behavior of
fgetc()
is conditioned on the initial state of the stream's error indicator. Provided only that the stream is not initially positioned at its end, and its end-of-file indicator is not initially set, "thefgetc
function returns the next character from the input stream pointed to by stream." That establishes that this, the case of most interest, is in fact allowed:1 0 1 Normal reading of valid data with error indicator set!
However, that the case is allowed in the abstract does not imply that it can be observed in practice. The details seem unspecified, and I would expect them to depend on the implementation of the driver serving the stream in question. It is entirely possible that having once encountered an error, the driver will continue to report an error on subsequent reads until reset appropriately, and perhaps longer. From the C perspective, that would be interpreted as an (additional) error occurring on each subsequent read, and nothing in the language specifications prevents that. Not even use of one of the functions that clear a stream's error indicator.
If codes does not clear the error indicator before hand and wants to
detect if a line of input had a rare input error, it seems to make
sense to test!feof()
and notferror()
to detect.
Is checking
ferror()
potentially misleading? or have I missed something about the error indicator?
I agree that if a stream's error indicator is initially set, its end-of-file indicator is not, and reading it with fgetc()
returns EOF
, then ferror()
does not usefully distinguish between the end-of-file and error cases whereas feof()
should.
On the other hand, whether one can usefully continue to read a given stream after an error has been encountered on it depends on implementation and possibly on specific circumstances. That applies even if the error indicator is cleared via a clearerr()
call, not to mention if the error indicator is not cleared.
* Although I agree that there is an ambiguity with respect to EOF
in the event that UCHAR_MAX > INT_MAX
, I assert that that is just one of several reasons why such an implementation would be problematic. As a practical matter, therefore, I disregard such implementations as entirely hypothetical.
Additional interest in "usefully continue to read a given stream after an error has been encountered" comes from reviewing input functions for robustness. Example: my_getline(). Such functions, when called, may or may not have the error indicator set. It is outside the function's control to prevent being called when a prior error had been encountered - just likefgetc()
. Detection of input error seems to better rely onc == EOF && !feof(stdin)
thanc == EOF && ferror(...)
, so I was seeking other good ideas concerning input error.
– chux
Nov 14 '18 at 11:33
1
@chux, I agree that having performedint c = fgetc(s);
in a conforming C implementation, without knowledge of the initial state of streams
, testing for an error on that read viac == EOF && !feof(s)
is valid, and it is more reliable than its analog involvingferror
. Ifferror
is to be used instead, then the stream's error indicator must first be ensured clear. I am unaware of any alternatives fundamentally different from these.
– John Bollinger
Nov 14 '18 at 14:14
add a comment |
Everything you say seems right, and ch==EOF && !feof(f)
is the right way to check for new errors without interfering with error accumulation.
add a comment |
Here is a very crude, very minimal program to explore the behaviour of GNU C library with respect to fgetc()
, ferror()
, and feof()
, as requested by OP in a comment:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
static volatile sig_atomic_t interrupted = 0;
static void interrupt_handler(int signum)
interrupted = 1;
static int install_interrupt(const int signum)
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = interrupt_handler;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return -1;
return 0;
int main(void)
!feof(stdin))
fprintf(stderr, "Expected error and end-of-file states; aborting.n");
return EXIT_FAILURE;
fprintf(stderr, "n");
fprintf(stderr, "Testing fgetc() when stream in error and end-of-file state.n");
fprintf(stderr, "Please type something, then press Enter.n");
n = 0;
c = fgetc(stdin);
while (c != EOF && c != 'n')
n++;
c = fgetc(stdin);
if (c == EOF)
fprintf(stderr, "Further input is not possible.n");
return EXIT_FAILURE;
else
fprintf(stderr, "Further input is possible: %d characters (including Enter) read.n", n + 1);
return EXIT_SUCCESS;
When I compile and run the above on Linux, the program will output
Testing stream error state. Please wait.
fgetc(stdin) returned EOF.
ferror(stdin) returns 1.
feof(stdin) returns 0.
The error state was caused by having signal delivery interrupt an fgetc(stdin)
call. As you can see, it does cause ferror(stdin)
to return nonzero. Note that feof(stdin)
returns 0, though.
The output continues:
Testing stream end-of-input state. Please press Ctrl+D.
Pressing Ctrl+C yields output
fgetc(stdin) returned EOF.
ferror(stdin) returns 1.
feof(stdin) returns 1.
At this point, the standard input is in both error and end-of-file states. The output continues:
Testing fgetc() when stream in error and end-of-file state.
Please type something, then press Enter.
If we now type say O K Enter, we get
Further input is possible: 3 characters (including Enter) read.
This proves that at least the GNU C library implementation does not check the stream error or end-of-file status at all. It will simply try to read more data (using the underlying read()
operation in POSIXy systems).
I also found similar results, yet tend to think it is non-conforming to the standard. (IOWs, a bug in that version). I also noticed that the end-of-file flag was cleared byfgetc()
call with 5 key"xn"
, Ctrl d,".n"
sequence with the'.'
key.
– chux
Nov 14 '18 at 18:57
@chux: On POSIXy systems, the EOF behaviour kinda makes sense, because if the file gets appended to after we read it till the end, we'll want to get the appended data; and since there is nocleareof()
function, reopening the file would be the only way to do that otherwise. Whether any of this is standards-conformant, I have no opinion: I've decided to steer away from language-lawyer. (If it is a bug in glibc, getting it fixed is a quixotic fight against people who believe they know the standards better than anyone else. Not worth the effort, IMO.)
– Nominal Animal
Nov 14 '18 at 19:09
Re: "there is nocleareof()
". Would notclearerr(istream)
be sufficient in lieu of reopening the file? IAC, thanks for the exploring the corners of C I/O.
– chux
Nov 14 '18 at 19:14
@NominalAnimal For the record, if you believe you have found a bug in glibc and you don't feel like arguing with Joseph and/or Paul Eggert about it, you can email me and I'll do it. Ulrich is no longer involved.
– zwol
Nov 14 '18 at 19:27
@zwol: Yeah, libio/getc.c:_IO_getc() and libio/getc_u.c:__getc_unlocked() are still missing an_IO_ferror_unlocked()
check in HEAD. I'd recommend turning_IO_getc_unlocked()
into a function that does the check, and returns__getc_unlocked_body()
. Also, an RFC to the devs: perhaps change_IO_EOF_SEEN
in libio/bits/types/struct_FILE.h to0x0030
, so thatfeof()
returns nonzero if eof or error.
– Nominal Animal
Nov 14 '18 at 20:58
|
show 2 more comments
My reading of the standard is that it doesn't explicitly say fgetc
is allowed to return a non-EOF
value if the error indicator was already set on the stream on entry, but it doesn't explicitly say that it can't, either. I sympathize with Nominal Animal's observation (which I shall hoist from comments on his answer in case it gets deleted or moved to chat; allow me to grind my personal axe for a moment and observe that the policy of treating comments as "ephemeral" is harmful and should be abolished):
IMHO the standard is then bass-ackwards: there is no practical need for EOF to be sticky, but if error is not sticky, then there is a real risk of accidentally missing errors.
However, if existing implementations are all consistently not treating error as sticky, changing the behavior will be very hard to sell to the committee. Therefore, I am soliciting tests from the community:
Below is a shortened, non-interactive version of Nominal Animal's test program. It only looks at the behavior of fgetc
after a read error, not after EOF. It uses SIGALRM
to interrupt a read, instead of control-C, so you don't have to do anything but run it.
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
static _Noreturn void
perror_exit (const char *msg)
perror (msg);
exit (1);
static void
handler (int unused)
int
main (void)
struct sigaction sa;
int pipefd[2];
FILE *fp;
int ch, pa;
setvbuf (stdout, 0, _IOLBF, 0);
sa.sa_handler = handler;
sa.sa_flags = 0; /* DO interrupt blocking system calls */
sigemptyset (&sa.sa_mask);
if (sigaction (SIGALRM, &sa, 0))
perror_exit ("sigaction");
if (pipe (pipefd))
perror_exit ("pipe");
fp = fdopen (pipefd[0], "r");
if (!fp)
perror_exit ("fdopen");
printf ("before fgetc 1, feof = %d ferror = %dn",
feof (fp), ferror (fp));
alarm (1);
ch = fgetc (fp);
if (ch == EOF)
printf ("after fgetc 1, ch = EOF feof = %d ferror = %dn",
feof (fp), ferror (fp));
else
printf ("after fgetc 1, ch = '%c' feof = %d ferror = %dn",
ch, feof (fp), ferror (fp));
write (pipefd[1], "x", 1);
alarm (1);
ch = fgetc (fp);
pa = alarm (0);
printf ("after fgetc 2, alarm %sn",
pa ? "did not fire" : "fired");
if (ch == EOF)
printf ("after fgetc 2, ch = EOF feof = %d ferror = %dn",
feof (fp), ferror (fp));
else
printf ("after fgetc 2, ch = '%c' feof = %d ferror = %dn",
ch, feof (fp), ferror (fp));
return 0;
On all of the Unixes I can get at at the moment, this program's output is consistent with John Bollinger's observation that
the case of most interest, is in fact allowed:
1 0 1 Normal reading of valid data with error indicator set!
I would particularly like to know what this program prints when run on alternative Linux-based C libraries (e.g. musl, bionic); Unixes which are not Linux nor are they BSD-phylum; and Windows. If you've got anything even more exotic please try that too. I'm marking this post community wiki; please edit it to add test results.
The test program should be acceptable to any C89-compliant compiler for an environment where unistd.h
exists and signal.h
defines sigaction
, except for one use of the C11 _Noreturn
keyword which is only to squelch warnings. If your compiler complains about _Noreturn
, compile with -D_Noreturn=
; the results will not be affected. If you don't have unistd.h
, the test program will not do anything meaningful in your environment. If you don't have sigaction
you may be able to adapt the program to use alternative interfaces, but you need to persuade SIGALRM
to interrupt a blocking read
somehow.
Results
before fgetc 1, feof = 0 ferror = 0
after fgetc 1, ch = EOF feof = 0 ferror = 1
after fgetc 2, alarm did not fire
after fgetc 2, ch = 'x' feof = 0 ferror = 1
("normal reading of valid data with error indicator set")
- Linux with glibc 2.27
- NetBSD 7.1.2
- FreeBSD 11.2-RELEASE-p4
- macOS 10.14, clang-1000.10.44.2
- macOS 10.14, gcc 8.2.0 (Homebrew)
.
before fgetc 1, feof = 0 ferror = 0
after fgetc 1, ch = EOF feof = 0 ferror = 1
after fgetc 2, alarm did not fire
after fgetc 2, ch = EOF feof = 0 ferror = 1
("sticky error" behavior: fgetc(fp)
immediately returns EOF without calling read
when ferror(fp)
is true on entry)
- Plan 9 (compiler does not recognize _Noreturn)
.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53272650%2fhow-a-stream-error-indicator-affects-following-input-code%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
Assuming no UB, are 5 of the 8 possible and not the 3 unexpected ones?
especially is valid input possible with error indicator set?
Speaking specifically to the provisions of the standard, I'm inclined to agree with your analysis:
Few functions are specified to clear the error indicator of a stream, and
fgetc()
is not one of them. More generally, none of them are data-transfer functions. Therefore, if the error indicator is set for a stream before that stream is presented tofgetc()
for reading, then it should still be set when that function returns, all other considerations notwithstanding. That covers these cases:*1 0 0 Unexpected
1 1 0 Unexpected
1 1 1 Input error or end-of-fileIt also covers this case with respect to the expected value of the error indicator, though it does not speak to whether it can actually happen:
1 0 1 Normal reading of valid data with error indicator set!
fgetc()
is specified to returnEOF
in every situation in which it is specified to set the end-of-file indicator on a stream. Therefore, iffgetc()
returns anything other thanEOF
then it will not, on that call, have set the stream's error (or end-of-file) indicator. That covers these cases:0 0 0 Normal reading of valid data
0 0 1 UnexpectedOn the other hand, if
fgetc()
does returnEOF
then either the stream's end-of-file indicator or its error indicator should afterward be found set. But the standard distinguishes between these cases, and specifies that the user can distinguish them via thefeof()
andferror()
functions. That covers these cases:*0 1 0 End-of-file
0 1 1 Input errorFinally, I concur that none of the behavior of
fgetc()
is conditioned on the initial state of the stream's error indicator. Provided only that the stream is not initially positioned at its end, and its end-of-file indicator is not initially set, "thefgetc
function returns the next character from the input stream pointed to by stream." That establishes that this, the case of most interest, is in fact allowed:1 0 1 Normal reading of valid data with error indicator set!
However, that the case is allowed in the abstract does not imply that it can be observed in practice. The details seem unspecified, and I would expect them to depend on the implementation of the driver serving the stream in question. It is entirely possible that having once encountered an error, the driver will continue to report an error on subsequent reads until reset appropriately, and perhaps longer. From the C perspective, that would be interpreted as an (additional) error occurring on each subsequent read, and nothing in the language specifications prevents that. Not even use of one of the functions that clear a stream's error indicator.
If codes does not clear the error indicator before hand and wants to
detect if a line of input had a rare input error, it seems to make
sense to test!feof()
and notferror()
to detect.
Is checking
ferror()
potentially misleading? or have I missed something about the error indicator?
I agree that if a stream's error indicator is initially set, its end-of-file indicator is not, and reading it with fgetc()
returns EOF
, then ferror()
does not usefully distinguish between the end-of-file and error cases whereas feof()
should.
On the other hand, whether one can usefully continue to read a given stream after an error has been encountered on it depends on implementation and possibly on specific circumstances. That applies even if the error indicator is cleared via a clearerr()
call, not to mention if the error indicator is not cleared.
* Although I agree that there is an ambiguity with respect to EOF
in the event that UCHAR_MAX > INT_MAX
, I assert that that is just one of several reasons why such an implementation would be problematic. As a practical matter, therefore, I disregard such implementations as entirely hypothetical.
Additional interest in "usefully continue to read a given stream after an error has been encountered" comes from reviewing input functions for robustness. Example: my_getline(). Such functions, when called, may or may not have the error indicator set. It is outside the function's control to prevent being called when a prior error had been encountered - just likefgetc()
. Detection of input error seems to better rely onc == EOF && !feof(stdin)
thanc == EOF && ferror(...)
, so I was seeking other good ideas concerning input error.
– chux
Nov 14 '18 at 11:33
1
@chux, I agree that having performedint c = fgetc(s);
in a conforming C implementation, without knowledge of the initial state of streams
, testing for an error on that read viac == EOF && !feof(s)
is valid, and it is more reliable than its analog involvingferror
. Ifferror
is to be used instead, then the stream's error indicator must first be ensured clear. I am unaware of any alternatives fundamentally different from these.
– John Bollinger
Nov 14 '18 at 14:14
add a comment |
Assuming no UB, are 5 of the 8 possible and not the 3 unexpected ones?
especially is valid input possible with error indicator set?
Speaking specifically to the provisions of the standard, I'm inclined to agree with your analysis:
Few functions are specified to clear the error indicator of a stream, and
fgetc()
is not one of them. More generally, none of them are data-transfer functions. Therefore, if the error indicator is set for a stream before that stream is presented tofgetc()
for reading, then it should still be set when that function returns, all other considerations notwithstanding. That covers these cases:*1 0 0 Unexpected
1 1 0 Unexpected
1 1 1 Input error or end-of-fileIt also covers this case with respect to the expected value of the error indicator, though it does not speak to whether it can actually happen:
1 0 1 Normal reading of valid data with error indicator set!
fgetc()
is specified to returnEOF
in every situation in which it is specified to set the end-of-file indicator on a stream. Therefore, iffgetc()
returns anything other thanEOF
then it will not, on that call, have set the stream's error (or end-of-file) indicator. That covers these cases:0 0 0 Normal reading of valid data
0 0 1 UnexpectedOn the other hand, if
fgetc()
does returnEOF
then either the stream's end-of-file indicator or its error indicator should afterward be found set. But the standard distinguishes between these cases, and specifies that the user can distinguish them via thefeof()
andferror()
functions. That covers these cases:*0 1 0 End-of-file
0 1 1 Input errorFinally, I concur that none of the behavior of
fgetc()
is conditioned on the initial state of the stream's error indicator. Provided only that the stream is not initially positioned at its end, and its end-of-file indicator is not initially set, "thefgetc
function returns the next character from the input stream pointed to by stream." That establishes that this, the case of most interest, is in fact allowed:1 0 1 Normal reading of valid data with error indicator set!
However, that the case is allowed in the abstract does not imply that it can be observed in practice. The details seem unspecified, and I would expect them to depend on the implementation of the driver serving the stream in question. It is entirely possible that having once encountered an error, the driver will continue to report an error on subsequent reads until reset appropriately, and perhaps longer. From the C perspective, that would be interpreted as an (additional) error occurring on each subsequent read, and nothing in the language specifications prevents that. Not even use of one of the functions that clear a stream's error indicator.
If codes does not clear the error indicator before hand and wants to
detect if a line of input had a rare input error, it seems to make
sense to test!feof()
and notferror()
to detect.
Is checking
ferror()
potentially misleading? or have I missed something about the error indicator?
I agree that if a stream's error indicator is initially set, its end-of-file indicator is not, and reading it with fgetc()
returns EOF
, then ferror()
does not usefully distinguish between the end-of-file and error cases whereas feof()
should.
On the other hand, whether one can usefully continue to read a given stream after an error has been encountered on it depends on implementation and possibly on specific circumstances. That applies even if the error indicator is cleared via a clearerr()
call, not to mention if the error indicator is not cleared.
* Although I agree that there is an ambiguity with respect to EOF
in the event that UCHAR_MAX > INT_MAX
, I assert that that is just one of several reasons why such an implementation would be problematic. As a practical matter, therefore, I disregard such implementations as entirely hypothetical.
Additional interest in "usefully continue to read a given stream after an error has been encountered" comes from reviewing input functions for robustness. Example: my_getline(). Such functions, when called, may or may not have the error indicator set. It is outside the function's control to prevent being called when a prior error had been encountered - just likefgetc()
. Detection of input error seems to better rely onc == EOF && !feof(stdin)
thanc == EOF && ferror(...)
, so I was seeking other good ideas concerning input error.
– chux
Nov 14 '18 at 11:33
1
@chux, I agree that having performedint c = fgetc(s);
in a conforming C implementation, without knowledge of the initial state of streams
, testing for an error on that read viac == EOF && !feof(s)
is valid, and it is more reliable than its analog involvingferror
. Ifferror
is to be used instead, then the stream's error indicator must first be ensured clear. I am unaware of any alternatives fundamentally different from these.
– John Bollinger
Nov 14 '18 at 14:14
add a comment |
Assuming no UB, are 5 of the 8 possible and not the 3 unexpected ones?
especially is valid input possible with error indicator set?
Speaking specifically to the provisions of the standard, I'm inclined to agree with your analysis:
Few functions are specified to clear the error indicator of a stream, and
fgetc()
is not one of them. More generally, none of them are data-transfer functions. Therefore, if the error indicator is set for a stream before that stream is presented tofgetc()
for reading, then it should still be set when that function returns, all other considerations notwithstanding. That covers these cases:*1 0 0 Unexpected
1 1 0 Unexpected
1 1 1 Input error or end-of-fileIt also covers this case with respect to the expected value of the error indicator, though it does not speak to whether it can actually happen:
1 0 1 Normal reading of valid data with error indicator set!
fgetc()
is specified to returnEOF
in every situation in which it is specified to set the end-of-file indicator on a stream. Therefore, iffgetc()
returns anything other thanEOF
then it will not, on that call, have set the stream's error (or end-of-file) indicator. That covers these cases:0 0 0 Normal reading of valid data
0 0 1 UnexpectedOn the other hand, if
fgetc()
does returnEOF
then either the stream's end-of-file indicator or its error indicator should afterward be found set. But the standard distinguishes between these cases, and specifies that the user can distinguish them via thefeof()
andferror()
functions. That covers these cases:*0 1 0 End-of-file
0 1 1 Input errorFinally, I concur that none of the behavior of
fgetc()
is conditioned on the initial state of the stream's error indicator. Provided only that the stream is not initially positioned at its end, and its end-of-file indicator is not initially set, "thefgetc
function returns the next character from the input stream pointed to by stream." That establishes that this, the case of most interest, is in fact allowed:1 0 1 Normal reading of valid data with error indicator set!
However, that the case is allowed in the abstract does not imply that it can be observed in practice. The details seem unspecified, and I would expect them to depend on the implementation of the driver serving the stream in question. It is entirely possible that having once encountered an error, the driver will continue to report an error on subsequent reads until reset appropriately, and perhaps longer. From the C perspective, that would be interpreted as an (additional) error occurring on each subsequent read, and nothing in the language specifications prevents that. Not even use of one of the functions that clear a stream's error indicator.
If codes does not clear the error indicator before hand and wants to
detect if a line of input had a rare input error, it seems to make
sense to test!feof()
and notferror()
to detect.
Is checking
ferror()
potentially misleading? or have I missed something about the error indicator?
I agree that if a stream's error indicator is initially set, its end-of-file indicator is not, and reading it with fgetc()
returns EOF
, then ferror()
does not usefully distinguish between the end-of-file and error cases whereas feof()
should.
On the other hand, whether one can usefully continue to read a given stream after an error has been encountered on it depends on implementation and possibly on specific circumstances. That applies even if the error indicator is cleared via a clearerr()
call, not to mention if the error indicator is not cleared.
* Although I agree that there is an ambiguity with respect to EOF
in the event that UCHAR_MAX > INT_MAX
, I assert that that is just one of several reasons why such an implementation would be problematic. As a practical matter, therefore, I disregard such implementations as entirely hypothetical.
Assuming no UB, are 5 of the 8 possible and not the 3 unexpected ones?
especially is valid input possible with error indicator set?
Speaking specifically to the provisions of the standard, I'm inclined to agree with your analysis:
Few functions are specified to clear the error indicator of a stream, and
fgetc()
is not one of them. More generally, none of them are data-transfer functions. Therefore, if the error indicator is set for a stream before that stream is presented tofgetc()
for reading, then it should still be set when that function returns, all other considerations notwithstanding. That covers these cases:*1 0 0 Unexpected
1 1 0 Unexpected
1 1 1 Input error or end-of-fileIt also covers this case with respect to the expected value of the error indicator, though it does not speak to whether it can actually happen:
1 0 1 Normal reading of valid data with error indicator set!
fgetc()
is specified to returnEOF
in every situation in which it is specified to set the end-of-file indicator on a stream. Therefore, iffgetc()
returns anything other thanEOF
then it will not, on that call, have set the stream's error (or end-of-file) indicator. That covers these cases:0 0 0 Normal reading of valid data
0 0 1 UnexpectedOn the other hand, if
fgetc()
does returnEOF
then either the stream's end-of-file indicator or its error indicator should afterward be found set. But the standard distinguishes between these cases, and specifies that the user can distinguish them via thefeof()
andferror()
functions. That covers these cases:*0 1 0 End-of-file
0 1 1 Input errorFinally, I concur that none of the behavior of
fgetc()
is conditioned on the initial state of the stream's error indicator. Provided only that the stream is not initially positioned at its end, and its end-of-file indicator is not initially set, "thefgetc
function returns the next character from the input stream pointed to by stream." That establishes that this, the case of most interest, is in fact allowed:1 0 1 Normal reading of valid data with error indicator set!
However, that the case is allowed in the abstract does not imply that it can be observed in practice. The details seem unspecified, and I would expect them to depend on the implementation of the driver serving the stream in question. It is entirely possible that having once encountered an error, the driver will continue to report an error on subsequent reads until reset appropriately, and perhaps longer. From the C perspective, that would be interpreted as an (additional) error occurring on each subsequent read, and nothing in the language specifications prevents that. Not even use of one of the functions that clear a stream's error indicator.
If codes does not clear the error indicator before hand and wants to
detect if a line of input had a rare input error, it seems to make
sense to test!feof()
and notferror()
to detect.
Is checking
ferror()
potentially misleading? or have I missed something about the error indicator?
I agree that if a stream's error indicator is initially set, its end-of-file indicator is not, and reading it with fgetc()
returns EOF
, then ferror()
does not usefully distinguish between the end-of-file and error cases whereas feof()
should.
On the other hand, whether one can usefully continue to read a given stream after an error has been encountered on it depends on implementation and possibly on specific circumstances. That applies even if the error indicator is cleared via a clearerr()
call, not to mention if the error indicator is not cleared.
* Although I agree that there is an ambiguity with respect to EOF
in the event that UCHAR_MAX > INT_MAX
, I assert that that is just one of several reasons why such an implementation would be problematic. As a practical matter, therefore, I disregard such implementations as entirely hypothetical.
edited Nov 14 '18 at 18:30
answered Nov 13 '18 at 6:14
John BollingerJohn Bollinger
79.2k74074
79.2k74074
Additional interest in "usefully continue to read a given stream after an error has been encountered" comes from reviewing input functions for robustness. Example: my_getline(). Such functions, when called, may or may not have the error indicator set. It is outside the function's control to prevent being called when a prior error had been encountered - just likefgetc()
. Detection of input error seems to better rely onc == EOF && !feof(stdin)
thanc == EOF && ferror(...)
, so I was seeking other good ideas concerning input error.
– chux
Nov 14 '18 at 11:33
1
@chux, I agree that having performedint c = fgetc(s);
in a conforming C implementation, without knowledge of the initial state of streams
, testing for an error on that read viac == EOF && !feof(s)
is valid, and it is more reliable than its analog involvingferror
. Ifferror
is to be used instead, then the stream's error indicator must first be ensured clear. I am unaware of any alternatives fundamentally different from these.
– John Bollinger
Nov 14 '18 at 14:14
add a comment |
Additional interest in "usefully continue to read a given stream after an error has been encountered" comes from reviewing input functions for robustness. Example: my_getline(). Such functions, when called, may or may not have the error indicator set. It is outside the function's control to prevent being called when a prior error had been encountered - just likefgetc()
. Detection of input error seems to better rely onc == EOF && !feof(stdin)
thanc == EOF && ferror(...)
, so I was seeking other good ideas concerning input error.
– chux
Nov 14 '18 at 11:33
1
@chux, I agree that having performedint c = fgetc(s);
in a conforming C implementation, without knowledge of the initial state of streams
, testing for an error on that read viac == EOF && !feof(s)
is valid, and it is more reliable than its analog involvingferror
. Ifferror
is to be used instead, then the stream's error indicator must first be ensured clear. I am unaware of any alternatives fundamentally different from these.
– John Bollinger
Nov 14 '18 at 14:14
Additional interest in "usefully continue to read a given stream after an error has been encountered" comes from reviewing input functions for robustness. Example: my_getline(). Such functions, when called, may or may not have the error indicator set. It is outside the function's control to prevent being called when a prior error had been encountered - just like
fgetc()
. Detection of input error seems to better rely on c == EOF && !feof(stdin)
than c == EOF && ferror(...)
, so I was seeking other good ideas concerning input error.– chux
Nov 14 '18 at 11:33
Additional interest in "usefully continue to read a given stream after an error has been encountered" comes from reviewing input functions for robustness. Example: my_getline(). Such functions, when called, may or may not have the error indicator set. It is outside the function's control to prevent being called when a prior error had been encountered - just like
fgetc()
. Detection of input error seems to better rely on c == EOF && !feof(stdin)
than c == EOF && ferror(...)
, so I was seeking other good ideas concerning input error.– chux
Nov 14 '18 at 11:33
1
1
@chux, I agree that having performed
int c = fgetc(s);
in a conforming C implementation, without knowledge of the initial state of stream s
, testing for an error on that read via c == EOF && !feof(s)
is valid, and it is more reliable than its analog involving ferror
. If ferror
is to be used instead, then the stream's error indicator must first be ensured clear. I am unaware of any alternatives fundamentally different from these.– John Bollinger
Nov 14 '18 at 14:14
@chux, I agree that having performed
int c = fgetc(s);
in a conforming C implementation, without knowledge of the initial state of stream s
, testing for an error on that read via c == EOF && !feof(s)
is valid, and it is more reliable than its analog involving ferror
. If ferror
is to be used instead, then the stream's error indicator must first be ensured clear. I am unaware of any alternatives fundamentally different from these.– John Bollinger
Nov 14 '18 at 14:14
add a comment |
Everything you say seems right, and ch==EOF && !feof(f)
is the right way to check for new errors without interfering with error accumulation.
add a comment |
Everything you say seems right, and ch==EOF && !feof(f)
is the right way to check for new errors without interfering with error accumulation.
add a comment |
Everything you say seems right, and ch==EOF && !feof(f)
is the right way to check for new errors without interfering with error accumulation.
Everything you say seems right, and ch==EOF && !feof(f)
is the right way to check for new errors without interfering with error accumulation.
answered Nov 13 '18 at 4:22
R..R..
155k26257561
155k26257561
add a comment |
add a comment |
Here is a very crude, very minimal program to explore the behaviour of GNU C library with respect to fgetc()
, ferror()
, and feof()
, as requested by OP in a comment:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
static volatile sig_atomic_t interrupted = 0;
static void interrupt_handler(int signum)
interrupted = 1;
static int install_interrupt(const int signum)
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = interrupt_handler;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return -1;
return 0;
int main(void)
!feof(stdin))
fprintf(stderr, "Expected error and end-of-file states; aborting.n");
return EXIT_FAILURE;
fprintf(stderr, "n");
fprintf(stderr, "Testing fgetc() when stream in error and end-of-file state.n");
fprintf(stderr, "Please type something, then press Enter.n");
n = 0;
c = fgetc(stdin);
while (c != EOF && c != 'n')
n++;
c = fgetc(stdin);
if (c == EOF)
fprintf(stderr, "Further input is not possible.n");
return EXIT_FAILURE;
else
fprintf(stderr, "Further input is possible: %d characters (including Enter) read.n", n + 1);
return EXIT_SUCCESS;
When I compile and run the above on Linux, the program will output
Testing stream error state. Please wait.
fgetc(stdin) returned EOF.
ferror(stdin) returns 1.
feof(stdin) returns 0.
The error state was caused by having signal delivery interrupt an fgetc(stdin)
call. As you can see, it does cause ferror(stdin)
to return nonzero. Note that feof(stdin)
returns 0, though.
The output continues:
Testing stream end-of-input state. Please press Ctrl+D.
Pressing Ctrl+C yields output
fgetc(stdin) returned EOF.
ferror(stdin) returns 1.
feof(stdin) returns 1.
At this point, the standard input is in both error and end-of-file states. The output continues:
Testing fgetc() when stream in error and end-of-file state.
Please type something, then press Enter.
If we now type say O K Enter, we get
Further input is possible: 3 characters (including Enter) read.
This proves that at least the GNU C library implementation does not check the stream error or end-of-file status at all. It will simply try to read more data (using the underlying read()
operation in POSIXy systems).
I also found similar results, yet tend to think it is non-conforming to the standard. (IOWs, a bug in that version). I also noticed that the end-of-file flag was cleared byfgetc()
call with 5 key"xn"
, Ctrl d,".n"
sequence with the'.'
key.
– chux
Nov 14 '18 at 18:57
@chux: On POSIXy systems, the EOF behaviour kinda makes sense, because if the file gets appended to after we read it till the end, we'll want to get the appended data; and since there is nocleareof()
function, reopening the file would be the only way to do that otherwise. Whether any of this is standards-conformant, I have no opinion: I've decided to steer away from language-lawyer. (If it is a bug in glibc, getting it fixed is a quixotic fight against people who believe they know the standards better than anyone else. Not worth the effort, IMO.)
– Nominal Animal
Nov 14 '18 at 19:09
Re: "there is nocleareof()
". Would notclearerr(istream)
be sufficient in lieu of reopening the file? IAC, thanks for the exploring the corners of C I/O.
– chux
Nov 14 '18 at 19:14
@NominalAnimal For the record, if you believe you have found a bug in glibc and you don't feel like arguing with Joseph and/or Paul Eggert about it, you can email me and I'll do it. Ulrich is no longer involved.
– zwol
Nov 14 '18 at 19:27
@zwol: Yeah, libio/getc.c:_IO_getc() and libio/getc_u.c:__getc_unlocked() are still missing an_IO_ferror_unlocked()
check in HEAD. I'd recommend turning_IO_getc_unlocked()
into a function that does the check, and returns__getc_unlocked_body()
. Also, an RFC to the devs: perhaps change_IO_EOF_SEEN
in libio/bits/types/struct_FILE.h to0x0030
, so thatfeof()
returns nonzero if eof or error.
– Nominal Animal
Nov 14 '18 at 20:58
|
show 2 more comments
Here is a very crude, very minimal program to explore the behaviour of GNU C library with respect to fgetc()
, ferror()
, and feof()
, as requested by OP in a comment:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
static volatile sig_atomic_t interrupted = 0;
static void interrupt_handler(int signum)
interrupted = 1;
static int install_interrupt(const int signum)
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = interrupt_handler;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return -1;
return 0;
int main(void)
!feof(stdin))
fprintf(stderr, "Expected error and end-of-file states; aborting.n");
return EXIT_FAILURE;
fprintf(stderr, "n");
fprintf(stderr, "Testing fgetc() when stream in error and end-of-file state.n");
fprintf(stderr, "Please type something, then press Enter.n");
n = 0;
c = fgetc(stdin);
while (c != EOF && c != 'n')
n++;
c = fgetc(stdin);
if (c == EOF)
fprintf(stderr, "Further input is not possible.n");
return EXIT_FAILURE;
else
fprintf(stderr, "Further input is possible: %d characters (including Enter) read.n", n + 1);
return EXIT_SUCCESS;
When I compile and run the above on Linux, the program will output
Testing stream error state. Please wait.
fgetc(stdin) returned EOF.
ferror(stdin) returns 1.
feof(stdin) returns 0.
The error state was caused by having signal delivery interrupt an fgetc(stdin)
call. As you can see, it does cause ferror(stdin)
to return nonzero. Note that feof(stdin)
returns 0, though.
The output continues:
Testing stream end-of-input state. Please press Ctrl+D.
Pressing Ctrl+C yields output
fgetc(stdin) returned EOF.
ferror(stdin) returns 1.
feof(stdin) returns 1.
At this point, the standard input is in both error and end-of-file states. The output continues:
Testing fgetc() when stream in error and end-of-file state.
Please type something, then press Enter.
If we now type say O K Enter, we get
Further input is possible: 3 characters (including Enter) read.
This proves that at least the GNU C library implementation does not check the stream error or end-of-file status at all. It will simply try to read more data (using the underlying read()
operation in POSIXy systems).
I also found similar results, yet tend to think it is non-conforming to the standard. (IOWs, a bug in that version). I also noticed that the end-of-file flag was cleared byfgetc()
call with 5 key"xn"
, Ctrl d,".n"
sequence with the'.'
key.
– chux
Nov 14 '18 at 18:57
@chux: On POSIXy systems, the EOF behaviour kinda makes sense, because if the file gets appended to after we read it till the end, we'll want to get the appended data; and since there is nocleareof()
function, reopening the file would be the only way to do that otherwise. Whether any of this is standards-conformant, I have no opinion: I've decided to steer away from language-lawyer. (If it is a bug in glibc, getting it fixed is a quixotic fight against people who believe they know the standards better than anyone else. Not worth the effort, IMO.)
– Nominal Animal
Nov 14 '18 at 19:09
Re: "there is nocleareof()
". Would notclearerr(istream)
be sufficient in lieu of reopening the file? IAC, thanks for the exploring the corners of C I/O.
– chux
Nov 14 '18 at 19:14
@NominalAnimal For the record, if you believe you have found a bug in glibc and you don't feel like arguing with Joseph and/or Paul Eggert about it, you can email me and I'll do it. Ulrich is no longer involved.
– zwol
Nov 14 '18 at 19:27
@zwol: Yeah, libio/getc.c:_IO_getc() and libio/getc_u.c:__getc_unlocked() are still missing an_IO_ferror_unlocked()
check in HEAD. I'd recommend turning_IO_getc_unlocked()
into a function that does the check, and returns__getc_unlocked_body()
. Also, an RFC to the devs: perhaps change_IO_EOF_SEEN
in libio/bits/types/struct_FILE.h to0x0030
, so thatfeof()
returns nonzero if eof or error.
– Nominal Animal
Nov 14 '18 at 20:58
|
show 2 more comments
Here is a very crude, very minimal program to explore the behaviour of GNU C library with respect to fgetc()
, ferror()
, and feof()
, as requested by OP in a comment:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
static volatile sig_atomic_t interrupted = 0;
static void interrupt_handler(int signum)
interrupted = 1;
static int install_interrupt(const int signum)
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = interrupt_handler;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return -1;
return 0;
int main(void)
!feof(stdin))
fprintf(stderr, "Expected error and end-of-file states; aborting.n");
return EXIT_FAILURE;
fprintf(stderr, "n");
fprintf(stderr, "Testing fgetc() when stream in error and end-of-file state.n");
fprintf(stderr, "Please type something, then press Enter.n");
n = 0;
c = fgetc(stdin);
while (c != EOF && c != 'n')
n++;
c = fgetc(stdin);
if (c == EOF)
fprintf(stderr, "Further input is not possible.n");
return EXIT_FAILURE;
else
fprintf(stderr, "Further input is possible: %d characters (including Enter) read.n", n + 1);
return EXIT_SUCCESS;
When I compile and run the above on Linux, the program will output
Testing stream error state. Please wait.
fgetc(stdin) returned EOF.
ferror(stdin) returns 1.
feof(stdin) returns 0.
The error state was caused by having signal delivery interrupt an fgetc(stdin)
call. As you can see, it does cause ferror(stdin)
to return nonzero. Note that feof(stdin)
returns 0, though.
The output continues:
Testing stream end-of-input state. Please press Ctrl+D.
Pressing Ctrl+C yields output
fgetc(stdin) returned EOF.
ferror(stdin) returns 1.
feof(stdin) returns 1.
At this point, the standard input is in both error and end-of-file states. The output continues:
Testing fgetc() when stream in error and end-of-file state.
Please type something, then press Enter.
If we now type say O K Enter, we get
Further input is possible: 3 characters (including Enter) read.
This proves that at least the GNU C library implementation does not check the stream error or end-of-file status at all. It will simply try to read more data (using the underlying read()
operation in POSIXy systems).
Here is a very crude, very minimal program to explore the behaviour of GNU C library with respect to fgetc()
, ferror()
, and feof()
, as requested by OP in a comment:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
static volatile sig_atomic_t interrupted = 0;
static void interrupt_handler(int signum)
interrupted = 1;
static int install_interrupt(const int signum)
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = interrupt_handler;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return -1;
return 0;
int main(void)
!feof(stdin))
fprintf(stderr, "Expected error and end-of-file states; aborting.n");
return EXIT_FAILURE;
fprintf(stderr, "n");
fprintf(stderr, "Testing fgetc() when stream in error and end-of-file state.n");
fprintf(stderr, "Please type something, then press Enter.n");
n = 0;
c = fgetc(stdin);
while (c != EOF && c != 'n')
n++;
c = fgetc(stdin);
if (c == EOF)
fprintf(stderr, "Further input is not possible.n");
return EXIT_FAILURE;
else
fprintf(stderr, "Further input is possible: %d characters (including Enter) read.n", n + 1);
return EXIT_SUCCESS;
When I compile and run the above on Linux, the program will output
Testing stream error state. Please wait.
fgetc(stdin) returned EOF.
ferror(stdin) returns 1.
feof(stdin) returns 0.
The error state was caused by having signal delivery interrupt an fgetc(stdin)
call. As you can see, it does cause ferror(stdin)
to return nonzero. Note that feof(stdin)
returns 0, though.
The output continues:
Testing stream end-of-input state. Please press Ctrl+D.
Pressing Ctrl+C yields output
fgetc(stdin) returned EOF.
ferror(stdin) returns 1.
feof(stdin) returns 1.
At this point, the standard input is in both error and end-of-file states. The output continues:
Testing fgetc() when stream in error and end-of-file state.
Please type something, then press Enter.
If we now type say O K Enter, we get
Further input is possible: 3 characters (including Enter) read.
This proves that at least the GNU C library implementation does not check the stream error or end-of-file status at all. It will simply try to read more data (using the underlying read()
operation in POSIXy systems).
answered Nov 14 '18 at 16:04
Nominal AnimalNominal Animal
29.5k33360
29.5k33360
I also found similar results, yet tend to think it is non-conforming to the standard. (IOWs, a bug in that version). I also noticed that the end-of-file flag was cleared byfgetc()
call with 5 key"xn"
, Ctrl d,".n"
sequence with the'.'
key.
– chux
Nov 14 '18 at 18:57
@chux: On POSIXy systems, the EOF behaviour kinda makes sense, because if the file gets appended to after we read it till the end, we'll want to get the appended data; and since there is nocleareof()
function, reopening the file would be the only way to do that otherwise. Whether any of this is standards-conformant, I have no opinion: I've decided to steer away from language-lawyer. (If it is a bug in glibc, getting it fixed is a quixotic fight against people who believe they know the standards better than anyone else. Not worth the effort, IMO.)
– Nominal Animal
Nov 14 '18 at 19:09
Re: "there is nocleareof()
". Would notclearerr(istream)
be sufficient in lieu of reopening the file? IAC, thanks for the exploring the corners of C I/O.
– chux
Nov 14 '18 at 19:14
@NominalAnimal For the record, if you believe you have found a bug in glibc and you don't feel like arguing with Joseph and/or Paul Eggert about it, you can email me and I'll do it. Ulrich is no longer involved.
– zwol
Nov 14 '18 at 19:27
@zwol: Yeah, libio/getc.c:_IO_getc() and libio/getc_u.c:__getc_unlocked() are still missing an_IO_ferror_unlocked()
check in HEAD. I'd recommend turning_IO_getc_unlocked()
into a function that does the check, and returns__getc_unlocked_body()
. Also, an RFC to the devs: perhaps change_IO_EOF_SEEN
in libio/bits/types/struct_FILE.h to0x0030
, so thatfeof()
returns nonzero if eof or error.
– Nominal Animal
Nov 14 '18 at 20:58
|
show 2 more comments
I also found similar results, yet tend to think it is non-conforming to the standard. (IOWs, a bug in that version). I also noticed that the end-of-file flag was cleared byfgetc()
call with 5 key"xn"
, Ctrl d,".n"
sequence with the'.'
key.
– chux
Nov 14 '18 at 18:57
@chux: On POSIXy systems, the EOF behaviour kinda makes sense, because if the file gets appended to after we read it till the end, we'll want to get the appended data; and since there is nocleareof()
function, reopening the file would be the only way to do that otherwise. Whether any of this is standards-conformant, I have no opinion: I've decided to steer away from language-lawyer. (If it is a bug in glibc, getting it fixed is a quixotic fight against people who believe they know the standards better than anyone else. Not worth the effort, IMO.)
– Nominal Animal
Nov 14 '18 at 19:09
Re: "there is nocleareof()
". Would notclearerr(istream)
be sufficient in lieu of reopening the file? IAC, thanks for the exploring the corners of C I/O.
– chux
Nov 14 '18 at 19:14
@NominalAnimal For the record, if you believe you have found a bug in glibc and you don't feel like arguing with Joseph and/or Paul Eggert about it, you can email me and I'll do it. Ulrich is no longer involved.
– zwol
Nov 14 '18 at 19:27
@zwol: Yeah, libio/getc.c:_IO_getc() and libio/getc_u.c:__getc_unlocked() are still missing an_IO_ferror_unlocked()
check in HEAD. I'd recommend turning_IO_getc_unlocked()
into a function that does the check, and returns__getc_unlocked_body()
. Also, an RFC to the devs: perhaps change_IO_EOF_SEEN
in libio/bits/types/struct_FILE.h to0x0030
, so thatfeof()
returns nonzero if eof or error.
– Nominal Animal
Nov 14 '18 at 20:58
I also found similar results, yet tend to think it is non-conforming to the standard. (IOWs, a bug in that version). I also noticed that the end-of-file flag was cleared by
fgetc()
call with 5 key "xn"
, Ctrl d, ".n"
sequence with the '.'
key.– chux
Nov 14 '18 at 18:57
I also found similar results, yet tend to think it is non-conforming to the standard. (IOWs, a bug in that version). I also noticed that the end-of-file flag was cleared by
fgetc()
call with 5 key "xn"
, Ctrl d, ".n"
sequence with the '.'
key.– chux
Nov 14 '18 at 18:57
@chux: On POSIXy systems, the EOF behaviour kinda makes sense, because if the file gets appended to after we read it till the end, we'll want to get the appended data; and since there is no
cleareof()
function, reopening the file would be the only way to do that otherwise. Whether any of this is standards-conformant, I have no opinion: I've decided to steer away from language-lawyer. (If it is a bug in glibc, getting it fixed is a quixotic fight against people who believe they know the standards better than anyone else. Not worth the effort, IMO.)– Nominal Animal
Nov 14 '18 at 19:09
@chux: On POSIXy systems, the EOF behaviour kinda makes sense, because if the file gets appended to after we read it till the end, we'll want to get the appended data; and since there is no
cleareof()
function, reopening the file would be the only way to do that otherwise. Whether any of this is standards-conformant, I have no opinion: I've decided to steer away from language-lawyer. (If it is a bug in glibc, getting it fixed is a quixotic fight against people who believe they know the standards better than anyone else. Not worth the effort, IMO.)– Nominal Animal
Nov 14 '18 at 19:09
Re: "there is no
cleareof()
". Would not clearerr(istream)
be sufficient in lieu of reopening the file? IAC, thanks for the exploring the corners of C I/O.– chux
Nov 14 '18 at 19:14
Re: "there is no
cleareof()
". Would not clearerr(istream)
be sufficient in lieu of reopening the file? IAC, thanks for the exploring the corners of C I/O.– chux
Nov 14 '18 at 19:14
@NominalAnimal For the record, if you believe you have found a bug in glibc and you don't feel like arguing with Joseph and/or Paul Eggert about it, you can email me and I'll do it. Ulrich is no longer involved.
– zwol
Nov 14 '18 at 19:27
@NominalAnimal For the record, if you believe you have found a bug in glibc and you don't feel like arguing with Joseph and/or Paul Eggert about it, you can email me and I'll do it. Ulrich is no longer involved.
– zwol
Nov 14 '18 at 19:27
@zwol: Yeah, libio/getc.c:_IO_getc() and libio/getc_u.c:__getc_unlocked() are still missing an
_IO_ferror_unlocked()
check in HEAD. I'd recommend turning _IO_getc_unlocked()
into a function that does the check, and returns __getc_unlocked_body()
. Also, an RFC to the devs: perhaps change _IO_EOF_SEEN
in libio/bits/types/struct_FILE.h to 0x0030
, so that feof()
returns nonzero if eof or error.– Nominal Animal
Nov 14 '18 at 20:58
@zwol: Yeah, libio/getc.c:_IO_getc() and libio/getc_u.c:__getc_unlocked() are still missing an
_IO_ferror_unlocked()
check in HEAD. I'd recommend turning _IO_getc_unlocked()
into a function that does the check, and returns __getc_unlocked_body()
. Also, an RFC to the devs: perhaps change _IO_EOF_SEEN
in libio/bits/types/struct_FILE.h to 0x0030
, so that feof()
returns nonzero if eof or error.– Nominal Animal
Nov 14 '18 at 20:58
|
show 2 more comments
My reading of the standard is that it doesn't explicitly say fgetc
is allowed to return a non-EOF
value if the error indicator was already set on the stream on entry, but it doesn't explicitly say that it can't, either. I sympathize with Nominal Animal's observation (which I shall hoist from comments on his answer in case it gets deleted or moved to chat; allow me to grind my personal axe for a moment and observe that the policy of treating comments as "ephemeral" is harmful and should be abolished):
IMHO the standard is then bass-ackwards: there is no practical need for EOF to be sticky, but if error is not sticky, then there is a real risk of accidentally missing errors.
However, if existing implementations are all consistently not treating error as sticky, changing the behavior will be very hard to sell to the committee. Therefore, I am soliciting tests from the community:
Below is a shortened, non-interactive version of Nominal Animal's test program. It only looks at the behavior of fgetc
after a read error, not after EOF. It uses SIGALRM
to interrupt a read, instead of control-C, so you don't have to do anything but run it.
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
static _Noreturn void
perror_exit (const char *msg)
perror (msg);
exit (1);
static void
handler (int unused)
int
main (void)
struct sigaction sa;
int pipefd[2];
FILE *fp;
int ch, pa;
setvbuf (stdout, 0, _IOLBF, 0);
sa.sa_handler = handler;
sa.sa_flags = 0; /* DO interrupt blocking system calls */
sigemptyset (&sa.sa_mask);
if (sigaction (SIGALRM, &sa, 0))
perror_exit ("sigaction");
if (pipe (pipefd))
perror_exit ("pipe");
fp = fdopen (pipefd[0], "r");
if (!fp)
perror_exit ("fdopen");
printf ("before fgetc 1, feof = %d ferror = %dn",
feof (fp), ferror (fp));
alarm (1);
ch = fgetc (fp);
if (ch == EOF)
printf ("after fgetc 1, ch = EOF feof = %d ferror = %dn",
feof (fp), ferror (fp));
else
printf ("after fgetc 1, ch = '%c' feof = %d ferror = %dn",
ch, feof (fp), ferror (fp));
write (pipefd[1], "x", 1);
alarm (1);
ch = fgetc (fp);
pa = alarm (0);
printf ("after fgetc 2, alarm %sn",
pa ? "did not fire" : "fired");
if (ch == EOF)
printf ("after fgetc 2, ch = EOF feof = %d ferror = %dn",
feof (fp), ferror (fp));
else
printf ("after fgetc 2, ch = '%c' feof = %d ferror = %dn",
ch, feof (fp), ferror (fp));
return 0;
On all of the Unixes I can get at at the moment, this program's output is consistent with John Bollinger's observation that
the case of most interest, is in fact allowed:
1 0 1 Normal reading of valid data with error indicator set!
I would particularly like to know what this program prints when run on alternative Linux-based C libraries (e.g. musl, bionic); Unixes which are not Linux nor are they BSD-phylum; and Windows. If you've got anything even more exotic please try that too. I'm marking this post community wiki; please edit it to add test results.
The test program should be acceptable to any C89-compliant compiler for an environment where unistd.h
exists and signal.h
defines sigaction
, except for one use of the C11 _Noreturn
keyword which is only to squelch warnings. If your compiler complains about _Noreturn
, compile with -D_Noreturn=
; the results will not be affected. If you don't have unistd.h
, the test program will not do anything meaningful in your environment. If you don't have sigaction
you may be able to adapt the program to use alternative interfaces, but you need to persuade SIGALRM
to interrupt a blocking read
somehow.
Results
before fgetc 1, feof = 0 ferror = 0
after fgetc 1, ch = EOF feof = 0 ferror = 1
after fgetc 2, alarm did not fire
after fgetc 2, ch = 'x' feof = 0 ferror = 1
("normal reading of valid data with error indicator set")
- Linux with glibc 2.27
- NetBSD 7.1.2
- FreeBSD 11.2-RELEASE-p4
- macOS 10.14, clang-1000.10.44.2
- macOS 10.14, gcc 8.2.0 (Homebrew)
.
before fgetc 1, feof = 0 ferror = 0
after fgetc 1, ch = EOF feof = 0 ferror = 1
after fgetc 2, alarm did not fire
after fgetc 2, ch = EOF feof = 0 ferror = 1
("sticky error" behavior: fgetc(fp)
immediately returns EOF without calling read
when ferror(fp)
is true on entry)
- Plan 9 (compiler does not recognize _Noreturn)
.
add a comment |
My reading of the standard is that it doesn't explicitly say fgetc
is allowed to return a non-EOF
value if the error indicator was already set on the stream on entry, but it doesn't explicitly say that it can't, either. I sympathize with Nominal Animal's observation (which I shall hoist from comments on his answer in case it gets deleted or moved to chat; allow me to grind my personal axe for a moment and observe that the policy of treating comments as "ephemeral" is harmful and should be abolished):
IMHO the standard is then bass-ackwards: there is no practical need for EOF to be sticky, but if error is not sticky, then there is a real risk of accidentally missing errors.
However, if existing implementations are all consistently not treating error as sticky, changing the behavior will be very hard to sell to the committee. Therefore, I am soliciting tests from the community:
Below is a shortened, non-interactive version of Nominal Animal's test program. It only looks at the behavior of fgetc
after a read error, not after EOF. It uses SIGALRM
to interrupt a read, instead of control-C, so you don't have to do anything but run it.
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
static _Noreturn void
perror_exit (const char *msg)
perror (msg);
exit (1);
static void
handler (int unused)
int
main (void)
struct sigaction sa;
int pipefd[2];
FILE *fp;
int ch, pa;
setvbuf (stdout, 0, _IOLBF, 0);
sa.sa_handler = handler;
sa.sa_flags = 0; /* DO interrupt blocking system calls */
sigemptyset (&sa.sa_mask);
if (sigaction (SIGALRM, &sa, 0))
perror_exit ("sigaction");
if (pipe (pipefd))
perror_exit ("pipe");
fp = fdopen (pipefd[0], "r");
if (!fp)
perror_exit ("fdopen");
printf ("before fgetc 1, feof = %d ferror = %dn",
feof (fp), ferror (fp));
alarm (1);
ch = fgetc (fp);
if (ch == EOF)
printf ("after fgetc 1, ch = EOF feof = %d ferror = %dn",
feof (fp), ferror (fp));
else
printf ("after fgetc 1, ch = '%c' feof = %d ferror = %dn",
ch, feof (fp), ferror (fp));
write (pipefd[1], "x", 1);
alarm (1);
ch = fgetc (fp);
pa = alarm (0);
printf ("after fgetc 2, alarm %sn",
pa ? "did not fire" : "fired");
if (ch == EOF)
printf ("after fgetc 2, ch = EOF feof = %d ferror = %dn",
feof (fp), ferror (fp));
else
printf ("after fgetc 2, ch = '%c' feof = %d ferror = %dn",
ch, feof (fp), ferror (fp));
return 0;
On all of the Unixes I can get at at the moment, this program's output is consistent with John Bollinger's observation that
the case of most interest, is in fact allowed:
1 0 1 Normal reading of valid data with error indicator set!
I would particularly like to know what this program prints when run on alternative Linux-based C libraries (e.g. musl, bionic); Unixes which are not Linux nor are they BSD-phylum; and Windows. If you've got anything even more exotic please try that too. I'm marking this post community wiki; please edit it to add test results.
The test program should be acceptable to any C89-compliant compiler for an environment where unistd.h
exists and signal.h
defines sigaction
, except for one use of the C11 _Noreturn
keyword which is only to squelch warnings. If your compiler complains about _Noreturn
, compile with -D_Noreturn=
; the results will not be affected. If you don't have unistd.h
, the test program will not do anything meaningful in your environment. If you don't have sigaction
you may be able to adapt the program to use alternative interfaces, but you need to persuade SIGALRM
to interrupt a blocking read
somehow.
Results
before fgetc 1, feof = 0 ferror = 0
after fgetc 1, ch = EOF feof = 0 ferror = 1
after fgetc 2, alarm did not fire
after fgetc 2, ch = 'x' feof = 0 ferror = 1
("normal reading of valid data with error indicator set")
- Linux with glibc 2.27
- NetBSD 7.1.2
- FreeBSD 11.2-RELEASE-p4
- macOS 10.14, clang-1000.10.44.2
- macOS 10.14, gcc 8.2.0 (Homebrew)
.
before fgetc 1, feof = 0 ferror = 0
after fgetc 1, ch = EOF feof = 0 ferror = 1
after fgetc 2, alarm did not fire
after fgetc 2, ch = EOF feof = 0 ferror = 1
("sticky error" behavior: fgetc(fp)
immediately returns EOF without calling read
when ferror(fp)
is true on entry)
- Plan 9 (compiler does not recognize _Noreturn)
.
add a comment |
My reading of the standard is that it doesn't explicitly say fgetc
is allowed to return a non-EOF
value if the error indicator was already set on the stream on entry, but it doesn't explicitly say that it can't, either. I sympathize with Nominal Animal's observation (which I shall hoist from comments on his answer in case it gets deleted or moved to chat; allow me to grind my personal axe for a moment and observe that the policy of treating comments as "ephemeral" is harmful and should be abolished):
IMHO the standard is then bass-ackwards: there is no practical need for EOF to be sticky, but if error is not sticky, then there is a real risk of accidentally missing errors.
However, if existing implementations are all consistently not treating error as sticky, changing the behavior will be very hard to sell to the committee. Therefore, I am soliciting tests from the community:
Below is a shortened, non-interactive version of Nominal Animal's test program. It only looks at the behavior of fgetc
after a read error, not after EOF. It uses SIGALRM
to interrupt a read, instead of control-C, so you don't have to do anything but run it.
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
static _Noreturn void
perror_exit (const char *msg)
perror (msg);
exit (1);
static void
handler (int unused)
int
main (void)
struct sigaction sa;
int pipefd[2];
FILE *fp;
int ch, pa;
setvbuf (stdout, 0, _IOLBF, 0);
sa.sa_handler = handler;
sa.sa_flags = 0; /* DO interrupt blocking system calls */
sigemptyset (&sa.sa_mask);
if (sigaction (SIGALRM, &sa, 0))
perror_exit ("sigaction");
if (pipe (pipefd))
perror_exit ("pipe");
fp = fdopen (pipefd[0], "r");
if (!fp)
perror_exit ("fdopen");
printf ("before fgetc 1, feof = %d ferror = %dn",
feof (fp), ferror (fp));
alarm (1);
ch = fgetc (fp);
if (ch == EOF)
printf ("after fgetc 1, ch = EOF feof = %d ferror = %dn",
feof (fp), ferror (fp));
else
printf ("after fgetc 1, ch = '%c' feof = %d ferror = %dn",
ch, feof (fp), ferror (fp));
write (pipefd[1], "x", 1);
alarm (1);
ch = fgetc (fp);
pa = alarm (0);
printf ("after fgetc 2, alarm %sn",
pa ? "did not fire" : "fired");
if (ch == EOF)
printf ("after fgetc 2, ch = EOF feof = %d ferror = %dn",
feof (fp), ferror (fp));
else
printf ("after fgetc 2, ch = '%c' feof = %d ferror = %dn",
ch, feof (fp), ferror (fp));
return 0;
On all of the Unixes I can get at at the moment, this program's output is consistent with John Bollinger's observation that
the case of most interest, is in fact allowed:
1 0 1 Normal reading of valid data with error indicator set!
I would particularly like to know what this program prints when run on alternative Linux-based C libraries (e.g. musl, bionic); Unixes which are not Linux nor are they BSD-phylum; and Windows. If you've got anything even more exotic please try that too. I'm marking this post community wiki; please edit it to add test results.
The test program should be acceptable to any C89-compliant compiler for an environment where unistd.h
exists and signal.h
defines sigaction
, except for one use of the C11 _Noreturn
keyword which is only to squelch warnings. If your compiler complains about _Noreturn
, compile with -D_Noreturn=
; the results will not be affected. If you don't have unistd.h
, the test program will not do anything meaningful in your environment. If you don't have sigaction
you may be able to adapt the program to use alternative interfaces, but you need to persuade SIGALRM
to interrupt a blocking read
somehow.
Results
before fgetc 1, feof = 0 ferror = 0
after fgetc 1, ch = EOF feof = 0 ferror = 1
after fgetc 2, alarm did not fire
after fgetc 2, ch = 'x' feof = 0 ferror = 1
("normal reading of valid data with error indicator set")
- Linux with glibc 2.27
- NetBSD 7.1.2
- FreeBSD 11.2-RELEASE-p4
- macOS 10.14, clang-1000.10.44.2
- macOS 10.14, gcc 8.2.0 (Homebrew)
.
before fgetc 1, feof = 0 ferror = 0
after fgetc 1, ch = EOF feof = 0 ferror = 1
after fgetc 2, alarm did not fire
after fgetc 2, ch = EOF feof = 0 ferror = 1
("sticky error" behavior: fgetc(fp)
immediately returns EOF without calling read
when ferror(fp)
is true on entry)
- Plan 9 (compiler does not recognize _Noreturn)
.
My reading of the standard is that it doesn't explicitly say fgetc
is allowed to return a non-EOF
value if the error indicator was already set on the stream on entry, but it doesn't explicitly say that it can't, either. I sympathize with Nominal Animal's observation (which I shall hoist from comments on his answer in case it gets deleted or moved to chat; allow me to grind my personal axe for a moment and observe that the policy of treating comments as "ephemeral" is harmful and should be abolished):
IMHO the standard is then bass-ackwards: there is no practical need for EOF to be sticky, but if error is not sticky, then there is a real risk of accidentally missing errors.
However, if existing implementations are all consistently not treating error as sticky, changing the behavior will be very hard to sell to the committee. Therefore, I am soliciting tests from the community:
Below is a shortened, non-interactive version of Nominal Animal's test program. It only looks at the behavior of fgetc
after a read error, not after EOF. It uses SIGALRM
to interrupt a read, instead of control-C, so you don't have to do anything but run it.
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
static _Noreturn void
perror_exit (const char *msg)
perror (msg);
exit (1);
static void
handler (int unused)
int
main (void)
struct sigaction sa;
int pipefd[2];
FILE *fp;
int ch, pa;
setvbuf (stdout, 0, _IOLBF, 0);
sa.sa_handler = handler;
sa.sa_flags = 0; /* DO interrupt blocking system calls */
sigemptyset (&sa.sa_mask);
if (sigaction (SIGALRM, &sa, 0))
perror_exit ("sigaction");
if (pipe (pipefd))
perror_exit ("pipe");
fp = fdopen (pipefd[0], "r");
if (!fp)
perror_exit ("fdopen");
printf ("before fgetc 1, feof = %d ferror = %dn",
feof (fp), ferror (fp));
alarm (1);
ch = fgetc (fp);
if (ch == EOF)
printf ("after fgetc 1, ch = EOF feof = %d ferror = %dn",
feof (fp), ferror (fp));
else
printf ("after fgetc 1, ch = '%c' feof = %d ferror = %dn",
ch, feof (fp), ferror (fp));
write (pipefd[1], "x", 1);
alarm (1);
ch = fgetc (fp);
pa = alarm (0);
printf ("after fgetc 2, alarm %sn",
pa ? "did not fire" : "fired");
if (ch == EOF)
printf ("after fgetc 2, ch = EOF feof = %d ferror = %dn",
feof (fp), ferror (fp));
else
printf ("after fgetc 2, ch = '%c' feof = %d ferror = %dn",
ch, feof (fp), ferror (fp));
return 0;
On all of the Unixes I can get at at the moment, this program's output is consistent with John Bollinger's observation that
the case of most interest, is in fact allowed:
1 0 1 Normal reading of valid data with error indicator set!
I would particularly like to know what this program prints when run on alternative Linux-based C libraries (e.g. musl, bionic); Unixes which are not Linux nor are they BSD-phylum; and Windows. If you've got anything even more exotic please try that too. I'm marking this post community wiki; please edit it to add test results.
The test program should be acceptable to any C89-compliant compiler for an environment where unistd.h
exists and signal.h
defines sigaction
, except for one use of the C11 _Noreturn
keyword which is only to squelch warnings. If your compiler complains about _Noreturn
, compile with -D_Noreturn=
; the results will not be affected. If you don't have unistd.h
, the test program will not do anything meaningful in your environment. If you don't have sigaction
you may be able to adapt the program to use alternative interfaces, but you need to persuade SIGALRM
to interrupt a blocking read
somehow.
Results
before fgetc 1, feof = 0 ferror = 0
after fgetc 1, ch = EOF feof = 0 ferror = 1
after fgetc 2, alarm did not fire
after fgetc 2, ch = 'x' feof = 0 ferror = 1
("normal reading of valid data with error indicator set")
- Linux with glibc 2.27
- NetBSD 7.1.2
- FreeBSD 11.2-RELEASE-p4
- macOS 10.14, clang-1000.10.44.2
- macOS 10.14, gcc 8.2.0 (Homebrew)
.
before fgetc 1, feof = 0 ferror = 0
after fgetc 1, ch = EOF feof = 0 ferror = 1
after fgetc 2, alarm did not fire
after fgetc 2, ch = EOF feof = 0 ferror = 1
("sticky error" behavior: fgetc(fp)
immediately returns EOF without calling read
when ferror(fp)
is true on entry)
- Plan 9 (compiler does not recognize _Noreturn)
.
edited Nov 26 '18 at 19:10
community wiki
4 revs, 3 users 94%
zwol
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53272650%2fhow-a-stream-error-indicator-affects-following-input-code%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Are you looking for a standards-based answer (perhaps language-lawyer), or a practical answer?
– Nominal Animal
Nov 13 '18 at 2:08
@NominalAnimal Closer to standards-based than only practical as I am trying to divine the error indicator mysteries to solve: when
fgetc()
returnsEOF
, is that due to a recent error, end-of-file, some wideunsigned char
or something else? LL added.– chux
Nov 13 '18 at 2:16
FWIW, with glibc it is always either due to detecting a new end-of-file or error condition, since the
fgetc()
/getc()
/getchar()
functions never examine the error flag.– Nominal Animal
Nov 13 '18 at 2:30
@NominalAnimal Does glibc also return
EOF
on existing end-of-file flag and not just a new end-of-file?– chux
Nov 13 '18 at 2:35
1
The behavior of glibc
fgetc
was changed in version 2.28, see sourceware.org/ml/libc-alpha/2018-08/msg00003.html and sourceware.org/bugzilla/show_bug.cgi?id=1190 . We sure thought 7.21.7p2,3 mandated "sticky EOF" -- the change is described as "correct[ing] a long-standing C99 conformance bug". (However, the EOF indicator and the error indicator are two separate bits.)– zwol
Nov 14 '18 at 17:35