summary refs log tree commit diff
diff options
context:
space:
mode:
authorLeah Neukirchen <leah@vuxu.org>2017-09-20 21:38:42 +0200
committerLeah Neukirchen <leah@vuxu.org>2017-09-20 21:38:42 +0200
commit8e3a3cb3d9231f5bcd282cf8429cff00beef65bb (patch)
treef8e88fd9cd5c24fde8e3933b7e946cd0b419acae
parent9c797d4df1125f5ec341b4ee17ee10daae559fd2 (diff)
downloadlr-8e3a3cb3d9231f5bcd282cf8429cff00beef65bb.tar.gz
lr-8e3a3cb3d9231f5bcd282cf8429cff00beef65bb.tar.xz
lr-8e3a3cb3d9231f5bcd282cf8429cff00beef65bb.zip
add -e REGEX as a shortcut to filter basenames
-rw-r--r--NEWS.md1
-rw-r--r--README.md3
-rw-r--r--lr.16
-rw-r--r--lr.c71
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
@@ -612,6 +612,38 @@ parse_dur(int64_t *n)
 }
 
 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()
 {
 	enum prop prop;
@@ -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);
 		}