3

I have to individually read characters and substrings from a stream in C while parsing them. I wish also to check for input error. The obvious way to do this is something like:

c = fgetc(f);
if(ferror(f)) {
    puts(strerror(errno));
    exit(1);
}
/* do something with c */
c = fgetc(f);
if(ferror(f)) {
    puts(strerror(errno));
    exit(1);
}
/* do something with c */

Etc. However, it would be much more practical and fast (in the non-exceptional case when there's no error) if I could do all the input operations and check for the error indicator later:

c = fgetc(f);
/* do something with c */
c = fgetc(f);
/* do something with c */
if(ferror(f)) {
    puts(strerror(errno));
    exit(1);
}

This would be possible if input operations like fgetc(), scanf() etc were simple passthrough no-ops when the error indicator of f is set. Say, an error occours in the first fgetc() and therefore the second fgetc() is a no-op that fails but change neither the error indicator of f nor errno.

A similiar question may be asked about output functions.

My question is: is this the behaviour of stdio functions? May I check ferror(f) after all operations and get errno then if I am sure that all those "do something with c" do not change errno?

Thanks!

  • 2
    You could write a `myfgetc` function that does what you describe. – Jabberwocky Feb 25 '16 at 09:07
  • Sure, a wrapper function. An obvious solution! – user1542207 Feb 25 '16 at 09:13
  • 1
    The subsequent function calls fail, but other functions may also change the value of `errno` — whether they failed or not. So, you lose accurate tracking of `errno` with your proposed scheme, but it otherwise works. – Jonathan Leffler Feb 25 '16 at 22:24
  • There is no reason to ever call `ferror` until `fgetc` returns EOF. – William Pursell Feb 25 '16 at 22:24
  • @William Pursell There is nothing wrong in calling ferror() even if fgetc() does not return EOF. if ferror() is set, then I know that fgetc() returned EOF. – user1542207 Feb 26 '16 at 23:11
  • The only thing wrong with calling `ferror` this way is that you aren't checking if fgetc returned EOF, so you don't actually know if you've gotten any data. I suppose you could call `ferror` and then check if c == EOF, or check `feof`, but doing so is extremely non-idiomatic. – William Pursell Feb 26 '16 at 23:21
  • "if ferror() is set, then I know that fgetc() returned EOF." --> No. `ferror()` returns the state of the _error indicator_ for the file. `ferror()` is true means that at sometime in the past, an error occurred, not necessarily the previous `*get()` - perhaps even long ago. – chux - Reinstate Monica Nov 25 '19 at 17:40

3 Answers3

0

No, those are not the defined semantics of errno.

Quoting this manual page:

Its value is significant only when the return value of the call indicated an error (i.e., -1 from most system calls; -1 or NULL from most library functions); a function that succeeds is allowed to change errno.

This implies that if you were to do two I/O operations where the first fails, and the second is a "no-op" (like read zero bytes) it could succeed and opt to clear errno, thus dropping the error set by the first call.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • I think that if the error indicator for f is set, all subsequent calls of fgetc(f) should fail. Am I wrong? My hope is that errno gets untouched in such cases. – user1542207 Feb 25 '16 at 09:21
  • @user1542207 *My hope is that errno gets untouched in such cases.* You plan on writing code based on *hope*? – Andrew Henle Feb 25 '16 at 10:02
  • @Andrew Henle No, I plan to write code based on specifications that's what I came here looking for. But the first step is always hope. – user1542207 Feb 25 '16 at 16:09
  • 3
    No function in the standard C library (or standard POSIX [library](http://pubs.opengroup.org/onlinepubs/9699919799/functions/errno.html)) sets `errno` to zero. The C11 standard says: _The value of `errno` in the initial thread is zero at program startup (the initial value of `errno` in other threads is an indeterminate value), but is never set to zero by any library function. The value of `errno` may be set to nonzero by a library function call whether or not there is an error, provided the use of `errno` is not documented in the description of the function in this International Standard._ – Jonathan Leffler Feb 25 '16 at 22:08
0

Answering my own question, it seems that the reliable way to implement what I was looking for is to write a wrapper function myfgetc(), as Michael Walz suggested, together with a global variable myerrno:

__thread int myerrno = 0;

int myfgetc(FILE *f) {
    int c;
    if(myerrno)
        return EOF;
    if((c = fgetc(f)) == EOF)
        myerrno = errno;
    return c;
}

The storage class __thread is added to myerrno so that every thread has its own myerrno. It can be ommited if the program is single threaded.

0

...is this (an error occurs in the first fgetc() and therefore the second fgetc() is a no-op that fails) the behaviour of stdio functions?

No - not a no-op.

FILE has: "an error indicator that records whether a read/write error has occurred," (C11dr § § 7.21.1 2), not that an error just occurred. It is a flag that accumulates the history of read errors.

For fgetc() and friends,

If a read error occurs, the error indicator for the stream is set and the fgetc function returns EOF. C11dr § 7.21.7.1 3.

This return of EOF due to input error differs from EOF due to end-of-file. The latter has an or "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 returns EOF". EOF due to input error does not have an or.

I interpret this to imply that the error indicator of the stream can be true and fgetc() does not return EOF as the byte just read was not in error.

How a stream error indicator affects following input code? may be useful.


May I check ferror(f) after all operations and get errno then if I am sure that all those "do something with c" do not change errno?

errno not that useful here. C does not specify any I/O functions as certainly setting errno - that is an extension of some compilers. C expressly prohibits standard functions from clearing errno.

Yes, code can check ferror(f) to see if an error had occurred sometime in the past. Examination of errno is not needed.


To clear both error indicator and end-of-file, research clearer().

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256