Reputation: 3751
I need help with printing out files and directories (plus subdirectories and files) recursively using C.
This is the code I have:
char filepath[250], filename[250];
void listdir(char *dir)
{
DIR *dp;
struct dirent *entry;
struct stat statbuf;
if((dp = opendir(dir)) == NULL)
{
fprintf(stderr,"cannot open directory: %s\n", dir);
return;
}
chdir(dir);
while((entry = readdir(dp)) != NULL)
{
if(lstat(entry->d_name, &statbuf) == 0)
{
if(statbuf.st_mode & S_IFDIR)
{
/* Found a directory, but ignore . and .. */
if(strcmp(".", entry->d_name) == 0 || strcmp("..", entry->d_name) == 0)
continue;
// Concatenate directory name
strcat(filepath, entry->d_name);
strcat(filepath, "/");
/* Recurse at a new indent level */
listdir(entry->d_name);
}
else
{
// Concatenate file name
strcpy(filename, filepath);
strcat(filename, entry->d_name);
puts(filename);
}
}
}
chdir("..");
closedir(dp);
}
There are two problems I have noticed with this code. Lets say this is my file structure:
index.c
main.c
test.o
test/hello.txt
test/Untitled.txt
test/random/
When I run the above program, there are two possible outputs I can get depending on the file structure.
One possible case (This one says Untitled.txt is inside the folder random, when it is not):
index.c
main.c
test.o
test/hello.txt
test/random/Untitled.txt
If I rename Untitled.txt to something like apple.txt, then it will print out fine. Which leads me to believe, it goes alphabetically.
How can I make it print out all the files first then go into a folder and print out all the files in there then repeat? So print files > go in folder > repeat.
Upvotes: 3
Views: 4458
Reputation: 223872
The problem is here:
strcat(filepath, entry->d_name);
strcat(filepath, "/");
/* Recurse at a new indent level */
listdir(entry->d_name);
You're appending to whatever value was in filepath
previously. So whenever you go come back out from one directory, you're appending to the value that filepath
was set to in the lower level directory.
After you come back from the recursive call, you need to remove the entry you added and any others that may have been added at lower levels:
int len = strlen(filepath);
strcat(filepath, entry->d_name);
strcat(filepath, "/");
/* Recurse at a new indent level */
listdir(entry->d_name);
/* cleanup filepath */
filepath[len] = '\0';
EDIT:
You could also do this without the globals and without changing directories:
void listdir(char *dir)
{
DIR *dp;
struct dirent *entry;
struct stat statbuf;
char *subdir;
if((dp = opendir(dir)) == NULL)
{
fprintf(stderr,"cannot open directory: %s\n", dir);
return;
}
while((entry = readdir(dp)) != NULL)
{
if(lstat(entry->d_name, &statbuf) == 0)
{
if(statbuf.st_mode & S_IFDIR)
{
/* Found a directory, but ignore . and .. */
if(strcmp(".", entry->d_name) == 0 || strcmp("..", entry->d_name) == 0)
continue;
// allocate space for parent directory, "/", subdir, plus NULL terminator
subdir = malloc(strlen(dir) + strlen(entry->d_name) + 2);
// Concatenate directory name
strcpy(subdir, dir);
strcat(subdir, "/");
strcat(subdir, entry->d_name);
/* Recurse at a new indent level */
listdir(subdir);
free(subdir);
}
else
{
// Print file name
printf("%s/%s\n", dir, entry->d_name);
}
}
}
closedir(dp);
}
Upvotes: 2