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
.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:

@ -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)

@ -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'
)

Loading…
Cancel
Save