Search code examples
csplitc-stringsstrtokfunction-definition

Strtok isn't returning as expected, am I using it wrong?


#include <stdlib.h>
#include <stdio.h>
#include <string.h>

// fiter string to the first |
char* filterstringfirst(char* command, int i){
    char *tok = command;
    int x = 0;
    while ((tok = strtok(tok, "|")) != NULL && x <= i)
    {
        if( x == i){
            return tok;
        }
        x++;
        printf(" ===  Parsed: --%s-- ===\n", tok);
        tok = NULL;
    }
    return tok;
        
}


int main () {
    char command[]  = "ls -a | sort -h | grep h | wc -l";
    
    char command2[]  = "ls -a | sort -h | grep h | wc -l";
    char* temp = command;

    char* x =  filterstringfirst(temp, 0);
    printf("%s\n",x);
    char* temp2 = command;

    char* x2 =  filterstringfirst(temp2, 1);
    printf("%s\n",x2);
    
    
   
    temp = command;
    

   return 0;
}

I have this function I made which is supposed to just return part of a string. The original string should be similar to "ls -l | grep temp | sort".

The idea was that it would be called with the string and a number, and return that segment. Eg. 0 -> "ls -l"

Now this works the first time I call it, but calling it again seems to break and end in a segfault.

char command[]  = "ls -a | sort -h | grep h | wc -l";
char* temp = command;

char* x =  filterstringfirst(temp, 0);
printf("%s\n",x);
char* temp2 = command;

char* x2 =  filterstringfirst(temp2, 1);
printf("%s\n",x2);`

This was my testing code

And the output:

ls -a  
===  Parsed: --ls -a -- === 
[1]    1126 segmentation fault  ./templ
 ➜  Current gcc -o templ templ.c
 ➜  Current ./templ ls -a  
===  Parsed: --ls -a -- === [1]   
 1136 segmentation fault  ./templ

Edit: Updated to have main too (based on comments)


Solution

  • strtok is destructive - it modifies the buffer passed in by replacing delimiters will null bytes.

    After

    char* x =  filterstringfirst(temp, 0);
    

    command will effectively be "ls -a ".

    If you want to use strtok here, you will either need to:

    • mimic strtok in your wrapping function, by passing NULL and the position to start from in subsequent calls, or

    • duplicate the string before using it, and return a copy of the token.

    An example of the second, with no error handling:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    char *get_token_at(char *command, size_t n) {
        size_t position = 0;
        char *copy = strdup(command);
        char *token = strtok(copy, "|");
        char *output = NULL;
    
        while (token && position < n) {
            token = strtok(NULL, "|");
            position++;
        }
    
        if (token && position == n)
            output = strdup(token);
    
        free(copy);
    
        return output;
    }
    
    int main(void) {
        char command[]  = "ls -a | sort -h | grep h | wc -l";
    
        char *x = get_token_at(command, 0);
        puts(x);
        free(x);
    
        x = get_token_at(command, 1);
        puts(x);
        free(x);
    }
    

    stdout:

    ls -a 
     sort -h
    

    (Note the whitespace in these tokens.)