Search code examples
cfgetslibc

Check incomplete line in fgets()


To detect it, I've seen the following if condition:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(){
  char line[5];
  while(fgets(line, 5, stdin)){
    int length = strlen(line);
    if (length == 4 && line[3] != '\n') {
      puts("line too long");
      return 1;
    }
  }
  puts("correct file");
  return 0;
}

which works here:

$ cc test.c -o test
$ printf '012\n' | ./test
correct file
$ printf '012' | ./test
correct file
$ printf '0123\n' | ./test
line too long

but fails when the length is equal to the maximum and it doesn't have a trailing newline:

$ printf '0123' | ./test
line too long

The detection was incorrect as the entire line fits in the buffer; there is no more to read from the file.


Solution

  • the entire line fits in the buffer; there is no more to read from the file.

    So check that,

    The function feof() checks if the previous operation ended on eof. If fgets read 4 characters, it did not end on eof, it ended reading after 4 characters. You have to peek "one more" character in front to trigger setting eof.

    ungetc(fgetc(stdin), stdin);
    if (length == 4 && (line[3] != '\n' || feof(stdin))) {
    

    Or:

    if (length == 4) {
      int c = getc(stdin);
      if (c == EOF) break;
      ungetc(c, stdin);
      if (line[3] != '\n') {
        puts("line too long");
        return 1;
      }
    }