Plan 9 from Bell Labs’s /usr/web/sources/contrib/nemo/root/sys/src/cmd/mail2fs/msgs.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ctype.h>
#include "util.h"

/*
 * Generate a list of msgs from a plan b mail box.
 * This can be used either to read mail by running the
 * program in the mail box directory or to generate
 * indexes for msgs
 */

#define dprint	if(debug)fprint

int showarch;
int showspam;
char** months;	// show only these dirs
char* showrunes;
int nmonths;
Biobuf	bout;
Biobuf	blist;
int debug;


char*
readf(char*f)
{
	static char	buf[1024];
	char	err[ERRMAX];
	Dir*	d;
	int	fd;
	long	n;

	fd = open(f, OREAD);
	if(fd < 0)
		return nil;
	d = dirfstat(fd);
	if(d == nil)
		goto fail;
	n = d->length;
	if(n == 0 || n > sizeof buf - 1)
		n = sizeof buf - 1;
	if(n == 0)
		n = read(fd, buf, n);
	else
		n = readn(fd, buf, n);
	if(n < 0)
		goto fail;
	buf[n] = 0;
	free(d);
	close(fd);
	return buf;
fail:
	rerrstr(err, sizeof(err));
	free(d);
	close(fd);
	werrstr(err);
	return nil;
}

char*
gethdr(char** hdrs, char* h)
{
	int	l;

	l = strlen(h);
	while(*hdrs){
		if(cistrncmp(*hdrs, h, l) == 0)
			return *hdrs + l + 1;
		hdrs++;
	}
	return "<none>";
}


char*
cleanfrom(char* f)
{
	char *c, *e;
	c = strdup(f);
	e = strchr(c, '@');
	if(!e)
		return nil;
	else
		*e = '\0';
	return c;
}

void
hdrline(char*fn, char* buf, char* flags)
{
	char*	hdrs[20+1];
	int	nhdrs;
	char*	f;
	char*	cf;
	char*	s;
	Dir*	de;
	int	n;
	int	fd;
	int	i;
	int	l;

	s = buf;
	for(i = 0; s && i < 20; i++){
		f = utfrune(s, '\n');
		if(f)
			*f++ = 0;
		hdrs[i] = s;
		s = f;
	}
	nhdrs = i;
	hdrs[nhdrs] = nil;

	f = gethdr(hdrs, "from");
	s = gethdr(hdrs, "subject");
	cf = cleanfrom(f);
	l = strlen(s);
	if(l > 48)
		l = 48;
	Bprint(&bout, "%s%-19.19s %-12.12s  %-*.*s\n", flags, fn, cf?cf:f, l, l, s);
	free(cf);
	fd = open(fn, OREAD);
	n = dirreadall(fd, &de);
	close(fd);
	if(n > 1)
		Bprint(&bout, "\t");
	for(i = 0; i < n; i++)
		if(strcmp(de[i].name, "text") != 0)
			Bprint(&bout, "\t%s/%s\n", fn, de[i].name);
}

int
mustshow(char* name)
{
	Rune r;
	int nc;

	if(isdigit(name[0]))
		return 1;
	if(showarch && name[0] == 'a' && name[1] == '.')
		return 1;
	if(showrunes != nil){
		nc = chartorune(&r, name);
		if(utfrune(showrunes, r) != nil && name[nc] == '.')
			return 1;
	}
	return 0;
}

static int
cmpent(void* a1, void* a2)
{
	Dir*	d1 = a1;
	Dir*	d2 = a2;
	int	n1, n2;

	n1 = atoi(mailnamenb(d1->name));
	n2 = atoi(mailnamenb(d2->name));
	return n2 - n1;
}

static void
attachline(char* msg, char* rel)
{
	Dir*	d;
	int	nd;
	int	fd;
	int	i;

	fd = open(msg, OREAD);
	if(fd < 0)
		return;
	nd = dirreadall(fd, &d);
	close(fd);
	for(i = 0; i < nd; i++)
		if(strcmp(d[i].name, "text") && strcmp(d[i].name, "raw") && strcmp(d[i].name, "L.mbox"))
		if(d[i].qid.type&QTDIR)
			Bprint(&bout, "\t%s/%s/text\n", rel, d[i].name);
		else
			Bprint(&bout, "\t%s/%s\n", rel, d[i].name);
	if(nd > 0)
		free(d);
}

static int
listf(char* file, int mayarchive)
{
	Biobuf*	bin;
	char*	ln;
	char*	s;
	char*	e;
	int	l;
	int	show;
	int	some;
	int	allarchived;
	Dir*	d;

	bin = Bopen(file, OREAD);
	if(bin == nil)
		return 0;
	show = some = 0;
	dprint(2, "%s: listing [file] %s\n", argv0, file);
	allarchived = 1;
	while((ln = Brdline(bin, '\n')) != nil){
		l = Blinelen(bin);
		if(l < 2)
			continue;
		ln[l-1] = 0;
		if(ln[0] >= '0' && ln[0] <= '9'){
			s = strchr(ln, '/');
			if(s != nil){
				s++;
				if(s[0] >= '0' && s[0] <= '9')
					allarchived = 0;
				show = mustshow(s);
				if(show){
					e = utfrune(s, ' ');
					if(e != nil)
						*e = 0;
					Bprint(&blist, "%s\n", s);
					if(e != nil)
						*e = ' ';
				}
			}
		}
		if(show != 0){
			some++;
			Bprint(&bout, "%s\n", ln);
		}
	}
	Bterm(bin);
	if(mayarchive && allarchived){
		/* move archive from 200901.l to 200901.la; skip it next time */
		dprint(2, "%s: %s -> %sa\n", argv0, file, file);
		d = dirstat(file);
		if(d != nil){
			s = smprint("%sa", d->name);
			if(s != nil){
				nulldir(d);
				d->name = s;
				dirwstat(file, d);
				free(s);
			}
			free(d);
		}
	}
	return some;
}

/*
 * list msgs in dir. If a file named `dir.l' or `dir.la' exists and is up to date
 * it is considered a listing of dir and used as a cache.
 */
int
list(char* dir)
{
	Dir*	d;
	Dir*	dd;
	int	n;
	int	fd;
	int	i; 
	char*	tf;
	char*	sf;
	int	some;
	char*	suf;
	char*	buf;
	char*	flags;
	char*	list;
	int	archivedlist;

	fd = open(dir, OREAD);
	if(fd < 0){
		fprint(2, "%s: %r\n", dir);
		return 0;
	}
	list = smprint("%s.l", dir);
	d = dirstat(list);
	dd = dirfstat(fd);
	archivedlist = 0;
	if(d == nil){
		free(list);
		list = smprint("%s.la", dir);
		d = dirstat(list);
		archivedlist = 1;
	}
	if(d != nil && dd != nil && d->length > 0 && dd->mtime < d->mtime){
		free(d);
		free(dd);
		close(fd);
		if(archivedlist && showarch == 0  && showrunes == nil)
			some = 0;
		else
			some = listf(list, archivedlist == 0);
		free(list);
		return some;
	}
	free(d);
	free(dd);
	dprint(2, "%s: listing %s\n", argv0, dir);
	n = dirreadall(fd, &d);
	close(fd);
	if(n <= 0)
		return 0;
	some = 0;
	qsort(d, n, sizeof(Dir), cmpent);
	for(i = 0; i < n; i++)
		if(mustshow(d[i].name)){
			dprint(2, "%s: listing %s/%s\n", argv0, dir, d[i].name);
			flags = "";
			some++;
			tf = smprint("%s/%s/text", dir, d[i].name);
			buf = readf(tf);
			sf = tf + strlen(dir) - 1;
			for(suf = sf; *suf != '/' && suf > tf; suf--)
				;
			suf++;
			hdrline(suf, buf, flags);
			free(tf);
			Bprint(&blist, "%s/%s\n", dir, d[i].name);
			tf = smprint("%s/%s", dir, d[i].name);
			sf = tf + strlen(dir) - 1;
			for(suf = sf; *suf != '/' && suf > tf; suf--)
				;
			suf++;
			attachline(tf, suf);
			free(tf);
		}
	free(d);
	return some;
}

int
member(char* s, char** list, int nlist)
{
	int	i;

	for(i = 0; i < nlist; i++)
		if(strcmp(s, list[i]) == 0)
			return 1;
	return 0;
}

int
listmbox(char* mbox)
{
	Dir*	d;
	int	fd;
	int	i;
	int	n;
	int	some;
	char*	dir;

	dprint(2, "%s: reading mbox...", argv0);
	fd = open(mbox, OREAD);
	if(fd < 0){
		fprint(2, "%s: %r\n", mbox);
		return -1;
	}
	n = dirreadall(fd, &d);
	close(fd);
	dprint(2, "done\n");
	if(n <= 0)
		return 0;
	some = 0;
	qsort(d, n, sizeof(Dir), cmpent);
	for(i = 0; i < n; i++)
		if(d[i].qid.type&QTDIR){
			if(nmonths > 0)
				if(!member(d[i].name, months, nmonths))
					continue;
			dir=smprint("%s/%s", mbox, d[i].name);
			some |= list(dir);
			free(dir);
		}
	free(d);
	return some;
}

void
usage(void)
{
	fprint(2, "usage: %s [-aD] [-s runes] [dir] [month...]\n", argv0);
	exits("usage");
}

void
main(int argc, char*argv[])
{
	char*	dir;
	char*	top;
	int	fd;
	char*	list;

	top = smprint("/mail/box/%s", getuser());
	ARGBEGIN{
	case 'D':
		debug++;
		break;
	case 'a':
		showarch++;
		break;
	case 's':
		showrunes = EARGF(usage());
		break;
	default:
		usage();
	}ARGEND;
	if(argc == 1)
		dir = cleanpath(argv[0], top);
	else{
		months = argv;
		nmonths= argc;
		dir = smprint("%s/msgs", top);
	}
	list = smprint("/tmp/msgs.%s", getuser());
	fd = create(list, OWRITE, 0640);
	if(fd < 0)
		sysfatal("%s: %r\n", list);
	free(list);
	Binit(&blist, fd, OWRITE);
	Binit(&bout, 1, OWRITE);
	if(!listmbox(dir))
		Bprint(&bout, "No mail\n");
	Bterm(&bout);
	Bterm(&blist);
	exits(nil);
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.