#define _POSIX_C_SOURCE 200809L #include "cftw.h" #include #include #include #include #include #include #include static int do_cftw(const char *const dirpath, int (*const fn)(const char *, const struct stat *, bool *, void *), bool *const done, void *const user) { int ret = -1; DIR *const d = opendir(dirpath); if (!d) { fprintf(stderr, "%s: opendir(2): %s\n", __func__, strerror(errno)); goto end; } for (;;) { errno = 0; struct dirent *const de = readdir(d); if (errno) { fprintf(stderr, "%s: readdir(3): %s\n", __func__, strerror(errno)); goto end; } else if (!de) break; const char *const path = de->d_name; if (!strcmp(path, ".") || !strcmp(path, "..")) continue; const char *const sep = dirpath[strlen(dirpath) - 1] == '/' ? "" : "/"; struct stat sb; struct dynstr d; dynstr_init(&d); if (dynstr_append(&d, "%s%s%s", dirpath, sep, path)) { fprintf(stderr, "%s: dynstr_append failed\n", __func__); return -1; } const int r = stat(d.str, &sb); if (r) fprintf(stderr, "%s: stat(2) %s: %s\n", __func__, path, strerror(errno)); else if (S_ISDIR(sb.st_mode)) { if ((ret = do_cftw(d.str, fn, done, user))) ; else if ((ret = fn(d.str, &sb, done, user))) ; } else if (S_ISREG(sb.st_mode)) ret = fn(d.str, &sb, done, user); else fprintf(stderr, "%s: unexpected st_mode %ju\n", __func__, (uintmax_t)sb.st_mode); dynstr_free(&d); if (ret || *done) goto end; } ret = 0; end: if (d && closedir(d)) { fprintf(stderr, "%s: closedir(2): %s\n", __func__, strerror(errno)); ret = -1; } return ret; } int cftw(const char *const dirpath, int (*const fn)(const char *, const struct stat *, bool *, void *), void *const user) { bool done = false; return do_cftw(dirpath, fn, &done, user); }