The MacOS behaviour is correct. According to the C standard §7.21.7.1/3 (the fgetc
library function), the end-of-file indication is sticky; once fgetc
sees an EOF, it must set the file's end-of-file indicator which will cause subsequent calls to return EOF
until the end-of-file indicator is cleared, for example with clearerr()
:
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. Otherwise, the fgetc function returns the next character from the input stream pointed to by stream. If a read error occurs, the error indicator for the stream is set and the fgetc function returns EOF.
Since other input functions, including scanf
, are supposed to act as if implemented by repeated calls to fgetc
, EOF should be sticky for them, too. If you want to continue reading after you receive an EOF return, you should call clearerr()
on the stream. (Or something else which resets the indicator, such as seek()
.)
For many years, the Gnu implementation of the standard C library did not follow the standard. It only reported EOF once, leaving the next fgetc
to wait for more input on devices like terminals and pipes. The bug, reported in 2006, was finally fixed in v2.28, released in August 2018, although that might not yet be part of the Centos distro.
[Note: there is a longer discussion about this behaviour in this answer, including a now-outdated grump (by me) and some links to historic discussions about the issue.]
In any case, it has always been clear that portable code should call clearerr()
, since BSD-derived standard library implementations (including MacOS) follow the standard as quoted above.