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