From 8e3a3cb3d9231f5bcd282cf8429cff00beef65bb Mon Sep 17 00:00:00 2001 From: Leah Neukirchen Date: Wed, 20 Sep 2017 21:38:42 +0200 Subject: add -e REGEX as a shortcut to filter basenames --- NEWS.md | 1 + README.md | 3 ++- lr.1 | 6 ++++++ lr.c | 71 +++++++++++++++++++++++++++++++++++---------------------------- 4 files changed, 48 insertions(+), 33 deletions(-) diff --git a/NEWS.md b/NEWS.md index 623c115..4c8c938 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,6 +3,7 @@ * Feature: lr is substantially faster as files only are stat(2)ed if the output requires it. * Feature: new option `-X` to print OSC 8 hyperlinks. +* Feature: new option `-e` for the common case of filtering file names. * Bug: lr doesn't fail on symlinks refering to themselves anymore. ## 1.0 (2017-08-29) diff --git a/README.md b/README.md index 4833d42..106ac68 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Over ls: ## Usage: - lr [-0|-F|-l [-TA|-TC|-TM]|-S|-f FMT] [-D] [-H|-L] [-1AGQXdhsx] [-U|-o ORD] [-t TEST]* PATH... + lr [-0|-F|-l [-TA|-TC|-TM]|-S|-f FMT] [-D] [-H|-L] [-1AGQXdhsx] [-U|-o ORD] [-e REGEX]* [-t TEST]* PATH... The special path argument `-` makes `lr` read file names from standard input, instead of traversing path. @@ -75,6 +75,7 @@ input, instead of traversing path. * `-x`: don't enter other filesystems. * `-U`: don't sort results. * `-o ORD`: sort according to the string `ORD`, see below. +* `-e REGEX`: only show files where basename matches `REGEX`. * `-t TEST`: only show files matching all `TEST`s, see below. ## Output formatting: diff --git a/lr.1 b/lr.1 index 75239bb..2dd0dc6 100644 --- a/lr.1 +++ b/lr.1 @@ -13,6 +13,7 @@ .Op Fl 1AGQXdhsx .Op Fl U | Fl o Ar ord .br +.Op Fl e Ar regex .Op Fl t Ar test .Op Fl C Oo Ar color Ns Li \&: Oc Ns Ar path .Ar path\ ... @@ -122,6 +123,9 @@ Sort according to .Ar ord , see .Sx SORT ORDER . +.It Fl e Ar regex +Only show files where basename matches the POSIX ERE +.Ar regex . .It Fl t Ar test Only show files matching the expression .Ar test , @@ -129,6 +133,8 @@ see .Sx TESTS . Multiple occurrences of .Fl t +and +.Fl e are regarded as a conjunction. .It Fl C Oo Ar color Ns Li \&: Oc Ns Ar path Behaves as if diff --git a/lr.c b/lr.c index aa6b0f1..ad34930 100644 --- a/lr.c +++ b/lr.c @@ -611,6 +611,38 @@ parse_dur(int64_t *n) return 0; } +static struct expr * +mkstrexpr(enum prop prop, enum op op, char *s, int negate) +{ + int r = 0; + struct expr *e = mkexpr(op); + e->a.prop = prop; + if (op == EXPR_REGEX) { + e->b.regex = malloc(sizeof (regex_t)); + r = regcomp(e->b.regex, s, REG_EXTENDED | REG_NOSUB); + } else if (op == EXPR_REGEXI) { + e->b.regex = malloc(sizeof (regex_t)); + r = regcomp(e->b.regex, s, REG_EXTENDED | REG_NOSUB | REG_ICASE); + } else { + e->b.string = s; + } + + if (r != 0) { + char msg[256]; + regerror(r, e->b.regex, msg, sizeof msg); + parse_error("invalid regex '%s': %s", s, msg); + exit(2); + } + + if (negate) { + struct expr *not = mkexpr(EXPR_NOT); + not->a.expr = e; + return not; + } + + return e; +} + static struct expr * parse_strcmp() { @@ -665,35 +697,8 @@ parse_strcmp() parse_error("invalid string operator at '%.15s'", pos); char *s; - if (parse_string(&s)) { - int r = 0; - struct expr *e = mkexpr(op); - e->a.prop = prop; - if (op == EXPR_REGEX) { - e->b.regex = malloc(sizeof (regex_t)); - r = regcomp(e->b.regex, s, REG_EXTENDED | REG_NOSUB); - } else if (op == EXPR_REGEXI) { - e->b.regex = malloc(sizeof (regex_t)); - r = regcomp(e->b.regex, s, REG_EXTENDED | REG_NOSUB | REG_ICASE); - } else { - e->b.string = s; - } - - if (r != 0) { - char msg[256]; - regerror(r, e->b.regex, msg, sizeof msg); - parse_error("invalid regex '%s': %s", s, msg); - exit(2); - } - - if (negate) { - struct expr *not = mkexpr(EXPR_NOT); - not->a.expr = e; - return not; - } - - return e; - } + if (parse_string(&s)) + return mkstrexpr(prop, op, s, negate); parse_error("invalid string at '%.15s'", pos); return 0; @@ -2083,11 +2088,11 @@ main(int argc, char *argv[]) setlocale(LC_ALL, ""); - while ((c = getopt(argc, argv, "01AC:DFGHLQST:UXdf:lho:st:x")) != -1) + while ((c = getopt(argc, argv, "01AC:DFGHLQST:UXde:f:lho:st:x")) != -1) switch (c) { case '0': format = zero_format; input_delim = 0; Qflag = 0; break; case '1': expr = chain(parse_expr("depth == 0 || prune"), EXPR_AND, expr); break; - case 'A': expr = chain(parse_expr("!(path ~~ \"*/.*\" && prune) && !path == \".\""), EXPR_AND, expr); break; + case 'A': expr = chain(expr, EXPR_AND, parse_expr("!(path ~~ \"*/.*\" && prune) && path != \".\"")); break; case 'C': if ((unsigned int)Cflag < sizeof Cflags / sizeof Cflags[0]) { @@ -2109,6 +2114,8 @@ main(int argc, char *argv[]) case 'U': Uflag++; break; case 'X': Xflag++; break; case 'd': expr = chain(parse_expr("type == d && prune || print"), EXPR_AND, expr); break; + case 'e': expr = chain(expr, EXPR_AND, + mkstrexpr(PROP_NAME, EXPR_REGEX, optarg, 0)); break; case 'f': format = optarg; break; case 'h': hflag++; break; case 'l': lflag++; Qflag++; format = long_format; break; @@ -2121,7 +2128,7 @@ main(int argc, char *argv[]) default: fprintf(stderr, "Usage: %s [-0|-F|-l [-TA|-TC|-TM]|-S|-f FMT] [-D] [-H|-L] [-1AGQdhsx]\n" -" [-U|-o ORD] [-t TEST]* [-C [COLOR:]PATH]* PATH...\n", argv0); +" [-U|-o ORD] [-e REGEX]* [-t TEST]* [-C [COLOR:]PATH]* PATH...\n", argv0); exit(2); } -- cgit 1.4.1