cstrtoketcpasswd

Separate first name with middle name and last name with strtok_r


So im having some issues related to my previous quest in C programming. This time on how to separate names from /etc/passwd file using strtok_r or strtok. The deal is that I want to separate the users names in two parts, first name and middle name (assuming they got one) in the first, and the last name in the second. But I can't find a way to separate the names correctly. I got it to work with their first name in the first part, but then the middle name is attached to the last name.

The /etc/passwd lines looks like this:

s214907:x:1240:1251:Jonas Villa,,,:/home/s214907:/bin/bash
s212167:x:1297:1306:Konstantina Pavlova Rusenkova,,,:/home/s212167:/bin/bash

Example of what I get:

[Konstantina] [Pavlova Ruskenova] 

Example of how I want it to print:

[Konstantina Pavlova] [Ruskenova]

This is my code so far:

          char line[256], words[20];
          char *mname, *tmp, *uid, *sp, *lname, *uname, *name, *fname, *tmps;
          int bindex=0, index, cnt =0, count;
          char *rest = NULL;
          FILE*fp = fopen("/etc/passwd","r");
          size_t len = 0;
          char *next = NULL;
          while(fgets(line,256,fp)!=NULL)
          {
                  sp       = strtok(line, ":");
                  tmp      = strtok(NULL, ":");
                  uid      = strtok(NULL, ":");
                  tmp      = strtok(NULL, ":");
                  name     = strtok(NULL, ":,");




                  if(atoi(uid) > 999)
                  {
                          for(tmps = strtok_r(name, " ", &rest);
                                  tmps != NULL;
                                  tmps = strtok_r(NULL, ",", &rest)) {
                                  tmps = strtok(tmps, ",");
                                  printf("[%s]\n", tmps);
                          }
                  }
                  cnt++;
          }
          fclose(fp);

Solution

  • Your approach to use strtok_r or strtok to split the names cannot work.

    After you tokenize the (complete) name, you get this:

    name="Konstantina Pavlova Rusenkova"
    

    You get this name by name = strtok(NULL, ":,");. Therefore it cannot contain any ','. If there was a ',' the string would have been chopped there.

    Any further tokenization using delimiter ',' cannot get any useful result except to return the complete string itself.

    This means in your inner loop tmps = strtok(tmps, ","); will not do anything.

    I added a few prints:

    printf("name=%s\n",name);
    
        if(atoi(uid) > 999)
        {
            for(tmps = strtok_r(name, " ", &rest);
                tmps != NULL;
                tmps = strtok_r(NULL, ",", &rest))
            {
                printf("tmps:%s\n", tmps); 
                tmps = strtok(tmps, ",");
                printf("  [%s]\n", tmps);
            }
        }
    

    The result is:

    name=Konstantina Pavlova Rusenkova
    tmps:Konstantina
      [Konstantina]
    tmps:Pavlova Rusenkova
      [Pavlova Rusenkova]
    

    The second print shows what you feed into your tmps=strtok(tmps,",");. As mentioned above the delimiter is never present leaving everything in the second brackets.

    In general, strtok and friends is not useful for splitting the name. If cuts the strings apart leaving no two names together. The whitespace is replaced by \0.

    Instead you could simply search for the last whitespace and split there:

        if(atoi(uid) > 999)
        {
            char *first = name;
            char *last = strrchr(name,' ');
    
            if (last != NULL)
            {
                *last = 0;
                last++;
    
                printf("[%s] [%s]\n", first, last);
            }
        }
    

    This will get

    name=Konstantina Pavlova Rusenkova
    [Konstantina Pavlova] [Rusenkova]
    

    It also still works for names with only 1 first name:

    name=James Kirk
    [James] [Kirk]
    

    If there is only one name in total you need to adapt a bit.

    Note:

    If for some reason you insist to use strtok also for splitting the name, you need to use delimiter ' ' and then glue together the first and second substring.

    This would look like this:

        if(atoi(uid) > 999)
        {
            for(tmps = strtok_r(name, " ", &rest);
                tmps != NULL;
                tmps = strtok_r(NULL, " ", &rest))
                {
                    printf("tmps:%s\n", tmps);
                    printf("  [%s]\n", tmps);
            }
        }
    

    with output

    name=Konstantina Pavlova Rusenkova
    tmps:Konstantina
      [Konstantina]
    tmps:Pavlova
      [Pavlova]
    tmps:Rusenkova
      [Rusenkova]