msgs.c revision 256281
117680Spst/*-
217680Spst * Copyright (c) 1980, 1993
317680Spst *	The Regents of the University of California.  All rights reserved.
417680Spst *
517680Spst * Redistribution and use in source and binary forms, with or without
617680Spst * modification, are permitted provided that the following conditions
717680Spst * are met:
817680Spst * 1. Redistributions of source code must retain the above copyright
917680Spst *    notice, this list of conditions and the following disclaimer.
1017680Spst * 2. Redistributions in binary form must reproduce the above copyright
1117680Spst *    notice, this list of conditions and the following disclaimer in the
1217680Spst *    documentation and/or other materials provided with the distribution.
1317680Spst * 4. Neither the name of the University nor the names of its contributors
1417680Spst *    may be used to endorse or promote products derived from this software
1517680Spst *    without specific prior written permission.
1617680Spst *
1717680Spst * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1817680Spst * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1917680Spst * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2057278Sfenner * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2157278Sfenner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2217680Spst * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2317680Spst * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2417680Spst * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25127675Sbms * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26172686Smlaier * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2717680Spst * SUCH DAMAGE.
2817680Spst */
2956893Sfenner
3056893Sfenner#ifndef lint
3156893Sfennerstatic const char copyright[] =
3256893Sfenner"@(#) Copyright (c) 1980, 1993\n\
33127675Sbms	The Regents of the University of California.  All rights reserved.\n";
34146778Ssam#endif /* not lint */
3517680Spst
36146778Ssam#if 0
37146778Ssam#ifndef lint
38146778Ssamstatic char sccsid[] = "@(#)msgs.c	8.2 (Berkeley) 4/28/95";
39146778Ssam#endif /* not lint */
40146778Ssam#endif
41146778Ssam
42146778Ssam#include <sys/cdefs.h>
43146778Ssam__FBSDID("$FreeBSD: stable/10/usr.bin/msgs/msgs.c 241848 2012-10-22 03:07:05Z eadler $");
44146778Ssam
45146778Ssam/*
46146778Ssam * msgs - a user bulletin board program
47146778Ssam *
48146778Ssam * usage:
49146778Ssam *	msgs [fhlopq] [[-]number]	to read messages
50146778Ssam *	msgs -s				to place messages
51146778Ssam *	msgs -c [-days]			to clean up the bulletin board
52146778Ssam *
53146778Ssam * prompt commands are:
54146778Ssam *	y	print message
55146778Ssam *	n	flush message, go to next message
56146778Ssam *	q	flush message, quit
57146778Ssam *	p	print message, turn on 'pipe thru more' mode
58146778Ssam *	P	print message, turn off 'pipe thru more' mode
59146778Ssam *	-	reprint last message
60146778Ssam *	s[-][<num>] [<filename>]	save message
61146778Ssam *	m[-][<num>]	mail with message in temp mbox
62146778Ssam *	x	exit without flushing this message
63146778Ssam *	<num>	print message number <num>
64146778Ssam */
65146778Ssam
66146778Ssam#define V7		/* will look for TERM in the environment */
67146778Ssam#define OBJECT		/* will object to messages without Subjects */
68146778Ssam/* #define REJECT */	/* will reject messages without Subjects
69146778Ssam			   (OBJECT must be defined also) */
70146778Ssam/* #define UNBUFFERED *//* use unbuffered output */
71146778Ssam
72146778Ssam#include <sys/param.h>
73146778Ssam#include <sys/stat.h>
74146778Ssam#include <ctype.h>
75146778Ssam#include <dirent.h>
76146778Ssam#include <err.h>
77146778Ssam#include <errno.h>
78146778Ssam#include <fcntl.h>
79146778Ssam#include <locale.h>
80146778Ssam#include <pwd.h>
81146778Ssam#include <setjmp.h>
82146778Ssam#include <termcap.h>
83146778Ssam#include <termios.h>
84172686Smlaier#include <signal.h>
85172686Smlaier#include <stdio.h>
86172686Smlaier#include <stdlib.h>
87146778Ssam#include <string.h>
88172686Smlaier#include <time.h>
89172686Smlaier#include <unistd.h>
90172686Smlaier#include "pathnames.h"
91172686Smlaier
92172686Smlaier#define	CMODE	0644		/* bounds file creation	mode */
93172686Smlaier#define NO	0
9456893Sfenner#define YES	1
9556893Sfenner#define SUPERUSER	0	/* superuser uid */
9656893Sfenner#define DAEMON		1	/* daemon uid */
9756893Sfenner#define NLINES	24		/* default number of lines/crt screen */
9856893Sfenner#define NDAYS	21		/* default keep time for messages */
9956893Sfenner#define DAYS	*24*60*60	/* seconds/day */
10056893Sfenner#define MSGSRC	".msgsrc"	/* user's rc file */
10157278Sfenner#define BOUNDS	"bounds"	/* message bounds file */
10257278Sfenner#define NEXT	"Next message? [yq]"
10356893Sfenner#define MORE	"More? [ynq]"
10456893Sfenner#define NOMORE	"(No more) [q] ?"
10556893Sfenner
10656893Sfennertypedef	char	bool;
10757278Sfenner
10857278Sfennerstatic FILE	*msgsrc;
10956893Sfennerstatic FILE	*newmsg;
11056893Sfennerstatic const char *sep = "-";
11156893Sfennerstatic char	inbuf[BUFSIZ];
11256893Sfennerstatic char	fname[MAXPATHLEN];
11356893Sfennerstatic char	cmdbuf[MAXPATHLEN + MAXPATHLEN];
11417680Spststatic char	subj[128];
11517680Spststatic char	from[128];
11617680Spststatic char	date[128];
11717680Spststatic char	*ptr;
11817680Spststatic char	*in;
11956893Sfennerstatic bool	local;
12017680Spststatic bool	ruptible;
12175118Sfennerstatic bool	totty;
12275118Sfennerstatic bool	seenfrom;
12356893Sfennerstatic bool	seensubj;
12456893Sfennerstatic bool	blankline;
12556893Sfennerstatic bool	printing = NO;
12656893Sfennerstatic bool	mailing = NO;
12756893Sfennerstatic bool	quitit = NO;
12898527Sfennerstatic bool	sending = NO;
12998527Sfennerstatic bool	intrpflg = NO;
13056893Sfennerstatic uid_t	uid;
13198527Sfennerstatic int	msg;
13298527Sfennerstatic int	prevmsg;
13398527Sfennerstatic int	lct;
13498527Sfennerstatic int	nlines;
13556893Sfennerstatic int	Lpp = 0;
13698527Sfennerstatic time_t	t;
13798527Sfennerstatic time_t	keep;
13898527Sfenner
13998527Sfenner/* option initialization */
14098527Sfennerstatic bool	hdrs = NO;
14198527Sfennerstatic bool	qopt = NO;
14298527Sfennerstatic bool	hush = NO;
14398527Sfennerstatic bool	send_msg = NO;
14498527Sfennerstatic bool	locomode = NO;
14598527Sfennerstatic bool	use_pager = NO;
14698527Sfennerstatic bool	clean = NO;
14798527Sfennerstatic bool	lastcmd = NO;
14856893Sfennerstatic jmp_buf	tstpbuf;
14956893Sfenner
15056893Sfennerstatic void	ask(const char *);
15198527Sfennerstatic void	gfrsub(FILE *);
15298527Sfennerstatic int	linecnt(FILE *);
15356893Sfennerstatic int	next(char *);
154162021Ssamstatic char	*nxtfld(char *);
15598527Sfennerstatic void	onsusp(int);
15698527Sfennerstatic void	onintr(int);
15798527Sfennerstatic void	prmesg(int);
15898527Sfennerstatic void	usage(void);
15998527Sfenner
16098527Sfennerint
16198527Sfennermain(int argc, char *argv[])
16298527Sfenner{
16398527Sfenner	bool newrc, already;
16498527Sfenner	int rcfirst = 0;		/* first message to print (from .rc) */
16598527Sfenner	int rcback = 0;			/* amount to back off of rcfirst */
16698527Sfenner	int firstmsg = 0, nextmsg = 0, lastmsg = 0;
16756893Sfenner	int blast = 0;
16898527Sfenner	struct stat buf;		/* stat to check access of bounds */
16998527Sfenner	FILE *bounds;
17098527Sfenner	char *cp;
17198527Sfenner
17298527Sfenner#ifdef UNBUFFERED
17398527Sfenner	setbuf(stdout, NULL);
17498527Sfenner#endif
175147904Ssam	setlocale(LC_ALL, "");
176147904Ssam
177147904Ssam	time(&t);
178147904Ssam	if (setuid(uid = getuid()) != 0)
179162021Ssam		err(1, "setuid failed");
18098527Sfenner	ruptible = (signal(SIGINT, SIG_IGN) == SIG_DFL);
181162021Ssam	if (ruptible)
18298527Sfenner		signal(SIGINT, SIG_DFL);
18398527Sfenner
18498527Sfenner	argc--, argv++;
18598527Sfenner	while (argc > 0) {
18698527Sfenner		if (isdigit(argv[0][0])) {	/* starting message # */
18798527Sfenner			rcfirst = atoi(argv[0]);
18898527Sfenner		}
18998527Sfenner		else if (isdigit(argv[0][1])) {	/* backward offset */
19098527Sfenner			rcback = atoi( &( argv[0][1] ) );
191127675Sbms		}
19298527Sfenner		else {
19398527Sfenner			ptr = *argv;
19498527Sfenner			while (*ptr) switch (*ptr++) {
19598527Sfenner
19698527Sfenner			case '-':
19798527Sfenner				break;
19898527Sfenner
19956893Sfenner			case 'c':
20056893Sfenner				if (uid != SUPERUSER && uid != DAEMON)
20156893Sfenner					errx(1,
20298527Sfenner				"only the super-user can use the c flag");
20398527Sfenner				clean = YES;
20498527Sfenner				break;
20598527Sfenner
20656893Sfenner			case 'f':		/* silently */
20798527Sfenner				hush = YES;
20856893Sfenner				break;
20998527Sfenner
21098527Sfenner			case 'h':		/* headers only */
21156893Sfenner				hdrs = YES;
21256893Sfenner				break;
21317680Spst
21456893Sfenner			case 'l':		/* local msgs only */
21517680Spst				locomode = YES;
21698527Sfenner				break;
21798527Sfenner
21817680Spst			case 'o':		/* option to save last message */
21998527Sfenner				lastcmd = YES;
22098527Sfenner				break;
22198527Sfenner
22217680Spst			case 'p':		/* pipe thru 'more' during long msgs */
223127675Sbms				use_pager = YES;
22498527Sfenner				break;
22517680Spst
22698527Sfenner			case 'q':		/* query only */
22798527Sfenner				qopt = YES;
22898527Sfenner				break;
22998527Sfenner
23098527Sfenner			case 's':		/* sending TO msgs */
23198527Sfenner				send_msg = YES;
23298527Sfenner				break;
23356893Sfenner
23498527Sfenner			default:
23598527Sfenner				usage();
23656893Sfenner			}
23798527Sfenner		}
23898527Sfenner		argc--, argv++;
23956893Sfenner	}
24098527Sfenner
24198527Sfenner	/*
24256893Sfenner	 * determine current message bounds
24398527Sfenner	 */
24456893Sfenner	snprintf(fname, sizeof(fname), "%s/%s", _PATH_MSGS, BOUNDS);
24598527Sfenner
24698527Sfenner	/*
24798527Sfenner	 * Test access rights to the bounds file
24898527Sfenner	 * This can be a little tricky.  if(send_msg), then
24998527Sfenner	 * we will create it.  We assume that if(send_msg),
25098527Sfenner	 * then you have write permission there.
25198527Sfenner	 * Else, it better be there, or we bail.
25217680Spst	 */
25398527Sfenner	if (send_msg != YES) {
25498527Sfenner		if (stat(fname, &buf) < 0) {
25598527Sfenner			if (hush != YES) {
25698527Sfenner				err(errno, "%s", fname);
25798527Sfenner			} else {
25898527Sfenner				exit(1);
25998527Sfenner			}
26098527Sfenner		}
261162021Ssam	}
26298527Sfenner	bounds = fopen(fname, "r");
26398527Sfenner
26498527Sfenner	if (bounds != NULL) {
26598527Sfenner		fscanf(bounds, "%d %d\n", &firstmsg, &lastmsg);
26698527Sfenner		fclose(bounds);
26798527Sfenner		blast = lastmsg;	/* save upper bound */
26898527Sfenner	}
26998527Sfenner
27098527Sfenner	if (clean)
27198527Sfenner		keep = t - (rcback? rcback : NDAYS) DAYS;
27298527Sfenner
27398527Sfenner	if (clean || bounds == NULL) {	/* relocate message bounds */
27498527Sfenner		struct dirent *dp;
27556893Sfenner		struct stat stbuf;
27698527Sfenner		bool seenany = NO;
27798527Sfenner		DIR	*dirp;
27898527Sfenner
27998527Sfenner		dirp = opendir(_PATH_MSGS);
28098527Sfenner		if (dirp == NULL)
28198527Sfenner			err(errno, "%s", _PATH_MSGS);
28298527Sfenner
28398527Sfenner		firstmsg = 32767;
284162021Ssam		lastmsg = 0;
28598527Sfenner
28698527Sfenner		for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)){
28756893Sfenner			cp = dp->d_name;
28856893Sfenner			int i = 0;
28998527Sfenner
29098527Sfenner			if (dp->d_ino == 0)
29198527Sfenner				continue;
29256893Sfenner			if (dp->d_namlen == 0)
29356893Sfenner				continue;
29498527Sfenner
29598527Sfenner			if (clean)
29698527Sfenner				snprintf(inbuf, sizeof(inbuf), "%s/%s", _PATH_MSGS, cp);
29798527Sfenner
29898527Sfenner			while (isdigit(*cp))
29998527Sfenner				i = i * 10 + *cp++ - '0';
30098527Sfenner			if (*cp)
30198527Sfenner				continue;	/* not a message! */
30298527Sfenner
30398527Sfenner			if (clean) {
30498527Sfenner				if (stat(inbuf, &stbuf) != 0)
30598527Sfenner					continue;
30698527Sfenner				if (stbuf.st_mtime < keep
30798527Sfenner				    && stbuf.st_mode&S_IWRITE) {
30898527Sfenner					unlink(inbuf);
30998527Sfenner					continue;
31098527Sfenner				}
31156893Sfenner			}
31298527Sfenner
31398527Sfenner			if (i > lastmsg)
31498527Sfenner				lastmsg = i;
31517680Spst			if (i < firstmsg)
31656893Sfenner				firstmsg = i;
31798527Sfenner			seenany = YES;
31898527Sfenner		}
31917680Spst		closedir(dirp);
32056893Sfenner
32156893Sfenner		if (!seenany) {
32256893Sfenner			if (blast != 0)	/* never lower the upper bound! */
32398527Sfenner				lastmsg = blast;
32498527Sfenner			firstmsg = lastmsg + 1;
32598527Sfenner		}
32656893Sfenner		else if (blast > lastmsg)
32756893Sfenner			lastmsg = blast;
32856893Sfenner
32956893Sfenner		if (!send_msg) {
33098527Sfenner			bounds = fopen(fname, "w");
33198527Sfenner			if (bounds == NULL)
33298527Sfenner				err(errno, "%s", fname);
33356893Sfenner			chmod(fname, CMODE);
33498527Sfenner			fprintf(bounds, "%d %d\n", firstmsg, lastmsg);
33598527Sfenner			fclose(bounds);
33698527Sfenner		}
33798527Sfenner	}
33898527Sfenner
33998527Sfenner	if (send_msg) {
34098527Sfenner		/*
34198527Sfenner		 * Send mode - place msgs in _PATH_MSGS
34298527Sfenner		 */
34398527Sfenner		bounds = fopen(fname, "w");
34498527Sfenner		if (bounds == NULL)
34598527Sfenner			err(errno, "%s", fname);
34698527Sfenner
34798527Sfenner		nextmsg = lastmsg + 1;
34856893Sfenner		snprintf(fname, sizeof(fname), "%s/%d", _PATH_MSGS, nextmsg);
34998527Sfenner		newmsg = fopen(fname, "w");
35098527Sfenner		if (newmsg == NULL)
35156893Sfenner			err(errno, "%s", fname);
35298527Sfenner		chmod(fname, CMODE);
35398527Sfenner
35498527Sfenner		fprintf(bounds, "%d %d\n", firstmsg, nextmsg);
35598527Sfenner		fclose(bounds);
35698527Sfenner
35798527Sfenner		sending = YES;
35898527Sfenner		if (ruptible)
35956893Sfenner			signal(SIGINT, onintr);
36098527Sfenner
36156893Sfenner		if (isatty(fileno(stdin))) {
36298527Sfenner			ptr = getpwuid(uid)->pw_name;
36356893Sfenner			printf("Message %d:\nFrom %s %sSubject: ",
36498527Sfenner				nextmsg, ptr, ctime(&t));
36556893Sfenner			fflush(stdout);
36698527Sfenner			fgets(inbuf, sizeof inbuf, stdin);
36798527Sfenner			putchar('\n');
36898527Sfenner			fflush(stdout);
36998527Sfenner			fprintf(newmsg, "From %s %sSubject: %s\n",
37098527Sfenner				ptr, ctime(&t), inbuf);
37198527Sfenner			blankline = seensubj = YES;
37298527Sfenner		}
37398527Sfenner		else
37498527Sfenner			blankline = seensubj = NO;
37598527Sfenner		for (;;) {
37698527Sfenner			fgets(inbuf, sizeof inbuf, stdin);
37798527Sfenner			if (feof(stdin) || ferror(stdin))
37898527Sfenner				break;
37998527Sfenner			blankline = (blankline || (inbuf[0] == '\n'));
38056893Sfenner			seensubj = (seensubj || (!blankline && (strncmp(inbuf, "Subj", 4) == 0)));
38198527Sfenner			fputs(inbuf, newmsg);
38298527Sfenner		}
38398527Sfenner#ifdef OBJECT
38498527Sfenner		if (!seensubj) {
38598527Sfenner			printf("NOTICE: Messages should have a Subject field!\n");
38698527Sfenner#ifdef REJECT
38798527Sfenner			unlink(fname);
38898527Sfenner#endif
38998527Sfenner			exit(1);
39098527Sfenner		}
39198527Sfenner#endif
39298527Sfenner		exit(ferror(stdin));
39398527Sfenner	}
39498527Sfenner	if (clean)
39598527Sfenner		exit(0);
39698527Sfenner
39798527Sfenner	/*
39898527Sfenner	 * prepare to display messages
39998527Sfenner	 */
40098527Sfenner	totty = (isatty(fileno(stdout)) != 0);
40198527Sfenner	use_pager = use_pager && totty;
40298527Sfenner
40398527Sfenner	if ((cp = getenv("HOME")) == NULL || *cp == '\0') {
40498527Sfenner		fprintf(stderr, "Error, no home directory!\n");
40598527Sfenner		exit(1);
40698527Sfenner	}
40798527Sfenner	snprintf(fname, sizeof(fname), "%s/%s", cp, MSGSRC);
40898527Sfenner	msgsrc = fopen(fname, "r");
40956893Sfenner	if (msgsrc) {
41098527Sfenner		newrc = NO;
41156893Sfenner		fscanf(msgsrc, "%d\n", &nextmsg);
41256893Sfenner		fclose(msgsrc);
41398527Sfenner		if (nextmsg > lastmsg+1) {
41498527Sfenner			printf("Warning: bounds have been reset (%d, %d)\n",
41556893Sfenner				firstmsg, lastmsg);
41656893Sfenner			truncate(fname, (off_t)0);
41756893Sfenner			newrc = YES;
41856893Sfenner		}
41956893Sfenner		else if (!rcfirst)
42056893Sfenner			rcfirst = nextmsg - rcback;
42156893Sfenner	}
42256893Sfenner	else
42356893Sfenner		newrc = YES;
42456893Sfenner	msgsrc = fopen(fname, "r+");
42556893Sfenner	if (msgsrc == NULL)
42656893Sfenner		msgsrc = fopen(fname, "w");
42756893Sfenner	if (msgsrc == NULL)
42856893Sfenner		err(errno, "%s", fname);
42956893Sfenner	if (rcfirst) {
43075118Sfenner		if (rcfirst > lastmsg+1) {
431146778Ssam			printf("Warning: the last message is number %d.\n",
432146778Ssam				lastmsg);
433172686Smlaier			rcfirst = nextmsg;
434146778Ssam		}
435146778Ssam		if (rcfirst > firstmsg)
436146778Ssam			firstmsg = rcfirst;	/* don't set below first msg */
437146778Ssam	}
438146778Ssam	if (newrc) {
439172686Smlaier		nextmsg = firstmsg;
440146778Ssam		rewind(msgsrc);
441146778Ssam		fprintf(msgsrc, "%d\n", nextmsg);
442146778Ssam		fflush(msgsrc);
443146778Ssam	}
444146778Ssam
445146778Ssam#ifdef V7
44698527Sfenner	if (totty) {
447172686Smlaier		struct winsize win;
448146778Ssam		if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1)
449146778Ssam			Lpp = win.ws_row;
45056893Sfenner		if (Lpp <= 0) {
45156893Sfenner			if (tgetent(inbuf, getenv("TERM")) <= 0
45256893Sfenner			    || (Lpp = tgetnum("li")) <= 0) {
45356893Sfenner				Lpp = NLINES;
45456893Sfenner			}
45556893Sfenner		}
45656893Sfenner	}
45756893Sfenner#endif
45856893Sfenner	Lpp -= 6;	/* for headers, etc. */
45956893Sfenner
46056893Sfenner	already = NO;
46156893Sfenner	prevmsg = firstmsg;
46256893Sfenner	printing = YES;
46356893Sfenner	if (ruptible)
46456893Sfenner		signal(SIGINT, onintr);
46556893Sfenner
46656893Sfenner	/*
46756893Sfenner	 * Main program loop
46856893Sfenner	 */
46956893Sfenner	for (msg = firstmsg; msg <= lastmsg; msg++) {
47056893Sfenner
47156893Sfenner		snprintf(fname, sizeof(fname), "%s/%d", _PATH_MSGS, msg);
47256893Sfenner		newmsg = fopen(fname, "r");
47356893Sfenner		if (newmsg == NULL)
47456893Sfenner			continue;
47556893Sfenner
47656893Sfenner		gfrsub(newmsg);		/* get From and Subject fields */
47756893Sfenner		if (locomode && !local) {
47856893Sfenner			fclose(newmsg);
47956893Sfenner			continue;
48056893Sfenner		}
48156893Sfenner
48256893Sfenner		if (qopt) {	/* This has to be located here */
48356893Sfenner			printf("There are new messages.\n");
48456893Sfenner			exit(0);
48556893Sfenner		}
48656893Sfenner
48756893Sfenner		if (already && !hdrs)
48856893Sfenner			putchar('\n');
48956893Sfenner
49056893Sfenner		/*
49156893Sfenner		 * Print header
49256893Sfenner		 */
49356893Sfenner		if (totty)
49456893Sfenner			signal(SIGTSTP, onsusp);
49556893Sfenner		(void) setjmp(tstpbuf);
49656893Sfenner		already = YES;
49756893Sfenner		nlines = 2;
49856893Sfenner		if (seenfrom) {
49956893Sfenner			printf("Message %d:\nFrom %s %s", msg, from, date);
50056893Sfenner			nlines++;
50156893Sfenner		}
50256893Sfenner		if (seensubj) {
50356893Sfenner			printf("Subject: %s", subj);
50456893Sfenner			nlines++;
50556893Sfenner		}
50656893Sfenner		else {
50756893Sfenner			if (seenfrom) {
50856893Sfenner				putchar('\n');
50956893Sfenner				nlines++;
51056893Sfenner			}
51156893Sfenner			while (nlines < 6
51256893Sfenner			    && fgets(inbuf, sizeof inbuf, newmsg)
51356893Sfenner			    && inbuf[0] != '\n') {
51456893Sfenner				fputs(inbuf, stdout);
51556893Sfenner				nlines++;
51656893Sfenner			}
51756893Sfenner		}
51856893Sfenner
51956893Sfenner		lct = linecnt(newmsg);
52056893Sfenner		if (lct)
52156893Sfenner			printf("(%d%sline%s) ", lct, seensubj? " " : " more ",
52256893Sfenner			    (lct == 1) ? "" : "s");
52356893Sfenner
52456893Sfenner		if (hdrs) {
52556893Sfenner			printf("\n-----\n");
52656893Sfenner			fclose(newmsg);
52756893Sfenner			continue;
52856893Sfenner		}
52956893Sfenner
53056893Sfenner		/*
53156893Sfenner		 * Ask user for command
53298527Sfenner		 */
53356893Sfenner		if (totty)
534162021Ssam			ask(lct? MORE : (msg==lastmsg? NOMORE : NEXT));
53556893Sfenner		else
53656893Sfenner			inbuf[0] = 'y';
53798527Sfenner		if (totty)
53856893Sfenner			signal(SIGTSTP, SIG_DFL);
539162021Ssamcmnd:
54056893Sfenner		in = inbuf;
54156893Sfenner		switch (*in) {
54298527Sfenner			case 'x':
54356893Sfenner				/* FALLTHROUGH */
54456893Sfenner			case 'X':
54556893Sfenner				exit(0);
54656893Sfenner				/* NOTREACHED */
54756893Sfenner
54856893Sfenner			case 'q':
54956893Sfenner				/* FALLTHROUGH */
550162021Ssam			case 'Q':
55156893Sfenner				quitit = YES;
55256893Sfenner				printf("--Postponed--\n");
55356893Sfenner				exit(0);
554162021Ssam				/* NOTREACHED */
55556893Sfenner
55656893Sfenner			case 'n':
55756893Sfenner				/* FALLTHROUGH */
55898527Sfenner			case 'N':
55956893Sfenner				if (msg >= nextmsg) sep = "Flushed";
56056893Sfenner				prevmsg = msg;
56156893Sfenner				break;
56256893Sfenner
56356893Sfenner			case 'p':
56456893Sfenner				/* FALLTHROUGH */
56556893Sfenner			case 'P':
56656893Sfenner				use_pager = (*in++ == 'p');
56756893Sfenner				/* FALLTHROUGH */
56898527Sfenner			case '\n':
56956893Sfenner				/* FALLTHROUGH */
57056893Sfenner			case 'y':
57156893Sfenner			default:
57256893Sfenner				if (*in == '-') {
57356893Sfenner					msg = prevmsg-1;
57456893Sfenner					sep = "replay";
57556893Sfenner					break;
57656893Sfenner				}
57756893Sfenner				if (isdigit(*in)) {
57856893Sfenner					msg = next(in);
57956893Sfenner					sep = in;
58056893Sfenner					break;
58198527Sfenner				}
58298527Sfenner
58356893Sfenner				prmesg(nlines + lct + (seensubj? 1 : 0));
58456893Sfenner				prevmsg = msg;
58556893Sfenner
58656893Sfenner		}
58756893Sfenner
58856893Sfenner		printf("--%s--\n", sep);
58956893Sfenner		sep = "-";
59056893Sfenner		if (msg >= nextmsg) {
59156893Sfenner			nextmsg = msg + 1;
59256893Sfenner			rewind(msgsrc);
59356893Sfenner			fprintf(msgsrc, "%d\n", nextmsg);
59456893Sfenner			fflush(msgsrc);
59556893Sfenner		}
59656893Sfenner		if (newmsg)
59756893Sfenner			fclose(newmsg);
59856893Sfenner		if (quitit)
59956893Sfenner			break;
60056893Sfenner	}
60156893Sfenner
60256893Sfenner	/*
60356893Sfenner	 * Make sure .rc file gets updated
60456893Sfenner	 */
60556893Sfenner	if (--msg >= nextmsg) {
60656893Sfenner		nextmsg = msg + 1;
60756893Sfenner		rewind(msgsrc);
60856893Sfenner		fprintf(msgsrc, "%d\n", nextmsg);
60956893Sfenner		fflush(msgsrc);
61056893Sfenner	}
61156893Sfenner	if (already && !quitit && lastcmd && totty) {
61256893Sfenner		/*
61356893Sfenner		 * save or reply to last message?
61456893Sfenner		 */
61556893Sfenner		msg = prevmsg;
61656893Sfenner		ask(NOMORE);
61756893Sfenner		if (inbuf[0] == '-' || isdigit(inbuf[0]))
61856893Sfenner			goto cmnd;
61956893Sfenner	}
62056893Sfenner	if (!(already || hush || qopt))
62156893Sfenner		printf("No new messages.\n");
62256893Sfenner	exit(0);
62356893Sfenner	/* NOTREACHED */
62456893Sfenner}
62556893Sfenner
62656893Sfennerstatic void
62756893Sfennerusage(void)
62856893Sfenner{
62956893Sfenner	fprintf(stderr, "usage: msgs [fhlopq] [[-]number]\n");
63056893Sfenner	exit(1);
63156893Sfenner}
63257278Sfenner
63357278Sfennerstatic void
63456893Sfennerprmesg(int length)
63556893Sfenner{
63656893Sfenner	FILE *outf;
637146778Ssam	char *env_pager;
63856893Sfenner
639172686Smlaier	if (use_pager && length > Lpp) {
640172686Smlaier		signal(SIGPIPE, SIG_IGN);
641172686Smlaier		signal(SIGQUIT, SIG_IGN);
642172686Smlaier		if ((env_pager = getenv("PAGER")) == NULL) {
643172686Smlaier			snprintf(cmdbuf, sizeof(cmdbuf), _PATH_PAGER, Lpp);
644172686Smlaier		} else {
645172686Smlaier			snprintf(cmdbuf, sizeof(cmdbuf), "%s", env_pager);
646172686Smlaier		}
647172686Smlaier		outf = popen(cmdbuf, "w");
64856893Sfenner		if (!outf)
649146778Ssam			outf = stdout;
65056893Sfenner		else
65156893Sfenner			setbuf(outf, (char *)NULL);
65256893Sfenner	}
65356893Sfenner	else
65456893Sfenner		outf = stdout;
65556893Sfenner
65656893Sfenner	if (seensubj)
65756893Sfenner		putc('\n', outf);
658146778Ssam
659172686Smlaier	while (fgets(inbuf, sizeof inbuf, newmsg)) {
660146778Ssam		fputs(inbuf, outf);
661146778Ssam		if (ferror(outf)) {
662146778Ssam			clearerr(outf);
663146778Ssam			break;
664146778Ssam		}
66556893Sfenner	}
666146778Ssam
667146778Ssam	if (outf != stdout) {
668146778Ssam		pclose(outf);
66956893Sfenner		signal(SIGPIPE, SIG_DFL);
670146778Ssam		signal(SIGQUIT, SIG_DFL);
671127675Sbms	}
672146778Ssam	else {
673127675Sbms		fflush(stdout);
674127675Sbms	}
675127675Sbms
676146778Ssam	/* force wait on output */
677146778Ssam	tcdrain(fileno(stdout));
678127675Sbms}
679127675Sbms
680146778Ssamstatic void
681127675Sbmsonintr(int unused __unused)
682127675Sbms{
683127675Sbms	signal(SIGINT, onintr);
684127675Sbms	if (mailing)
685146778Ssam		unlink(fname);
686146778Ssam	if (sending) {
687146778Ssam		unlink(fname);
688146778Ssam		puts("--Killed--");
689146778Ssam		exit(1);
690146778Ssam	}
691146778Ssam	if (printing) {
692146778Ssam		putchar('\n');
693146778Ssam		if (hdrs)
694146778Ssam			exit(0);
695146778Ssam		sep = "Interrupt";
696146778Ssam		if (newmsg)
697146778Ssam			fseeko(newmsg, (off_t)0, SEEK_END);
698146778Ssam		intrpflg = YES;
699111729Sfenner	}
700146778Ssam}
701146778Ssam
70256893Sfenner/*
70356893Sfenner * We have just gotten a susp.  Suspend and prepare to resume.
704146778Ssam */
705146778Ssamstatic void
706146778Ssamonsusp(int unused __unused)
707146778Ssam{
708146778Ssam	signal(SIGTSTP, SIG_DFL);
70956893Sfenner	sigsetmask(0);
710146778Ssam	kill(0, SIGTSTP);
711146778Ssam	signal(SIGTSTP, onsusp);
71298527Sfenner	if (!mailing)
71356893Sfenner		longjmp(tstpbuf, 0);
71456893Sfenner}
715146778Ssam
71698527Sfennerstatic int
71798527Sfennerlinecnt(FILE *f)
718146778Ssam{
719146778Ssam	off_t oldpos = ftello(f);
720127675Sbms	int l = 0;
721146778Ssam	char lbuf[BUFSIZ];
722146778Ssam
723127675Sbms	while (fgets(lbuf, sizeof lbuf, f))
724127675Sbms		l++;
725146778Ssam	clearerr(f);
726127675Sbms	fseeko(f, oldpos, SEEK_SET);
727127675Sbms	return (l);
728127675Sbms}
729127675Sbms
730127675Sbmsstatic int
731127675Sbmsnext(char *buf)
732127675Sbms{
733127675Sbms	int i;
734127675Sbms	sscanf(buf, "%d", &i);
73556893Sfenner	sprintf(buf, "Goto %d", i);
736146778Ssam	return(--i);
737146778Ssam}
738146778Ssam
73956893Sfennerstatic void
740146778Ssamask(const char *prompt)
741146778Ssam{
742146778Ssam	char	inch;
743146778Ssam	int	n, cmsg, fd;
74456893Sfenner	off_t	oldpos;
74556893Sfenner	FILE	*cpfrom, *cpto;
74656893Sfenner
74756893Sfenner	printf("%s ", prompt);
748146778Ssam	fflush(stdout);
74998527Sfenner	intrpflg = NO;
75056893Sfenner	(void) fgets(inbuf, sizeof inbuf, stdin);
75156893Sfenner	if ((n = strlen(inbuf)) > 0 && inbuf[n - 1] == '\n')
752172686Smlaier		inbuf[n - 1] = '\0';
753172686Smlaier	if (intrpflg)
754172686Smlaier		inbuf[0] = 'x';
755172686Smlaier
756172686Smlaier	/*
757172686Smlaier	 * Handle 'mail' and 'save' here.
758172686Smlaier	 */
759172686Smlaier	if ((inch = inbuf[0]) == 's' || inch == 'm') {
76056893Sfenner		if (inbuf[1] == '-')
76156893Sfenner			cmsg = prevmsg;
76256893Sfenner		else if (isdigit(inbuf[1]))
76375118Sfenner			cmsg = atoi(&inbuf[1]);
764172686Smlaier		else
765172686Smlaier			cmsg = msg;
766172686Smlaier		snprintf(fname, sizeof(fname), "%s/%d", _PATH_MSGS, cmsg);
767172686Smlaier
768172686Smlaier		oldpos = ftello(newmsg);
769172686Smlaier
77098527Sfenner		cpfrom = fopen(fname, "r");
771146778Ssam		if (!cpfrom) {
77256893Sfenner			printf("Message %d not found\n", cmsg);
77356893Sfenner			ask (prompt);
77498527Sfenner			return;
77556893Sfenner		}
77656893Sfenner
77756893Sfenner		if (inch == 's') {
778172686Smlaier			in = nxtfld(inbuf);
779172686Smlaier			if (*in) {
780172686Smlaier				for (n=0; in[n] > ' '; n++) { /* sizeof fname? */
78156893Sfenner					fname[n] = in[n];
78256893Sfenner				}
78398527Sfenner				fname[n] = '\0';
78456893Sfenner			}
785146778Ssam			else
78656893Sfenner				strcpy(fname, "Messages");
78756893Sfenner			fd = open(fname, O_RDWR|O_EXCL|O_CREAT|O_APPEND);
78856893Sfenner		}
78956893Sfenner		else {
79056893Sfenner			strcpy(fname, _PATH_TMP);
79156893Sfenner			fd = mkstemp(fname);
79256893Sfenner			if (fd != -1) {
79356893Sfenner				snprintf(cmdbuf, sizeof(cmdbuf), _PATH_MAIL,
79456893Sfenner				    fname);
79556893Sfenner				mailing = YES;
79656893Sfenner			}
79756893Sfenner		}
79856893Sfenner		if (fd == -1 || (cpto = fdopen(fd, "a")) == NULL) {
79956893Sfenner			if (fd != -1)
80056893Sfenner				close(fd);
80156893Sfenner			warn("%s", fname);
80256893Sfenner			mailing = NO;
80356893Sfenner			fseeko(newmsg, oldpos, SEEK_SET);
80456893Sfenner			ask(prompt);
805146778Ssam			return;
806146778Ssam		}
807146778Ssam
808146778Ssam		while ((n = fread(inbuf, 1, sizeof inbuf, cpfrom)))
809146778Ssam			fwrite(inbuf, 1, n, cpto);
810146778Ssam
811146778Ssam		fclose(cpfrom);
812146778Ssam		fclose(cpto);
813146778Ssam		fseeko(newmsg, oldpos, SEEK_SET);/* reposition current message */
814146778Ssam		if (inch == 's')
815146778Ssam			printf("Message %d saved in \"%s\"\n", cmsg, fname);
816146778Ssam		else {
817146778Ssam			system(cmdbuf);
818146778Ssam			unlink(fname);
819146778Ssam			mailing = NO;
820146778Ssam		}
821146778Ssam		ask(prompt);
822146778Ssam	}
823146778Ssam}
824146778Ssam
825146778Ssamstatic void
826146778Ssamgfrsub(FILE *infile)
827146778Ssam{
828146778Ssam	off_t frompos;
829146778Ssam	int count;
830146778Ssam
831146778Ssam	seensubj = seenfrom = NO;
832146778Ssam	local = YES;
833146778Ssam	subj[0] = from[0] = date[0] = '\0';
834146778Ssam
835146778Ssam	/*
836146778Ssam	 * Is this a normal message?
837146778Ssam	 */
838146778Ssam	if (fgets(inbuf, sizeof inbuf, infile)) {
839146778Ssam		if (strncmp(inbuf, "From", 4)==0) {
840146778Ssam			/*
841146778Ssam			 * expected form starts with From
842146778Ssam			 */
843146778Ssam			seenfrom = YES;
844146778Ssam			frompos = ftello(infile);
84556893Sfenner			ptr = from;
84656893Sfenner			in = nxtfld(inbuf);
84756893Sfenner			if (*in) {
84856893Sfenner				count = sizeof(from) - 1;
84956893Sfenner				while (*in && *in > ' ' && count-- > 0) {
85056893Sfenner					if (*in == ':' || *in == '@' ||
85156893Sfenner					    *in == '!')
85256893Sfenner						local = NO;
85356893Sfenner					*ptr++ = *in++;
85456893Sfenner				}
85556893Sfenner			}
856146778Ssam			*ptr = '\0';
85756893Sfenner			if (*(in = nxtfld(in)))
85856893Sfenner				strncpy(date, in, sizeof date);
85956893Sfenner			else {
86056893Sfenner				date[0] = '\n';
86156893Sfenner				date[1] = '\0';
86256893Sfenner			}
86356893Sfenner		}
86456893Sfenner		else {
86556893Sfenner			/*
86656893Sfenner			 * not the expected form
867146778Ssam			 */
86856893Sfenner			rewind(infile);
869146778Ssam			return;
87056893Sfenner		}
871146778Ssam	}
87256893Sfenner	else
87356893Sfenner		/*
87456893Sfenner		 * empty file ?
87556893Sfenner		 */
87656893Sfenner		return;
87756893Sfenner
87856893Sfenner	/*
879146778Ssam	 * look for Subject line until EOF or a blank line
88056893Sfenner	 */
88156893Sfenner	while (fgets(inbuf, sizeof inbuf, infile)
88256893Sfenner	    && !(blankline = (inbuf[0] == '\n'))) {
88356893Sfenner		/*
88456893Sfenner		 * extract Subject line
88556893Sfenner		 */
88656893Sfenner		if (!seensubj && strncmp(inbuf, "Subj", 4)==0) {
88756893Sfenner			seensubj = YES;
88856893Sfenner			frompos = ftello(infile);
88956893Sfenner			strncpy(subj, nxtfld(inbuf), sizeof subj);
89056893Sfenner		}
891146778Ssam	}
89256893Sfenner	if (!blankline)
89356893Sfenner		/*
894146778Ssam		 * ran into EOF
89556893Sfenner		 */
89656893Sfenner		fseeko(infile, frompos, SEEK_SET);
89756893Sfenner
89856893Sfenner	if (!seensubj)
89956893Sfenner		/*
90056893Sfenner		 * for possible use with Mail
90156893Sfenner		 */
902146778Ssam		strncpy(subj, "(No Subject)\n", sizeof subj);
90356893Sfenner}
90456893Sfenner
90556893Sfennerstatic char *
90656893Sfennernxtfld(char *s)
90756893Sfenner{
90856893Sfenner	if (*s) while (*s && !isspace(*s)) s++;     /* skip over this field */
90956893Sfenner	if (*s) while (*s && isspace(*s)) s++;    /* find start of next field */
91056893Sfenner	return (s);
91156893Sfenner}
91256893Sfenner