[BACK]Return to msgs.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / msgs

File: [local] / src / usr.bin / msgs / Attic / msgs.c (download)

Revision 1.34, Tue Oct 27 23:59:40 2009 UTC (14 years, 7 months ago) by deraadt
Branch: MAIN
CVS Tags: OPENBSD_4_8_BASE, OPENBSD_4_8, OPENBSD_4_7_BASE, OPENBSD_4_7
Changes since 1.33: +1 -15 lines

rcsid[] and sccsid[] and copyright[] are essentially unmaintained (and
unmaintainable).  these days, people use source.  these id's do not provide
any benefit, and do hurt the small install media
(the 33,000 line diff is essentially mechanical)
ok with the idea millert, ok dms

/*	$OpenBSD: msgs.c,v 1.34 2009/10/27 23:59:40 deraadt Exp $	*/
/*	$NetBSD: msgs.c,v 1.7 1995/09/28 06:57:40 tls Exp $	*/

/*-
 * Copyright (c) 1980, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * msgs - a user bulletin board program
 *
 * usage:
 *	msgs [fhlopqr] [[-]number]	to read messages
 *	msgs -s				to place messages
 *	msgs -c [-days]			to clean up the bulletin board
 *
 * prompt commands are:
 *	y	print message
 *	n	flush message, go to next message
 *	q	flush message, quit
 *	p	print message, turn on 'pipe thru more' mode
 *	P	print message, turn off 'pipe thru more' mode
 *	-	reprint last message
 *	s[-][<num>] [<filename>]	save message
 *	m[-][<num>]	mail with message in temp mbox
 *	x	exit without flushing this message
 *	<num>	print message number <num>
 */

#define OBJECT		/* will object to messages without Subjects */
#define REJECT	/* will reject messages without Subjects
			   (OBJECT must be defined also) */
#undef UNBUFFERED	/* use unbuffered output */

#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <pwd.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <term.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
#include "pathnames.h"

#define CMODE	0664		/* bounds file creation mode */
#define NO	0
#define YES	1
#define SUPERUSER	0	/* superuser uid */
#define DAEMON		1	/* daemon uid */
#define NLINES	24		/* default number of lines/crt screen */
#define NDAYS	21		/* default keep time for messages */
#define DAYS	*24*60*60	/* seconds/day */
#define MSGSRC	".msgsrc"	/* user's rc file */
#define BOUNDS	"bounds"	/* message bounds file */
#define NEXT	"Next message? [yq]"
#define MORE	"More? [ynq]"
#define NOMORE	"(No more) [q] ?"

typedef	char	bool;

FILE	*msgsrc;
FILE	*newmsg;
char	*sep = "-";
char	inbuf[BUFSIZ];
char	fname[MAXPATHLEN];
char	cmdbuf[MAXPATHLEN + MAXPATHLEN];
char	subj[128];
char	from[128];
char	date[128];
char	*ptr;
char	*in;
bool	local;
bool	ruptible;
bool	totty;
bool	seenfrom;
bool	seensubj;
bool	blankline;
bool	printing = NO;
bool	mailing = NO;
bool	quitit = NO;
bool	sending = NO;
bool	restricted = NO;
int	uid;
int	msg;
int	prevmsg;
int	lct;
int	nlines;
int	Lpp = 0;
time_t	t;
time_t	keep;
volatile sig_atomic_t	intrpflg = 0;

void prmesg(int);
void onintr(int);
void onsusp(int);
int linecnt(FILE *);
int next(char *, int);
void ask(char *);
void gfrsub(FILE *);
char *nxtfld(char *);

/* option initialization */
bool	hdrs = NO;
bool	qopt = NO;
bool	hush = NO;
bool	send_msg = NO;
bool	locomode = NO;
bool	use_pager = NO;
bool	clean = NO;
bool	lastcmd = NO;
jmp_buf	tstpbuf;

int
main(int argc, char *argv[])
{
	bool newrc, already;
	int rcfirst = 0;		/* first message to print (from .rc) */
	int rcback = 0;			/* amount to back off of rcfirst */
	int firstmsg, nextmsg, lastmsg = 0;
	int blast = 0;
	FILE *bounds;
	char *cp;

#ifdef UNBUFFERED
	setbuf(stdout, NULL);
#endif

	time(&t);
	uid = getuid();
	if (setresuid(uid, uid, uid) == -1) {
		perror("setresuid");
		exit(1);
	}
	ruptible = (signal(SIGINT, SIG_IGN) == SIG_DFL);
	if (ruptible)
		signal(SIGINT, SIG_DFL);

	argc--, argv++;
	while (argc > 0) {
		if (isdigit(argv[0][0])) {	/* starting message # */
			rcfirst = atoi(argv[0]);
		} else if (isdigit(argv[0][1])) {	/* backward offset */
			rcback = atoi(&(argv[0][1]));
		} else {
			ptr = *argv;
			while (*ptr) {
				switch (*ptr++) {
				case '-':
					break;
				case 'c':
					if (uid != SUPERUSER && uid != DAEMON) {
						fprintf(stderr, "Sorry\n");
						exit(1);
					}
					clean = YES;
					break;
				case 'f':	/* silently */
					hush = YES;
					break;
				case 'h':	/* headers only */
					hdrs = YES;
					break;
				case 'l':	/* local msgs only */
					locomode = YES;
					break;
				case 'o':	/* option to save last message */
					lastcmd = YES;
					break;
				case 'p':	/* pipe thru 'more' during long msgs */
					use_pager = YES;
					break;
				case 'q':	/* query only */
					qopt = YES;
					break;
				case 'r':	/* restricted */
					restricted = YES;
					break;
				case 's':	/* sending TO msgs */
					send_msg = YES;
					break;
				default:
					fprintf(stderr,
					    "usage: msgs [fhlopqr] [[-]number]\n"
					    "       msgs [-s]\n"
					    "       msgs [-c [-days]]\n");
					exit(1);
				}
			}
		}
		argc--, argv++;
	}

	/*
	 * determine current message bounds
	 */
	snprintf(fname, sizeof(fname), "%s/%s", _PATH_MSGS, BOUNDS);
	bounds = fopen(fname, "r");

	if (bounds == NULL) {
		if (errno == ENOENT) {
			if ((bounds = fopen(fname, "w+")) == NULL) {
				perror(fname);
				exit(1);
			}
			fprintf(bounds, "1 0\n");
			rewind(bounds);
		} else {
			perror(fname);
			exit(1);
		}
	}

	fscanf(bounds, "%d %d\n", &firstmsg, &lastmsg);
	fclose(bounds);
	blast = lastmsg;	/* save upper bound */

	if (clean)
		keep = t - (rcback? rcback : NDAYS) DAYS;

	if (clean || bounds == NULL) {	/* relocate message bounds */
		struct dirent *dp;
		struct stat stbuf;
		bool seenany = NO;
		DIR	*dirp;

		dirp = opendir(_PATH_MSGS);
		if (dirp == NULL) {
			perror(_PATH_MSGS);
			exit(errno);
		}
		chmod(fname, CMODE);

		firstmsg = 32767;
		lastmsg = 0;

		for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)){
			int i = 0;

			cp = dp->d_name;
			if (dp->d_ino == 0)
				continue;
			if (dp->d_namlen == 0)
				continue;

			if (clean)
				snprintf(inbuf, sizeof(inbuf), "%s/%s",
				    _PATH_MSGS, cp);

			while (isdigit(*cp))
				i = i * 10 + *cp++ - '0';
			if (*cp)
				continue;	/* not a message! */

			if (clean) {
				if (stat(inbuf, &stbuf) != 0)
					continue;
				if (stbuf.st_mtime < keep &&
				    stbuf.st_mode&S_IWRITE) {
					unlink(inbuf);
					continue;
				}
			}

			if (i > lastmsg)
				lastmsg = i;
			if (i < firstmsg)
				firstmsg = i;
			seenany = YES;
		}
		closedir(dirp);

		if (!seenany) {
			if (blast != 0)	/* never lower the upper bound! */
				lastmsg = blast;
			firstmsg = lastmsg + 1;
		} else if (blast > lastmsg)
			lastmsg = blast;

		if (!send_msg) {
			bounds = fopen(fname, "w");
			if (bounds == NULL) {
				perror(fname);
				exit(errno);
			}
			chmod(fname, CMODE);
			fprintf(bounds, "%d %d\n", firstmsg, lastmsg);
			fclose(bounds);
		}
	}

	if (send_msg) {
		/*
		 * Send mode - place msgs in _PATH_MSGS
		 */
		bounds = fopen(fname, "w");
		if (bounds == NULL) {
			perror(fname);
			exit(errno);
		}

		nextmsg = lastmsg + 1;
		snprintf(fname, sizeof(fname), "%s/%d", _PATH_MSGS, nextmsg);
		newmsg = fopen(fname, "w");
		if (newmsg == NULL) {
			perror(fname);
			exit(errno);
		}
		chmod(fname, 0644);

		fprintf(bounds, "%d %d\n", firstmsg, nextmsg);
		fclose(bounds);

		sending = YES;
		if (ruptible)
			signal(SIGINT, onintr);

		if (isatty(fileno(stdin))) {
			struct passwd *pw;

			if ((pw = getpwuid(uid)) == NULL) {
				perror("getpwuid");
				exit(1);
			}
			printf("Message %d:\nFrom %s %sSubject: ",
			    nextmsg, pw->pw_name, ctime(&t));
			fflush(stdout);
			if (fgets(inbuf, sizeof inbuf, stdin) == NULL)
				errx(1, "could not read input");
			putchar('\n');
			fflush(stdout);
			fprintf(newmsg, "From %s %sSubject: %s\n",
			    pw->pw_name, ctime(&t), inbuf);
			blankline = seensubj = YES;
		} else
			blankline = seensubj = NO;
		for (;;) {
			if (fgets(inbuf, sizeof inbuf, stdin) == NULL)
				break;
			blankline = (blankline || (inbuf[0] == '\n'));
			seensubj = (seensubj ||
			    (!blankline && (strncmp(inbuf, "Subj", 4) == 0)));
			fputs(inbuf, newmsg);
		}
#ifdef OBJECT
		if (!seensubj) {
			printf("NOTICE: Messages should have a Subject field!\n");
#ifdef REJECT
			unlink(fname);
#endif
			exit(1);
		}
#endif
		exit(ferror(stdin));
	}
	if (clean)
		exit(0);

	/*
	 * prepare to display messages
	 */
	totty = (isatty(fileno(stdout)) != 0);
	use_pager = use_pager && totty;

	if ((cp = getenv("HOME")) == NULL || *cp == '\0') {
		fprintf(stderr, "Error, no home directory!\n");
		exit(1);
	}
	snprintf(fname, sizeof(fname), "%s/%s", cp, MSGSRC);
	msgsrc = fopen(fname, "r");
	if (msgsrc) {
		newrc = NO;
		fscanf(msgsrc, "%d\n", &nextmsg);
		fclose(msgsrc);
		if (nextmsg > lastmsg+1) {
			printf("Warning: bounds have been reset (%d, %d)\n",
			    firstmsg, lastmsg);
			truncate(fname, (off_t)0);
			newrc = YES;
		} else if (!rcfirst)
			rcfirst = nextmsg - rcback;
	} else
		newrc = YES;
	msgsrc = fopen(fname, "r+");
	if (msgsrc == NULL)
		msgsrc = fopen(fname, "w");
	if (msgsrc == NULL) {
		perror(fname);
		exit(errno);
	}
	if (rcfirst) {
		if (rcfirst > lastmsg+1) {
			printf("Warning: the last message is number %d.\n",
			    lastmsg);
			rcfirst = nextmsg;
		}
		if (rcfirst > firstmsg)
			firstmsg = rcfirst;	/* don't set below first msg */
	}
	if (newrc) {
		nextmsg = firstmsg;
		fseeko(msgsrc, (off_t)0, SEEK_SET);
		fprintf(msgsrc, "%d\n", nextmsg);
		fflush(msgsrc);
	}

	if (totty) {
		struct winsize win;
		if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1)
			Lpp = win.ws_row;
		if (Lpp <= 0) {
			char *ttype = getenv("TERM");

			if (ttype != (char *)NULL) {
				if (tgetent(NULL, ttype) <= 0
				    || (Lpp = tgetnum("li")) <= 0) {
					Lpp = NLINES;
				}
			} else
				Lpp = NLINES;
		}
	}
	Lpp -= 6;	/* for headers, etc. */

	already = NO;
	prevmsg = firstmsg;
	printing = YES;
	if (ruptible)
		signal(SIGINT, onintr);

	/*
	 * Main program loop
	 */
	for (msg = firstmsg; msg <= lastmsg; msg++) {

		snprintf(fname, sizeof(fname), "%s/%d", _PATH_MSGS, msg);
		newmsg = fopen(fname, "r");
		if (newmsg == NULL)
			continue;

		gfrsub(newmsg);		/* get From and Subject fields */
		if (locomode && !local) {
			fclose(newmsg);
			continue;
		}

		if (qopt) {	/* This has to be located here */
			printf("There are new messages.\n");
			exit(0);
		}

		if (already && !hdrs)
			putchar('\n');

		/*
		 * Print header
		 */
		if (totty)
			signal(SIGTSTP, onsusp);
		(void) setjmp(tstpbuf);
		already = YES;
		nlines = 2;
		if (seenfrom) {
			printf("Message %d:\nFrom %s %s", msg, from, date);
			nlines++;
		}
		if (seensubj) {
			printf("Subject: %s", subj);
			nlines++;
		} else {
			if (seenfrom) {
				putchar('\n');
				nlines++;
			}
			while (nlines < 6 &&
			    fgets(inbuf, sizeof inbuf, newmsg) &&
			    inbuf[0] != '\n') {
				fputs(inbuf, stdout);
				nlines++;
			}
		}

		lct = linecnt(newmsg);
		if (lct)
			printf("(%d%slines) ", lct, seensubj? " " : " more ");

		if (hdrs) {
			printf("\n-----\n");
			fclose(newmsg);
			continue;
		}

		/*
		 * Ask user for command
		 */
		if (totty)
			ask(lct? MORE : (msg==lastmsg? NOMORE : NEXT));
		else
			inbuf[0] = 'y';
		if (totty)
			signal(SIGTSTP, SIG_DFL);
cmnd:
		in = inbuf;
		switch (*in) {
		case 'x':
		case 'X':
			exit(0);

		case 'q':
		case 'Q':
			quitit = YES;
			printf("--Postponed--\n");
			exit(0);
			/* intentional fall-thru */
		case 'n':
		case 'N':
			if (msg >= nextmsg)
				sep = "Flushed";
			prevmsg = msg;
			break;

		case 'p':
		case 'P':
			use_pager = (*in++ == 'p');
			/* intentional fallthru */
		case '\n':
		case 'y':
		default:
			if (*in == '-') {
				msg = prevmsg-1;
				sep = "replay";
				break;
			}
			if (isdigit(*in)) {
				msg = next(in, sizeof inbuf);
				sep = in;
				break;
			}

			prmesg(nlines + lct + (seensubj? 1 : 0));
			prevmsg = msg;
		}

		printf("--%s--\n", (intrpflg ? "Interrupt" : sep));
		sep = "-";
		if (msg >= nextmsg) {
			nextmsg = msg + 1;
			fseeko(msgsrc, (off_t)0, SEEK_SET);
			fprintf(msgsrc, "%d\n", nextmsg);
			fflush(msgsrc);
		}
		if (newmsg)
			fclose(newmsg);
		if (quitit)
			break;
	}

	/*
	 * Make sure .rc file gets updated
	 */
	if (--msg >= nextmsg) {
		nextmsg = msg + 1;
		fseeko(msgsrc, (off_t)0, SEEK_SET);
		fprintf(msgsrc, "%d\n", nextmsg);
		fflush(msgsrc);
	}
	if (already && !quitit && lastcmd && totty) {
		/*
		 * save or reply to last message?
		 */
		msg = prevmsg;
		ask(NOMORE);
		if (inbuf[0] == '-' || isdigit(inbuf[0]))
			goto cmnd;
	}
	if (!(already || hush || qopt))
		printf("No new messages.\n");
	exit(0);
}

void
prmesg(int length)
{
	FILE *outf;
	char *env_pager;

	if (use_pager && length > Lpp) {
		signal(SIGPIPE, SIG_IGN);
		signal(SIGQUIT, SIG_IGN);
		if ((env_pager = getenv("PAGER")) == NULL || *env_pager == '\0') {
			snprintf(cmdbuf, sizeof(cmdbuf), _PATH_PAGER, Lpp);
		} else {
			snprintf(cmdbuf, sizeof(cmdbuf), "%s", env_pager);
		}
		outf = popen(cmdbuf, "w");
		if (!outf)
			outf = stdout;
		else
			setbuf(outf, (char *)NULL);
	}
	else
		outf = stdout;

	if (seensubj)
		putc('\n', outf);

	while (fgets(inbuf, sizeof inbuf, newmsg)) {
		fputs(inbuf, outf);
		if (ferror(outf)) {
			clearerr(outf);
			break;
		}
	}

	if (outf != stdout) {
		pclose(outf);
		signal(SIGPIPE, SIG_DFL);
		signal(SIGQUIT, SIG_DFL);
	} else {
		fflush(stdout);
	}

	/* trick to force wait on output */
	tcdrain(fileno(stdout));
}

/* ARGSUSED */
void
onintr(int signo)
{
	int save_errno = errno;

	signal(SIGINT, onintr);
	if (mailing)
		unlink(fname);
	if (sending) {
		unlink(fname);
		write(STDOUT_FILENO, "--Killed--\n", strlen("--Killed--\n"));
		_exit(1);
	}
	if (printing) {
		write(STDOUT_FILENO, "\n", 1);
		if (hdrs)
			_exit(0);
		if (newmsg)
			fseeko(newmsg, (off_t)0, SEEK_END);
		intrpflg = 1;
	}
	errno = save_errno;
}

/*
 * We have just gotten a susp.  Suspend and prepare to resume.
 */
/* ARGSUSED */
void
onsusp(int signo)
{
	int save_errno = errno;
	sigset_t emptyset;

	signal(SIGTSTP, SIG_DFL);
	sigemptyset(&emptyset);
	sigprocmask(SIG_SETMASK, &emptyset, NULL);
	kill(0, SIGTSTP);
	signal(SIGTSTP, onsusp);
	errno = save_errno;

	if (!mailing)
		longjmp(tstpbuf, 1);
}

int
linecnt(FILE *f)
{
	off_t oldpos = ftello(f);

	int l = 0;
	char lbuf[BUFSIZ];

	while (fgets(lbuf, sizeof lbuf, f))
		l++;
	clearerr(f);
	fseeko(f, oldpos, SEEK_SET);
	return (l);
}

int
next(char *buf, int len)
{
	int i;
	sscanf(buf, "%d", &i);
	snprintf(buf, len, "Goto %d", i);
	return(--i);
}

void
ask(char *prompt)
{
	char	inch;
	int	n, cmsg, fd;
	off_t	oldpos;
	FILE	*cpfrom, *cpto;

	printf("%s ", prompt);
	fflush(stdout);
	intrpflg = 0;
	if (fgets(inbuf, sizeof inbuf, stdin) == NULL)
		errx(1, "could not read input");
	inbuf[strcspn(inbuf, "\n")] = '\0';
	if (intrpflg)
		inbuf[0] = 'x';

	/*
	 * Handle 'mail' and 'save' here.
	 */
	if (((inch = inbuf[0]) == 's' || inch == 'm') && !restricted) {
		if (inbuf[1] == '-')
			cmsg = prevmsg;
		else if (isdigit(inbuf[1]))
			cmsg = atoi(&inbuf[1]);
		else
			cmsg = msg;
		snprintf(fname, sizeof(fname), "%s/%d", _PATH_MSGS, cmsg);

		oldpos = ftello(newmsg);

		cpfrom = fopen(fname, "r");
		if (!cpfrom) {
			printf("Message %d not found\n", cmsg);
			ask (prompt);
			return;
		}

		if (inch == 's') {
			in = nxtfld(inbuf);
			if (*in) {
				for (n=0;
				    in[n] > ' ' && n < sizeof fname - 1;
				    n++) {
					fname[n] = in[n];
				}
				fname[n] = NULL;
			}
			else
				strlcpy(fname, "Messages", sizeof fname);
			fd = open(fname, O_RDWR|O_EXCL|O_CREAT|O_APPEND, 0666);
		} else {
			strlcpy(fname, _PATH_TMPFILE, sizeof fname);
			fd = mkstemp(fname);
			if (fd != -1) {
				snprintf(cmdbuf, sizeof(cmdbuf), _PATH_MAIL, fname);
				mailing = YES;
			}
		}
		if (fd == -1 || (cpto = fdopen(fd, "a")) == NULL) {
			if (fd != -1)
				close(fd);
			perror(fname);
			mailing = NO;
			fseeko(newmsg, oldpos, SEEK_SET);
			ask(prompt);
			return;
		}

		while ((n = fread(inbuf, 1, sizeof inbuf, cpfrom)))
			fwrite(inbuf, 1, n, cpto);

		fclose(cpfrom);
		fclose(cpto);
		fseeko(newmsg, oldpos, SEEK_SET);	/* reposition current message */
		if (inch == 's')
			printf("Message %d saved in \"%s\"\n", cmsg, fname);
		else {
			system(cmdbuf);
			unlink(fname);
			mailing = NO;
		}
		ask(prompt);
	}
}

void
gfrsub(FILE *infile)
{
	off_t frompos;

	seensubj = seenfrom = NO;
	local = YES;
	subj[0] = from[0] = date[0] = NULL;

	/*
	 * Is this a normal message?
	 */
	if (fgets(inbuf, sizeof inbuf, infile)) {
		if (strncmp(inbuf, "From", 4)==0) {
			/*
			 * expected form starts with From
			 */
			seenfrom = YES;
			frompos = ftello(infile);
			ptr = from;
			in = nxtfld(inbuf);
			if (*in) {
				while (*in && *in > ' ' &&
				    ptr - from < sizeof from -1) {
					if (*in == ':' || *in == '@' || *in == '!')
						local = NO;
					*ptr++ = *in++;
				}
			}
			*ptr = NULL;
			if (*(in = nxtfld(in)))
				strncpy(date, in, sizeof date);
			else {
				date[0] = '\n';
				date[1] = NULL;
			}
		} else {
			/*
			 * not the expected form
			 */
			fseeko(infile, (off_t)0, SEEK_SET);
			return;
		}
	} else
		/*
		 * empty file ?
		 */
		return;

	/*
	 * look for Subject line until EOF or a blank line
	 */
	while (fgets(inbuf, sizeof inbuf, infile) &&
	    !(blankline = (inbuf[0] == '\n'))) {
		/*
		 * extract Subject line
		 */
		if (!seensubj && strncmp(inbuf, "Subj", 4)==0) {
			seensubj = YES;
			frompos = ftello(infile);
			strncpy(subj, nxtfld(inbuf), sizeof subj);
		}
	}
	if (!blankline)
		/*
		 * ran into EOF
		 */
		fseeko(infile, frompos, SEEK_SET);

	if (!seensubj)
		/*
		 * for possible use with Mail
		 */
		strncpy(subj, "(No Subject)\n", sizeof subj);
}

char *
nxtfld(char *s)
{
	if (*s)
		while (*s && *s > ' ')
			s++;	/* skip over this field */
	if (*s)
		while (*s && *s <= ' ')
			s++;	/* find start of next field */
	return (s);
}