I am currently learning C and I am working on a task to improve some C-Code that might lead to a program crash.
Here is the code:
int main()
{
// Define buffers to store username and password
char username[16];
char password[16];
// Read username and password from user input
printf("Enter your name: ");
scanf("%s", username);
printf("Enter your password: ");
scanf("%s", password);
printf("[SHOUTING OUT LOUD] Hello, %s!\n", username);
return 0;
}
How can I make sure, that the input is not longer than 15 chars? Otherwise, the program could accidentally print out the password or overwrite the return address on the stack, which was subject of my other question:
Is it possible to crash this program somehow?
I already thought about putting the variables on the heap, but in the beginning, I don't know how long the input is. So I don't know, how much space I shall allocate.
Can somebody help me with this?
Thanks :)
Use a length modifier with the amount of bufferlength - 1 for the %s conversion specifier.
In this case, as both arrays are consisted of 16 char elements, use %15s.
Note: Always check the return value of input functions, if an error occurred at consuming the input.
printf("Enter your name: ");
if ( scanf("%15s", username)) != 1 )
{
fputs("Error at input - username!", stderr);
exit(1);
}
// No explicit need for a call to getchar() to catch the newline from stdin
// in your program. %s skips leading white space.
printf("Enter your password: ");
if ( scanf("%15s", password)) != 1 )
{
fputs("Error at input - password!", stderr);
exit(1);
}
or use fgets() instead:
printf("Enter your name: ");
if ( fgets(username, sizeof(username), stdin) == NULL )
{
fputs("Error at input - username!", stderr);
exit(1);
}
// fgets can catch the newline with. If this is the case, we need or want to
// remove this newline from the string by replacing it with '\0'.
// Else, we need to catch the left newline from stdin so that it is not
// catched by fetching the password.
size_t len = sizeof(username);
for ( size_t i = 0; i < len; i++ ) // Iterate through the buffered string.
{
if ( username[i] == '\n' )
{
username[i] = '\0'; // Replace newline with NUL.
break;
}
if ( i == len - 1 )
{
getchar(); // If no newline was in the string,
} // catch left newline from stdin.
}
printf("Enter your password: ");
if ( fgets(password, sizeof(password, stdin) == NULL )
{
fputs("Error at input - password!", stderr);
exit(1);
}
password[strcspn(password, "\n")] = '\0'; // Replace newline with NUL.
Note that although the latter use of fgets() seems more complicated, fgets() is general more safe than scanf() and the preferred choice to read strings.
Take a look at:
Disadvantages of scanf
Related
This question already has answers here:
scanf() leaves the newline character in the buffer
(7 answers)
Closed 1 year ago.
char toFind[100];
char replace[100];
int pos = 0;
printf("Enter a text: ");
scanf("%[^\n]", str);
printf("Enter a search pattern: ");
scanf("%[^\n]", toFind);
printf("Enter a substitute: ");
scanf("%[^\n]", replace);
pos = strnfnd(0, toFind);
strins(pos, replace);
printf("Der Text ist: %s", str);
This code sample let me read the value for str but skips the other two scanf. I have no idea why.
What can I do to fix this?
Ps: str is a global char array
After this call of scanf
scanf("%[^\n]", str);
the new line character '\n' still is present in the input buffer.
So the next call of scanf
scanf("%[^\n]", toFind);
that reads input until the new line character '\n' is encountered reads nothing.
You should write for example
scanf("%[^\n]%*c", str);
to remove the new line character '\n' from the input buffer.
Here is a demonstration program
#include <stdio.h>
int main( void )
{
char s[100];
scanf( "%99[^\n]%*c", s );
puts( s );
scanf( "%99[^\n]", s );
puts( s );
return 0;
}
In this case if to enter strings for example like
Hello
World
then the output will be
Hello
World
Another and more simple approach is to prepend the format string with a blank. For example
scanf(" %[^\n]", toFind);
^^^^
In this case all leading white space characters will be skipped.
Here is a demonstration program.
#include <stdio.h>
int main( void )
{
char s[100];
scanf( "%99[^\n]", s );
puts( s );
scanf( " %99[^\n]", s );
puts( s );
return 0;
}
In this case if to enter strings as shown above that is
Hello
World
then the program output will be
Hello
World
It depends where you run the compiled code - the operating system. In some cases when the "Enter" key is pressed, two bytes are emitted in the input stream (10 and 13) instead of '\n' (10). In that case you need to flush the extra symbols. Check the comments for the exact implementation.
There are problems with your scanf() usage: scanf("%[^\n]", str);
you do not specify the maximum number of characters to store into str, hence a sufficiently long line typed by the user will cause undefined behavior. This is a typical software flaw hackers can try and exploit. You can prevent this by specifying the limit as scanf("%99[^\n]", str);
you do not read the trailing newline entered by the user, so the next call scanf("%[^\n]", toFind); will fail because no characters different from '\n' are present in the input stream, since the first pending byte is '\n', or EOF.
you do not check the return value of the scanf() calls, so you cannot detect input errors such as the above.
note however that scanf("%99[^\n]", str); will fail if the user types enter immediately and str will not be modified in this case.
It is much safer to use fgets() instead of scanf() for this task:
char str[100];
char toFind[100];
char replace[100];
int pos = 0;
printf("Enter a text: ");
if (!fgets(str, sizeof str, stdin)) {
printf("input error\n");
return 1;
}
str[strcspn(str, "\n")] = '\0'; /* strip the trailing newline if any */
printf("Enter a search pattern: ");
if (!fgets(toFind, sizeof toFind, stdin)) {
printf("input error\n");
return 1;
}
toFind[strcspn(toFind, "\n")] = '\0'; /* strip the trailing newline if any */
printf("Enter a substitute: ");
if (!fgets(replace, sizeof replace, stdin)) {
printf("input error\n");
return 1;
}
replace[strcspn(replace, "\n")] = '\0'; /* strip the trailing newline if any */
pos = strnfnd(0, toFind);
strins(pos, replace);
printf("Der Text ist: %s\n", str);
I want to enter multiple printfs but i dont get opportunity to enter.
I can enter only 1, but after that it just ends the programme.I tried with do while but it didnt work
int main()
{
int number;
char username[30]="";
char fullName[30]="";
char password[30]="";
printf("Do you want to log in(1) or register (2)? \n");
scanf("%d",&number);
if (number==2)
{
printf("username : ");
scanf("%s",&username);
printf("Full name : ");
scanf("%s",&fullName);
printf("Password : ");
scanf("%s",&password);
printf("Repeat password : ");
scanf("%s",&password);
}
return 0;
}
Read full lines using fgets() into a suitably large buffer, then parse that.
Note that %s will stop at the first blank character, so a full name of "Mr X" will leave "X" in the input buffer, grabbing that for the password and so on. It's really not a robust way of getting input.
I can enter only 1, but after that it just ends the programme.
Of course, as the code has if (number==2) #Scadge
If you enter "2", consider the following:
scanf("%s",&fullname); will not save spaces or other white-spaces into fullname. Entering a full name like "John Doe" will save "John" into fullname and "Doe" into password.
Avoid using scanf().
Rather than use scanf() to read user input, read user input with fgets(). This is a fine opportunity for helper functions that can handle various input issues.
int read_int(const char *prompt) {
if (prompt) fputs(prompt, stdout);
fflush(stdout); // insure output is written before asking for input
char buffer[40];
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
return NULL;
}
int i;
if (sscanf(buffer, "%d", &i) == 1) {
return i;
}
// TBD - what should code do if invalid data entered. Try again?
}
char *read_line(char *dest, sizeof size, const char *prompt) {
if (prompt) fputs(prompt, stdout);
fflush(stdout); // insure output is written before asking for input
char buffer[size * 2 + 1]; // form buffer at _least 1 larger for \n
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
return NULL;
}
size_t len = strlen(buffer);
if (len > 0 && buffer[len-1] == '\n') buffer[--len] = '\0';
if (len >= size) {
// input too big - how do you want to handle this?
TBD_Code();
}
return strcpy(dest, buffer);
}
Now use these 2 helper functions for clean user input
// printf("Do you want to log in(1) or register (2)? \n");
// scanf("%d",&number);
number = read_int("Do you want to log in(1) or register (2)? \n");
...
// printf("username : ");
// scanf("%s",&username);
read_line(username, sizeof username, "username : ");
// printf("Full name : ");
// scanf("%s",&fullName);
read_line(fullName, sizeof fullName, "fullName : ");
Additional code could be added to check for end-of-file, extremely long lines, int range testing, etc.
Use c library function fgets().
#include <stdio.h>
int main(){
Char username[10];
printf(“Username: “);
fgets(username,10,stdin);
}
Given the following code:
#include <stdio.h>
int main()
{
int testcase;
char arr[30];
int f,F,m;
scanf("%d",&testcase);
while(testcase--)
{
printf("Enter the string\n");
fgets(arr,20,stdin);
printf("Enter a character\n");
F=getchar();
while((f=getchar())!=EOF && f!='\n')
;
putchar(F);
printf("\n");
printf("Enter a number\n");
scanf("%d",&m);
}
return 0;
}
I want a user to enter a string, a character and a number until the testcase becomes zero.
My doubts / questions:
1.User is unable to enter a string. It seems fgets is not working. Why?
2.If i use scanf instead of fgets,then getchar is not working properly, i.e whatever character I input in it just putchar as a new line. Why?
Thanks for the help.
Mixing functions like fgets(), scanf(), and getchar() is error-prone. The scanf() function usually leaves a \n character behind in the input stream, while fgets() usually does not, meaning that the next call to an I/O function may or may not need to cope with what the previous call has left in the input stream.
A better solution is to use one style of I/O function for all user input. fgets() used in conjunction with sscanf() works well for this. Return values from functions should be checked, and fgets() returns a null pointer in the event of an error; sscanf() returns the number of successful assignments made, which can be used to validate that input is as expected.
Here is a modified version of the posted code. fgets() stores input in a generously allocated buffer; note that this function stores input up to and including the \n character if there is enough room. If the input string is not expected to contain spaces, sscanf() can be used to extract the string, leaving no need to worry about the newline character; similarly, using sscanf() to extract character or numeric input relieves code of the burden of further handling of the \n.
#include <stdio.h>
int main(void)
{
int testcase;
char arr[30];
char F;
int m;
char buffer[1000];
do {
puts("Enter number of test cases:");
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
/* handle error */
}
} while (sscanf(buffer, "%d", &testcase) != 1 || testcase < 0);
while(testcase--)
{
puts("Enter the string");
/* if string should not contain spaces... */
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
/* handle error */
}
sscanf(buffer, "%29s", arr);
printf("You entered: %s\n", arr);
putchar('\n');
puts("Enter a character");
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
/* handle error */
}
sscanf(buffer, "%c", &F);
printf("You entered: %c\n", F);
putchar('\n');
do {
puts("Enter a number");
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
/* handle error */
}
} while (sscanf(buffer, "%d", &m) != 1);
printf("You entered: %d\n", m);
putchar('\n');
}
return 0;
}
On the other hand, if the input string may contain spaces, fgets() can read input directly into arr, but then the stored string will contain a \n character, which should probably be removed. One way of doing this is to use the strcspn() function to find the index of the \n:
#include <string.h> // for strcspn()
/* ... */
puts("Enter the string");
/* or, if string may contain spaces */
if (fgets(arr, sizeof arr, stdin) == NULL) {
/* handle error */
}
/* replace newline */
arr[strcspn(arr, "\r\n")] = '\0';
printf("You entered: %s\n", arr);
putchar('\n');
/* ... */
Note that a maximum width should always be specified when using %s with the scanf() functions to avoid buffer overflow. Here, it is %29s when reading into arr, since arr can hold 30 chars, and space must be reserved for the null terminator (\0). Return values from sscanf() are checked to see if user input is invalid, in which case the input is asked for again. If the number of test cases is less than 0, input must be entered again.
Finally got the solution how can we use scanf and fgets together safely.
#include <stdio.h>
int main()
{
int testcase,f,F,m;
char arr[30];
scanf("%d",&testcase);
while((f=getchar())!=EOF && f!='\n')
;
while(testcase--)
{
printf("Enter the string\n");
fgets(arr,30,stdin);
printf("Enter a character\n");
F=getchar();
while((f=getchar())!=EOF && f!='\n')
;
putchar(F);
printf("\n");
printf("Enter a number\n");
scanf("%d",&m);
while((f=getchar())!=EOF && f!='\n')
;
}
}
We need to make sure that before fgets read anything,flushout the buffer with simple while loop.
Thanks to all for the help.
A simple hack is to write a function to interpret the newline character. Call clear() after each scanf's
void clear (void){
int c = 0;
while ((c = getchar()) != '\n' && c != EOF);
}
Refer to this question for further explaination: C: Multiple scanf's, when I enter in a value for one scanf it skips the second scanf
What is the difference between fgets() and gets()?
I am trying break my loop when the user hits just "enter". It's working well with gets(), but I don't want to use gets().
I tried with fgets() and scanf() but I don't have the same results as with gets(). fgets() breaks the loop whatever user enters in text! Here is my code :
void enter(void)
{
int i,
for(i=top; i<MAX; i++)
{
printf(".> Enter name (ENTER to quit): ");
gets(cat[i].name);
if(!*cat[i].name)
break;
printf(".> Enter Last Name: ");
scanf("%s",cat[i].lastname);
printf(".> Enter Phone Number: ");
scanf("%s",cat[i].phonenum);
printf(".> Enter e-Mail: ");
scanf("%s",cat[i].info.mail);
printf(".> Enter Address: ");
scanf("%s",cat[i].info.address);
printf("\n");
}
top = i;
}
A difference between gets() and fgets() is that fgets() leaves the newline in the buffer. So instead of checking whether the first element of the input is 0, check whether it's '\n';
fgets(cat[i].name, sizeof cat[i].name, stdin);
if (cat[i].name[0] == '\n' || cat[i].name[0] == 0) {
// empty line or no input at all
break;
} else {
// remove the trailing newline
int len = strlen(cat[i].name);
cat[i].name[len-1] = 0;
}
Drop gets() and scanf().
Create a helper function to handle and qualify user input.
// Helper function that strips off _potential_ \n
char *read1line(const char * prompt, char *dest, sizeof size) {
fputs(prompt, stdout);
char buf[100];
*dest = '\0';
if (fgets(buf, sizeof buf, stdin) == NULL) {
return NULL; // EOF or I/O error
}
// Remove potential \n
size_t len = strlen(buf);
if (len > 0 && buf[len-1] == '\n') {
buf[--len] = `\0`;
}
// Line is empty or too long
if (len == 0 || len >= size) return NULL;
return memcpy(dest, buf, len+1);
}
void enter(void)
{
int i;
for(i=top; i<MAX; i++)
{
if (read1line(".> Enter name (ENTER to quit): ",
cat[i].name, sizeof cat[i].name) == NULL) break;
if (read1line(".> Enter Last Name: ",
cat[i].lastname, sizeof cat[i].lastname) == NULL) break;
if (read1line(".> Enter Phone Number: ",
cat[i].phonenum, sizeof cat[i].phonenum) == NULL) break;
if (read1line(".> Enter e-Mail: ",
cat[i].info.mail, sizeof cat[i].info.mail) == NULL) break;
if (read1line(".> Enter Address: ",
cat[i].info.address, sizeof cat[i].info.address) == NULL) break;
}
top = i;
}
Some attributes of fgets() and gets():
fgets() reads input and saves to a buffer until:
1) The buffer is 1 shy of being full - or -
2) '\n' is encountered - or -
3) The stream reaches an end-of-file condition - or -
4) An input error occurs.
gets() does #2 - #4 above except it scans, but does not save a '\n'.
gets() is depreciated in C99 and no longer part of C11.
The problematic difference between gets and fgets is that gets removes the trailing '\n' from an input line but fgets keeps it.
This means an 'empty' line returned by fgets will actually be the string "\n".
The nasty difference, that means it's best to avoid gets altogether, is that if you give gets a line that's too long your program will crash in very bad ways.
you can use fgets() with STDIN instead.
This function is secured and always insert a '\0' at the string end.
An example:
char inputbuffer[10];
char *p;
p = fgets(inputbuffer, sizeof(inputbuffer), stdin);
printf(">%s<\n", p); /* p is NULL on error, but printf is fair */
You'll get at most 9 characters + '\0', in this example.
I have the below code which is supposed to exit if the provided user input is empty i.e they press [ENTER] when asked for the input. However it doesnt do anything if [ENTER] is pressed.
printf("Enter the website URL:\n");
scanf("%s", str);
if(strlen(str) == 0) {
printf("Empty URL");
exit(2);
}
If the user just presses enter, the input will still contain a newline ('\n'). Your condition should be
if (!strcmp(str, "\n"))
I use a isempty function:
int isempty(const char *str)
{
for (; *str != '\0'; str++)
{
if (!isspace(*str))
{
return 0;
}
}
return 1;
}
Also, I would recommend using fgets over scanf, as scanf is unsafe and can lead to buffer overflows.
fgets(str, /* allocated size of str */, stdin);
%s with scanf() will discard any leading whitespace, of which it considers your Enter keypress. If you want to be able to accept an "empty" string, you'll need to accept your input in another way, perhaps using fgets():
printf("Enter the website URL:\n");
fgets(str, SIZE_OF_STR, stdin);
if(!strcmp(str,"\n")) {
printf("Empty URL");
exit(2);
}
Keep in mind the above code does not consider EOF, which would leave str unchanged.
shouldn't you check for '\n' - new line? Enter will represented as a new line character '\n'