Code:
#ifdef _WIN32
#include <direct.h>
#define getcwd _getcwd
#define chdir _chdir
#define lstat _stat
#else
#include <unistd.h>
#endif
#include <dirent.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
char* filesystem_current(void) {
size_t size = 8;
char* buffer = malloc(size);
while (getcwd(buffer, size) == NULL) {
size <<= 1;
buffer = realloc(buffer, size);
}
return buffer;
}
typedef bool (*filesystem_process_path)(const char* pathName, void* userData);
bool filesystem_process_path_NOP_(const char* ignored, void* unused) {
return true;
}
bool filesystem_process_recurse_(const char* pathName,
void* userData,
filesystem_process_path userEnterDirectory,
filesystem_process_path userProcessFile,
filesystem_process_path userLeaveDirectory) {
struct stat info = {0};
if (lstat(pathName, &info) != 0)
return false;
if (S_ISREG(info.st_mode))
return userProcessFile(pathName, userData);
else if (!S_ISDIR(info.st_mode))
return true; /* Ignore */
DIR* directory = opendir(pathName);
if (!directory)
return false;
chdir(pathName);
bool success = userEnterDirectory(pathName, userData);
while (success) {
struct dirent* next = readdir(directory);
if (next == NULL)
break;
char* path = next->d_name;
if (!strcmp(path, ".") || !strcmp(path, ".."))
continue;
success = filesystem_process_recurse_(path, userData, userEnterDirectory,
userProcessFile, userLeaveDirectory);
}
userLeaveDirectory(pathName, userData);
closedir(directory);
chdir("..");
return success;
}
bool filesystem_process_(const char* pathName,
void* userData,
filesystem_process_path userEnterDirectory,
filesystem_process_path userProcessFile,
filesystem_process_path userLeaveDirectory) {
if (!userEnterDirectory)
userEnterDirectory = filesystem_process_path_NOP_;
if (!userProcessFile)
userProcessFile = filesystem_process_path_NOP_;
if (!userLeaveDirectory)
userLeaveDirectory = filesystem_process_path_NOP_;
char* saved = filesystem_current();
bool success =
filesystem_process_recurse_(pathName, userData, userEnterDirectory,
userProcessFile, userLeaveDirectory);
chdir(saved);
free(saved);
return success;
}
#define filesystem_process(pathName, userData, userEnterDirectory, \
userProcessFile, userLeaveDirectory) \
filesystem_process_(pathName, (void*)userData, \
(filesystem_process_path)userEnterDirectory, \
(filesystem_process_path)userProcessFile, \
(filesystem_process_path)userLeaveDirectory)
#define filesystem_process_files(pathName, userData, userProcessFile) \
filesystem_process(pathName, userData, NULL, userProcessFile, NULL)
#define filesystem_process_files_before(pathName, userData, userProcessFile, \
userLeaveDirectory) \
filesystem_process(pathName, userData, NULL, userProcessFile, \
userLeaveDirectory)
#define filesystem_process_files_after(pathName, userData, userProcessFile, \
userEnterDirectory) \
filesystem_process(pathName, userData, userEnterDirectory, userProcessFile, \
NULL)
#define filesystem_process_directories_then( \
pathName, userData, userEnterDirectory, userLeaveDirectory) \
filesystem_process(pathName, userData, userEnterDirectory, NULL, \
userLeaveDirectory)
#define filesystem_process_directories(pathName, userData, userEnterDirectory) \
filesystem_process(pathName, userData, userEnterDirectory, NULL, NULL)
/*
Sample usage
*/
#include <stdio.h>
#include <stdlib.h>
bool print_file(char* path, size_t* total) {
char* current = filesystem_current();
printf("%s [%s]\n", path, current);
free(current);
++(*total);
return true;
}
void process(const char* path) {
printf("Processing `%s`...\n", path);
size_t count = 0;
bool ok = filesystem_process_files(path, &count, print_file);
printf("filesystem_process_files: ");
if (!ok)
puts("FAILURE");
else
printf("success (%zu files encountered)\n", count);
}
int main(int argc, char** argv) {
if (argc < 2)
process(".");
for (;;) {
char* path = *(++argv);
if (!path)
break;
process(path);
}
}