about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--man/mpick.166
-rw-r--r--mpick.c145
-rwxr-xr-xt/2000-mpick.t34
3 files changed, 44 insertions, 201 deletions
diff --git a/man/mpick.1 b/man/mpick.1
index bb7d324..007fca6 100644
--- a/man/mpick.1
+++ b/man/mpick.1
@@ -1,4 +1,4 @@
-.Dd July 27, 2016
+.Dd July 30, 2020
 .Dt MPICK 1
 .Os
 .Sh NAME
@@ -6,10 +6,11 @@
 .Nd advanced message filter
 .Sh SYNOPSIS
 .Nm
+.Op Fl F Ar file
 .Op Fl T
 .Op Fl v
 .Op Fl t Ar test
-.Op Ar msglist\ ...
+.Op Ar msgs\ ...
 .Sh DESCRIPTION
 .Nm
 prints all matching messages.
@@ -23,6 +24,12 @@ will read filenames from the standard input.
 .Pp
 The options are as follows:
 .Bl -tag -width Ds
+.It Fl F Ar file
+Read expression from
+.Ar file
+and only show matching messages, see
+.Sx TESTS .
+.Ar file ,
 .It Fl T
 Include whole thread.
 .It Fl t Ar test
@@ -33,61 +40,6 @@ see
 .It Fl v
 Print how many messages were tested and picked to standard error.
 .El
-.Sh MSGLISTS
-.Nm
-message lists
-.Pq msglist
-are mostly compatible with
-.Xr mailx 1 .
-They are message specifications used as shortened
-.Sx TESTS ,
-and can include:
-.Bl -tag -width Ds
-.It Ar n
-Message number
-.Ar n .
-.It Ar n Ns Cm \&: Ns Ar m , Ar n Ns Cm \&- Ns Ar m
-An inclusive range of message numbers between
-.Ar n
-and
-.Ar m .
-.It Ar address
-All messages from
-.Ar address .
-.It Cm \&/ Ns Ar string
-All messages with
-.Ar string
-in the subject line
-.Pq case ignored .
-.It Cm \&: Ns Ar c
-All messages of type
-.Ar c ,
-where
-.Ar c
-shall be one of:
-.Bl -tag -width Ds
-.It Cm D
-Draft messages.
-.It Cm P
-Passed
-.Pq resent, forwarded or bounced
-messages.
-.It Cm R
-Replied messages.
-.It Cm F
-Flagged messages.
-.It Cm d , Cm T
-Deleted messages.
-.It Cm n
-New messages.
-.It Cm o
-Old messages.
-.It Cm r , Cm S
-Read messages.
-.It Cm u
-Unread messages.
-.El
-.El
 .Sh TESTS
 .Nm
 tests are given by the following EBNF:
diff --git a/mpick.c b/mpick.c
index e62604b..bff0cc3 100644
--- a/mpick.c
+++ b/mpick.c
@@ -1007,108 +1007,6 @@ parse_buf(const char *f, char *s)
 	return e;
 }
 
-static struct expr *
-parse_msglist(char *s)
-{
-	int64_t n, m;
-	int r;
-	struct expr *e1, *e2;
-	char *d;
-	enum flags flag;
-
-	switch (*s) {
-	case '/':
-		s++;
-		e1 = mkexpr(EXPR_REGEXI);
-		e1->a.prop = PROP_HDR;
-		e1->b.string = xstrdup("subject");
-		e1->c.regex = malloc(sizeof (regex_t));
-		r = regcomp(e1->c.regex, s, REG_EXTENDED | REG_NOSUB | REG_ICASE);
-		if (r != 0) {
-			char msg[256];
-			regerror(r, e1->c.regex, msg, sizeof msg);
-			parse_error("invalid regex '%s': %s", s, msg);
-		}
-		return e1;
-	case ':':
-		n = 0;
-
-		switch (*++s) {
-		case '\0': parse_error("missing flag at '%s'", s-1);
-		case 'P': flag = FLAG_PASSED; break;
-		case 'F': flag = FLAG_FLAGGED; break;
-		case 'D': flag = FLAG_DRAFT; break;
-		case 'd': /* FALL THROUGH */
-		case 'T': flag = FLAG_TRASHED; break;
-		case 'u': n = 1; /* FALL THROUGH */
-		case 'r': /* FALL THROUGH */
-		case 'S': flag = FLAG_SEEN; break;
-		case 'o': n = 1; /* FALL THROUGH */
-		case 'n': flag = FLAG_NEW; break;
-		case 'R': flag = FLAG_REPLIED; break;
-		default: parse_error("unknown flag at '%s'", s);
-		}
-
-		e1 = mkexpr(EXPR_ANYSET);
-		e1->a.prop = PROP_FLAG;
-		e1->b.num = flag;
-
-		if (!n)
-			return e1;
-
-		e2 = mkexpr(EXPR_NOT);
-		e2->a.expr = e1;
-		return e2;
-	default:
-		pos = s;
-
-		if (((d = strchr(s, ':')) || (d = strchr(s, '-')))
-		    && parse_num(&n) && (pos = d + 1) && parse_num(&m)) {
-			/* index >= n */
-			e1 = mkexpr(EXPR_GE);
-			e1->a.prop = PROP_INDEX;
-			e1->b.num = n;
-
-			/* index <= m */
-			e2 = mkexpr(EXPR_LE);
-			e2->a.prop = PROP_INDEX;
-			e2->b.num = m;
-
-			/* e1 && e2 */
-			return chain(e1, EXPR_AND, e2);
-		} else if (parse_num(&n)) {
-			e1 = mkexpr(EXPR_EQ);
-			e1->a.prop = PROP_INDEX;
-			e1->b.num = n;
-
-			return e1;
-		} else {
-			char *disp, *addr;
-
-			blaze822_addr(s, &disp, &addr);
-			if (!disp && !addr)
-				parse_error("invalid address '%s'", s);
-
-			d = (disp) ? disp : addr;
-
-			e1 = mkexpr(EXPR_REGEXI);
-			e1->a.prop = (disp) ? PROP_HDR_DISP : PROP_HDR_ADDR;
-			e1->b.string = xstrdup("from");
-			e1->c.regex = malloc(sizeof (regex_t));
-
-			r = regcomp(e1->c.regex, d, REG_EXTENDED | REG_NOSUB | REG_ICASE);
-			if (r != 0) {
-				char msg[256];
-				regerror(r, e1->c.regex, msg, sizeof msg);
-				parse_error("invalid regex '%s': %s", d, msg);
-			}
-
-			return e1;
-		}
-	}
-	return 0;
-}
-
 time_t
 msg_date(struct mailinfo *m)
 {
@@ -1541,42 +1439,35 @@ main(int argc, char *argv[])
 	num = 1;
 	vflag = 0;
 
-	while ((c = getopt(argc, argv, "Tt:v")) != -1)
+	while ((c = getopt(argc, argv, "F:Tt:v")) != -1)
 		switch (c) {
-		case 'T': Tflag = need_thr = 1; break;
-		case 't': expr = chain(expr, EXPR_AND, parse_buf("argv", optarg)); break;
-		case 'v': vflag = 1; break;
-		default:
-			fprintf(stderr, "Usage: %s [-Tv] [-t test] [msglist ...]\n", argv0);
-			exit(1);
-		}
-
-	if (optind != argc) {
-		for (c = optind; c < argc; c++) {
-			if (strchr(argv[c], '/') && access(argv[c], R_OK) == 0) {
-				break;
-			}
-			expr = chain(expr, EXPR_AND, parse_msglist(argv[c]));
-		}
-
-		for (; c < argc; c++) {
+		case 'F':
+		{
 			char *s;
 			off_t len;
-			int r = slurp(argv[c], &s, &len);
+			int r = slurp(optarg, &s, &len);
 			if (r != 0) {
 				fprintf(stderr, "%s: error opening file '%s': %s\n",
-					argv0, argv[c], strerror(r));
+				    argv0, optarg, strerror(r));
 				exit(1);
 			}
-			expr = chain(expr, EXPR_AND, parse_buf(argv[c], s));
+			expr = chain(expr, EXPR_AND, parse_buf(optarg, s));
 			free(s);
+			break;
+		}
+		case 'T': Tflag = need_thr = 1; break;
+		case 't': expr = chain(expr, EXPR_AND, parse_buf("argv", optarg)); break;
+		case 'v': vflag = 1; break;
+		default:
+			fprintf(stderr, "Usage: %s [-Tv] [-t test] [-F file] [msgs...]\n", argv0);
+			exit(1);
 		}
-	}
 
-	if (isatty(0))
-		i = blaze822_loop1(":", need_thr ? collect : oneline);
+	void *cb = need_thr ? collect : oneline;
+	if (argc == optind && isatty(0))
+		i = blaze822_loop1(":", cb);
 	else
-		i = blaze822_loop(0, 0, need_thr ? collect : oneline);
+		i = blaze822_loop(argc-optind, argv+optind, cb);
 
 	/* print and free last thread */
 	if (Tflag && thr)
diff --git a/t/2000-mpick.t b/t/2000-mpick.t
index 2f530e8..27f784f 100755
--- a/t/2000-mpick.t
+++ b/t/2000-mpick.t
@@ -19,14 +19,14 @@ touch "inbox/cur/7:2,SR"
 touch "inbox/cur/8:2,SF"
 touch "inbox/cur/9:2,"
 
-check_same 'flag trashed' 'mlist inbox | mpick :T' 'mlist -T inbox'
-check_same 'flag not trashed' 'mlist inbox | mpick -t "!trashed"' 'mlist -t inbox'
-check_same 'flag seen' 'mlist inbox | mpick :S' 'mlist -S inbox'
+check_same 'flag trashed' 'mlist inbox | mpick -t trashed' 'mlist -T inbox'
+check_same 'flag not trashed' 'mlist inbox | mpick -t !trashed' 'mlist -t inbox'
+check_same 'flag seen' 'mlist inbox | mpick -t seen' 'mlist -S inbox'
 check_same 'flag not seen' 'mlist inbox | mpick -t !seen' 'mlist -s inbox'
-check_same 'flag seen and trashed' 'mlist inbox | mpick :S :T' 'mlist -ST inbox'
-check_same 'flag seen and not trashed' 'mlist inbox | mpick -t "seen && !trashed"' 'mlist -St inbox'
-check_same 'flag replied' 'mlist inbox | mpick :R' 'mlist -R inbox'
-check_same 'flag forwarded' 'mlist inbox | mpick :F' 'mlist -F inbox'
+check_same 'flag seen and trashed' 'mlist inbox | mpick -t seen -t trashed' 'mlist -ST inbox'
+check_same 'flag seen and not trashed' 'mlist inbox | mpick -t seen -t !trashed' 'mlist -St inbox'
+check_same 'flag replied' 'mlist inbox | mpick -t replied' 'mlist -R inbox'
+check_same 'flag forwarded' 'mlist inbox | mpick -t passed' 'mlist -P inbox'
 
 
 cat <<! | mmime >"inbox/cur/1:2,S"
@@ -62,14 +62,14 @@ Greetings
 !
 
 cat <<! >shebang
-#!$(command -v mpick)
+#!$(command -v mpick) -F
 from.addr == "peter@example.org" && from.disp == "Peter Example"
 !
 chmod +x shebang
 
-check 'search subject' 'mlist inbox | mpick /wow | grep -q inbox/cur/9:2,'
-check_test 'search addr' -eq 2 'mlist inbox | mpick peter@example.org | wc -l'
-check_test 'search name' -eq 2 'mlist inbox | mpick "Peter Example" | wc -l'
+check 'search subject' 'mlist inbox | mpick -t "subject =~~ \"wow\"" | grep -q inbox/cur/9:2,'
+check_test 'search addr' -eq 2 'mlist inbox | mpick -t "from.addr == \"peter@example.org\"" | wc -l'
+check_test 'search name' -eq 2 'mlist inbox | mpick -t "from.disp == \"Peter Example\"" | wc -l'
 check_test 'search spam' -eq 1 'mlist inbox | mpick -t "trashed && subject =~ \"pdf\"" | wc -l'
 check_test 'any header' -eq 1 'mlist inbox | mpick -t "\"Foo\" =~~ \"bar\"" | wc -l'
 check_test 'addr decode addr' -eq 2 'mlist inbox | mpick -t "from.addr == \"peter@example.org\"" | wc -l'
@@ -92,7 +92,7 @@ let bar = from.disp == "Peter Example"
 in
   foo && bar # another comment
 !
-check_test 'let expression' -eq 2 'mlist inbox | mpick ./expr | wc -l'
+check_test 'let expression' -eq 2 'mlist inbox | mpick -F ./expr | wc -l'
 
 cat <<! >expr
 let foo = from.addr == "peter@example.org"
@@ -101,7 +101,7 @@ let bar = from.disp == "Peter Example"
 in
   foo && foo
 !
-check_test 'let expression double free' -eq 2 'mlist inbox | mpick ./expr | wc -l'
+check_test 'let expression double free' -eq 2 'mlist inbox | mpick -F ./expr | wc -l'
 
 cat <<! >expr
 let foo =
@@ -111,7 +111,7 @@ let foo =
 in
   foo
 !
-check_test 'let expression nested' -eq 2 'mlist inbox | mpick ./expr | wc -l'
+check_test 'let expression nested' -eq 2 'mlist inbox | mpick -F ./expr | wc -l'
 
 cat <<! >expr
 let foo = from.addr == "peter@example.org"
@@ -119,7 +119,7 @@ let bar = foo && subject =~ "wow"
 in
   bar
 !
-check_test 'let scoping' -eq 1 'mlist inbox | mpick ./expr | wc -l'
+check_test 'let scoping' -eq 1 'mlist inbox | mpick -F ./expr | wc -l'
 
 cat <<! >expr
 let foo = from.addr == "peter@example.org"
@@ -127,8 +127,8 @@ let bar = from.disp == "Peter Example"
 in
   foo |"sed ""s/^/1:&/""" && bar |"sed ""s/^/2:&/""" && skip
 !
-check_test 'multi redir' -eq 4 'mlist inbox | mpick ./expr | wc -l'
-check_test 'multi redir prefixes' -eq 2 'mlist inbox | mpick ./expr | cut -d: -f1 | sort -u | wc -l'
+check_test 'multi redir' -eq 4 'mlist inbox | mpick -F ./expr | wc -l'
+check_test 'multi redir prefixes' -eq 2 'mlist inbox | mpick -F ./expr | cut -d: -f1 | sort -u | wc -l'
 
 )