shithub: front

Download patch

ref: 8e2a071b8bcd809467411005618145918111acdd
parent: af83b606f9b423c196a296f55ba6d67c5795cfa5
author: Ori Bernstein <ori@eigenstate.org>
date: Mon Dec 9 00:27:05 EST 2024

acme/Mail: fix redrawn line offsets, add support for flag filters

maintaining ->nsub was fragile, and didn't save very many cycles;
instead just compute it every time; it's only going to hurt with
a ton of giant threads.

--- a/sys/man/1/acmemail
+++ b/sys/man/1/acmemail
@@ -101,11 +101,17 @@
 .IR upasfs (4)
 .PD 0
 .TP
-.B Filter [pattern]
+.B Filter [filter] [*flags]
 Shows only messages where the sender or subject match
 the
-.I pattern
-regexp.
+.I filter
+regexp,
+or where the
+.I flags
+string matches the state of the message. The flags
+used are the same as for Mark, with the addition of
+.I u
+to represent unread messages.
 Filter without an argument resets the filtering,
 showing all messages again.
 .PD 0
@@ -127,15 +133,12 @@
 .TP
 .B Mark
 As with the message view, but applied to the open message.
-
 .PP
 The following text commands are recognized by the composition
 window:
-
 .TP
 .B Post
 Sends the message currently being composed.
-
 .SS Format strings
 The formatting of messages in the list view is controlled by the
 format string defined through the
--- a/sys/src/cmd/upas/Mail/mail.h
+++ b/sys/src/cmd/upas/Mail/mail.h
@@ -16,6 +16,7 @@
 	Stoplev	= 1<<1,	/* not a response to anything */
 	Sopen	= 1<<2,	/* opened for viewing */
 	Szap	= 1<<3, /* flushed, to be removed from list */
+	Shide	= 1<<4, /* hidden from view */
 };
 
 enum {
@@ -95,7 +96,6 @@
 	Mesg	*parent;
 	Mesg	**child;
 	int	nchild;
-	int	nsub;	/* transitive children */
 	
 	Mesg	*body;	/* best attachment to use, or nil */
 	Mesg	**parts;
--- a/sys/src/cmd/upas/Mail/mbox.c
+++ b/sys/src/cmd/upas/Mail/mbox.c
@@ -32,6 +32,7 @@
 
 Reprog	*mesgpat;
 Reprog	*filterpat;
+char	*filterflags;
 
 int	threadsort = 1;
 int	sender;
@@ -126,6 +127,45 @@
 }
 
 static int
+matchfilter(Mesg *m)
+{
+	char *p;
+	int ok;
+
+	ok = 1;
+	if(filterpat != nil
+	&& m->subject != nil
+	&& m->from != nil){
+		if(!regexec(filterpat, m->subject, nil, 0)
+		&& !regexec(filterpat, m->from, nil, 0))
+			ok = 0;
+	}
+	for(p = filterflags; p && *p; p++){
+		switch(*p){
+		case 's':	ok = ok && (m->flags & Fseen);	break;
+		case 'u':	ok = ok && !(m->flags & Fseen);	break;
+		case 'a':	ok = ok && (m->flags & Fresp);	break;
+		}
+	}
+	return ok;
+}
+
+static int
+nsub(Mesg *m)
+{
+	Mesg *c;
+	int n, i;
+
+	n = 0;
+	for(i = 0; i < m->nchild; i++){
+		c = m->child[i];
+		if(!(c->state & (Sdummy|Shide)))
+			n += nsub(c)+1;
+	}
+	return n;
+}
+
+static int
 mesglineno(Mesg *msg, int *depth)
 {
 	Mesg *p, *m;
@@ -143,9 +183,9 @@
 		for(i = 0; i < p->nchild; i++){
 			if(p->child[i] == m)
 				break;
-			o += p->child[i]->nsub + 1;
+			o += nsub(p->child[i]) + 1;
 		}
-		if(!(p->state & (Sdummy|Szap))){
+		if(!(p->state & (Sdummy|Shide))){
 			o++;
 			d++;
 		}
@@ -157,8 +197,8 @@
 		if(m == p)
 			break;
 		if(m->state & Stoplev){
-			n += mbox.mesg[i]->nsub;
-			if(!(m->state & (Sdummy|Szap)))
+			n += nsub(mbox.mesg[i]);
+			if(!(m->state & (Sdummy|Szap|Shide)))
 				n++;
 		}
 
@@ -170,7 +210,7 @@
 }
 
 static int
-addchild(Mesg *p, Mesg *m, int d)
+addchild(Mesg *p, Mesg *m)
 {
 	Mesg *q;
 
@@ -182,8 +222,6 @@
 		if(m->time > q->time)
 			q->time = m->time;
 	}
-	for(q = p; q != nil; q = q->parent)
-		q->nsub += d;
 	p->child = erealloc(p->child, ++p->nchild*sizeof(Mesg*));
 	p->child[p->nchild - 1] = m;
 	qsort(p->child, p->nchild, sizeof(Mesg*), rcmpmesg);
@@ -347,7 +385,6 @@
 load(char *name, char *digest, int ins)
 {
 	Mesg *m, *p;
-	int d;
 
 	if(strncmp(name, mbox.path, strlen(mbox.path)) == 0)
 		name += strlen(mbox.path);
@@ -357,13 +394,10 @@
 	if(digest != nil && strcmp(digest, m->digest) != 0)
 		goto error;
 	/* if we already have a dummy, populate it */
-	d = 1;
 	p = lookupid(m->messageid);
 	if(p != nil && (p->state & Sdummy)){
-		d = p->nsub + 1;
 		m->child = p->child;
 		m->nchild = p->nchild;
-		m->nsub = p->nsub;
 		mesgclear(p);
 		memcpy(p, m, sizeof(*p));
 		free(m);
@@ -387,8 +421,10 @@
 	p = lookupid(m->inreplyto);
 	if(p == nil)
 		p = placeholder(m->inreplyto, m->time, ins);
-	if(!addchild(p, m, d))
+	if(!addchild(p, m))
 		m->state |= Stoplev;
+	if(!matchfilter(m))
+		m->state |= Shide;
 	return m;
 error:
 	mesgfree(m);
@@ -576,22 +612,12 @@
 	Bputc(bp, '\n');
 }
 
-static int
-matchfilter(Mesg *m)
-{
-	if(filterpat == nil
-	|| regexec(filterpat, m->subject, nil, 0)
-	|| regexec(filterpat, m->from, nil, 0))
-		return 1;
-	return 0;
-}
-
 static void
 showmesg(Biobuf *bfd, Mesg *m, int depth, int recurse)
 {
 	int i;
 
-	if(!(m->state & Sdummy) && matchfilter(m)){
+	if(!(m->state & (Sdummy|Shide))){
 		fmtmesg(bfd, listfmt, m, depth);
 		depth++;
 	}
@@ -664,7 +690,7 @@
 static void
 relinkmsg(Mesg *p, Mesg *m)
 {
-	Mesg *c, *pp;
+	Mesg *c;
 	int i, j;
 
 	/* remove child, preserving order */
@@ -674,14 +700,12 @@
 			p->child[j++] = p->child[i];
 	}
 	p->nchild = j;
-	for(pp = p; pp != nil; pp = pp->parent)
-		pp->nsub -= m->nsub + 1;
 
 	/* reparent children */
 	for(i = 0; i < m->nchild; i++){
 		c = m->child[i];
 		c->parent = nil;
-		addchild(p, c, c->nsub + 1);
+		addchild(p, c);
 	}
 }
 
@@ -704,7 +728,7 @@
 			continue;
 
 		ln = mesglineno(m, nil);
-		fprint(mbox.addr, "%d,%d", ln, ln+m->nsub);
+		fprint(mbox.addr, "%d,%d", ln, ln+nsub(m));
 		write(mbox.data, "", 0);
 		if(m->flags & Ftodel)
 			fprint(fd, "delete %s %d", mailbox, atoi(m->name));
@@ -711,10 +735,8 @@
 
 		removeid(m);
 		m->state |= Szap;
-		if(p == nil && m->nsub != 0){
+		if(p == nil && nsub(m) != 0)
 			p = placeholder(m->messageid, m->time, 1);
-			p->nsub = m->nsub + 1;
-		}
 		if(p != nil)
 			relinkmsg(p, m);
 		for(j = 0; j < m->nchild; j++)
@@ -824,10 +846,10 @@
 		for(r = m; r->parent != nil; r = r->parent)
 			/* nothing */;
 		/* Bump whole thread up in list */
-		if(r->nsub > 0){
+		if(nsub(r) > 0){
 			ln = mesglineno(r, nil);
-			nr = r->nsub-1;
-			if(!(r->state & Sdummy))
+			nr = nsub(r)-1;
+			if(!(r->state & (Sdummy|Shide)))
 				nr++;
 			/*
 			 * We can end up with an empty container
@@ -881,18 +903,38 @@
 static void
 filter(char **filt, int nfilt)
 {
-	if(nfilt > 1){
-		fprint(2, "filter: only one argument supported");
+	Mesg *m;
+	int i;
+
+	if(nfilt > 2){
+Usage:
+		fprint(2, "usage: Filter [regexp] [*flags]");
 		return;
 	}
 	free(filterpat);
+	free(filterflags);
 	filterpat = nil;
-	if(nfilt == 1){
-		filterpat = regcomp(filt[0]);
-		if(filterpat == nil){
-			fprint(2, "Filter: %r");
-			return;
+	filterflags = nil;
+	for(i = 0; i < nfilt; i++){
+		if(*filt[i] == '*'){
+			if(filterflags != nil)
+				goto Usage;
+			filterflags = strdup(filt[i]+1);
+		}else{
+			if(filterpat != nil)
+				goto Usage;
+			filterpat = regcomp(filt[i]);
+			if(filterpat == nil){
+				fprint(2, "recomp: %r");
+				return;
+			}
 		}
+	}
+	for(i = 0; i < mbox.nmesg; i++){
+		m = mbox.mesg[i];
+		m->state &= ~Shide;
+		if(!matchfilter(m))
+			m->state |= Shide;
 	}
 	fprint(mbox.addr, ",");
 	showlist();
--