From 3e939dde0ad07d1ecbb9bed2123fff6d2d92dcab Mon Sep 17 00:00:00 2001 From: Xavier Del Campo Romero Date: Mon, 6 Oct 2025 16:19:16 +0200 Subject: cftw.c: Call user function at the end for directories The older, synchronous implementation for cftw recursed as long as there were children directories and, finally, it called the user function. This commit therefore fixes the newer, asynchronous implementation to honor that behaviour. Otherwise, rmdir_r would fail because rmdir(2) would be called for non-empty directories. --- cftw.c | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/cftw.c b/cftw.c index f512232..5083cc4 100644 --- a/cftw.c +++ b/cftw.c @@ -101,11 +101,14 @@ failure: return NULL; } -static enum cftw_state run(struct cftw *const c, struct cftw_entry *const e) +static enum cftw_state run(struct cftw *const c, struct cftw_entry *const e, + struct cftw_entry *const root) { - int error; + int error = 0; struct dirent *de; struct dynstr d; + const char *const dirpath = e->dirpath; + struct stat sb; dynstr_init(&d); errno = 0; @@ -117,15 +120,25 @@ static enum cftw_state run(struct cftw *const c, struct cftw_entry *const e) goto failure; } else if (!de) - return CFTW_OK; + { + if (e == root) + return CFTW_OK; + else if (stat(dirpath, &sb)) + { + fprintf(stderr, "%s: stat(2) %s: %s\n", __func__, dirpath, + strerror(errno)); + goto failure; + } - const char *const path = de->d_name, *const dirpath = e->dirpath; + return c->fn(dirpath, &sb, &c->done, c->user) ? CFTW_FATAL : CFTW_OK; + } + + const char *const path = de->d_name; if (!strcmp(path, ".") || !strcmp(path, "..")) return CFTW_AGAIN; const char *const sep = dirpath[strlen(dirpath) - 1] == '/' ? "" : "/"; - struct stat sb; if (dynstr_append(&d, "%s%s%s", dirpath, sep, path)) { @@ -145,8 +158,6 @@ static enum cftw_state run(struct cftw *const c, struct cftw_entry *const e) { if (!(e->child = entry(d.str))) goto failure; - - error = c->fn(d.str, &sb, &c->done, c->user); } else if (S_ISREG(sb.st_mode)) error = c->fn(d.str, &sb, &c->done, c->user); @@ -171,11 +182,12 @@ failure: return CFTW_FATAL; } -static enum cftw_state step(struct cftw *const c, struct cftw_entry *const e) +static enum cftw_state step(struct cftw *const c, struct cftw_entry *const e, + struct cftw_entry *const root) { if (e->child) { - switch (step(c, e->child)) + switch (step(c, e->child, root)) { case CFTW_AGAIN: return CFTW_AGAIN; @@ -195,12 +207,12 @@ static enum cftw_state step(struct cftw *const c, struct cftw_entry *const e) return CFTW_AGAIN; } - return run(c, e); + return run(c, e, root); } -enum cftw_state cftw_step(struct cftw *c) +enum cftw_state cftw_step(struct cftw *const c) { - return step(c, c->root); + return step(c, c->root, c->root); } struct cftw *cftw(const char *const dirpath, int (*const fn)(const char *, -- cgit v1.2.3