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 @TedLyngmo and @robertklep pointed in their comments, the string returned by getenv()
function shouldn't be changed by the application. So, the problem was solved by just copying the returned string into a new string and tokenizing the new one, leaving the original string untouched.
Here is the new working code refactored as @xing suggested in his answer to keep parsing and searching logic separated.
bool tokenize(char *str, char tokens[][100], int max_tokens, int *count, const char *delimiter) {
char *token;
int num_tokens = 0;
token = strtok(str, delimiter);
if (!token)
return false;
strcpy(tokens[0], token);
num_tokens++;
for (int i = 1; token && i < max_tokens; i++) {
token = strtok(NULL, delimiter);
if(token) {
strcpy(tokens[i], token);
num_tokens++;
}
}
*count = num_tokens;
return true;
}
bool str_in_path(char *target, char *result) {
const char *path = getenv("PATH");
char path_copy[strlen(path) + 1];
strcpy(path_copy, path); // copying the path to avoid manipulating the original string returned by getenv()
char dirs[20][100] = {};
int num_dirs;
tokenize(path_copy, dirs, 20, &num_dirs, ":"); // tokenizing the copied path instead of the original one.
for (int i = 0; i < num_dirs; i++) {
struct dirent *entry = NULL;
DIR *dir = opendir(dirs[i]);
if (dir == NULL) {
fprintf(stderr, "can't open directory %s\n", dirs[i]);
continue;
}
while ((entry = readdir(dir)) != NULL) {
if (!strcmp(target, entry->d_name)) {
strcpy(result, dirs[i]);
strcat(result, "/");
strcat(result, entry->d_name);
closedir(dir);
return true;
}
}
closedir(dir);
}
return false;
}