Simple test program in C called get1:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(void) {
errno = 0;
int ch = fgetc(stdin);
printf("ch = %d\n", ch);
if (errno)
printf("errno = %d: %s\n", errno, strerror(errno));
return 0;
}
It just prints the first byte read in decimal, and then shows errno and the associated error message if errno is not zero.
Some results (foo is a text file, empty is a file of length zero):
% ./get1 < foo
ch = 104
% ./get1 < empty
ch = -1
Ok, as expected. However:
% ./get1 < /dev/zero
ch = 0
errno = 25: Inappropriate ioctl for device
% ./get1 < /dev/null
ch = -1
errno = 25: Inappropriate ioctl for device
% ./get1 < /dev/random
ch = 196
errno = 25: Inappropriate ioctl for device
The read is working fine, but when I read from any of these devices it is setting errno. Why?
That was on macOS (Darwin). I get the same thing on Linux (Debian) and NetBSD, except a different error just for /dev/random (the errors on the other devices were the same as on macOS):
% ./get1 < /dev/random
ch = 170
errno = 22: Invalid argument
It was my understanding that if you don't get EOF, then there is no error. If you do get EOF, then you check errno to see if there was an error. In all of these cases there does not appear to be an error in getting a byte from the device, nor should there be. Yet errno was set.
(Removing the printf doesn't change anything, in case anyone is wondering.)
As it turns out, errno is not set when reading from such a device on AIX, SunOS, or OpenSUSE.
So what's the deal here? Is this a bug, albeit widespread? Is this acceptable behavior? Is this expected behavior? Why is it setting errno?
I wouldn't have said it was expected behaviour (and indeed, I cannot reproduce it on the systems I have handy), but it is permitted by both the C standard and by Posix.
Both the C standard and Posix generally prohibit library functions from setting
errnoto 0, so they cannot clearerrnoto indicate success. However, C permitserrnoto be modified by a library function "whether or not there is an error, provided the use oferrnois not documented in the description of the function" (§7.5 para 3). The C standard does not document the use oferrnobyfgetc.Posix is even more flexible: it specifies that
It does document the use of
errnobyfgetc, but that documentation does not indicate thaterrnoshall not be modified by a successful call, only that it should be modified by a read error.Specifically, what Posix says is
In other words,
EOFmight indicate a successful call (because end of file is not an error) or it might indicate an error. Only in the latter case iserrnoset to a meaningful value. Consequently, before checkingerrnoyou need to verify thatferror(stdin)reports an error.So, as I said, the behaviour is allowed by the standards. Why does the
fgetcimplementation do that? My guess is that thelibcimplementations on your systems are doing some sort of lazy initialization on the first call.