mpick: breaking cmdline options

Two breaking changes which will make mpick fit more in with
all other mblaze tools by using mmsg(7) arguments:
- Use the -F flag to read script files.
- Remove msglist support
pull/180/head
Duncan Overbruck 4 years ago committed by Leah Neukirchen
parent 96c506085f
commit d9c3914924

@ -1,4 +1,4 @@
.Dd July 27, 2016 .Dd July 30, 2020
.Dt MPICK 1 .Dt MPICK 1
.Os .Os
.Sh NAME .Sh NAME
@ -6,10 +6,11 @@
.Nd advanced message filter .Nd advanced message filter
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Fl F Ar file
.Op Fl T .Op Fl T
.Op Fl v .Op Fl v
.Op Fl t Ar test .Op Fl t Ar test
.Op Ar msglist\ ... .Op Ar msgs\ ...
.Sh DESCRIPTION .Sh DESCRIPTION
.Nm .Nm
prints all matching messages. prints all matching messages.
@ -23,6 +24,12 @@ will read filenames from the standard input.
.Pp .Pp
The options are as follows: The options are as follows:
.Bl -tag -width Ds .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 .It Fl T
Include whole thread. Include whole thread.
.It Fl t Ar test .It Fl t Ar test
@ -33,61 +40,6 @@ see
.It Fl v .It Fl v
Print how many messages were tested and picked to standard error. Print how many messages were tested and picked to standard error.
.El .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 .Sh TESTS
.Nm .Nm
tests are given by the following EBNF: tests are given by the following EBNF:

@ -1007,108 +1007,6 @@ parse_buf(const char *f, char *s)
return e; 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 time_t
msg_date(struct mailinfo *m) msg_date(struct mailinfo *m)
{ {
@ -1541,42 +1439,35 @@ main(int argc, char *argv[])
num = 1; num = 1;
vflag = 0; vflag = 0;
while ((c = getopt(argc, argv, "Tt:v")) != -1) while ((c = getopt(argc, argv, "F:Tt:v")) != -1)
switch (c) { switch (c) {
case 'T': Tflag = need_thr = 1; break; case 'F':
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++) {
char *s; char *s;
off_t len; off_t len;
int r = slurp(argv[c], &s, &len); int r = slurp(optarg, &s, &len);
if (r != 0) { if (r != 0) {
fprintf(stderr, "%s: error opening file '%s': %s\n", fprintf(stderr, "%s: error opening file '%s': %s\n",
argv0, argv[c], strerror(r)); argv0, optarg, strerror(r));
exit(1); exit(1);
} }
expr = chain(expr, EXPR_AND, parse_buf(argv[c], s)); expr = chain(expr, EXPR_AND, parse_buf(optarg, s));
free(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)) void *cb = need_thr ? collect : oneline;
i = blaze822_loop1(":", need_thr ? collect : oneline); if (argc == optind && isatty(0))
i = blaze822_loop1(":", cb);
else else
i = blaze822_loop(0, 0, need_thr ? collect : oneline); i = blaze822_loop(argc-optind, argv+optind, cb);
/* print and free last thread */ /* print and free last thread */
if (Tflag && thr) if (Tflag && thr)

@ -19,14 +19,14 @@ touch "inbox/cur/7:2,SR"
touch "inbox/cur/8:2,SF" touch "inbox/cur/8:2,SF"
touch "inbox/cur/9:2," touch "inbox/cur/9:2,"
check_same 'flag trashed' 'mlist inbox | mpick :T' 'mlist -T 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 not trashed' 'mlist inbox | mpick -t !trashed' 'mlist -t inbox'
check_same 'flag seen' 'mlist inbox | mpick :S' 'mlist -S 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 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 trashed' 'mlist inbox | mpick -t seen -t trashed' 'mlist -ST inbox'
check_same 'flag seen and not trashed' 'mlist inbox | mpick -t "seen && !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 :R' 'mlist -R inbox' check_same 'flag replied' 'mlist inbox | mpick -t replied' 'mlist -R inbox'
check_same 'flag forwarded' 'mlist inbox | mpick :F' 'mlist -F inbox' check_same 'flag forwarded' 'mlist inbox | mpick -t passed' 'mlist -P inbox'
cat <<! | mmime >"inbox/cur/1:2,S" cat <<! | mmime >"inbox/cur/1:2,S"
@ -62,14 +62,14 @@ Greetings
! !
cat <<! >shebang cat <<! >shebang
#!$(command -v mpick) #!$(command -v mpick) -F
from.addr == "peter@example.org" && from.disp == "Peter Example" from.addr == "peter@example.org" && from.disp == "Peter Example"
! !
chmod +x shebang chmod +x shebang
check 'search subject' 'mlist inbox | mpick /wow | grep -q inbox/cur/9:2,' check 'search subject' 'mlist inbox | mpick -t "subject =~~ \"wow\"" | grep -q inbox/cur/9:2,'
check_test 'search addr' -eq 2 'mlist inbox | mpick peter@example.org | wc -l' 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 "Peter Example" | 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 '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 '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' 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 in
foo && bar # another comment 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 cat <<! >expr
let foo = from.addr == "peter@example.org" let foo = from.addr == "peter@example.org"
@ -101,7 +101,7 @@ let bar = from.disp == "Peter Example"
in in
foo && foo 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 cat <<! >expr
let foo = let foo =
@ -111,7 +111,7 @@ let foo =
in in
foo 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 cat <<! >expr
let foo = from.addr == "peter@example.org" let foo = from.addr == "peter@example.org"
@ -119,7 +119,7 @@ let bar = foo && subject =~ "wow"
in in
bar 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 cat <<! >expr
let foo = from.addr == "peter@example.org" let foo = from.addr == "peter@example.org"
@ -127,8 +127,8 @@ let bar = from.disp == "Peter Example"
in in
foo |"sed ""s/^/1:&/""" && bar |"sed ""s/^/2:&/""" && skip 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' -eq 4 'mlist inbox | mpick -F ./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 prefixes' -eq 2 'mlist inbox | mpick -F ./expr | cut -d: -f1 | sort -u | wc -l'
) )

Loading…
Cancel
Save