It's a radical rewrite, but it is also effective. This is more along the lines of what I'd use:
#include <assert.h>
#include <stdio.h>
#include <string.h>
static int prompt_for_data(const char *prompt, const char *what, char *buffer, size_t buflen)
{
char line[256];
assert(prompt != 0 && *prompt != '\0');
assert(what != 0 && *what != '\0');
assert(buffer != 0);
assert(buflen > 1 && buflen < sizeof(line));
printf("%s", prompt);
if (fgets(line, sizeof(line), stdin) == 0)
{
fprintf(stderr, "EOF\n");
return EOF;
}
size_t len = strlen(line);
if (len == 0)
{
fprintf(stderr, "Zero length string? You can't use Control-@ in a %s\n",
what);
return EOF;
}
if (line[len - 1] != '\n')
{
fprintf(stderr, "Line far too long (%zu bytes)\n", len);
return EOF;
}
line[--len] = '\0';
/* Optionally chop leading and trailing white space - omitted */
if (len > buflen - 1)
{
fprintf(stderr, "Maximum length of %s is %zu (you entered %zu characters)\n",
what, buflen - 1, len);
return EOF;
}
if (len == 0)
{
fprintf(stderr, "You're supposed to enter a value for the %s\n", what);
return EOF;
}
strcpy(buffer, line);
return 0;
}
int main(void)
{
char userName[9];
char password[9];
if (prompt_for_data("Enter username: ", "username", userName, sizeof(userName)) == 0 &&
prompt_for_data("Enter password: ", "password", password, sizeof(password)) == 0)
{
char str[150];
snprintf(str, sizeof(str),
"INSERT INTO USERS (ID, USERNAME, PASSWORD, IP) VALUES"
"(%d, '%s', '%s', '%s');",
1, userName, password, "120.132.12.1");
puts(str);
}
return 0;
}
The sequence of 'prompt for value, read a line, check its length, copy it to the desired variable' is encapsulated in the prompt_for_data()
function. It is then used twice (but I'd probably write it as a function even if it was called just once) to get the username and password. Then, rather than using strcat()
, I use snprintf()
to create the output string.
You should also note that if you want to change the size of your user name and password from the quaintly archaic 8 characters plus terminating null to 32, the only lines of code that change are the declarations of userName
and password
. This is because I've carefully used sizeof(userName)
or sizeof(password)
when I needed to describe the size. Similarly with the other variables; for example, the fgets()
line doesn't change if I change the size of the line
variable. It is usually good practice to minimize the number of necessary changes when you change the size of a variable.
Note that you still have SQL injection issues. If I type "O'Reilly
" as the user name, the SQL is invalid. Remember Little Bobby Tables! One good thing about short user names; it makes SQL injection harder.
Sample runs
$ ./up
Enter username: beckah12
Enter password: beckah12
INSERT INTO USERS (ID, USERNAME, PASSWORD, IP) VALUES(1, 'beckah12', 'beckah12', '120.132.12.1');
$ ./up
Enter username: absolom12
Maximum length of username is 8 (you entered 9 characters)
$ ./up
Enter username: beckah12
Enter password: absolom12
Maximum length of password is 8 (you entered 9 characters)
$ ./up
Enter username:
You're supposed to enter a value for the username
$ ./up
Enter username: beckah12
Enter password:
You're supposed to enter a value for the password
$ ./up
Enter username: EOF
$ ./up
Enter username: beckah12
Enter password: EOF
$ ./up
Enter username: nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
Line far too long (255 bytes)
$