I have a function that is supposed to get a number from stdin
. It's supposed to check whether it's a valid number and optionally set it between a specific range. If the input is reasonably long (say 10 chars), then the function prints the error message and resets the loop, everything works as intended. However if I input something ridiculously long like:
1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
then something goes wrong. The loop keeps resetting but no matter what I type it doesn't accept it anymore, even if it would totally valid.
I guess this could be because stdin
is somehow overflown? But isn't that the point of fgets()
- it only reads a specific number of chars and discards everything else? How can I get around this without the use of exceptions?
The function in question:
int safeinp(int * num, const char *message, int low, int high)
{
long a;
char buf[11]; // 9 digits for the number + "\n\0"
int success; // flag for successful conversion
do
{
puts(message);
if (!fgets(buf, 11, stdin))
{
fprintf(stderr, "Unagle to obtain input.\n");
return 1;
}
// have some input, convert it to integer:
char *endptr;
a = strtol(buf, &endptr, 12);
if (errno == ERANGE)
{
//this if() right here is what gets executed endlessly if the input is bad
fprintf(stderr, "Invalid number.\n");
success = 0;
}
else if (endptr == buf)
{
fprintf(stderr, "Invalid input.\n");
success = 0;
}
else if (*endptr && *endptr != '\n')
{
fprintf(stderr, "Conversion error.\n");
success = 0;
}
else
{
success = 1;
if (low != high) {
a = (a < low) ? fprintf(stderr, "Input has been adjusted to fit the bounds.\n"), low : a;
a = (a > high) ? fprintf(stderr, "Input has been adjusted to fit the bounds.\n"), high : a;
}
*num = a;
}
} while (!success);
return success;
}
Problem occurs in Visual Studio 2017 for Windows 10.
No, fgets()
does not empty the "buffer". If you enter a long string, your code will read 10 characters at a time until it reaches the end-of-line of your input. The next loop will wait again.
Stripped down example:
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char **argv)
{
char buf[11];
do {
puts("TEST");
fflush(stdout);
if (!fgets(buf, sizeof(buf), stdin)) {
fprintf(stderr, "Unagle to obtain input.\n");
return 1;
}
printf("input: %s\n", buf);
} while (1);
return(0);
}
Test run:
$ gcc -Wall -o dummy dummy.c
$ ./dummy
TEST
123456789012345678901234567890
input: 1234567890
TEST
input: 1234567890
TEST
input: 1234567890
TEST
input:
TEST
^C
$
UPDATE: proposal which tries to eat all remaining characters until newline:
/* replacement for fgets(buf, sizeof(buf), stdin) */
char *p = buf;
char c;
unsigned left = sizeof(buf) - 1;
while ((left-- > 0) && ((c = fgetc(stdin)) != '\n')) {
if (feof(stdin)) {
return(1);
}
*p++ = c;
}
*p++ = '\0';
/* eat the rest until newline */
while (c != '\n') {
c = fgetc(stdin);
if (feof(stdin)) {
return(1);
}
}
New test run:
$ gcc -Wall -o dummy dummy.c
$ ./dummy
TEST
123456789012345678901234567890
input: 1234567890
TEST
1
input: 1
TEST
1234567890
input: 1234567890
TEST
^C