Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

getchar() loops over EOF when STDIN provided through a pipe

I'm in front of something I can't explain. There is no better way of explaining than giving an example :

#include <stdio.h>

int main ()
{
    char c;
    while (1) {
        c = getchar();
        printf("%x\n", c);
    }
   return(0);
}

If I execute this command, it just iterates indefinitely this way:

$ echo -n "A" | ./binary
41
ffffffff
ffffffff
ffffffff
ffffffff
...

From what I know and what I read (Confusion about how a getchar() loop works internally), I thought echo -n "A" would send a A to stdin and then trigger an EOF event (I'm not sure what EOF really is) once, thus my loop would iterate twice at most, and then would sleep waiting for a new input in stdin.

But no, it iterates over EOF, and I don't understand why.

I ran this command to try to understand :

$ echo -n "A" | strace ./binary
read(0, "A", 4096)                      = 1
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 4), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7ff6000
write(1, "41\n", 341
)                     = 3
read(0, "", 4096)                       = 0
write(1, "ffffffff\n", 9ffffffff
)               = 9
read(0, "", 4096)                       = 0
write(1, "ffffffff\n", 9ffffffff
)               = 9
...

So it appears read() is reading nothing, and returns 0, which is interpreted by getchar() as EOF. But why, oh why does it iterate like this, whereas when I execute this binary the normal way, it works as expected :

$ ./binary
A
41
a
B
42
a
^C
$

(Above output could be a bit confusing, but when I entered A then Return, I sent A then \n (0x0a) to stdin, so the binary just displays these in their hexa representations ,41 and a)

Could someone explain this to me ? What did I miss ?

Thanks a lot for reading !

like image 224
pixis Avatar asked Dec 15 '22 12:12

pixis


1 Answers

Once you encounter EOF, getchar will return immediately with return value EOF. The stream pointer for the stream will not advance, it will stay on the EOF marker. Subsequent calls to getchar will also return immediately, since the stream is still at the EOF marker.

Note that this differs from the behaviour when stdin is attached to an input device. In that case, getchar will pause, waiting for further input if the input buffer is empty. getcharwill not return EOF until an EOF is sent from the input device (CTRL-D sends EOF from a keyboard in Linux).

like image 95
Klas Lindbäck Avatar answered Jan 05 '23 17:01

Klas Lindbäck