Safe Version of gets()

Dave Sinkula 0 Tallied Votes 2K Views Share

Never use the function gets . Why? It is unsafe. Since the maximum string size cannot be specified, it is always susceptible to buffer overflow.

This snippet shows one way to do a safe version of gets . It reads characters from the stdin into a string up to the specified size and discards the trailing newline. It does not remove excess characters from the stdin either.

See also Read a Line of Text from the User.

#include <stdio.h>

char *sgets(char *line, size_t size)
{
   size_t i;
   for ( i = 0; i < size - 1; ++i )
   {
      int ch = fgetc(stdin);
      if ( ch == '\n' || ch == EOF )
      {
         break;
      }
      line[i] = ch;
   }
   line[i] = '\0';
   return line;
}

int main(void)
{
   int i;
   for ( i = 0; i < 3; ++i )
   {
      char text[20] = "";
      fputs("prompt: ", stdout);
      fflush(stdout);
      printf("text = \"%s\"\n", sgets(text, sizeof text));
   }
   return 0;
}

/* my input/output
prompt: 1234567890123456789012345
text = "1234567890123456789"
prompt: text = "012345"
prompt: hello world
text = "hello world"
*/
jscud 0 Newbie Poster

Could you instead just use fscanf with a formatted string which includes the max number of characters to be read? For example, with an 8 char buffer this would be

int found = fscanf(stdin, "%7[^\n]\n" buffer);

WaltP 2,905 Posting Sage w/ dash of thyme Team Colleague

You could, but a real programmer wouldn't use scanf() or fscanf() to read strings in the first place. Dave's suggestion is the second best choice. Best is to use fgets()

camdaddy09 -2 Newbie Poster

why is scanf() unreliable and unwanted?

Narue 5,707 Bad Cop Team Colleague

why is scanf() unreliable and unwanted?

scanf can be reliable when used correctly, but it's typically unwanted for string input because achieving reliability in that case is more difficult than simply using a more appropriate function. For example, here is fgets simulated using fscanf:

char *myfgets(char *dst, size_t limit, FILE *in)
{
    char fmt[50]; /* Arbitrary "big" size; laziness FTW */
    int len;

    /* Necessary to set a variable-based field width */
    sprintf(fmt, "%%%d[^\n]%%n", limit - 1);

    if (fscanf(in, fmt, dst, &len) != 1)
    {
        return NULL;
    }

    /* Is the buffer full? */
    if (len < limit - 1)
    {
        /* Extract and store the newline */
        dst[len++] = getc(in);
        dst[len] = '\0';
    }

    return dst;
}

On top of a relatively complex format string, the format string needs to be generated using sprintf to avoid using a magic number in many cases and because the magic number is pretty much impossible in this case (the limit is an unknown quantity by myfgets until runtime).

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.