xref: /plan9/sys/src/cmd/upas/ned/nedmail.c (revision a50f7bb8dde95766610303b107f5ae46620df42d)
17dd7cddfSDavid du Colombier #include "common.h"
27dd7cddfSDavid du Colombier #include <ctype.h>
37dd7cddfSDavid du Colombier #include <plumb.h>
47dd7cddfSDavid du Colombier 
57dd7cddfSDavid du Colombier typedef struct Message Message;
67dd7cddfSDavid du Colombier typedef struct Ctype Ctype;
77dd7cddfSDavid du Colombier typedef struct Cmd Cmd;
87dd7cddfSDavid du Colombier 
99a747e4fSDavid du Colombier char	root[Pathlen];
109a747e4fSDavid du Colombier char	mbname[Elemlen];
117dd7cddfSDavid du Colombier int	rootlen;
1280ee5cbfSDavid du Colombier int	didopen;
137dd7cddfSDavid du Colombier char	*user;
147dd7cddfSDavid du Colombier char	wd[2048];
157dd7cddfSDavid du Colombier String	*mbpath;
169a747e4fSDavid du Colombier int	natural;
17dc5a79c1SDavid du Colombier int	doflush;
187dd7cddfSDavid du Colombier 
197dd7cddfSDavid du Colombier int interrupted;
207dd7cddfSDavid du Colombier 
217dd7cddfSDavid du Colombier struct Message {
227dd7cddfSDavid du Colombier 	Message	*next;
237dd7cddfSDavid du Colombier 	Message	*prev;
247dd7cddfSDavid du Colombier 	Message	*cmd;
257dd7cddfSDavid du Colombier 	Message	*child;
267dd7cddfSDavid du Colombier 	Message	*parent;
277dd7cddfSDavid du Colombier 	String	*path;
287dd7cddfSDavid du Colombier 	int	id;
297dd7cddfSDavid du Colombier 	int	len;
307dd7cddfSDavid du Colombier 	int	fileno;	// number of directory
317dd7cddfSDavid du Colombier 	String	*info;
327dd7cddfSDavid du Colombier 	char	*from;
337dd7cddfSDavid du Colombier 	char	*to;
347dd7cddfSDavid du Colombier 	char	*cc;
357dd7cddfSDavid du Colombier 	char	*replyto;
367dd7cddfSDavid du Colombier 	char	*date;
377dd7cddfSDavid du Colombier 	char	*subject;
387dd7cddfSDavid du Colombier 	char	*type;
397dd7cddfSDavid du Colombier 	char	*disposition;
407dd7cddfSDavid du Colombier 	char	*filename;
417dd7cddfSDavid du Colombier 	char	deleted;
427dd7cddfSDavid du Colombier 	char	stored;
437dd7cddfSDavid du Colombier };
447dd7cddfSDavid du Colombier 
457dd7cddfSDavid du Colombier Message top;
467dd7cddfSDavid du Colombier 
477dd7cddfSDavid du Colombier struct Ctype {
487dd7cddfSDavid du Colombier 	char	*type;
497dd7cddfSDavid du Colombier 	char 	*ext;
507dd7cddfSDavid du Colombier 	int	display;
517dd7cddfSDavid du Colombier 	char	*plumbdest;
525f3668f2SDavid du Colombier 	Ctype	*next;
537dd7cddfSDavid du Colombier };
547dd7cddfSDavid du Colombier 
557dd7cddfSDavid du Colombier Ctype ctype[] = {
567dd7cddfSDavid du Colombier 	{ "text/plain",			"txt",	1,	0	},
579a747e4fSDavid du Colombier 	{ "text/html",			"htm",	1,	0	},
587dd7cddfSDavid du Colombier 	{ "text/html",			"html",	1,	0	},
597dd7cddfSDavid du Colombier 	{ "text/tab-separated-values",	"tsv",	1,	0	},
607dd7cddfSDavid du Colombier 	{ "text/richtext",		"rtx",	1,	0	},
615f3668f2SDavid du Colombier 	{ "text/rtf",			"rtf",	1,	0	},
628ccc32efSDavid du Colombier 	{ "text/calendar",		"ics",	1,	0	},
637dd7cddfSDavid du Colombier 	{ "text",			"txt",	1,	0	},
649a747e4fSDavid du Colombier 	{ "message/rfc822",		"msg",	0,	0	},
655f3668f2SDavid du Colombier 	{ "image/bmp",			"bmp",	0,	"image"	},
667dd7cddfSDavid du Colombier 	{ "image/jpeg",			"jpg",	0,	"image"	},
677dd7cddfSDavid du Colombier 	{ "image/gif",			"gif",	0,	"image"	},
68fa879b31SDavid du Colombier 	{ "image/png",			"png",	0,	"image"	},
69dc5a79c1SDavid du Colombier 	{ "application/pdf",		"pdf",	0,	"postscript"	},
70dc5a79c1SDavid du Colombier 	{ "application/postscript",	"ps",	0,	"postscript"	},
717dd7cddfSDavid du Colombier 	{ "application/",		0,	0,	0	},
727dd7cddfSDavid du Colombier 	{ "image/",			0,	0,	0	},
737dd7cddfSDavid du Colombier 	{ "multipart/",			"mul",	0,	0	},
745f3668f2SDavid du Colombier 
757dd7cddfSDavid du Colombier };
767dd7cddfSDavid du Colombier 
777dd7cddfSDavid du Colombier Message*	acmd(Cmd*, Message*);
787dd7cddfSDavid du Colombier Message*	bcmd(Cmd*, Message*);
797dd7cddfSDavid du Colombier Message*	dcmd(Cmd*, Message*);
807dd7cddfSDavid du Colombier Message*	eqcmd(Cmd*, Message*);
817dd7cddfSDavid du Colombier Message*	hcmd(Cmd*, Message*);
829a747e4fSDavid du Colombier Message*	Hcmd(Cmd*, Message*);
837dd7cddfSDavid du Colombier Message*	helpcmd(Cmd*, Message*);
847dd7cddfSDavid du Colombier Message*	icmd(Cmd*, Message*);
857dd7cddfSDavid du Colombier Message*	pcmd(Cmd*, Message*);
867dd7cddfSDavid du Colombier Message*	qcmd(Cmd*, Message*);
877dd7cddfSDavid du Colombier Message*	rcmd(Cmd*, Message*);
887dd7cddfSDavid du Colombier Message*	scmd(Cmd*, Message*);
897dd7cddfSDavid du Colombier Message*	ucmd(Cmd*, Message*);
907dd7cddfSDavid du Colombier Message*	wcmd(Cmd*, Message*);
917dd7cddfSDavid du Colombier Message*	xcmd(Cmd*, Message*);
92dc5a79c1SDavid du Colombier Message*	ycmd(Cmd*, Message*);
937dd7cddfSDavid du Colombier Message*	pipecmd(Cmd*, Message*);
946b6b9ac8SDavid du Colombier Message*	rpipecmd(Cmd*, Message*);
957dd7cddfSDavid du Colombier Message*	bangcmd(Cmd*, Message*);
967dd7cddfSDavid du Colombier Message*	Pcmd(Cmd*, Message*);
977dd7cddfSDavid du Colombier Message*	mcmd(Cmd*, Message*);
987dd7cddfSDavid du Colombier Message*	fcmd(Cmd*, Message*);
9980ee5cbfSDavid du Colombier Message*	quotecmd(Cmd*, Message*);
1007dd7cddfSDavid du Colombier 
1017dd7cddfSDavid du Colombier struct {
1027dd7cddfSDavid du Colombier 	char		*cmd;
1037dd7cddfSDavid du Colombier 	int		args;
1047dd7cddfSDavid du Colombier 	Message*	(*f)(Cmd*, Message*);
1057dd7cddfSDavid du Colombier 	char		*help;
1067dd7cddfSDavid du Colombier } cmdtab[] = {
1077dd7cddfSDavid du Colombier 	{ "a",	1,	acmd,	"a        reply to sender and recipients" },
1087dd7cddfSDavid du Colombier 	{ "A",	1,	acmd,	"A        reply to sender and recipients with copy" },
1097dd7cddfSDavid du Colombier 	{ "b",	0,	bcmd,	"b        print the next 10 headers" },
1107dd7cddfSDavid du Colombier 	{ "d",	0,	dcmd,	"d        mark for deletion" },
1117dd7cddfSDavid du Colombier 	{ "f",	0,	fcmd,	"f        file message by from address" },
1129a747e4fSDavid du Colombier 	{ "h",	0,	hcmd,	"h        print elided message summary (,h for all)" },
1137dd7cddfSDavid du Colombier 	{ "help", 0,	helpcmd, "help     print this info" },
1149a747e4fSDavid du Colombier 	{ "H",	0,	Hcmd,	"H        print message's MIME structure " },
1157dd7cddfSDavid du Colombier 	{ "i",	0,	icmd,	"i        incorporate new mail" },
1167dd7cddfSDavid du Colombier 	{ "m",	1,	mcmd,	"m addr   forward mail" },
1177dd7cddfSDavid du Colombier 	{ "M",	1,	mcmd,	"M addr   forward mail with message" },
1187dd7cddfSDavid du Colombier 	{ "p",	0,	pcmd,	"p        print the processed message" },
1197dd7cddfSDavid du Colombier 	{ "P",	0,	Pcmd,	"P        print the raw message" },
12080ee5cbfSDavid du Colombier 	{ "\"",	0,	quotecmd, "\"        print a quoted version of msg" },
1217dd7cddfSDavid du Colombier 	{ "q",	0,	qcmd,	"q        exit and remove all deleted mail" },
1227dd7cddfSDavid du Colombier 	{ "r",	1,	rcmd,	"r [addr] reply to sender plus any addrs specified" },
1239a747e4fSDavid du Colombier 	{ "rf",	1,	rcmd,	"rf [addr]file message and reply" },
1247dd7cddfSDavid du Colombier 	{ "R",	1,	rcmd,	"R [addr] reply including copy of message" },
1259a747e4fSDavid du Colombier 	{ "Rf",	1,	rcmd,	"Rf [addr]file message and reply with copy" },
1267dd7cddfSDavid du Colombier 	{ "s",	1,	scmd,	"s file   append raw message to file" },
1277dd7cddfSDavid du Colombier 	{ "u",	0,	ucmd,	"u        remove deletion mark" },
1287dd7cddfSDavid du Colombier 	{ "w",	1,	wcmd,	"w file   store message contents as file" },
129dc5a79c1SDavid du Colombier 	{ "x",	0,	xcmd,	"x        exit without flushing deleted messages" },
1305130c3f3SDavid du Colombier 	{ "y",	0,	ycmd,	"y        synchronize with mail box" },
1317dd7cddfSDavid du Colombier 	{ "=",	1,	eqcmd,	"=        print current message number" },
1326b6b9ac8SDavid du Colombier 	{ "|",	1,	pipecmd, "|cmd     pipe message body to a command" },
133c0eadb1cSDavid du Colombier 	{ "||",	1,	rpipecmd, "||cmd     pipe raw message to a command" },
1347dd7cddfSDavid du Colombier 	{ "!",	1,	bangcmd, "!cmd     run a command" },
1357dd7cddfSDavid du Colombier 	{ nil,	0,	nil, 	nil },
1367dd7cddfSDavid du Colombier };
1377dd7cddfSDavid du Colombier 
1387dd7cddfSDavid du Colombier enum
1397dd7cddfSDavid du Colombier {
1407dd7cddfSDavid du Colombier 	NARG=	32,
1417dd7cddfSDavid du Colombier };
1427dd7cddfSDavid du Colombier 
1437dd7cddfSDavid du Colombier struct Cmd {
1447dd7cddfSDavid du Colombier 	Message	*msgs;
1457dd7cddfSDavid du Colombier 	Message	*(*f)(Cmd*, Message*);
1467dd7cddfSDavid du Colombier 	int	an;
1477dd7cddfSDavid du Colombier 	char	*av[NARG];
1487dd7cddfSDavid du Colombier 	int	delete;
1497dd7cddfSDavid du Colombier };
1507dd7cddfSDavid du Colombier 
1517dd7cddfSDavid du Colombier Biobuf out;
1527dd7cddfSDavid du Colombier int startedfs;
1537dd7cddfSDavid du Colombier int reverse;
154ed250ae1SDavid du Colombier int longestfrom = 12;
1557dd7cddfSDavid du Colombier 
1567dd7cddfSDavid du Colombier String*		file2string(String*, char*);
1577dd7cddfSDavid du Colombier int		dir2message(Message*, int);
1587dd7cddfSDavid du Colombier int		filelen(String*, char*);
1597dd7cddfSDavid du Colombier String*		extendpath(String*, char*);
1607dd7cddfSDavid du Colombier void		snprintheader(char*, int, Message*);
1617dd7cddfSDavid du Colombier void		cracktime(char*, char*, int);
1627dd7cddfSDavid du Colombier int		cistrncmp(char*, char*, int);
1637dd7cddfSDavid du Colombier int		cistrcmp(char*, char*);
1647dd7cddfSDavid du Colombier Reprog*		parsesearch(char**);
1657dd7cddfSDavid du Colombier char*		parseaddr(char**, Message*, Message*, Message*, Message**);
1667dd7cddfSDavid du Colombier char*		parsecmd(char*, Cmd*, Message*, Message*);
1677dd7cddfSDavid du Colombier char*		readline(char*, char*, int);
1687dd7cddfSDavid du Colombier void		messagecount(Message*);
1697dd7cddfSDavid du Colombier void		system(char*, char**, int);
1707dd7cddfSDavid du Colombier void		mkid(String*, Message*);
1717dd7cddfSDavid du Colombier int		switchmb(char*, char*);
1727dd7cddfSDavid du Colombier void		closemb(void);
1737dd7cddfSDavid du Colombier int		lineize(char*, char**, int);
1747dd7cddfSDavid du Colombier int		rawsearch(Message*, Reprog*);
1757dd7cddfSDavid du Colombier Message*	dosingleton(Message*, char*);
1767dd7cddfSDavid du Colombier String*		rooted(String*);
1779a747e4fSDavid du Colombier int		plumb(Message*, Ctype*);
1787dd7cddfSDavid du Colombier String*		addrecolon(char*);
1797dd7cddfSDavid du Colombier void		exitfs(char*);
180dc5a79c1SDavid du Colombier Message*	flushdeleted(Message*);
1817dd7cddfSDavid du Colombier 
1827dd7cddfSDavid du Colombier void
usage(void)1837dd7cddfSDavid du Colombier usage(void)
1847dd7cddfSDavid du Colombier {
185ee55fa65SDavid du Colombier 	fprint(2, "usage: %s [-nr] [-f mailfile] [-s mailfile]\n", argv0);
186ee55fa65SDavid du Colombier 	fprint(2, "       %s -c dir\n", argv0);
1877dd7cddfSDavid du Colombier 	exits("usage");
1887dd7cddfSDavid du Colombier }
1897dd7cddfSDavid du Colombier 
1907dd7cddfSDavid du Colombier void
catchnote(void *,char * note)1917dd7cddfSDavid du Colombier catchnote(void*, char *note)
1927dd7cddfSDavid du Colombier {
1937dd7cddfSDavid du Colombier 	if(strstr(note, "interrupt") != nil){
1947dd7cddfSDavid du Colombier 		interrupted = 1;
1957dd7cddfSDavid du Colombier 		noted(NCONT);
1967dd7cddfSDavid du Colombier 	}
1977dd7cddfSDavid du Colombier 	noted(NDFLT);
1987dd7cddfSDavid du Colombier }
1997dd7cddfSDavid du Colombier 
2005130c3f3SDavid du Colombier char *
plural(int n)2015130c3f3SDavid du Colombier plural(int n)
2025130c3f3SDavid du Colombier {
2035130c3f3SDavid du Colombier 	if (n == 1)
2045130c3f3SDavid du Colombier 		return "";
2055130c3f3SDavid du Colombier 
2065130c3f3SDavid du Colombier 	return "s";
2075130c3f3SDavid du Colombier }
2085130c3f3SDavid du Colombier 
2097dd7cddfSDavid du Colombier void
main(int argc,char ** argv)2107dd7cddfSDavid du Colombier main(int argc, char **argv)
2117dd7cddfSDavid du Colombier {
2127dd7cddfSDavid du Colombier 	Message *cur, *m, *x;
2137dd7cddfSDavid du Colombier 	char cmdline[4*1024];
2147dd7cddfSDavid du Colombier 	Cmd cmd;
2155f3668f2SDavid du Colombier 	Ctype *cp;
2167dd7cddfSDavid du Colombier 	int n, cflag;
2177dd7cddfSDavid du Colombier 	char *av[4];
2187dd7cddfSDavid du Colombier 	String *prompt;
2193f8719e6SDavid du Colombier 	char *err, *file, *singleton;
2207dd7cddfSDavid du Colombier 
2213f8719e6SDavid du Colombier 	quotefmtinstall();
2227dd7cddfSDavid du Colombier 	Binit(&out, 1, OWRITE);
2237dd7cddfSDavid du Colombier 
22480ee5cbfSDavid du Colombier 	file = nil;
2257dd7cddfSDavid du Colombier 	singleton = nil;
2267dd7cddfSDavid du Colombier 	reverse = 1;
2277dd7cddfSDavid du Colombier 	cflag = 0;
2287dd7cddfSDavid du Colombier 	ARGBEGIN {
2297dd7cddfSDavid du Colombier 	case 'c':
2307dd7cddfSDavid du Colombier 		cflag = 1;
2317dd7cddfSDavid du Colombier 		break;
2327dd7cddfSDavid du Colombier 	case 'f':
23380ee5cbfSDavid du Colombier 		file = EARGF(usage());
2347dd7cddfSDavid du Colombier 		break;
2357dd7cddfSDavid du Colombier 	case 's':
23680ee5cbfSDavid du Colombier 		singleton = EARGF(usage());
2377dd7cddfSDavid du Colombier 		break;
2387dd7cddfSDavid du Colombier 	case 'r':
2397dd7cddfSDavid du Colombier 		reverse = 0;
2407dd7cddfSDavid du Colombier 		break;
2419a747e4fSDavid du Colombier 	case 'n':
2429a747e4fSDavid du Colombier 		natural = 1;
2439a747e4fSDavid du Colombier 		reverse = 0;
2449a747e4fSDavid du Colombier 		break;
24580ee5cbfSDavid du Colombier 	default:
24680ee5cbfSDavid du Colombier 		usage();
24780ee5cbfSDavid du Colombier 		break;
2487dd7cddfSDavid du Colombier 	} ARGEND;
2497dd7cddfSDavid du Colombier 
2507dd7cddfSDavid du Colombier 	user = getlog();
2517dd7cddfSDavid du Colombier 	if(user == nil || *user == 0)
2527dd7cddfSDavid du Colombier 		sysfatal("can't read user name");
2537dd7cddfSDavid du Colombier 
2547dd7cddfSDavid du Colombier 	if(cflag){
2557dd7cddfSDavid du Colombier 		if(argc > 0)
2567dd7cddfSDavid du Colombier 			creatembox(user, argv[0]);
2577dd7cddfSDavid du Colombier 		else
2587dd7cddfSDavid du Colombier 			creatembox(user, nil);
2597dd7cddfSDavid du Colombier 		exits(0);
2607dd7cddfSDavid du Colombier 	}
2617dd7cddfSDavid du Colombier 
2629a747e4fSDavid du Colombier 	if(argc)
2639a747e4fSDavid du Colombier 		usage();
2649a747e4fSDavid du Colombier 
2657dd7cddfSDavid du Colombier 	if(access("/mail/fs/ctl", 0) < 0){
2667dd7cddfSDavid du Colombier 		startedfs = 1;
2677dd7cddfSDavid du Colombier 		av[0] = "fs";
2687dd7cddfSDavid du Colombier 		av[1] = "-p";
2697dd7cddfSDavid du Colombier 		av[2] = 0;
2707dd7cddfSDavid du Colombier 		system("/bin/upas/fs", av, -1);
2717dd7cddfSDavid du Colombier 	}
2727dd7cddfSDavid du Colombier 
2737dd7cddfSDavid du Colombier 	switchmb(file, singleton);
2747dd7cddfSDavid du Colombier 
2757dd7cddfSDavid du Colombier 	top.path = s_copy(root);
2767dd7cddfSDavid du Colombier 
2775f3668f2SDavid du Colombier 	for(cp = ctype; cp < ctype+nelem(ctype)-1; cp++)
2785f3668f2SDavid du Colombier 		cp->next = cp+1;
2795f3668f2SDavid du Colombier 
2807dd7cddfSDavid du Colombier 	if(singleton != nil){
2817dd7cddfSDavid du Colombier 		cur = dosingleton(&top, singleton);
2827dd7cddfSDavid du Colombier 		if(cur == nil){
2837dd7cddfSDavid du Colombier 			Bprint(&out, "no message\n");
2847dd7cddfSDavid du Colombier 			exitfs(0);
2857dd7cddfSDavid du Colombier 		}
2867dd7cddfSDavid du Colombier 		pcmd(nil, cur);
2877dd7cddfSDavid du Colombier 	} else {
2887dd7cddfSDavid du Colombier 		cur = &top;
2897dd7cddfSDavid du Colombier 		n = dir2message(&top, reverse);
2907dd7cddfSDavid du Colombier 		if(n < 0)
2919a747e4fSDavid du Colombier 			sysfatal("can't read %s", s_to_c(top.path));
2925130c3f3SDavid du Colombier 		Bprint(&out, "%d message%s\n", n, plural(n));
2937dd7cddfSDavid du Colombier 	}
2947dd7cddfSDavid du Colombier 
2955f3668f2SDavid du Colombier 
2967dd7cddfSDavid du Colombier 	notify(catchnote);
2977dd7cddfSDavid du Colombier 	prompt = s_new();
2987dd7cddfSDavid du Colombier 	for(;;){
2997dd7cddfSDavid du Colombier 		s_reset(prompt);
3007dd7cddfSDavid du Colombier 		if(cur == &top)
3017dd7cddfSDavid du Colombier 			s_append(prompt, ": ");
3027dd7cddfSDavid du Colombier 		else {
3037dd7cddfSDavid du Colombier 			mkid(prompt, cur);
3047dd7cddfSDavid du Colombier 			s_append(prompt, ": ");
3057dd7cddfSDavid du Colombier 		}
3066b6b9ac8SDavid du Colombier 
3076b6b9ac8SDavid du Colombier 		// leave space at the end of cmd line in case parsecmd needs to
3086b6b9ac8SDavid du Colombier 		// add a space after a '|' or '!'
3096b6b9ac8SDavid du Colombier 		if(readline(s_to_c(prompt), cmdline, sizeof(cmdline)-1) == nil)
3107dd7cddfSDavid du Colombier 			break;
3117dd7cddfSDavid du Colombier 		err = parsecmd(cmdline, &cmd, top.child, cur);
3127dd7cddfSDavid du Colombier 		if(err != nil){
3137dd7cddfSDavid du Colombier 			Bprint(&out, "!%s\n", err);
3147dd7cddfSDavid du Colombier 			continue;
3157dd7cddfSDavid du Colombier 		}
3167dd7cddfSDavid du Colombier 		if(singleton != nil && cmd.f == icmd){
3177dd7cddfSDavid du Colombier 			Bprint(&out, "!illegal command\n");
3187dd7cddfSDavid du Colombier 			continue;
3197dd7cddfSDavid du Colombier 		}
3207dd7cddfSDavid du Colombier 		interrupted = 0;
3217dd7cddfSDavid du Colombier 		if(cmd.msgs == nil || cmd.msgs == &top){
3227dd7cddfSDavid du Colombier 			x = (*cmd.f)(&cmd, &top);
3237dd7cddfSDavid du Colombier 			if(x != nil)
3247dd7cddfSDavid du Colombier 				cur = x;
3257dd7cddfSDavid du Colombier 		} else for(m = cmd.msgs; m != nil; m = m->cmd){
3267dd7cddfSDavid du Colombier 			x = m;
3277dd7cddfSDavid du Colombier 			if(cmd.delete){
3287dd7cddfSDavid du Colombier 				dcmd(&cmd, x);
3297dd7cddfSDavid du Colombier 
3307dd7cddfSDavid du Colombier 				// dp acts differently than all other commands
3317dd7cddfSDavid du Colombier 				// since its an old lesk idiom that people love.
3327dd7cddfSDavid du Colombier 				// it deletes the current message, moves the current
3337dd7cddfSDavid du Colombier 				// pointer ahead one and prints.
3347dd7cddfSDavid du Colombier 				if(cmd.f == pcmd){
3357dd7cddfSDavid du Colombier 					if(x->next == nil){
3367dd7cddfSDavid du Colombier 						Bprint(&out, "!address\n");
3377dd7cddfSDavid du Colombier 						cur = x;
3387dd7cddfSDavid du Colombier 						break;
3397dd7cddfSDavid du Colombier 					} else
3407dd7cddfSDavid du Colombier 						x = x->next;
3417dd7cddfSDavid du Colombier 				}
3427dd7cddfSDavid du Colombier 			}
3437dd7cddfSDavid du Colombier 			x = (*cmd.f)(&cmd, x);
3447dd7cddfSDavid du Colombier 			if(x != nil)
3457dd7cddfSDavid du Colombier 				cur = x;
3467dd7cddfSDavid du Colombier 			if(interrupted)
3477dd7cddfSDavid du Colombier 				break;
3487dd7cddfSDavid du Colombier 			if(singleton != nil && (cmd.delete || cmd.f == dcmd))
3497dd7cddfSDavid du Colombier 				qcmd(nil, nil);
3507dd7cddfSDavid du Colombier 		}
351dc5a79c1SDavid du Colombier 		if(doflush)
352dc5a79c1SDavid du Colombier 			cur = flushdeleted(cur);
3537dd7cddfSDavid du Colombier 	}
3547dd7cddfSDavid du Colombier 	qcmd(nil, nil);
3557dd7cddfSDavid du Colombier }
3567dd7cddfSDavid du Colombier 
3577dd7cddfSDavid du Colombier //
3587dd7cddfSDavid du Colombier // read the message info
3597dd7cddfSDavid du Colombier //
3607dd7cddfSDavid du Colombier Message*
file2message(Message * parent,char * name)3617dd7cddfSDavid du Colombier file2message(Message *parent, char *name)
3627dd7cddfSDavid du Colombier {
3637dd7cddfSDavid du Colombier 	Message *m;
3647dd7cddfSDavid du Colombier 	String *path;
3657dd7cddfSDavid du Colombier 	char *f[10];
3667dd7cddfSDavid du Colombier 
3677dd7cddfSDavid du Colombier 	m = mallocz(sizeof(Message), 1);
3687dd7cddfSDavid du Colombier 	if(m == nil)
3697dd7cddfSDavid du Colombier 		return nil;
3707dd7cddfSDavid du Colombier 	m->path = path = extendpath(parent->path, name);
3717dd7cddfSDavid du Colombier 	m->fileno = atoi(name);
3727dd7cddfSDavid du Colombier 	m->info = file2string(path, "info");
3737dd7cddfSDavid du Colombier 	lineize(s_to_c(m->info), f, nelem(f));
3747dd7cddfSDavid du Colombier 	m->from = f[0];
3757dd7cddfSDavid du Colombier 	m->to = f[1];
3767dd7cddfSDavid du Colombier 	m->cc = f[2];
3777dd7cddfSDavid du Colombier 	m->replyto = f[3];
3787dd7cddfSDavid du Colombier 	m->date = f[4];
3797dd7cddfSDavid du Colombier 	m->subject = f[5];
3807dd7cddfSDavid du Colombier 	m->type = f[6];
3817dd7cddfSDavid du Colombier 	m->disposition = f[7];
3827dd7cddfSDavid du Colombier 	m->filename = f[8];
3837dd7cddfSDavid du Colombier 	m->len = filelen(path, "raw");
3849a747e4fSDavid du Colombier 	if(strstr(m->type, "multipart") != nil || strcmp(m->type, "message/rfc822") == 0)
3857dd7cddfSDavid du Colombier 		dir2message(m, 0);
3867dd7cddfSDavid du Colombier 	m->parent = parent;
3877dd7cddfSDavid du Colombier 
3887dd7cddfSDavid du Colombier 	return m;
3897dd7cddfSDavid du Colombier }
3907dd7cddfSDavid du Colombier 
391dc5a79c1SDavid du Colombier void
freemessage(Message * m)392dc5a79c1SDavid du Colombier freemessage(Message *m)
393dc5a79c1SDavid du Colombier {
394dc5a79c1SDavid du Colombier 	Message *nm, *next;
395dc5a79c1SDavid du Colombier 
396dc5a79c1SDavid du Colombier 	for(nm = m->child; nm != nil; nm = next){
397dc5a79c1SDavid du Colombier 		next = nm->next;
398dc5a79c1SDavid du Colombier 		freemessage(nm);
399dc5a79c1SDavid du Colombier 	}
400dc5a79c1SDavid du Colombier 	s_free(m->path);
401dc5a79c1SDavid du Colombier 	s_free(m->info);
402dc5a79c1SDavid du Colombier 	free(m);
403dc5a79c1SDavid du Colombier }
404dc5a79c1SDavid du Colombier 
4057dd7cddfSDavid du Colombier //
4067dd7cddfSDavid du Colombier //  read a directory into a list of messages
4077dd7cddfSDavid du Colombier //
4087dd7cddfSDavid du Colombier int
dir2message(Message * parent,int reverse)4097dd7cddfSDavid du Colombier dir2message(Message *parent, int reverse)
4107dd7cddfSDavid du Colombier {
4117dd7cddfSDavid du Colombier 	int i, n, fd, highest, newmsgs;
4129a747e4fSDavid du Colombier 	Dir *d;
4137dd7cddfSDavid du Colombier 	Message *first, *last, *m;
4147dd7cddfSDavid du Colombier 
4157dd7cddfSDavid du Colombier 	fd = open(s_to_c(parent->path), OREAD);
4167dd7cddfSDavid du Colombier 	if(fd < 0)
4177dd7cddfSDavid du Colombier 		return -1;
4187dd7cddfSDavid du Colombier 
4197dd7cddfSDavid du Colombier 	// count current entries
4207dd7cddfSDavid du Colombier 	first = parent->child;
4217dd7cddfSDavid du Colombier 	highest = newmsgs = 0;
4227dd7cddfSDavid du Colombier 	for(last = parent->child; last != nil && last->next != nil; last = last->next)
4237dd7cddfSDavid du Colombier 		if(last->fileno > highest)
4247dd7cddfSDavid du Colombier 			highest = last->fileno;
4257dd7cddfSDavid du Colombier 	if(last != nil)
4267dd7cddfSDavid du Colombier 		if(last->fileno > highest)
4277dd7cddfSDavid du Colombier 			highest = last->fileno;
4287dd7cddfSDavid du Colombier 
4299a747e4fSDavid du Colombier 	n = dirreadall(fd, &d);
4307dd7cddfSDavid du Colombier 	for(i = 0; i < n; i++){
4319a747e4fSDavid du Colombier 		if((d[i].qid.type & QTDIR) == 0)
4327dd7cddfSDavid du Colombier 			continue;
4337dd7cddfSDavid du Colombier 		if(atoi(d[i].name) <= highest)
4347dd7cddfSDavid du Colombier 			continue;
4357dd7cddfSDavid du Colombier 		m = file2message(parent, d[i].name);
4367dd7cddfSDavid du Colombier 		if(m == nil)
4377dd7cddfSDavid du Colombier 			break;
4387dd7cddfSDavid du Colombier 		newmsgs++;
4397dd7cddfSDavid du Colombier 		if(reverse){
4407dd7cddfSDavid du Colombier 			m->next = first;
4417dd7cddfSDavid du Colombier 			if(first != nil)
4427dd7cddfSDavid du Colombier 				first->prev = m;
4437dd7cddfSDavid du Colombier 			first = m;
4447dd7cddfSDavid du Colombier 		} else {
4457dd7cddfSDavid du Colombier 			if(first == nil)
4467dd7cddfSDavid du Colombier 				first = m;
4477dd7cddfSDavid du Colombier 			else
4487dd7cddfSDavid du Colombier 				last->next = m;
4497dd7cddfSDavid du Colombier 			m->prev = last;
4507dd7cddfSDavid du Colombier 			last = m;
4517dd7cddfSDavid du Colombier 		}
4527dd7cddfSDavid du Colombier 	}
4539a747e4fSDavid du Colombier 	free(d);
4547dd7cddfSDavid du Colombier 	close(fd);
4557dd7cddfSDavid du Colombier 	parent->child = first;
4567dd7cddfSDavid du Colombier 
457ed250ae1SDavid du Colombier 	// renumber and file longest from
4587dd7cddfSDavid du Colombier 	i = 1;
459ed250ae1SDavid du Colombier 	longestfrom = 12;
460ed250ae1SDavid du Colombier 	for(m = first; m != nil; m = m->next){
4619a747e4fSDavid du Colombier 		m->id = natural ? m->fileno : i++;
462ed250ae1SDavid du Colombier 		n = strlen(m->from);
463ed250ae1SDavid du Colombier 		if(n > longestfrom)
464ed250ae1SDavid du Colombier 			longestfrom = n;
465ed250ae1SDavid du Colombier 	}
4667dd7cddfSDavid du Colombier 
4677dd7cddfSDavid du Colombier 	return newmsgs;
4687dd7cddfSDavid du Colombier }
4697dd7cddfSDavid du Colombier 
4707dd7cddfSDavid du Colombier //
4717dd7cddfSDavid du Colombier //  point directly to a message
4727dd7cddfSDavid du Colombier //
4737dd7cddfSDavid du Colombier Message*
dosingleton(Message * parent,char * path)4747dd7cddfSDavid du Colombier dosingleton(Message *parent, char *path)
4757dd7cddfSDavid du Colombier {
4767dd7cddfSDavid du Colombier 	char *p, *np;
4777dd7cddfSDavid du Colombier 	Message *m;
4787dd7cddfSDavid du Colombier 
4797dd7cddfSDavid du Colombier 	// walk down to message and read it
4807dd7cddfSDavid du Colombier 	if(strlen(path) < rootlen)
4817dd7cddfSDavid du Colombier 		return nil;
4827dd7cddfSDavid du Colombier 	if(path[rootlen] != '/')
4837dd7cddfSDavid du Colombier 		return nil;
4847dd7cddfSDavid du Colombier 	p = path+rootlen+1;
4857dd7cddfSDavid du Colombier 	np = strchr(p, '/');
4867dd7cddfSDavid du Colombier 	if(np != nil)
4877dd7cddfSDavid du Colombier 		*np = 0;
4887dd7cddfSDavid du Colombier 	m = file2message(parent, p);
4897dd7cddfSDavid du Colombier 	if(m == nil)
4907dd7cddfSDavid du Colombier 		return nil;
4917dd7cddfSDavid du Colombier 	parent->child = m;
4927dd7cddfSDavid du Colombier 	m->id = 1;
4937dd7cddfSDavid du Colombier 
4947dd7cddfSDavid du Colombier 	// walk down to requested component
4957dd7cddfSDavid du Colombier 	while(np != nil){
4967dd7cddfSDavid du Colombier 		*np = '/';
4977dd7cddfSDavid du Colombier 		np = strchr(np+1, '/');
4987dd7cddfSDavid du Colombier 		if(np != nil)
4997dd7cddfSDavid du Colombier 			*np = 0;
5007dd7cddfSDavid du Colombier 		for(m = m->child; m != nil; m = m->next)
5017dd7cddfSDavid du Colombier 			if(strcmp(path, s_to_c(m->path)) == 0)
5027dd7cddfSDavid du Colombier 				return m;
5037dd7cddfSDavid du Colombier 		if(m == nil)
5047dd7cddfSDavid du Colombier 			return nil;
5057dd7cddfSDavid du Colombier 	}
5067dd7cddfSDavid du Colombier 	return m;
5077dd7cddfSDavid du Colombier }
5087dd7cddfSDavid du Colombier 
5097dd7cddfSDavid du Colombier //
5107dd7cddfSDavid du Colombier //  read a file into a string
5117dd7cddfSDavid du Colombier //
5127dd7cddfSDavid du Colombier String*
file2string(String * dir,char * file)5137dd7cddfSDavid du Colombier file2string(String *dir, char *file)
5147dd7cddfSDavid du Colombier {
5157dd7cddfSDavid du Colombier 	String *s;
5169a747e4fSDavid du Colombier 	int fd, n, m;
5177dd7cddfSDavid du Colombier 
5187dd7cddfSDavid du Colombier 	s = extendpath(dir, file);
5197dd7cddfSDavid du Colombier 	fd = open(s_to_c(s), OREAD);
5209a747e4fSDavid du Colombier 	s_grow(s, 512);			/* avoid multiple reads on info files */
5217dd7cddfSDavid du Colombier 	s_reset(s);
5227dd7cddfSDavid du Colombier 	if(fd < 0)
5237dd7cddfSDavid du Colombier 		return s;
5247dd7cddfSDavid du Colombier 
5257dd7cddfSDavid du Colombier 	for(;;){
5267dd7cddfSDavid du Colombier 		n = s->end - s->ptr;
5277dd7cddfSDavid du Colombier 		if(n == 0){
52880ee5cbfSDavid du Colombier 			s_grow(s, 128);
5297dd7cddfSDavid du Colombier 			continue;
5307dd7cddfSDavid du Colombier 		}
5319a747e4fSDavid du Colombier 		m = read(fd, s->ptr, n);
5329a747e4fSDavid du Colombier 		if(m <= 0)
5337dd7cddfSDavid du Colombier 			break;
5349a747e4fSDavid du Colombier 		s->ptr += m;
5359a747e4fSDavid du Colombier 		if(m < n)
5369a747e4fSDavid du Colombier 			break;
5377dd7cddfSDavid du Colombier 	}
5387dd7cddfSDavid du Colombier 	s_terminate(s);
5397dd7cddfSDavid du Colombier 	close(fd);
5407dd7cddfSDavid du Colombier 
5417dd7cddfSDavid du Colombier 	return s;
5427dd7cddfSDavid du Colombier }
5437dd7cddfSDavid du Colombier 
5447dd7cddfSDavid du Colombier //
5457dd7cddfSDavid du Colombier //  get the length of a file
5467dd7cddfSDavid du Colombier //
5477dd7cddfSDavid du Colombier int
filelen(String * dir,char * file)5487dd7cddfSDavid du Colombier filelen(String *dir, char *file)
5497dd7cddfSDavid du Colombier {
5507dd7cddfSDavid du Colombier 	String *path;
5519a747e4fSDavid du Colombier 	Dir *d;
5529a747e4fSDavid du Colombier 	int rv;
5537dd7cddfSDavid du Colombier 
5547dd7cddfSDavid du Colombier 	path = extendpath(dir, file);
5559a747e4fSDavid du Colombier 	d = dirstat(s_to_c(path));
5569a747e4fSDavid du Colombier 	if(d == nil){
5577dd7cddfSDavid du Colombier 		s_free(path);
5587dd7cddfSDavid du Colombier 		return -1;
5597dd7cddfSDavid du Colombier 	}
5607dd7cddfSDavid du Colombier 	s_free(path);
5619a747e4fSDavid du Colombier 	rv = d->length;
5629a747e4fSDavid du Colombier 	free(d);
5639a747e4fSDavid du Colombier 	return rv;
5647dd7cddfSDavid du Colombier }
5657dd7cddfSDavid du Colombier 
5667dd7cddfSDavid du Colombier //
5677dd7cddfSDavid du Colombier //  walk the path name an element
5687dd7cddfSDavid du Colombier //
5697dd7cddfSDavid du Colombier String*
extendpath(String * dir,char * name)5707dd7cddfSDavid du Colombier extendpath(String *dir, char *name)
5717dd7cddfSDavid du Colombier {
5727dd7cddfSDavid du Colombier 	String *path;
5737dd7cddfSDavid du Colombier 
5747dd7cddfSDavid du Colombier 	if(strcmp(s_to_c(dir), ".") == 0)
5757dd7cddfSDavid du Colombier 		path = s_new();
5767dd7cddfSDavid du Colombier 	else {
5777dd7cddfSDavid du Colombier 		path = s_copy(s_to_c(dir));
5787dd7cddfSDavid du Colombier 		s_append(path, "/");
5797dd7cddfSDavid du Colombier 	}
5807dd7cddfSDavid du Colombier 	s_append(path, name);
5817dd7cddfSDavid du Colombier 	return path;
5827dd7cddfSDavid du Colombier }
5837dd7cddfSDavid du Colombier 
5847dd7cddfSDavid du Colombier int
cistrncmp(char * a,char * b,int n)5857dd7cddfSDavid du Colombier cistrncmp(char *a, char *b, int n)
5867dd7cddfSDavid du Colombier {
5877dd7cddfSDavid du Colombier 	while(n-- > 0){
5887dd7cddfSDavid du Colombier 		if(tolower(*a++) != tolower(*b++))
5897dd7cddfSDavid du Colombier 			return -1;
5907dd7cddfSDavid du Colombier 	}
5917dd7cddfSDavid du Colombier 	return 0;
5927dd7cddfSDavid du Colombier }
5937dd7cddfSDavid du Colombier 
5947dd7cddfSDavid du Colombier int
cistrcmp(char * a,char * b)5957dd7cddfSDavid du Colombier cistrcmp(char *a, char *b)
5967dd7cddfSDavid du Colombier {
5977dd7cddfSDavid du Colombier 	for(;;){
5987dd7cddfSDavid du Colombier 		if(tolower(*a) != tolower(*b++))
5997dd7cddfSDavid du Colombier 			return -1;
6007dd7cddfSDavid du Colombier 		if(*a++ == 0)
6017dd7cddfSDavid du Colombier 			break;
6027dd7cddfSDavid du Colombier 	}
6037dd7cddfSDavid du Colombier 	return 0;
6047dd7cddfSDavid du Colombier }
6057dd7cddfSDavid du Colombier 
6067dd7cddfSDavid du Colombier char*
nosecs(char * t)6077dd7cddfSDavid du Colombier nosecs(char *t)
6087dd7cddfSDavid du Colombier {
6097dd7cddfSDavid du Colombier 	char *p;
6107dd7cddfSDavid du Colombier 
6117dd7cddfSDavid du Colombier 	p = strchr(t, ':');
6127dd7cddfSDavid du Colombier 	if(p == nil)
6137dd7cddfSDavid du Colombier 		return t;
6147dd7cddfSDavid du Colombier 	p = strchr(p+1, ':');
6157dd7cddfSDavid du Colombier 	if(p != nil)
6167dd7cddfSDavid du Colombier 		*p = 0;
6177dd7cddfSDavid du Colombier 	return t;
6187dd7cddfSDavid du Colombier }
6197dd7cddfSDavid du Colombier 
6209a747e4fSDavid du Colombier char *months[12] =
6219a747e4fSDavid du Colombier {
6229a747e4fSDavid du Colombier 	"jan", "feb", "mar", "apr", "may", "jun",
6239a747e4fSDavid du Colombier 	"jul", "aug", "sep", "oct", "nov", "dec"
6249a747e4fSDavid du Colombier };
6259a747e4fSDavid du Colombier 
6269a747e4fSDavid du Colombier int
month(char * m)6279a747e4fSDavid du Colombier month(char *m)
6289a747e4fSDavid du Colombier {
6299a747e4fSDavid du Colombier 	int i;
6309a747e4fSDavid du Colombier 
6319a747e4fSDavid du Colombier 	for(i = 0; i < 12; i++)
6329a747e4fSDavid du Colombier 		if(cistrcmp(m, months[i]) == 0)
6339a747e4fSDavid du Colombier 			return i+1;
6349a747e4fSDavid du Colombier 	return 1;
6359a747e4fSDavid du Colombier }
6369a747e4fSDavid du Colombier 
6379a747e4fSDavid du Colombier enum
6389a747e4fSDavid du Colombier {
6399a747e4fSDavid du Colombier 	Yearsecs= 365*24*60*60
6409a747e4fSDavid du Colombier };
6419a747e4fSDavid du Colombier 
6427dd7cddfSDavid du Colombier void
cracktime(char * d,char * out,int len)6437dd7cddfSDavid du Colombier cracktime(char *d, char *out, int len)
6447dd7cddfSDavid du Colombier {
6457dd7cddfSDavid du Colombier 	char in[64];
6467dd7cddfSDavid du Colombier 	char *f[6];
6477dd7cddfSDavid du Colombier 	int n;
6489a747e4fSDavid du Colombier 	Tm tm;
6499a747e4fSDavid du Colombier 	long now, then;
6509a747e4fSDavid du Colombier 	char *dtime;
6517dd7cddfSDavid du Colombier 
6527dd7cddfSDavid du Colombier 	*out = 0;
6537dd7cddfSDavid du Colombier 	if(d == nil)
6547dd7cddfSDavid du Colombier 		return;
6557dd7cddfSDavid du Colombier 	strncpy(in, d, sizeof(in));
6567dd7cddfSDavid du Colombier 	in[sizeof(in)-1] = 0;
6579a747e4fSDavid du Colombier 	n = getfields(in, f, 6, 1, " \t\r\n");
6587dd7cddfSDavid du Colombier 	if(n != 6){
6597dd7cddfSDavid du Colombier 		// unknown style
6607dd7cddfSDavid du Colombier 		snprint(out, 16, "%10.10s", d);
6617dd7cddfSDavid du Colombier 		return;
6627dd7cddfSDavid du Colombier 	}
6639a747e4fSDavid du Colombier 	now = time(0);
6649a747e4fSDavid du Colombier 	memset(&tm, 0, sizeof tm);
6657dd7cddfSDavid du Colombier 	if(strchr(f[0], ',') != nil && strchr(f[4], ':') != nil){
6667dd7cddfSDavid du Colombier 		// 822 style
6679a747e4fSDavid du Colombier 		tm.year = atoi(f[3])-1900;
6689a747e4fSDavid du Colombier 		tm.mon = month(f[2]);
6699a747e4fSDavid du Colombier 		tm.mday = atoi(f[1]);
6709a747e4fSDavid du Colombier 		dtime = nosecs(f[4]);
6719a747e4fSDavid du Colombier 		then = tm2sec(&tm);
6727dd7cddfSDavid du Colombier 	} else if(strchr(f[3], ':') != nil){
6737dd7cddfSDavid du Colombier 		// unix style
6749a747e4fSDavid du Colombier 		tm.year = atoi(f[5])-1900;
6759a747e4fSDavid du Colombier 		tm.mon = month(f[1]);
6769a747e4fSDavid du Colombier 		tm.mday = atoi(f[2]);
6779a747e4fSDavid du Colombier 		dtime = nosecs(f[3]);
6789a747e4fSDavid du Colombier 		then = tm2sec(&tm);
6797dd7cddfSDavid du Colombier 	} else {
6809a747e4fSDavid du Colombier 		then = now;
6819a747e4fSDavid du Colombier 		tm = *localtime(now);
6829a747e4fSDavid du Colombier 		dtime = "";
6837dd7cddfSDavid du Colombier 	}
6849a747e4fSDavid du Colombier 
6859a747e4fSDavid du Colombier 	if(now - then < Yearsecs/2)
6869a747e4fSDavid du Colombier 		snprint(out, len, "%2d/%2.2d %s", tm.mon, tm.mday, dtime);
6879a747e4fSDavid du Colombier 	else
6889a747e4fSDavid du Colombier 		snprint(out, len, "%2d/%2.2d  %4d", tm.mon, tm.mday, tm.year+1900);
6897dd7cddfSDavid du Colombier }
6907dd7cddfSDavid du Colombier 
6917dd7cddfSDavid du Colombier Ctype*
findctype(Message * m)6925f3668f2SDavid du Colombier findctype(Message *m)
6937dd7cddfSDavid du Colombier {
6945f3668f2SDavid du Colombier 	char *p;
6955f3668f2SDavid du Colombier 	char ftype[128];
6965f3668f2SDavid du Colombier 	int n, pfd[2];
6975f3668f2SDavid du Colombier 	Ctype *a, *cp;
6985f3668f2SDavid du Colombier 	static Ctype nulltype	= { "", 0, 0, 0 };
6995f3668f2SDavid du Colombier 	static Ctype bintype 	= { "application/octet-stream", "bin", 0, 0 };
7007dd7cddfSDavid du Colombier 
7015f3668f2SDavid du Colombier 	for(cp = ctype; cp; cp = cp->next)
7025f3668f2SDavid du Colombier 		if(strncmp(cp->type, m->type, strlen(cp->type)) == 0)
7037dd7cddfSDavid du Colombier 			return cp;
7045f3668f2SDavid du Colombier 
7055f3668f2SDavid du Colombier /*	use file(1) for any unknown mimetypes
7065f3668f2SDavid du Colombier  *
7075f3668f2SDavid du Colombier  *	if (strcmp(m->type, bintype.type) != 0)
7085f3668f2SDavid du Colombier  *		return &nulltype;
7095f3668f2SDavid du Colombier  */
7105f3668f2SDavid du Colombier 	if(pipe(pfd) < 0)
7115f3668f2SDavid du Colombier 		return &bintype;
7125f3668f2SDavid du Colombier 
7135f3668f2SDavid du Colombier 	*ftype = 0;
7145f3668f2SDavid du Colombier 	switch(fork()){
7155f3668f2SDavid du Colombier 	case -1:
7165f3668f2SDavid du Colombier 		break;
7175f3668f2SDavid du Colombier 	case 0:
7185f3668f2SDavid du Colombier 		close(pfd[1]);
7195f3668f2SDavid du Colombier 		close(0);
7205f3668f2SDavid du Colombier 		dup(pfd[0], 0);
7215f3668f2SDavid du Colombier 		close(1);
7225f3668f2SDavid du Colombier 		dup(pfd[0], 1);
723f19e7b74SDavid du Colombier 		execl("/bin/file", "file", "-m", s_to_c(extendpath(m->path, "body")), nil);
7245f3668f2SDavid du Colombier 		exits(0);
7255f3668f2SDavid du Colombier 	default:
7265f3668f2SDavid du Colombier 		close(pfd[0]);
7275f3668f2SDavid du Colombier 		n = read(pfd[1], ftype, sizeof(ftype));
7285f3668f2SDavid du Colombier 		if(n > 0)
7295f3668f2SDavid du Colombier 			ftype[n] = 0;
7305f3668f2SDavid du Colombier 		close(pfd[1]);
7315f3668f2SDavid du Colombier 		waitpid();
7325f3668f2SDavid du Colombier 		break;
7335f3668f2SDavid du Colombier 	}
7345f3668f2SDavid du Colombier 
7355f3668f2SDavid du Colombier 	if (*ftype=='\0' || (p = strchr(ftype, '/')) == nil)
7365f3668f2SDavid du Colombier 		return &bintype;
7375f3668f2SDavid du Colombier 	*p++ = 0;
7385f3668f2SDavid du Colombier 
7395f3668f2SDavid du Colombier 	a = mallocz(sizeof(Ctype), 1);
7405f3668f2SDavid du Colombier 	a->type = strdup(ftype);
7415f3668f2SDavid du Colombier 	a->ext = strdup(p);
7425f3668f2SDavid du Colombier 	a->display = 0;
7435f3668f2SDavid du Colombier 	a->plumbdest = strdup(ftype);
7445f3668f2SDavid du Colombier 	for(cp = ctype; cp->next; cp = cp->next)
7455f3668f2SDavid du Colombier 		continue;
7465f3668f2SDavid du Colombier 	cp->next = a;
7475f3668f2SDavid du Colombier 	a->next = nil;
7485f3668f2SDavid du Colombier 	return a;
7497dd7cddfSDavid du Colombier }
7507dd7cddfSDavid du Colombier 
7517dd7cddfSDavid du Colombier void
mkid(String * s,Message * m)7527dd7cddfSDavid du Colombier mkid(String *s, Message *m)
7537dd7cddfSDavid du Colombier {
7547dd7cddfSDavid du Colombier 	char buf[32];
7557dd7cddfSDavid du Colombier 
7567dd7cddfSDavid du Colombier 	if(m->parent != &top){
7577dd7cddfSDavid du Colombier 		mkid(s, m->parent);
7587dd7cddfSDavid du Colombier 		s_append(s, ".");
7597dd7cddfSDavid du Colombier 	}
7607dd7cddfSDavid du Colombier 	sprint(buf, "%d", m->id);
7617dd7cddfSDavid du Colombier 	s_append(s, buf);
7627dd7cddfSDavid du Colombier }
7637dd7cddfSDavid du Colombier 
7647dd7cddfSDavid du Colombier void
snprintheader(char * buf,int len,Message * m)7657dd7cddfSDavid du Colombier snprintheader(char *buf, int len, Message *m)
7667dd7cddfSDavid du Colombier {
7677dd7cddfSDavid du Colombier 	char timebuf[32];
7687dd7cddfSDavid du Colombier 	String *id;
7699739e468SDavid du Colombier 	char *p, *q;
7707dd7cddfSDavid du Colombier 
7717dd7cddfSDavid du Colombier 	// create id
7727dd7cddfSDavid du Colombier 	id = s_new();
7737dd7cddfSDavid du Colombier 	mkid(id, m);
7747dd7cddfSDavid du Colombier 
7757dd7cddfSDavid du Colombier 	if(*m->from == 0){
7767dd7cddfSDavid du Colombier 		// no from
777e288d156SDavid du Colombier 		snprint(buf, len, "%-3s    %s %6d  %s",
7787dd7cddfSDavid du Colombier 			s_to_c(id),
7799a747e4fSDavid du Colombier 			m->type,
7807dd7cddfSDavid du Colombier 			m->len,
7817dd7cddfSDavid du Colombier 			m->filename);
7827dd7cddfSDavid du Colombier 	} else if(*m->subject){
783ed250ae1SDavid du Colombier 		q = p = strdup(m->subject);
784ed250ae1SDavid du Colombier 		while(*p == ' ')
785ed250ae1SDavid du Colombier 			p++;
786ed250ae1SDavid du Colombier 		if(strlen(p) > 50)
787ed250ae1SDavid du Colombier 			p[50] = 0;
7887dd7cddfSDavid du Colombier 		cracktime(m->date, timebuf, sizeof(timebuf));
789ed250ae1SDavid du Colombier 		snprint(buf, len, "%-3s %c%c%c %6d  %11.11s %-*.*s %s",
7907dd7cddfSDavid du Colombier 			s_to_c(id),
7919a747e4fSDavid du Colombier 			m->child ? 'H' : ' ',
7927dd7cddfSDavid du Colombier 			m->deleted ? 'd' : ' ',
7937dd7cddfSDavid du Colombier 			m->stored ? 's' : ' ',
7949a747e4fSDavid du Colombier 			m->len,
7957dd7cddfSDavid du Colombier 			timebuf,
796ed250ae1SDavid du Colombier 			longestfrom, longestfrom, m->from,
797ed250ae1SDavid du Colombier 			p);
798ed250ae1SDavid du Colombier 		free(q);
7997dd7cddfSDavid du Colombier 	} else {
8007dd7cddfSDavid du Colombier 		cracktime(m->date, timebuf, sizeof(timebuf));
801e288d156SDavid du Colombier 		snprint(buf, len, "%-3s %c%c%c %6d  %11.11s %s",
8027dd7cddfSDavid du Colombier 			s_to_c(id),
8039a747e4fSDavid du Colombier 			m->child ? 'H' : ' ',
8047dd7cddfSDavid du Colombier 			m->deleted ? 'd' : ' ',
8057dd7cddfSDavid du Colombier 			m->stored ? 's' : ' ',
8069a747e4fSDavid du Colombier 			m->len,
8077dd7cddfSDavid du Colombier 			timebuf,
8087dd7cddfSDavid du Colombier 			m->from);
8097dd7cddfSDavid du Colombier 	}
8107dd7cddfSDavid du Colombier 	s_free(id);
8117dd7cddfSDavid du Colombier }
8127dd7cddfSDavid du Colombier 
8139a747e4fSDavid du Colombier char *spaces = "                                                                    ";
8149a747e4fSDavid du Colombier 
8159a747e4fSDavid du Colombier void
snprintHeader(char * buf,int len,int indent,Message * m)8169a747e4fSDavid du Colombier snprintHeader(char *buf, int len, int indent, Message *m)
8179a747e4fSDavid du Colombier {
8189a747e4fSDavid du Colombier 	String *id;
8199a747e4fSDavid du Colombier 	char typeid[64];
8209a747e4fSDavid du Colombier 	char *p, *e;
8219a747e4fSDavid du Colombier 
8229a747e4fSDavid du Colombier 	// create id
8239a747e4fSDavid du Colombier 	id = s_new();
8249a747e4fSDavid du Colombier 	mkid(id, m);
8259a747e4fSDavid du Colombier 
8269a747e4fSDavid du Colombier 	e = buf + len;
8279a747e4fSDavid du Colombier 
8289a747e4fSDavid du Colombier 	snprint(typeid, sizeof typeid, "%s    %s", s_to_c(id), m->type);
8299a747e4fSDavid du Colombier 	if(indent < 6)
8309a747e4fSDavid du Colombier 		p = seprint(buf, e, "%-32s %-6d ", typeid, m->len);
8319a747e4fSDavid du Colombier 	else
8329a747e4fSDavid du Colombier 		p = seprint(buf, e, "%-64s %-6d ", typeid, m->len);
8339a747e4fSDavid du Colombier 	if(m->filename && *m->filename)
8349a747e4fSDavid du Colombier 		p = seprint(p, e, "(file,%s)", m->filename);
8359a747e4fSDavid du Colombier 	if(m->from && *m->from)
8369a747e4fSDavid du Colombier 		p = seprint(p, e, "(from,%s)", m->from);
8379a747e4fSDavid du Colombier 	if(m->subject && *m->subject)
8389a747e4fSDavid du Colombier 		seprint(p, e, "(subj,%s)", m->subject);
8399a747e4fSDavid du Colombier 
8409a747e4fSDavid du Colombier 	s_free(id);
8419a747e4fSDavid du Colombier }
8429a747e4fSDavid du Colombier 
8437dd7cddfSDavid du Colombier char sstring[256];
8447dd7cddfSDavid du Colombier 
8457dd7cddfSDavid du Colombier //	cmd := range cmd ' ' arg-list ;
8467dd7cddfSDavid du Colombier //	range := address
8477dd7cddfSDavid du Colombier //		| address ',' address
8487dd7cddfSDavid du Colombier //		| 'g' search ;
8497dd7cddfSDavid du Colombier //	address := msgno
8507dd7cddfSDavid du Colombier //		| search ;
8517dd7cddfSDavid du Colombier //	msgno := number
8527dd7cddfSDavid du Colombier //		| number '/' msgno ;
8537dd7cddfSDavid du Colombier //	search := '/' string '/'
8547dd7cddfSDavid du Colombier //		| '%' string '%' ;
8557dd7cddfSDavid du Colombier //
8567dd7cddfSDavid du Colombier Reprog*
parsesearch(char ** pp)8577dd7cddfSDavid du Colombier parsesearch(char **pp)
8587dd7cddfSDavid du Colombier {
8597dd7cddfSDavid du Colombier 	char *p, *np;
8607dd7cddfSDavid du Colombier 	int c, n;
8617dd7cddfSDavid du Colombier 
8627dd7cddfSDavid du Colombier 	p = *pp;
8637dd7cddfSDavid du Colombier 	c = *p++;
8647dd7cddfSDavid du Colombier 	np = strchr(p, c);
8657dd7cddfSDavid du Colombier 	if(np != nil){
8667dd7cddfSDavid du Colombier 		*np++ = 0;
8677dd7cddfSDavid du Colombier 		*pp = np;
8687dd7cddfSDavid du Colombier 	} else {
8697dd7cddfSDavid du Colombier 		n = strlen(p);
8707dd7cddfSDavid du Colombier 		*pp = p + n;
8717dd7cddfSDavid du Colombier 	}
8727dd7cddfSDavid du Colombier 	if(*p == 0)
8737dd7cddfSDavid du Colombier 		p = sstring;
8747dd7cddfSDavid du Colombier 	else{
8757dd7cddfSDavid du Colombier 		strncpy(sstring, p, sizeof(sstring));
8767dd7cddfSDavid du Colombier 		sstring[sizeof(sstring)-1] = 0;
8777dd7cddfSDavid du Colombier 	}
8787dd7cddfSDavid du Colombier 	return regcomp(p);
8797dd7cddfSDavid du Colombier }
8807dd7cddfSDavid du Colombier 
8813a189962SDavid du Colombier static char *
num2msg(Message ** mp,int sign,int n,Message * first,Message * cur)8823a189962SDavid du Colombier num2msg(Message **mp, int sign, int n, Message *first, Message *cur)
8837dd7cddfSDavid du Colombier {
8847dd7cddfSDavid du Colombier 	Message *m;
8857dd7cddfSDavid du Colombier 
8867dd7cddfSDavid du Colombier 	m = nil;
8877dd7cddfSDavid du Colombier 	switch(sign){
8887dd7cddfSDavid du Colombier 	case 0:
8897dd7cddfSDavid du Colombier 		for(m = first; m != nil; m = m->next)
8907dd7cddfSDavid du Colombier 			if(m->id == n)
8917dd7cddfSDavid du Colombier 				break;
8927dd7cddfSDavid du Colombier 		break;
8937dd7cddfSDavid du Colombier 	case -1:
8947dd7cddfSDavid du Colombier 		if(cur != &top)
8957dd7cddfSDavid du Colombier 			for(m = cur; m != nil && n > 0; n--)
8967dd7cddfSDavid du Colombier 				m = m->prev;
8977dd7cddfSDavid du Colombier 		break;
8987dd7cddfSDavid du Colombier 	case 1:
8997dd7cddfSDavid du Colombier 		if(cur == &top){
9007dd7cddfSDavid du Colombier 			n--;
9017dd7cddfSDavid du Colombier 			cur = first;
9027dd7cddfSDavid du Colombier 		}
9037dd7cddfSDavid du Colombier 		for(m = cur; m != nil && n > 0; n--)
9047dd7cddfSDavid du Colombier 			m = m->next;
9057dd7cddfSDavid du Colombier 		break;
9067dd7cddfSDavid du Colombier 	}
9077dd7cddfSDavid du Colombier 	if(m == nil)
9087dd7cddfSDavid du Colombier 		return "address";
9097dd7cddfSDavid du Colombier 	*mp = m;
9103a189962SDavid du Colombier 	return nil;
9113a189962SDavid du Colombier }
9123a189962SDavid du Colombier 
9133a189962SDavid du Colombier char*
parseaddr(char ** pp,Message * first,Message * cur,Message * unspec,Message ** mp)9143a189962SDavid du Colombier parseaddr(char **pp, Message *first, Message *cur, Message *unspec, Message **mp)
9153a189962SDavid du Colombier {
9163a189962SDavid du Colombier 	int n;
9173a189962SDavid du Colombier 	Message *m;
9183a189962SDavid du Colombier 	char *p, *err;
9193a189962SDavid du Colombier 	Reprog *prog;
9203a189962SDavid du Colombier 	int c, sign;
9213a189962SDavid du Colombier 	char buf[256];
9223a189962SDavid du Colombier 
9233a189962SDavid du Colombier 	*mp = nil;
9243a189962SDavid du Colombier 	p = *pp;
9253a189962SDavid du Colombier 
9263a189962SDavid du Colombier 	if(*p == '+'){
9273a189962SDavid du Colombier 		sign = 1;
9283a189962SDavid du Colombier 		p++;
9293a189962SDavid du Colombier 		*pp = p;
9303a189962SDavid du Colombier 	} else if(*p == '-'){
9313a189962SDavid du Colombier 		sign = -1;
9323a189962SDavid du Colombier 		p++;
9333a189962SDavid du Colombier 		*pp = p;
9343a189962SDavid du Colombier 	} else
9353a189962SDavid du Colombier 		sign = 0;
9363a189962SDavid du Colombier 
9373a189962SDavid du Colombier 	/*
9383a189962SDavid du Colombier 	 * TODO: verify & install this.
9393a189962SDavid du Colombier 	 * make + and - mean +1 and -1, as in ed.  then -,.d won't
9403a189962SDavid du Colombier 	 * delete all messages up to the current one.  - geoff
9413a189962SDavid du Colombier 	 */
9423a189962SDavid du Colombier 	if(sign && (!isascii(*p) || !isdigit(*p))) {
9433a189962SDavid du Colombier 		err = num2msg(mp, sign, 1, first, cur);
9443a189962SDavid du Colombier 		if (err != nil)
9453a189962SDavid du Colombier 			return err;
9463a189962SDavid du Colombier 	}
9473a189962SDavid du Colombier 
9483a189962SDavid du Colombier 	switch(*p){
9493a189962SDavid du Colombier 	default:
9503a189962SDavid du Colombier 		if(sign){
9513a189962SDavid du Colombier 			n = 1;
9523a189962SDavid du Colombier 			goto number;
9533a189962SDavid du Colombier 		}
9543a189962SDavid du Colombier 		*mp = unspec;
9553a189962SDavid du Colombier 		break;
9563a189962SDavid du Colombier 	case '0': case '1': case '2': case '3': case '4':
9573a189962SDavid du Colombier 	case '5': case '6': case '7': case '8': case '9':
9583a189962SDavid du Colombier 		n = strtoul(p, pp, 10);
9593a189962SDavid du Colombier 		if(n == 0){
9603a189962SDavid du Colombier 			if(sign)
9613a189962SDavid du Colombier 				*mp = cur;
9623a189962SDavid du Colombier 			else
9633a189962SDavid du Colombier 				*mp = &top;
9643a189962SDavid du Colombier 			break;
9653a189962SDavid du Colombier 		}
9663a189962SDavid du Colombier 		/* fall through */
9673a189962SDavid du Colombier 	number:
9683a189962SDavid du Colombier 		err = num2msg(mp, sign, n, first, cur);
9693a189962SDavid du Colombier 		if (err != nil)
9703a189962SDavid du Colombier 			return err;
9717dd7cddfSDavid du Colombier 		break;
9727dd7cddfSDavid du Colombier 	case '%':
9737dd7cddfSDavid du Colombier 	case '/':
9747dd7cddfSDavid du Colombier 	case '?':
9757dd7cddfSDavid du Colombier 		c = *p;
9767dd7cddfSDavid du Colombier 		prog = parsesearch(pp);
9777dd7cddfSDavid du Colombier 		if(prog == nil)
9787dd7cddfSDavid du Colombier 			return "badly formed regular expression";
9797dd7cddfSDavid du Colombier 		m = nil;
9807dd7cddfSDavid du Colombier 		switch(c){
9817dd7cddfSDavid du Colombier 		case '%':
9827dd7cddfSDavid du Colombier 			for(m = cur == &top ? first : cur->next; m != nil; m = m->next){
9837dd7cddfSDavid du Colombier 				if(rawsearch(m, prog))
9847dd7cddfSDavid du Colombier 					break;
9857dd7cddfSDavid du Colombier 			}
9867dd7cddfSDavid du Colombier 			break;
9877dd7cddfSDavid du Colombier 		case '/':
9887dd7cddfSDavid du Colombier 			for(m = cur == &top ? first : cur->next; m != nil; m = m->next){
9897dd7cddfSDavid du Colombier 				snprintheader(buf, sizeof(buf), m);
9907dd7cddfSDavid du Colombier 				if(regexec(prog, buf, nil, 0))
9917dd7cddfSDavid du Colombier 					break;
9927dd7cddfSDavid du Colombier 			}
9937dd7cddfSDavid du Colombier 			break;
9947dd7cddfSDavid du Colombier 		case '?':
9957dd7cddfSDavid du Colombier 			for(m = cur == &top ? nil : cur->prev; m != nil; m = m->prev){
9967dd7cddfSDavid du Colombier 				snprintheader(buf, sizeof(buf), m);
9977dd7cddfSDavid du Colombier 				if(regexec(prog, buf, nil, 0))
9987dd7cddfSDavid du Colombier 					break;
9997dd7cddfSDavid du Colombier 			}
10007dd7cddfSDavid du Colombier 			break;
10017dd7cddfSDavid du Colombier 		}
10027dd7cddfSDavid du Colombier 		if(m == nil)
10037dd7cddfSDavid du Colombier 			return "search";
10047dd7cddfSDavid du Colombier 		*mp = m;
10057dd7cddfSDavid du Colombier 		free(prog);
10067dd7cddfSDavid du Colombier 		break;
10077dd7cddfSDavid du Colombier 	case '$':
10087dd7cddfSDavid du Colombier 		for(m = first; m != nil && m->next != nil; m = m->next)
10097dd7cddfSDavid du Colombier 			;
10107dd7cddfSDavid du Colombier 		*mp = m;
10117dd7cddfSDavid du Colombier 		*pp = p+1;
10127dd7cddfSDavid du Colombier 		break;
10137dd7cddfSDavid du Colombier 	case '.':
10147dd7cddfSDavid du Colombier 		*mp = cur;
10157dd7cddfSDavid du Colombier 		*pp = p+1;
10167dd7cddfSDavid du Colombier 		break;
10177dd7cddfSDavid du Colombier 	case ',':
10183a189962SDavid du Colombier 		if (*mp == nil)
10197dd7cddfSDavid du Colombier 			*mp = first;
10207dd7cddfSDavid du Colombier 		*pp = p;
10217dd7cddfSDavid du Colombier 		break;
10227dd7cddfSDavid du Colombier 	}
10237dd7cddfSDavid du Colombier 
10247dd7cddfSDavid du Colombier 	if(*mp != nil && **pp == '.'){
10257dd7cddfSDavid du Colombier 		(*pp)++;
102680ee5cbfSDavid du Colombier 		if((*mp)->child == nil)
102780ee5cbfSDavid du Colombier 			return "no sub parts";
10287dd7cddfSDavid du Colombier 		return parseaddr(pp, (*mp)->child, (*mp)->child, (*mp)->child, mp);
10297dd7cddfSDavid du Colombier 	}
10307dd7cddfSDavid du Colombier 	if(**pp == '+' || **pp == '-' || **pp == '/' || **pp == '%')
10317dd7cddfSDavid du Colombier 		return parseaddr(pp, first, *mp, *mp, mp);
10327dd7cddfSDavid du Colombier 
10337dd7cddfSDavid du Colombier 	return nil;
10347dd7cddfSDavid du Colombier }
10357dd7cddfSDavid du Colombier 
10367dd7cddfSDavid du Colombier //
10377dd7cddfSDavid du Colombier //  search a message for a regular expression match
10387dd7cddfSDavid du Colombier //
10397dd7cddfSDavid du Colombier int
rawsearch(Message * m,Reprog * prog)10407dd7cddfSDavid du Colombier rawsearch(Message *m, Reprog *prog)
10417dd7cddfSDavid du Colombier {
10427dd7cddfSDavid du Colombier 	char buf[4096+1];
10437dd7cddfSDavid du Colombier 	int i, fd, rv;
10447dd7cddfSDavid du Colombier 	String *path;
10457dd7cddfSDavid du Colombier 
10467dd7cddfSDavid du Colombier 	path = extendpath(m->path, "raw");
10477dd7cddfSDavid du Colombier 	fd = open(s_to_c(path), OREAD);
10487dd7cddfSDavid du Colombier 	if(fd < 0)
10497dd7cddfSDavid du Colombier 		return 0;
10507dd7cddfSDavid du Colombier 
10517dd7cddfSDavid du Colombier 	// march through raw message 4096 bytes at a time
10527dd7cddfSDavid du Colombier 	// with a 128 byte overlap to chain the re search.
10537dd7cddfSDavid du Colombier 	rv = 0;
10547dd7cddfSDavid du Colombier 	for(;;){
10557dd7cddfSDavid du Colombier 		i = read(fd, buf, sizeof(buf)-1);
10567dd7cddfSDavid du Colombier 		if(i <= 0)
10577dd7cddfSDavid du Colombier 			break;
10587dd7cddfSDavid du Colombier 		buf[i] = 0;
10597dd7cddfSDavid du Colombier 		if(regexec(prog, buf, nil, 0)){
10607dd7cddfSDavid du Colombier 			rv = 1;
10617dd7cddfSDavid du Colombier 			break;
10627dd7cddfSDavid du Colombier 		}
10637dd7cddfSDavid du Colombier 		if(i < sizeof(buf)-1)
10647dd7cddfSDavid du Colombier 			break;
10657dd7cddfSDavid du Colombier 		if(seek(fd, -128LL, 1) < 0)
10667dd7cddfSDavid du Colombier 			break;
10677dd7cddfSDavid du Colombier 	}
10687dd7cddfSDavid du Colombier 
10697dd7cddfSDavid du Colombier 	close(fd);
10707dd7cddfSDavid du Colombier 	s_free(path);
10717dd7cddfSDavid du Colombier 	return rv;
10727dd7cddfSDavid du Colombier }
10737dd7cddfSDavid du Colombier 
10747dd7cddfSDavid du Colombier 
10757dd7cddfSDavid du Colombier char*
parsecmd(char * p,Cmd * cmd,Message * first,Message * cur)10767dd7cddfSDavid du Colombier parsecmd(char *p, Cmd *cmd, Message *first, Message *cur)
10777dd7cddfSDavid du Colombier {
10787dd7cddfSDavid du Colombier 	Reprog *prog;
10797dd7cddfSDavid du Colombier 	Message *m, *s, *e, **l, *last;
10807dd7cddfSDavid du Colombier 	char buf[256];
10817dd7cddfSDavid du Colombier 	char *err;
10827dd7cddfSDavid du Colombier 	int i, c;
10836b6b9ac8SDavid du Colombier 	char *q;
10849a747e4fSDavid du Colombier 	static char errbuf[Errlen];
10857dd7cddfSDavid du Colombier 
10867dd7cddfSDavid du Colombier 	cmd->delete = 0;
10877dd7cddfSDavid du Colombier 	l = &cmd->msgs;
10887dd7cddfSDavid du Colombier 	*l = nil;
10897dd7cddfSDavid du Colombier 
10907dd7cddfSDavid du Colombier 	// eat white space
10917dd7cddfSDavid du Colombier 	while(*p == ' ')
10927dd7cddfSDavid du Colombier 		p++;
10937dd7cddfSDavid du Colombier 
10947dd7cddfSDavid du Colombier 	// null command is a special case (advance and print)
10957dd7cddfSDavid du Colombier 	if(*p == 0){
10967dd7cddfSDavid du Colombier 		if(cur == &top){
10977dd7cddfSDavid du Colombier 			// special case
10987dd7cddfSDavid du Colombier 			m = first;
10997dd7cddfSDavid du Colombier 		} else {
11007dd7cddfSDavid du Colombier 			// walk to the next message even if we have to go up
11017dd7cddfSDavid du Colombier 			m = cur->next;
11027dd7cddfSDavid du Colombier 			while(m == nil && cur->parent != nil){
11037dd7cddfSDavid du Colombier 				cur = cur->parent;
11047dd7cddfSDavid du Colombier 				m = cur->next;
11057dd7cddfSDavid du Colombier 			}
11067dd7cddfSDavid du Colombier 		}
11077dd7cddfSDavid du Colombier 		if(m == nil)
11087dd7cddfSDavid du Colombier 			return "address";
11097dd7cddfSDavid du Colombier 		*l = m;
11107dd7cddfSDavid du Colombier 		m->cmd = nil;
11117dd7cddfSDavid du Colombier 		cmd->an = 0;
11127dd7cddfSDavid du Colombier 		cmd->f = pcmd;
11137dd7cddfSDavid du Colombier 		return nil;
11147dd7cddfSDavid du Colombier 	}
11157dd7cddfSDavid du Colombier 
11167dd7cddfSDavid du Colombier 	// global search ?
11177dd7cddfSDavid du Colombier 	if(*p == 'g'){
11187dd7cddfSDavid du Colombier 		p++;
11197dd7cddfSDavid du Colombier 
11207dd7cddfSDavid du Colombier 		// no search string means all messages
11217dd7cddfSDavid du Colombier 		if(*p != '/' && *p != '%'){
11227dd7cddfSDavid du Colombier 			for(m = first; m != nil; m = m->next){
11237dd7cddfSDavid du Colombier 				*l = m;
11247dd7cddfSDavid du Colombier 				l = &m->cmd;
11257dd7cddfSDavid du Colombier 				*l = nil;
11267dd7cddfSDavid du Colombier 			}
11279a747e4fSDavid du Colombier 		} else {
11287dd7cddfSDavid du Colombier 			// mark all messages matching this search string
11297dd7cddfSDavid du Colombier 			c = *p;
11307dd7cddfSDavid du Colombier 			prog = parsesearch(&p);
11317dd7cddfSDavid du Colombier 			if(prog == nil)
11327dd7cddfSDavid du Colombier 				return "badly formed regular expression";
11337dd7cddfSDavid du Colombier 			if(c == '%'){
11347dd7cddfSDavid du Colombier 				for(m = first; m != nil; m = m->next){
11357dd7cddfSDavid du Colombier 					if(rawsearch(m, prog)){
11367dd7cddfSDavid du Colombier 						*l = m;
11377dd7cddfSDavid du Colombier 						l = &m->cmd;
11387dd7cddfSDavid du Colombier 						*l = nil;
11397dd7cddfSDavid du Colombier 					}
11407dd7cddfSDavid du Colombier 				}
11417dd7cddfSDavid du Colombier 			} else {
11427dd7cddfSDavid du Colombier 				for(m = first; m != nil; m = m->next){
11437dd7cddfSDavid du Colombier 					snprintheader(buf, sizeof(buf), m);
11447dd7cddfSDavid du Colombier 					if(regexec(prog, buf, nil, 0)){
11457dd7cddfSDavid du Colombier 						*l = m;
11467dd7cddfSDavid du Colombier 						l = &m->cmd;
11477dd7cddfSDavid du Colombier 						*l = nil;
11487dd7cddfSDavid du Colombier 					}
11497dd7cddfSDavid du Colombier 				}
11507dd7cddfSDavid du Colombier 			}
11517dd7cddfSDavid du Colombier 			free(prog);
11529a747e4fSDavid du Colombier 		}
11537dd7cddfSDavid du Colombier 	} else {
11547dd7cddfSDavid du Colombier 
11557dd7cddfSDavid du Colombier 		// parse an address
11567dd7cddfSDavid du Colombier 		s = e = nil;
11577dd7cddfSDavid du Colombier 		err = parseaddr(&p, first, cur, cur, &s);
11587dd7cddfSDavid du Colombier 		if(err != nil)
11597dd7cddfSDavid du Colombier 			return err;
11607dd7cddfSDavid du Colombier 		if(*p == ','){
11617dd7cddfSDavid du Colombier 			// this is an address range
11627dd7cddfSDavid du Colombier 			if(s == &top)
11637dd7cddfSDavid du Colombier 				s = first;
11647dd7cddfSDavid du Colombier 			p++;
11657dd7cddfSDavid du Colombier 			for(last = s; last != nil && last->next != nil; last = last->next)
11667dd7cddfSDavid du Colombier 				;
11677dd7cddfSDavid du Colombier 			err = parseaddr(&p, first, cur, last, &e);
11687dd7cddfSDavid du Colombier 			if(err != nil)
11697dd7cddfSDavid du Colombier 				return err;
11707dd7cddfSDavid du Colombier 
11717dd7cddfSDavid du Colombier 			// select all messages in the range
11727dd7cddfSDavid du Colombier 			for(; s != nil; s = s->next){
11737dd7cddfSDavid du Colombier 				*l = s;
11747dd7cddfSDavid du Colombier 				l = &s->cmd;
11757dd7cddfSDavid du Colombier 				*l = nil;
11767dd7cddfSDavid du Colombier 				if(s == e)
11777dd7cddfSDavid du Colombier 					break;
11787dd7cddfSDavid du Colombier 			}
11797dd7cddfSDavid du Colombier 			if(s == nil)
11807dd7cddfSDavid du Colombier 				return "null address range";
11817dd7cddfSDavid du Colombier 		} else {
11827dd7cddfSDavid du Colombier 			// single address
11837dd7cddfSDavid du Colombier 			if(s != &top){
11847dd7cddfSDavid du Colombier 				*l = s;
11857dd7cddfSDavid du Colombier 				s->cmd = nil;
11867dd7cddfSDavid du Colombier 			}
11877dd7cddfSDavid du Colombier 		}
11887dd7cddfSDavid du Colombier 	}
11897dd7cddfSDavid du Colombier 
11906b6b9ac8SDavid du Colombier 	// insert a space after '!'s and '|'s
11916b6b9ac8SDavid du Colombier 	for(q = p; *q; q++)
11926b6b9ac8SDavid du Colombier 		if(*q != '!' && *q != '|')
11936b6b9ac8SDavid du Colombier 			break;
11946b6b9ac8SDavid du Colombier 	if(q != p && *q != ' '){
11956b6b9ac8SDavid du Colombier 		memmove(q+1, q, strlen(q)+1);
11966b6b9ac8SDavid du Colombier 		*q = ' ';
11976b6b9ac8SDavid du Colombier 	}
11986b6b9ac8SDavid du Colombier 
11999a747e4fSDavid du Colombier 	cmd->an = getfields(p, cmd->av, nelem(cmd->av) - 1, 1, " \t\r\n");
12007dd7cddfSDavid du Colombier 	if(cmd->an == 0 || *cmd->av[0] == 0)
12017dd7cddfSDavid du Colombier 		cmd->f = pcmd;
12027dd7cddfSDavid du Colombier 	else {
12037dd7cddfSDavid du Colombier 		// hack to allow all messages to start with 'd'
12047dd7cddfSDavid du Colombier 		if(*(cmd->av[0]) == 'd' && *(cmd->av[0]+1) != 0){
12057dd7cddfSDavid du Colombier 			cmd->delete = 1;
12067dd7cddfSDavid du Colombier 			cmd->av[0]++;
12077dd7cddfSDavid du Colombier 		}
12087dd7cddfSDavid du Colombier 
12097dd7cddfSDavid du Colombier 		// search command table
12107dd7cddfSDavid du Colombier 		for(i = 0; cmdtab[i].cmd != nil; i++)
12117dd7cddfSDavid du Colombier 			if(strcmp(cmd->av[0], cmdtab[i].cmd) == 0)
12127dd7cddfSDavid du Colombier 				break;
12137dd7cddfSDavid du Colombier 		if(cmdtab[i].cmd == nil)
12147dd7cddfSDavid du Colombier 			return "illegal command";
12157dd7cddfSDavid du Colombier 		if(cmdtab[i].args == 0 && cmd->an > 1){
12167dd7cddfSDavid du Colombier 			snprint(errbuf, sizeof(errbuf), "%s doesn't take an argument", cmdtab[i].cmd);
12177dd7cddfSDavid du Colombier 			return errbuf;
12187dd7cddfSDavid du Colombier 		}
12197dd7cddfSDavid du Colombier 		cmd->f = cmdtab[i].f;
12207dd7cddfSDavid du Colombier 	}
12217dd7cddfSDavid du Colombier 	return nil;
12227dd7cddfSDavid du Colombier }
12237dd7cddfSDavid du Colombier 
12247dd7cddfSDavid du Colombier // inefficient read from standard input
12257dd7cddfSDavid du Colombier char*
readline(char * prompt,char * line,int len)12267dd7cddfSDavid du Colombier readline(char *prompt, char *line, int len)
12277dd7cddfSDavid du Colombier {
12287dd7cddfSDavid du Colombier 	char *p, *e;
12297dd7cddfSDavid du Colombier 	int n;
12307dd7cddfSDavid du Colombier 
12317dd7cddfSDavid du Colombier retry:
12327dd7cddfSDavid du Colombier 	interrupted = 0;
12337dd7cddfSDavid du Colombier 	Bprint(&out, "%s", prompt);
12347dd7cddfSDavid du Colombier 	Bflush(&out);
12357dd7cddfSDavid du Colombier 	e = line + len;
12367dd7cddfSDavid du Colombier 	for(p = line; p < e; p++){
12377dd7cddfSDavid du Colombier 		n = read(0, p, 1);
12387dd7cddfSDavid du Colombier 		if(n < 0){
12397dd7cddfSDavid du Colombier 			if(interrupted)
12407dd7cddfSDavid du Colombier 				goto retry;
12417dd7cddfSDavid du Colombier 			return nil;
12427dd7cddfSDavid du Colombier 		}
12437dd7cddfSDavid du Colombier 		if(n == 0)
12447dd7cddfSDavid du Colombier 			return nil;
12457dd7cddfSDavid du Colombier 		if(*p == '\n')
12467dd7cddfSDavid du Colombier 			break;
12477dd7cddfSDavid du Colombier 	}
12487dd7cddfSDavid du Colombier 	*p = 0;
12497dd7cddfSDavid du Colombier 	return line;
12507dd7cddfSDavid du Colombier }
12517dd7cddfSDavid du Colombier 
12527dd7cddfSDavid du Colombier void
messagecount(Message * m)12537dd7cddfSDavid du Colombier messagecount(Message *m)
12547dd7cddfSDavid du Colombier {
12557dd7cddfSDavid du Colombier 	int i;
12567dd7cddfSDavid du Colombier 
12577dd7cddfSDavid du Colombier 	i = 0;
12587dd7cddfSDavid du Colombier 	for(; m != nil; m = m->next)
12597dd7cddfSDavid du Colombier 		i++;
12605130c3f3SDavid du Colombier 	Bprint(&out, "%d message%s\n", i, plural(i));
12617dd7cddfSDavid du Colombier }
12627dd7cddfSDavid du Colombier 
12637dd7cddfSDavid du Colombier Message*
aichcmd(Message * m,int indent)12649a747e4fSDavid du Colombier aichcmd(Message *m, int indent)
12657dd7cddfSDavid du Colombier {
12669a747e4fSDavid du Colombier 	char	hdr[256];
12677dd7cddfSDavid du Colombier 
12687dd7cddfSDavid du Colombier 	if(m == &top)
12697dd7cddfSDavid du Colombier 		return nil;
12709a747e4fSDavid du Colombier 
12719a747e4fSDavid du Colombier 	snprintHeader(hdr, sizeof(hdr), indent, m);
12727dd7cddfSDavid du Colombier 	Bprint(&out, "%s\n", hdr);
12737dd7cddfSDavid du Colombier 	for(m = m->child; m != nil; m = m->next)
12749a747e4fSDavid du Colombier 		aichcmd(m, indent+1);
12759a747e4fSDavid du Colombier 	return nil;
12769a747e4fSDavid du Colombier }
12779a747e4fSDavid du Colombier 
12789a747e4fSDavid du Colombier Message*
Hcmd(Cmd *,Message * m)12799a747e4fSDavid du Colombier Hcmd(Cmd*, Message *m)
12809a747e4fSDavid du Colombier {
12819a747e4fSDavid du Colombier 	if(m == &top)
12829a747e4fSDavid du Colombier 		return nil;
12839a747e4fSDavid du Colombier 	aichcmd(m, 0);
12849a747e4fSDavid du Colombier 	return nil;
12859a747e4fSDavid du Colombier }
12869a747e4fSDavid du Colombier 
12879a747e4fSDavid du Colombier Message*
hcmd(Cmd *,Message * m)12889a747e4fSDavid du Colombier hcmd(Cmd*, Message *m)
12899a747e4fSDavid du Colombier {
12909a747e4fSDavid du Colombier 	char	hdr[256];
12919a747e4fSDavid du Colombier 
12929a747e4fSDavid du Colombier 	if(m == &top)
12939a747e4fSDavid du Colombier 		return nil;
12949a747e4fSDavid du Colombier 
12959a747e4fSDavid du Colombier 	snprintheader(hdr, sizeof(hdr), m);
12969a747e4fSDavid du Colombier 	Bprint(&out, "%s\n", hdr);
12977dd7cddfSDavid du Colombier 	return nil;
12987dd7cddfSDavid du Colombier }
12997dd7cddfSDavid du Colombier 
13007dd7cddfSDavid du Colombier Message*
bcmd(Cmd *,Message * m)13017dd7cddfSDavid du Colombier bcmd(Cmd*, Message *m)
13027dd7cddfSDavid du Colombier {
13037dd7cddfSDavid du Colombier 	int i;
13047dd7cddfSDavid du Colombier 	Message *om = m;
13057dd7cddfSDavid du Colombier 
13067dd7cddfSDavid du Colombier 	if(m == &top)
13077dd7cddfSDavid du Colombier 		m = top.child;
13087dd7cddfSDavid du Colombier 	for(i = 0; i < 10 && m != nil; i++){
13097dd7cddfSDavid du Colombier 		hcmd(nil, m);
13107dd7cddfSDavid du Colombier 		om = m;
13117dd7cddfSDavid du Colombier 		m = m->next;
13127dd7cddfSDavid du Colombier 	}
13137dd7cddfSDavid du Colombier 
13147dd7cddfSDavid du Colombier 	return om;
13157dd7cddfSDavid du Colombier }
13167dd7cddfSDavid du Colombier 
13177dd7cddfSDavid du Colombier Message*
ncmd(Cmd *,Message * m)13187dd7cddfSDavid du Colombier ncmd(Cmd*, Message *m)
13197dd7cddfSDavid du Colombier {
13207dd7cddfSDavid du Colombier 	if(m == &top)
13217dd7cddfSDavid du Colombier 		return m->child;
13227dd7cddfSDavid du Colombier 	return m->next;
13237dd7cddfSDavid du Colombier }
13247dd7cddfSDavid du Colombier 
13258ccc32efSDavid du Colombier /* turn crlfs into newlines */
13268ccc32efSDavid du Colombier int
decrlf(char * buf,int n)13278ccc32efSDavid du Colombier decrlf(char *buf, int n)
13288ccc32efSDavid du Colombier {
13298ccc32efSDavid du Colombier 	char *nl;
13308ccc32efSDavid du Colombier 	int left;
13318ccc32efSDavid du Colombier 
1332*a50f7bb8SDavid du Colombier 	for(nl = buf, left = n;
1333*a50f7bb8SDavid du Colombier 	    left >= 2 && (nl = memchr(nl, '\r', left)) != nil;
1334*a50f7bb8SDavid du Colombier 	    left = n - (nl - buf))
1335*a50f7bb8SDavid du Colombier 		if(nl[1] == '\n'){	/* newline? delete the cr */
1336*a50f7bb8SDavid du Colombier 			--n;	/* portion left is about to get smaller */
1337*a50f7bb8SDavid du Colombier 			memmove(nl, nl+1, n - (nl - buf));
1338*a50f7bb8SDavid du Colombier 		}else
1339*a50f7bb8SDavid du Colombier 			nl++;
13408ccc32efSDavid du Colombier 	return n;
13418ccc32efSDavid du Colombier }
13428ccc32efSDavid du Colombier 
13437dd7cddfSDavid du Colombier int
printpart(String * s,char * part)13447dd7cddfSDavid du Colombier printpart(String *s, char *part)
13457dd7cddfSDavid du Colombier {
13467dd7cddfSDavid du Colombier 	char buf[4096];
13477dd7cddfSDavid du Colombier 	int n, fd, tot;
13487dd7cddfSDavid du Colombier 	String *path;
13497dd7cddfSDavid du Colombier 
13507dd7cddfSDavid du Colombier 	path = extendpath(s, part);
13517dd7cddfSDavid du Colombier 	fd = open(s_to_c(path), OREAD);
13527dd7cddfSDavid du Colombier 	s_free(path);
13537dd7cddfSDavid du Colombier 	if(fd < 0){
1354556cddebSDavid du Colombier 		fprint(2, "!message disappeared\n");
13557dd7cddfSDavid du Colombier 		return 0;
13567dd7cddfSDavid du Colombier 	}
13577dd7cddfSDavid du Colombier 	tot = 0;
13587dd7cddfSDavid du Colombier 	while((n = read(fd, buf, sizeof(buf))) > 0){
13597dd7cddfSDavid du Colombier 		if(interrupted)
13607dd7cddfSDavid du Colombier 			break;
13618ccc32efSDavid du Colombier 		n = decrlf(buf, n);
1362*a50f7bb8SDavid du Colombier 		if(n > 0 && Bwrite(&out, buf, n) <= 0)
13637dd7cddfSDavid du Colombier 			break;
13647dd7cddfSDavid du Colombier 		tot += n;
13657dd7cddfSDavid du Colombier 	}
13667dd7cddfSDavid du Colombier 	close(fd);
13677dd7cddfSDavid du Colombier 	return tot;
13687dd7cddfSDavid du Colombier }
13697dd7cddfSDavid du Colombier 
13709a747e4fSDavid du Colombier int
printhtml(Message * m)13719a747e4fSDavid du Colombier printhtml(Message *m)
13729a747e4fSDavid du Colombier {
13739a747e4fSDavid du Colombier 	Cmd c;
13749a747e4fSDavid du Colombier 
1375d9306527SDavid du Colombier 	c.an = 3;
13769a747e4fSDavid du Colombier 	c.av[1] = "/bin/htmlfmt";
137723acd784SDavid du Colombier 	c.av[2] = "-l 40 -cutf-8";
13789a747e4fSDavid du Colombier 	Bprint(&out, "!%s\n", c.av[1]);
13799a747e4fSDavid du Colombier 	Bflush(&out);
13809a747e4fSDavid du Colombier 	pipecmd(&c, m);
13819a747e4fSDavid du Colombier 	return 0;
13829a747e4fSDavid du Colombier }
13839a747e4fSDavid du Colombier 
13847dd7cddfSDavid du Colombier Message*
Pcmd(Cmd *,Message * m)13857dd7cddfSDavid du Colombier Pcmd(Cmd*, Message *m)
13867dd7cddfSDavid du Colombier {
13877dd7cddfSDavid du Colombier 	if(m == &top)
13887dd7cddfSDavid du Colombier 		return &top;
13897dd7cddfSDavid du Colombier 	if(m->parent == &top)
13907dd7cddfSDavid du Colombier 		printpart(m->path, "unixheader");
13917dd7cddfSDavid du Colombier 	printpart(m->path, "raw");
13927dd7cddfSDavid du Colombier 	return m;
13937dd7cddfSDavid du Colombier }
13947dd7cddfSDavid du Colombier 
13959a747e4fSDavid du Colombier void
compress(char * p)13969a747e4fSDavid du Colombier compress(char *p)
13979a747e4fSDavid du Colombier {
13989a747e4fSDavid du Colombier 	char *np;
13999a747e4fSDavid du Colombier 	int last;
14009a747e4fSDavid du Colombier 
14019a747e4fSDavid du Colombier 	last = ' ';
14029a747e4fSDavid du Colombier 	for(np = p; *p; p++){
14039a747e4fSDavid du Colombier 		if(*p != ' ' || last != ' '){
14049a747e4fSDavid du Colombier 			last = *p;
14059a747e4fSDavid du Colombier 			*np++ = last;
14069a747e4fSDavid du Colombier 		}
14079a747e4fSDavid du Colombier 	}
14089a747e4fSDavid du Colombier 	*np = 0;
14099a747e4fSDavid du Colombier }
14109a747e4fSDavid du Colombier 
14118ccc32efSDavid du Colombier /*
14128ccc32efSDavid du Colombier  * find the best alternative part.
14138ccc32efSDavid du Colombier  *
14148ccc32efSDavid du Colombier  * turkeys have started emitting empty text/plain parts,
14158ccc32efSDavid du Colombier  * with the actual content in a text/html part, which complicates the choice.
14168ccc32efSDavid du Colombier  *
14178ccc32efSDavid du Colombier  * bigger turkeys emit a tiny base64-encoded text/plain part,
14188ccc32efSDavid du Colombier  * a small base64-encoded text/html part, and the real content is in
14198ccc32efSDavid du Colombier  * a text/calendar part.
14208ccc32efSDavid du Colombier  *
14218ccc32efSDavid du Colombier  * the magic lengths are empirically derived.
14228ccc32efSDavid du Colombier  * as turkeys devolve and mutate, this will only get worse.
14238ccc32efSDavid du Colombier  */
14248ccc32efSDavid du Colombier static Message*
bestalt(Message * m)14258ccc32efSDavid du Colombier bestalt(Message *m)
14268ccc32efSDavid du Colombier {
1427*a50f7bb8SDavid du Colombier 	Message *nm, *realplain, *realhtml, *realcal;
14288ccc32efSDavid du Colombier 	Ctype *cp;
14298ccc32efSDavid du Colombier 
14308ccc32efSDavid du Colombier 	realplain = realhtml = realcal = nil;
14318ccc32efSDavid du Colombier 	for(nm = m->child; nm != nil; nm = nm->next){
14328ccc32efSDavid du Colombier 		cp = findctype(nm);
14338ccc32efSDavid du Colombier 		if(cp->ext != nil)
14348ccc32efSDavid du Colombier 			if(strncmp(cp->ext, "txt", 3) == 0 && nm->len >= 88)
14358ccc32efSDavid du Colombier 				realplain = nm;
14368ccc32efSDavid du Colombier 			else if(strncmp(cp->ext, "html", 3) == 0 &&
14378ccc32efSDavid du Colombier 			    nm->len >= 670)
14388ccc32efSDavid du Colombier 				realhtml = nm;
14398ccc32efSDavid du Colombier 			else if(strncmp(cp->ext, "ics", 3) == 0)
14408ccc32efSDavid du Colombier 				realcal = nm;
14418ccc32efSDavid du Colombier 	}
14428ccc32efSDavid du Colombier 	if(realplain == nil && realhtml == nil && realcal)
14438ccc32efSDavid du Colombier 		return realcal;			/* super-turkey */
14448ccc32efSDavid du Colombier 	else if(realplain == nil && realhtml)
14458ccc32efSDavid du Colombier 		return realhtml;		/* regular turkey */
14468ccc32efSDavid du Colombier 	else
14478ccc32efSDavid du Colombier 		return realplain;
14488ccc32efSDavid du Colombier }
14498ccc32efSDavid du Colombier 
14508ccc32efSDavid du Colombier static void
pralt(Message * m)14518ccc32efSDavid du Colombier pralt(Message *m)
14528ccc32efSDavid du Colombier {
14538ccc32efSDavid du Colombier 	Message *nm;
14548ccc32efSDavid du Colombier 	Ctype *cp;
14558ccc32efSDavid du Colombier 
14568ccc32efSDavid du Colombier 	nm = bestalt(m);
14578ccc32efSDavid du Colombier 	if(nm == nil)
14588ccc32efSDavid du Colombier 		/* no winner.  print the first displayable part. */
14598ccc32efSDavid du Colombier 		for(nm = m->child; nm != nil; nm = nm->next){
14608ccc32efSDavid du Colombier 			cp = findctype(nm);
14618ccc32efSDavid du Colombier 			if(cp->display)
14628ccc32efSDavid du Colombier 				break;
14638ccc32efSDavid du Colombier 		}
14648ccc32efSDavid du Colombier 	if(nm != nil)
14658ccc32efSDavid du Colombier 		pcmd(nil, nm);
14668ccc32efSDavid du Colombier 	else
14678ccc32efSDavid du Colombier 		hcmd(nil, m);
14688ccc32efSDavid du Colombier }
14698ccc32efSDavid du Colombier 
14707dd7cddfSDavid du Colombier Message*
pcmd(Cmd *,Message * m)14717dd7cddfSDavid du Colombier pcmd(Cmd*, Message *m)
14727dd7cddfSDavid du Colombier {
14737dd7cddfSDavid du Colombier 	Message *nm;
14747dd7cddfSDavid du Colombier 	Ctype *cp;
14757dd7cddfSDavid du Colombier 	String *s;
14769a747e4fSDavid du Colombier 	char buf[128];
14777dd7cddfSDavid du Colombier 
1478*a50f7bb8SDavid du Colombier 	if(m == nil)
1479*a50f7bb8SDavid du Colombier 		return m;
14807dd7cddfSDavid du Colombier 	if(m == &top)
14817dd7cddfSDavid du Colombier 		return &top;
14827dd7cddfSDavid du Colombier 	if(m->parent == &top)
14837dd7cddfSDavid du Colombier 		printpart(m->path, "unixheader");
14847dd7cddfSDavid du Colombier 	if(printpart(m->path, "header") > 0)
14857dd7cddfSDavid du Colombier 		Bprint(&out, "\n");
14865f3668f2SDavid du Colombier 	cp = findctype(m);
14877dd7cddfSDavid du Colombier 	if(cp->display){
14889a747e4fSDavid du Colombier 		if(strcmp(m->type, "text/html") == 0)
14899a747e4fSDavid du Colombier 			printhtml(m);
14909a747e4fSDavid du Colombier 		else
14917dd7cddfSDavid du Colombier 			printpart(m->path, "body");
14928ccc32efSDavid du Colombier 	} else if(strcmp(m->type, "multipart/alternative") == 0)
14938ccc32efSDavid du Colombier 		pralt(m);
14948ccc32efSDavid du Colombier 	else if(strncmp(m->type, "multipart/", 10) == 0){
14957dd7cddfSDavid du Colombier 		nm = m->child;
14967dd7cddfSDavid du Colombier 		if(nm != nil){
14979a747e4fSDavid du Colombier 			// always print first part
14987dd7cddfSDavid du Colombier 			pcmd(nil, nm);
14999a747e4fSDavid du Colombier 
15007dd7cddfSDavid du Colombier 			for(nm = nm->next; nm != nil; nm = nm->next){
15017dd7cddfSDavid du Colombier 				s = rooted(s_clone(nm->path));
15025f3668f2SDavid du Colombier 				cp = findctype(nm);
15039a747e4fSDavid du Colombier 				snprintHeader(buf, sizeof buf, -1, nm);
15049a747e4fSDavid du Colombier 				compress(buf);
15059a747e4fSDavid du Colombier 				if(strcmp(nm->disposition, "inline") == 0){
15067dd7cddfSDavid du Colombier 					if(cp->ext != nil)
15079a747e4fSDavid du Colombier 						Bprint(&out, "\n--- %s %s/body.%s\n\n",
15089a747e4fSDavid du Colombier 							buf, s_to_c(s), cp->ext);
15097dd7cddfSDavid du Colombier 					else
15109a747e4fSDavid du Colombier 						Bprint(&out, "\n--- %s %s/body\n\n",
15119a747e4fSDavid du Colombier 							buf, s_to_c(s));
15127dd7cddfSDavid du Colombier 					pcmd(nil, nm);
15137dd7cddfSDavid du Colombier 				} else {
15147dd7cddfSDavid du Colombier 					if(cp->ext != nil)
15159a747e4fSDavid du Colombier 						Bprint(&out, "\n!--- %s %s/body.%s\n",
15169a747e4fSDavid du Colombier 							buf, s_to_c(s), cp->ext);
15177dd7cddfSDavid du Colombier 					else
15189a747e4fSDavid du Colombier 						Bprint(&out, "\n!--- %s %s/body\n",
15199a747e4fSDavid du Colombier 							buf, s_to_c(s));
15207dd7cddfSDavid du Colombier 				}
15219a747e4fSDavid du Colombier 				s_free(s);
15227dd7cddfSDavid du Colombier 			}
15237dd7cddfSDavid du Colombier 		} else {
15247dd7cddfSDavid du Colombier 			hcmd(nil, m);
15257dd7cddfSDavid du Colombier 		}
15269a747e4fSDavid du Colombier 	} else if(strcmp(m->type, "message/rfc822") == 0){
15279a747e4fSDavid du Colombier 		pcmd(nil, m->child);
15289a747e4fSDavid du Colombier 	} else if(plumb(m, cp) >= 0)
15299a747e4fSDavid du Colombier 		Bprint(&out, "\n!--- using plumber to display message of type %s\n", m->type);
15309a747e4fSDavid du Colombier 	else
15319a747e4fSDavid du Colombier 		Bprint(&out, "\n!--- cannot display messages of type %s\n", m->type);
15327dd7cddfSDavid du Colombier 	return m;
15337dd7cddfSDavid du Colombier }
15347dd7cddfSDavid du Colombier 
153580ee5cbfSDavid du Colombier void
printpartindented(String * s,char * part,char * indent)153680ee5cbfSDavid du Colombier printpartindented(String *s, char *part, char *indent)
153780ee5cbfSDavid du Colombier {
153880ee5cbfSDavid du Colombier 	char *p;
153980ee5cbfSDavid du Colombier 	String *path;
154080ee5cbfSDavid du Colombier 	Biobuf *b;
154180ee5cbfSDavid du Colombier 
154280ee5cbfSDavid du Colombier 	path = extendpath(s, part);
154380ee5cbfSDavid du Colombier 	b = Bopen(s_to_c(path), OREAD);
154480ee5cbfSDavid du Colombier 	s_free(path);
154580ee5cbfSDavid du Colombier 	if(b == nil){
1546556cddebSDavid du Colombier 		fprint(2, "!message disappeared\n");
154780ee5cbfSDavid du Colombier 		return;
154880ee5cbfSDavid du Colombier 	}
154980ee5cbfSDavid du Colombier 	while((p = Brdline(b, '\n')) != nil){
155080ee5cbfSDavid du Colombier 		if(interrupted)
155180ee5cbfSDavid du Colombier 			break;
155280ee5cbfSDavid du Colombier 		p[Blinelen(b)-1] = 0;
15534d29a2fcSDavid du Colombier 		if(Bprint(&out, "%s%s\n", indent, p) < 0)
155480ee5cbfSDavid du Colombier 			break;
155580ee5cbfSDavid du Colombier 	}
155680ee5cbfSDavid du Colombier 	Bprint(&out, "\n");
155780ee5cbfSDavid du Colombier 	Bterm(b);
155880ee5cbfSDavid du Colombier }
155980ee5cbfSDavid du Colombier 
156080ee5cbfSDavid du Colombier Message*
quotecmd(Cmd *,Message * m)156180ee5cbfSDavid du Colombier quotecmd(Cmd*, Message *m)
156280ee5cbfSDavid du Colombier {
156380ee5cbfSDavid du Colombier 	Message *nm;
156480ee5cbfSDavid du Colombier 	Ctype *cp;
156580ee5cbfSDavid du Colombier 
156680ee5cbfSDavid du Colombier 	if(m == &top)
156780ee5cbfSDavid du Colombier 		return &top;
156880ee5cbfSDavid du Colombier 	Bprint(&out, "\n");
156980ee5cbfSDavid du Colombier 	if(m->from != nil && *m->from)
157080ee5cbfSDavid du Colombier 		Bprint(&out, "On %s, %s wrote:\n", m->date, m->from);
15715f3668f2SDavid du Colombier 	cp = findctype(m);
157280ee5cbfSDavid du Colombier 	if(cp->display){
157380ee5cbfSDavid du Colombier 		printpartindented(m->path, "body", "> ");
157480ee5cbfSDavid du Colombier 	} else if(strcmp(m->type, "multipart/alternative") == 0){
157580ee5cbfSDavid du Colombier 		for(nm = m->child; nm != nil; nm = nm->next){
15765f3668f2SDavid du Colombier 			cp = findctype(nm);
157780ee5cbfSDavid du Colombier 			if(cp->ext != nil && strncmp(cp->ext, "txt", 3) == 0)
157880ee5cbfSDavid du Colombier 				break;
157980ee5cbfSDavid du Colombier 		}
158080ee5cbfSDavid du Colombier 		if(nm == nil)
158180ee5cbfSDavid du Colombier 			for(nm = m->child; nm != nil; nm = nm->next){
15825f3668f2SDavid du Colombier 				cp = findctype(nm);
158380ee5cbfSDavid du Colombier 				if(cp->display)
158480ee5cbfSDavid du Colombier 					break;
158580ee5cbfSDavid du Colombier 			}
158680ee5cbfSDavid du Colombier 		if(nm != nil)
158780ee5cbfSDavid du Colombier 			quotecmd(nil, nm);
158880ee5cbfSDavid du Colombier 	} else if(strncmp(m->type, "multipart/", 10) == 0){
158980ee5cbfSDavid du Colombier 		nm = m->child;
159080ee5cbfSDavid du Colombier 		if(nm != nil){
15915f3668f2SDavid du Colombier 			cp = findctype(nm);
159280ee5cbfSDavid du Colombier 			if(cp->display || strncmp(m->type, "multipart/", 10) == 0)
159380ee5cbfSDavid du Colombier 				quotecmd(nil, nm);
159480ee5cbfSDavid du Colombier 		}
159580ee5cbfSDavid du Colombier 	}
159680ee5cbfSDavid du Colombier 	return m;
159780ee5cbfSDavid du Colombier }
159880ee5cbfSDavid du Colombier 
1599dc5a79c1SDavid du Colombier // really delete messages
16007dd7cddfSDavid du Colombier Message*
flushdeleted(Message * cur)1601dc5a79c1SDavid du Colombier flushdeleted(Message *cur)
16027dd7cddfSDavid du Colombier {
1603dc5a79c1SDavid du Colombier 	Message *m, **l;
16047dd7cddfSDavid du Colombier 	char buf[1024], *p, *e, *msg;
16057dd7cddfSDavid du Colombier 	int deld, n, fd;
16065130c3f3SDavid du Colombier 	int i;
16077dd7cddfSDavid du Colombier 
1608dc5a79c1SDavid du Colombier 	doflush = 0;
16097dd7cddfSDavid du Colombier 	deld = 0;
16107dd7cddfSDavid du Colombier 
16117dd7cddfSDavid du Colombier 	fd = open("/mail/fs/ctl", ORDWR);
16127dd7cddfSDavid du Colombier 	if(fd < 0){
16137dd7cddfSDavid du Colombier 		fprint(2, "!can't delete mail, opening /mail/fs/ctl: %r\n");
16147dd7cddfSDavid du Colombier 		exitfs(0);
16157dd7cddfSDavid du Colombier 	}
16167dd7cddfSDavid du Colombier 	e = &buf[sizeof(buf)];
16177dd7cddfSDavid du Colombier 	p = seprint(buf, e, "delete %s", mbname);
16187dd7cddfSDavid du Colombier 	n = 0;
1619dc5a79c1SDavid du Colombier 	for(l = &top.child; *l != nil;){
1620dc5a79c1SDavid du Colombier 		m = *l;
1621dc5a79c1SDavid du Colombier 		if(!m->deleted){
1622dc5a79c1SDavid du Colombier 			l = &(*l)->next;
1623dc5a79c1SDavid du Colombier 			continue;
1624dc5a79c1SDavid du Colombier 		}
1625dc5a79c1SDavid du Colombier 
1626dc5a79c1SDavid du Colombier 		// don't return a pointer to a deleted message
1627dc5a79c1SDavid du Colombier 		if(m == cur)
1628dc5a79c1SDavid du Colombier 			cur = m->next;
1629dc5a79c1SDavid du Colombier 
16307dd7cddfSDavid du Colombier 		deld++;
16317dd7cddfSDavid du Colombier 		msg = strrchr(s_to_c(m->path), '/');
16327dd7cddfSDavid du Colombier 		if(msg == nil)
16337dd7cddfSDavid du Colombier 			msg = s_to_c(m->path);
16347dd7cddfSDavid du Colombier 		else
16357dd7cddfSDavid du Colombier 			msg++;
16367dd7cddfSDavid du Colombier 		if(e-p < 10){
16377dd7cddfSDavid du Colombier 			write(fd, buf, p-buf);
16387dd7cddfSDavid du Colombier 			n = 0;
16397dd7cddfSDavid du Colombier 			p = seprint(buf, e, "delete %s", mbname);
16407dd7cddfSDavid du Colombier 		}
16417dd7cddfSDavid du Colombier 		p = seprint(p, e, " %s", msg);
16427dd7cddfSDavid du Colombier 		n++;
1643dc5a79c1SDavid du Colombier 
1644dc5a79c1SDavid du Colombier 		// unchain and free
1645dc5a79c1SDavid du Colombier 		*l = m->next;
1646dc5a79c1SDavid du Colombier 		if(m->next)
1647dc5a79c1SDavid du Colombier 			m->next->prev = m->prev;
1648dc5a79c1SDavid du Colombier 		freemessage(m);
16497dd7cddfSDavid du Colombier 	}
16507dd7cddfSDavid du Colombier 	if(n)
16517dd7cddfSDavid du Colombier 		write(fd, buf, p-buf);
165280ee5cbfSDavid du Colombier 
1653dc5a79c1SDavid du Colombier 	close(fd);
16547dd7cddfSDavid du Colombier 
16555130c3f3SDavid du Colombier 	if(deld)
16565130c3f3SDavid du Colombier 		Bprint(&out, "!%d message%s deleted\n", deld, plural(deld));
16575130c3f3SDavid du Colombier 
16585130c3f3SDavid du Colombier 	// renumber
16595130c3f3SDavid du Colombier 	i = 1;
16605130c3f3SDavid du Colombier 	for(m = top.child; m != nil; m = m->next)
16615130c3f3SDavid du Colombier 		m->id = natural ? m->fileno : i++;
1662dc5a79c1SDavid du Colombier 
166323acd784SDavid du Colombier 	// if we're out of messages, go back to first
166423acd784SDavid du Colombier 	// if no first, return the fake first
166523acd784SDavid du Colombier 	if(cur == nil){
166623acd784SDavid du Colombier 		if(top.child)
1667dc5a79c1SDavid du Colombier 			return top.child;
166823acd784SDavid du Colombier 		else
166923acd784SDavid du Colombier 			return &top;
167023acd784SDavid du Colombier 	}
1671dc5a79c1SDavid du Colombier 	return cur;
1672dc5a79c1SDavid du Colombier }
1673dc5a79c1SDavid du Colombier 
1674dc5a79c1SDavid du Colombier Message*
qcmd(Cmd *,Message *)1675dc5a79c1SDavid du Colombier qcmd(Cmd*, Message*)
1676dc5a79c1SDavid du Colombier {
1677dc5a79c1SDavid du Colombier 	flushdeleted(nil);
1678dc5a79c1SDavid du Colombier 
1679dc5a79c1SDavid du Colombier 	if(didopen)
1680dc5a79c1SDavid du Colombier 		closemb();
16817dd7cddfSDavid du Colombier 	Bflush(&out);
16827dd7cddfSDavid du Colombier 
16837dd7cddfSDavid du Colombier 	exitfs(0);
16847dd7cddfSDavid du Colombier 	return nil;	// not reached
16857dd7cddfSDavid du Colombier }
16867dd7cddfSDavid du Colombier 
16877dd7cddfSDavid du Colombier Message*
ycmd(Cmd *,Message * m)1688dc5a79c1SDavid du Colombier ycmd(Cmd*, Message *m)
1689dc5a79c1SDavid du Colombier {
1690dc5a79c1SDavid du Colombier 	doflush = 1;
1691dc5a79c1SDavid du Colombier 
1692dc5a79c1SDavid du Colombier 	return icmd(nil, m);
1693dc5a79c1SDavid du Colombier }
1694dc5a79c1SDavid du Colombier 
1695dc5a79c1SDavid du Colombier Message*
xcmd(Cmd *,Message *)16967dd7cddfSDavid du Colombier xcmd(Cmd*, Message*)
16977dd7cddfSDavid du Colombier {
16987dd7cddfSDavid du Colombier 	exitfs(0);
16997dd7cddfSDavid du Colombier 	return nil;	// not reached
17007dd7cddfSDavid du Colombier }
17017dd7cddfSDavid du Colombier 
17027dd7cddfSDavid du Colombier Message*
eqcmd(Cmd *,Message * m)17037dd7cddfSDavid du Colombier eqcmd(Cmd*, Message *m)
17047dd7cddfSDavid du Colombier {
17057dd7cddfSDavid du Colombier 	if(m == &top)
17067dd7cddfSDavid du Colombier 		Bprint(&out, "0\n");
17077dd7cddfSDavid du Colombier 	else
17087dd7cddfSDavid du Colombier 		Bprint(&out, "%d\n", m->id);
17097dd7cddfSDavid du Colombier 	return nil;
17107dd7cddfSDavid du Colombier }
17117dd7cddfSDavid du Colombier 
17127dd7cddfSDavid du Colombier Message*
dcmd(Cmd *,Message * m)17137dd7cddfSDavid du Colombier dcmd(Cmd*, Message *m)
17147dd7cddfSDavid du Colombier {
17157dd7cddfSDavid du Colombier 	if(m == &top){
17167dd7cddfSDavid du Colombier 		Bprint(&out, "!address\n");
17177dd7cddfSDavid du Colombier 		return nil;
17187dd7cddfSDavid du Colombier 	}
17197dd7cddfSDavid du Colombier 	while(m->parent != &top)
17207dd7cddfSDavid du Colombier 		m = m->parent;
17217dd7cddfSDavid du Colombier 	m->deleted = 1;
17227dd7cddfSDavid du Colombier 	return m;
17237dd7cddfSDavid du Colombier }
17247dd7cddfSDavid du Colombier 
17257dd7cddfSDavid du Colombier Message*
ucmd(Cmd *,Message * m)17267dd7cddfSDavid du Colombier ucmd(Cmd*, Message *m)
17277dd7cddfSDavid du Colombier {
17287dd7cddfSDavid du Colombier 	if(m == &top)
17297dd7cddfSDavid du Colombier 		return nil;
17307dd7cddfSDavid du Colombier 	while(m->parent != &top)
17317dd7cddfSDavid du Colombier 		m = m->parent;
1732dc5a79c1SDavid du Colombier 	if(m->deleted < 0)
1733dc5a79c1SDavid du Colombier 		Bprint(&out, "!can't undelete, already flushed\n");
17347dd7cddfSDavid du Colombier 	m->deleted = 0;
17357dd7cddfSDavid du Colombier 	return m;
17367dd7cddfSDavid du Colombier }
17377dd7cddfSDavid du Colombier 
17387dd7cddfSDavid du Colombier 
17397dd7cddfSDavid du Colombier Message*
icmd(Cmd *,Message * m)17407dd7cddfSDavid du Colombier icmd(Cmd*, Message *m)
17417dd7cddfSDavid du Colombier {
17427dd7cddfSDavid du Colombier 	int n;
17437dd7cddfSDavid du Colombier 
17447dd7cddfSDavid du Colombier 	n = dir2message(&top, reverse);
17457dd7cddfSDavid du Colombier 	if(n > 0)
17465130c3f3SDavid du Colombier 		Bprint(&out, "%d new message%s\n", n, plural(n));
17477dd7cddfSDavid du Colombier 	return m;
17487dd7cddfSDavid du Colombier }
17497dd7cddfSDavid du Colombier 
17507dd7cddfSDavid du Colombier Message*
helpcmd(Cmd *,Message * m)17517dd7cddfSDavid du Colombier helpcmd(Cmd*, Message *m)
17527dd7cddfSDavid du Colombier {
17537dd7cddfSDavid du Colombier 	int i;
17547dd7cddfSDavid du Colombier 
17557dd7cddfSDavid du Colombier 	Bprint(&out, "Commands are of the form [<range>] <command> [args]\n");
17567dd7cddfSDavid du Colombier 	Bprint(&out, "<range> := <addr> | <addr>','<addr>| 'g'<search>\n");
17577dd7cddfSDavid du Colombier 	Bprint(&out, "<addr> := '.' | '$' | '^' | <number> | <search> | <addr>'+'<addr> | <addr>'-'<addr>\n");
17587dd7cddfSDavid du Colombier 	Bprint(&out, "<search> := '/'<regexp>'/' | '?'<regexp>'?' | '%%'<regexp>'%%'\n");
17597dd7cddfSDavid du Colombier 	Bprint(&out, "<command> :=\n");
17607dd7cddfSDavid du Colombier 	for(i = 0; cmdtab[i].cmd != nil; i++)
17617dd7cddfSDavid du Colombier 		Bprint(&out, "%s\n", cmdtab[i].help);
17627dd7cddfSDavid du Colombier 	return m;
17637dd7cddfSDavid du Colombier }
17647dd7cddfSDavid du Colombier 
17657dd7cddfSDavid du Colombier int
tomailer(char ** av)17667dd7cddfSDavid du Colombier tomailer(char **av)
17677dd7cddfSDavid du Colombier {
17689a747e4fSDavid du Colombier 	Waitmsg *w;
17697dd7cddfSDavid du Colombier 	int pid, i;
17707dd7cddfSDavid du Colombier 
17717dd7cddfSDavid du Colombier 	// start the mailer and get out of the way
17727dd7cddfSDavid du Colombier 	switch(pid = fork()){
17737dd7cddfSDavid du Colombier 	case -1:
17747dd7cddfSDavid du Colombier 		fprint(2, "can't fork: %r\n");
17757dd7cddfSDavid du Colombier 		return -1;
17767dd7cddfSDavid du Colombier 	case 0:
17777dd7cddfSDavid du Colombier 		Bprint(&out, "!/bin/upas/marshal");
17783f8719e6SDavid du Colombier 		for(i = 1; av[i]; i++)
17793f8719e6SDavid du Colombier 			Bprint(&out, " %q", av[i]);
17807dd7cddfSDavid du Colombier 		Bprint(&out, "\n");
17817dd7cddfSDavid du Colombier 		Bflush(&out);
17827dd7cddfSDavid du Colombier 		av[0] = "marshal";
17837dd7cddfSDavid du Colombier 		chdir(wd);
17847dd7cddfSDavid du Colombier 		exec("/bin/upas/marshal", av);
17857dd7cddfSDavid du Colombier 		fprint(2, "couldn't exec /bin/upas/marshal\n");
17867dd7cddfSDavid du Colombier 		exits(0);
17877dd7cddfSDavid du Colombier 	default:
17889a747e4fSDavid du Colombier 		w = wait();
17899a747e4fSDavid du Colombier 		if(w == nil){
17907dd7cddfSDavid du Colombier 			if(interrupted)
17917dd7cddfSDavid du Colombier 				postnote(PNPROC, pid, "die");
17929a747e4fSDavid du Colombier 			waitpid();
17937dd7cddfSDavid du Colombier 			return -1;
17947dd7cddfSDavid du Colombier 		}
17959a747e4fSDavid du Colombier 		if(w->msg[0]){
17969a747e4fSDavid du Colombier 			fprint(2, "mailer failed: %s\n", w->msg);
17979a747e4fSDavid du Colombier 			free(w);
17987dd7cddfSDavid du Colombier 			return -1;
17997dd7cddfSDavid du Colombier 		}
18009a747e4fSDavid du Colombier 		free(w);
18017dd7cddfSDavid du Colombier 		Bprint(&out, "!\n");
18027dd7cddfSDavid du Colombier 		break;
18037dd7cddfSDavid du Colombier 	}
18047dd7cddfSDavid du Colombier 	return 0;
18057dd7cddfSDavid du Colombier }
18067dd7cddfSDavid du Colombier 
18077dd7cddfSDavid du Colombier //
18087dd7cddfSDavid du Colombier // like tokenize but obey "" quoting
18097dd7cddfSDavid du Colombier //
18107dd7cddfSDavid du Colombier int
tokenize822(char * str,char ** args,int max)18117dd7cddfSDavid du Colombier tokenize822(char *str, char **args, int max)
18127dd7cddfSDavid du Colombier {
18137dd7cddfSDavid du Colombier 	int na;
18147dd7cddfSDavid du Colombier 	int intok = 0, inquote = 0;
18157dd7cddfSDavid du Colombier 
18167dd7cddfSDavid du Colombier 	if(max <= 0)
18177dd7cddfSDavid du Colombier 		return 0;
18187dd7cddfSDavid du Colombier 	for(na=0; ;str++)
18197dd7cddfSDavid du Colombier 		switch(*str) {
18207dd7cddfSDavid du Colombier 		case ' ':
18217dd7cddfSDavid du Colombier 		case '\t':
18227dd7cddfSDavid du Colombier 			if(inquote)
18237dd7cddfSDavid du Colombier 				goto Default;
18247dd7cddfSDavid du Colombier 			/* fall through */
18257dd7cddfSDavid du Colombier 		case '\n':
18267dd7cddfSDavid du Colombier 			*str = 0;
18277dd7cddfSDavid du Colombier 			if(!intok)
18287dd7cddfSDavid du Colombier 				continue;
18297dd7cddfSDavid du Colombier 			intok = 0;
18307dd7cddfSDavid du Colombier 			if(na < max)
18317dd7cddfSDavid du Colombier 				continue;
18327dd7cddfSDavid du Colombier 			/* fall through */
18337dd7cddfSDavid du Colombier 		case 0:
18347dd7cddfSDavid du Colombier 			return na;
18357dd7cddfSDavid du Colombier 		case '"':
18367dd7cddfSDavid du Colombier 			inquote ^= 1;
18377dd7cddfSDavid du Colombier 			/* fall through */
18387dd7cddfSDavid du Colombier 		Default:
18397dd7cddfSDavid du Colombier 		default:
18407dd7cddfSDavid du Colombier 			if(intok)
18417dd7cddfSDavid du Colombier 				continue;
18427dd7cddfSDavid du Colombier 			args[na++] = str;
18437dd7cddfSDavid du Colombier 			intok = 1;
18447dd7cddfSDavid du Colombier 		}
18457dd7cddfSDavid du Colombier }
18467dd7cddfSDavid du Colombier 
18479739e468SDavid du Colombier /* return reply-to address & set *nmp to corresponding Message */
18489739e468SDavid du Colombier static char *
getreplyto(Message * m,Message ** nmp)18499739e468SDavid du Colombier getreplyto(Message *m, Message **nmp)
18509739e468SDavid du Colombier {
18519739e468SDavid du Colombier 	Message *nm;
18529739e468SDavid du Colombier 
18539739e468SDavid du Colombier 	for(nm = m; nm != &top; nm = nm->parent)
18549739e468SDavid du Colombier  		if(*nm->replyto != 0)
18559739e468SDavid du Colombier 			break;
18569739e468SDavid du Colombier 	*nmp = nm;
18579739e468SDavid du Colombier 	return nm? nm->replyto: nil;
18589739e468SDavid du Colombier }
18599739e468SDavid du Colombier 
18607dd7cddfSDavid du Colombier Message*
rcmd(Cmd * c,Message * m)18617dd7cddfSDavid du Colombier rcmd(Cmd *c, Message *m)
18627dd7cddfSDavid du Colombier {
18639739e468SDavid du Colombier 	char *addr;
18647dd7cddfSDavid du Colombier 	char *av[128];
18657dd7cddfSDavid du Colombier 	int i, ai = 1;
18669739e468SDavid du Colombier 	String *from, *rpath, *path = nil, *subject = nil;
18677dd7cddfSDavid du Colombier 	Message *nm;
18687dd7cddfSDavid du Colombier 
18697dd7cddfSDavid du Colombier 	if(m == &top){
18707dd7cddfSDavid du Colombier 		Bprint(&out, "!address\n");
18717dd7cddfSDavid du Colombier 		return nil;
18727dd7cddfSDavid du Colombier 	}
18737dd7cddfSDavid du Colombier 
18749739e468SDavid du Colombier 	addr = getreplyto(m, &nm);
18757dd7cddfSDavid du Colombier 	if(addr == nil){
18767dd7cddfSDavid du Colombier 		Bprint(&out, "!no reply address\n");
18777dd7cddfSDavid du Colombier 		return nil;
18787dd7cddfSDavid du Colombier 	}
18797dd7cddfSDavid du Colombier 	if(nm == &top){
18807dd7cddfSDavid du Colombier 		print("!noone to reply to\n");
18817dd7cddfSDavid du Colombier 		return nil;
18827dd7cddfSDavid du Colombier 	}
18837dd7cddfSDavid du Colombier 
18847dd7cddfSDavid du Colombier 	for(nm = m; nm != &top; nm = nm->parent){
18857dd7cddfSDavid du Colombier 		if(*nm->subject){
18867dd7cddfSDavid du Colombier 			av[ai++] = "-s";
18877dd7cddfSDavid du Colombier 			subject = addrecolon(nm->subject);
18889739e468SDavid du Colombier 			av[ai++] = s_to_c(subject);
18897dd7cddfSDavid du Colombier 			break;
18907dd7cddfSDavid du Colombier 		}
18917dd7cddfSDavid du Colombier 	}
18927dd7cddfSDavid du Colombier 
1893106486e8SDavid du Colombier 	av[ai++] = "-R";
1894106486e8SDavid du Colombier 	rpath = rooted(s_clone(m->path));
1895106486e8SDavid du Colombier 	av[ai++] = s_to_c(rpath);
1896106486e8SDavid du Colombier 
18977dd7cddfSDavid du Colombier 	if(strchr(c->av[0], 'f') != nil){
18987dd7cddfSDavid du Colombier 		fcmd(c, m);
18997dd7cddfSDavid du Colombier 		av[ai++] = "-F";
19007dd7cddfSDavid du Colombier 	}
19017dd7cddfSDavid du Colombier 
19027dd7cddfSDavid du Colombier 	if(strchr(c->av[0], 'R') != nil){
19037dd7cddfSDavid du Colombier 		av[ai++] = "-t";
19047dd7cddfSDavid du Colombier 		av[ai++] = "message/rfc822";
19057dd7cddfSDavid du Colombier 		av[ai++] = "-A";
19067dd7cddfSDavid du Colombier 		path = rooted(extendpath(m->path, "raw"));
19077dd7cddfSDavid du Colombier 		av[ai++] = s_to_c(path);
19087dd7cddfSDavid du Colombier 	}
19097dd7cddfSDavid du Colombier 
19107dd7cddfSDavid du Colombier 	for(i = 1; i < c->an && ai < nelem(av)-1; i++)
19117dd7cddfSDavid du Colombier 		av[ai++] = c->av[i];
19127dd7cddfSDavid du Colombier 	from = s_copy(addr);
19137dd7cddfSDavid du Colombier 	ai += tokenize822(s_to_c(from), &av[ai], nelem(av) - ai);
19147dd7cddfSDavid du Colombier 	av[ai] = 0;
19157dd7cddfSDavid du Colombier 	if(tomailer(av) < 0)
19167dd7cddfSDavid du Colombier 		m = nil;
19177dd7cddfSDavid du Colombier 	s_free(path);
1918106486e8SDavid du Colombier 	s_free(rpath);
19197dd7cddfSDavid du Colombier 	s_free(subject);
19207dd7cddfSDavid du Colombier 	s_free(from);
19217dd7cddfSDavid du Colombier 	return m;
19227dd7cddfSDavid du Colombier }
19237dd7cddfSDavid du Colombier 
19247dd7cddfSDavid du Colombier Message*
mcmd(Cmd * c,Message * m)19257dd7cddfSDavid du Colombier mcmd(Cmd *c, Message *m)
19267dd7cddfSDavid du Colombier {
19277dd7cddfSDavid du Colombier 	char **av;
19287dd7cddfSDavid du Colombier 	int i, ai;
19297dd7cddfSDavid du Colombier 	String *path;
19307dd7cddfSDavid du Colombier 
19317dd7cddfSDavid du Colombier 	if(m == &top){
19327dd7cddfSDavid du Colombier 		Bprint(&out, "!address\n");
19337dd7cddfSDavid du Colombier 		return nil;
19347dd7cddfSDavid du Colombier 	}
19357dd7cddfSDavid du Colombier 
19367dd7cddfSDavid du Colombier 	if(c->an < 2){
19377dd7cddfSDavid du Colombier 		fprint(2, "!usage: M list-of addresses\n");
19387dd7cddfSDavid du Colombier 		return nil;
19397dd7cddfSDavid du Colombier 	}
19407dd7cddfSDavid du Colombier 
19417dd7cddfSDavid du Colombier 	ai = 1;
19427dd7cddfSDavid du Colombier 	av = malloc(sizeof(char*)*(c->an + 8));
19437dd7cddfSDavid du Colombier 
19447dd7cddfSDavid du Colombier 	av[ai++] = "-t";
19457dd7cddfSDavid du Colombier 	if(m->parent == &top)
19467dd7cddfSDavid du Colombier 		av[ai++] = "message/rfc822";
19477dd7cddfSDavid du Colombier 	else
19487dd7cddfSDavid du Colombier 		av[ai++] = "mime";
19497dd7cddfSDavid du Colombier 
19507dd7cddfSDavid du Colombier 	av[ai++] = "-A";
19517dd7cddfSDavid du Colombier 	path = rooted(extendpath(m->path, "raw"));
19527dd7cddfSDavid du Colombier 	av[ai++] = s_to_c(path);
19537dd7cddfSDavid du Colombier 
19547dd7cddfSDavid du Colombier 	if(strchr(c->av[0], 'M') == nil)
19557dd7cddfSDavid du Colombier 		av[ai++] = "-n";
19567dd7cddfSDavid du Colombier 
19577dd7cddfSDavid du Colombier 	for(i = 1; i < c->an; i++)
19587dd7cddfSDavid du Colombier 		av[ai++] = c->av[i];
19597dd7cddfSDavid du Colombier 	av[ai] = 0;
19607dd7cddfSDavid du Colombier 
19617dd7cddfSDavid du Colombier 	if(tomailer(av) < 0)
19627dd7cddfSDavid du Colombier 		m = nil;
19637dd7cddfSDavid du Colombier 	if(path != nil)
19647dd7cddfSDavid du Colombier 		s_free(path);
19657dd7cddfSDavid du Colombier 	free(av);
19667dd7cddfSDavid du Colombier 	return m;
19677dd7cddfSDavid du Colombier }
19687dd7cddfSDavid du Colombier 
19697dd7cddfSDavid du Colombier Message*
acmd(Cmd * c,Message * m)19707dd7cddfSDavid du Colombier acmd(Cmd *c, Message *m)
19717dd7cddfSDavid du Colombier {
19727dd7cddfSDavid du Colombier 	char *av[128];
19739739e468SDavid du Colombier 	int i, ai = 1;
19749739e468SDavid du Colombier 	String *from, *rpath, *path = nil, *subject = nil;
19759739e468SDavid du Colombier 	String *to, *cc;
19767dd7cddfSDavid du Colombier 
19777dd7cddfSDavid du Colombier 	if(m == &top){
19787dd7cddfSDavid du Colombier 		Bprint(&out, "!address\n");
19797dd7cddfSDavid du Colombier 		return nil;
19807dd7cddfSDavid du Colombier 	}
19817dd7cddfSDavid du Colombier 
19827dd7cddfSDavid du Colombier 	if(*m->subject){
19837dd7cddfSDavid du Colombier 		av[ai++] = "-s";
19847dd7cddfSDavid du Colombier 		subject = addrecolon(m->subject);
19857dd7cddfSDavid du Colombier 		av[ai++] = s_to_c(subject);
19867dd7cddfSDavid du Colombier 	}
19877dd7cddfSDavid du Colombier 
19889739e468SDavid du Colombier 	av[ai++] = "-R";
19899739e468SDavid du Colombier 	rpath = rooted(s_clone(m->path));
19909739e468SDavid du Colombier 	av[ai++] = s_to_c(rpath);
19919739e468SDavid du Colombier 
19927dd7cddfSDavid du Colombier 	if(strchr(c->av[0], 'A') != nil){
19937dd7cddfSDavid du Colombier 		av[ai++] = "-t";
19947dd7cddfSDavid du Colombier 		av[ai++] = "message/rfc822";
19957dd7cddfSDavid du Colombier 		av[ai++] = "-A";
19967dd7cddfSDavid du Colombier 		path = rooted(extendpath(m->path, "raw"));
19977dd7cddfSDavid du Colombier 		av[ai++] = s_to_c(path);
19987dd7cddfSDavid du Colombier 	}
19997dd7cddfSDavid du Colombier 
20007dd7cddfSDavid du Colombier 	for(i = 1; i < c->an && ai < nelem(av)-1; i++)
20017dd7cddfSDavid du Colombier 		av[ai++] = c->av[i];
20027dd7cddfSDavid du Colombier 	from = s_copy(m->from);
20037dd7cddfSDavid du Colombier 	ai += tokenize822(s_to_c(from), &av[ai], nelem(av) - ai);
20047dd7cddfSDavid du Colombier 	to = s_copy(m->to);
20057dd7cddfSDavid du Colombier 	ai += tokenize822(s_to_c(to), &av[ai], nelem(av) - ai);
20067dd7cddfSDavid du Colombier 	cc = s_copy(m->cc);
20077dd7cddfSDavid du Colombier 	ai += tokenize822(s_to_c(cc), &av[ai], nelem(av) - ai);
20087dd7cddfSDavid du Colombier 	av[ai] = 0;
20097dd7cddfSDavid du Colombier 	if(tomailer(av) < 0)
20109739e468SDavid du Colombier 		m = nil;
20119739e468SDavid du Colombier 	s_free(path);
20129739e468SDavid du Colombier 	s_free(rpath);
20139739e468SDavid du Colombier 	s_free(subject);
20147dd7cddfSDavid du Colombier 	s_free(from);
20157dd7cddfSDavid du Colombier 	s_free(to);
20167dd7cddfSDavid du Colombier 	s_free(cc);
20177dd7cddfSDavid du Colombier 	return m;
20187dd7cddfSDavid du Colombier }
20197dd7cddfSDavid du Colombier 
20207dd7cddfSDavid du Colombier String *
relpath(char * path,String * to)20217dd7cddfSDavid du Colombier relpath(char *path, String *to)
20227dd7cddfSDavid du Colombier {
20237dd7cddfSDavid du Colombier 	if (*path=='/' || strncmp(path, "./", 2) == 0
20247dd7cddfSDavid du Colombier 			      || strncmp(path, "../", 3) == 0) {
20257dd7cddfSDavid du Colombier 		to = s_append(to, path);
202680ee5cbfSDavid du Colombier 	} else if(mbpath) {
20277dd7cddfSDavid du Colombier 		to = s_append(to, s_to_c(mbpath));
20287dd7cddfSDavid du Colombier 		to->ptr = strrchr(to->base, '/')+1;
20297dd7cddfSDavid du Colombier 		s_append(to, path);
20307dd7cddfSDavid du Colombier 	}
20317dd7cddfSDavid du Colombier 	return to;
20327dd7cddfSDavid du Colombier }
20337dd7cddfSDavid du Colombier 
20347dd7cddfSDavid du Colombier int
appendtofile(Message * m,char * part,char * base,int mbox)20357dd7cddfSDavid du Colombier appendtofile(Message *m, char *part, char *base, int mbox)
20367dd7cddfSDavid du Colombier {
20377dd7cddfSDavid du Colombier 	String *file, *h;
2038ba64361bSDavid du Colombier 	int in, out, rv;
20397dd7cddfSDavid du Colombier 
20407dd7cddfSDavid du Colombier 	file = extendpath(m->path, part);
20417dd7cddfSDavid du Colombier 	in = open(s_to_c(file), OREAD);
20427dd7cddfSDavid du Colombier 	if(in < 0){
20437dd7cddfSDavid du Colombier 		fprint(2, "!message disappeared\n");
20447dd7cddfSDavid du Colombier 		return -1;
20457dd7cddfSDavid du Colombier 	}
20467dd7cddfSDavid du Colombier 
20477dd7cddfSDavid du Colombier 	s_reset(file);
2048dc5a79c1SDavid du Colombier 
20497dd7cddfSDavid du Colombier 	relpath(base, file);
2050dc5a79c1SDavid du Colombier 	if(sysisdir(s_to_c(file))){
2051dc5a79c1SDavid du Colombier 		s_append(file, "/");
2052dc5a79c1SDavid du Colombier 		if(m->filename && strchr(m->filename, '/') == nil)
2053dc5a79c1SDavid du Colombier 			s_append(file, m->filename);
2054dc5a79c1SDavid du Colombier 		else {
2055dc5a79c1SDavid du Colombier 			s_append(file, "att.XXXXXXXXXXX");
2056dc5a79c1SDavid du Colombier 			mktemp(s_to_c(file));
2057dc5a79c1SDavid du Colombier 		}
2058dc5a79c1SDavid du Colombier 	}
20597dd7cddfSDavid du Colombier 	if(mbox)
20607dd7cddfSDavid du Colombier 		out = open(s_to_c(file), OWRITE);
20617dd7cddfSDavid du Colombier 	else
20627dd7cddfSDavid du Colombier 		out = open(s_to_c(file), OWRITE|OTRUNC);
20637dd7cddfSDavid du Colombier 	if(out < 0){
20647dd7cddfSDavid du Colombier 		out = create(s_to_c(file), OWRITE, 0666);
20657dd7cddfSDavid du Colombier 		if(out < 0){
20667dd7cddfSDavid du Colombier 			fprint(2, "!can't open %s: %r\n", s_to_c(file));
20677dd7cddfSDavid du Colombier 			close(in);
2068ba64361bSDavid du Colombier 			s_free(file);
20697dd7cddfSDavid du Colombier 			return -1;
20707dd7cddfSDavid du Colombier 		}
20717dd7cddfSDavid du Colombier 	}
20727dd7cddfSDavid du Colombier 	if(mbox)
20737dd7cddfSDavid du Colombier 		seek(out, 0, 2);
20747dd7cddfSDavid du Colombier 
20757dd7cddfSDavid du Colombier 	// put on a 'From ' line
20767dd7cddfSDavid du Colombier 	if(mbox){
20777dd7cddfSDavid du Colombier 		while(m->parent != &top)
20787dd7cddfSDavid du Colombier 			m = m->parent;
20797dd7cddfSDavid du Colombier 		h = file2string(m->path, "unixheader");
20807dd7cddfSDavid du Colombier 		fprint(out, "%s", s_to_c(h));
20817dd7cddfSDavid du Colombier 		s_free(h);
20827dd7cddfSDavid du Colombier 	}
20837dd7cddfSDavid du Colombier 
2084ba64361bSDavid du Colombier 	// copy the message escaping what we have to ad adding newlines if we have to
20857dd7cddfSDavid du Colombier 	if(mbox)
2086ba64361bSDavid du Colombier 		rv = appendfiletombox(in, out);
2087ba64361bSDavid du Colombier 	else
2088ba64361bSDavid du Colombier 		rv = appendfiletofile(in, out);
20897dd7cddfSDavid du Colombier 
20907dd7cddfSDavid du Colombier 	close(in);
20917dd7cddfSDavid du Colombier 	close(out);
20927dd7cddfSDavid du Colombier 
20937dd7cddfSDavid du Colombier 	if(rv >= 0)
20947dd7cddfSDavid du Colombier 		print("!saved in %s\n", s_to_c(file));
20957dd7cddfSDavid du Colombier 	s_free(file);
20967dd7cddfSDavid du Colombier 	return rv;
20977dd7cddfSDavid du Colombier }
20987dd7cddfSDavid du Colombier 
20997dd7cddfSDavid du Colombier Message*
scmd(Cmd * c,Message * m)21007dd7cddfSDavid du Colombier scmd(Cmd *c, Message *m)
21017dd7cddfSDavid du Colombier {
21027dd7cddfSDavid du Colombier 	char *file;
21037dd7cddfSDavid du Colombier 
21047dd7cddfSDavid du Colombier 	if(m == &top){
21057dd7cddfSDavid du Colombier 		Bprint(&out, "!address\n");
21067dd7cddfSDavid du Colombier 		return nil;
21077dd7cddfSDavid du Colombier 	}
21087dd7cddfSDavid du Colombier 
21097dd7cddfSDavid du Colombier 	switch(c->an){
21107dd7cddfSDavid du Colombier 	case 1:
21117dd7cddfSDavid du Colombier 		file = "stored";
21127dd7cddfSDavid du Colombier 		break;
21137dd7cddfSDavid du Colombier 	case 2:
21147dd7cddfSDavid du Colombier 		file = c->av[1];
21157dd7cddfSDavid du Colombier 		break;
21167dd7cddfSDavid du Colombier 	default:
21177dd7cddfSDavid du Colombier 		fprint(2, "!usage: s filename\n");
21187dd7cddfSDavid du Colombier 		return nil;
21197dd7cddfSDavid du Colombier 	}
21207dd7cddfSDavid du Colombier 
21217dd7cddfSDavid du Colombier 	if(appendtofile(m, "raw", file, 1) < 0)
21227dd7cddfSDavid du Colombier 		return nil;
21237dd7cddfSDavid du Colombier 
21247dd7cddfSDavid du Colombier 	m->stored = 1;
21257dd7cddfSDavid du Colombier 	return m;
21267dd7cddfSDavid du Colombier }
21277dd7cddfSDavid du Colombier 
21287dd7cddfSDavid du Colombier Message*
wcmd(Cmd * c,Message * m)21297dd7cddfSDavid du Colombier wcmd(Cmd *c, Message *m)
21307dd7cddfSDavid du Colombier {
21317dd7cddfSDavid du Colombier 	char *file;
21327dd7cddfSDavid du Colombier 
21337dd7cddfSDavid du Colombier 	if(m == &top){
21347dd7cddfSDavid du Colombier 		Bprint(&out, "!address\n");
21357dd7cddfSDavid du Colombier 		return nil;
21367dd7cddfSDavid du Colombier 	}
21377dd7cddfSDavid du Colombier 
21387dd7cddfSDavid du Colombier 	switch(c->an){
21397dd7cddfSDavid du Colombier 	case 2:
21407dd7cddfSDavid du Colombier 		file = c->av[1];
21417dd7cddfSDavid du Colombier 		break;
21427dd7cddfSDavid du Colombier 	case 1:
21437dd7cddfSDavid du Colombier 		if(*m->filename == 0){
21447dd7cddfSDavid du Colombier 			fprint(2, "!usage: w filename\n");
21457dd7cddfSDavid du Colombier 			return nil;
21467dd7cddfSDavid du Colombier 		}
21477dd7cddfSDavid du Colombier 		file = strrchr(m->filename, '/');
21487dd7cddfSDavid du Colombier 		if(file != nil)
21497dd7cddfSDavid du Colombier 			file++;
21507dd7cddfSDavid du Colombier 		else
21517dd7cddfSDavid du Colombier 			file = m->filename;
21527dd7cddfSDavid du Colombier 		break;
21537dd7cddfSDavid du Colombier 	default:
21547dd7cddfSDavid du Colombier 		fprint(2, "!usage: w filename\n");
21557dd7cddfSDavid du Colombier 		return nil;
21567dd7cddfSDavid du Colombier 	}
21577dd7cddfSDavid du Colombier 
21587dd7cddfSDavid du Colombier 	if(appendtofile(m, "body", file, 0) < 0)
21597dd7cddfSDavid du Colombier 		return nil;
21607dd7cddfSDavid du Colombier 	m->stored = 1;
21617dd7cddfSDavid du Colombier 	return m;
21627dd7cddfSDavid du Colombier }
21637dd7cddfSDavid du Colombier 
21646b6b9ac8SDavid du Colombier char *specialfile[] =
21656b6b9ac8SDavid du Colombier {
21666b6b9ac8SDavid du Colombier 	"pipeto",
21676b6b9ac8SDavid du Colombier 	"pipefrom",
21686b6b9ac8SDavid du Colombier 	"L.mbox",
21696b6b9ac8SDavid du Colombier 	"forward",
21706b6b9ac8SDavid du Colombier 	"names"
21716b6b9ac8SDavid du Colombier };
21726b6b9ac8SDavid du Colombier 
21736b6b9ac8SDavid du Colombier // return 1 if this is a special file
21746b6b9ac8SDavid du Colombier static int
special(String * s)21756b6b9ac8SDavid du Colombier special(String *s)
21767dd7cddfSDavid du Colombier {
21777dd7cddfSDavid du Colombier 	char *p;
21786b6b9ac8SDavid du Colombier 	int i;
21796b6b9ac8SDavid du Colombier 
21806b6b9ac8SDavid du Colombier 	p = strrchr(s_to_c(s), '/');
21816b6b9ac8SDavid du Colombier 	if(p == nil)
21826b6b9ac8SDavid du Colombier 		p = s_to_c(s);
21836b6b9ac8SDavid du Colombier 	else
21846b6b9ac8SDavid du Colombier 		p++;
21856b6b9ac8SDavid du Colombier 	for(i = 0; i < nelem(specialfile); i++)
21866b6b9ac8SDavid du Colombier 		if(strcmp(p, specialfile[i]) == 0)
21876b6b9ac8SDavid du Colombier 			return 1;
21886b6b9ac8SDavid du Colombier 	return 0;
21896b6b9ac8SDavid du Colombier }
21906b6b9ac8SDavid du Colombier 
21916b6b9ac8SDavid du Colombier // open the folder using the recipients account name
21926b6b9ac8SDavid du Colombier static String*
foldername(char * rcvr)21936b6b9ac8SDavid du Colombier foldername(char *rcvr)
21946b6b9ac8SDavid du Colombier {
21956b6b9ac8SDavid du Colombier 	char *p;
21966b6b9ac8SDavid du Colombier 	int c;
21976b6b9ac8SDavid du Colombier 	String *file;
21986b6b9ac8SDavid du Colombier 	Dir *d;
21996b6b9ac8SDavid du Colombier 	int scarey;
22006b6b9ac8SDavid du Colombier 
22016b6b9ac8SDavid du Colombier 	file = s_new();
22026b6b9ac8SDavid du Colombier 	mboxpath("f", user, file, 0);
22036b6b9ac8SDavid du Colombier 	d = dirstat(s_to_c(file));
22046b6b9ac8SDavid du Colombier 
22056b6b9ac8SDavid du Colombier 	// if $mail/f exists, store there, otherwise in $mail
22066b6b9ac8SDavid du Colombier 	s_restart(file);
22076b6b9ac8SDavid du Colombier 	if(d && d->qid.type == QTDIR){
22086b6b9ac8SDavid du Colombier 		scarey = 0;
22096b6b9ac8SDavid du Colombier 		s_append(file, "f/");
22106b6b9ac8SDavid du Colombier 	} else {
22116b6b9ac8SDavid du Colombier 		scarey = 1;
22126b6b9ac8SDavid du Colombier 	}
22136b6b9ac8SDavid du Colombier 	free(d);
22147dd7cddfSDavid du Colombier 
22157dd7cddfSDavid du Colombier 	p = strrchr(rcvr, '!');
22167dd7cddfSDavid du Colombier 	if(p != nil)
22177dd7cddfSDavid du Colombier 		rcvr = p+1;
22187dd7cddfSDavid du Colombier 
22196b6b9ac8SDavid du Colombier 	while(*rcvr && *rcvr != '@'){
22206b6b9ac8SDavid du Colombier 		c = *rcvr++;
22216b6b9ac8SDavid du Colombier 		if(c == '/')
22226b6b9ac8SDavid du Colombier 			c = '_';
22236b6b9ac8SDavid du Colombier 		s_putc(file, c);
22246b6b9ac8SDavid du Colombier 	}
22256b6b9ac8SDavid du Colombier 	s_terminate(file);
22266b6b9ac8SDavid du Colombier 
22276b6b9ac8SDavid du Colombier 	if(scarey && special(file)){
22286b6b9ac8SDavid du Colombier 		fprint(2, "!won't overwrite %s\n", s_to_c(file));
22296b6b9ac8SDavid du Colombier 		s_free(file);
22306b6b9ac8SDavid du Colombier 		return nil;
22316b6b9ac8SDavid du Colombier 	}
22326b6b9ac8SDavid du Colombier 
22336b6b9ac8SDavid du Colombier 	return file;
22347dd7cddfSDavid du Colombier }
22357dd7cddfSDavid du Colombier 
22367dd7cddfSDavid du Colombier Message*
fcmd(Cmd * c,Message * m)22377dd7cddfSDavid du Colombier fcmd(Cmd *c, Message *m)
22387dd7cddfSDavid du Colombier {
22396b6b9ac8SDavid du Colombier 	String *folder;
22407dd7cddfSDavid du Colombier 
22417dd7cddfSDavid du Colombier 	if(c->an > 1){
22427dd7cddfSDavid du Colombier 		fprint(2, "!usage: f takes no arguments\n");
22437dd7cddfSDavid du Colombier 		return nil;
22447dd7cddfSDavid du Colombier 	}
22457dd7cddfSDavid du Colombier 
22467dd7cddfSDavid du Colombier 	if(m == &top){
22477dd7cddfSDavid du Colombier 		Bprint(&out, "!address\n");
22487dd7cddfSDavid du Colombier 		return nil;
22497dd7cddfSDavid du Colombier 	}
22507dd7cddfSDavid du Colombier 
22516b6b9ac8SDavid du Colombier 	folder = foldername(m->from);
22526b6b9ac8SDavid du Colombier 	if(folder == nil)
22537dd7cddfSDavid du Colombier 		return nil;
22547dd7cddfSDavid du Colombier 
22556b6b9ac8SDavid du Colombier 	if(appendtofile(m, "raw", s_to_c(folder), 1) < 0){
22566b6b9ac8SDavid du Colombier 		s_free(folder);
22576b6b9ac8SDavid du Colombier 		return nil;
22586b6b9ac8SDavid du Colombier 	}
22596b6b9ac8SDavid du Colombier 	s_free(folder);
22606b6b9ac8SDavid du Colombier 
22617dd7cddfSDavid du Colombier 	m->stored = 1;
22627dd7cddfSDavid du Colombier 	return m;
22637dd7cddfSDavid du Colombier }
22647dd7cddfSDavid du Colombier 
22657dd7cddfSDavid du Colombier void
system(char * cmd,char ** av,int in)22667dd7cddfSDavid du Colombier system(char *cmd, char **av, int in)
22677dd7cddfSDavid du Colombier {
22687dd7cddfSDavid du Colombier 	int pid;
22697dd7cddfSDavid du Colombier 
22707dd7cddfSDavid du Colombier 	switch(pid=fork()){
22717dd7cddfSDavid du Colombier 	case -1:
22727dd7cddfSDavid du Colombier 		return;
22737dd7cddfSDavid du Colombier 	case 0:
22747dd7cddfSDavid du Colombier 		if(in >= 0){
22757dd7cddfSDavid du Colombier 			close(0);
22767dd7cddfSDavid du Colombier 			dup(in, 0);
22777dd7cddfSDavid du Colombier 			close(in);
22787dd7cddfSDavid du Colombier 		}
22797dd7cddfSDavid du Colombier 		if(wd[0] != 0)
22807dd7cddfSDavid du Colombier 			chdir(wd);
22817dd7cddfSDavid du Colombier 		exec(cmd, av);
22827dd7cddfSDavid du Colombier 		fprint(2, "!couldn't exec %s\n", cmd);
22837dd7cddfSDavid du Colombier 		exits(0);
22847dd7cddfSDavid du Colombier 	default:
22857dd7cddfSDavid du Colombier 		if(in >= 0)
22867dd7cddfSDavid du Colombier 			close(in);
22879a747e4fSDavid du Colombier 		while(waitpid() < 0){
22887dd7cddfSDavid du Colombier 			if(!interrupted)
22897dd7cddfSDavid du Colombier 				break;
22907dd7cddfSDavid du Colombier 			postnote(PNPROC, pid, "die");
22917dd7cddfSDavid du Colombier 			continue;
22927dd7cddfSDavid du Colombier 		}
22937dd7cddfSDavid du Colombier 		break;
22947dd7cddfSDavid du Colombier 	}
22957dd7cddfSDavid du Colombier }
22967dd7cddfSDavid du Colombier 
22977dd7cddfSDavid du Colombier Message*
bangcmd(Cmd * c,Message * m)22987dd7cddfSDavid du Colombier bangcmd(Cmd *c, Message *m)
22997dd7cddfSDavid du Colombier {
23007dd7cddfSDavid du Colombier 	char cmd[4*1024];
23017dd7cddfSDavid du Colombier 	char *p, *e;
23027dd7cddfSDavid du Colombier 	char *av[4];
23037dd7cddfSDavid du Colombier 	int i;
23047dd7cddfSDavid du Colombier 
23057dd7cddfSDavid du Colombier 	cmd[0] = 0;
23067dd7cddfSDavid du Colombier 	p = cmd;
23077dd7cddfSDavid du Colombier 	e = cmd+sizeof(cmd);
23087dd7cddfSDavid du Colombier 	for(i = 1; i < c->an; i++)
23097dd7cddfSDavid du Colombier 		p = seprint(p, e, "%s ", c->av[i]);
23107dd7cddfSDavid du Colombier 	av[0] = "rc";
23117dd7cddfSDavid du Colombier 	av[1] = "-c";
23127dd7cddfSDavid du Colombier 	av[2] = cmd;
23137dd7cddfSDavid du Colombier 	av[3] = 0;
23147dd7cddfSDavid du Colombier 	system("/bin/rc", av, -1);
23157dd7cddfSDavid du Colombier 	Bprint(&out, "!\n");
23167dd7cddfSDavid du Colombier 	return m;
23177dd7cddfSDavid du Colombier }
23187dd7cddfSDavid du Colombier 
23197dd7cddfSDavid du Colombier Message*
xpipecmd(Cmd * c,Message * m,char * part)23206b6b9ac8SDavid du Colombier xpipecmd(Cmd *c, Message *m, char *part)
23217dd7cddfSDavid du Colombier {
23227dd7cddfSDavid du Colombier 	char cmd[128];
23237dd7cddfSDavid du Colombier 	char *p, *e;
23247dd7cddfSDavid du Colombier 	char *av[4];
23257dd7cddfSDavid du Colombier 	String *path;
23267dd7cddfSDavid du Colombier 	int i, fd;
23277dd7cddfSDavid du Colombier 
23287dd7cddfSDavid du Colombier 	if(c->an < 2){
23297dd7cddfSDavid du Colombier 		Bprint(&out, "!usage: | cmd\n");
23307dd7cddfSDavid du Colombier 		return nil;
23317dd7cddfSDavid du Colombier 	}
23327dd7cddfSDavid du Colombier 
23337dd7cddfSDavid du Colombier 	if(m == &top){
23347dd7cddfSDavid du Colombier 		Bprint(&out, "!address\n");
23357dd7cddfSDavid du Colombier 		return nil;
23367dd7cddfSDavid du Colombier 	}
23377dd7cddfSDavid du Colombier 
23386b6b9ac8SDavid du Colombier 	path = extendpath(m->path, part);
23397dd7cddfSDavid du Colombier 	fd = open(s_to_c(path), OREAD);
23407dd7cddfSDavid du Colombier 	s_free(path);
23416b6b9ac8SDavid du Colombier 	if(fd < 0){	// compatibility with older upas/fs
23426b6b9ac8SDavid du Colombier 		path = extendpath(m->path, "raw");
23436b6b9ac8SDavid du Colombier 		fd = open(s_to_c(path), OREAD);
23446b6b9ac8SDavid du Colombier 		s_free(path);
23456b6b9ac8SDavid du Colombier 	}
23467dd7cddfSDavid du Colombier 	if(fd < 0){
23477dd7cddfSDavid du Colombier 		fprint(2, "!message disappeared\n");
23487dd7cddfSDavid du Colombier 		return nil;
23497dd7cddfSDavid du Colombier 	}
23507dd7cddfSDavid du Colombier 
23517dd7cddfSDavid du Colombier 	p = cmd;
23527dd7cddfSDavid du Colombier 	e = cmd+sizeof(cmd);
23537dd7cddfSDavid du Colombier 	cmd[0] = 0;
23547dd7cddfSDavid du Colombier 	for(i = 1; i < c->an; i++)
23557dd7cddfSDavid du Colombier 		p = seprint(p, e, "%s ", c->av[i]);
23567dd7cddfSDavid du Colombier 	av[0] = "rc";
23577dd7cddfSDavid du Colombier 	av[1] = "-c";
23587dd7cddfSDavid du Colombier 	av[2] = cmd;
23597dd7cddfSDavid du Colombier 	av[3] = 0;
23607dd7cddfSDavid du Colombier 	system("/bin/rc", av, fd);	/* system closes fd */
23617dd7cddfSDavid du Colombier 	Bprint(&out, "!\n");
23627dd7cddfSDavid du Colombier 	return m;
23637dd7cddfSDavid du Colombier }
23647dd7cddfSDavid du Colombier 
23656b6b9ac8SDavid du Colombier Message*
pipecmd(Cmd * c,Message * m)23666b6b9ac8SDavid du Colombier pipecmd(Cmd *c, Message *m)
23676b6b9ac8SDavid du Colombier {
23686b6b9ac8SDavid du Colombier 	return xpipecmd(c, m, "body");
23696b6b9ac8SDavid du Colombier }
23706b6b9ac8SDavid du Colombier 
23716b6b9ac8SDavid du Colombier Message*
rpipecmd(Cmd * c,Message * m)23726b6b9ac8SDavid du Colombier rpipecmd(Cmd *c, Message *m)
23736b6b9ac8SDavid du Colombier {
23746b6b9ac8SDavid du Colombier 	return xpipecmd(c, m, "rawunix");
23756b6b9ac8SDavid du Colombier }
23766b6b9ac8SDavid du Colombier 
23777dd7cddfSDavid du Colombier void
closemb(void)23787dd7cddfSDavid du Colombier closemb(void)
23797dd7cddfSDavid du Colombier {
23807dd7cddfSDavid du Colombier 	int fd;
23817dd7cddfSDavid du Colombier 
23827dd7cddfSDavid du Colombier 	fd = open("/mail/fs/ctl", ORDWR);
23837dd7cddfSDavid du Colombier 	if(fd < 0)
23849a747e4fSDavid du Colombier 		sysfatal("can't open /mail/fs/ctl: %r");
23857dd7cddfSDavid du Colombier 
23867dd7cddfSDavid du Colombier 	// close current mailbox
23877dd7cddfSDavid du Colombier 	if(*mbname && strcmp(mbname, "mbox") != 0)
23887dd7cddfSDavid du Colombier 		fprint(fd, "close %s", mbname);
23897dd7cddfSDavid du Colombier 
23907dd7cddfSDavid du Colombier 	close(fd);
23917dd7cddfSDavid du Colombier }
23927dd7cddfSDavid du Colombier 
23937dd7cddfSDavid du Colombier int
switchmb(char * file,char * singleton)23947dd7cddfSDavid du Colombier switchmb(char *file, char *singleton)
23957dd7cddfSDavid du Colombier {
23967dd7cddfSDavid du Colombier 	char *p;
23977dd7cddfSDavid du Colombier 	int n, fd;
23987dd7cddfSDavid du Colombier 	String *path;
23997dd7cddfSDavid du Colombier 	char buf[256];
24007dd7cddfSDavid du Colombier 
240180ee5cbfSDavid du Colombier 	// if the user didn't say anything and there
240280ee5cbfSDavid du Colombier 	// is an mbox mounted already, use that one
240380ee5cbfSDavid du Colombier 	// so that the upas/fs -fdefault default is honored.
240480ee5cbfSDavid du Colombier 	if(file
240580ee5cbfSDavid du Colombier 	|| (singleton && access(singleton, 0)<0)
240680ee5cbfSDavid du Colombier 	|| (!singleton && access("/mail/fs/mbox", 0)<0)){
240780ee5cbfSDavid du Colombier 		if(file == nil)
240880ee5cbfSDavid du Colombier 			file = "mbox";
240980ee5cbfSDavid du Colombier 
24107dd7cddfSDavid du Colombier 		// close current mailbox
24117dd7cddfSDavid du Colombier 		closemb();
241280ee5cbfSDavid du Colombier 		didopen = 1;
24137dd7cddfSDavid du Colombier 
24147dd7cddfSDavid du Colombier 		fd = open("/mail/fs/ctl", ORDWR);
24157dd7cddfSDavid du Colombier 		if(fd < 0)
24169a747e4fSDavid du Colombier 			sysfatal("can't open /mail/fs/ctl: %r");
24177dd7cddfSDavid du Colombier 
24187dd7cddfSDavid du Colombier 		path = s_new();
24197dd7cddfSDavid du Colombier 
24207dd7cddfSDavid du Colombier 		// get an absolute path to the mail box
24217dd7cddfSDavid du Colombier 		if(strncmp(file, "./", 2) == 0){
24227dd7cddfSDavid du Colombier 			// resolve path here since upas/fs doesn't know
24237dd7cddfSDavid du Colombier 			// our working directory
24247dd7cddfSDavid du Colombier 			if(getwd(buf, sizeof(buf)-strlen(file)) == nil){
24257dd7cddfSDavid du Colombier 				fprint(2, "!can't get working directory: %s\n", buf);
24267dd7cddfSDavid du Colombier 				return -1;
24277dd7cddfSDavid du Colombier 			}
24287dd7cddfSDavid du Colombier 			s_append(path, buf);
24297dd7cddfSDavid du Colombier 			s_append(path, file+1);
24307dd7cddfSDavid du Colombier 		} else {
24317dd7cddfSDavid du Colombier 			mboxpath(file, user, path, 0);
24327dd7cddfSDavid du Colombier 		}
24337dd7cddfSDavid du Colombier 
24347dd7cddfSDavid du Colombier 		// make up a handle to use when talking to fs
24357dd7cddfSDavid du Colombier 		p = strrchr(file, '/');
24367dd7cddfSDavid du Colombier 		if(p == nil){
24377dd7cddfSDavid du Colombier 			// if its in the mailbox directory, just use the name
24387dd7cddfSDavid du Colombier 			strncpy(mbname, file, sizeof(mbname));
24397dd7cddfSDavid du Colombier 			mbname[sizeof(mbname)-1] = 0;
24407dd7cddfSDavid du Colombier 		} else {
24417dd7cddfSDavid du Colombier 			// make up a mailbox name
24427dd7cddfSDavid du Colombier 			p = strrchr(s_to_c(path), '/');
24437dd7cddfSDavid du Colombier 			p++;
24447dd7cddfSDavid du Colombier 			if(*p == 0){
24457dd7cddfSDavid du Colombier 				fprint(2, "!bad mbox name");
24467dd7cddfSDavid du Colombier 				return -1;
24477dd7cddfSDavid du Colombier 			}
24487dd7cddfSDavid du Colombier 			strncpy(mbname, p, sizeof(mbname));
24497dd7cddfSDavid du Colombier 			mbname[sizeof(mbname)-1] = 0;
24507dd7cddfSDavid du Colombier 			n = strlen(mbname);
24519a747e4fSDavid du Colombier 			if(n > Elemlen-12)
24529a747e4fSDavid du Colombier 				n = Elemlen-12;
24537dd7cddfSDavid du Colombier 			sprint(mbname+n, "%ld", time(0));
24547dd7cddfSDavid du Colombier 		}
24557dd7cddfSDavid du Colombier 
24567dd7cddfSDavid du Colombier 		if(fprint(fd, "open %s %s", s_to_c(path), mbname) < 0){
24577dd7cddfSDavid du Colombier 			fprint(2, "!can't 'open %s %s': %r\n", file, mbname);
24587dd7cddfSDavid du Colombier 			s_free(path);
24597dd7cddfSDavid du Colombier 			return -1;
24607dd7cddfSDavid du Colombier 		}
246180ee5cbfSDavid du Colombier 		close(fd);
2462031ba9d1SDavid du Colombier 	}else
2463031ba9d1SDavid du Colombier 	if (singleton && access(singleton, 0)==0
2464031ba9d1SDavid du Colombier 	    && strncmp(singleton, "/mail/fs/", 9) == 0){
2465031ba9d1SDavid du Colombier 		if ((p = strchr(singleton +10, '/')) == nil){
2466031ba9d1SDavid du Colombier 			fprint(2, "!bad mbox name");
2467031ba9d1SDavid du Colombier 			return -1;
2468031ba9d1SDavid du Colombier 		}
2469031ba9d1SDavid du Colombier 		n = p-(singleton+9);
2470031ba9d1SDavid du Colombier 		strncpy(mbname, singleton+9, n);
2471031ba9d1SDavid du Colombier 		mbname[n+1] = 0;
2472031ba9d1SDavid du Colombier 		path = s_reset(nil);
2473031ba9d1SDavid du Colombier 		mboxpath(mbname, user, path, 0);
247480ee5cbfSDavid du Colombier 	}else{
247580ee5cbfSDavid du Colombier 		path = s_reset(nil);
247680ee5cbfSDavid du Colombier 		mboxpath("mbox", user, path, 0);
247780ee5cbfSDavid du Colombier 		strcpy(mbname, "mbox");
247880ee5cbfSDavid du Colombier 	}
247980ee5cbfSDavid du Colombier 
248004c2b4eaSDavid du Colombier 	snprint(root, sizeof root, "/mail/fs/%s", mbname);
24817dd7cddfSDavid du Colombier 	if(getwd(wd, sizeof(wd)) == 0)
24827dd7cddfSDavid du Colombier 		wd[0] = 0;
24837dd7cddfSDavid du Colombier 	if(singleton == nil && chdir(root) >= 0)
24847dd7cddfSDavid du Colombier 		strcpy(root, ".");
24857dd7cddfSDavid du Colombier 	rootlen = strlen(root);
24867dd7cddfSDavid du Colombier 
24877dd7cddfSDavid du Colombier 	if(mbpath != nil)
24887dd7cddfSDavid du Colombier 		s_free(mbpath);
24897dd7cddfSDavid du Colombier 	mbpath = path;
24907dd7cddfSDavid du Colombier 	return 0;
24917dd7cddfSDavid du Colombier }
24927dd7cddfSDavid du Colombier 
24937dd7cddfSDavid du Colombier // like tokenize but for into lines
24947dd7cddfSDavid du Colombier int
lineize(char * s,char ** f,int n)24957dd7cddfSDavid du Colombier lineize(char *s, char **f, int n)
24967dd7cddfSDavid du Colombier {
24977dd7cddfSDavid du Colombier 	int i;
24987dd7cddfSDavid du Colombier 
24997dd7cddfSDavid du Colombier 	for(i = 0; *s && i < n; i++){
25007dd7cddfSDavid du Colombier 		f[i] = s;
25017dd7cddfSDavid du Colombier 		s = strchr(s, '\n');
25027dd7cddfSDavid du Colombier 		if(s == nil)
25037dd7cddfSDavid du Colombier 			break;
25047dd7cddfSDavid du Colombier 		*s++ = 0;
25057dd7cddfSDavid du Colombier 	}
25067dd7cddfSDavid du Colombier 	return i;
25077dd7cddfSDavid du Colombier }
25087dd7cddfSDavid du Colombier 
25097dd7cddfSDavid du Colombier 
25107dd7cddfSDavid du Colombier 
25117dd7cddfSDavid du Colombier String*
rooted(String * s)25127dd7cddfSDavid du Colombier rooted(String *s)
25137dd7cddfSDavid du Colombier {
25147dd7cddfSDavid du Colombier 	static char buf[256];
25157dd7cddfSDavid du Colombier 
25167dd7cddfSDavid du Colombier 	if(strcmp(root, ".") != 0)
25177dd7cddfSDavid du Colombier 		return s;
25187dd7cddfSDavid du Colombier 	snprint(buf, sizeof(buf), "/mail/fs/%s/%s", mbname, s_to_c(s));
25197dd7cddfSDavid du Colombier 	s_free(s);
25207dd7cddfSDavid du Colombier 	return s_copy(buf);
25217dd7cddfSDavid du Colombier }
25227dd7cddfSDavid du Colombier 
25239a747e4fSDavid du Colombier int
plumb(Message * m,Ctype * cp)25247dd7cddfSDavid du Colombier plumb(Message *m, Ctype *cp)
25257dd7cddfSDavid du Colombier {
25267dd7cddfSDavid du Colombier 	String *s;
25277dd7cddfSDavid du Colombier 	Plumbmsg *pm;
25287dd7cddfSDavid du Colombier 	static int fd = -2;
25297dd7cddfSDavid du Colombier 
25307dd7cddfSDavid du Colombier 	if(cp->plumbdest == nil)
25319a747e4fSDavid du Colombier 		return -1;
25327dd7cddfSDavid du Colombier 
25337dd7cddfSDavid du Colombier 	if(fd < -1)
25347dd7cddfSDavid du Colombier 		fd = plumbopen("send", OWRITE);
25359a747e4fSDavid du Colombier 	if(fd < 0)
25369a747e4fSDavid du Colombier 		return -1;
25377dd7cddfSDavid du Colombier 
25387dd7cddfSDavid du Colombier 	pm = mallocz(sizeof(Plumbmsg), 1);
25397dd7cddfSDavid du Colombier 	pm->src = strdup("mail");
25407dd7cddfSDavid du Colombier 	if(*cp->plumbdest)
25417dd7cddfSDavid du Colombier 		pm->dst = strdup(cp->plumbdest);
25427dd7cddfSDavid du Colombier 	pm->wdir = nil;
25437dd7cddfSDavid du Colombier 	pm->type = strdup("text");
25447dd7cddfSDavid du Colombier 	pm->ndata = -1;
25457dd7cddfSDavid du Colombier 	s = rooted(extendpath(m->path, "body"));
25467dd7cddfSDavid du Colombier 	if(cp->ext != nil){
25477dd7cddfSDavid du Colombier 		s_append(s, ".");
25487dd7cddfSDavid du Colombier 		s_append(s, cp->ext);
25497dd7cddfSDavid du Colombier 	}
25507dd7cddfSDavid du Colombier 	pm->data = strdup(s_to_c(s));
25517dd7cddfSDavid du Colombier 	s_free(s);
25527dd7cddfSDavid du Colombier 	plumbsend(fd, pm);
25537dd7cddfSDavid du Colombier 	plumbfree(pm);
25549a747e4fSDavid du Colombier 	return 0;
25557dd7cddfSDavid du Colombier }
25567dd7cddfSDavid du Colombier 
25577dd7cddfSDavid du Colombier void
regerror(char *)25587dd7cddfSDavid du Colombier regerror(char*)
25597dd7cddfSDavid du Colombier {
25607dd7cddfSDavid du Colombier }
25617dd7cddfSDavid du Colombier 
25627dd7cddfSDavid du Colombier String*
addrecolon(char * s)25637dd7cddfSDavid du Colombier addrecolon(char *s)
25647dd7cddfSDavid du Colombier {
25657dd7cddfSDavid du Colombier 	String *str;
25667dd7cddfSDavid du Colombier 
25677dd7cddfSDavid du Colombier 	if(cistrncmp(s, "re:", 3) != 0){
25687dd7cddfSDavid du Colombier 		str = s_copy("Re: ");
25697dd7cddfSDavid du Colombier 		s_append(str, s);
25707dd7cddfSDavid du Colombier 	} else
25717dd7cddfSDavid du Colombier 		str = s_copy(s);
25727dd7cddfSDavid du Colombier 	return str;
25737dd7cddfSDavid du Colombier }
25747dd7cddfSDavid du Colombier 
25757dd7cddfSDavid du Colombier void
exitfs(char * rv)25767dd7cddfSDavid du Colombier exitfs(char *rv)
25777dd7cddfSDavid du Colombier {
25787dd7cddfSDavid du Colombier 	if(startedfs)
25797dd7cddfSDavid du Colombier 		unmount(nil, "/mail/fs");
258014cc0f53SDavid du Colombier 	chdir("/sys/src/cmd/upas/ned");		/* for profiling? */
25817dd7cddfSDavid du Colombier 	exits(rv);
25827dd7cddfSDavid du Colombier }
2583