Why does an fread loop require an extra Ctrl+D to signal EOF with glibc?

前端 未结 1 1093
难免孤独
难免孤独 2021-02-19 19:13

Normally, to indicate EOF to a program attached to standard input on a Linux terminal, I need to press Ctrl+D once if I just pressed Enter, or twice otherwise. I noticed that th

1条回答
  •  囚心锁ツ
    2021-02-19 19:58

    I've managed to confirm that this is due to an unambiguous bug in glibc versions prior to 2.28 (commit 2cc7bad). Relevant quotes from the C standard:

    The byte input/output functions — those functions described in this subclause that perform input/output: [...], fread

    The byte input functions read characters from the stream as if by successive calls to the fgetc function.

    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.

    (emphasis on "or" mine)

    The following program demonstrates the bug with fgetc:

    #include 
    
    int main(void) {
        while(fgetc(stdin) != EOF) {
            puts("Read and discarded a character from stdin");
        }
        puts("fgetc(stdin) returned EOF");
        if(!feof(stdin)) {
            /* Included only for completeness. Doesn't occur in my testing. */
            puts("Standard violation! After fgetc returned EOF, the end-of-file indicator wasn't set");
            return 1;
        }
        if(fgetc(stdin) != EOF) {
            /* This happens with glibc in my testing. */
            puts("Standard violation! When fgetc was called with the end-of-file indicator set, it didn't return EOF");
            return 1;
        }
        /* This happens with musl in my testing. */
        puts("No standard violation detected");
        return 0;
    }
    

    To demonstrate the bug:

    1. Compile the program and execute it
    2. Press Ctrl+D
    3. Press Enter

    The exact bug is that if the end-of-file stream indicator is set, but the stream is not at end-of-file, glibc's fgetc will return the next character from the stream, rather than EOF as the standard requires.

    Since fread is defined in terms of fgetc, this is the cause of what I originally saw. It's previously been reported as glibc bug #1190 and has been fixed since commit 2cc7bad in February 2018, which landed in glibc 2.28 in August 2018.

    0 讨论(0)
提交回复
热议问题