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
errno
to 0, so they cannot clearerrno
to indicate success. However, C permitserrno
to be modified by a library function "whether or not there is an error, provided the use oferrno
is not documented in the description of the function" (§7.5 para 3). The C standard does not document the use oferrno
byfgetc
.Posix is even more flexible: it specifies that
It does document the use of
errno
byfgetc
, but that documentation does not indicate thaterrno
shall 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,
EOF
might indicate a successful call (because end of file is not an error) or it might indicate an error. Only in the latter case iserrno
set to a meaningful value. Consequently, before checkingerrno
you need to verify thatferror(stdin)
reports an error.So, as I said, the behaviour is allowed by the standards. Why does the
fgetc
implementation do that? My guess is that thelibc
implementations on your systems are doing some sort of lazy initialization on the first call.