xref: /netbsd-src/usr.bin/msgs/msgs.c (revision 5f7096188587a2c7c95fa3c69b78e1ec9c7923d0)
1 /*-
2  * Copyright (c) 1980 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 char copyright[] =
36 "@(#) Copyright (c) 1980 The Regents of the University of California.\n\
37  All rights reserved.\n";
38 #endif /* not lint */
39 
40 #ifndef lint
41 /*static char sccsid[] = "from: @(#)msgs.c	5.8 (Berkeley) 2/4/91";*/
42 static char rcsid[] = "$Id: msgs.c,v 1.3 1993/10/26 18:24:02 jtc Exp $";
43 #endif /* not lint */
44 
45 /*
46  * msgs - a user bulletin board program
47  *
48  * usage:
49  *	msgs [fhlopq] [[-]number]	to read messages
50  *	msgs -s				to place messages
51  *	msgs -c [-days]			to clean up the bulletin board
52  *
53  * prompt commands are:
54  *	y	print message
55  *	n	flush message, go to next message
56  *	q	flush message, quit
57  *	p	print message, turn on 'pipe thru more' mode
58  *	P	print message, turn off 'pipe thru more' mode
59  *	-	reprint last message
60  *	s[-][<num>] [<filename>]	save message
61  *	m[-][<num>]	mail with message in temp mbox
62  *	x	exit without flushing this message
63  *	<num>	print message number <num>
64  */
65 
66 #define V7		/* will look for TERM in the environment */
67 #define OBJECT		/* will object to messages without Subjects */
68 /* #define REJECT	/* will reject messages without Subjects
69 			   (OBJECT must be defined also) */
70 /* #define UNBUFFERED	/* use unbuffered output */
71 
72 #include <sys/param.h>
73 #include <sys/dir.h>
74 #include <sys/stat.h>
75 #include <ctype.h>
76 #include <errno.h>
77 #include <pwd.h>
78 #include <setjmp.h>
79 #include <sgtty.h>
80 #include <signal.h>
81 #include <stdio.h>
82 #include <stdlib.h>
83 #include <string.h>
84 #include <time.h>
85 #include "pathnames.h"
86 
87 #define CMODE	0666		/* bounds file creation mode */
88 #define NO	0
89 #define YES	1
90 #define SUPERUSER	0	/* superuser uid */
91 #define DAEMON		1	/* daemon uid */
92 #define NLINES	24		/* default number of lines/crt screen */
93 #define NDAYS	21		/* default keep time for messages */
94 #define DAYS	*24*60*60	/* seconds/day */
95 #define MSGSRC	".msgsrc"	/* user's rc file */
96 #define BOUNDS	"bounds"	/* message bounds file */
97 #define NEXT	"Next message? [yq]"
98 #define MORE	"More? [ynq]"
99 #define NOMORE	"(No more) [q] ?"
100 
101 typedef	char	bool;
102 
103 FILE	*msgsrc;
104 FILE	*newmsg;
105 char	*sep = "-";
106 char	inbuf[BUFSIZ];
107 char	fname[128];
108 char	cmdbuf[128];
109 char	subj[128];
110 char	from[128];
111 char	date[128];
112 char	*ptr;
113 char	*in;
114 bool	local;
115 bool	ruptible;
116 bool	totty;
117 bool	seenfrom;
118 bool	seensubj;
119 bool	blankline;
120 bool	printing = NO;
121 bool	mailing = NO;
122 bool	quitit = NO;
123 bool	sending = NO;
124 bool	intrpflg = NO;
125 int	uid;
126 int	msg;
127 int	prevmsg;
128 int	lct;
129 int	nlines;
130 int	Lpp = 0;
131 time_t	t;
132 time_t	keep;
133 struct	sgttyb	otty;
134 
135 char	*mktemp();
136 char	*nxtfld();
137 void	onintr();
138 void	onsusp();
139 
140 /* option initialization */
141 bool	hdrs = NO;
142 bool	qopt = NO;
143 bool	hush = NO;
144 bool	send_msg = NO;
145 bool	locomode = NO;
146 bool	use_pager = NO;
147 bool	clean = NO;
148 bool	lastcmd = NO;
149 jmp_buf	tstpbuf;
150 
151 main(argc, argv)
152 int argc; char *argv[];
153 {
154 	bool newrc, already;
155 	int rcfirst = 0;		/* first message to print (from .rc) */
156 	int rcback = 0;			/* amount to back off of rcfirst */
157 	int firstmsg, nextmsg, lastmsg = 0;
158 	int blast = 0;
159 	FILE *bounds;
160 
161 #ifdef UNBUFFERED
162 	setbuf(stdout, NULL);
163 #endif
164 
165 	gtty(fileno(stdout), &otty);
166 	time(&t);
167 	setuid(uid = getuid());
168 	ruptible = (signal(SIGINT, SIG_IGN) == SIG_DFL);
169 	if (ruptible)
170 		signal(SIGINT, SIG_DFL);
171 
172 	argc--, argv++;
173 	while (argc > 0) {
174 		if (isdigit(argv[0][0])) {	/* starting message # */
175 			rcfirst = atoi(argv[0]);
176 		}
177 		else if (isdigit(argv[0][1])) {	/* backward offset */
178 			rcback = atoi( &( argv[0][1] ) );
179 		}
180 		else {
181 			ptr = *argv;
182 			while (*ptr) switch (*ptr++) {
183 
184 			case '-':
185 				break;
186 
187 			case 'c':
188 				if (uid != SUPERUSER && uid != DAEMON) {
189 					fprintf(stderr, "Sorry\n");
190 					exit(1);
191 				}
192 				clean = YES;
193 				break;
194 
195 			case 'f':		/* silently */
196 				hush = YES;
197 				break;
198 
199 			case 'h':		/* headers only */
200 				hdrs = YES;
201 				break;
202 
203 			case 'l':		/* local msgs only */
204 				locomode = YES;
205 				break;
206 
207 			case 'o':		/* option to save last message */
208 				lastcmd = YES;
209 				break;
210 
211 			case 'p':		/* pipe thru 'more' during long msgs */
212 				use_pager = YES;
213 				break;
214 
215 			case 'q':		/* query only */
216 				qopt = YES;
217 				break;
218 
219 			case 's':		/* sending TO msgs */
220 				send_msg = YES;
221 				break;
222 
223 			default:
224 				fprintf(stderr,
225 					"usage: msgs [fhlopq] [[-]number]\n");
226 				exit(1);
227 			}
228 		}
229 		argc--, argv++;
230 	}
231 
232 	/*
233 	 * determine current message bounds
234 	 */
235 	sprintf(fname, "%s/%s", _PATH_MSGS, BOUNDS);
236 	bounds = fopen(fname, "r");
237 
238 	if (bounds != NULL) {
239 		fscanf(bounds, "%d %d\n", &firstmsg, &lastmsg);
240 		fclose(bounds);
241 		blast = lastmsg;	/* save upper bound */
242 	}
243 
244 	if (clean)
245 		keep = t - (rcback? rcback : NDAYS) DAYS;
246 
247 	if (clean || bounds == NULL) {	/* relocate message bounds */
248 		struct direct *dp;
249 		struct stat stbuf;
250 		bool seenany = NO;
251 		DIR	*dirp;
252 
253 		dirp = opendir(_PATH_MSGS);
254 		if (dirp == NULL) {
255 			perror(_PATH_MSGS);
256 			exit(errno);
257 		}
258 
259 		firstmsg = 32767;
260 		lastmsg = 0;
261 
262 		for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)){
263 			register char *cp = dp->d_name;
264 			register int i = 0;
265 
266 			if (dp->d_ino == 0)
267 				continue;
268 			if (dp->d_namlen == 0)
269 				continue;
270 
271 			if (clean)
272 				sprintf(inbuf, "%s/%s", _PATH_MSGS, cp);
273 
274 			while (isdigit(*cp))
275 				i = i * 10 + *cp++ - '0';
276 			if (*cp)
277 				continue;	/* not a message! */
278 
279 			if (clean) {
280 				if (stat(inbuf, &stbuf) != 0)
281 					continue;
282 				if (stbuf.st_mtime < keep
283 				    && stbuf.st_mode&S_IWRITE) {
284 					unlink(inbuf);
285 					continue;
286 				}
287 			}
288 
289 			if (i > lastmsg)
290 				lastmsg = i;
291 			if (i < firstmsg)
292 				firstmsg = i;
293 			seenany = YES;
294 		}
295 		closedir(dirp);
296 
297 		if (!seenany) {
298 			if (blast != 0)	/* never lower the upper bound! */
299 				lastmsg = blast;
300 			firstmsg = lastmsg + 1;
301 		}
302 		else if (blast > lastmsg)
303 			lastmsg = blast;
304 
305 		if (!send_msg) {
306 			bounds = fopen(fname, "w");
307 			if (bounds == NULL) {
308 				perror(fname);
309 				exit(errno);
310 			}
311 			chmod(fname, CMODE);
312 			fprintf(bounds, "%d %d\n", firstmsg, lastmsg);
313 			fclose(bounds);
314 		}
315 	}
316 
317 	if (send_msg) {
318 		/*
319 		 * Send mode - place msgs in _PATH_MSGS
320 		 */
321 		bounds = fopen(fname, "w");
322 		if (bounds == NULL) {
323 			perror(fname);
324 			exit(errno);
325 		}
326 
327 		nextmsg = lastmsg + 1;
328 		sprintf(fname, "%s/%d", _PATH_MSGS, nextmsg);
329 		newmsg = fopen(fname, "w");
330 		if (newmsg == NULL) {
331 			perror(fname);
332 			exit(errno);
333 		}
334 		chmod(fname, 0644);
335 
336 		fprintf(bounds, "%d %d\n", firstmsg, nextmsg);
337 		fclose(bounds);
338 
339 		sending = YES;
340 		if (ruptible)
341 			signal(SIGINT, onintr);
342 
343 		if (isatty(fileno(stdin))) {
344 			ptr = getpwuid(uid)->pw_name;
345 			printf("Message %d:\nFrom %s %sSubject: ",
346 				nextmsg, ptr, ctime(&t));
347 			fflush(stdout);
348 			fgets(inbuf, sizeof inbuf, stdin);
349 			putchar('\n');
350 			fflush(stdout);
351 			fprintf(newmsg, "From %s %sSubject: %s\n",
352 				ptr, ctime(&t), inbuf);
353 			blankline = seensubj = YES;
354 		}
355 		else
356 			blankline = seensubj = NO;
357 		for (;;) {
358 			fgets(inbuf, sizeof inbuf, stdin);
359 			if (feof(stdin) || ferror(stdin))
360 				break;
361 			blankline = (blankline || (inbuf[0] == '\n'));
362 			seensubj = (seensubj || (!blankline && (strncmp(inbuf, "Subj", 4) == 0)));
363 			fputs(inbuf, newmsg);
364 		}
365 #ifdef OBJECT
366 		if (!seensubj) {
367 			printf("NOTICE: Messages should have a Subject field!\n");
368 #ifdef REJECT
369 			unlink(fname);
370 #endif
371 			exit(1);
372 		}
373 #endif
374 		exit(ferror(stdin));
375 	}
376 	if (clean)
377 		exit(0);
378 
379 	/*
380 	 * prepare to display messages
381 	 */
382 	totty = (isatty(fileno(stdout)) != 0);
383 	use_pager = use_pager && totty;
384 
385 	sprintf(fname, "%s/%s", getenv("HOME"), MSGSRC);
386 	msgsrc = fopen(fname, "r");
387 	if (msgsrc) {
388 		newrc = NO;
389 		fscanf(msgsrc, "%d\n", &nextmsg);
390 		fclose(msgsrc);
391 		if (nextmsg > lastmsg+1) {
392 			printf("Warning: bounds have been reset (%d, %d)\n",
393 				firstmsg, lastmsg);
394 			truncate(fname, (off_t)0);
395 			newrc = YES;
396 		}
397 		else if (!rcfirst)
398 			rcfirst = nextmsg - rcback;
399 	}
400 	else
401 		newrc = YES;
402 	msgsrc = fopen(fname, "w");
403 	if (msgsrc == NULL) {
404 		perror(fname);
405 		exit(errno);
406 	}
407 	if (rcfirst) {
408 		if (rcfirst > lastmsg+1) {
409 			printf("Warning: the last message is number %d.\n",
410 				lastmsg);
411 			rcfirst = nextmsg;
412 		}
413 		if (rcfirst > firstmsg)
414 			firstmsg = rcfirst;	/* don't set below first msg */
415 	}
416 	if (newrc) {
417 		nextmsg = firstmsg;
418 		fseek(msgsrc, 0L, 0);
419 		fprintf(msgsrc, "%d\n", nextmsg);
420 		fflush(msgsrc);
421 	}
422 
423 #ifdef V7
424 	if (totty) {
425 		struct winsize win;
426 		if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1)
427 			Lpp = win.ws_row;
428 		if (Lpp <= 0) {
429 			if (tgetent(inbuf, getenv("TERM")) <= 0
430 			    || (Lpp = tgetnum("li")) <= 0) {
431 				Lpp = NLINES;
432 			}
433 		}
434 	}
435 #endif
436 	Lpp -= 6;	/* for headers, etc. */
437 
438 	already = NO;
439 	prevmsg = firstmsg;
440 	printing = YES;
441 	if (ruptible)
442 		signal(SIGINT, onintr);
443 
444 	/*
445 	 * Main program loop
446 	 */
447 	for (msg = firstmsg; msg <= lastmsg; msg++) {
448 
449 		sprintf(fname, "%s/%d", _PATH_MSGS, msg);
450 		newmsg = fopen(fname, "r");
451 		if (newmsg == NULL)
452 			continue;
453 
454 		gfrsub(newmsg);		/* get From and Subject fields */
455 		if (locomode && !local) {
456 			fclose(newmsg);
457 			continue;
458 		}
459 
460 		if (qopt) {	/* This has to be located here */
461 			printf("There are new messages.\n");
462 			exit(0);
463 		}
464 
465 		if (already && !hdrs)
466 			putchar('\n');
467 
468 		/*
469 		 * Print header
470 		 */
471 		if (totty)
472 			signal(SIGTSTP, onsusp);
473 		(void) setjmp(tstpbuf);
474 		already = YES;
475 		nlines = 2;
476 		if (seenfrom) {
477 			printf("Message %d:\nFrom %s %s", msg, from, date);
478 			nlines++;
479 		}
480 		if (seensubj) {
481 			printf("Subject: %s", subj);
482 			nlines++;
483 		}
484 		else {
485 			if (seenfrom) {
486 				putchar('\n');
487 				nlines++;
488 			}
489 			while (nlines < 6
490 			    && fgets(inbuf, sizeof inbuf, newmsg)
491 			    && inbuf[0] != '\n') {
492 				fputs(inbuf, stdout);
493 				nlines++;
494 			}
495 		}
496 
497 		lct = linecnt(newmsg);
498 		if (lct)
499 			printf("(%d%slines) ", lct, seensubj? " " : " more ");
500 
501 		if (hdrs) {
502 			printf("\n-----\n");
503 			fclose(newmsg);
504 			continue;
505 		}
506 
507 		/*
508 		 * Ask user for command
509 		 */
510 		if (totty)
511 			ask(lct? MORE : (msg==lastmsg? NOMORE : NEXT));
512 		else
513 			inbuf[0] = 'y';
514 		if (totty)
515 			signal(SIGTSTP, SIG_DFL);
516 cmnd:
517 		in = inbuf;
518 		switch (*in) {
519 			case 'x':
520 			case 'X':
521 				exit(0);
522 
523 			case 'q':
524 			case 'Q':
525 				quitit = YES;
526 				printf("--Postponed--\n");
527 				exit(0);
528 				/* intentional fall-thru */
529 			case 'n':
530 			case 'N':
531 				if (msg >= nextmsg) sep = "Flushed";
532 				prevmsg = msg;
533 				break;
534 
535 			case 'p':
536 			case 'P':
537 				use_pager = (*in++ == 'p');
538 				/* intentional fallthru */
539 			case '\n':
540 			case 'y':
541 			default:
542 				if (*in == '-') {
543 					msg = prevmsg-1;
544 					sep = "replay";
545 					break;
546 				}
547 				if (isdigit(*in)) {
548 					msg = next(in);
549 					sep = in;
550 					break;
551 				}
552 
553 				prmesg(nlines + lct + (seensubj? 1 : 0));
554 				prevmsg = msg;
555 
556 		}
557 
558 		printf("--%s--\n", sep);
559 		sep = "-";
560 		if (msg >= nextmsg) {
561 			nextmsg = msg + 1;
562 			fseek(msgsrc, 0L, 0);
563 			fprintf(msgsrc, "%d\n", nextmsg);
564 			fflush(msgsrc);
565 		}
566 		if (newmsg)
567 			fclose(newmsg);
568 		if (quitit)
569 			break;
570 	}
571 
572 	/*
573 	 * Make sure .rc file gets updated
574 	 */
575 	if (--msg >= nextmsg) {
576 		nextmsg = msg + 1;
577 		fseek(msgsrc, 0L, 0);
578 		fprintf(msgsrc, "%d\n", nextmsg);
579 		fflush(msgsrc);
580 	}
581 	if (already && !quitit && lastcmd && totty) {
582 		/*
583 		 * save or reply to last message?
584 		 */
585 		msg = prevmsg;
586 		ask(NOMORE);
587 		if (inbuf[0] == '-' || isdigit(inbuf[0]))
588 			goto cmnd;
589 	}
590 	if (!(already || hush || qopt))
591 		printf("No new messages.\n");
592 	exit(0);
593 }
594 
595 prmesg(length)
596 int length;
597 {
598 	FILE *outf;
599 
600 	if (use_pager && length > Lpp) {
601 		signal(SIGPIPE, SIG_IGN);
602 		signal(SIGQUIT, SIG_IGN);
603 		sprintf(cmdbuf, _PATH_PAGER, Lpp);
604 		outf = popen(cmdbuf, "w");
605 		if (!outf)
606 			outf = stdout;
607 		else
608 			setbuf(outf, (char *)NULL);
609 	}
610 	else
611 		outf = stdout;
612 
613 	if (seensubj)
614 		putc('\n', outf);
615 
616 	while (fgets(inbuf, sizeof inbuf, newmsg)) {
617 		fputs(inbuf, outf);
618 		if (ferror(outf)) {
619 			clearerr(outf);
620 			break;
621 		}
622 	}
623 
624 	if (outf != stdout) {
625 		pclose(outf);
626 		signal(SIGPIPE, SIG_DFL);
627 		signal(SIGQUIT, SIG_DFL);
628 	}
629 	else {
630 		fflush(stdout);
631 	}
632 
633 	/* trick to force wait on output */
634 	stty(fileno(stdout), &otty);
635 }
636 
637 void
638 onintr()
639 {
640 	signal(SIGINT, onintr);
641 	if (mailing)
642 		unlink(fname);
643 	if (sending) {
644 		unlink(fname);
645 		puts("--Killed--");
646 		exit(1);
647 	}
648 	if (printing) {
649 		putchar('\n');
650 		if (hdrs)
651 			exit(0);
652 		sep = "Interrupt";
653 		if (newmsg)
654 			fseek(newmsg, 0L, 2);
655 		intrpflg = YES;
656 	}
657 }
658 
659 /*
660  * We have just gotten a susp.  Suspend and prepare to resume.
661  */
662 void
663 onsusp()
664 {
665 
666 	signal(SIGTSTP, SIG_DFL);
667 	sigsetmask(0);
668 	kill(0, SIGTSTP);
669 	signal(SIGTSTP, onsusp);
670 	if (!mailing)
671 		longjmp(tstpbuf, 0);
672 }
673 
674 linecnt(f)
675 FILE *f;
676 {
677 	off_t oldpos = ftell(f);
678 	int l = 0;
679 	char lbuf[BUFSIZ];
680 
681 	while (fgets(lbuf, sizeof lbuf, f))
682 		l++;
683 	clearerr(f);
684 	fseek(f, oldpos, 0);
685 	return (l);
686 }
687 
688 next(buf)
689 char *buf;
690 {
691 	int i;
692 	sscanf(buf, "%d", &i);
693 	sprintf(buf, "Goto %d", i);
694 	return(--i);
695 }
696 
697 ask(prompt)
698 char *prompt;
699 {
700 	char	inch;
701 	int	n, cmsg;
702 	off_t	oldpos;
703 	FILE	*cpfrom, *cpto;
704 
705 	printf("%s ", prompt);
706 	fflush(stdout);
707 	intrpflg = NO;
708 	(void) fgets(inbuf, sizeof inbuf, stdin);
709 	if ((n = strlen(inbuf)) > 0 && inbuf[n - 1] == '\n')
710 		inbuf[n - 1] = '\0';
711 	if (intrpflg)
712 		inbuf[0] = 'x';
713 
714 	/*
715 	 * Handle 'mail' and 'save' here.
716 	 */
717 	if ((inch = inbuf[0]) == 's' || inch == 'm') {
718 		if (inbuf[1] == '-')
719 			cmsg = prevmsg;
720 		else if (isdigit(inbuf[1]))
721 			cmsg = atoi(&inbuf[1]);
722 		else
723 			cmsg = msg;
724 		sprintf(fname, "%s/%d", _PATH_MSGS, cmsg);
725 
726 		oldpos = ftell(newmsg);
727 
728 		cpfrom = fopen(fname, "r");
729 		if (!cpfrom) {
730 			printf("Message %d not found\n", cmsg);
731 			ask (prompt);
732 			return;
733 		}
734 
735 		if (inch == 's') {
736 			in = nxtfld(inbuf);
737 			if (*in) {
738 				for (n=0; in[n] > ' '; n++) { /* sizeof fname? */
739 					fname[n] = in[n];
740 				}
741 				fname[n] = NULL;
742 			}
743 			else
744 				strcpy(fname, "Messages");
745 		}
746 		else {
747 			strcpy(fname, _PATH_TMP);
748 			mktemp(fname);
749 			sprintf(cmdbuf, _PATH_MAIL, fname);
750 			mailing = YES;
751 		}
752 		cpto = fopen(fname, "a");
753 		if (!cpto) {
754 			perror(fname);
755 			mailing = NO;
756 			fseek(newmsg, oldpos, 0);
757 			ask(prompt);
758 			return;
759 		}
760 
761 		while (n = fread(inbuf, 1, sizeof inbuf, cpfrom))
762 			fwrite(inbuf, 1, n, cpto);
763 
764 		fclose(cpfrom);
765 		fclose(cpto);
766 		fseek(newmsg, oldpos, 0);	/* reposition current message */
767 		if (inch == 's')
768 			printf("Message %d saved in \"%s\"\n", cmsg, fname);
769 		else {
770 			system(cmdbuf);
771 			unlink(fname);
772 			mailing = NO;
773 		}
774 		ask(prompt);
775 	}
776 }
777 
778 gfrsub(infile)
779 FILE *infile;
780 {
781 	off_t frompos;
782 
783 	seensubj = seenfrom = NO;
784 	local = YES;
785 	subj[0] = from[0] = date[0] = NULL;
786 
787 	/*
788 	 * Is this a normal message?
789 	 */
790 	if (fgets(inbuf, sizeof inbuf, infile)) {
791 		if (strncmp(inbuf, "From", 4)==0) {
792 			/*
793 			 * expected form starts with From
794 			 */
795 			seenfrom = YES;
796 			frompos = ftell(infile);
797 			ptr = from;
798 			in = nxtfld(inbuf);
799 			if (*in) while (*in && *in > ' ') {
800 				if (*in == ':' || *in == '@' || *in == '!')
801 					local = NO;
802 				*ptr++ = *in++;
803 				/* what about sizeof from ? */
804 			}
805 			*ptr = NULL;
806 			if (*(in = nxtfld(in)))
807 				strncpy(date, in, sizeof date);
808 			else {
809 				date[0] = '\n';
810 				date[1] = NULL;
811 			}
812 		}
813 		else {
814 			/*
815 			 * not the expected form
816 			 */
817 			fseek(infile, 0L, 0);
818 			return;
819 		}
820 	}
821 	else
822 		/*
823 		 * empty file ?
824 		 */
825 		return;
826 
827 	/*
828 	 * look for Subject line until EOF or a blank line
829 	 */
830 	while (fgets(inbuf, sizeof inbuf, infile)
831 	    && !(blankline = (inbuf[0] == '\n'))) {
832 		/*
833 		 * extract Subject line
834 		 */
835 		if (!seensubj && strncmp(inbuf, "Subj", 4)==0) {
836 			seensubj = YES;
837 			frompos = ftell(infile);
838 			strncpy(subj, nxtfld(inbuf), sizeof subj);
839 		}
840 	}
841 	if (!blankline)
842 		/*
843 		 * ran into EOF
844 		 */
845 		fseek(infile, frompos, 0);
846 
847 	if (!seensubj)
848 		/*
849 		 * for possible use with Mail
850 		 */
851 		strncpy(subj, "(No Subject)\n", sizeof subj);
852 }
853 
854 char *
855 nxtfld(s)
856 char *s;
857 {
858 	if (*s) while (*s && *s > ' ') s++;	/* skip over this field */
859 	if (*s) while (*s && *s <= ' ') s++;	/* find start of next field */
860 	return (s);
861 }
862