msgs.c revision 1.2 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.2 1993/08/01 18:10:58 mycroft 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, "a");
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