The content of the while
loop will never be executed, because the condition in the line
while(user_len > 20) {
will be false at the start of the program, because user_len
will have the value 0
.
Also, the lines
char user ();
int user_name();
are not prototype declarations. They are forward declarations of functions that take an unspecified number of arguments. If you want to declare that they take no arguments, then you should change them to:
char user( void );
int user_name( void );
See the following question for more information:
Warning/error "function declaration isn't a prototype"
The following code has a serious problem:
printf("Enter your username:\n");
scanf("%s",User_data.username);
user_len = strlen(User_data.username);
if (user_len > 20) {
printf("\nusername is too long please enter again:%d\n",user_len);
} else {
printf("Username is: %s\n",User_data.username);
}
There is no point in checking for a buffer overflow after the buffer overflow has already occurred. The buffer overflow must be prevented in the first place, in order to prevent your program from invoking undefined behavior (i.e. to prevent your program from possibly crashing).
The easiest way to prevent the buffer overflow would be to change the scanf
format string from "%s"
to "%19s"
, which would limit the number of matched characters to 19
, so that it will not write more than 20
characters to User_data.username
(including the terminating null character).
However, this solution is not ideal, as it is possible that scanf
will leave non-whitespace characters from that line on the input stream (this is also possible when using "%s"
). These non-whitespace characters will likely cause trouble in the next loop iteration when scanf
is called.
For this reason, it would probably be best if you used the function fgets
instead of scanf
. The function fgets has the advantage that it will always read exactly one line at a time (provided that the supplied input buffer is large enough to store the whole line), which is not necessarily the case with scanf
.
Here is my solution to the problem, which uses fgets
instead of scanf
:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
struct User_name
{
int user_id;
char username[20];
char password[30];
} User_data;
//make all the variable
int i;
int user_len;
int pass_len;
//prototype function
int user_name( void );
int main( void )
{
//get username
user_name();
}
int user_name( void )
{
while( true )
{
char line[100];
char *p;
//prompt user for input
printf( "Enter your username: " );
//attempt to read one line of input
if ( fgets( line, sizeof line, stdin ) == NULL )
{
printf( "Error reading input!\n" );
exit( EXIT_FAILURE );
}
//find newline character
p = strchr( line, '\n' );
//make sure that entire line was read in (i.e. that
//the buffer was not too small)
if ( p == NULL )
{
//a missing newline character is ok on end-of-file condition
if ( !feof( stdin ) )
{
int c;
//discard remainder of line
do
{
c = getchar();
if ( c == EOF )
{
fprintf( stderr, "Error reading from input\n" );
return false;
}
} while ( c != '\n' );
goto line_too_long;
}
}
else
{
//remove newline character by overwriting it with null character
*p = '\0';
}
//find length of string
user_len = strlen( line );
//check if length is acceptable
if ( user_len >= 20 )
{
goto line_too_long;
}
//username is ok, so copy it
strcpy( User_data.username, line );
//break out of infinite loop
break;
line_too_long:
printf( "Line was too long, try again!\n" );
}
//print username
printf( "Username is: %s\n", User_data.username );
return user_len;
}
This program has the following behavior:
Enter your username: ThisIsAVeryLongUserName
Line was too long, try again!
Enter your username: ShortUserName
Username is: ShortUserName
Note that this program uses one goto
label and two goto
statements. Normally it is a good idea to try to not use goto
, but to use normal control flow statements instead. However, in this case, I believe that using goto
was appropriate. The alternative would have been to duplicate the line printf( "Line was too long, try again!\n" );
and use a continue
statement in two places in the program. I believe that it is better to handle this error in one place in the program (though I understand that this topic is highly controversial and that some people would consider it better to use code duplication).
Another issue worth mentioning is that you are using global variables (which I have taken over in my solution). This is considered bad programming style and should generally be avoided. Here is a modified version of my solution which avoids the use of global variables:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
struct user_data
{
int user_id;
char username[20];
char password[30];
};
//prototype function
int user_name( void );
int main( void )
{
//get username
user_name();
}
int user_name( void )
{
struct user_data ud;
int user_len;
while( true )
{
char line[100];
char *p;
//prompt user for input
printf( "Enter your username: " );
//attempt to read one line of input
if ( fgets( line, sizeof line, stdin ) == NULL )
{
printf( "Error reading input!\n" );
exit( EXIT_FAILURE );
}
//find newline character
p = strchr( line, '\n' );
//make sure that entire line was read in (i.e. that
//the buffer was not too small)
if ( p == NULL )
{
//a missing newline character is ok on end-of-file condition
if ( !feof( stdin ) )
{
int c;
//discard remainder of line
do
{
c = getchar();
if ( c == EOF )
{
fprintf( stderr, "Error reading from input\n" );
return false;
}
} while ( c != '\n' );
goto line_too_long;
}
}
else
{
//remove newline character by overwriting it with null character
*p = '\0';
}
//find length of string
user_len = strlen( line );
//check if length is acceptable
if ( user_len >= 20 )
{
goto line_too_long;
}
//username is ok, so copy it
strcpy( ud.username, line );
//break out of infinite loop
break;
line_too_long:
printf( "Line was too long, try again!\n" );
}
//print username
printf( "Username is: %s\n", ud.username );
return user_len;
}