Search code examples
cstdinflushfgets

How to prevent the user from entering more data than the maximum limit?


This code asks the user for data and subsequently a number:

$ cat read.c
#include<stdio.h>
#include<stdlib.h>
#define MAX 10

int main() {
    char* c = (char*) malloc(MAX * sizeof(char));
    int num;

    printf("Enter data (max: %d chars):\n", MAX);
    fgets(c, MAX, stdin);
    // how do I discard all that is there on STDIN here?

    printf("Enter num:\n");
    scanf("%d", &num);

    printf("data: %s", c);
    printf("num: %d\n", num);
}
$

The problem is that apart from the instruction that states the maximum number of chars, there is nothing that stops the user from entering more, which is subsequently read into num as junk:

$ ./read
Enter data (max 10 chars):
lazer
Enter num:
5
data: lazer
num: 5
$ ./read
Enter data (max 10 chars):
lazerprofile
Enter num:
data: lazerprofnum: 134514043
$ 

Is there a way to discard all that is there on STDIN after the fgets call?


Solution

  • The scanf() function is terrible for user input, and it's not that great for file input unless you somehow know your input data is correct (don't be that trusting!) Plus, you should always check the return value for fgets() since NULL indicates EOF or some other exception. Keep in mind that you get the user's newline character at the end of your fgets() data unless the maximum is reached first. I might do it this way as a first pass:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #define MAX 10
    
    void eat_extra(void) {
        int ch;
    
        // Eat characters until we get the newline
        while ((ch = getchar()) != '\n') {
            if (ch < 0)
                exit(EXIT_FAILURE); // EOF!
        }
    }
    
    int main() {
        char c[MAX+1]; // The +1 is for the null terminator
        char n[16]; // Arbitrary maximum number length is 15 plus null terminator
        int num;
    
        printf("Enter data (max: %d chars):\n", MAX);
        if (fgets(c, MAX, stdin)) { // Only proceed if we actually got input
            // Did we get the newline?
            if (NULL == strchr(c, '\n'))
                eat_extra(); // You could just exit with "Too much data!" here too
    
            printf("Enter num:\n");
            if (fgets(n, sizeof(n) - 1, stdin)) {
                num = atoi(n); // You could also use sscanf() here
                printf("data: %s", c);
                printf("num: %d\n", num);
            }
        }
    
        return 0;
    }