#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ctype.h>
char *mntpt = "/mail/fs/mbox";
typedef struct Mesg Mesg;
struct Mesg {
char* num;
char* id; /* Message-ID */
char* parentid;
int nkids;
Mesg** kids;
};
Mesg* msgs;
long nmsgs;
Mesg** threads;
long nthreads;
void*
emalloc(uint n)
{
void *p;
p = malloc(n);
if(p == nil)
sysfatal("can't malloc: %r");
memset(p, 0, n);
setmalloctag(p, getcallerpc(&n));
return p;
}
void*
erealloc(void *p, uint n)
{
p = realloc(p, n);
if(p == nil)
sysfatal("can't realloc: %r");
setmalloctag(p, getcallerpc(&n));
return p;
}
char*
estrdup(char *s)
{
char *t;
t = emalloc(strlen(s)+1);
strcpy(t, s);
return t;
}
char*
digest(char *id)
{
char *s, *d;
s = nil;
d = strchr(id, '<');
if(d){
s = strchr(++d, '>');
if(s)
*s = 0;
}
if(d == nil || s == nil)
d = id;
return d;
}
char*
field(char *num, char *left)
{
char path[32], buf[8192];
char *right;
int fd, n;
snprint(path, sizeof path, "%s/%s/%s", mntpt, num, left);
fd = open(path, OREAD);
if(fd < 0)
sysfatal("opening file %s: %r", path);
n = read(fd, buf, sizeof buf);
if(n < 0)
sysfatal("reading %s: %r", path);
buf[n] = 0;
right = estrdup(buf);
close(fd);
return right;
}
Mesg*
newmsg(Mesg *m, char *num)
{
char *s, *t, *q;
Biobuf *b;
char hp[100];
m->num = estrdup(num);
s = field(num, "messageid");
m->id = estrdup(digest(s));
free(s);
s = field(num, "inreplyto");
m->parentid = estrdup(digest(s));
free(s);
/*
* If In-Reply-To doesn't exist, try References. The last
* <...> part is the parent id.
*/
if(m->parentid[0] == 0){
snprint(hp, sizeof hp, "%s/%s/rawheader", mntpt, num);
b = Bopen(hp, OREAD);
if(b == nil)
sysfatal("Bopen %s: %r", hp);
while((s = Brdline(b, '\n')) != nil){
s[Blinelen(b)-1] = 0;
if(strncmp(s, "References:", 11) != 0)
continue;
t = strrchr(s, '<');
if(t == nil)
break;
t++;
q = strchr(t, '>');
if(q == nil)
break;
*q = 0;
free(m->parentid);
m->parentid = estrdup(t);
}
Bterm(b);
}
m->kids = nil;
m->nkids = 0;
return m;
}
void
cleanup(void)
{
int i;
for(i = 0; i < nmsgs; i++){
free(msgs[i].num);
free(msgs[i].id);
free(msgs[i].parentid);
free(msgs[i].kids);
}
free(msgs);
free(threads);
}
Mesg*
findparent(Mesg *c)
{
int i;
char *pid, *mid;
Mesg *m, *p;
p = nil;
pid = c->parentid;
for(i = 0; i < nmsgs; i++){
m = &msgs[i];
mid = m->id;
if(c != m && strcmp(mid, pid) == 0){
p = m;
p->kids = erealloc(p->kids, (p->nkids+1)*sizeof(Mesg*));
p->kids[p->nkids++] = c;
break;
}
}
return p;
}
void
printthreads(Mesg **ms, int len, int depth)
{
int i;
char *subj, *from, ind[100];
strcpy(ind, "");
if(depth < nelem(ind)-1){
memset(ind, '\t', depth);
ind[depth] = 0;
}
for(i = 0; i < len; i++){
subj = field(ms[i]->num, "subject");
from = field(ms[i]->num, "from");
if(depth == 0)
print("%s%s/ %s %s\n", ind, ms[i]->num, from, subj);
else
print("%s%s/ %s\n", ind, ms[i]->num, from);
printthreads(ms[i]->kids, ms[i]->nkids, depth+1);
}
}
void
usage(void)
{
fprint(2, "usage: upas/threads [upasmntpt]\n");
exits("usage");
}
void
main(int argc, char *argv[])
{
int fd;
long i, n;
Dir *ms;
Mesg *p;
ARGBEGIN {
default:
usage();
} ARGEND
if(argc > 0)
mntpt = argv[0];
fd = open(mntpt, OREAD);
if(fd < 0)
sysfatal("opening %s: %r", mntpt);
n = dirreadall(fd, &ms);
msgs = emalloc(n*sizeof(Mesg));
for(i = 0; i < n; i++)
if(isdigit(ms[i].name[0])){
newmsg(&msgs[nmsgs], ms[i].name);
nmsgs++;
}
close(fd);
/* nthreads <= nmesg */
threads = emalloc(nmsgs*sizeof(Mesg*));
for(i = 0; i < nmsgs; i++){
p = findparent(&msgs[i]);
if(p == nil)
threads[nthreads++] = &msgs[i];
}
printthreads(threads, nthreads, 0);
cleanup();
exits(nil);
}
|