From a4dcfe8b6d1eaabe172322a81721f355647257ff Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 8 Feb 2017 19:15:11 -0500 Subject: Add support for -x?type with multiple types This functionality is already part of GNU findutils git. --- bftw.c | 56 +++++++++++++++----------------- bftw.h | 29 ++++++++++------- eval.c | 30 ++--------------- parse.c | 93 +++++++++++++++++++++++++++++++++-------------------- printf.c | 80 +++++++++------------------------------------ tests.sh | 12 ++++++- tests/test_0142.out | 6 ++++ tests/test_0143.out | 9 ++++++ 8 files changed, 146 insertions(+), 169 deletions(-) create mode 100644 tests/test_0142.out create mode 100644 tests/test_0143.out diff --git a/bftw.c b/bftw.c index d62a71f..d239d40 100644 --- a/bftw.c +++ b/bftw.c @@ -523,67 +523,63 @@ static void ftwbuf_use_dirent(struct BFTW *ftwbuf, const struct dirent *de) { #endif } -/** Call stat() and use the results. */ -static int ftwbuf_stat(struct BFTW *ftwbuf, struct stat *sb) { - int ret = fstatat(ftwbuf->at_fd, ftwbuf->at_path, sb, ftwbuf->at_flags); - if (ret != 0) { - return ret; - } - - ftwbuf->statbuf = sb; - - switch (sb->st_mode & S_IFMT) { +enum bftw_typeflag bftw_mode_to_typeflag(mode_t mode) { + switch (mode & S_IFMT) { #ifdef S_IFBLK case S_IFBLK: - ftwbuf->typeflag = BFTW_BLK; - break; + return BFTW_BLK; #endif #ifdef S_IFCHR case S_IFCHR: - ftwbuf->typeflag = BFTW_CHR; - break; + return BFTW_CHR; #endif #ifdef S_IFDIR case S_IFDIR: - ftwbuf->typeflag = BFTW_DIR; - break; + return BFTW_DIR; #endif #ifdef S_IFDOOR case S_IFDOOR: - ftwbuf->typeflag = BFTW_DOOR; - break; + return BFTW_DOOR; #endif #ifdef S_IFIFO case S_IFIFO: - ftwbuf->typeflag = BFTW_FIFO; - break; + return BFTW_FIFO; #endif #ifdef S_IFLNK case S_IFLNK: - ftwbuf->typeflag = BFTW_LNK; - break; + return BFTW_LNK; #endif #ifdef S_IFPORT case S_IFPORT: - ftwbuf->typeflag = BFTW_PORT; - break; + return BFTW_PORT; #endif #ifdef S_IFREG case S_IFREG: - ftwbuf->typeflag = BFTW_REG; - break; + return BFTW_REG; #endif #ifdef S_IFSOCK case S_IFSOCK: - ftwbuf->typeflag = BFTW_SOCK; - break; + return BFTW_SOCK; #endif #ifdef S_IFWHT case S_IFWHT: - ftwbuf->typeflag = BFTW_WHT; - break; + return BFTW_WHT; #endif + + default: + return BFTW_UNKNOWN; } +} + +/** Call stat() and use the results. */ +static int ftwbuf_stat(struct BFTW *ftwbuf, struct stat *sb) { + int ret = fstatat(ftwbuf->at_fd, ftwbuf->at_path, sb, ftwbuf->at_flags); + if (ret != 0) { + return ret; + } + + ftwbuf->statbuf = sb; + ftwbuf->typeflag = bftw_mode_to_typeflag(sb->st_mode); return 0; } diff --git a/bftw.h b/bftw.h index 2fb1ef0..3bfe2a2 100644 --- a/bftw.h +++ b/bftw.h @@ -20,31 +20,36 @@ */ enum bftw_typeflag { /** Unknown type. */ - BFTW_UNKNOWN, + BFTW_UNKNOWN = 0, /** Block device. */ - BFTW_BLK, + BFTW_BLK = 1 << 0, /** Character device. */ - BFTW_CHR, + BFTW_CHR = 1 << 1, /** Directory. */ - BFTW_DIR, + BFTW_DIR = 1 << 2, /** Solaris door. */ - BFTW_DOOR, + BFTW_DOOR = 1 << 3, /** Pipe. */ - BFTW_FIFO, + BFTW_FIFO = 1 << 4, /** Symbolic link. */ - BFTW_LNK, + BFTW_LNK = 1 << 5, /** Solaris event port. */ - BFTW_PORT, + BFTW_PORT = 1 << 6, /** Regular file. */ - BFTW_REG, + BFTW_REG = 1 << 7, /** Socket. */ - BFTW_SOCK, + BFTW_SOCK = 1 << 8, /** BSD whiteout. */ - BFTW_WHT, + BFTW_WHT = 1 << 9, /** An error occurred for this file. */ - BFTW_ERROR, + BFTW_ERROR = 1 << 10, }; +/** + * Convert a stat() st_mode to a bftw() typeflag. + */ +enum bftw_typeflag bftw_mode_to_typeflag(mode_t mode); + /** * Possible visit occurrences. */ diff --git a/eval.c b/eval.c index 6109d44..37bea60 100644 --- a/eval.c +++ b/eval.c @@ -866,7 +866,7 @@ bool eval_sparse(const struct expr *expr, struct eval_state *state) { * -type test. */ bool eval_type(const struct expr *expr, struct eval_state *state) { - return state->ftwbuf->typeflag == expr->idata; + return state->ftwbuf->typeflag & expr->idata; } /** @@ -900,33 +900,7 @@ bool eval_xtype(const struct expr *expr, struct eval_state *state) { } } - switch ((enum bftw_typeflag)expr->idata) { - case BFTW_UNKNOWN: - case BFTW_ERROR: - break; - case BFTW_BLK: - return S_ISBLK(sb.st_mode); - case BFTW_CHR: - return S_ISCHR(sb.st_mode); - case BFTW_DIR: - return S_ISDIR(sb.st_mode); - case BFTW_DOOR: - return S_ISDOOR(sb.st_mode); - case BFTW_FIFO: - return S_ISFIFO(sb.st_mode); - case BFTW_LNK: - return S_ISLNK(sb.st_mode); - case BFTW_PORT: - return S_ISPORT(sb.st_mode); - case BFTW_REG: - return S_ISREG(sb.st_mode); - case BFTW_SOCK: - return S_ISSOCK(sb.st_mode); - case BFTW_WHT: - return S_ISWHT(sb.st_mode); - } - - return false; + return bftw_mode_to_typeflag(sb.st_mode) & expr->idata; } #if _POSIX_MONOTONIC_CLOCK > 0 diff --git a/parse.c b/parse.c index eb431aa..2d24dda 100644 --- a/parse.c +++ b/parse.c @@ -1754,7 +1754,7 @@ static struct expr *parse_sparse(struct parser_state *state, int arg1, int arg2) } /** - * Parse -x?type [bcdpfls]. + * Parse -x?type [bcdpflsD]. */ static struct expr *parse_type(struct parser_state *state, int x, int arg2) { eval_fn *eval = x ? eval_xtype : eval_type; @@ -1763,44 +1763,69 @@ static struct expr *parse_type(struct parser_state *state, int x, int arg2) { return NULL; } - int typeflag = BFTW_UNKNOWN; + enum bftw_typeflag types = 0; - switch (expr->sdata[0]) { - case 'b': - typeflag = BFTW_BLK; - break; - case 'c': - typeflag = BFTW_CHR; - break; - case 'd': - typeflag = BFTW_DIR; - break; - case 'D': - typeflag = BFTW_DOOR; - break; - case 'p': - typeflag = BFTW_FIFO; - break; - case 'f': - typeflag = BFTW_REG; - break; - case 'l': - typeflag = BFTW_LNK; - break; - case 's': - typeflag = BFTW_SOCK; - break; - } + const char *c = expr->sdata; + while (true) { + switch (*c) { + case 'b': + types |= BFTW_BLK; + break; + case 'c': + types |= BFTW_CHR; + break; + case 'd': + types |= BFTW_DIR; + break; + case 'D': + types |= BFTW_DOOR; + break; + case 'p': + types |= BFTW_FIFO; + break; + case 'f': + types |= BFTW_REG; + break; + case 'l': + types |= BFTW_LNK; + break; + case 's': + types |= BFTW_SOCK; + break; - if (typeflag == BFTW_UNKNOWN || expr->sdata[1] != '\0') { - pretty_error(state->cmdline->stderr_colors, - "error: Unknown type flag '%s'.\n", expr->sdata); - free_expr(expr); - return NULL; + case '\0': + pretty_error(state->cmdline->stderr_colors, + "error: %s %s: Expected a type flag.\n", + expr->argv[0], expr->argv[1]); + goto fail; + + default: + pretty_error(state->cmdline->stderr_colors, + "error: %s %s: Unknown type flag '%c' (expected one of [bcdpflsD]).\n", + expr->argv[0], expr->argv[1], *c); + goto fail; + } + + ++c; + if (*c == '\0') { + break; + } else if (*c == ',') { + ++c; + continue; + } else { + pretty_error(state->cmdline->stderr_colors, + "error: %s %s: Types must be comma-separated.\n", + expr->argv[0], expr->argv[1]); + goto fail; + } } - expr->idata = typeflag; + expr->idata = types; return expr; + +fail: + free_expr(expr); + return NULL; } /** diff --git a/printf.c b/printf.c index 0977d5e..cad4dc0 100644 --- a/printf.c +++ b/printf.c @@ -318,39 +318,32 @@ static int bfs_printf_u(FILE *file, const struct bfs_printf_directive *directive return fprintf(file, directive->str, pwd->pw_name); } -/** %y: type */ -static int bfs_printf_y(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { - const char *type; - switch (ftwbuf->typeflag) { +static const char *bfs_printf_type(enum bftw_typeflag typeflag) { + switch (typeflag) { case BFTW_BLK: - type = "b"; - break; + return "b"; case BFTW_CHR: - type = "c"; - break; + return "c"; case BFTW_DIR: - type = "d"; - break; + return "d"; case BFTW_DOOR: - type = "D"; - break; + return "D"; case BFTW_FIFO: - type = "p"; - break; + return "p"; case BFTW_LNK: - type = "l"; - break; + return "l"; case BFTW_REG: - type = "f"; - break; + return "f"; case BFTW_SOCK: - type = "s"; - break; + return "s"; default: - type = "U"; - break; + return "U"; } +} +/** %y: type */ +static int bfs_printf_y(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { + const char *type = bfs_printf_type(ftwbuf->typeflag); return fprintf(file, directive->str, type); } @@ -364,48 +357,7 @@ static int bfs_printf_Y(FILE *file, const struct bfs_printf_directive *directive struct stat sb; if (fstatat(ftwbuf->at_fd, ftwbuf->at_path, &sb, 0) == 0) { - switch (sb.st_mode & S_IFMT) { -#ifdef S_IFBLK - case S_IFBLK: - type = "b"; - break; -#endif -#ifdef S_IFCHR - case S_IFCHR: - type = "c"; - break; -#endif -#ifdef S_IFDIR - case S_IFDIR: - type = "d"; - break; -#endif -#ifdef S_IFDOOR - case S_IFDOOR: - type = "D"; - break; -#endif -#ifdef S_IFIFO - case S_IFIFO: - type = "p"; - break; -#endif -#ifdef S_IFLNK - case S_IFLNK: - type = "l"; - break; -#endif -#ifdef S_IFREG - case S_IFREG: - type = "f"; - break; -#endif -#ifdef S_IFSOCK - case S_IFSOCK: - type = "s"; - break; -#endif - } + type = bfs_printf_type(bftw_mode_to_typeflag(sb.st_mode)); } else { switch (errno) { case ELOOP: diff --git a/tests.sh b/tests.sh index a6d9784..ecaf837 100755 --- a/tests.sh +++ b/tests.sh @@ -858,9 +858,19 @@ function test_0141() { bfs_diff basic -maxdepth 0 -printf '%p' } +function test_0142() { + [ "$ALL" ] || return 0 + bfs_diff links -type f,d,c +} + +function test_0143() { + [ "$ALL" ] || return 0 + bfs_diff links -xtype f,d,c +} + result=0 -for i in {1..141}; do +for i in {1..143}; do test="test_$(printf '%04d' $i)" if [ -t 1 ]; then diff --git a/tests/test_0142.out b/tests/test_0142.out new file mode 100644 index 0000000..1f94970 --- /dev/null +++ b/tests/test_0142.out @@ -0,0 +1,6 @@ +links +links/a +links/c +links/d +links/d/e +links/d/e/f diff --git a/tests/test_0143.out b/tests/test_0143.out new file mode 100644 index 0000000..8849bb9 --- /dev/null +++ b/tests/test_0143.out @@ -0,0 +1,9 @@ +links +links/a +links/b +links/c +links/d +links/h +links/d/e +links/d/e/f +links/d/e/g -- cgit v1.2.3