Originally published by Robert Beisert at fortcollinsprogram.robert-beisert.com

Linux + C – Reading from stdin

We know how to send input from the terminal. Now we’ll look at the right and wrong ways to do keyboard input within your program.

Reading Input from Standard Input

Have you ever seen a program which used a prompt to ask for something? For a simple case, it could look like this:

Username:
>>
Password:
>>

There are numerous functions in C which can create prompts and read responses like these, but many are extremely dangerous.

For example, we have the function scanf(). scanf() takes two arguments: a string, and a destination. That makes it fairly easy, because we can write something like this:

int age;
printf(“Enter your age below:n”);
scanf(“%d”, &age);

Another excellent example is gets(), which reads from the terminal directly into a string. That looks something like this:

char name[25];
printf(“Enter your first namen”);
gets(name);

There is a simple problem with both of these functions: they don’t care how big the input is. In the case of our gets() code, we could type

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

and the program would try to read it into the 25 character array we specified. If the input doen’t fit, it will try to overwrite other variables and (potentially) even code. This is no good.

The natural response is to use a function like fgets(), which takes three arguments: a string pointer (to store the input), the SIZE of the data we’ll accept, and the source of the input. That means we can write code like this:

char name[25];
printf(“Enter your last namen”);
fgets(name, 25, stdin);

which reads up to 25 characters from the standard input (terminal) stream and stores the result in the name[] array.

input.c

Note: There is still a potential buffer overflow here. Can you figure out how it works?


#include <stdio.h>
#include <stdlib.h>
//string.h contains the string copying and comparison functions we want
#include <string.h>

int main(int argc, char *argv[])
{
char first[25];
char last[25];
char last_copy[25];
int age;
int i

printf("Input your current age\n");
scanf("%d", &age);
//Scanf will read your whitespace characters and push them to the next read
//We have to clear them out with this
while(getchar() != '\n');

printf("Input your last name\n");
fgets(last, 25, stdin);

//Eliminate the \n character, which does get read into the character array
for(i=0; i<25;i++)
{
if(last[i] == '\n')
last[i] = '\0';
}

//strncpy allows us to copy one string into another string
//The unsafe version (no size check) is strcpy
strncpy(last_copy, last, 25);
printf("Input your first name\n");
gets(first);

//Check whether you managed to overflow the buffer with gets
if(strncmp(last, last_copy, 25))
{
printf("Well done. You learned the buffer overflow, you cheater\n");
strncpy(last, last_copy, 25);
printf("We'll fix that right quick...\n");
}
// The \t marker represents a tab, much like \n represents a newline
printf("%s\t%s\t%d\n", first, last, age);
return 0;
}