0
votes

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);
    }*/
   }

}

3
Does the compiler yell out warnings being fed this code? - alk

3 Answers

2
votes

x[0] is a character, but x is a char*. strncmp should just take x as an argument, not x[0]. That is, you do not want

strncmp(x[0],"a",1)

but rather

strncmp(x,"a",1)

Alternatively, if you really want to emphasize that you're starting at the first character of x, you could do either of:

strncmp(x+0,"a",1)

strncmp(&x[0],"a",1)
0
votes

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");
}
0
votes

Comments for the future

It is generally not helpful to remove the #include lines from the full sourcecode. If I want to go and compile your code, I now have to go burn a few minutes pulling them together. It's a waste of time.

Here's the extra headers that I needed to add:

#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define MSGSIZE 100
#define PORTNUM 12

#define SA struct sockaddr

void error(const char *);
void send_userName(const char *, int);

Now then...

So if I add those headers, and I try to compile your code, I get some seriously scary warnings. Let's look at the first class of warnings.

This class is where you are passing variables to functions that are looking for a different type of variable.

foo.c:59:19: warning: incompatible integer to pointer conversion passing 'char' to parameter of type 'char *'; take the address with & [-Wint-conversion]
    while ( fgets(myName[i],MSGSIZE,stdin ) == NULL){}
                  ^~~~~~~~~
                  &
/usr/include/stdio.h:236:30: note: passing argument to parameter here
char    *fgets(char * __restrict, int, FILE *);
                                ^
foo.c:74:18: warning: comparison between pointer and integer ('int' and 'void *')
        if (x[0] != NULL)
            ~~~~ ^  ~~~~
foo.c:77:26: warning: incompatible integer to pointer conversion passing 'char' to parameter of type 'const char *'; take the address with & [-Wint-conversion]
            if (strncmp (x[0],"a",1) == 0)
                         ^~~~
                         &
/usr/include/string.h:84:26: note: passing argument to parameter here
int      strncmp(const char *, const char *, size_t);
                             ^

The second class is where you use uninitialized variables:

foo.c:59:26: warning: variable 'i' is uninitialized when used here [-Wuninitialized]
    while ( fgets(myName[i],MSGSIZE,stdin ) == NULL){}
                         ^
foo.c:18:18: note: initialize the variable 'i' to silence this warning
    int sockfd, i;
                 ^
                  = 0

You should really try to fix these, because your code is broken otherwise. If you don't understand why a particular warning is firing, then you should ask about that.