From 309c9a29e32edc07cb06e329669be7e30f8d9ed5 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 16 Sep 2015 12:25:53 -0400 Subject: bftw() interface improvements: - Use enums instead of ints where it makes sense - Move the file path inside struct BFTW - Expose a fd and relative path for *at() calls --- bfs.c | 46 ++++++++++++------------- bftw.c | 90 +++++++++++++++++++++---------------------------- bftw.h | 118 +++++++++++++++++++++++++++++++++++++--------------------------- color.c | 14 ++++---- color.h | 10 +++--- 5 files changed, 141 insertions(+), 137 deletions(-) diff --git a/bfs.c b/bfs.c index 359df63..d640068 100644 --- a/bfs.c +++ b/bfs.c @@ -36,14 +36,12 @@ typedef struct cmdline cmdline; * Ephemeral state for evaluating an expression. */ typedef struct { - /** The path to the encountered file. */ - const char *fpath; - /** Additional data about the current file. */ + /** Data about the current file. */ const struct BFTW *ftwbuf; /** The parsed command line. */ const cmdline *cl; /** The bftw() callback return value. */ - int ret; + bftw_action action; } eval_state; /** @@ -288,15 +286,16 @@ static const char *skip_paths(parser_state *state) { * -delete action. */ static bool eval_delete(const expression *expr, eval_state *state) { + const struct BFTW *ftwbuf = state->ftwbuf; + int flag = 0; - if (state->ftwbuf->typeflag == BFTW_DIR) { + if (ftwbuf->typeflag == BFTW_DIR) { flag |= AT_REMOVEDIR; } - // TODO: Have bftw() tell us a better base fd to use - if (unlinkat(AT_FDCWD, state->fpath, flag) != 0) { - print_error(state->cl->colors, state->fpath, errno); - state->ret = BFTW_STOP; + if (unlinkat(ftwbuf->at_fd, ftwbuf->at_path, flag) != 0) { + print_error(state->cl->colors, ftwbuf->path, errno); + state->action = BFTW_STOP; } return true; @@ -306,7 +305,7 @@ static bool eval_delete(const expression *expr, eval_state *state) { * -prune action. */ static bool eval_prune(const expression *expr, eval_state *state) { - state->ret = BFTW_SKIP_SUBTREE; + state->action = BFTW_SKIP_SUBTREE; return true; } @@ -314,8 +313,8 @@ static bool eval_prune(const expression *expr, eval_state *state) { * -hidden test. */ static bool eval_hidden(const expression *expr, eval_state *state) { - size_t base = state->ftwbuf->base; - return base > 0 && state->fpath[base] == '.'; + const struct BFTW *ftwbuf = state->ftwbuf; + return ftwbuf->nameoff > 0 && ftwbuf->path[ftwbuf->nameoff] == '.'; } /** @@ -334,14 +333,15 @@ static bool eval_nohidden(const expression *expr, eval_state *state) { * -name test. */ static bool eval_name(const expression *expr, eval_state *state) { - return fnmatch(expr->sdata, state->fpath + state->ftwbuf->base, 0) == 0; + const struct BFTW *ftwbuf = state->ftwbuf; + return fnmatch(expr->sdata, ftwbuf->path + ftwbuf->nameoff, 0) == 0; } /** * -print action. */ static bool eval_print(const expression *expr, eval_state *state) { - pretty_print(state->cl->colors, state->fpath, state->ftwbuf); + pretty_print(state->cl->colors, state->ftwbuf); return true; } @@ -349,7 +349,8 @@ static bool eval_print(const expression *expr, eval_state *state) { * -print0 action. */ static bool eval_print0(const expression *expr, eval_state *state) { - fwrite(state->fpath, 1, strlen(state->fpath) + 1, stdout); + const char *path = state->ftwbuf->path; + fwrite(path, 1, strlen(path) + 1, stdout); return true; } @@ -854,30 +855,29 @@ static int infer_nopenfd() { /** * bftw() callback. */ -static int cmdline_callback(const char *fpath, const struct BFTW *ftwbuf, void *ptr) { +static bftw_action cmdline_callback(const struct BFTW *ftwbuf, void *ptr) { const cmdline *cl = ptr; if (ftwbuf->typeflag == BFTW_ERROR) { - print_error(cl->colors, fpath, ftwbuf->error); + print_error(cl->colors, ftwbuf->path, ftwbuf->error); return BFTW_SKIP_SUBTREE; } eval_state state = { - .fpath = fpath, .ftwbuf = ftwbuf, .cl = cl, - .ret = BFTW_CONTINUE, + .action = BFTW_CONTINUE, }; - if (ftwbuf->level >= cl->maxdepth) { - state.ret = BFTW_SKIP_SUBTREE; + if (ftwbuf->depth >= cl->maxdepth) { + state.action = BFTW_SKIP_SUBTREE; } - if (ftwbuf->level >= cl->mindepth && ftwbuf->level <= cl->maxdepth) { + if (ftwbuf->depth >= cl->mindepth && ftwbuf->depth <= cl->maxdepth) { cl->expr->eval(cl->expr, &state); } - return state.ret; + return state.action; } /** diff --git a/bftw.c b/bftw.c index 834ffca..0bc49e1 100644 --- a/bftw.c +++ b/bftw.c @@ -274,33 +274,25 @@ static int dircache_entry_path(const dircache_entry *entry, dynstr *path) { * The cache containing the entry. * @param entry * The entry being accessed. - * @param[out] fd + * @param[out] at_fd * Will hold the appropriate file descriptor to use. - * @param[in,out] relpath + * @param[in,out] at_path * Will hold the appropriate path to use. * @return The closest open ancestor entry. */ -static dircache_entry *dircache_entry_base(dircache *cache, dircache_entry *entry, int *fd, const char **relpath) { - size_t nameoff = entry->namelen + entry->nameoff; +static dircache_entry *dircache_entry_base(dircache *cache, dircache_entry *entry, int *at_fd, const char **at_path) { dircache_entry *base = entry; - while (base && !base->dir) { - nameoff = base->nameoff; + do { base = base->parent; - } + } while (base && !base->dir); if (base) { dircache_lru_remove(cache, base); dircache_lru_add(cache, base); - *fd = dirfd(base->dir); - *relpath += nameoff; - - // fstatat(fd, "") doesn't stat() fd itself, but - // fstatat(fd, ".") does, at least for directories - if (!**relpath) { - *relpath = "."; - } + *at_fd = dirfd(base->dir); + *at_path += base->nameoff + base->namelen; } return base; @@ -325,11 +317,11 @@ static DIR *dircache_entry_open(dircache *cache, dircache_entry *entry, const ch dircache_entry_close(cache, cache->lru_tail); } - int fd = AT_FDCWD; - const char *relpath = path; - dircache_entry *base = dircache_entry_base(cache, entry, &fd, &relpath); + int at_fd = AT_FDCWD; + const char *at_path = path; + dircache_entry *base = dircache_entry_base(cache, entry, &at_fd, &at_path); - DIR *dir = opendirat(fd, relpath); + DIR *dir = opendirat(at_fd, at_path); if (!dir && errno == EMFILE @@ -338,7 +330,7 @@ static DIR *dircache_entry_open(dircache *cache, dircache_entry *entry, const ch // Too many open files, shrink the LRU cache dircache_entry_close(cache, cache->lru_tail); --cache->lru_remaining; - dir = opendirat(fd, relpath); + dir = opendirat(at_fd, at_path); } if (dir) { @@ -445,14 +437,6 @@ static dircache_entry *dirqueue_pop(dirqueue *queue) { return entry; } -static void ftwbuf_init(struct BFTW *ftwbuf, int base, int level) { - ftwbuf->statbuf = NULL; - ftwbuf->typeflag = BFTW_UNKNOWN; - ftwbuf->base = base; - ftwbuf->level = level; - ftwbuf->error = 0; -} - static void ftwbuf_set_error(struct BFTW *ftwbuf, int error) { ftwbuf->typeflag = BFTW_ERROR; ftwbuf->error = error; @@ -486,8 +470,8 @@ static void ftwbuf_use_dirent(struct BFTW *ftwbuf, const struct dirent *de) { #endif } -static int ftwbuf_stat(struct BFTW *ftwbuf, struct stat *sb, int fd, const char *path) { - int ret = fstatat(fd, path, sb, AT_SYMLINK_NOFOLLOW); +static int ftwbuf_stat(struct BFTW *ftwbuf, struct stat *sb) { + int ret = fstatat(ftwbuf->at_fd, ftwbuf->at_path, sb, AT_SYMLINK_NOFOLLOW); if (ret != 0) { return ret; } @@ -604,45 +588,47 @@ static int bftw_path_concat(bftw_state *state, const char *subpath) { * Initialize the buffers with data about the current path. */ static void bftw_init_buffers(bftw_state *state, const struct dirent *de) { - size_t base = 0; - size_t level = 0; + struct BFTW *ftwbuf = &state->ftwbuf; + ftwbuf->path = state->path.str; + ftwbuf->nameoff = 0; + ftwbuf->error = 0; + ftwbuf->depth = 0; + ftwbuf->statbuf = NULL; + ftwbuf->at_fd = AT_FDCWD; + ftwbuf->at_path = ftwbuf->path; dircache_entry *current = state->current; if (current) { - base = current->nameoff; - level = current->depth; + ftwbuf->nameoff = current->nameoff; + ftwbuf->depth = current->depth; if (state->status == BFTW_CHILD) { - base += current->namelen; - ++level; + ftwbuf->nameoff += current->namelen; + ++ftwbuf->depth; } - } - ftwbuf_init(&state->ftwbuf, base, level); + dircache_entry_base(&state->cache, current, &ftwbuf->at_fd, &ftwbuf->at_path); + } if (de) { - ftwbuf_use_dirent(&state->ftwbuf, de); + ftwbuf_use_dirent(ftwbuf, de); } else if (state->status != BFTW_CHILD) { - state->ftwbuf.typeflag = BFTW_DIR; + ftwbuf->typeflag = BFTW_DIR; + } else { + ftwbuf->typeflag = BFTW_UNKNOWN; } // In BFTW_DEPTH mode, defer the stat() call for directories if ((state->flags & BFTW_DEPTH) - && state->ftwbuf.typeflag == BFTW_DIR + && ftwbuf->typeflag == BFTW_DIR && state->status == BFTW_CHILD) { return; } - if ((state->flags & BFTW_STAT) || state->ftwbuf.typeflag == BFTW_UNKNOWN) { - int fd = AT_FDCWD; - const char *relpath = state->path.str; - if (current) { - dircache_entry_base(&state->cache, current, &fd, &relpath); - } - - if (ftwbuf_stat(&state->ftwbuf, &state->statbuf, fd, relpath) != 0) { + if ((state->flags & BFTW_STAT) || ftwbuf->typeflag == BFTW_UNKNOWN) { + if (ftwbuf_stat(ftwbuf, &state->statbuf) != 0) { state->error = errno; - ftwbuf_set_error(&state->ftwbuf, state->error); + ftwbuf_set_error(ftwbuf, state->error); } } } @@ -666,7 +652,7 @@ static int bftw_handle_path(bftw_state *state) { return BFTW_CONTINUE; } - int action = state->fn(state->path.str, &state->ftwbuf, state->ptr); + bftw_action action = state->fn(&state->ftwbuf, state->ptr); switch (action) { case BFTW_CONTINUE: case BFTW_SKIP_SIBLINGS: @@ -766,7 +752,7 @@ static void bftw_state_free(bftw_state *state) { dynstr_free(&state->path); } -int bftw(const char *path, bftw_fn *fn, int nopenfd, int flags, void *ptr) { +int bftw(const char *path, bftw_fn *fn, int nopenfd, bftw_flags flags, void *ptr) { int ret = -1; bftw_state state; diff --git a/bftw.h b/bftw.h index a368777..14055ac 100644 --- a/bftw.h +++ b/bftw.h @@ -12,37 +12,90 @@ #ifndef BFS_BFTW_H #define BFS_BFTW_H +#include #include +/** + * Possible file types. + */ +typedef enum { + /** Unknown type. */ + BFTW_UNKNOWN, + /** Block device. */ + BFTW_BLK, + /** Character device. */ + BFTW_CHR, + /** Directory. */ + BFTW_DIR, + /** Pipe. */ + BFTW_FIFO, + /** Symbolic link. */ + BFTW_LNK, + /** Regular file. */ + BFTW_REG, + /** Socket. */ + BFTW_SOCK, + /** An error occurred for this file. */ + BFTW_ERROR, +} bftw_typeflag; + /** * Data about the current file for the bftw() callback. */ struct BFTW { - /** A stat() buffer; may be NULL if no stat() call was needed. */ - const struct stat *statbuf; - /** A typeflag value (see below). */ - int typeflag; - /** The string offset of the filename in the path. */ - int base; - /** The depth of this file in the walk. */ - int level; + /** The path to the file. */ + const char *path; + /** The string offset of the filename. */ + size_t nameoff; + + /** The depth of this file in the traversal. */ + size_t depth; + + /** The file type. */ + bftw_typeflag typeflag; /** The errno that occurred, if typeflag == BFTW_ERROR. */ int error; + + /** A stat() buffer; may be NULL if no stat() call was needed. */ + const struct stat *statbuf; + + /** A parent file descriptor for the *at() family of calls. */ + int at_fd; + /** The path relative to atfd for the *at() family of calls. */ + const char *at_path; }; +typedef enum { + /** Keep walking. */ + BFTW_CONTINUE, + /** Skip this path's siblings. */ + BFTW_SKIP_SIBLINGS, + /** Skip this path's children. */ + BFTW_SKIP_SUBTREE, + /** Stop walking. */ + BFTW_STOP, +} bftw_action; + /** * Callback function type for bftw(). * - * @param fpath - * The path to the encountered file. * @param ftwbuf - * Additional data about the current file. + * Data about the current file. * @param ptr * The pointer passed to bftw(). * @return - * An action value (see below). + * An action value. */ -typedef int bftw_fn(const char *fpath, const struct BFTW *ftwbuf, void *ptr); +typedef bftw_action bftw_fn(const struct BFTW *ftwbuf, void *ptr); + +typedef enum { + /** stat() each encountered file. */ + BFTW_STAT = 1 << 0, + /** Attempt to recover from encountered errors. */ + BFTW_RECOVER = 1 << 1, + /** Visit all of a directory's descendants before the directory itself. */ + BFTW_DEPTH = 1 << 2, +} bftw_flags; /** * Breadth First Tree Walk (or Better File Tree Walk). @@ -58,47 +111,12 @@ typedef int bftw_fn(const char *fpath, const struct BFTW *ftwbuf, void *ptr); * @param nopenfd * The maximum number of file descriptors to keep open. * @param flags - * Flags that control bftw() behavior (see below). + * Flags that control bftw() behavior. * @param ptr * A generic pointer which is passed to fn(). * @return * 0 on success, or -1 on failure. */ -int bftw(const char *path, bftw_fn *fn, int nopenfd, int flags, void *ptr); - -/** typeflag: Block device. */ -#define BFTW_BLK 0 -/** typeflag: Character device. */ -#define BFTW_CHR 1 -/** typeflag: Directory. */ -#define BFTW_DIR 2 -/** typeflag: Pipe. */ -#define BFTW_FIFO 3 -/** typeflag: Symbolic link. */ -#define BFTW_LNK 4 -/** typeflag: Regular file. */ -#define BFTW_REG 5 -/** typeflag: Socket. */ -#define BFTW_SOCK 6 -/** typeflag: Unknown type. */ -#define BFTW_UNKNOWN 7 -/** typeflag: An error occurred for this file. */ -#define BFTW_ERROR 8 - -/** action: Keep walking. */ -#define BFTW_CONTINUE 0 -/** action: Skip this path's siblings. */ -#define BFTW_SKIP_SIBLINGS 1 -/** action: Skip this path's children. */ -#define BFTW_SKIP_SUBTREE 2 -/** action: Stop walking. */ -#define BFTW_STOP 3 - -/** flag: stat() each encountered file. */ -#define BFTW_STAT (1 << 0) -/** flag: Attempt to recover from encountered errors. */ -#define BFTW_RECOVER (1 << 1) -/** flag: Visit all of a directory's descendants before the directory itself. */ -#define BFTW_DEPTH (1 << 2) +int bftw(const char *path, bftw_fn *fn, int nopenfd, bftw_flags flags, void *ptr); #endif // BFS_BFTW_H diff --git a/color.c b/color.c index 2928fe2..b67d6a4 100644 --- a/color.c +++ b/color.c @@ -282,18 +282,20 @@ static void print_esc(const char *esc, FILE *file) { fputs("m", file); } -void pretty_print(const color_table *colors, const char *fpath, const struct BFTW *ftwbuf) { +void pretty_print(const color_table *colors, const struct BFTW *ftwbuf) { + const char *path = ftwbuf->path; + if (!colors) { - puts(fpath); + puts(path); return; } - const char *filename = fpath + ftwbuf->base; + const char *filename = path + ftwbuf->nameoff; if (colors->dir) { print_esc(colors->dir, stdout); } - fwrite(fpath, 1, ftwbuf->base, stdout); + fwrite(path, 1, ftwbuf->nameoff, stdout); if (colors->dir) { print_esc(colors->reset, stdout); } @@ -309,7 +311,7 @@ void pretty_print(const color_table *colors, const char *fpath, const struct BFT fputs("\n", stdout); } -void print_error(const color_table *colors, const char *fpath, int error) { +void print_error(const color_table *colors, const char *path, int error) { const char *color = NULL; if (colors) { color = colors->orphan; @@ -318,7 +320,7 @@ void print_error(const color_table *colors, const char *fpath, int error) { if (color) { print_esc(color, stderr); } - fprintf(stderr, "Error at %s: %s\n", fpath, strerror(error)); + fprintf(stderr, "Error at %s: %s\n", path, strerror(error)); if (color) { print_esc(colors->reset, stderr); } diff --git a/color.h b/color.h index 25523de..e8d984c 100644 --- a/color.h +++ b/color.h @@ -33,24 +33,22 @@ color_table *parse_colors(char *ls_colors); * * @param colors * The color table to use. - * @param fpath - * The file path to print. * @param ftwbuf - * The bftw() data for fpath. + * The bftw() data for the current path. */ -void pretty_print(const color_table *colors, const char *fpath, const struct BFTW *ftwbuf); +void pretty_print(const color_table *colors, const struct BFTW *ftwbuf); /** * Pretty-print an error. * * @param colors * The color table to use. - * @param fpath + * @param path * The file path in error. * @param error * The error code that occurred. */ -void print_error(const color_table *colors, const char *fpath, int error); +void print_error(const color_table *colors, const char *path, int error); /** * Free a color table. -- cgit v1.2.3