Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Read from stdin with fgets, bug if input is greater than size

Tags:

c

I'm creating a command line application with a prompt. It works fine as long as the input fits inside the buffer, but when the input is larger I get some weird behavior. Below is a simplified minimal example which has the same bug.

int main()
{
    for (;;) {
        const int size = 8;
        char str[size];

        printf("> ");
        fgets(str, size, stdin);

        toupper_str(str);
        printf("Result: %s\n", str);
    }
}

It works fine if the input is in smaller than size.

> asdf
Result: ASDF
> 

When the input is larger, the portion of the input in range is processed, and in the next loop iteration, the rest of the given input is immediately returned from fgets. This leads to that portion of the input also being processed and some weird output.

> asdfghjk
Result: ASDFGHJ
> Result: K

> 

I can see if the input is larger than or equal to size by comparing the last character to a newline. fgets retains the newline as long as it fits.

fgets(str, size, stdin);
if (str[strlen(str) - 1] != '\n') {
    fprintf(stderr, "Input too long\n");
}

When this is detected, how do I stop it from reading the rest of the too long input on the next iteration?

I've seen similar questions on here, but none that ask the same question.

like image 855
jacwah Avatar asked Jan 09 '23 04:01

jacwah


1 Answers

how do I stop it from reading the rest of the too long input on the next iteration?

Code needs to 1) detect if input is "too long" 2) consume the additional input.

fgets() will not overfill it buffer. If it does fill the buffer, the last char in the buffer is '\0'. So set that to non-'\0' before reading. Then code knows if the entire buffer was filled. Then check if the preceding char was a '\n'. If it is not an end-of-line, additional char may exist in stdin

char str[100];  // Insure buffer is at least size 2
for (;;) {
  str[sizeof str - 1] = `x`;
  if (fgets(str, size, stdin) == NULL) {
    // No more to read or IO error
    break;
  }
  int extra_data_found = 0;
  if (str[sizeof str - 1] == '\0' && str[sizeof str - 2] != '\n') {
    // Cope with potential extra data in `stdin`: read and toss
    int ch;
    while ((ch = fgetc(stdin)) != '\n' && ch != EOF) {
      extra_data_found = 1;
    }
  }
  // Use `str` as needed, noting if additional unsaved data found
  foo(str, extra_data_found);
}

Note: on a file error, fgets() returns NULL and the contents of str are undefined.

Note: Instead of str[sizeof str - 1] == '\0', code could use strlen(str) == sizeof str - 1. This gets fooled should fgets() read a null character '\0'.

Corner cases:
1. Typical str will be up to 98 char and then '\n' and '\0'. Is it OK is the last str is 99 char and then '\0'?
2. If #1 is OK, then may a typical str have 99 char and then '\0'?

like image 104
chux - Reinstate Monica Avatar answered Jan 14 '23 19:01

chux - Reinstate Monica