2016-09-05 12:03:46 +00:00
|
|
|
// FNM_CASEFOLD
|
2016-07-22 01:10:17 +00:00
|
|
|
#define _GNU_SOURCE
|
2016-09-05 12:03:46 +00:00
|
|
|
#include <fnmatch.h>
|
|
|
|
|
|
|
|
// strptime
|
2016-07-28 15:53:40 +00:00
|
|
|
#ifndef _XOPEN_SOURCE
|
|
|
|
#define _XOPEN_SOURCE 700
|
|
|
|
#endif
|
|
|
|
|
2017-02-21 18:11:46 +00:00
|
|
|
#ifdef __has_include
|
|
|
|
#if __has_include(<stdnoreturn.h>)
|
|
|
|
#include <stdnoreturn.h>
|
|
|
|
#else
|
|
|
|
#define noreturn /**/
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
#define noreturn /**/
|
|
|
|
#endif
|
|
|
|
|
2016-07-28 15:53:40 +00:00
|
|
|
/* For Solaris. */
|
|
|
|
#if !defined(FNM_CASEFOLD) && defined(FNM_IGNORECASE)
|
|
|
|
#define FNM_CASEFOLD FNM_IGNORECASE
|
|
|
|
#endif
|
2016-07-22 01:10:17 +00:00
|
|
|
|
|
|
|
#include <sys/ioctl.h>
|
2019-01-28 14:30:19 +00:00
|
|
|
#include <sys/mman.h>
|
2016-07-22 01:10:17 +00:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
2017-08-31 15:30:17 +00:00
|
|
|
#include <limits.h>
|
|
|
|
#include <locale.h>
|
|
|
|
#include <regex.h>
|
|
|
|
#include <stdarg.h>
|
2016-07-22 01:10:17 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2016-07-28 15:53:40 +00:00
|
|
|
#include <strings.h>
|
2016-07-22 01:10:17 +00:00
|
|
|
#include <time.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <wchar.h>
|
|
|
|
|
|
|
|
#include "blaze822.h"
|
2019-11-21 01:15:41 +00:00
|
|
|
#include "xpledge.h"
|
2016-07-22 01:10:17 +00:00
|
|
|
|
|
|
|
enum op {
|
|
|
|
EXPR_OR = 1,
|
|
|
|
EXPR_AND,
|
2019-01-28 10:12:18 +00:00
|
|
|
EXPR_COND,
|
2016-07-22 01:10:17 +00:00
|
|
|
EXPR_NOT,
|
|
|
|
EXPR_LT,
|
|
|
|
EXPR_LE,
|
|
|
|
EXPR_EQ,
|
|
|
|
EXPR_NEQ,
|
|
|
|
EXPR_GE,
|
|
|
|
EXPR_GT,
|
|
|
|
EXPR_STREQ,
|
|
|
|
EXPR_STREQI,
|
|
|
|
EXPR_GLOB,
|
|
|
|
EXPR_GLOBI,
|
|
|
|
EXPR_REGEX,
|
|
|
|
EXPR_REGEXI,
|
2016-07-23 15:40:29 +00:00
|
|
|
EXPR_PRUNE,
|
2016-07-22 01:10:17 +00:00
|
|
|
EXPR_PRINT,
|
2019-01-28 09:53:05 +00:00
|
|
|
EXPR_REDIR_FILE,
|
|
|
|
EXPR_REDIR_PIPE,
|
2016-07-22 01:10:17 +00:00
|
|
|
EXPR_TYPE,
|
|
|
|
EXPR_ALLSET,
|
|
|
|
EXPR_ANYSET,
|
2020-02-05 14:30:53 +00:00
|
|
|
EXPR_BINDING,
|
2016-07-22 01:10:17 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
enum prop {
|
|
|
|
PROP_ATIME = 1,
|
|
|
|
PROP_CTIME,
|
|
|
|
PROP_DEPTH,
|
2016-07-31 15:05:20 +00:00
|
|
|
PROP_KEPT,
|
2016-07-22 01:10:17 +00:00
|
|
|
PROP_MTIME,
|
|
|
|
PROP_PATH,
|
2016-07-24 15:59:11 +00:00
|
|
|
PROP_REPLIES,
|
2016-07-22 01:10:17 +00:00
|
|
|
PROP_SIZE,
|
|
|
|
PROP_TOTAL,
|
|
|
|
PROP_INDEX,
|
|
|
|
PROP_DATE,
|
|
|
|
PROP_FLAG,
|
2019-01-29 13:07:55 +00:00
|
|
|
PROP_HDR,
|
|
|
|
PROP_HDR_ADDR,
|
|
|
|
PROP_HDR_DISP,
|
2016-07-22 01:10:17 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
enum flags {
|
|
|
|
FLAG_PASSED = 1,
|
|
|
|
FLAG_REPLIED = 2,
|
|
|
|
FLAG_SEEN = 4,
|
|
|
|
FLAG_TRASHED = 8,
|
|
|
|
FLAG_DRAFT = 16,
|
|
|
|
FLAG_FLAGGED = 32,
|
|
|
|
/* custom */
|
|
|
|
FLAG_NEW = 64,
|
|
|
|
FLAG_CUR = 128,
|
2016-07-23 15:40:29 +00:00
|
|
|
|
|
|
|
FLAG_PARENT = 256,
|
|
|
|
FLAG_CHILD = 512,
|
2016-07-28 16:47:17 +00:00
|
|
|
|
|
|
|
FLAG_INFO = 1024,
|
2016-07-22 01:10:17 +00:00
|
|
|
};
|
|
|
|
|
2016-07-28 23:49:28 +00:00
|
|
|
enum var {
|
|
|
|
VAR_CUR = 1,
|
|
|
|
};
|
|
|
|
|
2016-07-22 01:10:17 +00:00
|
|
|
struct expr {
|
|
|
|
enum op op;
|
|
|
|
union {
|
|
|
|
enum prop prop;
|
|
|
|
struct expr *expr;
|
|
|
|
char *string;
|
|
|
|
int64_t num;
|
|
|
|
regex_t *regex;
|
2016-07-28 23:49:28 +00:00
|
|
|
enum var var;
|
2020-02-05 14:30:53 +00:00
|
|
|
struct binding *binding;
|
2019-01-28 10:12:18 +00:00
|
|
|
} a, b, c;
|
2016-07-28 23:49:28 +00:00
|
|
|
int extra;
|
2016-07-22 01:10:17 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct mailinfo {
|
2019-01-28 14:10:24 +00:00
|
|
|
char *file;
|
2016-07-22 01:10:17 +00:00
|
|
|
char *fpath;
|
|
|
|
struct stat *sb;
|
|
|
|
struct message *msg;
|
|
|
|
time_t date;
|
|
|
|
int depth;
|
|
|
|
int index;
|
2016-07-23 15:40:29 +00:00
|
|
|
int replies;
|
|
|
|
int matched;
|
|
|
|
int prune;
|
2017-03-30 16:32:52 +00:00
|
|
|
int flags;
|
2016-07-22 01:10:17 +00:00
|
|
|
off_t total;
|
|
|
|
};
|
|
|
|
|
2016-07-23 15:40:29 +00:00
|
|
|
struct mlist {
|
|
|
|
struct mailinfo *m;
|
2016-07-24 15:59:11 +00:00
|
|
|
struct mlist *parent;
|
2016-07-23 15:40:29 +00:00
|
|
|
struct mlist *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct thread {
|
|
|
|
int matched;
|
|
|
|
struct mlist childs[100];
|
|
|
|
struct mlist *cur;
|
|
|
|
};
|
|
|
|
|
2019-01-28 09:53:05 +00:00
|
|
|
struct file {
|
|
|
|
enum op op;
|
|
|
|
const char *name;
|
|
|
|
const char *mode;
|
|
|
|
FILE *fp;
|
|
|
|
struct file *next;
|
|
|
|
};
|
|
|
|
|
2019-01-28 14:00:56 +00:00
|
|
|
struct pos {
|
|
|
|
char *pos;
|
|
|
|
char *line;
|
|
|
|
size_t linenr;
|
|
|
|
};
|
|
|
|
|
2019-01-28 15:22:29 +00:00
|
|
|
struct binding {
|
|
|
|
char *name;
|
|
|
|
size_t nlen;
|
|
|
|
size_t refcount;
|
|
|
|
struct expr *expr;
|
|
|
|
struct binding *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct scope {
|
|
|
|
struct binding *bindings;
|
|
|
|
struct scope *prev;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct scope *scopeq = NULL;
|
|
|
|
|
2016-07-23 15:40:29 +00:00
|
|
|
static struct thread *thr;
|
|
|
|
|
2016-07-22 01:10:17 +00:00
|
|
|
static char *argv0;
|
2016-07-23 15:40:29 +00:00
|
|
|
static int Tflag;
|
2016-07-22 01:10:17 +00:00
|
|
|
|
2016-07-23 16:45:23 +00:00
|
|
|
static int need_thr;
|
|
|
|
|
2016-07-22 01:10:17 +00:00
|
|
|
static long kept;
|
2016-07-22 23:16:24 +00:00
|
|
|
static long num;
|
2016-07-22 01:10:17 +00:00
|
|
|
|
|
|
|
static struct expr *expr;
|
2016-07-28 23:49:28 +00:00
|
|
|
static long cur_idx;
|
2016-07-22 01:10:17 +00:00
|
|
|
static char *cur;
|
|
|
|
static time_t now;
|
2016-07-23 15:40:29 +00:00
|
|
|
static int prune;
|
2016-07-22 01:10:17 +00:00
|
|
|
|
2019-01-28 14:00:56 +00:00
|
|
|
static char *pos;
|
|
|
|
static const char *fname;
|
|
|
|
static char *line = NULL;
|
|
|
|
static int linenr = 0;
|
|
|
|
|
2019-01-28 09:53:05 +00:00
|
|
|
static struct file *files, *fileq = NULL;
|
|
|
|
|
2019-01-28 13:45:40 +00:00
|
|
|
static void *
|
|
|
|
xcalloc(size_t nmemb, size_t size)
|
|
|
|
{
|
|
|
|
void *r;
|
|
|
|
if ((r = calloc(nmemb, size)))
|
|
|
|
return r;
|
|
|
|
perror("calloc");
|
|
|
|
exit(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
xstrdup(const char *s)
|
|
|
|
{
|
|
|
|
char *r;
|
|
|
|
if ((r = strdup(s)))
|
|
|
|
return r;
|
|
|
|
perror("strdup");
|
|
|
|
exit(2);
|
|
|
|
}
|
|
|
|
|
2016-07-22 01:10:17 +00:00
|
|
|
static void
|
|
|
|
ws()
|
|
|
|
{
|
2019-01-28 14:00:56 +00:00
|
|
|
for (; *pos;) {
|
|
|
|
while (isspace((unsigned char)*pos)) {
|
|
|
|
if (*pos == '\n') {
|
|
|
|
line = pos+1;
|
|
|
|
linenr++;
|
|
|
|
}
|
|
|
|
pos++;
|
|
|
|
}
|
|
|
|
if (*pos != '#')
|
|
|
|
break;
|
|
|
|
|
|
|
|
pos += strcspn(pos, "\n\0");
|
|
|
|
if (*pos != '\n')
|
|
|
|
break;
|
|
|
|
}
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2016-07-28 16:09:10 +00:00
|
|
|
token(char *token)
|
2016-07-22 01:10:17 +00:00
|
|
|
{
|
|
|
|
if (strncmp(pos, token, strlen(token)) == 0) {
|
|
|
|
pos += strlen(token);
|
|
|
|
ws();
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-28 09:53:05 +00:00
|
|
|
static int
|
|
|
|
peek(char *token)
|
|
|
|
{
|
|
|
|
return strncmp(pos, token, strlen(token)) == 0;
|
|
|
|
}
|
|
|
|
|
2017-02-21 18:11:46 +00:00
|
|
|
noreturn static void
|
2016-07-28 16:09:10 +00:00
|
|
|
parse_error(char *msg, ...)
|
2016-07-22 01:10:17 +00:00
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, msg);
|
2019-01-28 14:00:56 +00:00
|
|
|
fprintf(stderr, "%s: parse error: %s:%d:%ld: ", argv0, fname, linenr, pos-line+1);
|
2016-07-22 01:10:17 +00:00
|
|
|
vfprintf(stderr, msg, ap);
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
exit(2);
|
|
|
|
}
|
|
|
|
|
2019-01-28 14:00:56 +00:00
|
|
|
noreturn static void
|
|
|
|
parse_error_at(struct pos *savepos, char *msg, ...)
|
|
|
|
{
|
|
|
|
char *e;
|
|
|
|
|
|
|
|
if (savepos) {
|
|
|
|
pos = savepos->pos;
|
|
|
|
line = savepos->line;
|
|
|
|
linenr = savepos->linenr;
|
|
|
|
}
|
|
|
|
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, msg);
|
|
|
|
fprintf(stderr, "%s: parse error: %s:%d:%ld: ", argv0, fname, linenr, pos-line+1);
|
|
|
|
vfprintf(stderr, msg, ap);
|
|
|
|
fprintf(stderr, " at '");
|
|
|
|
for (e = pos+15; *pos && *pos != '\n' && pos <= e; pos++)
|
|
|
|
putc(*pos, stderr);
|
|
|
|
fprintf(stderr, "'\n");
|
|
|
|
exit(2);
|
|
|
|
}
|
|
|
|
|
2016-07-22 01:10:17 +00:00
|
|
|
static struct expr *
|
|
|
|
mkexpr(enum op op)
|
|
|
|
{
|
|
|
|
struct expr *e = malloc(sizeof (struct expr));
|
|
|
|
if (!e)
|
|
|
|
parse_error("out of memory");
|
|
|
|
e->op = op;
|
2017-03-30 16:32:52 +00:00
|
|
|
e->extra = 0;
|
2016-07-22 01:10:17 +00:00
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
2019-01-28 13:38:05 +00:00
|
|
|
static void
|
|
|
|
freeexpr(struct expr *e)
|
|
|
|
{
|
|
|
|
if (!e) return;
|
|
|
|
switch (e->op) {
|
|
|
|
case EXPR_OR:
|
|
|
|
case EXPR_AND:
|
|
|
|
freeexpr(e->a.expr);
|
|
|
|
freeexpr(e->b.expr);
|
|
|
|
break;
|
|
|
|
case EXPR_COND:
|
|
|
|
freeexpr(e->a.expr);
|
|
|
|
freeexpr(e->b.expr);
|
|
|
|
freeexpr(e->c.expr);
|
|
|
|
break;
|
|
|
|
case EXPR_NOT:
|
|
|
|
freeexpr(e->a.expr);
|
|
|
|
break;
|
2020-02-05 14:30:53 +00:00
|
|
|
case EXPR_BINDING:
|
|
|
|
e->a.binding->refcount -= 1;
|
|
|
|
if (e->a.binding->refcount == 0) {
|
|
|
|
freeexpr(e->a.binding->expr);
|
|
|
|
free(e->a.binding);
|
|
|
|
}
|
|
|
|
break;
|
2019-01-28 13:38:05 +00:00
|
|
|
case EXPR_REDIR_FILE:
|
|
|
|
case EXPR_REDIR_PIPE:
|
|
|
|
free(e->a.string);
|
|
|
|
free(e->b.string);
|
|
|
|
break;
|
|
|
|
case EXPR_STREQ:
|
|
|
|
case EXPR_STREQI:
|
|
|
|
case EXPR_GLOB:
|
|
|
|
case EXPR_GLOBI:
|
|
|
|
case EXPR_REGEX:
|
|
|
|
case EXPR_REGEXI:
|
|
|
|
switch (e->a.prop) {
|
2019-01-29 13:07:55 +00:00
|
|
|
case PROP_PATH: break;
|
|
|
|
case PROP_HDR:
|
|
|
|
case PROP_HDR_ADDR:
|
|
|
|
case PROP_HDR_DISP:
|
|
|
|
free(e->b.string);
|
2019-01-28 13:38:05 +00:00
|
|
|
}
|
|
|
|
if (e->op == EXPR_REGEX || e->op == EXPR_REGEXI)
|
2019-01-29 13:07:55 +00:00
|
|
|
regfree(e->c.regex);
|
2019-01-28 13:38:05 +00:00
|
|
|
else
|
2019-01-29 13:07:55 +00:00
|
|
|
free(e->c.string);
|
2020-02-05 14:30:53 +00:00
|
|
|
break;
|
2019-01-28 13:38:05 +00:00
|
|
|
}
|
|
|
|
free(e);
|
|
|
|
}
|
|
|
|
|
2016-07-22 01:10:17 +00:00
|
|
|
static struct expr *
|
|
|
|
chain(struct expr *e1, enum op op, struct expr *e2)
|
|
|
|
{
|
|
|
|
struct expr *i, *j, *e;
|
|
|
|
if (!e1)
|
|
|
|
return e2;
|
|
|
|
if (!e2)
|
|
|
|
return e1;
|
|
|
|
for (j = 0, i = e1; i->op == op; j = i, i = i->b.expr)
|
|
|
|
;
|
|
|
|
if (!j) {
|
|
|
|
e = mkexpr(op);
|
|
|
|
e->a.expr = e1;
|
|
|
|
e->b.expr = e2;
|
|
|
|
return e;
|
|
|
|
} else {
|
|
|
|
e = mkexpr(op);
|
|
|
|
e->a.expr = i;
|
|
|
|
e->b.expr = e2;
|
|
|
|
j->b.expr = e;
|
|
|
|
return e1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static enum op
|
|
|
|
parse_op()
|
|
|
|
{
|
|
|
|
if (token("<="))
|
|
|
|
return EXPR_LE;
|
|
|
|
else if (token("<"))
|
|
|
|
return EXPR_LT;
|
|
|
|
else if (token(">="))
|
|
|
|
return EXPR_GE;
|
|
|
|
else if (token(">"))
|
|
|
|
return EXPR_GT;
|
|
|
|
else if (token("==") || token("="))
|
|
|
|
return EXPR_EQ;
|
|
|
|
else if (token("!="))
|
|
|
|
return EXPR_NEQ;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-01-28 15:22:29 +00:00
|
|
|
static int
|
|
|
|
parse_ident(char **sp, size_t *lp)
|
|
|
|
{
|
|
|
|
char *p = pos;
|
|
|
|
if (!isalpha(*pos) && *pos != '_')
|
|
|
|
return 0;
|
|
|
|
p++;
|
|
|
|
while (*p && (isalnum(*p) || *p == '_'))
|
|
|
|
p++;
|
|
|
|
*sp = pos;
|
|
|
|
*lp = p-pos;
|
|
|
|
pos = p;
|
|
|
|
ws();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct expr *
|
|
|
|
parse_binding()
|
|
|
|
{
|
|
|
|
struct scope *sc;
|
|
|
|
struct binding *b;
|
|
|
|
char *s;
|
|
|
|
size_t l = 0;
|
2020-02-05 15:07:41 +00:00
|
|
|
struct pos savepos = { pos, line, linenr };
|
2019-01-28 15:22:29 +00:00
|
|
|
|
|
|
|
if (parse_ident(&s, &l)) {
|
|
|
|
for (sc = scopeq; sc; sc = sc->prev) {
|
|
|
|
for (b = sc->bindings; b; b = b->next) {
|
|
|
|
if (b->nlen == l && strncmp(b->name, s, l) == 0) {
|
2020-02-05 14:30:53 +00:00
|
|
|
struct expr *e = mkexpr(EXPR_BINDING);
|
|
|
|
e->a.binding = b;
|
2019-01-28 15:22:29 +00:00
|
|
|
b->refcount++;
|
2020-02-05 14:30:53 +00:00
|
|
|
return e;
|
2019-01-28 15:22:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-02-05 15:07:41 +00:00
|
|
|
|
|
|
|
parse_error_at(&savepos, "unknown expression");
|
2019-01-28 15:22:29 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-01-28 10:12:18 +00:00
|
|
|
static struct expr *parse_cond();
|
2016-07-22 01:10:17 +00:00
|
|
|
|
2019-01-28 15:22:29 +00:00
|
|
|
static struct expr *
|
|
|
|
parse_let()
|
|
|
|
{
|
|
|
|
if (!token("let"))
|
|
|
|
return parse_binding();
|
|
|
|
|
|
|
|
struct scope *sc;
|
|
|
|
char *s;
|
|
|
|
size_t l;
|
|
|
|
|
|
|
|
sc = xcalloc(1, sizeof (struct scope));
|
|
|
|
sc->prev = scopeq;
|
|
|
|
scopeq = sc;
|
|
|
|
|
|
|
|
struct binding *b, *bq;
|
|
|
|
struct expr *e;
|
|
|
|
bq = NULL;
|
|
|
|
for (;;) {
|
|
|
|
if (!parse_ident(&s, &l))
|
|
|
|
parse_error_at(NULL, "missing ident");
|
|
|
|
if (!token("="))
|
|
|
|
parse_error_at(NULL, "missing =");
|
|
|
|
e = parse_cond();
|
|
|
|
b = xcalloc(1, sizeof (struct binding));
|
|
|
|
b->name = s;
|
|
|
|
b->nlen = l;
|
|
|
|
b->expr = e;
|
2020-02-05 14:30:53 +00:00
|
|
|
if (bq) {
|
|
|
|
bq->next = b;
|
|
|
|
bq = b;
|
|
|
|
} else {
|
|
|
|
bq = sc->bindings = b;
|
|
|
|
}
|
2019-01-28 15:22:29 +00:00
|
|
|
if (!token("let"))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!token("in"))
|
|
|
|
parse_error_at(NULL, "missing `in`");
|
|
|
|
|
|
|
|
e = parse_cond();
|
|
|
|
|
|
|
|
struct binding *bs;
|
|
|
|
for (b = sc->bindings; b; b = bs) {
|
|
|
|
bs = b->next;
|
2020-02-05 14:30:53 +00:00
|
|
|
if (b->refcount != 0)
|
|
|
|
continue;
|
|
|
|
freeexpr(b->expr);
|
2019-01-28 15:22:29 +00:00
|
|
|
free(b);
|
|
|
|
}
|
|
|
|
|
|
|
|
scopeq = sc->prev;
|
|
|
|
free(sc);
|
|
|
|
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct expr *parse_cmp();
|
|
|
|
|
2016-07-22 01:10:17 +00:00
|
|
|
static struct expr *
|
|
|
|
parse_inner()
|
|
|
|
{
|
2016-07-23 15:40:29 +00:00
|
|
|
if (token("prune")) {
|
2019-01-28 14:41:29 +00:00
|
|
|
return mkexpr(EXPR_PRUNE);
|
2016-07-23 15:40:29 +00:00
|
|
|
} else if (token("print")) {
|
2019-01-28 14:41:29 +00:00
|
|
|
return mkexpr(EXPR_PRINT);
|
2019-01-28 09:58:27 +00:00
|
|
|
} else if (token("skip")) {
|
|
|
|
struct expr *e = mkexpr(EXPR_PRINT);
|
|
|
|
struct expr *not = mkexpr(EXPR_NOT);
|
|
|
|
not->a.expr = e;
|
|
|
|
return not;
|
2016-07-22 01:10:17 +00:00
|
|
|
} else if (token("!")) {
|
|
|
|
struct expr *e = parse_cmp();
|
|
|
|
struct expr *not = mkexpr(EXPR_NOT);
|
|
|
|
not->a.expr = e;
|
|
|
|
return not;
|
2019-01-28 14:41:29 +00:00
|
|
|
}
|
|
|
|
if (peek("(")) {
|
|
|
|
struct pos savepos = { pos, line, linenr };
|
|
|
|
(void) token("(");
|
2019-01-28 10:12:18 +00:00
|
|
|
struct expr *e = parse_cond();
|
2016-07-22 01:10:17 +00:00
|
|
|
if (token(")"))
|
|
|
|
return e;
|
2019-01-28 14:41:29 +00:00
|
|
|
parse_error_at(&savepos, "unterminated (");
|
2016-07-22 01:10:17 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2019-01-28 15:22:29 +00:00
|
|
|
return parse_let();
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parse_string(char **s)
|
|
|
|
{
|
|
|
|
char *buf = 0;
|
|
|
|
size_t bufsiz = 0;
|
|
|
|
size_t len = 0;
|
|
|
|
|
|
|
|
if (*pos == '"') {
|
|
|
|
pos++;
|
|
|
|
while (*pos &&
|
|
|
|
(*pos != '"' || (*pos == '"' && *(pos+1) == '"'))) {
|
2019-01-28 15:24:01 +00:00
|
|
|
if (len+1 >= bufsiz) {
|
2016-07-22 01:10:17 +00:00
|
|
|
bufsiz = 2*bufsiz + 16;
|
|
|
|
buf = realloc(buf, bufsiz);
|
|
|
|
if (!buf)
|
|
|
|
parse_error("string too long");
|
|
|
|
}
|
|
|
|
if (*pos == '"')
|
|
|
|
pos++;
|
|
|
|
buf[len++] = *pos++;
|
|
|
|
}
|
|
|
|
if (!*pos)
|
|
|
|
parse_error("unterminated string");
|
|
|
|
if (buf)
|
|
|
|
buf[len] = 0;
|
|
|
|
pos++;
|
|
|
|
ws();
|
2019-06-06 19:48:24 +00:00
|
|
|
*s = buf ? buf : xstrdup("");
|
2016-07-22 01:10:17 +00:00
|
|
|
return 1;
|
|
|
|
} else if (*pos == '$') {
|
|
|
|
char t;
|
|
|
|
char *e = ++pos;
|
|
|
|
|
2017-08-31 15:30:17 +00:00
|
|
|
while (isalnum((unsigned char)*pos) || *pos == '_')
|
2016-07-22 01:10:17 +00:00
|
|
|
pos++;
|
|
|
|
if (e == pos)
|
|
|
|
parse_error("invalid environment variable name");
|
|
|
|
|
|
|
|
t = *pos;
|
|
|
|
*pos = 0;
|
|
|
|
*s = getenv(e);
|
|
|
|
if (!*s)
|
2020-06-07 12:26:38 +00:00
|
|
|
*s = "";
|
|
|
|
*s = xstrdup(*s);
|
2016-07-22 01:10:17 +00:00
|
|
|
*pos = t;
|
|
|
|
ws();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct expr *
|
|
|
|
parse_strcmp()
|
|
|
|
{
|
|
|
|
enum prop prop;
|
|
|
|
enum op op;
|
2017-10-26 19:50:29 +00:00
|
|
|
int negate;
|
2017-04-17 00:54:22 +00:00
|
|
|
char *h;
|
2016-07-22 01:10:17 +00:00
|
|
|
|
2017-04-17 00:54:22 +00:00
|
|
|
h = 0;
|
|
|
|
prop = 0;
|
2017-10-26 19:50:29 +00:00
|
|
|
negate = 0;
|
2017-04-17 00:54:22 +00:00
|
|
|
|
|
|
|
if (token("from"))
|
2019-01-29 13:07:55 +00:00
|
|
|
h = xstrdup("from");
|
2016-07-22 01:10:17 +00:00
|
|
|
else if (token("to"))
|
2019-01-29 13:07:55 +00:00
|
|
|
h = xstrdup("to");
|
2017-04-17 00:54:22 +00:00
|
|
|
else if (token("subject"))
|
2019-01-29 13:07:55 +00:00
|
|
|
h = xstrdup("subject");
|
|
|
|
else if (token("path"))
|
|
|
|
prop = PROP_PATH;
|
2017-04-17 00:54:22 +00:00
|
|
|
else if (!parse_string(&h))
|
2016-07-22 01:10:17 +00:00
|
|
|
return parse_inner();
|
|
|
|
|
2019-01-29 13:07:55 +00:00
|
|
|
if (!prop) {
|
|
|
|
if (token(".")) {
|
|
|
|
if (token("addr")) prop = PROP_HDR_ADDR;
|
|
|
|
else if (token("disp")) prop = PROP_HDR_DISP;
|
|
|
|
else parse_error_at(NULL, "unknown decode parameter");
|
|
|
|
} else
|
|
|
|
prop = PROP_HDR;
|
|
|
|
}
|
|
|
|
|
2016-07-22 01:10:17 +00:00
|
|
|
if (token("~~~"))
|
|
|
|
op = EXPR_GLOBI;
|
|
|
|
else if (token("~~"))
|
|
|
|
op = EXPR_GLOB;
|
|
|
|
else if (token("=~~"))
|
|
|
|
op = EXPR_REGEXI;
|
|
|
|
else if (token("=~"))
|
|
|
|
op = EXPR_REGEX;
|
|
|
|
else if (token("==="))
|
|
|
|
op = EXPR_STREQI;
|
|
|
|
else if (token("=="))
|
|
|
|
op = EXPR_STREQ;
|
|
|
|
else if (token("="))
|
|
|
|
op = EXPR_STREQ;
|
2017-10-26 19:50:29 +00:00
|
|
|
else if (token("!~~~"))
|
|
|
|
negate = 1, op = EXPR_GLOBI;
|
|
|
|
else if (token("!~~"))
|
|
|
|
negate = 1, op = EXPR_GLOB;
|
|
|
|
else if (token("!=~~"))
|
|
|
|
negate = 1, op = EXPR_REGEXI;
|
|
|
|
else if (token("!=~"))
|
|
|
|
negate = 1, op = EXPR_REGEX;
|
|
|
|
else if (token("!==="))
|
|
|
|
negate = 1, op = EXPR_STREQI;
|
|
|
|
else if (token("!==") || token("!="))
|
|
|
|
negate = 1, op = EXPR_STREQ;
|
2016-07-22 01:10:17 +00:00
|
|
|
else
|
2019-01-28 14:00:56 +00:00
|
|
|
parse_error_at(NULL, "invalid string operator");
|
2016-07-22 01:10:17 +00:00
|
|
|
|
|
|
|
char *s;
|
2016-07-28 17:31:56 +00:00
|
|
|
if (!parse_string(&s)) {
|
2019-01-28 14:00:56 +00:00
|
|
|
parse_error_at(NULL, "invalid string");
|
2016-07-28 17:31:56 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2016-07-22 01:10:17 +00:00
|
|
|
|
2016-07-28 17:31:56 +00:00
|
|
|
int r = 0;
|
|
|
|
struct expr *e = mkexpr(op);
|
2019-01-29 13:07:55 +00:00
|
|
|
e->a.prop = prop;
|
|
|
|
switch (prop) {
|
|
|
|
case PROP_HDR:
|
|
|
|
case PROP_HDR_ADDR:
|
|
|
|
case PROP_HDR_DISP:
|
|
|
|
e->b.string = h;
|
|
|
|
break;
|
|
|
|
case PROP_PATH: break;
|
2016-07-28 17:31:56 +00:00
|
|
|
}
|
2016-07-22 01:10:17 +00:00
|
|
|
|
2019-01-28 13:45:40 +00:00
|
|
|
if (op == EXPR_REGEX || op == EXPR_REGEXI) {
|
2019-01-29 13:07:55 +00:00
|
|
|
e->c.regex = malloc(sizeof (regex_t));
|
|
|
|
r = regcomp(e->c.regex, s, REG_EXTENDED | REG_NOSUB |
|
2019-01-28 13:45:40 +00:00
|
|
|
(op == EXPR_REGEXI ? REG_ICASE : 0));
|
|
|
|
if (r != 0) {
|
|
|
|
char msg[256];
|
2019-01-29 13:07:55 +00:00
|
|
|
regerror(r, e->c.regex, msg, sizeof msg);
|
2019-01-28 13:45:40 +00:00
|
|
|
parse_error("invalid regex '%s': %s", s, msg);
|
|
|
|
exit(2);
|
|
|
|
}
|
|
|
|
free(s);
|
2016-07-28 17:31:56 +00:00
|
|
|
} else {
|
2019-01-29 13:07:55 +00:00
|
|
|
e->c.string = s;
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
|
|
|
|
2017-10-26 19:50:29 +00:00
|
|
|
if (negate) {
|
|
|
|
struct expr *not = mkexpr(EXPR_NOT);
|
|
|
|
not->a.expr = e;
|
|
|
|
return not;
|
|
|
|
}
|
|
|
|
|
2016-07-28 17:31:56 +00:00
|
|
|
return e;
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int64_t
|
|
|
|
parse_num(int64_t *r)
|
|
|
|
{
|
|
|
|
char *s = pos;
|
2017-08-31 15:30:17 +00:00
|
|
|
if (isdigit((unsigned char)*pos)) {
|
2016-07-22 01:10:17 +00:00
|
|
|
int64_t n;
|
|
|
|
|
2017-08-31 15:30:17 +00:00
|
|
|
for (n = 0; isdigit((unsigned char)*pos) && n <= INT64_MAX / 10 - 10; pos++)
|
2016-07-22 01:10:17 +00:00
|
|
|
n = 10 * n + (*pos - '0');
|
2017-08-31 15:30:17 +00:00
|
|
|
if (isdigit((unsigned char)*pos))
|
2016-07-22 01:10:17 +00:00
|
|
|
parse_error("number too big: %s", s);
|
|
|
|
if (token("c")) ;
|
|
|
|
else if (token("b")) n *= 512LL;
|
|
|
|
else if (token("k")) n *= 1024LL;
|
|
|
|
else if (token("M")) n *= 1024LL * 1024;
|
|
|
|
else if (token("G")) n *= 1024LL * 1024 * 1024;
|
|
|
|
else if (token("T")) n *= 1024LL * 1024 * 1024 * 1024;
|
|
|
|
ws();
|
|
|
|
*r = n;
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct expr *
|
|
|
|
parse_flag()
|
|
|
|
{
|
|
|
|
enum flags flag;
|
|
|
|
|
|
|
|
if (token("passed")) {
|
|
|
|
flag = FLAG_PASSED;
|
|
|
|
} else if (token("replied")) {
|
|
|
|
flag = FLAG_REPLIED;
|
|
|
|
} else if (token("seen")) {
|
|
|
|
flag = FLAG_SEEN;
|
|
|
|
} else if (token("trashed")) {
|
|
|
|
flag = FLAG_TRASHED;
|
|
|
|
} else if (token("draft")) {
|
|
|
|
flag = FLAG_DRAFT;
|
|
|
|
} else if (token("flagged")) {
|
|
|
|
flag = FLAG_FLAGGED;
|
|
|
|
} else if (token("new")) {
|
|
|
|
flag = FLAG_NEW;
|
|
|
|
} else if (token("cur")) {
|
|
|
|
flag = FLAG_CUR;
|
2016-07-28 16:47:17 +00:00
|
|
|
} else if (token("info")) {
|
|
|
|
flag = FLAG_INFO;
|
2016-07-23 16:45:23 +00:00
|
|
|
} else if (token("parent")) {
|
|
|
|
flag = FLAG_PARENT;
|
|
|
|
need_thr = 1;
|
|
|
|
} else if (token("child")) {
|
|
|
|
flag = FLAG_CHILD;
|
|
|
|
need_thr = 1;
|
2016-07-22 01:10:17 +00:00
|
|
|
} else
|
|
|
|
return parse_strcmp();
|
|
|
|
|
|
|
|
struct expr *e = mkexpr(EXPR_ANYSET);
|
|
|
|
e->a.prop = PROP_FLAG;
|
|
|
|
e->b.num = flag;
|
|
|
|
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct expr *
|
|
|
|
parse_cmp()
|
|
|
|
{
|
|
|
|
enum prop prop;
|
|
|
|
enum op op;
|
|
|
|
|
|
|
|
if (token("depth"))
|
|
|
|
prop = PROP_DEPTH;
|
2016-07-31 15:05:20 +00:00
|
|
|
else if (token("kept"))
|
|
|
|
prop = PROP_KEPT;
|
2016-07-22 01:10:17 +00:00
|
|
|
else if (token("index"))
|
|
|
|
prop = PROP_INDEX;
|
2016-07-24 15:59:11 +00:00
|
|
|
else if (token("replies")) {
|
|
|
|
prop = PROP_REPLIES;
|
|
|
|
need_thr = 1;
|
|
|
|
} else if (token("size"))
|
2016-07-22 01:10:17 +00:00
|
|
|
prop = PROP_SIZE;
|
|
|
|
else if (token("total"))
|
|
|
|
prop = PROP_TOTAL;
|
|
|
|
else
|
|
|
|
return parse_flag();
|
|
|
|
|
|
|
|
if (!(op = parse_op()))
|
2019-01-28 14:00:56 +00:00
|
|
|
parse_error_at(NULL, "invalid comparison");
|
2016-07-22 01:10:17 +00:00
|
|
|
|
|
|
|
int64_t n;
|
|
|
|
if (parse_num(&n)) {
|
|
|
|
struct expr *e = mkexpr(op);
|
|
|
|
e->a.prop = prop;
|
|
|
|
e->b.num = n;
|
|
|
|
return e;
|
2017-08-31 15:30:17 +00:00
|
|
|
} else if (token("cur")) {
|
2016-07-28 23:49:28 +00:00
|
|
|
struct expr *e = mkexpr(op);
|
|
|
|
e->a.prop = prop;
|
|
|
|
e->b.var = VAR_CUR;
|
|
|
|
e->extra = 1;
|
|
|
|
return e;
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parse_dur(int64_t *n)
|
|
|
|
{
|
|
|
|
char *s, *r;
|
|
|
|
if (!parse_string(&s))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (*s == '/' || *s == '.') {
|
|
|
|
struct stat st;
|
|
|
|
if (stat(s, &st) < 0)
|
|
|
|
parse_error("can't stat file '%s': %s",
|
|
|
|
s, strerror(errno));
|
|
|
|
*n = st.st_mtime;
|
2019-06-06 19:48:57 +00:00
|
|
|
goto ret;
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct tm tm = { 0 };
|
|
|
|
r = strptime(s, "%Y-%m-%d %H:%M:%S", &tm);
|
|
|
|
if (r && !*r) {
|
|
|
|
*n = mktime(&tm);
|
2019-06-06 19:48:57 +00:00
|
|
|
goto ret;
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
|
|
|
r = strptime(s, "%Y-%m-%d", &tm);
|
|
|
|
if (r && !*r) {
|
|
|
|
tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
|
|
|
|
*n = mktime(&tm);
|
2019-06-06 19:48:57 +00:00
|
|
|
goto ret;
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
|
|
|
r = strptime(s, "%H:%M:%S", &tm);
|
|
|
|
if (r && !*r) {
|
|
|
|
struct tm *tmnow = localtime(&now);
|
|
|
|
tm.tm_year = tmnow->tm_year;
|
|
|
|
tm.tm_mon = tmnow->tm_mon;
|
|
|
|
tm.tm_mday = tmnow->tm_mday;
|
|
|
|
*n = mktime(&tm);
|
2019-06-06 19:48:57 +00:00
|
|
|
goto ret;
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
|
|
|
r = strptime(s, "%H:%M", &tm);
|
|
|
|
if (r && !*r) {
|
|
|
|
struct tm *tmnow = localtime(&now);
|
|
|
|
tm.tm_year = tmnow->tm_year;
|
|
|
|
tm.tm_mon = tmnow->tm_mon;
|
|
|
|
tm.tm_mday = tmnow->tm_mday;
|
|
|
|
tm.tm_sec = 0;
|
|
|
|
*n = mktime(&tm);
|
2019-06-06 19:48:57 +00:00
|
|
|
goto ret;
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (*s == '-') {
|
|
|
|
int64_t d;
|
2019-06-06 19:48:57 +00:00
|
|
|
errno = 0;
|
|
|
|
d = strtol(s+1, &r, 10);
|
2016-07-22 01:10:17 +00:00
|
|
|
if (errno == 0 && r[0] == 'd' && !r[1]) {
|
|
|
|
struct tm *tmnow = localtime(&now);
|
|
|
|
tmnow->tm_mday -= d;
|
|
|
|
tmnow->tm_hour = tmnow->tm_min = tmnow->tm_sec = 0;
|
|
|
|
*n = mktime(tmnow);
|
2019-06-06 19:48:57 +00:00
|
|
|
goto ret;
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
|
|
|
if (errno == 0 && r[0] == 'h' && !r[1]) {
|
|
|
|
*n = now - (d*60*60);
|
2019-06-06 19:48:57 +00:00
|
|
|
goto ret;
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
|
|
|
if (errno == 0 && r[0] == 'm' && !r[1]) {
|
|
|
|
*n = now - (d*60);
|
2019-06-06 19:48:57 +00:00
|
|
|
goto ret;
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
|
|
|
if (errno == 0 && r[0] == 's' && !r[1]) {
|
|
|
|
*n = now - d;
|
2019-06-06 19:48:57 +00:00
|
|
|
goto ret;
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
2019-06-06 19:48:57 +00:00
|
|
|
parse_error("invalid relative time format '%s'", s);
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
parse_error("invalid time format '%s'", s);
|
|
|
|
return 0;
|
2019-06-06 19:48:57 +00:00
|
|
|
ret:
|
|
|
|
free(s);
|
|
|
|
return 1;
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct expr *
|
|
|
|
parse_timecmp()
|
|
|
|
{
|
|
|
|
enum prop prop;
|
|
|
|
enum op op;
|
|
|
|
|
|
|
|
if (token("atime"))
|
|
|
|
prop = PROP_ATIME;
|
|
|
|
else if (token("ctime"))
|
|
|
|
prop = PROP_CTIME;
|
|
|
|
else if (token("mtime"))
|
|
|
|
prop = PROP_MTIME;
|
|
|
|
else if (token("date"))
|
|
|
|
prop = PROP_DATE;
|
|
|
|
else
|
|
|
|
return parse_cmp();
|
|
|
|
|
|
|
|
op = parse_op();
|
|
|
|
if (!op)
|
2019-01-28 14:00:56 +00:00
|
|
|
parse_error_at(NULL, "invalid comparison");
|
2016-07-22 01:10:17 +00:00
|
|
|
|
|
|
|
int64_t n;
|
|
|
|
if (parse_num(&n) || parse_dur(&n)) {
|
|
|
|
struct expr *e = mkexpr(op);
|
|
|
|
e->a.prop = prop;
|
|
|
|
e->b.num = n;
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-01-28 09:53:05 +00:00
|
|
|
static struct expr *
|
|
|
|
parse_redir(struct expr *e)
|
|
|
|
{
|
|
|
|
char *s;
|
|
|
|
const char *m;
|
|
|
|
|
|
|
|
if (peek("||"))
|
|
|
|
return e;
|
|
|
|
|
|
|
|
if (token("|")) {
|
|
|
|
if (!parse_string(&s))
|
2019-01-28 14:00:56 +00:00
|
|
|
parse_error_at(NULL, "expected command");
|
2019-01-28 09:53:05 +00:00
|
|
|
struct expr *r = mkexpr(EXPR_REDIR_PIPE);
|
|
|
|
r->a.string = s;
|
2019-01-28 13:45:40 +00:00
|
|
|
r->b.string = xstrdup("w");
|
2019-01-28 09:53:05 +00:00
|
|
|
return chain(e, EXPR_AND, r);
|
|
|
|
}
|
|
|
|
else if (token(">>")) m = "a+";
|
|
|
|
else if (token(">")) m = "w+";
|
|
|
|
else return e;
|
|
|
|
|
|
|
|
if (!parse_string(&s))
|
2019-01-28 14:00:56 +00:00
|
|
|
parse_error_at(NULL, "expected file name");
|
2019-01-28 09:53:05 +00:00
|
|
|
struct expr *r = mkexpr(EXPR_REDIR_FILE);
|
|
|
|
r->a.string = s;
|
2019-01-28 13:45:40 +00:00
|
|
|
r->b.string = xstrdup(m);
|
2019-01-28 09:53:05 +00:00
|
|
|
return chain(e, EXPR_AND, r);
|
|
|
|
}
|
|
|
|
|
2016-07-22 01:10:17 +00:00
|
|
|
static struct expr *
|
|
|
|
parse_and()
|
|
|
|
{
|
2019-01-28 09:53:05 +00:00
|
|
|
struct expr *e1 = parse_redir(parse_timecmp());
|
2016-07-22 01:10:17 +00:00
|
|
|
struct expr *r = e1;
|
|
|
|
|
|
|
|
while (token("&&")) {
|
2019-01-28 09:53:05 +00:00
|
|
|
struct expr *e2 = parse_redir(parse_timecmp());
|
2016-07-22 01:10:17 +00:00
|
|
|
r = chain(r, EXPR_AND, e2);
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct expr *
|
|
|
|
parse_or()
|
|
|
|
{
|
|
|
|
struct expr *e1 = parse_and();
|
|
|
|
struct expr *r = e1;
|
|
|
|
|
|
|
|
while (token("||")) {
|
|
|
|
struct expr *e2 = parse_and();
|
|
|
|
r = chain(r, EXPR_OR, e2);
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct expr *
|
2019-01-28 10:12:18 +00:00
|
|
|
parse_cond()
|
|
|
|
{
|
|
|
|
struct expr *e1 = parse_or();
|
|
|
|
|
|
|
|
if (token("?")) {
|
|
|
|
struct expr *e2 = parse_or();
|
|
|
|
if (token(":")) {
|
|
|
|
struct expr *e3 = parse_cond();
|
|
|
|
struct expr *r = mkexpr(EXPR_COND);
|
|
|
|
r->a.expr = e1;
|
|
|
|
r->b.expr = e2;
|
|
|
|
r->c.expr = e3;
|
|
|
|
|
|
|
|
return r;
|
|
|
|
} else {
|
2019-01-28 14:00:56 +00:00
|
|
|
parse_error_at(NULL, "expected :", pos);
|
2019-01-28 10:12:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return e1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct expr *
|
|
|
|
parse_expr()
|
2016-07-22 01:10:17 +00:00
|
|
|
{
|
2019-01-28 14:30:19 +00:00
|
|
|
ws();
|
|
|
|
return parse_cond();
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct expr *
|
|
|
|
parse_buf(const char *f, char *s)
|
|
|
|
{
|
|
|
|
struct expr *e;
|
|
|
|
fname = f;
|
|
|
|
line = pos = s;
|
|
|
|
linenr = 1;
|
|
|
|
e = parse_expr();
|
2016-07-22 01:10:17 +00:00
|
|
|
if (*pos)
|
2019-01-28 14:00:56 +00:00
|
|
|
parse_error_at(NULL, "trailing garbage");
|
2016-07-22 01:10:17 +00:00
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
time_t
|
|
|
|
msg_date(struct mailinfo *m)
|
|
|
|
{
|
|
|
|
if (m->date)
|
|
|
|
return m->date;
|
|
|
|
|
2019-01-28 14:10:24 +00:00
|
|
|
// XXX: date comparisation should handle zero dates
|
|
|
|
if (!m->msg && m->fpath) {
|
|
|
|
if (!(m->msg = blaze822(m->fpath))) {
|
|
|
|
m->fpath = NULL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2017-03-29 18:20:44 +00:00
|
|
|
|
2016-07-22 01:10:17 +00:00
|
|
|
char *b;
|
2016-07-24 15:59:11 +00:00
|
|
|
if (m->msg && (b = blaze822_hdr(m->msg, "date")))
|
2016-07-22 01:10:17 +00:00
|
|
|
return (m->date = blaze822_date(b));
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
2019-01-29 13:07:55 +00:00
|
|
|
msg_hdr(char **s, const char *h, struct mailinfo *m)
|
2016-07-22 01:10:17 +00:00
|
|
|
{
|
2019-01-28 14:10:24 +00:00
|
|
|
static char hdrbuf[4096];
|
2017-03-29 18:20:44 +00:00
|
|
|
|
2019-01-28 14:10:24 +00:00
|
|
|
if (!m->msg && m->fpath) {
|
|
|
|
if (!(m->msg = blaze822(m->fpath))) {
|
|
|
|
m->fpath = NULL;
|
2019-01-29 13:07:55 +00:00
|
|
|
return NULL;
|
2019-01-28 14:10:24 +00:00
|
|
|
}
|
|
|
|
}
|
2017-03-30 17:36:56 +00:00
|
|
|
|
2019-01-29 13:07:55 +00:00
|
|
|
// XXX: only return one header for now
|
|
|
|
if (*s)
|
|
|
|
return NULL;
|
|
|
|
|
2019-01-28 14:10:24 +00:00
|
|
|
char *b;
|
2019-01-29 13:07:55 +00:00
|
|
|
if (!m->msg || !(b = blaze822_chdr(m->msg, h)))
|
|
|
|
return NULL;
|
|
|
|
*s = b;
|
2016-07-22 01:10:17 +00:00
|
|
|
|
2019-01-28 14:10:24 +00:00
|
|
|
blaze822_decode_rfc2047(hdrbuf, b, sizeof hdrbuf - 1, "UTF-8");
|
|
|
|
return hdrbuf;
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
|
|
|
|
2016-07-28 17:31:56 +00:00
|
|
|
char *
|
2019-01-29 13:07:55 +00:00
|
|
|
msg_hdr_addr(char **s, const char *h, struct mailinfo *m, int rdisp)
|
2016-07-28 17:31:56 +00:00
|
|
|
{
|
2019-01-28 14:10:24 +00:00
|
|
|
if (!m->msg && m->fpath) {
|
|
|
|
if (!(m->msg = blaze822(m->fpath))) {
|
|
|
|
m->fpath = NULL;
|
2019-01-29 13:07:55 +00:00
|
|
|
return NULL;
|
2019-01-28 14:10:24 +00:00
|
|
|
}
|
|
|
|
}
|
2017-03-29 18:20:44 +00:00
|
|
|
|
2019-01-29 13:07:55 +00:00
|
|
|
char *b = *s;
|
|
|
|
if (!b) {
|
|
|
|
if (!m->msg || !(b = blaze822_chdr(m->msg, h)))
|
|
|
|
return NULL;
|
|
|
|
}
|
2016-07-28 17:31:56 +00:00
|
|
|
|
|
|
|
char *disp, *addr;
|
2019-01-29 13:07:55 +00:00
|
|
|
*s = blaze822_addr(b, &disp, &addr);
|
2016-07-28 17:31:56 +00:00
|
|
|
|
2019-01-29 13:07:55 +00:00
|
|
|
if (rdisp)
|
2016-07-28 17:31:56 +00:00
|
|
|
return disp;
|
2019-01-29 13:07:55 +00:00
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
msg_hdr_address(char **s, const char *h, struct mailinfo *m)
|
|
|
|
{
|
|
|
|
return msg_hdr_addr(s, h, m, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
msg_hdr_display(char **s, const char *h, struct mailinfo *m)
|
|
|
|
{
|
|
|
|
return msg_hdr_addr(s, h, m, 1);
|
2016-07-28 17:31:56 +00:00
|
|
|
}
|
|
|
|
|
2019-01-28 09:53:05 +00:00
|
|
|
FILE *
|
|
|
|
redir(struct expr *e)
|
|
|
|
{
|
|
|
|
struct file *file;
|
|
|
|
FILE *fp;
|
|
|
|
|
|
|
|
for (file = files; file; file = file->next) {
|
|
|
|
if (e->op == file->op &&
|
|
|
|
strcmp(e->a.string, file->name) == 0 &&
|
|
|
|
strcmp(e->b.string, file->mode) == 0)
|
|
|
|
return file->fp;
|
|
|
|
}
|
|
|
|
|
|
|
|
fflush(stdout);
|
|
|
|
fp = NULL;
|
|
|
|
switch (e->op) {
|
|
|
|
case EXPR_REDIR_FILE: fp = fopen(e->a.string, e->b.string); break;
|
|
|
|
case EXPR_REDIR_PIPE: fp = popen(e->a.string, e->b.string); break;
|
|
|
|
}
|
|
|
|
if (!fp) {
|
|
|
|
fprintf(stderr, "%s: %s: %s\n", argv0, e->a.string, strerror(errno));
|
|
|
|
exit(3);
|
|
|
|
}
|
2019-01-28 13:45:40 +00:00
|
|
|
file = xcalloc(1, sizeof (struct file));
|
2019-01-28 09:53:05 +00:00
|
|
|
file->op = e->op;
|
|
|
|
file->name = e->a.string;
|
|
|
|
file->mode = e->b.string;
|
|
|
|
file->fp = fp;
|
|
|
|
if (!files) files = file;
|
|
|
|
if (fileq) fileq->next = file;
|
|
|
|
fileq = file;
|
|
|
|
return fp;
|
|
|
|
}
|
|
|
|
|
2016-07-22 01:10:17 +00:00
|
|
|
int
|
|
|
|
eval(struct expr *e, struct mailinfo *m)
|
|
|
|
{
|
2019-01-28 09:53:05 +00:00
|
|
|
FILE *fp;
|
2016-07-22 01:10:17 +00:00
|
|
|
switch (e->op) {
|
|
|
|
case EXPR_OR:
|
|
|
|
return eval(e->a.expr, m) || eval(e->b.expr, m);
|
|
|
|
case EXPR_AND:
|
|
|
|
return eval(e->a.expr, m) && eval(e->b.expr, m);
|
2019-01-28 10:12:18 +00:00
|
|
|
case EXPR_COND:
|
|
|
|
return eval(e->a.expr, m)
|
|
|
|
? eval(e->b.expr, m)
|
|
|
|
: eval(e->c.expr, m);
|
2016-07-22 01:10:17 +00:00
|
|
|
case EXPR_NOT:
|
|
|
|
return !eval(e->a.expr, m);
|
2020-02-05 14:30:53 +00:00
|
|
|
case EXPR_BINDING:
|
|
|
|
return eval(e->a.binding->expr, m);
|
2016-07-23 15:40:29 +00:00
|
|
|
case EXPR_PRUNE:
|
|
|
|
prune = 1;
|
|
|
|
return 1;
|
2016-07-22 01:10:17 +00:00
|
|
|
case EXPR_PRINT:
|
|
|
|
return 1;
|
2019-01-28 09:53:05 +00:00
|
|
|
case EXPR_REDIR_FILE:
|
|
|
|
case EXPR_REDIR_PIPE:
|
|
|
|
fp = redir(e);
|
2019-01-28 14:10:24 +00:00
|
|
|
fputs(m->file, fp);
|
2019-01-28 09:53:05 +00:00
|
|
|
putc('\n', fp);
|
|
|
|
fflush(fp);
|
|
|
|
return 1;
|
2016-07-22 01:10:17 +00:00
|
|
|
case EXPR_LT:
|
|
|
|
case EXPR_LE:
|
|
|
|
case EXPR_EQ:
|
|
|
|
case EXPR_NEQ:
|
|
|
|
case EXPR_GE:
|
|
|
|
case EXPR_GT:
|
|
|
|
case EXPR_ALLSET:
|
|
|
|
case EXPR_ANYSET: {
|
2016-07-31 14:51:49 +00:00
|
|
|
long v = 0, n;
|
2016-07-22 01:10:17 +00:00
|
|
|
|
2019-01-28 14:10:24 +00:00
|
|
|
if (!m->sb && m->fpath && (
|
2016-07-22 01:10:17 +00:00
|
|
|
e->a.prop == PROP_ATIME ||
|
|
|
|
e->a.prop == PROP_CTIME ||
|
|
|
|
e->a.prop == PROP_MTIME ||
|
2019-01-28 14:10:24 +00:00
|
|
|
e->a.prop == PROP_SIZE)) {
|
|
|
|
m->sb = xcalloc(1, sizeof *m->sb);
|
|
|
|
if (stat(m->fpath, m->sb) == -1)
|
|
|
|
m->fpath = NULL;
|
|
|
|
// XXX: stat based expressions should handle 0
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
|
|
|
|
2017-03-30 16:32:52 +00:00
|
|
|
n = e->b.num;
|
|
|
|
|
|
|
|
if (e->extra)
|
2016-07-28 23:49:28 +00:00
|
|
|
switch (e->b.var) {
|
|
|
|
case VAR_CUR:
|
|
|
|
if (!cur_idx)
|
2016-07-31 14:51:49 +00:00
|
|
|
n = (e->op == EXPR_LT || e->op == EXPR_LE) ? LONG_MAX : -1;
|
2016-07-28 23:49:28 +00:00
|
|
|
else
|
2016-07-31 14:51:49 +00:00
|
|
|
n = cur_idx;
|
2016-07-28 23:49:28 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-07-22 01:10:17 +00:00
|
|
|
switch (e->a.prop) {
|
2017-02-21 18:11:46 +00:00
|
|
|
case PROP_ATIME: if (m->sb) v = m->sb->st_atime; break;
|
|
|
|
case PROP_CTIME: if (m->sb) v = m->sb->st_ctime; break;
|
|
|
|
case PROP_MTIME: if (m->sb) v = m->sb->st_mtime; break;
|
2016-07-31 15:05:20 +00:00
|
|
|
case PROP_KEPT: v = kept; break;
|
2016-07-24 15:59:11 +00:00
|
|
|
case PROP_REPLIES: v = m->replies; break;
|
2017-02-21 18:11:46 +00:00
|
|
|
case PROP_SIZE: if (m->sb) v = m->sb->st_size; break;
|
2016-07-29 00:20:24 +00:00
|
|
|
case PROP_DATE: v = msg_date(m); break;
|
2016-07-22 01:10:17 +00:00
|
|
|
case PROP_FLAG: v = m->flags; break;
|
|
|
|
case PROP_INDEX: v = m->index; break;
|
|
|
|
case PROP_DEPTH: v = m->depth; break;
|
2018-02-05 14:36:13 +00:00
|
|
|
default: parse_error("unknown property");
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (e->op) {
|
2016-07-31 14:51:49 +00:00
|
|
|
case EXPR_LT: return v < n;
|
|
|
|
case EXPR_LE: return v <= n;
|
|
|
|
case EXPR_EQ: return v == n;
|
|
|
|
case EXPR_NEQ: return v != n;
|
|
|
|
case EXPR_GE: return v >= n;
|
|
|
|
case EXPR_GT: return v > n;
|
|
|
|
case EXPR_ALLSET: return (v & n) == n;
|
|
|
|
case EXPR_ANYSET: return (v & n) > 0;
|
2018-02-05 14:36:13 +00:00
|
|
|
default: parse_error("invalid operator");
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
case EXPR_STREQ:
|
|
|
|
case EXPR_STREQI:
|
|
|
|
case EXPR_GLOB:
|
|
|
|
case EXPR_GLOBI:
|
|
|
|
case EXPR_REGEX:
|
|
|
|
case EXPR_REGEXI: {
|
2019-01-29 13:07:55 +00:00
|
|
|
const char *s = NULL;
|
|
|
|
char *p = NULL;
|
|
|
|
char *(*fn)(char **, const char *, struct mailinfo *) = 0;
|
|
|
|
int rv = 0;
|
2017-08-31 15:30:17 +00:00
|
|
|
switch (e->a.prop) {
|
2019-01-29 13:07:55 +00:00
|
|
|
case PROP_HDR: fn = msg_hdr; break;
|
|
|
|
case PROP_HDR_ADDR: fn = msg_hdr_address; break;
|
|
|
|
case PROP_HDR_DISP: fn = msg_hdr_display; break;
|
2019-01-28 14:10:24 +00:00
|
|
|
case PROP_PATH: s = m->fpath ? m->fpath : ""; break;
|
2019-01-29 13:07:55 +00:00
|
|
|
default: return 0;
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
2019-01-29 13:07:55 +00:00
|
|
|
for (;;) {
|
|
|
|
if (fn && !(s = fn(&p, e->b.string, m)))
|
|
|
|
break;
|
|
|
|
switch (e->op) {
|
|
|
|
case EXPR_STREQ: rv = strcmp(e->c.string, s) == 0; break;
|
|
|
|
case EXPR_STREQI: rv = strcasecmp(e->c.string, s) == 0; break;
|
|
|
|
case EXPR_GLOB: rv = fnmatch(e->c.string, s, 0) == 0; break;
|
|
|
|
case EXPR_GLOBI:
|
|
|
|
rv = fnmatch(e->c.string, s, FNM_CASEFOLD) == 0; break;
|
|
|
|
case EXPR_REGEX:
|
|
|
|
case EXPR_REGEXI:
|
|
|
|
rv = regexec(e->c.regex, s, 0, 0, 0) == 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!fn || rv) return rv;
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
2019-01-29 13:07:55 +00:00
|
|
|
return 0;
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-07-23 15:40:29 +00:00
|
|
|
struct mailinfo *
|
2019-01-28 14:10:24 +00:00
|
|
|
mailfile(struct mailinfo *m, char *file)
|
2016-07-22 01:10:17 +00:00
|
|
|
{
|
|
|
|
static int init;
|
|
|
|
if (!init) {
|
|
|
|
// delay loading of the seqmap until we need to scan the first
|
|
|
|
// file, in case someone in the pipe updated the map before
|
|
|
|
char *seqmap = blaze822_seq_open(0);
|
|
|
|
blaze822_seq_load(seqmap);
|
|
|
|
cur = blaze822_seq_cur();
|
|
|
|
init = 1;
|
|
|
|
}
|
2019-01-28 14:10:24 +00:00
|
|
|
char *fpath = file;
|
2016-07-22 01:10:17 +00:00
|
|
|
|
2016-07-23 15:40:29 +00:00
|
|
|
m->index = num++;
|
2019-01-28 14:10:24 +00:00
|
|
|
m->file = file;
|
2016-07-23 15:40:29 +00:00
|
|
|
|
2019-01-28 14:10:24 +00:00
|
|
|
while (*fpath == ' ' || *fpath == '\t') {
|
2016-07-23 15:40:29 +00:00
|
|
|
m->depth++;
|
2019-01-28 14:10:24 +00:00
|
|
|
fpath++;
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
|
|
|
|
2019-01-28 14:10:24 +00:00
|
|
|
char *e = fpath + strlen(fpath) - 1;
|
|
|
|
while (fpath < e && (*e == ' ' || *e == '\t'))
|
2016-07-22 01:10:17 +00:00
|
|
|
*e-- = 0;
|
|
|
|
|
2019-01-28 14:10:24 +00:00
|
|
|
if (fpath[0] == '<') {
|
2016-07-28 16:47:17 +00:00
|
|
|
m->flags |= FLAG_SEEN | FLAG_INFO;
|
2019-01-28 14:10:24 +00:00
|
|
|
m->fpath = NULL;
|
2016-07-28 16:47:17 +00:00
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
2019-01-28 14:10:24 +00:00
|
|
|
if ((e = strrchr(fpath, '/') - 1) && (e - fpath) >= 2 &&
|
2016-07-22 19:34:54 +00:00
|
|
|
*e-- == 'w' && *e-- == 'e' && *e-- == 'n')
|
2016-07-23 15:40:29 +00:00
|
|
|
m->flags |= FLAG_NEW;
|
2016-07-22 01:10:17 +00:00
|
|
|
|
2019-01-28 14:10:24 +00:00
|
|
|
if (cur && strcmp(cur, fpath) == 0) {
|
2016-07-23 15:40:29 +00:00
|
|
|
m->flags |= FLAG_CUR;
|
2016-07-28 23:49:28 +00:00
|
|
|
cur_idx = m->index;
|
|
|
|
}
|
2016-07-22 01:10:17 +00:00
|
|
|
|
2019-01-28 14:10:24 +00:00
|
|
|
char *f = strstr(fpath, ":2,");
|
2016-07-22 01:10:17 +00:00
|
|
|
if (f) {
|
|
|
|
if (strchr(f, 'P'))
|
2016-07-23 15:40:29 +00:00
|
|
|
m->flags |= FLAG_PASSED;
|
2016-07-22 01:10:17 +00:00
|
|
|
if (strchr(f, 'R'))
|
2016-07-23 15:40:29 +00:00
|
|
|
m->flags |= FLAG_REPLIED;
|
2016-07-22 01:10:17 +00:00
|
|
|
if (strchr(f, 'S'))
|
2016-07-23 15:40:29 +00:00
|
|
|
m->flags |= FLAG_SEEN;
|
2016-07-22 01:10:17 +00:00
|
|
|
if (strchr(f, 'T'))
|
2016-07-23 15:40:29 +00:00
|
|
|
m->flags |= FLAG_TRASHED;
|
2016-07-22 01:10:17 +00:00
|
|
|
if (strchr(f, 'D'))
|
2016-07-23 15:40:29 +00:00
|
|
|
m->flags |= FLAG_DRAFT;
|
2016-07-22 01:10:17 +00:00
|
|
|
if (strchr(f, 'F'))
|
2016-07-23 15:40:29 +00:00
|
|
|
m->flags |= FLAG_FLAGGED;
|
|
|
|
}
|
|
|
|
|
2019-01-28 14:10:24 +00:00
|
|
|
m->fpath = fpath;
|
2016-07-23 15:40:29 +00:00
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
do_thr()
|
|
|
|
{
|
|
|
|
struct mlist *ml;
|
|
|
|
|
|
|
|
if (!thr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (ml = thr->childs; ml; ml = ml->next) {
|
|
|
|
if (!ml->m)
|
|
|
|
continue;
|
|
|
|
if ((ml->m->prune = prune) || (Tflag && thr->matched))
|
|
|
|
continue;
|
|
|
|
if (expr && eval(expr, ml->m)) {
|
|
|
|
ml->m->matched = 1;
|
|
|
|
thr->matched++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
prune = 0;
|
|
|
|
|
|
|
|
for (ml = thr->childs; ml; ml = ml->next) {
|
|
|
|
if (!ml->m)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (((Tflag && thr->matched) || ml->m->matched) && !ml->m->prune) {
|
2019-01-28 14:10:24 +00:00
|
|
|
fputs(ml->m->file, stdout);
|
2019-01-28 09:53:05 +00:00
|
|
|
putc('\n', stdout);
|
2016-07-23 15:40:29 +00:00
|
|
|
|
|
|
|
kept++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free collected mails */
|
|
|
|
if (ml->m->msg)
|
|
|
|
blaze822_free(ml->m->msg);
|
|
|
|
|
|
|
|
if (ml->m->sb)
|
|
|
|
free(ml->m->sb);
|
|
|
|
|
2019-01-28 14:10:24 +00:00
|
|
|
free(ml->m->file);
|
2016-07-23 15:40:29 +00:00
|
|
|
free(ml->m);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(thr);
|
2016-07-24 15:59:11 +00:00
|
|
|
thr = 0;
|
2016-07-23 15:40:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
collect(char *file)
|
|
|
|
{
|
|
|
|
struct mailinfo *m;
|
|
|
|
struct mlist *ml;
|
2019-01-28 14:10:24 +00:00
|
|
|
char *f;
|
2016-07-23 15:40:29 +00:00
|
|
|
|
2019-01-28 14:10:24 +00:00
|
|
|
f = xstrdup(file);
|
|
|
|
m = xcalloc(1, sizeof *m);
|
|
|
|
m = mailfile(m, f);
|
2016-07-23 15:40:29 +00:00
|
|
|
|
|
|
|
if (m->depth == 0) {
|
2019-01-28 14:10:24 +00:00
|
|
|
/* process previous thread */
|
2016-07-23 15:40:29 +00:00
|
|
|
if (thr)
|
|
|
|
do_thr();
|
|
|
|
|
|
|
|
/* new thread */
|
2019-01-28 13:45:40 +00:00
|
|
|
thr = xcalloc(1, sizeof *thr);
|
2016-07-23 15:40:29 +00:00
|
|
|
thr->matched = 0;
|
2016-07-23 16:45:23 +00:00
|
|
|
ml = thr->cur = thr->childs;
|
2016-07-23 15:40:29 +00:00
|
|
|
thr->cur->m = m;
|
|
|
|
} else {
|
2016-07-24 15:59:11 +00:00
|
|
|
ml = thr->cur + 1;
|
|
|
|
|
|
|
|
if (thr->cur->m->depth < m->depth) {
|
2017-03-30 15:41:11 +00:00
|
|
|
/* previous mail is a parent */
|
2016-07-23 16:45:23 +00:00
|
|
|
thr->cur->m->flags |= FLAG_PARENT;
|
2016-07-24 15:59:11 +00:00
|
|
|
ml->parent = thr->cur;
|
|
|
|
} else if (thr->cur->m->depth == m->depth) {
|
|
|
|
/* same depth == same parent */
|
|
|
|
ml->parent = thr->cur->parent;
|
|
|
|
} else if (thr->cur->m->depth > m->depth) {
|
|
|
|
/* find parent mail */
|
|
|
|
struct mlist *pl;
|
2017-08-31 15:30:17 +00:00
|
|
|
for (pl = thr->cur; pl->m->depth >= m->depth; pl--) ;
|
2016-07-24 15:59:11 +00:00
|
|
|
ml->parent = pl;
|
|
|
|
}
|
|
|
|
|
2016-07-23 16:45:23 +00:00
|
|
|
m->flags |= FLAG_CHILD;
|
|
|
|
|
2016-07-23 15:40:29 +00:00
|
|
|
thr->cur->next = ml;
|
|
|
|
thr->cur = ml;
|
|
|
|
ml->m = m;
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
|
|
|
|
2016-07-24 15:59:11 +00:00
|
|
|
for (ml = ml->parent; ml; ml = ml->parent)
|
|
|
|
ml->m->replies++;
|
2016-07-23 15:40:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
oneline(char *file)
|
|
|
|
{
|
2019-01-28 14:10:24 +00:00
|
|
|
struct mailinfo m = { 0 };
|
|
|
|
m.index = num++;
|
|
|
|
(void) mailfile(&m, file);
|
|
|
|
if (expr && !eval(expr, &m))
|
2016-07-22 01:10:17 +00:00
|
|
|
goto out;
|
|
|
|
|
2016-07-24 15:59:11 +00:00
|
|
|
fputs(file, stdout);
|
2019-01-28 09:53:05 +00:00
|
|
|
putc('\n', stdout);
|
|
|
|
fflush(stdout);
|
2016-07-22 01:10:17 +00:00
|
|
|
kept++;
|
|
|
|
|
|
|
|
out:
|
2019-01-28 14:10:24 +00:00
|
|
|
if (m.msg)
|
|
|
|
blaze822_free(m.msg);
|
|
|
|
if (m.sb)
|
|
|
|
free(m.sb);
|
2016-07-22 01:10:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
long i;
|
|
|
|
int c;
|
2018-09-25 13:14:43 +00:00
|
|
|
int vflag;
|
2016-07-22 01:10:17 +00:00
|
|
|
|
|
|
|
argv0 = argv[0];
|
2016-07-29 00:20:24 +00:00
|
|
|
now = time(0);
|
2017-04-27 21:48:49 +00:00
|
|
|
num = 1;
|
2018-09-25 13:14:43 +00:00
|
|
|
vflag = 0;
|
2016-07-22 01:10:17 +00:00
|
|
|
|
2020-07-30 20:10:14 +00:00
|
|
|
while ((c = getopt(argc, argv, "F:Tt:v")) != -1)
|
2016-07-22 01:10:17 +00:00
|
|
|
switch (c) {
|
2020-07-30 20:10:14 +00:00
|
|
|
case 'F':
|
|
|
|
{
|
2019-06-06 23:24:49 +00:00
|
|
|
char *s;
|
|
|
|
off_t len;
|
2020-07-30 20:10:14 +00:00
|
|
|
int r = slurp(optarg, &s, &len);
|
2019-06-06 23:24:49 +00:00
|
|
|
if (r != 0) {
|
|
|
|
fprintf(stderr, "%s: error opening file '%s': %s\n",
|
2020-07-30 20:10:14 +00:00
|
|
|
argv0, optarg, strerror(r));
|
2019-01-28 14:30:19 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
2020-07-30 20:10:14 +00:00
|
|
|
expr = chain(expr, EXPR_AND, parse_buf(optarg, s));
|
2019-06-06 23:24:49 +00:00
|
|
|
free(s);
|
2020-07-30 20:10:14 +00:00
|
|
|
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);
|
2019-01-28 14:30:19 +00:00
|
|
|
}
|
2016-07-22 01:10:17 +00:00
|
|
|
|
2020-09-06 14:56:18 +00:00
|
|
|
xpledge("stdio rpath wpath cpath proc exec", 0);
|
2019-11-21 01:15:41 +00:00
|
|
|
|
2020-07-30 20:10:14 +00:00
|
|
|
void *cb = need_thr ? collect : oneline;
|
|
|
|
if (argc == optind && isatty(0))
|
|
|
|
i = blaze822_loop1(":", cb);
|
2016-07-28 15:53:51 +00:00
|
|
|
else
|
2020-07-30 20:10:14 +00:00
|
|
|
i = blaze822_loop(argc-optind, argv+optind, cb);
|
2016-07-23 15:40:29 +00:00
|
|
|
|
|
|
|
/* print and free last thread */
|
|
|
|
if (Tflag && thr)
|
|
|
|
do_thr();
|
2016-07-22 01:10:17 +00:00
|
|
|
|
2019-01-28 13:38:05 +00:00
|
|
|
freeexpr(expr);
|
|
|
|
|
2018-09-25 13:14:43 +00:00
|
|
|
if (vflag)
|
|
|
|
fprintf(stderr, "%ld mails tested, %ld picked.\n", i, kept);
|
2019-01-28 09:53:05 +00:00
|
|
|
|
|
|
|
for (; files; files = fileq) {
|
|
|
|
fileq = files->next;
|
|
|
|
if (files->op == EXPR_REDIR_PIPE)
|
|
|
|
pclose(files->fp);
|
|
|
|
else
|
|
|
|
fclose(files->fp);
|
|
|
|
free(files);
|
|
|
|
}
|
|
|
|
|
2016-07-22 01:10:17 +00:00
|
|
|
return 0;
|
|
|
|
}
|