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