Defensive Input – Solution

Writing an input routine isn’t that difficult in C. I cobble together simple input routines all the time. The scanf() function is brilliant for such situations. When you have to test input, things get dicey, which I hope you discovered as you attempted this month’s exercise.

Take scanf() for example. Suppose you tried a solution with a simple scanf() for integer input:

scanf("%d",&input);

If your user is disobedient and types in a string, then the program may crash.

As an alternative, you could modify scanf() to fetch a single character:

scanf(%c",&input);

The problem here is that text input (a string) generates a response for each character in the input stream.

You could use fgets() to process input as a line of text. The issue you’ll discover with that function is that the newline at the end of the input is retained in the stream. The next input function reads the newline, which probably isn’t what you want.

If you tried and tested all the C library input functions, then you will come to realize that the best solution is simply to write your own input routine. Have that function anticipate any and all forms of input, eliminate the excess, and then process whatever was typed to generate the desired result.

Here is my solution:

#include <stdio.h>

#define LIMIT 24
#define TRUE 1
#define FALSE 0

int one_digit(void);

int main()
{
    int value;

    while(1)
    {
        printf("Enter a number 1 to 9: ");
        value = one_digit();
        if( value != -1 )
            break;
        puts("Incorrect input. Try again:");
    }
    printf("You input %d\nThank you!\n",value);

    return(0);
}

int one_digit(void)
{
    char text[LIMIT];
    int count = 0;
    int overflow = FALSE;
    char t = '\0';

    /* read in an entire string */
    while( t != '\n')
    {
        t = getchar();
        text[count] = t;
        count++;
        if (count >= LIMIT)
        {
            overflow = TRUE;
            count = 0;  /* reset buffer */
        }
    }

    /* check exit conditions */
    if(overflow)            /* too much text typed */
        return(-1);
    if( count != 2 )        /* more than one digit */
       return(-1);
                    /* check single digit range */
    if( text[0] >= '1' && text[0] <= '9' )
        return(text[0] - '0');  /* return value */
    else
        return(-1);
}

The one_digit() function at Line 26 handles the single-digit input chore. It’s job is to return a valid digit in the range of 1 to 9. Any other input, no matter how whacky, generates a -1 return value. So the endless while loop at Line 13 keeps spinning until valid input is received (Lines 16 – 18). Valid input breaks the loop.

To read input, the one_digit() function uses a throw-away buffer, defined as text[] at Line 28. The while loop at Line 34 reads standard input until a newline is intercepted.

Each character input (at Line 36) is stored in the text buffer (Line 37). A count variable checks the buffer size (Line 39). If the buffer overflows, the overflow variable is set TRUE (line 41) and the buffer is reset (Line 42). Input continues until the newline is encountered.

When the while loop stops, the buffer is examined by the exit conditions starting at Line 47. If the buffer was overflowed, condition -1 is returned (Lines 47 and 48). If the buffer contains more than a single character (Line 49), that’s also an error condition and -1 is returned. What’s left is a single character in the buffer. Line 52 checks to see if it’s in range and, if so, the character is converted to a value and returned at Line 53. All other conditions fail with the return statement at Line 55.

Yeah, that’s a lot of defensive programming, but the result is fairly bullet-proof when it comes to grabbing the right type of input.

As usual, other solutions are possible. As long as you can aggressively type anything at the prompt and have only valid input processed, then your solution is good.

Leave a Reply