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