I'm trying to write a function to search in the PATH directories. I used getenv("PATH")
to get the path string, then used strtok()
to split it.
The second time I call my function it does not reinitialize the path string. it searches only in the first part of the path.
I know that strtok()
modifies the original string terminating each token with a \0
. but in my case I don't understand why calling the function again doesn't create a new string with the normal delimiters ":".
Here is my code:
bool str_in_path(char *target, char *result) {
char *path = getenv("PATH");
char *token;
char tokens[10][100];
int count = 0;
token = strtok(path, ":");
strcpy(tokens[0], token);
count++;
for (int i = 1; token && i < 10; i++) {
token = strtok(NULL, delimiter);
if (token) {
strcpy(tokens[i], token);
count++;
}
}
for (int i = 0; i < count; i++) {
struct dirent *entry = NULL
DIR *dir = opendir(tokens[i]);
if (dir == NULL) {
fprintf(stderr, "can't open directory %s\n", tokens[i]);
continue;
}
while ((entry = readdir(dir)) != NULL) {
if (!strcmp(target, entry->d_name)) {
strcpy(result, tokens[i]);
strcat(result, "/");
strcat(result, entry->d_name);
closedir(dir);
return true;
}
}
closedir(dir);
}
return false;
}
The first time I call the function it behaves as I expect. the second time it searches only the first part of the path. although it calls char *path = getenv("PATH");
to initialize the path string. why is that and how can I solve it.
As mentioned in @chux's comment, strcspn()
can be used to parse without modifying the source string.
Also consider breaking the function into two functions. One to parse the path and the other to open the directories.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <dirent.h>
char **parse_path ( char *path, char *delimiter) {
char **tokens = NULL;
char **tmp = NULL;
char *token = path;
char *end = path;
size_t items = 0;
size_t span = 0;
while ( *token) { // not terminating zero
end += strcspn ( token, delimiter);
if ( end == token) {
break;
}
// allocate pointers
if ( NULL == ( tmp = realloc ( tokens, sizeof *tokens * (items + 2)))) {
fprintf ( stderr, "problem realloc tokens\n");
break;
}
tokens = tmp;
span = end - token;
// allocate memory to a pointer
if ( NULL == ( tokens[items] = malloc ( span + 1))) {
fprintf ( stderr, "problem malloc token[items]\n");
break;
}
memmove ( tokens[items], token, span);
tokens[items][span] = 0; // zero terminate
++items;
tokens[items] = NULL; // sentenal NULL
if ( *end) { // not terminating zero
++end; // advance past one delimiter
}
token = end;
}
return tokens;
}
bool str_in_path_tokens ( char **tokens, char *target, char *result) {
while ( tokens && *tokens) {
struct dirent *entry = NULL;
DIR *dir = opendir ( *tokens);
if ( dir == NULL) {
fprintf(stderr, "can't open directory %s\n", *tokens);
continue;
}
while ( ( entry = readdir ( dir)) != NULL) {
if ( ! strcmp ( target, entry->d_name)) {
strcpy(result, *tokens);
strcat(result, "/");
strcat(result, entry->d_name);
closedir(dir);
return true;
}
}
closedir(dir);
++tokens;
}
return false;
}
void show_path_tokens ( char **tokens) {
while ( tokens && *tokens) {
printf ( "%s\n", *tokens);
++tokens;
}
}
void free_path_tokens ( char **tokens) {
while ( tokens && *tokens) {
free ( *tokens);
++tokens;
}
}
int main ( void) {
char *path = getenv ( "PATH");
char **pathtokens = NULL;
printf ( "%s\n", path);
pathtokens = parse_path ( path, ":");
show_path_tokens ( pathtokens);
// call
// str_in_path_tokens ( pathtokens, target, result);
// as needed with various targets and results
// no need to reparse the path
free_path_tokens ( pathtokens);
free ( pathtokens);
}