5

I always struggle with return values of system calls - they are just so inconsistent! Normally I check if they are NULL or -1 and then call perror. However, for fgets, the man page says:

gets() and fgets() return s on success, and NULL on error or when end of file occurs while no characters have been read.

which means the return value NULL is not necessarily an error - it can also be EOF. Is errno set when the end of file is reached? Can I still call perror in this case?

If not, what is the common way to tell if the call returned an error versus EOF. I want to use perror with NULL string for errors and a custom string for EOF.

Joel Cunningham
  • 641
  • 6
  • 17
Xufeng
  • 6,452
  • 8
  • 24
  • 30

2 Answers2

7

Use ferror and feof to distinguish between error and EOF. There's no general way to find out exactly what the error was, if there was an error, but you can tell that there was one.

Standard C (f)gets (and (f)getc) are not required to set errno, although a conforming library implementation can set errno to a non-zero value pretty much at will.

Posix does requires that (f)get{c,s} set errno on read errors, but that only helps you after you have determined that there was a read error (by calling ferror). It's also important to remember that library functions never set errno to 0, but may set errno to a non-zero value even if no error occurs. So you cannot test errno as a replacement for checking the error return of any library function, including fgets. And since end-of-file is not an error, errno will (probably) not be modified on EOF, so its value in that case is meaningless.

rici
  • 234,347
  • 28
  • 237
  • 341
  • @Xufeng: No, it gets information directly from the `FILE` object, in some implementation-specific way. – rici Mar 24 '14 at 05:52
  • @Xufeng: Not really, since `errno` hasn't been set (at least, not by `ferror` or `(f)gets`), so the result of `perror` will be misleading at best. About all you can do is print some localized version of `I/O error`, but then `perror` wouldn't do any better either, even if `errno` was set. – rici Mar 24 '14 at 05:54
  • 1
    Oh since you said fgets does not set errno so I did a bit of research and found that fgets it NOT a system call in the first place.. :) – Xufeng Mar 24 '14 at 05:56
  • @Xufeng: Yes, that's the important thing to remember here. System calls are consistent in the use of `errno`. But only functions in the section 2 of the manual are system calls. `gets` and `fgets` are standard C library functions and standard C library is portable and does not have a slightest idea about existence of unix-specific interfaces like `errno`. – Jan Hudec Mar 24 '14 at 05:58
  • 2
    @JanHudec: Standard C inludes a definition of the header file `` which includes a definition of `errno`: "... which expands to a modifiable lvalue that has type int and thread local storage duration, the value of which is set to a positive error number by several library functions." One such function is `strtod`, which is not a system call either. – rici Mar 24 '14 at 06:00
  • @rici: Hm, you are right. Standard C library does use `errno` _sometimes_. And not other times like `FILE*` functions. – Jan Hudec Mar 24 '14 at 06:14
  • Note: it is possible for `fgets()` to return `NULL` and neither `feof()` nor `ferror()` are true in corner cases. http://stackoverflow.com/q/23388620/2410359 – chux - Reinstate Monica Feb 19 '15 at 23:50
  • @chux: That seems to be true with some C libraries, but it's not clear to me that it is compliant with the standard. In any event, that exceptional case is neither an EOF nor a read error, so it's not relevant to the question, afaics. I should have said in the answer that fgets and fgetc are not required by standard C to set errno, but Posix does require it to be set; nonetheless, Posix doesn't specify that errno be set to `EINVAL` for a call to `fgets` with length 0, although that would seem logical -- like the C standard, it is only concerned about identifying EOF and read errors. – rici Feb 20 '15 at 02:46
  • @rici I mentioned a 3rd possible reason for what might cause `fgets()` to return `NULL` due to your correct "Use `ferror` and `feof` to distinguish between error and EOF". Most code I have seen will use a test of one (`ferror` or `feof`) and assume that only 1 of those 2 could have happened. IAC, the referenced 3rd method in my comment is exceedingly rare. OTOH so if `ferror()`. – chux - Reinstate Monica Feb 20 '15 at 02:52
  • @chux: Ok, I described the errno behaviour better (Std C maybe, Posix yes). glibc and bsd derivative fgets return NULL immediately (without setting errno) if `n <= 0`. (glibc's handling of the `n == 1` case was fixed in 2005, see [bug 713](https://sourceware.org/bugzilla/show_bug.cgi?id=713). Such a call is clearly a bug in the calling code and would best be dealt with by `assert(n>0)` prior to the `fgets`. IMHO. – rici Feb 20 '15 at 04:05
2

According to fputs own documentation, yes, EOF does set errno. The man pages infer it indirectly as opposed to stating it outright, which hopefully will be amended. The function fputs returns an integer that will either be positive on success or EOF on failure. So the key to error handling fputs is to setup a code block that checks the return value of fputs as it is being called. The following is a snippet of how I've been taught to handle fputs errors.

if (fputs(buffer, stdout) == EOF)
{
  fprintf(stderr, "fputs returned EOF: %s\n", strerror(errno));
  // .. and now do whatever cleanup you need to do.
  // or be lazy and exit(-1)
}

Here I am writing the contents of buffer to standard output and checking to see if fputs returns EOF. EOF indicates an error code was set, so as long as you follow the documentation on the man pages for fputs, you should be able to create a bunch of if statements to check the various error codes errno can be set to.

(1) What is buffer? Some character array I declared elsewhere.

(2) What does fprintf do? It prints output to a passed in file descriptor, which is in this case standard error (stderr... it prints to console like stdout, but for errors).

(3) What is strerror? It is a function defined in the string.h header that prints error information for the passed in error code. It has information for every single error code that errno can be set to. The header string.h should NOT be confused with strings.h, which is a BSD linux header file that does not contain strerror(3).

Edit: Ok, I messed up. You were looking for an answer on fgets, not fputs.

To check for an error on fgets, do the following

if (fgets(buffer, BUF_SIZE, myFile) == NULL)
{ 
  // print out error as a string to stderr
  fprintf(stderr, "fgets error occurred: %s\n", strerror(errno));
  // do cleanup
}
// special: you also need to check errno AFTER the if statement...

The thing is, the only way you are getting an error on this is if the stream becomes unreadable, which is either due to permissions or trying to read something that is in write mode. In the case of a network, it may be possible for something to cut off your connection in the middle of reading, in which case you need to check the error code after the fgets if statement as well. But it will set the error code if something went wrong.

At least that is if the man pages are correct. See the linux man pages for more details. Only error code that can be set is the "I can't read this thing" code, which is errno == EBADF

Jack Humbert
  • 129
  • 2
  • 9
  • Notes: 1) OP's post is on `fgets()` and not `fputs()`. 2) Per the C11 standard, `fputs()` does nothing with `errno`. Maybe it does within *nix. – chux - Reinstate Monica Feb 19 '15 at 23:55
  • @chux: The C standard doesn't say that `fputs()` does nothing with `errno`. It doesn't say whether it does anything or not. [N1570](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) 7.5p3: "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." [POSIX specifies](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fputc.html) a number of `errno` values that can be set by `fputc`, and therefore by `fputs`. – Keith Thompson Feb 20 '15 at 00:21
  • Yeah, I kind of botched on this one. I was flipping between stuff and misread the question. You are correct. – user4586128 Feb 20 '15 at 00:28
  • @Keith Thompson Yes - better said than my comment about `errno`. – chux - Reinstate Monica Feb 20 '15 at 02:36
  • Generally, if something cuts the network, the underlying call to `read()` will just perennially block until the file system itself gives up (which might or might not result in the current process going into disk sleep). Also, welcome to the site, and thanks for working hard on this answer - I think you'll do quite well here with that kind of style. – Tim Post Feb 20 '15 at 03:16
  • `== NULL`, not `== null` – Keith Thompson Feb 20 '15 at 07:51