[net.sources] IMS

allbery@ncoast.UUCP (Brandon S. Allbery) (07/22/86)

IMS is a simple ``folder''-oriented mail reader (think of a baby MH).  It can
be used in interactive mode, or combined with shell scripts to manipulate mail
a' la MH.  (One simple idea is a shell script:

#! /bin/sh
exec ims "$0" "$@"

which is linked to IMS command names and executes the IMS commands.)  The
manual page describes its use.

I have found that it is faster than mailx (Mail) in most things, faster than
/bin/mail in many... and ENORMOUSLY faster than msg (although I haven't tried
its new incarnation as ``elm'' yet).  But it's not full-screen oriented.

This mailer is still tiny and simple at present.  I'll be adding to it, in
particular adding to the folder support, as only the current folder and the
save folder are generally accessible.  But for now it's fast and does the
things I want it to do.

It has been tested under System III and System V; it should work under 4.2BSD
if you #define BSD (it uses BSD-compatible directory routines under USG
unices) but it won't work as is under V7.  It also doesn't store timezones in
messages under 4.2; I haven't the faintest idea how to retrieve the timezone.

Cut at the dotted line, feed to /bin/sh, make, and enjoy!

++Brandon
-------------------------------- coupe ici ----------------------------------
#! /bin/sh
#
# Shell archive created Thu., Jul. 17, 1986 by tdi2!brandon.
# Contents:
#
#	-rw-r--r--   1 brandon  20          8881 Jul 17 08:54 ims.c
#	-rw-r--r--   1 brandon  20         10207 Jul 17 08:55 imsread.c
#	-rw-r--r--   1 brandon  20          3279 Jul 17 09:04 imsdel.c
#	-rw-r--r--   1 brandon  20          5932 Jul 17 11:11 imsinc.c
#	-rw-r--r--   1 brandon  20         12610 Jul 17 09:42 imssend.c
#	-rw-r--r--   1 brandon  20          4618 Jul 17 09:07 imsset.c
#	-rw-r--r--   1 brandon  sys         5411 Jul 17 09:11 imsalias.c
#	-rw-r--r--   1 brandon  20          1934 Jul  4 19:47 ndir.c
#	-rw-r--r--   1 brandon  20          1540 Jul  4 20:01 eopen.c
#	-rw-r--r--   1 brandon  sys         1756 Jul 17 09:47 ims.h
#	-rw-rw-rw-   1 brandon  20          2471 Jul  3 19:52 ndir.h
#	-rw-r--r--   1 brandon  20         18403 Jul 17 10:44 ims.1
#	-rw-r--r--   1 brandon  20          1501 Jul 17 11:26 Makefile
#
# You may unpack this archive with sh or ksh, but not csh.
#

if test -r "ims.c"; then
	echo "File ims.c exists.  Enter new name or RETURN to skip.  (. to replace.)"
	read newname
	case "$newname" in
	".")	newname="ims.c"
	esac
else
	newname="ims.c"
fi
if test -z "$newname"; then
	echo "shx - $newname (skipped)"
else
	case "$newname" in
	"$sfile")
		echo "shx - $sfile (as $newname)"
		;;
	*)	echo "shx - $newname"
	esac
	sed 's/^X//' << '--EOF:ims.c--' > "$newname"
X#include "ims.h"
X
Xstruct cmdtab {
X	char *c_cmd;
X	int (*c_func)();
X	char *c_help;
X} cmdtab[] = {
X	"delete",	delmsg,		"Delete a message",
X	"undelete",	undelmsg,	"Recover a deleted message",
X	"expunge",	expunge,	"Recover space occupied by deleted messages",
X	"type",		readmsg,	"Display a message",
X	"print",	printmsg,	"Print a message on printer",
X	"save",		savemsg,	"Save a message in a file or folder",
X	"set",		varops,		"Display or modify IMS variables",
X	"next",		nextmsg,	"Go to the next message",
X	"+",		nextmsg,	"Go to the next message",
X	"previous",	prevmsg,	"Go to the previous message",
X	"-",		prevmsg,	"Go to the previous message",
X	"write",	savemsg,	"Save a message in a file or folder",
X	"reply",	reply,		"Reply to a message",
X	"goto",		gomsg,		"Set the current message",
X	"mail",		mailto,		"Send mail to users",
X	"send",		mailto,		"Send mail to users",
X	"list",		listmsg,	"List the headers of messages",
X	"headers",	listmsg,	"List the headers of messages",
X	"forward",	forward,	"Forward a message to another person",
X	"from",		listmsg,	"List the headers of messages",
X	"quit",		byebye,		"Leave IMS, expunging deleted messages",
X	"exit",		nxbyebye,	"Leave IMS without expunging messages",
X	"xit",		nxbyebye,	"Leave IMS without expunging messages",
X	"?",		help,		"Get help on a command",
X	"help",		help,		"Get help on a command",
X	"folder",	setfolder,	"Set or show the current folder",
X	"read",		readmbox,	"Read old-style mailbox into folder",
X	"inc",		readmbox,	"Read old-style mailbox into folder",
X	"folders",	foldlist,	"List the existing folders",
X	"alias",	aliasops,		"Display or create mail aliases",
X	"unalias",	unalias,	"Delete mail aliases",
X	(char *) 0,	readmsg,	"Read the named or numbered message",
X};
X
Xchar folder[256];
Xchar cabinet[256];
Xchar mailbox[1024];
Xchar prompt[256];
Xint msg;
Xjmp_buf mainloop;
Xchar sysmbox[1024];
Xchar autoread[256] = "No";
Xchar lines[256] = "24";
Xint _x;
X
Xintr() {
X	longjmp(mainloop, 1);
X}
X
Xmain(argc, argv)
Xchar *argv[]; {
X	char line[512];
X	char *cp;
X	int nmsg, incf;
X	struct stat sbuf;
X
X	if ((cp = getenv("HOME")) == (char *) 0)
X		cp = "/usr/guest";
X	sprintf(cabinet, "%s/.mail", cp);
X	sprintf(sysmbox, SYSMAILBOX, getlogin());
X	strcpy(mailbox, sysmbox);
X	strcpy(line, "incoming-mail");
X	strcpy(prompt, "IMS> ");
X	readvars();
X	readalias();
X	if (access(cabinet, 0) < 0)
X		if (mkdir(cabinet) < 0) {
X			puts("Can't create your mail cabinet.  Please check your CABINET and your home\ndirectory.");
X			exit(1);
X		}
X	if ((cp = getenv("FOLDER")) != (char *) 0)
X		strcpy(line, cp);
X	if (setfolder(line) < 0) {
X		puts("Please check your CABINET and FOLDER.");
X		exit(1);
X	}
X	if ((cp = getenv("MAIL")) != (char *) 0)
X		strcpy(mailbox, cp);
X	if ((cp = getenv("PAGER")) != (char *) 0)
X		strcpy(pager, cp);
X	if ((cp = getenv("VISUAL")) == (char *) 0)
X		if ((cp = getenv("EDITOR")) == (char *) 0)
X			cp = (char *) 0;
X	if (cp != (char *) 0)
X		strcpy(editor, cp);
X	if ((cp = getenv("PRINTMSG")) == (char *) 0)
X		strcpy(printcmd, "pr -h \"Printed by: ${LOGNAME:-${USER:-anonymous}} \" | lpr");
X	else
X		strcpy(printcmd, cp);
X	if ((cp = getenv("SAVEFOLDER")) != (char *) 0)
X		strcpy(savefolder, cp);
X	if ((cp = getenv("LINES")) != (char *) 0)
X		strcpy(lines, cp);
X	incf = 1;
X	nmsg = 1;
X	if (argc >= nmsg + 1 && strcmp(argv[nmsg], "-i") == 0) {
X		incf = 0;
X		nmsg++;
X	}
X	line[0] = '\0';
X	for (; nmsg < argc; nmsg++) {
X		strcat(line, " ");
X		strcat(line, argv[nmsg]);
X	}
X	if (line[0] != '\0') {
X		dispatch(line);
X		exit(0);
X	}
X	if (incf) {
X		msg = receive(mailbox);
X		if (msg == -1)
X			puts("Your system mailbox has been deleted.  Please contact your system administrator\nin order to have it restored.");
X		if (msg != 0)
X			printf("New messages start at %d.\n", msg);
X		else {
X			listmsg("");
X			msg = 1;
X		};
X	}
X	_x = 0;
X	if (isatty(2)) {
X		stat(ttyname(2), &sbuf);
X		if (sbuf.st_mode & 0111) {
X			_x = 1;
X			chmod(ttyname(2), sbuf.st_mode & ~0111);
X		}
X	}
X	signal(SIGINT, intr);
X	signal(SIGQUIT, SIG_IGN);
X	signal(SIGPIPE, SIG_IGN);
X	if (setjmp(mainloop) != 0)
X		puts("\nCommand interrupted.");
X	for (;;) {
X		if (newmail(mailbox)) {
X			puts("New mail has arrived.  Reading your mailbox...");
X			if (autoread[0] == 'Y' || autoread[0] == 'y' || autoread[0] == 'T' || autoread[0] == 't') {
X				nmsg = receive(mailbox);
X				if (nmsg == -1)
X					puts("Your system mailbox has been deleted.  Please contact your system administrator\nin order to have it restored.");
X				if (nmsg != 0)
X					printf("New messages start at %d.\n", nmsg);
X			}
X		}
X		printf("%s", prompt);
X		if (gets(line) == (char *) 0) {
X			putchar('\n');
X			expunge("");
X			break;
X		}
X		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
X			;
X		if (*cp == '!') {
X			system(cp + 1);
X			continue;
X		}
X		if (dispatch(cp) == 0)
X			break;
X	}
X	if (_x) {
X		stat(ttyname(2), &sbuf);
X		chmod(ttyname(2), sbuf.st_mode | 0111);
X	}
X	exit(0);
X}
X
Xdispatch(cmd)
Xchar *cmd; {
X	char *cp, *cmdp, *nextp;
X	int cnt, status, ran;
X	
X	for (;;) {
X		if ((nextp = strchr(cmd, ';')) == (char *) 0)
X			nextp = "\376";
X		else
X			*nextp++ = '\0';
X		for (cp = cmd; *cp == ' ' || *cp == '\t'; cp++)
X			;
X		for (cmdp = cp; *cp != '\0' && *cp != '\t' && *cp != ' '; cp++)
X			;
X		if (*cp != '\0')
X			*cp++ = '\0';
X		if (*cmdp == '\376')
X			break;
X		ran = 0;
X		cnt = 0;
X		if (strlen(cmdp) != 0) {
X			for (cnt = 0; cmdtab[cnt].c_cmd != (char *) 0; cnt++) {
X				if (strlen(cmdp) > strlen(cmdtab[cnt].c_cmd))
X					continue;
X				if (strncmp(cmdp, cmdtab[cnt].c_cmd, strlen(cmdp)) == 0)
X					if ((status = (*cmdtab[cnt].c_func)(cp)) < 1)
X						return status;
X					else {
X						ran = 1;
X						break;
X					}
X			}
X		}
X		while (cmdtab[cnt].c_cmd != (char *) 0)
X			cnt++;
X		if (!ran) {
X			if (cmdtab[cnt].c_func != (int (*)()) 0) {
X				if ((status = (*cmdtab[cnt].c_func)(cmdp)) < 1)
X					return status;
X			}
X			else {
X				printf("Unknown command: \"%s\".  Type \"help\" for help.\n", cmdp);
X				return -1;
X			}
X		}
X		cmd = nextp;
X	}
X	return status;
X}
X
Xhelp(cmdp)
Xchar *cmdp; {
X	char *cp, *dp;
X	int cnt, icnt;
X
X	for (cp = cmdp; *cp == ' ' || *cp == '\t'; cp++)
X		;
X	for (dp = cp; *dp != ' ' && *dp != '\t' && *dp != '\0'; dp++)
X		;
X	if (*dp != '\0')
X		*dp++ = '\0';
X	while (*dp == ' ' || *dp == '\t')
X		;
X	if (*dp != '\0') {
X		puts("Usage:  help [topic]");
X		return -1;
X	}
X	icnt = 0;
X	if (atoi(lines) <= 3)
X		strcpy(lines, "24");
X	for (cnt = 0; cmdtab[cnt].c_cmd != (char *) 0; cnt++) {
X		if (strlen(cp) > strlen(cmdtab[cnt].c_cmd))
X			continue;
X		if (*cp == '\0' || strncmp(cp, cmdtab[cnt].c_cmd, strlen(cp)) == 0) {
X			printf("  %-16s   %s\n", cmdtab[cnt].c_cmd, cmdtab[cnt].c_help);
X			icnt++;
X			if (icnt % (atoi(lines) - 1) == 0)
X				getcont();
X		}
X	}
X	if (*cp != '\0' && icnt == 0)
X		if (strcmp(cp, "default") == 0)
X			printf("  %-16s   %s\n", "Default:", cmdtab[cnt].c_help);
X		else {
X			printf("Unknown topic: \"%s\".\n", cp);
X			return -1;
X		}
X	else if (*cp == '\0')
X		printf("  %-16s   %s\n", "Default:", cmdtab[cnt].c_help);
X	return 1;
X}
X
Xsetfolder(cmdp)
Xchar *cmdp; {
X	char *cp;
X	
X	for (; *cmdp == ' ' || *cmdp == '\t'; cmdp++)
X		;
X	if (*cmdp == '\0') {
X		printf("The current folder is \"%s\".\n", folder);
X		return 1;
X	}
X	for (cp = cmdp; *cp != '\0' && *cp != ' ' && *cp != '\t'; cp++)
X		;
X	if (*cp != '\0')
X		*cp++ = '\0';
X	while (*cp != '\0')
X		if (*cp != ' ' || *cp != '\t') {
X			puts("Too many arguments.  Usage: folder [folder-name]");
X			return -1;
X		}
X	if (access(location(cmdp), 0) < 0) {
X		if (mkdir(location(cmdp)) < 0) {
X			fprintf(stderr, "Can't create \"%s\" folder.\n", cmdp);
X			return -1;
X		}
X		if (access(location(cmdp), 0) < 0) {
X			fprintf(stderr, "I can't get at your \"%s\" folder.\n, cmdp");
X			return -1;
X		}
X	}
X	strcpy(folder, cmdp);
X	return 1;
X}
X
Xmkdir(dir)
Xchar *dir; {
X	int pid, status;
X	
X	switch (pid = fork()) {
X		case -1:
X			perror("");
X			printf("Can't create directory \"%s\".\n", dir);
X			return -1;
X		case 0:
X			execl("/bin/mkdir", "mkdir", dir, (char *) 0);
X			_exit(-100);
X		default:
X			while (wait(&status) != pid)
X				;
X			if (status != 0)
X				return -1;
X	}
X	return 0;
X}
X
Xchar *location(folder)
Xchar *folder; {
X	static char buf[1024];
X	
X	if (folder[0] == '/' || folder[0] == '.')
X		return folder;
X	sprintf(buf, "%s/%s", cabinet, folder);
X	return buf;
X}
X
Xbyebye(cmdp)
Xchar *cmdp; {
X	for (; *cmdp == ' ' || *cmdp == '\t'; cmdp++)
X		;
X	if (*cmdp != '\0') {
X		puts("Too many arguments.  Usage: quit\n                        Or: xit");
X		return -1;
X	}
X	if (expunge("") < 0)
X		return -1;
X	return 0;
X}
X
Xnxbyebye(cmdp)
Xchar *cmdp; {
X	for (; *cmdp == ' ' || *cmdp == '\t'; cmdp++)
X		;
X	if (*cmdp != '\0') {
X		puts("Too many arguments.  Usage: xit");
X		return -1;
X	}
X	return 0;
X}
X
Xchar *basename(s)
Xchar *s; {
X	char *cp;
X	
X	if ((cp = strrchr(s, '/')) != (char *) 0)
X		return cp + 1;
X	return s;
X}
X
Xisfolder(f)
Xchar *f; {
X	struct stat s;
X	
X	if (stat(location(f), &s) < 0)
X		return 0;
X	return (s.st_mode & S_IFMT) == S_IFDIR;
X}
X
Xissysmbox(mbox)
Xchar *mbox; {
X	return strcmp(mbox, sysmbox) == 0;
X}
--EOF:ims.c--
fi

if test -r "imsread.c"; then
	echo "File imsread.c exists.  Enter new name or RETURN to skip.  (. to replace.)"
	read newname
	case "$newname" in
	".")	newname="imsread.c"
	esac
else
	newname="imsread.c"
fi
if test -z "$newname"; then
	echo "shx - $newname (skipped)"
else
	case "$newname" in
	"$sfile")
		echo "shx - $sfile (as $newname)"
		;;
	*)	echo "shx - $newname"
	esac
	sed 's/^X//' << '--EOF:imsread.c--' > "$newname"
X#include "ims.h"
X
Xchar pager[1024] = "/usr/ucb/more";
Xchar printcmd[1024];
Xchar savefolder[256] = "saved-mail";
Xchar askappend[] = "Yes";
X
Xnextmsg(cmdp)
Xchar *cmdp; {
X	for (; *cmdp == ' ' || *cmdp == '\t'; cmdp++)
X		;
X	if (*cmdp != '\0') {
X		puts("Usage:  next");
X		return -1;
X	}
X	msg++;
X	if (autonext[0] == 'y' || autonext[0] == 'Y' || autonext[0] == 't' || autonext[0] == 'T')
X		if (readmsg("-q") < 0) {
X			puts("No next message.");
X			return -1;
X		}
X	else if (!ismsg(msg)) {
X		puts("No next message.");
X		return -1;
X	}
X	return 1;
X}
X
Xprevmsg(cmdp)
Xchar *cmdp; {
X	for (; *cmdp == ' ' || *cmdp == '\t'; cmdp++)
X		;
X	if (*cmdp != '\0') {
X		puts("Usage:  previous");
X		return -1;
X	}
X	msg--;
X	if (autonext[0] == 'y' || autonext[0] == 'Y' || autonext[0] == 't' || autonext[0] == 'T')
X		if (readmsg("-q") < 0) {
X			puts("No next message.");
X			return -1;
X		}
X	else if (!ismsg(msg)) {
X		puts("No next message.");
X		return -1;
X	}
X	return 1;
X}
X
Xreadmsg(cmdp)
Xchar *cmdp; {
X	char *cp;
X	int foldmsg, quiet;
X	char mname[80];
X
X	quiet = 0;
X
X_again:
X	foldmsg = 0;
X	for (; *cmdp == ' ' || *cmdp == '\t'; cmdp++)
X		;
X	if (*cmdp != '\0') {
X		for (cp = cmdp; *cp != '\0' && *cp != ' ' && *cp != '\t'; cp++)
X			if (strchr("0123456789", *cp) == (char *) 0)
X				foldmsg = 1;
X		if (*cp != '\0')
X			*cp++ = '\0';
X		if (strcmp(cmdp, "-q") == 0) {
X			if (quiet) {
X				puts("Usage:  type [-q] [message-number]");
X				return -1;
X			}
X			else {
X				quiet = 1;
X				cmdp = cp;
X				goto _again;
X			}
X		}
X		while (*cp != '\0')
X			if (*cp != '\t' && *cp != ' ') {
X				puts("Too many arguments.  Usage: type [-q] [message-number]");
X				return -1;
X			}
X		if (!ismsg(cmdp)) {
X			if (!quiet)
X				puts("No such message.");
X			return -1;
X		}
X		if (!foldmsg)
X			msg = atoi(cmdp);
X	}
X	if (foldmsg)
X		return dorms(cmdp, pager);
X	sprintf(mname, ".%d", msg);
X	if (ismsg(mname))
X		return dorms(mname, pager);
X	sprintf(mname, "%d", msg);
X	if (!ismsg(mname)) {
X		if (!quiet)
X			printf("Message %d doesn't exist.\n", msg);
X		return -1;
X	}
X	return dorms(mname, pager);
X}
X
Xchar *mlocation(msg)
Xchar *msg; {
X	static char path[1024];
X	
X	if ((msg[0] == '.' && msg[1] == '/') || msg[0] == '/')
X		strcpy(path, msg);
X	else
X		sprintf(path, "%s/%s", location(folder), msg);
X	return path;
X}
X
Xchar *mflocation(folder, msg)
Xchar *folder, *msg; {
X	static char path[1024];
X	
X	if ((msg[0] == '.' && msg[1] == '/') || msg[0] == '/')
X		strcpy(path, msg);
X	else
X		sprintf(path, "%s/%s", location(folder), msg);
X	return path;
X}
X
Xdorms(msg, cmd)
Xchar *msg, *cmd; {
X	FILE *fp;
X	int status;
X
X	if (cmd == (char *) 0)
X		fp = (FILE *) 0;
X	else if ((fp = epopen(cmd, "w")) == (FILE *) 0) {
X		printf("Can't open pipe to command: %s\n", cmd);
X		return -1;
X	}
X	status = docopy(msg, fp);
X	if (fp != (FILE *) 0)
X		pclose(fp);
X	return 1;
X}
X
Xdosave(msg, file)
Xchar *msg, *file; {
X	FILE *fp;
X	int status;
X	char yesno[512];
X	char *cp;
X
X	if ((askappend[0] == 'Y' || askappend[0] == 'y' || askappend[0] == 'T' || askappend[0] == 't') && file != (char *) 0 && access(file, 0) == 0) {
X		printf("File exists.  Do you wish to append to it? ");
X		if (gets(yesno) == (char *) 0) {
X			puts("No");
X			return -1;
X		}
X		for (cp = yesno; *cp != ' ' && *cp != '\t'; cp++)
X			;
X		if (*cp != 'y' && *cp != 'Y')
X			return -1;
X	}
X	if (file == (char *) 0)
X		fp = (FILE *) 0;
X	else if ((fp = efopen(file, "a")) == (FILE *) 0) {
X		printf("Can't open file: %s\n", file);
X		return -1;
X	}
X	status = docopy(msg, fp);
X	if (fp != (FILE *) 0)
X		fclose(fp);
X	return status;
X}
X
Xdocopy(msg, mfp)
Xchar *msg;
XFILE *mfp; {
X	char line[1024];
X	FILE *fp;
X
X	if ((fp = efopen(mlocation(msg), "r")) == (FILE *) 0) {
X		printf("Message %s doesn't exist.\n", msg);
X		return -1;
X	}
X	if (mfp == (FILE *) 0)
X		mfp = stdout;
X	if (msg[0] == '.')
X		fprintf(mfp, "Message %s (deleted):\n", msg + 1);
X	else
X		fprintf(mfp, "Message %s:\n", msg);
X	dopipe(fp, mfp);
X	fclose(fp);
X	return 1;
X}
X
Xdopipe(msg, outp)
XFILE *msg, *outp; {
X	char ch;
X	
X	while ((ch = getc(msg)) != EOF)
X		putc(ch, outp);
X}
X
Xismsg(msg)
Xchar *msg; {
X	char delmsg[256];
X	struct stat sbuf;
X
X	strcpy(delmsg, ".");
X	strcat(delmsg, msg);
X	if (access(mlocation(delmsg), 0) == 0) {
X		stat(mlocation(delmsg), &sbuf);
X		return (sbuf.st_mode & S_IFMT) != S_IFDIR;
X	}
X	if (access(mlocation(msg), 0) < 0)
X		return 0;
X	stat(mlocation(msg), &sbuf);
X	return (sbuf.st_mode & S_IFMT) != S_IFDIR;
X}
X
Xisdeleted(msg)
Xchar *msg; {
X	char delmsg[256];
X
X	strcpy(delmsg, ".");
X	strcat(delmsg, msg);
X	return access(mlocation(delmsg), 0) == 0;
X}
X
Xprintmsg(cmdp)
Xchar *cmdp; {
X	char *cp;
X	int foldmsg;
X	char mname[80];
X
X	foldmsg = 0;
X	for (; *cmdp == ' ' || *cmdp == '\t'; cmdp++)
X		;
X	if (*cmdp != '\0') {
X		for (cp = cmdp; *cp != '\0' && *cp != ' ' && *cp != '\t'; cp++)
X			if (strchr("0123456789", *cp) == (char *) 0)
X				foldmsg = 1;
X		if (*cp != '\0')
X			*cp++ = '\0';
X		while (*cp != '\0')
X			if (*cp != '\t' && *cp != ' ') {
X				puts("Too many arguments.  Usage: print [message-number]");
X				return -1;
X			}
X		if (!ismsg(cmdp)) {
X			puts("No such message.");
X			return -1;
X		}
X		if (!foldmsg)
X			msg = atoi(cmdp);
X	}
X	if (foldmsg)
X		return dorms(cmdp, printcmd);
X	sprintf(mname, ".%d", msg);
X	if (ismsg(mname))
X		return dorms(mname, printcmd);
X	sprintf(mname, "%d", msg);
X	if (!ismsg(mname)) {
X		printf("Message %d doesn't exist.\n", msg);
X		return -1;
X	}
X	return dorms(mname, printcmd);
X}
X
Xsavemsg(cmdp)
Xchar *cmdp; {
X	char *cp, *savep;
X	int foldmsg, status;
X	char mname[80], savefile[80];
X
X	foldmsg = 0;
X	for (; *cmdp == ' ' || *cmdp == '\t'; cmdp++)
X		;
X	if (*cmdp == '\0')
X		sprintf(savefile, "%s/%s", location(savefolder), folder);
X	else {
X		for (cp = cmdp; *cp != ' ' && *cp != '\t' && *cp != '\0'; cp++)
X			;
X		if (*cp != '\0')
X			*cp++ = '\0';
X		if (strchr(cmdp, '/') != (char *) 0)
X			strcpy(savefile, cmdp);
X		else
X			sprintf(savefile, "%s/%s", location(savefolder), cmdp);
X		for (savep = cp; *cp != '\0' && *cp != ' ' && *cp != '\t'; cp++)
X			if (strchr("0123456789", *cp) == (char *) 0)
X				foldmsg = 1;
X		if (*cp != '\0')
X			*cp++ = '\0';
X		while (*cp == ' ' || *cp == '\t')
X			cp++;
X		if (*cp != '\0') {
X			puts("Too many arguments.  Usage: save file [message-number]");
X			return -1;
X		}
X		if (*savep != '\0') {
X			if (!ismsg(savep)) {
X				puts("No such message.");
X				return -1;
X			}
X			if (!foldmsg)
X				msg = atoi(savep);
X		}
X	}
X	if (foldmsg)
X		return dosave(savep, savefile);
X	sprintf(mname, ".%d", msg);
X	if (ismsg(mname))
X		status = dosave(mname, savefile);
X	else {
X		sprintf(mname, "%d", msg);
X		if (!ismsg(mname)) {
X			printf("Message %d doesn't exist.\n", msg);
X			return -1;
X		}
X		status = dosave(mname, savefile);
X	}
X	if (status < 1)
X		return status;
X	return delmsg("");
X}
X
Xgomsg(cmdp)
Xchar *cmdp; {
X	char *cp;
X
X	for (; *cmdp == ' ' || *cmdp == '\t'; cmdp++)
X		;
X	if (*cmdp == '\0') {
X		puts("Usage: goto message-number");
X		return -1;
X	}
X	for (cp = cmdp; *cp != '\0' && *cp != ' ' && *cp != '\t'; cp++)
X		if (strchr("0123456789", *cp) == (char *) 0) {
X			puts("Invalid message number.  Usage: goto message-number");
X			return -1;
X		}
X	if (*cp != '\0')
X		*cp++ = '\0';
X	while (*cp != '\0')
X		if (*cp != '\t' && *cp != ' ') {
X			puts("Too many arguments.  Usage: goto message-number");
X			return -1;
X		}
X	if (!ismsg(cmdp)) {
X		puts("No such message.");
X		return -1;
X	}
X	msg = atoi(cmdp);
X	if (autonext[0] == 'y' || autonext[0] == 'Y' || autonext[0] == 't' || autonext[0] == 'T')
X		return readmsg("");
X	return 1;
X}
X
Xlistmsg(cmdp)
Xchar *cmdp; {
X	char *cp;
X	DIR *dp;
X	struct direct *entry;
X	extern int errno;
X	extern char *sys_errlist[];
X	FILE *mp;
X	char mline[2048], from[21], subj[51];
X	int cnt, cmsg;
X
X	for (; *cmdp == ' ' || *cmdp == '\t'; cmdp++)
X		;
X	if (*cmdp == '\0')
X		cmdp = folder;
X	else {
X		for (cp = cmdp; *cp != '\0' && *cp != ' ' && *cp != '\t'; cp++)
X			;
X		if (*cp != '\0')
X			*cp++ = '\0';
X		while (*cp != '\0')
X			if (*cp != '\t' && *cp != ' ') {
X				puts("Too many arguments.  Usage: list [folder-name]\nOr: list folders");
X				return -1;
X			}
X		if (!isfolder(cmdp)) {
X			puts("No such folder.");
X			return -1;
X		}
X	}
X	if ((dp = opendir(location(cmdp))) == (DIR *) 0) {
X		puts("Can't open folder for listing.");
X		return -1;
X	}
X	cnt = 0;
X	if (atoi(lines) < 3)
X		strcpy(lines, "24");
X	while ((entry = readdir(dp)) != (struct direct *) 0) {
X		if (entry->d_name[0] == '.' && (entry->d_name[1] == '\0' || (entry->d_name[1] == '.' && entry->d_name[2] == '\0')))
X			continue;
X		from[0] = '\0';
X		subj[0] = '\0';
X		cnt++;
X		if (cnt == 1)
X			printf("Contents of folder \"%s\":\nSt Name     From                 Subject\n", cmdp);
X		if (sscanf(entry->d_name, "%d", &cmsg) != 1)
X			putchar(' ');
X		else if (cmsg == msg)
X			putchar('*');
X		else
X			putchar(' ');
X		if (entry->d_name[0] == '.')
X			printf("D %-8.8s ", &entry->d_name[1]);
X		else
X			printf("  %-8.8s ", entry->d_name);
X		if ((mp = efopen(mflocation(cmdp, entry->d_name), "r")) == (FILE *) 0) {
X			puts("unreadable");
X			if (cnt % (atoi(lines) - 1) == 0)
X				getcont();
X			continue;
X		}
X		while (fgets(mline, sizeof mline, mp) != (char *) 0) {
X			if (mline[0] == '\n')
X				break;
X			if (strncmp(mline, "Subject: ", 9) == 0) {
X				mline[strlen(mline) - 1] = '\0';
X				strncpy(subj, &mline[9], 50);
X				subj[50] = '\0';
X			}
X			if (strncmp(mline, "From: ", 6) == 0) {
X				mline[strlen(mline) - 1] = '\0';
X				strncpy(from, &mline[6], 20);
X				from[20] = '\0';
X			}
X		}
X		printf("%-20.20s", from);
X		if (subj[0] != '\0')
X			printf(" \"%.50s\"", subj);
X		putchar('\n');
X		if (cnt % (atoi(lines) - 1) == 0)
X			getcont();
X		fclose(mp);
X	}
X	if (cnt == 0)
X		printf("No messages in folder \"%s\".\n", folder);
X	return 1;
X}
X
Xfoldlist(cmdp)
Xchar *cmdp; {
X	DIR *dp;
X	struct direct *entry;
X	int cnt;
X	
X	while (*cmdp == '\t' || *cmdp == ' ')
X		cmdp++;
X	if (*cmdp != '\0') {
X		puts("Usage:  folders");
X		return -1;
X	}
X	if ((dp = opendir(cabinet)) == (DIR *) 0) {
X		puts("Can't open cabinet to list folders.");
X		return -1;
X	}
X	puts("The following folders exist in the cabinet:");
X	if (atoi(lines) <= 3)
X		strcpy(lines, "24");
X	while ((entry = readdir(dp)) != (struct direct *) 0) {
X		if (entry->d_name[0] == '.' && (entry->d_name[1] == '\0' || (entry->d_name[1] == '.' && entry->d_name[2] == '\0')))
X			continue;
X		printf("  %s\n", entry->d_name);
X		if (++cnt % atoi(lines) - 1 == 0)
X			getcont();
X	}
X	return 1;
X}
--EOF:imsread.c--
fi

if test -r "imsdel.c"; then
	echo "File imsdel.c exists.  Enter new name or RETURN to skip.  (. to replace.)"
	read newname
	case "$newname" in
	".")	newname="imsdel.c"
	esac
else
	newname="imsdel.c"
fi
if test -z "$newname"; then
	echo "shx - $newname (skipped)"
else
	case "$newname" in
	"$sfile")
		echo "shx - $sfile (as $newname)"
		;;
	*)	echo "shx - $newname"
	esac
	sed 's/^X//' << '--EOF:imsdel.c--' > "$newname"
X#include "ims.h"
X
Xchar autonext[128] = "No";
X
Xdelmsg(cmdp)
Xchar *cmdp; {
X	char *cp;
X	char mname[80], curmsg[80], mt1[100], mt2[100];
X
X	for (; *cmdp == ' ' || *cmdp == '\t'; cmdp++)
X		;
X	if (*cmdp != '\0') {
X		for (cp = cmdp; *cp != '\0' && *cp != ' ' && *cp != '\t'; cp++)
X			;
X		if (*cp != '\0')
X			*cp++ = '\0';
X		while (*cp != '\0')
X			if (*cp != '\t' && *cp != ' ') {
X				puts("Too many arguments.  Usage: delete [message-number]");
X				return -1;
X			}
X		if (!ismsg(cmdp)) {
X			puts("No such message.");
X			return -1;
X		}
X	}
X	else {
X		sprintf(curmsg, "%d", msg);
X		cmdp = curmsg;
X	}
X	sprintf(mname, ".%s", cmdp);
X	if (ismsg(mname)) {
X		puts("The message is already deleted.  Type \"expunge\" to free its space.");
X		return -1;
X	}
X	if (!ismsg(cmdp)) {
X		puts("There is no such message.");
X		return -1;
X	}
X	strcpy(mname, ".");
X	strcat(mname, cmdp);
X	strcpy(mt1, mlocation(cmdp));
X	strcpy(mt2, mlocation(mname));
X	if (link(mt1, mt2) < 0) {
X		puts("Could not flag the message as deleted.");
X		return -1;
X	}
X	if (unlink(mt1) < 0) {
X		puts("Could not flag the message as deleted.");
X		unlink(mt2);
X		return -1;
X	}
X	msg++;
X	if (autonext[0] == 'y' || autonext[0] == 'Y' || autonext[0] == 't' || autonext[0] == 'T')
X		if (readmsg("-q") < 0) {
X			puts("No next message.");
X			return -1;
X		}
X	return 1;
X}
X
Xundelmsg(cmdp)
Xchar *cmdp; {
X	char *cp;
X	char mname[80], curmsg[80], mt1[100], mt2[100];
X
X	for (; *cmdp == ' ' || *cmdp == '\t'; cmdp++)
X		;
X	if (*cmdp != '\0') {
X		for (cp = cmdp; *cp != '\0' && *cp != ' ' && *cp != '\t'; cp++)
X			;
X		if (*cp != '\0')
X			*cp++ = '\0';
X		while (*cp != '\0')
X			if (*cp != '\t' && *cp != ' ') {
X				puts("Too many arguments.  Usage: undelete [message-number]");
X				return -1;
X			}
X		if (!ismsg(cmdp)) {
X			puts("No such message.");
X			return -1;
X		}
X	}
X	else {
X		sprintf(curmsg, "%d", msg);
X		cmdp = curmsg;
X	}
X	sprintf(mname, ".%s", cmdp);
X	if (!ismsg(mname)) {
X		puts("The message isn't deleted.");
X		return -1;
X	}
X	strcpy(mname, ".");
X	strcat(mname, cmdp);
X	strcpy(mt1, mlocation(cmdp));
X	strcpy(mt2, mlocation(mname));
X	if (link(mt2, mt1) < 0) {
X		puts("Could not flag the message as restored.");
X		return -1;
X	}
X	if (unlink(mt2) < 0) {
X		puts("Could not flag the message as restored.");
X		unlink(mt1);
X		return -1;
X	}
X	return 1;
X}
X
Xexpunge(cmdp)
Xchar *cmdp; {
X	char *cp;
X	DIR *dp;
X	struct direct *entry;
X	extern int errno;
X	extern char *sys_errlist[];
X
X	for (; *cmdp == ' ' || *cmdp == '\t'; cmdp++)
X		;
X	if (*cmdp == '\0')
X		cmdp = folder;
X	else {
X		for (cp = cmdp; *cp != '\0' && *cp != ' ' && *cp != '\t'; cp++)
X			;
X		if (*cp != '\0')
X			*cp++ = '\0';
X		while (*cp != '\0')
X			if (*cp != '\t' && *cp != ' ') {
X				puts("Too many arguments.  Usage: expunge [folder-name]");
X				return -1;
X			}
X		if (!isfolder(cmdp)) {
X			puts("No such folder.");
X			return -1;
X		}
X	}
X	if ((dp = opendir(location(cmdp))) == (DIR *) 0) {
X		puts("Can't open folder for expunging.");
X		return -1;
X	}
X	while ((entry = readdir(dp)) != (struct direct *) 0) {
X		if (entry->d_name[0] != '.')
X			continue;
X		if (entry->d_name[1] == '\0' || (entry->d_name[1] == '.') && entry->d_name[2] == '\0')
X			continue;
X		if (unlink(mflocation(cmdp, entry->d_name)) < 0)
X			printf("Couldn't expunge message: %s (%s)\n", &entry->d_name[1], sys_errlist[errno]);
X	}
X	return 1;
X}
--EOF:imsdel.c--
fi

if test -r "imsinc.c"; then
	echo "File imsinc.c exists.  Enter new name or RETURN to skip.  (. to replace.)"
	read newname
	case "$newname" in
	".")	newname="imsinc.c"
	esac
else
	newname="imsinc.c"
fi
if test -z "$newname"; then
	echo "shx - $newname (skipped)"
else
	case "$newname" in
	"$sfile")
		echo "shx - $sfile (as $newname)"
		;;
	*)	echo "shx - $newname"
	esac
	sed 's/^X//' << '--EOF:imsinc.c--' > "$newname"
X#include "ims.h"
X
Xreadmbox(cmdp)
Xchar *cmdp; {
X	char *cp;
X	int cnt;
X
X	for (; *cmdp == ' ' || *cmdp == '\t'; cmdp++)
X		;
X	if (*cmdp != '\0') {
X		for (cp = cmdp; *cp != '\0' && *cp != ' ' && *cp != '\t'; cp++)
X			;
X		if (*cp != '\0')
X			*cp++ = '\0';
X		while (*cp != '\0')
X			if (*cp != '\t' && *cp != ' ') {
X				puts("Too many arguments.  Usage: read [mailbox-name]");
X				return -1;
X			}
X	}
X	else
X		cmdp = mailbox;
X	if ((cnt = receive(cmdp)) == -1) {
X		puts("No mailbox.");
X		return -1;
X	}
X	return 1;
X}
X
Xreceive(mbox)
Xchar *mbox; {
X	FILE *mbp, *mp;
X	char line[1024], from[512], subj[512], date[512], msgn[40], path[1024];
X	static char cc[10240], to[10240];
X	char *cp;
X	int first, next, cnt;
X	enum {
X		Initial,
X		HLine1,
X		Header,
X		Body,
X	} state;
X
X	next = 0;
X	do
X		sprintf(msgn, "%d", ++next);
X	while (access(mlocation(msgn), 0) == 0);
X	if (issysmbox(mbox))
X		if (lockmbox(1) < 0) {
X			puts("Mailbox is locked.  Try again in a few minutes.");
X			return 0;
X		}
X	if ((mbp = efopen(mbox, "r")) == (FILE *) 0) {
X		printf("Can't open mailbox: %s\n", mbox);
X		if (issysmbox(mbox))
X			lockmbox(0);
X		return 0;
X	}
X	first = next;
X	state = Initial;
X	cnt = 0;
X	while (fgets(line, sizeof line, mbp) != (char *) 0) {
X		if (strncmp(line, "From ", 5) == 0) {
X			if (state != Initial) {
X				fclose(mp);
X				do
X					sprintf(msgn, "%d", ++next);
X				while (ismsg(msgn));
X			}
X			if ((mp = efopen(mlocation(msgn), "w")) == (FILE *) 0) {
X				printf("Error:  can't create new message %d.  Mailbox restored.\n", next);
X				fclose(mbp);
X				if (issysmbox(mbox))
X					lockmbox(0);
X				return 0;
X			}
X			cnt++;
X			strcpy(to, "");
X			strcpy(from, "");
X			strcpy(path, "Path: ");
X			strcpy(cc, "");
X			strcpy(subj, "");
X			strcpy(date, "Date: ");
X			for (cp = line + 5; *cp != ' '; cp++)
X				;
X			*cp++ = '\0';
X			strcat(path, line + 5);
X			strcat(path, "\n");
X			while (*cp == ' ')
X				cp++;
X			strcat(date, cp);
X			state = HLine1;
X			continue;
X		}
X		if (state == HLine1) {
X			state = Body;
X			if (strncmp(line, ">From ", 6) == 0 && rmtinc(path, line)) {
X				state = HLine1;
X				continue;
X			}
X			for (cp = line; *cp != '\0'; cp++)
X				if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01235456789_-", *cp) == (char *) 0)
X					break;
X			if (*cp == ':' && *(cp + 1) == ' ')
X				state = Header;
X			else {
X				if (from[0] == '\0') {
X					strcpy(from, "From: ");
X					strcat(from, &path[6]);
X				}
X				fputs(date, mp);
X				fputs(path, mp);
X				fputs(from, mp);
X				fputs(subj, mp);
X				fputs(to, mp);
X				fputs(cc, mp);
X				putc('\n', mp);
X				if (cnt == 1)
X					printf("New mail in folder \"%s\":\n", folder);
X				printf("  %5d   %-20.*s", next, (strlen(from) > 27? 20: strlen(from) - 7), &from[6]);
X				if (subj[0] != '\0')
X					printf(" \"%.*s\"", (subj[0] == '\0'? 0: (strlen(subj) < 50? strlen(subj) - 10: 40)), &subj[9]);
X				putchar('\n');
X			}
X		}
X		if (state == Header && line[0] == '\n') {
X			if (from[0] == '\0') {
X				strcpy(from, "From: ");
X				strcat(from, &path[6]);
X			}
X			fputs(date, mp);
X			fputs(path, mp);
X			fputs(from, mp);
X			fputs(subj, mp);
X			fputs(to, mp);
X			fputs(cc, mp);
X			if (cnt == 1)
X				printf("New mail in folder \"%s\":\n", folder);
X			printf("  %5d   %-20.*s", next, (strlen(from) > 27? 20: strlen(from) - 7), &from[6]);
X			if (subj[0] != '\0')
X				printf(" \"%.*s\"", (subj[0] == '\0'? 0: (strlen(subj) < 50? strlen(subj) - 10: 40)), &subj[9]);
X			putchar('\n');
X			state = Body;
X		}
X		if (state == Body) {	
X			fputs(line, mp);
X			continue;
X		}
X		if (strncmp(line, "Date: ", 6) == 0) {
X			strcpy(date, line);
X			continue;
X		}
X		if (strncmp(line, "From: ", 6) == 0) {
X			strcpy(from, line);
X			continue;
X		}
X		if (strncmp(line, "Subject: ", 9) == 0) {
X			strcpy(subj, line);
X			continue;
X		}
X		if (strncmp(line, "To: ", 4) == 0) {
X			strcat(to, line);
X			continue;
X		}
X		if (strncmp(line, "Cc: ", 4) == 0) {
X			strcat(cc, line);
X			continue;
X		}
X	}
X	if (state != Initial)
X		fclose(mp);
X	fclose(mbp);
X	if (!issysmbox(mbox)) {
X		if (unlink(mbox) < 0)
X			puts("Couldn't delete the mailbox.");
X	}
X	else {
X		if ((mbp = efopen(mbox, "w")) == (FILE *) 0)
X			puts("Couldn't empty your mailbox; check the ownership.");
X		else
X			fclose(mbp);
X		if (lockmbox(0) < 0)
X			puts("Can't unlock your mailbox (please inform a system administrator).");
X	}
X	if (cnt == 0) {
X		puts("No new messages.");
X		return 0;
X	}
X	printf("Received %d new message%s into folder.\n", cnt, (cnt == 1? "": "s"));
X	return first;
X}
X
Xlockmbox(flag) {
X	char lockname[1024];
X	struct stat sbuf;
X	long now;
X	int lfd;
X	
X	strcpy(lockname, sysmbox);
X	strcat(lockname, ".lock");
X	if (flag == 0)
X		return unlink(lockname);
X	if (stat(lockname, &sbuf) == 0) {
X		time(&now);
X		if (now - sbuf.st_ctime < 300)
X			return -1;
X		else if (unlink(lockname) < 0) {
X			puts("Can't unlock your mailbox (fatal error).");
X			exit(1);
X		}
X	}
X	if ((lfd = creat(lockname, 0600)) < 0) {
X		puts("Can't lock your mailbox (fatal error).");
X		exit(1);
X	}
X	close(lfd);
X	return 0;
X}
X
Xnewmail(f)
Xchar *f; {
X	struct stat sbuf;
X	
X	if (stat(f, &sbuf) < 0)
X		return 0;
X	return sbuf.st_size > 0L;
X}
X
X/*
X * line contains ``>From user ...''
X * if after date comes ``remote from ...'' then generate new path from it
X */
X
Xrmtinc(path, line)
Xchar *path, *line; {
X	char *cp, *rpath;
X	
X	if (line[strlen(line) - 1] == '\n')
X		line[strlen(line) - 1] = '\0';
X	for (cp = line + strlen(line) - 1; *cp == ' '; cp--)
X		if (cp == line)
X			return 0;
X	*(cp + 1) = '\0';
X	while (*cp != ' ')
X		if (--cp == line)
X			return 0;
X	rpath = cp + 1;
X	while (*cp == ' ')
X		if (--cp == line)
X			return 0;
X	*(cp + 1) = '\0';
X	while (*cp != ' ')
X		if (--cp == line)
X			return 0;
X	if (strcmp(cp + 1, "from") != 0)
X		return 0;
X	while (*cp == ' ')
X		if (--cp == line)
X			return 0;
X	*(cp + 1) = '\0';
X	while (*cp != ' ')
X		if (--cp == line)
X			return 0;
X	if (strcmp(cp + 1, "remote") != 0)
X		return 0;
X	cp = line + 6;
X	while (*cp != ' ')
X		cp++;
X	*cp = '\0';
X	sprintf(path, "Path: %s!%s\n", rpath, line + 6);
X	return 1;
X}
--EOF:imsinc.c--
fi

if test -r "imssend.c"; then
	echo "File imssend.c exists.  Enter new name or RETURN to skip.  (. to replace.)"
	read newname
	case "$newname" in
	".")	newname="imssend.c"
	esac
else
	newname="imssend.c"
fi
if test -z "$newname"; then
	echo "shx - $newname (skipped)"
else
	case "$newname" in
	"$sfile")
		echo "shx - $sfile (as $newname)"
		;;
	*)	echo "shx - $newname"
	esac
	sed 's/^X//' << '--EOF:imssend.c--' > "$newname"
X#include "ims.h"
X
Xchar *mdate();
X
Xchar sender[1024] = "/bin/mail";
Xchar editor[1024] = "/usr/ucb/vi";
Xchar edforward[256] = "No";
Xchar alicount[256] = "20";
X
Xstatic char *_month_[] = {
X	"January",
X	"February",
X	"March",
X	"April",
X	"May",
X	"June",
X	"July",
X	"August",
X	"September",
X	"October",
X	"November",
X	"December",
X};
X
Xstatic char *_weekday_[] = {
X	"Sunday",
X	"Monday",
X	"Tuesday",
X	"Wednesday",
X	"Thursday",
X	"Friday",
X	"Saturday",
X};
X
Xsendmail(fd, to, cc, subj, bcc)
XFILE *fd;
Xchar *to, *cc, *subj, *bcc; {
X	char *cp;
X	FILE *rmail;
X	char name[512];
X	static char toline[512], ccline[512];
X	struct passwd *curuser;
X	
X	if (fd == (FILE *) 0)
X		return 0;
X	if ((curuser = getpwnam(getlogin())) == (struct passwd *) 0) {
X		puts("Who are you, anyway?  I can't send mail from nobody!");
X		return -1;
X	}
X	strcpy(name, curuser->pw_gecos);
X	if ((cp = strchr(name, ',')) != (char *) 0)
X		*cp = '\0';
X	strcpy(toline, to);
X	strcpy(ccline, cc);
X	while (*to == ' ' || *to == '\t')
X		to++;
X	if (*to == '\0') {
X		puts("Empty To: list.");
X		return 0;
X	}
X	sendlist(to, name, toline, ccline, subj, fd, 0);
X	sendlist(cc, name, toline, ccline, subj, fd, 0);
X	sendlist(bcc, name, toline, ccline, subj, fd, 0);
X	return 1;
X}
X
Xsendlist(list, from, toline, ccline, subj, fd, acount)
Xchar *list, *from, *toline, *ccline, *subj;
XFILE *fd; {
X	char *cp;
X	char aexp[1024];
X
X	if (atoi(alicount) < 0)
X		strcpy(alicount, "20");
X	if (acount > atoi(alicount)) {
X		puts("Alias loop.");
X		return -1;
X	}
X	while (*list != '\0') {
X		while (*list == ' ' || *list == '\t')
X			list++;
X		for (cp = list; *cp != ',' && *cp != '\0' && *cp != ' ' && *cp != '\t'; cp++)
X			;
X		if (*cp != '\0')
X			*cp++ = '\0';
X		if (*list == '\0')
X			break;
X		if (xalias(list, aexp))
X			return sendlist(aexp, from, toline, ccline, subj, fd, acount + 1);
X		smtp(from, list, toline, ccline, subj, fd);
X		list = cp;
X	}
X	return 1;
X}
X
Xsmtp(from, to, toline, ccline, subjline, fp)
Xchar *from, *to, *toline, *ccline, *subjline;
XFILE *fp; {
X	char cmd[256];
X	char ch;
X	FILE *rmail;
X
X	sprintf(cmd, "exec %s %s", sender, to);
X	if ((rmail = epopen(cmd, "w")) == (FILE *) 0) {
X		puts("Can't run mailer.");
X		return 0;
X	}
X	fprintf(rmail, "Date: %s\nFrom: %s  <%s>\nSubject: %s\nTo: %s\n%s%s\n", mdate(), from, getlogin(), subjline, toline, (ccline[0] == '\0'? "": "Cc: "), ccline, (ccline[0] == '\0'? "": "\n"));
X	rewind(fp);
X	while ((ch = getc(fp)) != EOF)
X		putc(ch, rmail);
X	if (pclose(rmail) != 0)
X		printf("Mailer failed to %s.\n", to);
X}
X
Xreply(cmdp)
Xchar *cmdp; {
X	char *cp;
X	char curbuf[256], to[256], cc[256], subj[256], bcc[256], tmpf[80], mline[5120];
X	FILE *fp, *mfp;
X
X	while (*cmdp == ' ' || *cmdp == '\t')
X		cmdp++;
X	if (*cmdp != '\0') {
X		for (cp = cmdp; *cp != ' ' && *cp != '\t' && *cp != '\0'; cp++)
X			;
X		if (*cp != '\0')
X			*cp++ = '\0';
X		while (*cp == ' ' || *cp == '\t')
X			cp++;
X		if (*cp != '\0') {
X			puts("Wrong number of arguments.  Usage:  reply [message-number]");
X			return -1;
X		}
X		if (!ismsg(cmdp)) {
X			puts("No such message.");
X			return -1;
X		}
X		if (isdeleted(cmdp)) {
X			sprintf(curbuf, ".%s", cmdp);
X			cmdp = curbuf;
X		}
X	}
X	else {
X		sprintf(curbuf, "%d", msg);
X		if (isdeleted(curbuf))
X			sprintf(curbuf, ".%d", msg);
X		cmdp = curbuf;
X	}
X	bcc[0] = '\0';
X	gettcs(cmdp, to, cc, subj);
X	edenv(to, cc, subj, bcc);
X	sprintf(tmpf, "/tmp/ms%05dr", getpid());
X	if ((fp = efopen(tmpf, "w")) == (FILE *) 0) {
X		puts("Can't open message temp file.");
X		return -1;
X	}
X	if ((mfp = efopen(mlocation(cmdp), "r")) == (FILE *) 0) {
X		perror(mlocation(cmdp));
X		puts("Can't open original message.");
X		fputs("> Couldn't copy the original message.\n", fp);
X		fclose(fp);
X	}
X	else {
X		fputs("+---------------\n", fp);
X		while (fgets(mline, sizeof mline, mfp) != (char *) 0) {
X			fputs("| ", fp);
X			fputs(mline, fp);
X		}
X		fputs("+---------------\n\n\n", fp);
X		fclose(fp);
X		fclose(mfp);
X	}
X	enter(tmpf);
X	if (!suresend()) {
X		unlink(tmpf);
X		puts("Reply aborted.");
X		return 1;
X	}
X	puts("Please check the envelope below and make any necessary changes.");
X	if (!edenv(to, cc, subj, bcc))
X		return -1;
X	if ((fp = efopen(tmpf, "r")) == (FILE *) 0) {
X		puts("Can't open reply file.");
X		return -1;
X	}
X	if (!sendmail(fp, to, cc, subj, bcc)) {
X		fclose(fp);
X		dead(tmpf);
X	}
X	fclose(fp);
X	unlink(tmpf);
X	return 1;
X}
X
Xmailto(cmdp)
Xchar *cmdp; {
X	char *cp;
X	char curbuf[256], to[256], cc[256], subj[256], bcc[256], tmpf[80];
X	FILE *fp;
X
X	while (*cmdp == ' ' || *cmdp == '\t')
X		cmdp++;
X	if (*cmdp != '\0') {
X		for (cp = cmdp; *cp != ' ' && *cp != '\t' && *cp != '\0'; cp++)
X			;
X		if (*cp != '\0')
X			*cp++ = '\0';
X		while (*cp == ' ' || *cp == '\t')
X			cp++;
X		if (*cp != '\0') {
X			puts("Wrong number of arguments.  Usage:  mail [user-name]");
X			return -1;
X		}
X	}
X	bcc[0] = '\0';
X	strcpy(to, cmdp);
X	subj[0] = '\0';
X	cc[0] = '\0';
X	edenv(to, cc, subj, bcc);
X	sprintf(tmpf, "/tmp/ms%05ds", getpid());
X	if ((fp = efopen(tmpf, "w")) == (FILE *) 0) {
X		puts("Can't open message temp file.");
X		return -1;
X	}
X	fclose(fp);
X	enter(tmpf);
X	if (!suresend()) {
X		unlink(tmpf);
X		puts("Mail aborted.");
X		return 1;
X	}
X	puts("Please check the envelope below and make any necessary changes.");
X	if (!edenv(to, cc, subj, bcc))
X		return -1;
X	if ((fp = efopen(tmpf, "r")) == (FILE *) 0) {
X		puts("Can't open message temp file.");
X		return -1;
X	}
X	if (!sendmail(fp, to, cc, subj, bcc)) {
X		fclose(fp);
X		dead(tmpf);
X	}
X	fclose(fp);
X	unlink(tmpf);
X	return 1;
X}
X
Xforward(cmdp)
Xchar *cmdp; {
X	char *cp;
X	char curbuf[256], to[256], cc[256], subj[256], bcc[256], tmpf[80];
X	static char mline[5120];
X	FILE *fp, *mfp;
X
X	while (*cmdp == ' ' || *cmdp == '\t')
X		cmdp++;
X	if (*cmdp == '\0') {
X		puts("Wrong number of arguments.  Usage:  forward user-name [message-number]");
X		return -1;
X	}
X	for (cp = cmdp; *cp != ' ' && *cp != '\t' && *cp != '\0'; cp++)
X		;
X	if (*cp != '\0')
X		*cp++ = '\0';
X	strcpy(to, cmdp);
X	cmdp = cp;
X	if (*cmdp != '\0') {
X		for (cp = cmdp; *cp != ' ' && *cp != '\t' && *cp != '\0'; cp++)
X			;
X		if (*cp != '\0')
X			*cp++ = '\0';
X		while (*cp == ' ' || *cp == '\t')
X			cp++;
X		if (*cp != '\0') {
X			puts("Wrong number of arguments.  Usage:  forward user-name [message-number]");
X			return -1;
X		}
X		if (!ismsg(cmdp)) {
X			puts("No such message.");
X			return -1;
X		}
X		if (isdeleted(cmdp)) {
X			sprintf(curbuf, ".%s", cmdp);
X			cmdp = curbuf;
X		}
X	}
X	else {
X		sprintf(curbuf, "%d", msg);
X		if (isdeleted(curbuf))
X			sprintf(curbuf, ".%d", msg);
X		cmdp = curbuf;
X	}
X	gettcs(cmdp, (char *) 0, (char *) 0, subj);
X	if (subj[0] == '\0')
X		sprintf(subj, "Mail forwarded from \"%s\"", getlogin());
X	bcc[0] = '\0';
X	cc[0] = '\0';
X	edenv(to, cc, subj, bcc);
X	sprintf(tmpf, "/tmp/ms%05df", getpid());
X	if ((fp = efopen(tmpf, "w")) == (FILE *) 0) {
X		puts("Can't open message temp file.");
X		return -1;
X	}
X	if ((mfp = efopen(mlocation(cmdp), "r")) == (FILE *) 0) {
X		perror(mlocation(cmdp));
X		puts("Can't open original message.");
X		fclose(fp);
X		unlink(tmpf);
X		return -1;
X	}
X	else {
X		while (fgets(mline, sizeof mline, mfp) != (char *) 0)
X			fputs(mline, fp);
X		fclose(fp);
X		fclose(mfp);
X	}
X	if ((edforward[0] == 'Y' || edforward[0] == 'y' || edforward[0] == 'T' || edforward[0] == 't') && isatty(0))
X		enter(tmpf);
X	if (!suresend()) {
X		unlink(tmpf);
X		puts("Forward aborted.");
X		return 1;
X	}
X	if (edforward[0] == 'Y' || edforward[0] == 'y' || edforward[0] == 'T' || edforward[0] == 't')
X		edenv(to, cc, subj, bcc);
X	if ((fp = efopen(tmpf, "r")) == (FILE *) 0) {
X		puts("Can't open message temp file.");
X		return -1;
X	}
X	if (!sendmail(fp, to, cc, subj, bcc)) {
X		fclose(fp);
X		dead(tmpf);
X	}
X	fclose(fp);
X	unlink(tmpf);
X	return 1;
X}
X
Xenter(file)
Xchar *file; {
X	int (*intr)(), (*quit)();
X	int pid, status;
X
X	if (!isatty(0)) {
X		FILE *fp;
X		char iline[1024];
X		
X		if ((fp = efopen(file, "w")) == (FILE *) 0) {
X			printf("Can't open file \"%s\".\n", file);
X			return;
X		}
X		while (gets(iline) != (char *) 0)
X			fprintf(fp, "%s\n", iline);
X		fclose(fp);
X		return;
X	}
X	switch (pid = fork()) {
X		case -1:
X			puts("Couldn't fork.");
X			break;
X		case 0:
X			setgid(getgid());
X			setuid(getuid());
X			execlp(editor, editor, file, (char *) 0);
X			perror(editor);
X			exit(0);
X		default:
X			intr = signal(SIGINT, SIG_IGN);
X			quit = signal(SIGQUIT, SIG_IGN);
X			while (wait(&status) != pid)
X				;
X			signal(SIGINT, intr);
X			signal(SIGQUIT, quit);
X			if (status != 0)
X				puts("Editor failure detected.");
X	}
X}
X
Xdead(file)
Xchar *file; {
X/*
X * Usually, the real mailer (rmail) will deal with dead.letter files.  If
X * yours doesn't, change this function.
X */
X}
X
Xedenv(to, cc, subj, bcc)
Xchar *to, *subj, *cc, *bcc; {
X	printf("To: ");
X	edstr(to);
X	printf("Subject: ");
X	edstr(subj);
X	printf("Cc: ");
X	edstr(cc);
X	printf("Bcc: ");
X	edstr(bcc);
X}
X
Xedstr(str)
Xchar *str; {
X	char ch;
X	char *sp;
X	struct TERMIO ottyp, nttyp;
X	int (*intr)(), (*quit)();
X	
X	if (ioctl(0, TIO_GET, &ottyp) < 0)
X		return;
X	intr = signal(SIGINT, SIG_IGN);
X	quit = signal(SIGQUIT, SIG_IGN);
X	nttyp = ottyp;
X#ifdef USG
X	nttyp.c_lflag &= ~(ICANON|ECHO|ECHOE);
X	nttyp.c_cc[VMIN] = 1;
X	nttyp.c_cc[VTIME] = 0;
X#else  USG
X	nttyp.sg_flags |= CBREAK;
X	nttyp.sg_flags &= ~ECHO;
X#endif USG
X	ioctl(0, TIO_SET, &nttyp);
X	for (sp = str; *sp != '\0'; sp++)
X		putchar(*sp);
X	for (;;) {
X		if ((ch = (getchar() & 0x7f)) == '\n')
X			break;
X		if (ch == nttyp.TIO_ERASE) {
X			if (sp == str) {
X				putchar('\7');
X				continue;
X			}
X			putchar('\b');
X			putchar(' ');
X			putchar('\b');
X			sp--;
X			continue;
X		}
X		if (ch < ' ') {
X			putchar('\7');
X			continue;
X		}
X		putchar(ch);
X		*sp++ = ch;
X	}
X	ioctl(0, TIO_SET, &ottyp);
X	putchar('\n');
X	*sp = '\0';
X	signal(SIGINT, intr);
X	signal(SIGQUIT, quit);
X}
X
Xgettcs(msg, to, cc, subj)
Xchar *msg, *to, *cc, *subj; {
X	FILE *fp;
X	char mline[5120];
X	char *cp;
X	
X	if ((fp = efopen(mlocation(msg), "r")) == (FILE *) 0) {
X		puts("Can't read envelope of message.");
X		return;
X	}
X	if (to != (char *) 0)
X		to[0] = '\0';
X	if (cc != (char *) 0)
X		cc[0] = '\0';
X	if (subj != (char *) 0)
X		subj[0] = '\0';
X	while (fgets(mline, sizeof mline, fp) != (char *) 0) {
X		if (mline[0] == '\n' || mline[0] == ' ')
X			break;
X		if ((cp = strrchr(mline, '\n')) != (char *) 0)
X			*cp = '\0';
X		if (to != (char *) 0 && strncmp(mline, "Path: ", 6) == 0) {
X			if (to[0] != '\0')
X				strcat(to, ", ");
X			strcat(to, &mline[6]);
X			continue;
X		}
X		if (to != (char *) 0 && strncmp(mline, "To: ", 4) == 0) {
X			if (to[0] != '\0')
X				strcat(to, ", ");
X			strcat(to, &mline[4]);
X			continue;
X		}
X		if (subj != (char *) 0 && strncmp(mline, "Subject: ", 9) == 0) {
X			strcpy(subj, &mline[9]);
X			continue;
X		}
X		if (cc != (char *) 0 && strncmp(mline, "Cc: ", 4) == 0) {
X			if (cc[0] != '\0')
X				strcat(cc, ", ");
X			strcat(cc, &mline[4]);
X			continue;
X		}
X	}
X	fclose(fp);
X}
X
Xchar *mdate() {
X	long now;
X	struct tm *today;
X	static char dbuf[80];
X	int hr;
X	char ap;
X#ifdef USG
X	extern int daylight;
X	extern char *tzname[];
X#endif USG
X	
X	time(&now);
X	today = localtime(&now);
X	if (today->tm_hour < 12) {
X		ap = 'A';
X		hr = today->tm_hour;
X	}
X	else {
X		ap = 'P';
X		hr = today->tm_hour - 12;
X	}
X	if (hr == 0)
X		hr = 12;
X	sprintf(dbuf, "%s, %s %d, 19%d at %d:%02d %c.M.", _weekday_[today->tm_wday], _month_[today->tm_mon], today->tm_mday, today->tm_year, hr, today->tm_min, ap);
X#ifdef USG
X	tzset();
X	strcat(dbuf, " ");
X	strcat(dbuf, tzname[daylight]);
X#endif USG
X	return dbuf;
X}
X
Xsuresend() {
X	char ch;
X	struct TERMIO ottyp, nttyp;
X	int (*intr)(), (*quit)();
X	
X	if (ioctl(0, TIO_GET, &ottyp) < 0)
X		return 1;
X	intr = signal(SIGINT, SIG_IGN);
X	quit = signal(SIGQUIT, SIG_IGN);
X	nttyp = ottyp;
X#ifdef USG
X	nttyp.c_lflag &= ~(ICANON|ECHO|ECHOE);
X	nttyp.c_cc[VMIN] = 1;
X	nttyp.c_cc[VTIME] = 0;
X#else  USG
X	nttyp.sg_flags |= CBREAK;
X	nttyp.sg_flags &= ~ECHO;
X#endif USG
X	ioctl(0, TIO_SET, &nttyp);
X	printf("Are you sure you want to send this (Y)? ");
X	while ((ch = getchar()) == EOF || (ch < ' ' && ch != '\r' && ch != '\n' && ch != '\t') || ch > '~')
X		;
X	if (ch == 'N' || ch == 'n' || ch == '\t')
X		puts("No");
X	else
X		puts("Yes");
X	ioctl(0, TIO_SET, &ottyp);
X	signal(SIGINT, intr);
X	signal(SIGQUIT, quit);
X	return ch != 'N' && ch != 'n' && ch != '\t';
X}
X
Xgetcont() {
X	struct TERMIO ottyp, nttyp;
X	int (*intr)(), (*quit)();
X	
X	intr = signal(SIGINT, SIG_IGN);
X	quit = signal(SIGQUIT, SIG_IGN);
X	if (ioctl(0, TIO_GET, &ottyp) < 0)
X		return;
X	nttyp = ottyp;
X#ifdef USG
X	nttyp.c_lflag &= ~(ICANON|ECHO|ECHOE);
X	nttyp.c_cc[VMIN] = 1;
X	nttyp.c_cc[VTIME] = 0;
X#else  USG
X	nttyp.sg_flags |= CBREAK;
X	nttyp.sg_flags &= ~ECHO;
X#endif USG
X	ioctl(0, TIO_SET, &nttyp);
X	printf("--- Press any key to continue ---");
X	getchar();
X	printf("\r                                 \r");
X	ioctl(0, TIO_SET, &ottyp);
X	signal(SIGINT, intr);
X	signal(SIGQUIT, quit);
X}
--EOF:imssend.c--
fi

if test -r "imsset.c"; then
	echo "File imsset.c exists.  Enter new name or RETURN to skip.  (. to replace.)"
	read newname
	case "$newname" in
	".")	newname="imsset.c"
	esac
else
	newname="imsset.c"
fi
if test -z "$newname"; then
	echo "shx - $newname (skipped)"
else
	case "$newname" in
	"$sfile")
		echo "shx - $sfile (as $newname)"
		;;
	*)	echo "shx - $newname"
	esac
	sed 's/^X//' << '--EOF:imsset.c--' > "$newname"
X#include "ims.h"
X
Xstruct var {
X	char *v_name;
X	char *v_val;
X} var[] = {
X	"folder",			folder,
X	"cabinet",			cabinet,
X	"prompt",			prompt,
X	"pager",			pager,
X	"system-mailbox",		sysmbox,
X	"mail-sending-command",		sender,
X	"mailbox",			mailbox,
X	"editor",			editor,
X	"save-folder",			savefolder,
X	"print-command",		printcmd,
X	"type-next-automatically",	autonext,
X	"read-new-mail-automatically",	autoread,
X	"ask-before-append",		askappend,
X	"lines",			lines,
X	"edit-forwarded-mail",		edforward,
X	"alias-recursion-level",	alicount,
X	(char *) 0,			(char *) 0,
X};
X
Xreadvars() {
X	char vfile[256];
X	char *cp;
X	
X	varead(IMSINIT);
X	if ((cp = getenv("IMSINIT")) != (char *) 0)
X		strcpy(vfile, cp);
X	else {
X		if ((cp = getenv("HOME")) == (char *) 0)
X			cp = "/usr/guest";
X		sprintf(vfile, "%s/.imsinit", cp);
X	}
X	varead(vfile);
X}
X
Xvaread(vfile)
Xchar *vfile; {
X	char vline[512], v[80], val[128];
X	char *cp, *dp, *ip;
X	FILE *fp;
X	int lineno, canon;
X
X	if ((fp = efopen(vfile, "r")) == (FILE *) 0)
X		return;
X	lineno = 0;
X	while (fgets(vline, sizeof vline, fp) != (char *) 0) {
X		lineno++;
X		if ((cp = strchr(vline, '\n')) != (char *) 0)
X			*cp = '\0';
X		if ((cp = strchr(vline, '#')) != (char *) 0)
X			*cp = '\0';
X		for (cp = vline; *cp == ' ' || *cp == '\t'; cp++)
X			;
X		if (*cp == '\0')
X			continue;
X		for (dp = v; *cp != '\0' && strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01235456789_-", *cp) != (char *) 0; cp++)
X			*dp++ = *cp;
X		*dp = '\0';
X		if (v[0] == '\0') {
X			printf("Error in \"%s\", line %d:  Missing or invalid variable name\n", vfile, lineno);
X			continue;
X		}
X		while (*cp == ' ' || *cp == '\t')
X			cp++;
X		if (*cp != '=') {
X			printf("Error in \"%s\", line %d:  Missing ``=''\n", vfile, lineno);
X			continue;
X		}
X		cp++;
X		while (*cp == ' ' || *cp == '\t')
X			cp++;
X		canon = 0;
X		if (*cp == '"') {
X			canon = 1;
X			cp++;
X		}
X		for (dp = val; *cp != '\0' && (canon? *cp != '"': *cp != ' ' && *cp != '\t'); cp++) {
X			if (*cp == '\\' && *(cp + 1) != '\0')
X				*dp++ = *++cp;
X			else if (*cp != '\\')
X				*dp++ = *cp;
X			else {
X				*dp++ = '\n';
X				if (fgets(vline, sizeof vline, fp) == (char *) 0) {
X					cp++;
X					printf("Unexpected end of file in \"%s\", line %d\n", vfile, lineno + 1);
X					break;
X				}
X				lineno++;
X				if ((ip = strchr(vline, '\n')) != (char *) 0)
X					*ip = '\0';
X				if ((ip = strchr(vline, '#')) != (char *) 0)
X					*ip = '\0';
X				cp = vline;
X				*dp++ = *cp;
X			}
X		}
X		if (canon) {
X			if (*cp == '\0') {
X				printf("Error in \"%s\", line %d:  No closing quote\n", vfile, lineno);
X				continue;
X			}
X			cp++;
X		}
X		*dp = '\0';
X		while (*cp == ' ' || *cp == '\t')
X			cp++;
X		if (*cp != '\0') {
X			printf("Error in \"%s\", line %d:  Text after set command\n", vfile, lineno);
X			continue;
X		}
X		setvar(v, val);
X	}
X	fclose(fp);
X}
X
Xsetvar(v, val)
Xchar *v, *val; {
X	struct var *vp;
X	
X	for (vp = var; vp->v_name != (char *) 0; vp++)
X		if (strcmp(vp->v_name, v) == 0)
X			break;
X	if (vp->v_name == (char *) 0) {
X		printf("Unknown variable: %s\n", v);
X		return -1;
X	}
X	strcpy(vp->v_val, val);
X	return 0;
X}
X
Xvarops(cmdp)
Xchar *cmdp; {
X	char *cp, *dp;
X	char v[80], val[128];
X	int canon;
X	
X	while (*cmdp == ' ' || *cmdp == '\t')
X		;
X	if (*cmdp == '\0') {
X		listvar("");
X		return 1;
X	}
X	for (dp = v, cp = cmdp; *cp != '\0' && strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01235456789_-", *cp) != (char *) 0; cp++)
X		*dp++ = *cp;
X	*dp = '\0';
X	while (*cp == ' ' || *cp == '\t')
X		cp++;
X	if (*cp == '\0') {
X		listvar(v);
X		return 1;
X	}
X	if (*cp != '=') {
X		puts("Invalid assignment statement syntax.  Usage:  set [variable [= value]]");
X		return -1;
X	}
X	cp++;
X	while (*cp == ' ' || *cp == '\t')
X		cp++;
X	canon = 0;
X	if (*cp == '"') {
X		canon = 1;
X		cp++;
X	}
X	for (dp = val; *cp != '\0' && (canon? *cp != '"': *cp != ' ' && *cp != '\t'); cp++) {
X		if (*cp == '\\' && *(cp + 1) != '\0')
X			*dp++ = *++cp;
X		else if (*cp != '\\')
X			*dp++ = *cp;
X		else {
X			puts("Multiline variables invalid on command line.");
X			return -1;
X		}
X	}
X	if (canon) {
X		if (*cp == '\0') {
X			printf("Missing closing quote.");
X			return -1;
X		}
X		cp++;
X	}
X	*dp = '\0';
X	while (*cp == ' ' || *cp == '\t')
X		cp++;
X	if (*cp != '\0') {
X		printf("Extra text after assignment.");
X		return -1;
X	}
X	setvar(v, val);
X	return 1;
X}
X
Xlistvar(v)
Xchar *v; {
X	struct var *vp;
X	int cnt;
X	
X	cnt = 0;
X	for (vp = var; vp->v_name != (char *) 0; vp++)
X		if (v[0] == '\0' || strcmp(vp->v_name, v) == 0) {
X			printf("%s => \"%s\"\n", vp->v_name, vp->v_val);
X			cnt++;
X		}
X	if (cnt == 0)
X		if (v[0] == '\0')
X			puts("There are no variables in this version of IMS.");
X		else
X			printf("Unknown variable: \"%s\".\n", v);
X}
--EOF:imsset.c--
fi

if test -r "imsalias.c"; then
	echo "File imsalias.c exists.  Enter new name or RETURN to skip.  (. to replace.)"
	read newname
	case "$newname" in
	".")	newname="imsalias.c"
	esac
else
	newname="imsalias.c"
fi
if test -z "$newname"; then
	echo "shx - $newname (skipped)"
else
	case "$newname" in
	"$sfile")
		echo "shx - $sfile (as $newname)"
		;;
	*)	echo "shx - $newname"
	esac
	sed 's/^X//' << '--EOF:imsalias.c--' > "$newname"
X#include "ims.h"
X
Xstruct alias {
X	char *a_name;
X	char *a_value;
X	struct alias *a_next;
X} *alias = (struct alias *) 0;
X
Xreadalias() {
X	char vfile[256];
X	char *cp;
X	
X	aliread(IMSALIAS);
X	if ((cp = getenv("IMSALIAS")) != (char *) 0)
X		strcpy(vfile, cp);
X	else {
X		if ((cp = getenv("HOME")) == (char *) 0)
X			cp = "/usr/guest";
X		sprintf(vfile, "%s/.imsalias", cp);
X	}
X	aliread(vfile);
X}
X
Xaliread(vfile)
Xchar *vfile; {
X	char vline[1024], v[80], val[1024];
X	char *cp, *dp, *ip;
X	FILE *fp;
X	int lineno;
X	
X	if ((fp = efopen(vfile, "r")) == (FILE *) 0)
X		return;
X	lineno = 0;
X	while (fgets(vline, sizeof vline, fp) != (char *) 0) {
X		lineno++;
X		if ((cp = strchr(vline, '\n')) != (char *) 0)
X			*cp = '\0';
X		if ((cp = strchr(vline, '#')) != (char *) 0)
X			*cp = '\0';
X		for (cp = vline; *cp == ' ' || *cp == '\t'; cp++)
X			;
X		if (*cp == '\0')
X			continue;
X		for (dp = v; *cp != '\0' && strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01235456789_-", *cp) != (char *) 0; cp++)
X			*dp++ = *cp;
X		*dp = '\0';
X		if (v[0] == '\0') {
X			printf("Error in \"%s\", line %d:  Missing or invalid alias name\n", vfile, lineno);
X			continue;
X		}
X		while (*cp == ' ' || *cp == '\t')
X			cp++;
X		if (*cp != ':') {
X			printf("Error in \"%s\", line %d:  Missing ``:''\n", vfile, lineno);
X			continue;
X		}
X		cp++;
X		while (*cp == ' ' || *cp == '\t')
X			cp++;
X		for (dp = val; *cp != '\0'; cp++) {
X			if (*cp == '\\' && *(cp + 1) != '\0')
X				*dp++ = *++cp;
X			else if (*cp != '\\')
X				*dp++ = *cp;
X			else {
X				*dp++ = '\n';
X				if (fgets(vline, sizeof vline, fp) == (char *) 0) {
X					cp++;
X					printf("Unexpected end of file in \"%s\", line %d\n", vfile, lineno + 1);
X					break;
X				}
X				lineno++;
X				if ((ip = strchr(vline, '\n')) != (char *) 0)
X					*ip = '\0';
X				if ((ip = strchr(vline, '#')) != (char *) 0)
X					*ip = '\0';
X				cp = vline;
X				*dp++ = *cp;
X			}
X		}
X		setalias(v, val);
X	}
X	fclose(fp);
X}
X
Xsetalias(ali, value)
Xchar *ali, *value; {
X	struct alias *ap, *newa;
X	
X	for (ap = alias; ap->a_name != (char *) 0; ap = ap->a_next)
X		if (strcmp(ap->a_name, ali) == 0)
X			break;
X	if (ap->a_name == (char *) 0) {
X		if (value == (char *) 0 || value[0] == '\0') {
X			printf("No such alias: %s.\n", ali);
X			return -1;
X		}
X		if ((newa = (struct alias *) calloc(1, sizeof *newa)) == (struct alias *) 0) {
X			fprintf(stderr, "No room for new alias: %s.\n", ali);
X			return -1;
X		}
X		if ((newa->a_name = calloc(strlen(ali) + 1, sizeof *newa->a_name)) == (char *) 0) {
X			fprintf(stderr, "No room for new alias name: %s.\n", ali);
X			free((char *) newa);
X			return -1;
X		}
X		if ((newa->a_value = calloc(strlen(value) + 1, sizeof *newa->a_value)) == (char *) 0) {
X			fprintf(stderr, "No room for new alias value: %s.\n", ali);
X			free(newa->a_name);
X			free((char *) newa);
X			return -1;
X		}
X		strcpy(newa->a_name, ali);
X		strcpy(newa->a_value, value);
X		newa->a_next = alias;
X		alias = newa;
X		return 2;
X	}
X	free(ap->a_value);
X	if (value == (char *) 0 || value[0] == '\0') {
X		free(ap->a_name);
X		for (newa = alias; newa->a_next != (struct alias *) 0; newa = ap->a_next)
X			if (newa->a_next == ap)
X				break;
X		if (newa == (struct alias *) 0)
X			alias = ap->a_next;
X		else
X			newa->a_next = ap->a_next;
X		free((char *) ap);
X		return 0;
X	}
X	if ((ap->a_value = calloc(strlen(value), sizeof *ap->a_value)) == (char *) 0) {
X		fprintf(stderr, "No space for new value of alias: %s.  Alias deleted.\n", ali);
X		for (newa = alias; newa->a_next != (struct alias *) 0; newa = newa->a_next)
X			if (newa->a_next == ap)
X				break;
X		if (newa == (struct alias *) 0)
X			alias = ap->a_next;
X		else
X			newa->a_next = ap->a_next;
X		free(ap->a_name);
X		free((char *) ap);
X		return -1;
X	}
X	strcpy(ap->a_value, value);
X	return 1;
X}
X
Xxalias(to, buf)
Xchar *to, *buf; {
X	struct alias *ap;
X	
X	for (ap = alias; ap != (struct alias *) 0; ap = ap->a_next)
X		if (strcmp(ap->a_name, to) == 0)
X			break;
X	if (ap == (struct alias *) 0) {
X		strcpy(buf, to);
X		return 0;
X	}
X	strcpy(buf, ap->a_value);
X	return 1;
X}
X
Xunalias(cmdp)
Xchar *cmdp; {
X	char *cp;
X	int didalias;
X	
X	didalias = 0;
X	
X	while (*cmdp != '\0') {
X		while (*cmdp == ' ' || *cmdp == '\t')
X			cmdp++;
X		if (*cmdp == '\0')
X			break;
X		for (cp = cmdp; *cp != ',' && *cp != ' ' && *cp != '\t'; cp++)
X			;
X		if (*cp != '\0')
X			*cp++ = '\0';
X		didalias++;
X		setalias(cmdp, "");
X		cmdp = cp;
X	}
X	if (didalias == 0) {
X		puts("Usage:  unalias alias-list\n");
X		return -1;
X	}
X	return 1;
X}
X
Xaliasops(cmdp)
Xchar *cmdp; {
X	char *cp, *dp;
X	char aexp[1024];
X	
X	while (*cmdp == ' ' || *cmdp == '\t')
X		cmdp++;
X	if (*cmdp == '\0')
X		return listalias();
X	for (cp = cmdp; *cp != '\0' && *cp != '\t' && *cp != ' '; cp++)
X		;
X	if (*cp != '\0')
X		*cp++ = '\0';
X	for (dp = cmdp; *dp != '\0' && strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01235456789_-", *dp) != (char *) 0; dp++)
X		;
X	if (*dp != '\0') {
X		printf("Illegal alias name: %s.\n", cmdp);
X		return -1;
X	}
X	while (*cp == ' ' || *cp == '\t')
X		cp++;
X	if (*cp == '\0')
X		if (xalias(cmdp, aexp)) {
X			printf("Alias %s: %s\n", cmdp, aexp);
X			return 1;
X		}
X		else {
X			printf("Unknown alias: %s.\n", cmdp);
X			return -1;
X		}
X	if (setalias(cmdp, cp) < 0)
X		return -1;
X	return 1;
X}
X
Xlistalias() {
X	struct alias *ap;
X	int nalias;
X	
X	nalias = 0;
X	for (ap = alias; ap != (struct alias *) 0; ap = ap->a_next, nalias++)
X		printf("  %s: %s\n", ap->a_name, ap->a_value);
X	if (nalias != 0)
X		return 1;
X	puts("No aliases defined.");
X	return -1;
X}
--EOF:imsalias.c--
fi
exit 0
-- 
  ---------------- ****	Brandon S. Allbery		UUCP:
 /              / ****	Tridelta Industries, Inc.       decvax!cwruecmp!ncoast!
----    -------- ****	7350 Corporate Blvd.		    tdi2!brandon
   /   / /---,  /--/	Mentor, Ohio 44060		PHONE:  (home)
  /   / /    / /  /	     -- HOME --			+1 216 974 9210
 /   / /    / /  /	6615 Center St. Apt. A1-105	ARPA:  ncoast!allbery%
----  /----~ /--/	Mentor, Ohio 44060-4101		case.CSNET@csnet-relay
-------------------------------------------------------------------------------
			Space -- The Final Frontier

allbery@ncoast.UUCP (Brandon S. Allbery) (07/22/86)

#! /bin/sh
if test -r "ndir.c"; then
	echo "File ndir.c exists.  Enter new name or RETURN to skip.  (. to replace.)"
	read newname
	case "$newname" in
	".")	newname="ndir.c"
	esac
else
	newname="ndir.c"
fi
if test -z "$newname"; then
	echo "shx - $newname (skipped)"
else
	case "$newname" in
	"$sfile")
		echo "shx - $sfile (as $newname)"
		;;
	*)	echo "shx - $newname"
	esac
	sed 's/^X//' << '--EOF:ndir.c--' > "$newname"
X#include <sys/types.h>
X#include "ndir.h"
X
X#ifndef BSD
X
X/*
X * close a directory.
X */
Xclosedir(dirp)
X        register DIR *dirp;
X{
X        close(dirp->dd_fd);
X        dirp->dd_fd = -1;
X        dirp->dd_loc = 0;
X        free(dirp);
X}
X
X
X
X/*
X * open a directory.
X */
XDIR *
Xopendir(name)
X        char *name;
X{
X        register DIR *dirp;
X        register int fd;
X
X        if ((fd = eopen(name, 0)) == -1)
X                return NULL;
X        if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
X                close (fd);
X                return NULL;
X        }
X        dirp->dd_fd = fd;
X        dirp->dd_loc = 0;
X        return dirp;
X}
X
X
X
X/*
X * read an old style directory entry and present it as a new one
X */
X#define ODIRSIZ 14
X
Xstruct  olddirect {
X        ino_t   od_ino;
X        char    od_name[ODIRSIZ];
X};
X
X/*
X * get next entry in a directory.
X */
Xstruct direct *
Xreaddir(dirp)
X        register DIR *dirp;
X{
X        register struct olddirect *dp;
X        static struct direct dir;
X
X        for (;;) {
X                if (dirp->dd_loc == 0) {
X                        dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, 
X                            DIRBLKSIZ);
X                        if (dirp->dd_size <= 0)
X                                return NULL;
X                }
X                if (dirp->dd_loc >= dirp->dd_size) {
X                        dirp->dd_loc = 0;
X                        continue;
X                }
X                dp = (struct olddirect *)(dirp->dd_buf + dirp->dd_loc);
X                dirp->dd_loc += sizeof(struct olddirect);
X                if (dp->od_ino == 0)
X                        continue;
X                dir.d_ino = dp->od_ino;
X                strncpy(dir.d_name, dp->od_name, ODIRSIZ);
X                dir.d_name[ODIRSIZ] = '\0'; /* insure null termination */
X                dir.d_namlen = strlen(dir.d_name);
X                dir.d_reclen = DIRBLKSIZ;
X                return (&dir);
X        }
X}
X
X#endif BSD
--EOF:ndir.c--
fi

if test -r "eopen.c"; then
	echo "File eopen.c exists.  Enter new name or RETURN to skip.  (. to replace.)"
	read newname
	case "$newname" in
	".")	newname="eopen.c"
	esac
else
	newname="eopen.c"
fi
if test -z "$newname"; then
	echo "shx - $newname (skipped)"
else
	case "$newname" in
	"$sfile")
		echo "shx - $sfile (as $newname)"
		;;
	*)	echo "shx - $newname"
	esac
	sed 's/^X//' << '--EOF:eopen.c--' > "$newname"
X#include <stdio.h>
X#include <errno.h>
X
Xextern int errno;
Xstatic int __pop_pid = -1;
X
XFILE *efopen(file, mode)
Xchar *file, *mode; {
X	int alevel, chmg;
X	FILE *fp;
X	
X	switch (mode[0]) {
X		case 'r':
X			alevel = 4;
X			break;
X		case 'w':
X		case 'a':
X			alevel = 2;
X			break;
X	}
X	if (mode[1] == '+')
X		alevel = 6;
X	chmg = 0;
X	if (access(file, alevel) < 0)
X		if (errno != ENOENT)
X			return (FILE *) 0;
X		else
X			chmg = 1;
X	if ((fp = fopen(file, mode)) == (FILE *) 0)
X		return (FILE *) 0;
X	if (chmg)
X		chown(file, getuid(), getgid());
X	return fp;
X}
X
Xeopen(file, mode)
Xchar *file; {
X	int alevel, fd, chmg;
X	
X	switch (mode & 3) {
X		case 0:
X			alevel = 4;
X			break;
X		case 1:
X			alevel = 2;
X			break;
X		case 2:
X			alevel = 6;
X			break;
X	}
X	chmg = 0;
X	if (access(file, alevel) < 0)
X		if (errno != ENOENT)
X			return -1;
X		else
X			chmg = 1;
X	if ((fd = open(file, mode)) == -1)
X		return -1;
X	if (chmg)
X		chown(file, getuid(), getgid());
X	return fd;
X}
X		
XFILE *epopen(cmd, mode)
Xchar *mode; {
X	FILE *pfp;
X	int pfd[2];
X	int pmode;
X	
X	pmode = (*mode == 'r'? 1: 0);
X	pipe(pfd);
X	switch (__pop_pid = fork()) {
X		case -1:
X			return (FILE *) 0;
X		case 0:
X			setgid(getgid());
X			setuid(getuid());
X			close(pmode);
X			dup(pfd[pmode]);
X			close(pfd[!pmode]);
X			close(pfd[pmode]);
X			execl("/bin/sh", "sh", "-c", cmd, (char *) 0);
X			_exit(100);
X		default:
X			close(pfd[pmode]);
X			return fdopen(pfd[!pmode], mode);
X	}
X}
X
Xpclose(fp)
XFILE *fp; {
X	int status;
X
X	fclose(fp);
X	if (__pop_pid == -1)
X		return -1;
X	while (wait(&status) != __pop_pid)
X		;
X	return status;
X}
--EOF:eopen.c--
fi

if test -r "ims.h"; then
	echo "File ims.h exists.  Enter new name or RETURN to skip.  (. to replace.)"
	read newname
	case "$newname" in
	".")	newname="ims.h"
	esac
else
	newname="ims.h"
fi
if test -z "$newname"; then
	echo "shx - $newname (skipped)"
else
	case "$newname" in
	"$sfile")
		echo "shx - $sfile (as $newname)"
		;;
	*)	echo "shx - $newname"
	esac
	sed 's/^X//' << '--EOF:ims.h--' > "$newname"
X#include <stdio.h>
X#include <signal.h>
X#include <setjmp.h>
X#include <pwd.h>
X#include <time.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/ioctl.h>
X#include "ndir.h"
X
X#define IMSINIT		"/usr/lib/imsinit"
X#define IMSALIAS	"/usr/lib/imsaliases"
X
X#ifndef USG
X#define strchr		index
X#define strrchr		rindex
X#endif  USG
X
Xextern int readmsg(), printmsg(), reply(), gomsg(), mailto(), listmsg(),
X	forward(), byebye(), help(), setfolder(), readmbox(), savemsg(),
X	delmsg(), undelmsg(), nxbyebye(), expunge(), nextmsg(), prevmsg(),
X	varops(), foldlist(), aliasops(), unalias();
X
Xextern char *location();
Xextern char *strchr();
Xextern char *strrchr();
Xextern char *getenv();
Xextern FILE *efopen();
Xextern char *mlocation();
Xextern char *mflocation();
Xextern char *calloc();
Xextern char *realloc();
Xextern FILE *epopen();
Xextern char *basename();
Xextern char *getlogin();
Xextern struct passwd *getpwnam();
Xextern struct tm *localtime();
X
Xextern char folder[];
Xextern char cabinet[];
Xextern char pager[];
Xextern char sysmbox[];
Xextern char prompt[];
Xextern char sender[];
Xextern char mailbox[];
Xextern char editor[];
Xextern char printcmd[];
Xextern char savefolder[];
Xextern char autonext[];
Xextern char autoread[];
Xextern char askappend[];
Xextern char lines[];
Xextern char edforward[];
Xextern char alicount[];
Xextern int msg;
X
X#ifdef USG
X#define SYSMAILBOX	"/usr/mail/%s"
X#else
X#define SYSMAILBOX	"/usr/spool/mail/%s"
X#define strchr		index
X#define strrchr		rindex
X#endif USG
X
X#ifndef USG
X#include <sgtty.h>
X#define TERMIO		sgttyb
X#define TIO_GET		TIOCGETP
X#define TIO_SET		TIOCSETN
X#define TIO_ERASE	sg_erase
X#else   USG
X#include <termio.h>
X#define TERMIO		termio
X#define TIO_GET		TCGETA
X#define TIO_SET		TCSETAW
X#define TIO_ERASE	c_cc[VERASE]
X#endif  USG
--EOF:ims.h--
fi

if test -r "ndir.h"; then
	echo "File ndir.h exists.  Enter new name or RETURN to skip.  (. to replace.)"
	read newname
	case "$newname" in
	".")	newname="ndir.h"
	esac
else
	newname="ndir.h"
fi
if test -z "$newname"; then
	echo "shx - $newname (skipped)"
else
	case "$newname" in
	"$sfile")
		echo "shx - $sfile (as $newname)"
		;;
	*)	echo "shx - $newname"
	esac
	sed 's/^X//' << '--EOF:ndir.h--' > "$newname"
X/*	dir.h	4.4	82/07/25	*/
X
X#ifdef BSD
X#include <sys/dir.h>
X#else
X
X/*
X * A directory consists of some number of blocks of DIRBLKSIZ
X * bytes, where DIRBLKSIZ is chosen such that it can be transferred
X * to disk in a single atomic operation (e.g. 512 bytes on most machines).
X *
X * Each DIRBLKSIZ byte block contains some number of directory entry
X * structures, which are of variable length.  Each directory entry has
X * a struct direct at the front of it, containing its inode number,
X * the length of the entry, and the length of the name contained in
X * the entry.  These are followed by the name padded to a 4 byte boundary
X * with null bytes.  All names are guaranteed null terminated.
X * The maximum length of a name in a directory is MAXNAMLEN.
X *
X * The macro DIRSIZ(dp) gives the amount of space required to represent
X * a directory entry.  Free space in a directory is represented by
X * entries which have dp->d_reclen >= DIRSIZ(dp).  All DIRBLKSIZ bytes
X * in a directory block are claimed by the directory entries.  This
X * usually results in the last entry in a directory having a large
X * dp->d_reclen.  When entries are deleted from a directory, the
X * space is returned to the previous entry in the same directory
X * block by increasing its dp->d_reclen.  If the first entry of
X * a directory block is free, then its dp->d_ino is set to 0.
X * Entries other than the first in a directory do not normally have
X * dp->d_ino set to 0.
X */
X#define DIRBLKSIZ	512
X#define	MAXNAMLEN	255
X
Xstruct	direct {
X	long	d_ino;			/* inode number of entry */
X	short	d_reclen;		/* length of this record */
X	short	d_namlen;		/* length of string in d_name */
X	char	d_name[MAXNAMLEN + 1];	/* name must be no longer than this */
X};
X
X/*
X * The DIRSIZ macro gives the minimum record length which will hold
X * the directory entry.  This requires the amount of space in struct direct
X * without the d_name field, plus enough space for the name with a terminating
X * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary.
X */
X#ifdef DIRSIZ
X#undef DIRSIZ
X#endif
X#define DIRSIZ(dp) \
X    ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
X
X#ifndef KERNEL
X/*
X * Definitions for library routines operating on directories.
X */
Xtypedef struct _dirdesc {
X	int	dd_fd;
X	long	dd_loc;
X	long	dd_size;
X	char	dd_buf[DIRBLKSIZ];
X} DIR;
X#ifndef NULL
X#define NULL 0
X#endif
Xextern	DIR *opendir();
Xextern	struct direct *readdir();
Xextern	closedir();
X#endif KERNEL
X
X#endif BSD
--EOF:ndir.h--
fi

if test -r "ims.1"; then
	echo "File ims.1 exists.  Enter new name or RETURN to skip.  (. to replace.)"
	read newname
	case "$newname" in
	".")	newname="ims.1"
	esac
else
	newname="ims.1"
fi
if test -z "$newname"; then
	echo "shx - $newname (skipped)"
else
	case "$newname" in
	"$sfile")
		echo "shx - $sfile (as $newname)"
		;;
	*)	echo "shx - $newname"
	esac
	sed 's/^X//' << '--EOF:ims.1--' > "$newname"
X.TH IMS LOCAL
X.ds i \fBims\fR
X.SH NAME
X\*i \- Intelligent Message System
X.SH SYNOPSIS
X.B \*i
X[ -i ]
X.br
X.B \*i
X.I \*i-command-string
X.SH DESCRIPTION
X\*i is a mail system intended to replace 
X.BR mail (1)
Xfor regular use.  It uses
X.B mail
Xto send messages, in order to retain compatibility with other mailers.
X.PP
X\*i implements a true ``folder'' mechanism for handling mail.  A mail
Xcabinet (by default 
X.IR $HOME/.mail )
Xis established the first time the program is run, as is a standard
Xfolder (by default
X.IR incoming-mail ).
XOn entry, mail is read into files in the standard folder, unless the
X.I -i
Xoption is given or an
X.I \*i-command-string
Xis specified.  The message pointer is then set to the first new message
Xand a command prompt is issued.
X.PP
XThere are two ways to use \*i:
X.IR "interactive mode" ,
Xwhich issues a prompt until a
X.I quit
Xor
X.I xit
Xcommand is issued or a
X.I control-D
Xis typed, and
X.IR "command mode" ,
Xwhich executes the command line and then exits as if the
X.I xit
Xcommand had been issued.  The two modes are described in detail below.
X.SH SETUP
X\*i understands a number of environment variables, supports variables of
Xits own, and provides reasonable defaults for anything not specified.
XThe environment variables, \*i variables, and their meanings are described
Xin Table 1 at the end of this document.
X.PP
XEnvironment variables are specified in the usual way (see
X.IR sh (1)
Xor
X.IR csh (1)
Xfor examples).  \*i variables are set in the file specified by the
X.I IMSINIT
Xvariable, or $HOME/.imsinit if no
X.I IMSINIT
Xenvironment variable exists.  They may also be set by the
X.I set
Xcommand from within \*i.
XThere is also a system IMSINIT file,
X.IR /usr/lib/imsinit .
X.PP
XThe
X.I IMSINIT
Xfile contains assignment statements of the form:
X.sp
X.nf
X.ce
X\fIvariable\fR = \fIvalue\fR
X.fi
X.sp
XA 
X.I value
Xmay be a single word or quoted text; if quoted, the value may comprise
Xseveral lines of the
X.I IMSINIT
Xfile, but this is discouraged because few, if any, \*i commands make use
Xof multiple-line values.  The
X.I print-command
Xvariable is a possible exception.
X.SH COMMANDS
X\*i supports a large number of commands for manipulating messages and
Xfolders.  They are detailed below.
X.nr i) 5
X.de LS
X.PP
X.ns
X.in +5n+\\n(i)n
X.ll -5n
X..
X.de LI
X.sp
X.ti -\\n(i)n
X\fB\\$1\fR \-
X..
X.de L+
X.sp
X.ti -\\n(i)n
X\fB\\$1\fR
X.br
X.ns
X..
X.de LE
X.ll
X.in
X.PP
X.ns
X..
X.LS
X.LI delete
XDelete the named message, or the current message.  If
X.I type-next-automatically
Xis affirmative, a
X.I type
Xcommand is executed with no arguments afterward.
X.LI undelete
XRestore a message deleted with the
X.I delete
Xcommand.  Note that if
X.I xit
Xis used to exit \*i, deleted messages will remain deleted but will also
Xremain recoverable; an
X.I expunge
Xcommand, either explicit or via the
X.I quit
Xcommand, is required to physically delete messages.
X.LI expunge
XPhysically delete messages marked for deletion with the
X.I delete
Xcommand.  An implicit 
X.I expunge
Xis done by the
X.I quit
Xcommand.  Unless an
X.I expunge
Xis performed, deleted messages still exist and are recoverable with
X.IR undelete .
X