I'm really new to C, but I've been writing C++ for a while. I'm writing a client sever chat program. I need to prompt the user with a couple of different options at the beginning of the session, after they've entered a username. At first I was attempting to use a getchar() function, but for one reason or another, any statements of the following pattern would not yield expected results:
int x = getchar();
if (x == '2') doSomething();
If the user entered 2, it would never go to the "doSomething" area. So I tried to use fgets and strncmp instead. But now, I keep getting segmentation faults on strncmp. Here is the most relevant part of the code, with some commented out sections from my attempts to use getchar. Admittedly this is kind of messy, because I was just throwing it together as a test. I thought maybe allocating extra space to the string would help prevent seg faults but of course it didn't.
for( ; ; )
{
printf("\r\n1.List Users \r\n2.Chat \r\n3.Exit \r\n \r\n \r\n");
char *x = malloc(5);
fgets(x, 2, stdin);
if (x[0] != NULL)
{
if (strncmp (x[0],"a",1) == 0)
{
printf("yay");
}
}
/* int x = getchar();
if(x == 'a') // Compare input to 'q' character
break;
fprintf(stdout, "%d\n", x);*/
/*x = c - '0';
if (x == 1)
getUsers(sockfd);
if ( x == 2 )
{
pthread_create(&sndThread, NULL, do_send, (void *) sockfd);
pthread_create(&rcvThread, NULL, do_recv, (void *) sockfd);
pthread_join(sndThread, NULL);
pthread_join(rcvThread, NULL);
}
if ( x == 3 )
{
close(sockfd);
exit(0);
}*/
}
You can see in the leftover comments the remains of attempts to do things such as casting a char to int with a subtract. This comes from stuff I've found on the internet. I also heard on the internet that getchar leaves \n's in the input buffer.
So here's my entire code for the client so you can put that in context:
int main(int argc, char **argv)
{
int sockfd, i;
char *myName = malloc(MSGSIZE);
char c;
struct sockaddr_in servaddr;
int status;
pthread_t sndThread;
pthread_t rcvThread;
if(argc != 2)
{
printf("Error: expected IP address argument");
exit(1);
}
if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
error("Socket error");
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORTNUM);
if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <=0)
{
printf("inet_pton error for %s \n", argv[1]);
exit(3);
}
if(connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)
{
error("Connect error");
}
printf("Type in a username: \r\n");
while ( fgets(myName[i],MSGSIZE,stdin ) == NULL){}
printf(">%s<\n",myName);
send_userName(myName,sockfd);
for( ; ; )
{
printf("\r\n1.List Users \r\n2.Chat \r\n3.Exit \r\n \r\n \r\n");
char *x = malloc(5);
fgets(x, 2, stdin);
if (x[0] != NULL)
{
if (strncmp (x[0],"a",1) == 0)
{
printf("yay");
}
}
/* int x = getchar();
if(x == 'a') // Compare input to 'q' character
break;
fprintf(stdout, "%d\n", x);*/
/*x = c - '0';
if (x == 1)
getUsers(sockfd);
if ( x == 2 )
{
pthread_create(&sndThread, NULL, do_send, (void *) sockfd);
pthread_create(&rcvThread, NULL, do_recv, (void *) sockfd);
pthread_join(sndThread, NULL);
pthread_join(rcvThread, NULL);
}
if ( x == 3 )
{
close(sockfd);
exit(0);
}*/
}
}
The behavior of getchar()
depends on the mode of your terminal. Most operate in "cooked" mode which means getchar()
returns after you've entered a whole line (and pressed enter). Terminals do this in order to allow line editing. To make getchar()
return immediately, you need to switch it into "raw" mode.
Next, you should enable all compiler warnings because it would have told you what is wrong with the code above:
strncmp()
expects char*
as first parameter but you passed char
. That means the code will read from arbitrary memory.
x[0] != NULL
doesn't make sense either (compare character against null pointer). To know whether fgets()
didn't return anything, look at its return code.
char * success = fgets(x, 2, stdin);
if(success == null) { ... error handling... }
if (strncmp (x,"a",1) == 0) {
printf("yay");
}