diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2019-05-04 11:51:56 -0400 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2019-05-04 11:55:07 -0400 |
commit | 09119a3e8fa5f206eb4254144a1c8e77bc6e587e (patch) | |
tree | 924146ec43e912f2271ab90a470cd09492919db7 | |
parent | d8e10d648b12b8595e9f177ec8f1a71d24aecea5 (diff) |
bftw: Add a caching stat() API to struct BFTW
-rw-r--r-- | bftw.c | 95 | ||||
-rw-r--r-- | bftw.h | 47 | ||||
-rw-r--r-- | color.c | 69 | ||||
-rw-r--r-- | color.h | 4 | ||||
-rw-r--r-- | eval.c | 209 | ||||
-rw-r--r-- | printf.c | 305 | ||||
-rw-r--r-- | printf.h | 16 | ||||
-rwxr-xr-x | tests.sh | 10 | ||||
-rw-r--r-- | tests/test_color_L_ln_target.out | 20 |
9 files changed, 432 insertions, 343 deletions
@@ -749,16 +749,57 @@ static enum bftw_typeflag bftw_dirent_typeflag(const struct dirent *de) { return BFTW_UNKNOWN; } -/** Call stat() and use the results. */ -static int bftw_stat(struct BFTW *ftwbuf, struct bfs_stat *sb) { - int ret = bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, ftwbuf->stat_flags, sb); - if (ret == 0) { - ftwbuf->statbuf = sb; - ftwbuf->typeflag = bftw_mode_typeflag(sb->mode); +/** Cached bfs_stat(). */ +static const struct bfs_stat *bftw_stat_impl(struct BFTW *ftwbuf, struct bftw_stat *cache, enum bfs_stat_flag flags) { + if (!cache->buf) { + if (cache->error) { + errno = cache->error; + } else if (bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, flags, &cache->storage) == 0) { + cache->buf = &cache->storage; + } else { + cache->error = errno; + } } + + return cache->buf; +} + +const struct bfs_stat *bftw_stat(struct BFTW *ftwbuf, enum bfs_stat_flag flags) { + const struct bfs_stat *ret; + + if (flags & BFS_STAT_NOFOLLOW) { + ret = bftw_stat_impl(ftwbuf, &ftwbuf->lstat_cache, BFS_STAT_NOFOLLOW); + if (ret && !S_ISLNK(ret->mode) && !ftwbuf->stat_cache.buf) { + // Non-link, so share stat info + ftwbuf->stat_cache.buf = ret; + } + } else { + ret = bftw_stat_impl(ftwbuf, &ftwbuf->stat_cache, BFS_STAT_FOLLOW); + if (!ret && (flags & BFS_STAT_TRYFOLLOW) && is_nonexistence_error(errno)) { + ret = bftw_stat_impl(ftwbuf, &ftwbuf->lstat_cache, BFS_STAT_NOFOLLOW); + } + } + return ret; } +enum bftw_typeflag bftw_typeflag(struct BFTW *ftwbuf, enum bfs_stat_flag flags) { + if (ftwbuf->stat_flags & BFS_STAT_NOFOLLOW) { + if ((flags & BFS_STAT_NOFOLLOW) || ftwbuf->typeflag != BFTW_LNK) { + return ftwbuf->typeflag; + } + } else if ((flags & (BFS_STAT_NOFOLLOW | BFS_STAT_TRYFOLLOW)) == BFS_STAT_TRYFOLLOW || ftwbuf->typeflag == BFTW_LNK) { + return ftwbuf->typeflag; + } + + const struct bfs_stat *statbuf = bftw_stat(ftwbuf, flags); + if (statbuf) { + return bftw_mode_typeflag(statbuf->mode); + } else { + return BFTW_ERROR; + } +} + /** * Holds the current state of the bftw() traversal. */ @@ -790,8 +831,6 @@ struct bftw_state { /** Extra data about the current file. */ struct BFTW ftwbuf; - /** bfs_stat() buffer for the current file. */ - struct bfs_stat statbuf; }; /** @@ -901,6 +940,12 @@ static bool bftw_need_stat(struct bftw_state *state) { return false; } +/** Initialize bftw_stat cache. */ +static void bftw_stat_init(struct bftw_stat *cache) { + cache->buf = NULL; + cache->error = 0; +} + /** * Initialize the buffers with data about the current path. */ @@ -915,10 +960,11 @@ static void bftw_prepare_visit(struct bftw_state *state) { ftwbuf->depth = 0; ftwbuf->visit = state->visit; ftwbuf->error = reader->error; - ftwbuf->statbuf = NULL; ftwbuf->at_fd = AT_FDCWD; ftwbuf->at_path = ftwbuf->path; ftwbuf->stat_flags = BFS_STAT_NOFOLLOW; + bftw_stat_init(&ftwbuf->lstat_cache); + bftw_stat_init(&ftwbuf->stat_cache); if (dir) { ftwbuf->nameoff = dir->nameoff; @@ -960,8 +1006,12 @@ static void bftw_prepare_visit(struct bftw_state *state) { ftwbuf->stat_flags = BFS_STAT_TRYFOLLOW; } + const struct bfs_stat *statbuf = NULL; if (bftw_need_stat(state)) { - if (bftw_stat(ftwbuf, &state->statbuf) != 0) { + statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags); + if (statbuf) { + ftwbuf->typeflag = bftw_mode_typeflag(statbuf->mode); + } else { ftwbuf->typeflag = BFTW_ERROR; ftwbuf->error = errno; return; @@ -969,7 +1019,6 @@ static void bftw_prepare_visit(struct bftw_state *state) { } if (ftwbuf->typeflag == BFTW_DIR && (state->flags & BFTW_DETECT_CYCLES)) { - const struct bfs_stat *statbuf = ftwbuf->statbuf; for (const struct bftw_dir *parent = dir; parent; parent = parent->parent) { if (parent->depth == ftwbuf->depth) { continue; @@ -1030,7 +1079,11 @@ static int bftw_push(struct bftw_state *state, const char *name) { return -1; } - const struct bfs_stat *statbuf = state->ftwbuf.statbuf; + const struct BFTW *ftwbuf = &state->ftwbuf; + const struct bfs_stat *statbuf = ftwbuf->stat_cache.buf; + if (!statbuf) { + statbuf = ftwbuf->lstat_cache.buf; + } if (statbuf) { dir->dev = statbuf->dev; dir->ino = statbuf->ino; @@ -1194,17 +1247,19 @@ int bftw(const char *path, const struct bftw_args *args) { goto done; } - if (state.ftwbuf.typeflag == BFTW_DIR) { - const struct bfs_stat *statbuf = state.ftwbuf.statbuf; - if ((args->flags & BFTW_XDEV) - && statbuf - && statbuf->dev != reader->dir->dev) { + if (state.ftwbuf.typeflag != BFTW_DIR) { + goto read; + } + + if (args->flags & BFTW_XDEV) { + const struct bfs_stat *statbuf = bftw_stat(&state.ftwbuf, state.ftwbuf.stat_flags); + if (statbuf && statbuf->dev != reader->dir->dev) { goto read; } + } - if (bftw_push(&state, name) != 0) { - goto done; - } + if (bftw_push(&state, name) != 0) { + goto done; } read: @@ -71,6 +71,18 @@ enum bftw_visit { }; /** + * Cached bfs_stat() info for a file. + */ +struct bftw_stat { + /** A pointer to the bfs_stat() buffer, if available. */ + const struct bfs_stat *buf; + /** Storage for the bfs_stat() buffer, if needed. */ + struct bfs_stat storage; + /** The cached error code, if any. */ + int error; +}; + +/** * Data about the current file for the bftw() callback. */ struct BFTW { @@ -91,18 +103,47 @@ struct BFTW { /** The errno that occurred, if typeflag == BFTW_ERROR. */ int error; - /** A bfs_stat() buffer; may be NULL if no stat() call was needed. */ - const struct bfs_stat *statbuf; - /** A parent file descriptor for the *at() family of calls. */ int at_fd; /** The path relative to at_fd for the *at() family of calls. */ const char *at_path; + /** Flags for bfs_stat(). */ enum bfs_stat_flag stat_flags; + /** Cached bfs_stat() info for BFS_STAT_NOFOLLOW. */ + struct bftw_stat lstat_cache; + /** Cached bfs_stat() info for BFS_STAT_FOLLOW. */ + struct bftw_stat stat_cache; }; /** + * Get bfs_stat() info for a file encountered during bftw(), caching the result + * whenever possible. + * + * @param ftwbuf + * bftw() data for the file to stat. + * @param flags + * flags for bfs_stat(). Pass ftwbuf->stat_flags for the default flags. + * @return + * A pointer to a bfs_stat() buffer, or NULL if the call failed. + */ +const struct bfs_stat *bftw_stat(struct BFTW *ftwbuf, enum bfs_stat_flag flags); + +/** + * Get the type of a file encountered during bftw(), with flags controlling + * whether to follow links. This function will avoid calling bfs_stat() if + * possible. + * + * @param ftwbuf + * bftw() data for the file to check. + * @param flags + * flags for bfs_stat(). Pass ftwbuf->stat_flags for the default flags. + * @return + * The type of the file, or BFTW_ERROR if an error occurred. + */ +enum bftw_typeflag bftw_typeflag(struct BFTW *ftwbuf, enum bfs_stat_flag flags); + +/** * Walk actions returned by the bftw() callback. */ enum bftw_action { @@ -550,8 +550,8 @@ static bool is_link_broken(const struct BFTW *ftwbuf) { } /** Get the color for a file. */ -static const char *file_color(const struct colors *colors, const char *filename, const struct BFTW *ftwbuf) { - const struct bfs_stat *sb = ftwbuf->statbuf; +static const char *file_color(const struct colors *colors, const char *filename, struct BFTW *ftwbuf, enum bfs_stat_flag flags) { + const struct bfs_stat *sb = bftw_stat(ftwbuf, flags); if (!sb) { if (colors->missing) { return colors->missing; @@ -692,54 +692,54 @@ static int print_colored(const struct colors *colors, const char *esc, const cha } /** Print a path with colors. */ -static int print_path_colored(CFILE *cfile, const struct BFTW *ftwbuf) { +static int print_path_colored(CFILE *cfile, const char *path, struct BFTW *ftwbuf, enum bfs_stat_flag flags) { const struct colors *colors = cfile->colors; FILE *file = cfile->file; - const char *path = ftwbuf->path; - if (ftwbuf->nameoff > 0) { - if (print_colored(colors, colors->directory, path, ftwbuf->nameoff, file) != 0) { + size_t nameoff; + if (path == ftwbuf->path) { + nameoff = ftwbuf->nameoff; + } else { + nameoff = xbasename(path) - path; + } + + if (nameoff > 0) { + if (print_colored(colors, colors->directory, path, nameoff, file) != 0) { return -1; } } - const char *filename = path + ftwbuf->nameoff; - const char *color = file_color(colors, filename, ftwbuf); + const char *filename = path + nameoff; + const char *color = file_color(colors, filename, ftwbuf, flags); return print_colored(colors, color, filename, strlen(filename), file); } -/** Call stat() again to resolve a link target. */ -static void restat(struct BFTW *ftwbuf, struct bfs_stat *statbuf) { - if (bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, ftwbuf->stat_flags, statbuf) == 0) { - ftwbuf->statbuf = statbuf; - } -} - /** Print the path to a file with the appropriate colors. */ -static int print_path(CFILE *cfile, const struct BFTW *ftwbuf) { +static int print_path(CFILE *cfile, struct BFTW *ftwbuf) { const struct colors *colors = cfile->colors; if (!colors) { return fputs(ftwbuf->path, cfile->file) == EOF ? -1 : 0; } - if (colors && colors->link_as_target) { - if (ftwbuf->typeflag == BFTW_LNK && (ftwbuf->stat_flags & BFS_STAT_NOFOLLOW)) { - struct BFTW altbuf = *ftwbuf; - altbuf.stat_flags = BFS_STAT_FOLLOW; - struct bfs_stat statbuf; - restat(&altbuf, &statbuf); - return print_path_colored(cfile, &altbuf); - } + enum bfs_stat_flag flags = ftwbuf->stat_flags; + if (colors && colors->link_as_target && ftwbuf->typeflag == BFTW_LNK) { + flags = BFS_STAT_TRYFOLLOW; } - return print_path_colored(cfile, ftwbuf); + return print_path_colored(cfile, ftwbuf->path, ftwbuf, flags); } /** Print a link target with the appropriate colors. */ -static int print_link_target(CFILE *cfile, const struct BFTW *ftwbuf) { +static int print_link_target(CFILE *cfile, struct BFTW *ftwbuf) { int ret = -1; - char *target = xreadlinkat(ftwbuf->at_fd, ftwbuf->at_path, 0); + size_t len = 0; + const struct bfs_stat *statbuf = bftw_stat(ftwbuf, BFS_STAT_NOFOLLOW); + if (statbuf) { + len = statbuf->size; + } + + char *target = xreadlinkat(ftwbuf->at_fd, ftwbuf->at_path, len); if (!target) { goto done; } @@ -749,16 +749,7 @@ static int print_link_target(CFILE *cfile, const struct BFTW *ftwbuf) { goto done; } - struct BFTW altbuf = *ftwbuf; - altbuf.path = target; - altbuf.nameoff = xbasename(target) - target; - altbuf.stat_flags = BFS_STAT_FOLLOW; - altbuf.statbuf = NULL; - - struct bfs_stat statbuf; - restat(&altbuf, &statbuf); - - ret = print_path_colored(cfile, &altbuf); + ret = print_path_colored(cfile, target, ftwbuf, BFS_STAT_FOLLOW); done: free(target); @@ -837,13 +828,13 @@ int cvfprintf(CFILE *cfile, const char *format, va_list args) { case 'p': switch (*++i) { case 'P': - if (print_path(cfile, va_arg(args, const struct BFTW *)) != 0) { + if (print_path(cfile, va_arg(args, struct BFTW *)) != 0) { return -1; } break; case 'L': - if (print_link_target(cfile, va_arg(args, const struct BFTW *)) != 0) { + if (print_link_target(cfile, va_arg(args, struct BFTW *)) != 0) { return -1; } break; @@ -106,8 +106,8 @@ int cfclose(CFILE *cfile); * %s: A string * %zu: A size_t * %m: strerror(errno) - * %pP: A colored file path, from a const struct BFTW * argument - * %pL: A colored link target, from a const struct BFTW * argument + * %pP: A colored file path, from a struct BFTW * argument + * %pL: A colored link target, from a struct BFTW * argument * %%: A literal '%' * ${cc}: Change the color to 'cc' * $$: A literal '$' @@ -59,79 +59,8 @@ struct eval_state { int *ret; /** Whether to quit immediately. */ bool *quit; - /** A bfs_stat() buffer, if necessary. */ - struct bfs_stat statbuf; - /** A bfs_stat() buffer for -xtype style tests. */ - struct bfs_stat xstatbuf; }; -#define DEBUG_FLAG(flags, flag) \ - do { \ - if ((flags & flag) || flags == flag) { \ - fputs(#flag, stderr); \ - flags ^= flag; \ - if (flags) { \ - fputs(" | ", stderr); \ - } \ - } \ - } while (0) - -/** - * Debug stat() calls. - */ -static void debug_stat(const struct eval_state *state, enum bfs_stat_flag flags) { - if (!(state->cmdline->debug & DEBUG_STAT)) { - return; - } - - struct BFTW *ftwbuf = state->ftwbuf; - - fprintf(stderr, "bfs_stat("); - if (ftwbuf->at_fd == AT_FDCWD) { - fprintf(stderr, "AT_FDCWD"); - } else { - size_t baselen = strlen(ftwbuf->path) - strlen(ftwbuf->at_path); - fprintf(stderr, "\""); - fwrite(ftwbuf->path, 1, baselen, stderr); - fprintf(stderr, "\""); - } - - fprintf(stderr, ", \"%s\", ", ftwbuf->at_path); - - DEBUG_FLAG(flags, BFS_STAT_FOLLOW); - DEBUG_FLAG(flags, BFS_STAT_NOFOLLOW); - DEBUG_FLAG(flags, BFS_STAT_TRYFOLLOW); - - fprintf(stderr, ")\n"); -} - -/** - * Perform a bfs_stat() call if necessary. - */ -static const struct bfs_stat *eval_try_stat(struct eval_state *state) { - struct BFTW *ftwbuf = state->ftwbuf; - - if (ftwbuf->statbuf) { - goto done; - } - - if (ftwbuf->error) { - errno = ftwbuf->error; - goto done; - } - - debug_stat(state, ftwbuf->stat_flags); - - if (bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, ftwbuf->stat_flags, &state->statbuf) == 0) { - ftwbuf->statbuf = &state->statbuf; - } else { - ftwbuf->error = errno; - } - -done: - return ftwbuf->statbuf; -} - /** * Print an error message. */ @@ -141,10 +70,6 @@ static void eval_error(struct eval_state *state, const char *format, ...) { const struct cmdline *cmdline = state->cmdline; CFILE *cerr = cmdline->cerr; - if (cerr->colors) { - eval_try_stat(state); - } - bfs_error(cmdline, "%pP: ", state->ftwbuf); va_list args; @@ -177,7 +102,8 @@ static void eval_report_error(struct eval_state *state) { * Perform a bfs_stat() call if necessary. */ static const struct bfs_stat *eval_stat(struct eval_state *state) { - const struct bfs_stat *ret = eval_try_stat(state); + struct BFTW *ftwbuf = state->ftwbuf; + const struct bfs_stat *ret = bftw_stat(ftwbuf, ftwbuf->stat_flags); if (!ret) { eval_report_error(state); } @@ -185,22 +111,6 @@ static const struct bfs_stat *eval_stat(struct eval_state *state) { } /** - * Perform a bfs_stat() call for tests that flip the follow flag, like -xtype. - */ -static const struct bfs_stat *eval_xstat(struct eval_state *state) { - struct BFTW *ftwbuf = state->ftwbuf; - struct bfs_stat *statbuf = &state->xstatbuf; - if (!statbuf->mask) { - enum bfs_stat_flag flags = ftwbuf->stat_flags ^ (BFS_STAT_NOFOLLOW | BFS_STAT_TRYFOLLOW); - debug_stat(state, flags); - if (bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, flags, statbuf) != 0) { - return NULL; - } - } - return statbuf; -} - -/** * Get the difference (in seconds) between two struct timespecs. */ static time_t timespec_diff(const struct timespec *lhs, const struct timespec *rhs) { @@ -415,21 +325,14 @@ bool eval_delete(const struct expr *expr, struct eval_state *state) { } int flag = 0; - if (ftwbuf->stat_flags & BFS_STAT_NOFOLLOW) { - if (ftwbuf->typeflag == BFTW_DIR) { - flag |= AT_REMOVEDIR; - } - } else if (ftwbuf->typeflag != BFTW_LNK) { - // We need to know the actual type of the path, not what it points to - const struct bfs_stat *statbuf = eval_xstat(state); - if (statbuf) { - if (S_ISDIR(statbuf->mode)) { - flag |= AT_REMOVEDIR; - } - } else { - eval_report_error(state); - return false; - } + + // We need to know the actual type of the path, not what it points to + enum bftw_typeflag type = bftw_typeflag(ftwbuf, BFS_STAT_NOFOLLOW); + if (type == BFTW_DIR) { + flag |= AT_REMOVEDIR; + } else if (type == BFTW_ERROR) { + eval_report_error(state); + return false; } if (unlinkat(ftwbuf->at_fd, ftwbuf->at_path, flag) != 0) { @@ -698,7 +601,7 @@ bool eval_perm(const struct expr *expr, struct eval_state *state) { bool eval_fls(const struct expr *expr, struct eval_state *state) { CFILE *cfile = expr->cfile; FILE *file = cfile->file; - const struct BFTW *ftwbuf = state->ftwbuf; + struct BFTW *ftwbuf = state->ftwbuf; const struct bfs_stat *statbuf = eval_stat(state); if (!statbuf) { goto done; @@ -825,17 +728,10 @@ bool eval_fprint0(const struct expr *expr, struct eval_state *state) { * -f?printf action. */ bool eval_fprintf(const struct expr *expr, struct eval_state *state) { - if (expr->printf->needs_stat) { - if (!eval_stat(state)) { - goto done; - } - } - if (bfs_printf(expr->cfile->file, expr->printf, state->ftwbuf) != 0) { eval_report_error(state); } -done: return true; } @@ -989,22 +885,13 @@ bool eval_type(const struct expr *expr, struct eval_state *state) { */ bool eval_xtype(const struct expr *expr, struct eval_state *state) { struct BFTW *ftwbuf = state->ftwbuf; - - bool follow = !(ftwbuf->stat_flags & BFS_STAT_NOFOLLOW); - bool is_link = ftwbuf->typeflag == BFTW_LNK; - if (follow == is_link) { - return eval_type(expr, state); - } - - const struct bfs_stat *statbuf = eval_xstat(state); - if (statbuf) { - return bftw_mode_typeflag(statbuf->mode) & expr->idata; - } else if (!follow && is_nonexistence_error(errno)) { - // Broken symlink - return eval_type(expr, state); - } else { + enum bfs_stat_flag flags = ftwbuf->stat_flags ^ (BFS_STAT_NOFOLLOW | BFS_STAT_TRYFOLLOW); + enum bftw_typeflag type = bftw_typeflag(ftwbuf, flags); + if (type == BFTW_ERROR) { eval_report_error(state); return false; + } else { + return type & expr->idata; } } @@ -1156,6 +1043,61 @@ static bool eval_file_unique(struct eval_state *state, struct trie *seen) { } } +#define DEBUG_FLAG(flags, flag) \ + do { \ + if ((flags & flag) || flags == flag) { \ + fputs(#flag, stderr); \ + flags ^= flag; \ + if (flags) { \ + fputs(" | ", stderr); \ + } \ + } \ + } while (0) + +/** + * Log a stat() call. + */ +static void debug_stat(const struct BFTW *ftwbuf, const struct bftw_stat *cache, enum bfs_stat_flag flags) { + fprintf(stderr, "bfs_stat("); + if (ftwbuf->at_fd == AT_FDCWD) { + fprintf(stderr, "AT_FDCWD"); + } else { + size_t baselen = strlen(ftwbuf->path) - strlen(ftwbuf->at_path); + fprintf(stderr, "\""); + fwrite(ftwbuf->path, 1, baselen, stderr); + fprintf(stderr, "\""); + } + + fprintf(stderr, ", \"%s\", ", ftwbuf->at_path); + + DEBUG_FLAG(flags, BFS_STAT_FOLLOW); + DEBUG_FLAG(flags, BFS_STAT_NOFOLLOW); + DEBUG_FLAG(flags, BFS_STAT_TRYFOLLOW); + + fprintf(stderr, ") == %d", cache->buf ? 0 : -1); + + if (cache->error) { + fprintf(stderr, " [%d]", cache->error); + } + + fprintf(stderr, "\n"); +} + +/** + * Log any stat() calls that happened. + */ +static void debug_stats(const struct BFTW *ftwbuf) { + const struct bfs_stat *statbuf = ftwbuf->stat_cache.buf; + if (statbuf || ftwbuf->stat_cache.error) { + debug_stat(ftwbuf, &ftwbuf->stat_cache, BFS_STAT_FOLLOW); + } + + const struct bfs_stat *lstatbuf = ftwbuf->lstat_cache.buf; + if ((lstatbuf && lstatbuf != statbuf) || ftwbuf->lstat_cache.error) { + debug_stat(ftwbuf, &ftwbuf->lstat_cache, BFS_STAT_NOFOLLOW); + } +} + /** * Dump the bftw_typeflag for -D search. */ @@ -1237,11 +1179,6 @@ static enum bftw_action cmdline_callback(struct BFTW *ftwbuf, void *ptr) { state.action = BFTW_CONTINUE; state.ret = &args->ret; state.quit = &args->quit; - state.xstatbuf.mask = 0; - - if (ftwbuf->statbuf) { - debug_stat(&state, ftwbuf->stat_flags); - } if (ftwbuf->typeflag == BFTW_ERROR) { if (!eval_should_ignore(&state, ftwbuf->error)) { @@ -1284,6 +1221,10 @@ static enum bftw_action cmdline_callback(struct BFTW *ftwbuf, void *ptr) { } done: + if (cmdline->debug & DEBUG_STAT) { + debug_stats(ftwbuf); + } + if (cmdline->debug & DEBUG_SEARCH) { fprintf(stderr, "cmdline_callback({ " @@ -33,12 +33,9 @@ #include <string.h> #include <time.h> -typedef int bfs_printf_fn(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf); +typedef int bfs_printf_fn(FILE *file, const struct bfs_printf *directive, struct BFTW *ftwbuf); -/** - * A single directive in a printf command. - */ -struct bfs_printf_directive { +struct bfs_printf { /** The printing function to invoke. */ bfs_printf_fn *fn; /** String data associated with this directive. */ @@ -50,11 +47,11 @@ struct bfs_printf_directive { /** The current mount table. */ const struct bfs_mtab *mtab; /** The next printf directive in the chain. */ - struct bfs_printf_directive *next; + struct bfs_printf *next; }; /** Print some text as-is. */ -static int bfs_printf_literal(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { +static int bfs_printf_literal(FILE *file, const struct bfs_printf *directive, struct BFTW *ftwbuf) { size_t len = dstrlen(directive->str); if (fwrite(directive->str, 1, len, file) == len) { return 0; @@ -64,7 +61,7 @@ static int bfs_printf_literal(FILE *file, const struct bfs_printf_directive *dir } /** \c: flush */ -static int bfs_printf_flush(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { +static int bfs_printf_flush(FILE *file, const struct bfs_printf *directive, struct BFTW *ftwbuf) { return fflush(file); } @@ -78,12 +75,17 @@ static int bfs_printf_flush(FILE *file, const struct bfs_printf_directive *direc (void)ret /** %a, %c, %t: ctime() */ -static int bfs_printf_ctime(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { +static int bfs_printf_ctime(FILE *file, const struct bfs_printf *directive, struct BFTW *ftwbuf) { // Not using ctime() itself because GNU find adds nanoseconds static const char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; static const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; - const struct timespec *ts = bfs_stat_time(ftwbuf->statbuf, directive->stat_field); + const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags); + if (!statbuf) { + return -1; + } + + const struct timespec *ts = bfs_stat_time(statbuf, directive->stat_field); if (!ts) { return -1; } @@ -107,8 +109,13 @@ static int bfs_printf_ctime(FILE *file, const struct bfs_printf_directive *direc } /** %A, %B/%W, %C, %T: strftime() */ -static int bfs_printf_strftime(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { - const struct timespec *ts = bfs_stat_time(ftwbuf->statbuf, directive->stat_field); +static int bfs_printf_strftime(FILE *file, const struct bfs_printf *directive, struct BFTW *ftwbuf) { + const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags); + if (!statbuf) { + return -1; + } + + const struct timespec *ts = bfs_stat_time(statbuf, directive->stat_field); if (!ts) { return -1; } @@ -170,43 +177,68 @@ static int bfs_printf_strftime(FILE *file, const struct bfs_printf_directive *di } /** %b: blocks */ -static int bfs_printf_b(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { - uintmax_t blocks = ((uintmax_t)ftwbuf->statbuf->blocks*BFS_STAT_BLKSIZE + 511)/512; +static int bfs_printf_b(FILE *file, const struct bfs_printf *directive, struct BFTW *ftwbuf) { + const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags); + if (!statbuf) { + return -1; + } + + uintmax_t blocks = ((uintmax_t)statbuf->blocks*BFS_STAT_BLKSIZE + 511)/512; BFS_PRINTF_BUF(buf, "%ju", blocks); return fprintf(file, directive->str, buf); } /** %d: depth */ -static int bfs_printf_d(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { +static int bfs_printf_d(FILE *file, const struct bfs_printf *directive, struct BFTW *ftwbuf) { return fprintf(file, directive->str, (intmax_t)ftwbuf->depth); } /** %D: device */ -static int bfs_printf_D(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { - BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)ftwbuf->statbuf->dev); +static int bfs_printf_D(FILE *file, const struct bfs_printf *directive, struct BFTW *ftwbuf) { + const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags); + if (!statbuf) { + return -1; + } + + BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)statbuf->dev); return fprintf(file, directive->str, buf); } /** %f: file name */ -static int bfs_printf_f(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { +static int bfs_printf_f(FILE *file, const struct bfs_printf *directive, struct BFTW *ftwbuf) { return fprintf(file, directive->str, ftwbuf->path + ftwbuf->nameoff); } /** %F: file system type */ -static int bfs_printf_F(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { - const char *type = bfs_fstype(directive->mtab, ftwbuf->statbuf); +static int bfs_printf_F(FILE *file, const struct bfs_printf *directive, struct BFTW *ftwbuf) { + const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags); + if (!statbuf) { + return -1; + } + + const char *type = bfs_fstype(directive->mtab, statbuf); return fprintf(file, directive->str, type); } /** %G: gid */ -static int bfs_printf_G(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { - BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)ftwbuf->statbuf->gid); +static int bfs_printf_G(FILE *file, const struct bfs_printf *directive, struct BFTW *ftwbuf) { + const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags); + if (!statbuf) { + return -1; + } + + BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)statbuf->gid); return fprintf(file, directive->str, buf); } /** %g: group name */ -static int bfs_printf_g(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { - struct group *grp = getgrgid(ftwbuf->statbuf->gid); +static int bfs_printf_g(FILE *file, const struct bfs_printf *directive, struct BFTW *ftwbuf) { + const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags); + if (!statbuf) { + return -1; + } + + struct group *grp = getgrgid(statbuf->gid); if (!grp) { return bfs_printf_G(file, directive, ftwbuf); } @@ -215,7 +247,7 @@ static int bfs_printf_g(FILE *file, const struct bfs_printf_directive *directive } /** %h: leading directories */ -static int bfs_printf_h(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { +static int bfs_printf_h(FILE *file, const struct bfs_printf *directive, struct BFTW *ftwbuf) { char *copy = NULL; const char *buf; @@ -242,25 +274,35 @@ static int bfs_printf_h(FILE *file, const struct bfs_printf_directive *directive } /** %H: current root */ -static int bfs_printf_H(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { +static int bfs_printf_H(FILE *file, const struct bfs_printf *directive, struct BFTW *ftwbuf) { return fprintf(file, directive->str, ftwbuf->root); } /** %i: inode */ -static int bfs_printf_i(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { - BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)ftwbuf->statbuf->ino); +static int bfs_printf_i(FILE *file, const struct bfs_printf *directive, struct BFTW *ftwbuf) { + const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags); + if (!statbuf) { + return -1; + } + + BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)statbuf->ino); return fprintf(file, directive->str, buf); } /** %k: 1K blocks */ -static int bfs_printf_k(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { - uintmax_t blocks = ((uintmax_t)ftwbuf->statbuf->blocks*BFS_STAT_BLKSIZE + 1023)/1024; +static int bfs_printf_k(FILE *file, const struct bfs_printf *directive, struct BFTW *ftwbuf) { + const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags); + if (!statbuf) { + return -1; + } + + uintmax_t blocks = ((uintmax_t)statbuf->blocks*BFS_STAT_BLKSIZE + 1023)/1024; BFS_PRINTF_BUF(buf, "%ju", blocks); return fprintf(file, directive->str, buf); } /** %l: link target */ -static int bfs_printf_l(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { +static int bfs_printf_l(FILE *file, const struct bfs_printf *directive, struct BFTW *ftwbuf) { if (ftwbuf->typeflag != BFTW_LNK) { return 0; } @@ -276,30 +318,45 @@ static int bfs_printf_l(FILE *file, const struct bfs_printf_directive *directive } /** %m: mode */ -static int bfs_printf_m(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { - return fprintf(file, directive->str, (unsigned int)(ftwbuf->statbuf->mode & 07777)); +static int bfs_printf_m(FILE *file, const struct bfs_printf *directive, struct BFTW *ftwbuf) { + const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags); + if (!statbuf) { + return -1; + } + + return fprintf(file, directive->str, (unsigned int)(statbuf->mode & 07777)); } /** %M: symbolic mode */ -static int bfs_printf_M(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { +static int bfs_printf_M(FILE *file, const struct bfs_printf *directive, struct BFTW *ftwbuf) { + const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags); + if (!statbuf) { + return -1; + } + char buf[11]; - format_mode(ftwbuf->statbuf->mode, buf); + format_mode(statbuf->mode, buf); return fprintf(file, directive->str, buf); } /** %n: link count */ -static int bfs_printf_n(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { - BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)ftwbuf->statbuf->nlink); +static int bfs_printf_n(FILE *file, const struct bfs_printf *directive, struct BFTW *ftwbuf) { + const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags); + if (!statbuf) { + return -1; + } + + BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)statbuf->nlink); return fprintf(file, directive->str, buf); } /** %p: full path */ -static int bfs_printf_p(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { +static int bfs_printf_p(FILE *file, const struct bfs_printf *directive, struct BFTW *ftwbuf) { return fprintf(file, directive->str, ftwbuf->path); } /** %P: path after root */ -static int bfs_printf_P(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { +static int bfs_printf_P(FILE *file, const struct bfs_printf *directive, struct BFTW *ftwbuf) { const char *path = ftwbuf->path + strlen(ftwbuf->root); if (path[0] == '/') { ++path; @@ -308,32 +365,51 @@ static int bfs_printf_P(FILE *file, const struct bfs_printf_directive *directive } /** %s: size */ -static int bfs_printf_s(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { - BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)ftwbuf->statbuf->size); +static int bfs_printf_s(FILE *file, const struct bfs_printf *directive, struct BFTW *ftwbuf) { + const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags); + if (!statbuf) { + return -1; + } + + BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)statbuf->size); return fprintf(file, directive->str, buf); } /** %S: sparseness */ -static int bfs_printf_S(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { +static int bfs_printf_S(FILE *file, const struct bfs_printf *directive, struct BFTW *ftwbuf) { + const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags); |