1 /*- 2 * Copyright (c) 1980, 1993 3 * The Regents of the University of California. 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 static char copyright[] = 36 "@(#) Copyright (c) 1980, 1993\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38 #endif /* not lint */ 39 40 #ifndef lint 41 static char sccsid[] = "@(#)msgs.c 8.1 (Berkeley) 6/6/93"; 42 #endif /* not lint */ 43 44 /* 45 * msgs - a user bulletin board program 46 * 47 * usage: 48 * msgs [fhlopq] [[-]number] to read messages 49 * msgs -s to place messages 50 * msgs -c [-days] to clean up the bulletin board 51 * 52 * prompt commands are: 53 * y print message 54 * n flush message, go to next message 55 * q flush message, quit 56 * p print message, turn on 'pipe thru more' mode 57 * P print message, turn off 'pipe thru more' mode 58 * - reprint last message 59 * s[-][<num>] [<filename>] save message 60 * m[-][<num>] mail with message in temp mbox 61 * x exit without flushing this message 62 * <num> print message number <num> 63 */ 64 65 #define V7 /* will look for TERM in the environment */ 66 #define OBJECT /* will object to messages without Subjects */ 67 /* #define REJECT /* will reject messages without Subjects 68 (OBJECT must be defined also) */ 69 /* #define UNBUFFERED /* use unbuffered output */ 70 71 #include <sys/param.h> 72 #include <sys/dir.h> 73 #include <sys/stat.h> 74 #include <ctype.h> 75 #include <errno.h> 76 #include <pwd.h> 77 #include <setjmp.h> 78 #include <sgtty.h> 79 #include <signal.h> 80 #include <stdio.h> 81 #include <stdlib.h> 82 #include <string.h> 83 #include <time.h> 84 #include <unistd.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, "r+"); 403 if (msgsrc == NULL) 404 msgsrc = fopen(fname, "w"); 405 if (msgsrc == NULL) { 406 perror(fname); 407 exit(errno); 408 } 409 if (rcfirst) { 410 if (rcfirst > lastmsg+1) { 411 printf("Warning: the last message is number %d.\n", 412 lastmsg); 413 rcfirst = nextmsg; 414 } 415 if (rcfirst > firstmsg) 416 firstmsg = rcfirst; /* don't set below first msg */ 417 } 418 if (newrc) { 419 nextmsg = firstmsg; 420 fseek(msgsrc, 0L, 0); 421 fprintf(msgsrc, "%d\n", nextmsg); 422 fflush(msgsrc); 423 } 424 425 #ifdef V7 426 if (totty) { 427 struct winsize win; 428 if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1) 429 Lpp = win.ws_row; 430 if (Lpp <= 0) { 431 if (tgetent(inbuf, getenv("TERM")) <= 0 432 || (Lpp = tgetnum("li")) <= 0) { 433 Lpp = NLINES; 434 } 435 } 436 } 437 #endif 438 Lpp -= 6; /* for headers, etc. */ 439 440 already = NO; 441 prevmsg = firstmsg; 442 printing = YES; 443 if (ruptible) 444 signal(SIGINT, onintr); 445 446 /* 447 * Main program loop 448 */ 449 for (msg = firstmsg; msg <= lastmsg; msg++) { 450 451 sprintf(fname, "%s/%d", _PATH_MSGS, msg); 452 newmsg = fopen(fname, "r"); 453 if (newmsg == NULL) 454 continue; 455 456 gfrsub(newmsg); /* get From and Subject fields */ 457 if (locomode && !local) { 458 fclose(newmsg); 459 continue; 460 } 461 462 if (qopt) { /* This has to be located here */ 463 printf("There are new messages.\n"); 464 exit(0); 465 } 466 467 if (already && !hdrs) 468 putchar('\n'); 469 470 /* 471 * Print header 472 */ 473 if (totty) 474 signal(SIGTSTP, onsusp); 475 (void) setjmp(tstpbuf); 476 already = YES; 477 nlines = 2; 478 if (seenfrom) { 479 printf("Message %d:\nFrom %s %s", msg, from, date); 480 nlines++; 481 } 482 if (seensubj) { 483 printf("Subject: %s", subj); 484 nlines++; 485 } 486 else { 487 if (seenfrom) { 488 putchar('\n'); 489 nlines++; 490 } 491 while (nlines < 6 492 && fgets(inbuf, sizeof inbuf, newmsg) 493 && inbuf[0] != '\n') { 494 fputs(inbuf, stdout); 495 nlines++; 496 } 497 } 498 499 lct = linecnt(newmsg); 500 if (lct) 501 printf("(%d%slines) ", lct, seensubj? " " : " more "); 502 503 if (hdrs) { 504 printf("\n-----\n"); 505 fclose(newmsg); 506 continue; 507 } 508 509 /* 510 * Ask user for command 511 */ 512 if (totty) 513 ask(lct? MORE : (msg==lastmsg? NOMORE : NEXT)); 514 else 515 inbuf[0] = 'y'; 516 if (totty) 517 signal(SIGTSTP, SIG_DFL); 518 cmnd: 519 in = inbuf; 520 switch (*in) { 521 case 'x': 522 case 'X': 523 exit(0); 524 525 case 'q': 526 case 'Q': 527 quitit = YES; 528 printf("--Postponed--\n"); 529 exit(0); 530 /* intentional fall-thru */ 531 case 'n': 532 case 'N': 533 if (msg >= nextmsg) sep = "Flushed"; 534 prevmsg = msg; 535 break; 536 537 case 'p': 538 case 'P': 539 use_pager = (*in++ == 'p'); 540 /* intentional fallthru */ 541 case '\n': 542 case 'y': 543 default: 544 if (*in == '-') { 545 msg = prevmsg-1; 546 sep = "replay"; 547 break; 548 } 549 if (isdigit(*in)) { 550 msg = next(in); 551 sep = in; 552 break; 553 } 554 555 prmesg(nlines + lct + (seensubj? 1 : 0)); 556 prevmsg = msg; 557 558 } 559 560 printf("--%s--\n", sep); 561 sep = "-"; 562 if (msg >= nextmsg) { 563 nextmsg = msg + 1; 564 fseek(msgsrc, 0L, 0); 565 fprintf(msgsrc, "%d\n", nextmsg); 566 fflush(msgsrc); 567 } 568 if (newmsg) 569 fclose(newmsg); 570 if (quitit) 571 break; 572 } 573 574 /* 575 * Make sure .rc file gets updated 576 */ 577 if (--msg >= nextmsg) { 578 nextmsg = msg + 1; 579 fseek(msgsrc, 0L, 0); 580 fprintf(msgsrc, "%d\n", nextmsg); 581 fflush(msgsrc); 582 } 583 if (already && !quitit && lastcmd && totty) { 584 /* 585 * save or reply to last message? 586 */ 587 msg = prevmsg; 588 ask(NOMORE); 589 if (inbuf[0] == '-' || isdigit(inbuf[0])) 590 goto cmnd; 591 } 592 if (!(already || hush || qopt)) 593 printf("No new messages.\n"); 594 exit(0); 595 } 596 597 prmesg(length) 598 int length; 599 { 600 FILE *outf; 601 602 if (use_pager && length > Lpp) { 603 signal(SIGPIPE, SIG_IGN); 604 signal(SIGQUIT, SIG_IGN); 605 sprintf(cmdbuf, _PATH_PAGER, Lpp); 606 outf = popen(cmdbuf, "w"); 607 if (!outf) 608 outf = stdout; 609 else 610 setbuf(outf, (char *)NULL); 611 } 612 else 613 outf = stdout; 614 615 if (seensubj) 616 putc('\n', outf); 617 618 while (fgets(inbuf, sizeof inbuf, newmsg)) { 619 fputs(inbuf, outf); 620 if (ferror(outf)) { 621 clearerr(outf); 622 break; 623 } 624 } 625 626 if (outf != stdout) { 627 pclose(outf); 628 signal(SIGPIPE, SIG_DFL); 629 signal(SIGQUIT, SIG_DFL); 630 } 631 else { 632 fflush(stdout); 633 } 634 635 /* trick to force wait on output */ 636 stty(fileno(stdout), &otty); 637 } 638 639 void 640 onintr() 641 { 642 signal(SIGINT, onintr); 643 if (mailing) 644 unlink(fname); 645 if (sending) { 646 unlink(fname); 647 puts("--Killed--"); 648 exit(1); 649 } 650 if (printing) { 651 putchar('\n'); 652 if (hdrs) 653 exit(0); 654 sep = "Interrupt"; 655 if (newmsg) 656 fseek(newmsg, 0L, 2); 657 intrpflg = YES; 658 } 659 } 660 661 /* 662 * We have just gotten a susp. Suspend and prepare to resume. 663 */ 664 void 665 onsusp() 666 { 667 668 signal(SIGTSTP, SIG_DFL); 669 sigsetmask(0); 670 kill(0, SIGTSTP); 671 signal(SIGTSTP, onsusp); 672 if (!mailing) 673 longjmp(tstpbuf, 0); 674 } 675 676 linecnt(f) 677 FILE *f; 678 { 679 off_t oldpos = ftell(f); 680 int l = 0; 681 char lbuf[BUFSIZ]; 682 683 while (fgets(lbuf, sizeof lbuf, f)) 684 l++; 685 clearerr(f); 686 fseek(f, oldpos, 0); 687 return (l); 688 } 689 690 next(buf) 691 char *buf; 692 { 693 int i; 694 sscanf(buf, "%d", &i); 695 sprintf(buf, "Goto %d", i); 696 return(--i); 697 } 698 699 ask(prompt) 700 char *prompt; 701 { 702 char inch; 703 int n, cmsg; 704 off_t oldpos; 705 FILE *cpfrom, *cpto; 706 707 printf("%s ", prompt); 708 fflush(stdout); 709 intrpflg = NO; 710 (void) fgets(inbuf, sizeof inbuf, stdin); 711 if ((n = strlen(inbuf)) > 0 && inbuf[n - 1] == '\n') 712 inbuf[n - 1] = '\0'; 713 if (intrpflg) 714 inbuf[0] = 'x'; 715 716 /* 717 * Handle 'mail' and 'save' here. 718 */ 719 if ((inch = inbuf[0]) == 's' || inch == 'm') { 720 if (inbuf[1] == '-') 721 cmsg = prevmsg; 722 else if (isdigit(inbuf[1])) 723 cmsg = atoi(&inbuf[1]); 724 else 725 cmsg = msg; 726 sprintf(fname, "%s/%d", _PATH_MSGS, cmsg); 727 728 oldpos = ftell(newmsg); 729 730 cpfrom = fopen(fname, "r"); 731 if (!cpfrom) { 732 printf("Message %d not found\n", cmsg); 733 ask (prompt); 734 return; 735 } 736 737 if (inch == 's') { 738 in = nxtfld(inbuf); 739 if (*in) { 740 for (n=0; in[n] > ' '; n++) { /* sizeof fname? */ 741 fname[n] = in[n]; 742 } 743 fname[n] = NULL; 744 } 745 else 746 strcpy(fname, "Messages"); 747 } 748 else { 749 strcpy(fname, _PATH_TMP); 750 mktemp(fname); 751 sprintf(cmdbuf, _PATH_MAIL, fname); 752 mailing = YES; 753 } 754 cpto = fopen(fname, "a"); 755 if (!cpto) { 756 perror(fname); 757 mailing = NO; 758 fseek(newmsg, oldpos, 0); 759 ask(prompt); 760 return; 761 } 762 763 while (n = fread(inbuf, 1, sizeof inbuf, cpfrom)) 764 fwrite(inbuf, 1, n, cpto); 765 766 fclose(cpfrom); 767 fclose(cpto); 768 fseek(newmsg, oldpos, 0); /* reposition current message */ 769 if (inch == 's') 770 printf("Message %d saved in \"%s\"\n", cmsg, fname); 771 else { 772 system(cmdbuf); 773 unlink(fname); 774 mailing = NO; 775 } 776 ask(prompt); 777 } 778 } 779 780 gfrsub(infile) 781 FILE *infile; 782 { 783 off_t frompos; 784 785 seensubj = seenfrom = NO; 786 local = YES; 787 subj[0] = from[0] = date[0] = NULL; 788 789 /* 790 * Is this a normal message? 791 */ 792 if (fgets(inbuf, sizeof inbuf, infile)) { 793 if (strncmp(inbuf, "From", 4)==0) { 794 /* 795 * expected form starts with From 796 */ 797 seenfrom = YES; 798 frompos = ftell(infile); 799 ptr = from; 800 in = nxtfld(inbuf); 801 if (*in) while (*in && *in > ' ') { 802 if (*in == ':' || *in == '@' || *in == '!') 803 local = NO; 804 *ptr++ = *in++; 805 /* what about sizeof from ? */ 806 } 807 *ptr = NULL; 808 if (*(in = nxtfld(in))) 809 strncpy(date, in, sizeof date); 810 else { 811 date[0] = '\n'; 812 date[1] = NULL; 813 } 814 } 815 else { 816 /* 817 * not the expected form 818 */ 819 fseek(infile, 0L, 0); 820 return; 821 } 822 } 823 else 824 /* 825 * empty file ? 826 */ 827 return; 828 829 /* 830 * look for Subject line until EOF or a blank line 831 */ 832 while (fgets(inbuf, sizeof inbuf, infile) 833 && !(blankline = (inbuf[0] == '\n'))) { 834 /* 835 * extract Subject line 836 */ 837 if (!seensubj && strncmp(inbuf, "Subj", 4)==0) { 838 seensubj = YES; 839 frompos = ftell(infile); 840 strncpy(subj, nxtfld(inbuf), sizeof subj); 841 } 842 } 843 if (!blankline) 844 /* 845 * ran into EOF 846 */ 847 fseek(infile, frompos, 0); 848 849 if (!seensubj) 850 /* 851 * for possible use with Mail 852 */ 853 strncpy(subj, "(No Subject)\n", sizeof subj); 854 } 855 856 char * 857 nxtfld(s) 858 char *s; 859 { 860 if (*s) while (*s && *s > ' ') s++; /* skip over this field */ 861 if (*s) while (*s && *s <= ' ') s++; /* find start of next field */ 862 return (s); 863 } 864