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