about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDuncaen <mail@duncano.de>2019-01-29 14:07:55 +0100
committerLeah Neukirchen <chneukirchen@gmail.com>2020-05-15 18:41:13 +0200
commit8ae4801dd4f98a6d946b3fddce4f4078015068cd (patch)
tree2b96c67868d909fbd5a7c06b44ea4694c5ecb63f
parent32f0fcbc0d69e3dc1ee6d635ec64fcbdbfc0d132 (diff)
downloadmblaze-8ae4801dd4f98a6d946b3fddce4f4078015068cd.tar.gz
mblaze-8ae4801dd4f98a6d946b3fddce4f4078015068cd.tar.xz
mblaze-8ae4801dd4f98a6d946b3fddce4f4078015068cd.zip
mpick: add header decodeop for better address matching
-rw-r--r--man/mpick.110
-rw-r--r--mpick.c176
-rwxr-xr-xt/2000-mpick.t4
3 files changed, 115 insertions, 75 deletions
diff --git a/man/mpick.1 b/man/mpick.1
index 53d5120..bb7d324 100644
--- a/man/mpick.1
+++ b/man/mpick.1
@@ -103,6 +103,7 @@ tests are given by the following EBNF:
              | <flagprop>
              | <timeprop> <numop> <dur>
              | <numprop> <numop> <num>
+             | <hdrprop> <decodeop> <strop> <str>
              | <strprop> <strop> <str>
              | prune             -- do not match further messages in thread
              | print             -- always true value
@@ -139,8 +140,13 @@ tests are given by the following EBNF:
                       | T )?     -- *1024*1024*1024*1024
              | cur               -- index of cur message
 
-<strprop>  ::= from | subject | to
-             | <str>             -- header name
+<hdrprop>  ::= from | to | subject | <str>
+
+<decodeop> ::= . addr          -- match address parts
+             | . disp          -- match address display parts
+             |                 -- empty matches raw headers
+
+<strprop>  ::= path
 
 <strop>    ::= == | = | !=       -- string (in)equality
              | ===    | !===     -- case insensitive string (in)equality
diff --git a/mpick.c b/mpick.c
index 3dc6660..bff90b8 100644
--- a/mpick.c
+++ b/mpick.c
@@ -80,11 +80,12 @@ enum prop {
 	PROP_REPLIES,
 	PROP_SIZE,
 	PROP_TOTAL,
-	PROP_FROM,
-	PROP_TO,
 	PROP_INDEX,
 	PROP_DATE,
 	PROP_FLAG,
+	PROP_HDR,
+	PROP_HDR_ADDR,
+	PROP_HDR_DISP,
 };
 
 enum flags {
@@ -332,16 +333,17 @@ freeexpr(struct expr *e)
 	case EXPR_REGEX:
 	case EXPR_REGEXI:
 		switch (e->a.prop) {
-		case PROP_PATH:
-		case PROP_FROM:
-		case PROP_TO:
-			break;
-		default: free(e->a.string);
+		case PROP_PATH: break;
+		case PROP_HDR:
+		case PROP_HDR_ADDR:
+		case PROP_HDR_DISP:
+			free(e->b.string);
+		default: return;
 		}
 		if (e->op == EXPR_REGEX || e->op == EXPR_REGEXI)
-			regfree(e->b.regex);
+			regfree(e->c.regex);
 		else
-			free(e->b.string);
+			free(e->c.string);
 	}
 	free(e);
 }
@@ -579,14 +581,25 @@ parse_strcmp()
 	negate = 0;
 
 	if (token("from"))
-		prop = PROP_FROM;
+		h = xstrdup("from");
 	else if (token("to"))
-		prop = PROP_TO;
+		h = xstrdup("to");
 	else if (token("subject"))
-		h = "subject";
+		h = xstrdup("subject");
+	else if (token("path"))
+		prop = PROP_PATH;
 	else if (!parse_string(&h))
 		return parse_inner();
 
+	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;
+	}
+
 	if (token("~~~"))
 		op = EXPR_GLOBI;
 	else if (token("~~"))
@@ -624,35 +637,29 @@ parse_strcmp()
 
 	int r = 0;
 	struct expr *e = mkexpr(op);
-
-	if (prop)
-		e->a.prop = prop;
-	else
-		e->a.string = h;
-
-	if (prop == PROP_FROM || prop == PROP_TO) {
-		char *disp, *addr;
-		blaze822_addr(s, &disp, &addr);
-		if (!disp && !addr)
-			parse_error_at(NULL, "invalid address");
-		free(s);
-		s = xstrdup((disp) ? disp : addr);
-		e->extra = (disp) ? 0 : 1;
+	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;
 	}
 
 	if (op == EXPR_REGEX || op == EXPR_REGEXI) {
-		e->b.regex = malloc(sizeof (regex_t));
-		r = regcomp(e->b.regex, s, REG_EXTENDED | REG_NOSUB |
+		e->c.regex = malloc(sizeof (regex_t));
+		r = regcomp(e->c.regex, s, REG_EXTENDED | REG_NOSUB |
 		    (op == EXPR_REGEXI ? REG_ICASE : 0));
 		if (r != 0) {
 			char msg[256];
-			regerror(r, e->b.regex, msg, sizeof msg);
+			regerror(r, e->c.regex, msg, sizeof msg);
 			parse_error("invalid regex '%s': %s", s, msg);
 			exit(2);
 		}
 		free(s);
 	} else {
-		e->b.string = s;
+		e->c.string = s;
 	}
 
 	if (negate) {
@@ -996,12 +1003,13 @@ parse_msglist(char *s)
 	case '/':
 		s++;
 		e1 = mkexpr(EXPR_REGEXI);
-		e1->a.string = xstrdup("subject");
-		e1->b.regex = malloc(sizeof (regex_t));
-		r = regcomp(e1->b.regex, s, REG_EXTENDED | REG_NOSUB | REG_ICASE);
+		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->b.regex, msg, sizeof msg);
+			regerror(r, e1->c.regex, msg, sizeof msg);
 			parse_error("invalid regex '%s': %s", s, msg);
 		}
 		return e1;
@@ -1067,14 +1075,14 @@ parse_msglist(char *s)
 			d = (disp) ? disp : addr;
 
 			e1 = mkexpr(EXPR_REGEXI);
-			e1->a.prop = PROP_FROM;
-			e1->b.regex = malloc(sizeof (regex_t));
-			e1->extra = (disp) ? 0 : 1;
+			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->b.regex, d, REG_EXTENDED | REG_NOSUB | REG_ICASE);
+			r = regcomp(e1->c.regex, d, REG_EXTENDED | REG_NOSUB | REG_ICASE);
 			if (r != 0) {
 				char msg[256];
-				regerror(r, e1->b.regex, msg, sizeof msg);
+				regerror(r, e1->c.regex, msg, sizeof msg);
 				parse_error("invalid regex '%s': %s", d, msg);
 			}
 
@@ -1106,54 +1114,64 @@ msg_date(struct mailinfo *m)
 }
 
 char *
-msg_hdr(struct mailinfo *m, const char *h)
+msg_hdr(char **s, const char *h, struct mailinfo *m)
 {
 	static char hdrbuf[4096];
 
 	if (!m->msg && m->fpath) {
 		if (!(m->msg = blaze822(m->fpath))) {
 			m->fpath = NULL;
-			*hdrbuf = 0;
-			return hdrbuf;
+			return NULL;
 		}
 	}
 
+	// XXX: only return one header for now
+	if (*s)
+		return NULL;
+
 	char *b;
-	if (!m->msg || !(b = blaze822_chdr(m->msg, h))) {
-		*hdrbuf = 0;
-		return hdrbuf;
-	}
+	if (!m->msg || !(b = blaze822_chdr(m->msg, h)))
+		return NULL;
+	*s = b;
 
 	blaze822_decode_rfc2047(hdrbuf, b, sizeof hdrbuf - 1, "UTF-8");
 	return hdrbuf;
 }
 
 char *
-msg_addr(struct mailinfo *m, char *h, int t)
+msg_hdr_addr(char **s, const char *h, struct mailinfo *m, int rdisp)
 {
 	if (!m->msg && m->fpath) {
 		if (!(m->msg = blaze822(m->fpath))) {
 			m->fpath = NULL;
-			return "";
+			return NULL;
 		}
 	}
 
-	char *b;
-	if (m->msg == 0 || (b = blaze822_chdr(m->msg, h)) == 0)
-		return "";
+	char *b = *s;
+	if (!b) {
+		if (!m->msg || !(b = blaze822_chdr(m->msg, h)))
+			return NULL;
+	}
 
 	char *disp, *addr;
-	blaze822_addr(b, &disp, &addr);
+	*s = blaze822_addr(b, &disp, &addr);
 
-	if (t) {
-		if (!addr)
-			return "";
-		return addr;
-	} else {
-		if (!disp)
-			return "";
+	if (rdisp)
 		return disp;
-	}
+	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);
 }
 
 FILE *
@@ -1283,21 +1301,34 @@ eval(struct expr *e, struct mailinfo *m)
 	case EXPR_GLOBI:
 	case EXPR_REGEX:
 	case EXPR_REGEXI: {
-		const char *s;
+		const char *s = NULL;
+		char *p = NULL;
+		char *(*fn)(char **, const char *, struct mailinfo *) = 0;
+		int rv = 0;
 		switch (e->a.prop) {
+		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;
 		case PROP_PATH: s = m->fpath ? m->fpath : ""; break;
-		case PROP_FROM: s = msg_addr(m, "from", e->extra); break;
-		case PROP_TO: s = msg_addr(m, "to", e->extra); break;
-		default: s = msg_hdr(m, e->a.string); break;
+		default: return 0;
 		}
-		switch (e->op) {
-		case EXPR_STREQ: return strcmp(e->b.string, s) == 0;
-		case EXPR_STREQI: return strcasecmp(e->b.string, s) == 0;
-		case EXPR_GLOB: return fnmatch(e->b.string, s, 0) == 0;
-		case EXPR_GLOBI: return fnmatch(e->b.string, s, FNM_CASEFOLD) == 0;
-		case EXPR_REGEX:
-		case EXPR_REGEXI: return regexec(e->b.regex, s, 0, 0, 0) == 0;
+		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;
 		}
+		return 0;
 	}
 	}
 	return 0;
@@ -1504,8 +1535,9 @@ main(int argc, char *argv[])
 
 	if (optind != argc) {
 		for (c = optind; c < argc; c++) {
-			if (strchr(argv[c], '/') && access(argv[c], R_OK) == 0)
+			if (strchr(argv[c], '/') && access(argv[c], R_OK) == 0) {
 				break;
+			}
 			expr = chain(expr, EXPR_AND, parse_msglist(argv[c]));
 		}
 
diff --git a/t/2000-mpick.t b/t/2000-mpick.t
index 8e32aec..9af444d 100755
--- a/t/2000-mpick.t
+++ b/t/2000-mpick.t
@@ -1,7 +1,7 @@
 #!/bin/sh -e
 cd ${0%/*}
 . ./lib.sh
-plan 13
+plan 15
 
 rm -rf test.dir
 mkdir test.dir
@@ -66,5 +66,7 @@ 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_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'
+check_test 'addr decode disp' -eq 2 'mlist inbox | mpick -t "from.disp == \"Peter Example\"" | wc -l'
 
 )