Other Ways to Fix fgets() Input

The C language string-input function was once gets(). That function has become Chernobyl in that it exists but you go there only when you don’t care about your health.

The problem with gets() was that it lacked bounds-checking; you could input an infinite number of characters, overflowing the buffer. This process is how various trojans and worms were created long ago. So instead of gets(), I recommend using the fgets() function, which features bounds-checking.

Alas, the fgets() function also retains the newline character at the end of input, which is often not what you want. So further processing is necessary when you must peel off the trailing newline.

A solution I offered in a blog post from 2013 uses fgets() in an input() function that strips away the newline. Here is that function:

void input(char *string,int length)
{
    int x;

    fgets(string,length,stdin);
    for(x=0;x<=length;x++)
    {
        if( string[x] == '\n')
        {
            string[x] = '\0';
            break;
        }
    }
}

The input() function gathers text from standard input, up to the value set by variable length, then it churns through the buffer, replacing the newline ('\n') with a null character ('\0'). The code works, but I wrote it to be illustrative so it’s clunkier than what I’d normally code.

Recently, I had to craft the input() function for another program. After I was done, I compared what I wrote today with the input() function from four years ago. Here’s my newer version:

void input(char *string,int length)
{
    fgets(string,length,stdin);
    while(*string != '\n')
        string++;
    *string = '\0';
}

The difference is how I located the newline and replaced it with the null character. Instead of a for loop, I used while. Instead of array notation, I used pointers. Otherwise, the code has the same result: The newline is gone, replaced by a null character.

I could make the loop tighter, but at this point it becomes obfuscated:

void input(char *string,int length)
{
    fgets(string,length,stdin);
    while(*string++ != '\n')
        ;
    *--string = '\0';
}

Incrementing the string variable takes place in the while loop’s condition. The ++ operator is suffixed, which means that the pointer is incremented after its value is fetched.

Because the pointer is post-incremented, it must be decremented after the loop to replace '\n' with '\0'. So, in the final statement, the -- is prefixed to the string pointer variable; its location is decremented before the value is assigned.

Yeah, the condensed code can be confusing. Not only does it use pointers, but unless you use these techinques all the time, prefixing and suffixing operators causes most programmers to stop and think about the order of operations.

Is there a reason to go one way or the other?

No. I prefer readable code, so when I compose a program I typically code a function as illustrated in the first example. The same process holds true with the ternary operator (?:): I write the code the long way, then reduce it down later. Still, as long as the program does what you want, you’re good.

Here is the full program, the modified version of the one I presented originally back in June, 2013:

#include <stdio.h>

/* This function has a flaw; see the comments to this post */
void input(char *string,int length)
{
    fgets(string,length,stdin);
    while(*string++ != '\n')
        ;
    *--string = '\0';
}

int main()
{
    char firstname[32],lastname[32];

    printf("What is your first name? ");
    input(firstname,32);
    printf("What is your last name? ");
    input(lastname,32);
    printf("Please to meet you %s %s.\n",
            firstname,
            lastname);

    return(0);
}

2 thoughts on “Other Ways to Fix fgets() Input

  1. When I compile and run the final code if I enter more than 30 characters for the first or last name I get a Segmentation Fault. How can this be avoided? (By comparison, when using the ‘for loop’ method any characters entered in excess of the variable size are ignored.

  2. You are correct, and I appreciate that you pointed out my error. Here’s what happened:

    The fgets() function does its job, truncating input at 31 characters, but the stream still contains characters. Therefore, the while loop continues to look for that newline and it overflows the buffer. That’s how you get a segmentation fault.

    I’ve redone the function, shown below, to account for stream input and a lack of newline. The function now truncates input at the value of the length variable.

    void input(char *string,int length)
    {
        int x=0;
    
        fgets(string,length,stdin);
        while(*string)
        {
            if(*string == '\n')
            {
                *string = '\0';
                return;
            }
            x++;
            if(x == length)
            {
                *string = '\0';
                return;
            }
            string++;
        }
    }

    Thank you again for pointing out my error. I feel foolish for not completely testing the function before I posted it.

Leave a Reply