1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * University Copyright- Copyright (c) 1982, 1986, 1988 32 * The Regents of the University of California 33 * All Rights Reserved 34 * 35 * University Acknowledgment- Portions of this document are derived from 36 * software developed by the University of California, Berkeley, and its 37 * contributors. 38 */ 39 40 #pragma ident "%Z%%M% %I% %E% SMI" 41 42 #include "rcv.h" 43 #include <locale.h> 44 45 /* 46 * mailx -- a modified version of a University of California at Berkeley 47 * mail program 48 * 49 * Lexical processing of commands. 50 */ 51 52 #ifdef SIGCONT 53 static void contin(int); 54 #endif 55 static int isprefix(char *as1, char *as2); 56 static const struct cmd *lex(char word[]); 57 static int Passeren(void); 58 static void setmsize(int sz); 59 60 /* 61 * Set up editing on the given file name. 62 * If isedit is true, we are considered to be editing the file, 63 * otherwise we are reading our mail which has signficance for 64 * mbox and so forth. 65 */ 66 67 int 68 setfile(char *name, int isedit) 69 { 70 FILE *ibuf; 71 int i; 72 static int shudclob; 73 static char efile[PATHSIZE]; 74 char fortest[128]; 75 struct stat stbuf; 76 int exrc = 1; 77 int rc = -1; 78 int fd = -1; 79 80 if (!isedit && issysmbox) 81 lockmail(); 82 if (stat(name, &stbuf) < 0 && errno == EOVERFLOW) { 83 fprintf(stderr, gettext("mailbox %s is too large to" 84 " accept incoming mail\n"), name); 85 goto doret; 86 } 87 if ((ibuf = fopen(name, "r")) == NULL) { 88 extern int errno; 89 int sverrno = errno; 90 int filethere = (access(name, 0) == 0); 91 errno = sverrno; 92 if (exitflg) 93 goto doexit; /* no mail, return error */ 94 if (isedit || filethere) 95 perror(name); 96 else if (!Hflag) { 97 char *f = strrchr(name, '/'); 98 if (f == NOSTR) 99 fprintf(stderr, gettext("No mail.\n")); 100 else 101 fprintf(stderr, gettext("No mail for %s\n"), 102 f+1); 103 } 104 goto doret; 105 } 106 fstat(fileno(ibuf), &stbuf); 107 if (stbuf.st_size == 0L || (stbuf.st_mode&S_IFMT) != S_IFREG) { 108 if (exitflg) 109 goto doexit; /* no mail, return error */ 110 if (isedit) 111 if (stbuf.st_size == 0L) 112 fprintf(stderr, gettext("%s: empty file\n"), 113 name); 114 else 115 fprintf(stderr, 116 gettext("%s: not a regular file\n"), name); 117 else if (!Hflag) { 118 if (strrchr(name, '/') == NOSTR) 119 fprintf(stderr, gettext("No mail.\n")); 120 else 121 fprintf(stderr, gettext("No mail for %s\n"), 122 strrchr(name, '/') + 1); 123 } 124 fclose(ibuf); 125 goto doret; 126 } 127 128 if (fgets(fortest, sizeof (fortest), ibuf) == NULL) { 129 perror(gettext("mailx: Unable to read from mail file")); 130 goto doexit; 131 } 132 133 fseek(ibuf, (long)(BUFSIZ+1), 0); /* flush input buffer */ 134 fseek(ibuf, 0L, 0); 135 if (strncmp(fortest, "Forward to ", 11) == 0) { 136 if (exitflg) 137 goto doexit; /* no mail, return error */ 138 fprintf(stderr, gettext("Your mail is being forwarded to %s"), 139 fortest+11); 140 fclose(ibuf); 141 goto doret; 142 } 143 if (exitflg) { 144 exrc = 0; 145 goto doexit; /* there is mail, return success */ 146 } 147 148 /* 149 * Looks like all will be well. Must hold signals 150 * while we are reading the new file, else we will ruin 151 * the message[] data structure. 152 * Copy the messages into /tmp and set pointers. 153 */ 154 155 holdsigs(); 156 if (shudclob) { 157 /* 158 * Now that we know we can switch to the new file 159 * it's safe to close out the current file. 160 * 161 * If we're switching to the file we are currently 162 * editing, don't allow it to be removed as a side 163 * effect of closing it out. 164 */ 165 if (edit) 166 edstop(strcmp(editfile, name) == 0); 167 else { 168 quit(strcmp(editfile, name) == 0); 169 if (issysmbox) 170 Verhogen(); 171 } 172 fflush(stdout); 173 fclose(itf); 174 fclose(otf); 175 free(message); 176 space = 0; 177 } 178 readonly = 0; 179 if (!isedit && issysmbox && !Hflag) 180 readonly = Passeren() == -1; 181 lock(ibuf, "r", 1); 182 fstat(fileno(ibuf), &stbuf); 183 utimep->actime = stbuf.st_atime; 184 utimep->modtime = stbuf.st_mtime; 185 186 if (!readonly) 187 if ((i = open(name, O_WRONLY)) < 0) 188 readonly++; 189 else 190 close(i); 191 shudclob = 1; 192 edit = isedit; 193 nstrcpy(efile, PATHSIZE, name); 194 editfile = efile; 195 #ifdef notdef 196 if (name != mailname) 197 nstrcpy(mailname, PATHSIZE, name); 198 #endif 199 mailsize = fsize(ibuf); 200 if ((fd = open(tempMesg, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 || 201 (otf = fdopen(fd, "w")) == NULL) { 202 perror(tempMesg); 203 if (!edit && issysmbox) 204 Verhogen(); 205 goto doexit; 206 } 207 if ((itf = fopen(tempMesg, "r")) == NULL) { 208 perror(tempMesg); 209 if (!edit && issysmbox) 210 Verhogen(); 211 goto doexit; 212 } 213 removefile(tempMesg); 214 setptr(ibuf); 215 setmsize(msgCount); 216 fclose(ibuf); 217 relsesigs(); 218 sawcom = 0; 219 rc = 0; 220 221 doret: 222 if (!isedit && issysmbox) 223 unlockmail(); 224 return (rc); 225 226 doexit: 227 if (!isedit && issysmbox) 228 unlockmail(); 229 exit(exrc ? exrc : rpterr); 230 /* NOTREACHED */ 231 } 232 233 /* global to semaphores */ 234 static char semfn[128]; 235 static FILE *semfp = NULL; 236 237 /* 238 * return -1 if file is already being read, 0 otherwise 239 */ 240 static int 241 Passeren(void) 242 { 243 char *home; 244 245 if ((home = getenv("HOME")) == NULL) 246 return (0); 247 snprintf(semfn, sizeof (semfn), "%s%s", home, "/.Maillock"); 248 if ((semfp = fopen(semfn, "w")) == NULL) { 249 fprintf(stderr, 250 gettext("WARNING: Can't open mail lock file (%s).\n"), semfn); 251 fprintf(stderr, 252 gettext("\t Assuming you are not already reading mail.\n")); 253 return (0); 254 } 255 if (lock(semfp, "w", 0) < 0) { 256 if (errno == ENOLCK) { 257 fprintf(stderr, 258 gettext("WARNING: Unable to acquire mail lock, no record locks available.\n")); 259 fprintf(stderr, 260 gettext("\t Assuming you are not already reading mail.\n")); 261 return (0); 262 } 263 fprintf(stderr, 264 gettext("WARNING: You are already reading mail.\n")); 265 fprintf(stderr, 266 gettext("\t This instance of mail is read only.\n")); 267 fclose(semfp); 268 semfp = NULL; 269 return (-1); 270 } 271 return (0); 272 } 273 274 void 275 Verhogen(void) 276 { 277 if (semfp != NULL) { 278 unlink(semfn); 279 fclose(semfp); 280 } 281 } 282 283 /* 284 * Interpret user commands one by one. If standard input is not a tty, 285 * print no prompt. 286 */ 287 288 static int *msgvec; 289 static int shudprompt; 290 291 void 292 commands(void) 293 { 294 int eofloop; 295 register int n; 296 char linebuf[LINESIZE]; 297 char line[LINESIZE]; 298 struct stat minfo; 299 FILE *ibuf; 300 301 #ifdef SIGCONT 302 sigset(SIGCONT, SIG_DFL); 303 #endif 304 if (rcvmode && !sourcing) { 305 if (sigset(SIGINT, SIG_IGN) != SIG_IGN) 306 sigset(SIGINT, stop); 307 if (sigset(SIGHUP, SIG_IGN) != SIG_IGN) 308 sigset(SIGHUP, hangup); 309 } 310 for (;;) { 311 setjmp(srbuf); 312 313 /* 314 * Print the prompt, if needed. Clear out 315 * string space, and flush the output. 316 */ 317 318 if (!rcvmode && !sourcing) 319 return; 320 eofloop = 0; 321 top: 322 if ((shudprompt = (intty && !sourcing)) != 0) { 323 if (prompt == NOSTR) { 324 if ((int)value("bsdcompat")) 325 prompt = "& "; 326 else 327 prompt = ""; 328 } 329 #ifdef SIGCONT 330 sigset(SIGCONT, contin); 331 #endif 332 if (intty && value("autoinc") && 333 stat(editfile, &minfo) >= 0 && 334 minfo.st_size > mailsize) { 335 int OmsgCount, i; 336 337 OmsgCount = msgCount; 338 fseek(otf, 0L, 2); 339 holdsigs(); 340 if (!edit && issysmbox) 341 lockmail(); 342 if ((ibuf = fopen(editfile, "r")) == NULL) { 343 fprintf(stderr, 344 gettext("Can't reopen %s\n"), 345 editfile); 346 if (!edit && issysmbox) 347 unlockmail(); 348 exit(1); 349 /* NOTREACHED */ 350 } 351 if (edit || !issysmbox) 352 lock(ibuf, "r", 1); 353 fseek(ibuf, mailsize, 0); 354 mailsize = fsize(ibuf); 355 setptr(ibuf); 356 setmsize(msgCount); 357 fclose(ibuf); 358 if (!edit && issysmbox) 359 unlockmail(); 360 if (msgCount-OmsgCount > 0) { 361 printf(gettext( 362 "New mail has arrived.\n")); 363 if (msgCount - OmsgCount == 1) 364 printf(gettext( 365 "Loaded 1 new message\n")); 366 else 367 printf(gettext( 368 "Loaded %d new messages\n"), 369 msgCount-OmsgCount); 370 if (value("header") != NOSTR) 371 for (i = OmsgCount+1; 372 i <= msgCount; i++) { 373 printhead(i); 374 sreset(); 375 } 376 } 377 relsesigs(); 378 } 379 printf("%s", prompt); 380 } 381 flush(); 382 sreset(); 383 384 /* 385 * Read a line of commands from the current input 386 * and handle end of file specially. 387 */ 388 389 n = 0; 390 linebuf[0] = '\0'; 391 for (;;) { 392 if (readline(input, line) <= 0) { 393 if (n != 0) 394 break; 395 if (loading) 396 return; 397 if (sourcing) { 398 unstack(); 399 goto more; 400 } 401 if (value("ignoreeof") != NOSTR && shudprompt) { 402 if (++eofloop < 25) { 403 printf(gettext( 404 "Use \"quit\" to quit.\n")); 405 goto top; 406 } 407 } 408 return; 409 } 410 if ((n = strlen(line)) == 0) 411 break; 412 n--; 413 if (line[n] != '\\') 414 break; 415 line[n++] = ' '; 416 if (n > LINESIZE - (int)strlen(linebuf) - 1) 417 break; 418 strcat(linebuf, line); 419 } 420 n = LINESIZE - strlen(linebuf) - 1; 421 if ((int)strlen(line) > n) { 422 printf(gettext( 423 "Line plus continuation line too long:\n\t%s\n\nplus\n\t%s\n"), 424 linebuf, line); 425 if (loading) 426 return; 427 if (sourcing) { 428 unstack(); 429 goto more; 430 } 431 return; 432 } 433 strncat(linebuf, line, n); 434 #ifdef SIGCONT 435 sigset(SIGCONT, SIG_DFL); 436 #endif 437 if (execute(linebuf, 0)) 438 return; 439 more:; 440 } 441 } 442 443 /* 444 * Execute a single command. If the command executed 445 * is "quit," then return non-zero so that the caller 446 * will know to return back to main, if he cares. 447 * Contxt is non-zero if called while composing mail. 448 */ 449 450 int 451 execute(char linebuf[], int contxt) 452 { 453 char word[LINESIZE]; 454 char *arglist[MAXARGC]; 455 const struct cmd *com; 456 register char *cp, *cp2; 457 register int c, e; 458 int muvec[2]; 459 460 /* 461 * Strip the white space away from the beginning 462 * of the command, then scan out a word, which 463 * consists of anything except digits and white space. 464 * 465 * Handle |, ! and # differently to get the correct 466 * lexical conventions. 467 */ 468 469 cp = linebuf; 470 while (any(*cp, " \t")) 471 cp++; 472 cp2 = word; 473 if (any(*cp, "!|#")) 474 *cp2++ = *cp++; 475 else 476 while (*cp && !any(*cp, " \t0123456789$^.:/-+*'\"")) 477 *cp2++ = *cp++; 478 *cp2 = '\0'; 479 480 /* 481 * Look up the command; if not found, complain. 482 * Normally, a blank command would map to the 483 * first command in the table; while sourcing, 484 * however, we ignore blank lines to eliminate 485 * confusion. 486 */ 487 488 if (sourcing && equal(word, "")) 489 return (0); 490 com = lex(word); 491 if (com == NONE) { 492 fprintf(stderr, gettext("Unknown command: \"%s\"\n"), word); 493 if (loading) { 494 cond = CANY; 495 return (1); 496 } 497 if (sourcing) { 498 cond = CANY; 499 unstack(); 500 } 501 return (0); 502 } 503 504 /* 505 * See if we should execute the command -- if a conditional 506 * we always execute it, otherwise, check the state of cond. 507 */ 508 509 if ((com->c_argtype & F) == 0) 510 if (cond == CRCV && !rcvmode || cond == CSEND && rcvmode || 511 cond == CTTY && !intty || cond == CNOTTY && intty) 512 return (0); 513 514 /* 515 * Special case so that quit causes a return to 516 * main, who will call the quit code directly. 517 * If we are in a source file, just unstack. 518 */ 519 520 if (com->c_func == (int (*)(void *))edstop) { 521 if (sourcing) { 522 if (loading) 523 return (1); 524 unstack(); 525 return (0); 526 } 527 return (1); 528 } 529 530 /* 531 * Process the arguments to the command, depending 532 * on the type he expects. Default to an error. 533 * If we are sourcing an interactive command, it's 534 * an error. 535 */ 536 537 if (!rcvmode && (com->c_argtype & M) == 0) { 538 fprintf(stderr, 539 gettext("May not execute \"%s\" while sending\n"), 540 com->c_name); 541 if (loading) 542 return (1); 543 if (sourcing) 544 unstack(); 545 return (0); 546 } 547 if (sourcing && com->c_argtype & I) { 548 fprintf(stderr, 549 gettext("May not execute \"%s\" while sourcing\n"), 550 com->c_name); 551 rpterr = 1; 552 if (loading) 553 return (1); 554 unstack(); 555 return (0); 556 } 557 if (readonly && com->c_argtype & W) { 558 fprintf(stderr, gettext( 559 "May not execute \"%s\" -- message file is read only\n"), 560 com->c_name); 561 if (loading) 562 return (1); 563 if (sourcing) 564 unstack(); 565 return (0); 566 } 567 if (contxt && com->c_argtype & R) { 568 fprintf(stderr, gettext("Cannot recursively invoke \"%s\"\n"), 569 com->c_name); 570 return (0); 571 } 572 e = 1; 573 switch (com->c_argtype & ~(F|P|I|M|T|W|R)) { 574 case MSGLIST: 575 /* 576 * A message list defaulting to nearest forward 577 * legal message. 578 */ 579 if (msgvec == 0) { 580 fprintf(stderr, 581 gettext("Illegal use of \"message list\"\n")); 582 return (-1); 583 } 584 if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0) 585 break; 586 if (c == 0) 587 if (msgCount == 0) 588 *msgvec = NULL; 589 else { 590 *msgvec = first(com->c_msgflag, 591 com->c_msgmask); 592 msgvec[1] = NULL; 593 } 594 if (*msgvec == NULL) { 595 fprintf(stderr, gettext("No applicable messages\n")); 596 break; 597 } 598 e = (*com->c_func)(msgvec); 599 break; 600 601 case NDMLIST: 602 /* 603 * A message operand with no defaults, but no error 604 * if none exists. There will be an error if the 605 * msgvec pointer is of zero value. 606 */ 607 if (msgvec == 0) { 608 fprintf(stderr, 609 gettext("Illegal use of \"message operand\"\n")); 610 return (-1); 611 } 612 if (getmessage(cp, msgvec, com->c_msgflag) < 0) 613 break; 614 e = (*com->c_func)(msgvec); 615 break; 616 617 case STRLIST: 618 /* 619 * Just the straight string, with 620 * leading blanks removed. 621 */ 622 while (any(*cp, " \t")) 623 cp++; 624 e = (*com->c_func)(cp); 625 break; 626 627 case RAWLIST: 628 /* 629 * A vector of strings, in shell style. 630 */ 631 if ((c = getrawlist(cp, arglist, 632 sizeof (arglist) / sizeof (*arglist))) < 0) 633 break; 634 if (c < com->c_minargs) { 635 fprintf(stderr, 636 gettext("%s requires at least %d arg(s)\n"), 637 com->c_name, com->c_minargs); 638 break; 639 } 640 if (c > com->c_maxargs) { 641 fprintf(stderr, 642 gettext("%s takes no more than %d arg(s)\n"), 643 com->c_name, com->c_maxargs); 644 break; 645 } 646 e = (*com->c_func)(arglist); 647 break; 648 649 case NOLIST: 650 /* 651 * Just the constant zero, for exiting, 652 * eg. 653 */ 654 e = (*com->c_func)(0); 655 break; 656 657 default: 658 panic("Unknown argtype"); 659 } 660 661 /* 662 * Exit the current source file on 663 * error. 664 */ 665 666 if (e && loading) 667 return (1); 668 if (e && sourcing) 669 unstack(); 670 if (com->c_func == (int (*)(void *))edstop) 671 return (1); 672 if (value("autoprint") != NOSTR && com->c_argtype & P) 673 if ((dot->m_flag & MDELETED) == 0) { 674 muvec[0] = dot - &message[0] + 1; 675 muvec[1] = 0; 676 type(muvec); 677 } 678 if (!sourcing && (com->c_argtype & T) == 0) 679 sawcom = 1; 680 return (0); 681 } 682 683 #ifdef SIGCONT 684 /* 685 * When we wake up after ^Z, reprint the prompt. 686 */ 687 static void 688 #ifdef __cplusplus 689 contin(int) 690 #else 691 /* ARGSUSED */ 692 contin(int s) 693 #endif 694 { 695 if (shudprompt) 696 printf("%s", prompt); 697 fflush(stdout); 698 } 699 #endif 700 701 /* 702 * Branch here on hangup signal and simulate quit. 703 */ 704 void 705 #ifdef __cplusplus 706 hangup(int) 707 #else 708 /* ARGSUSED */ 709 hangup(int s) 710 #endif 711 { 712 713 holdsigs(); 714 #ifdef OLD_BSD_SIGS 715 sigignore(SIGHUP); 716 #endif 717 if (edit) { 718 if (setjmp(srbuf)) 719 exit(rpterr); 720 edstop(0); 721 } else { 722 if (issysmbox) 723 Verhogen(); 724 if (value("exit") != NOSTR) 725 exit(1); 726 else 727 quit(0); 728 } 729 exit(rpterr); 730 } 731 732 /* 733 * Set the size of the message vector used to construct argument 734 * lists to message list functions. 735 */ 736 737 static void 738 setmsize(int sz) 739 { 740 741 if (msgvec != (int *)0) 742 free(msgvec); 743 if (sz < 1) 744 sz = 1; /* need at least one cell for terminating 0 */ 745 if ((msgvec = (int *) 746 calloc((unsigned)(sz + 1), sizeof (*msgvec))) == NULL) 747 panic("Failed to allocate memory for message vector"); 748 } 749 750 /* 751 * Find the correct command in the command table corresponding 752 * to the passed command "word" 753 */ 754 755 static const struct cmd * 756 lex(char word[]) 757 { 758 register const struct cmd *cp; 759 760 for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++) 761 if (isprefix(word, cp->c_name)) 762 return (cp); 763 return (NONE); 764 } 765 766 /* 767 * Determine if as1 is a valid prefix of as2. 768 */ 769 static int 770 isprefix(char *as1, char *as2) 771 { 772 register char *s1, *s2; 773 774 s1 = as1; 775 s2 = as2; 776 while (*s1++ == *s2) 777 if (*s2++ == '\0') 778 return (1); 779 return (*--s1 == '\0'); 780 } 781 782 /* 783 * The following gets called on receipt of a rubout. This is 784 * to abort printout of a command, mainly. 785 * Dispatching here when command() is inactive crashes rcv. 786 * Close all open files except 0, 1, 2, and the temporary. 787 * The special call to getuserid() is needed so it won't get 788 * annoyed about losing its open file. 789 * Also, unstack all source files. 790 */ 791 792 static int inithdr; /* am printing startup headers */ 793 794 void 795 stop(int s) 796 { 797 register NODE *head; 798 799 noreset = 0; 800 if (!inithdr) 801 sawcom++; 802 inithdr = 0; 803 while (sourcing) 804 unstack(); 805 (void) getuserid((char *)0); 806 for (head = fplist; head != (NODE *)NULL; head = head->next) { 807 if (head->fp == stdin || head->fp == stdout) 808 continue; 809 if (head->fp == itf || head->fp == otf) 810 continue; 811 if (head->fp == stderr) 812 continue; 813 if (head->fp == semfp) 814 continue; 815 if (head->fp == pipef) { 816 npclose(pipef); 817 pipef = NULL; 818 continue; 819 } 820 fclose(head->fp); 821 } 822 if (image >= 0) { 823 close(image); 824 image = -1; 825 } 826 if (s) { 827 fprintf(stderr, gettext("Interrupt\n")); 828 fflush(stderr); 829 #ifdef OLD_BSD_SIGS 830 sigrelse(s); 831 #endif 832 } 833 longjmp(srbuf, 1); 834 } 835 836 /* 837 * Announce the presence of the current mailx version, 838 * give the message count, and print a header listing. 839 */ 840 841 #define GREETING "%s Type ? for help.\n" 842 843 void 844 announce(void) 845 { 846 int vec[2], mdot; 847 extern const char *const version; 848 849 if (!Hflag && value("quiet") == NOSTR) 850 printf(gettext(GREETING), version); 851 mdot = newfileinfo(1); 852 vec[0] = mdot; 853 vec[1] = 0; 854 dot = &message[mdot - 1]; 855 if (msgCount > 0 && !noheader) { 856 inithdr++; 857 headers(vec); 858 inithdr = 0; 859 } 860 } 861 862 /* 863 * Announce information about the file we are editing. 864 * Return a likely place to set dot. 865 */ 866 int 867 newfileinfo(int start) 868 { 869 register struct message *mp; 870 register int u, n, mdot, d, s; 871 char fname[BUFSIZ], zname[BUFSIZ], *ename; 872 873 if (Hflag) 874 return (1); /* fake it--return message 1 */ 875 for (mp = &message[start - 1]; mp < &message[msgCount]; mp++) 876 if ((mp->m_flag & (MNEW|MREAD)) == MNEW) 877 break; 878 if (mp >= &message[msgCount]) 879 for (mp = &message[start - 1]; mp < &message[msgCount]; mp++) 880 if ((mp->m_flag & MREAD) == 0) 881 break; 882 if (mp < &message[msgCount]) 883 mdot = mp - &message[0] + 1; 884 else 885 mdot = 1; 886 n = u = d = s = 0; 887 for (mp = &message[start - 1]; mp < &message[msgCount]; mp++) { 888 if (mp->m_flag & MNEW) 889 n++; 890 if ((mp->m_flag & MREAD) == 0) 891 u++; 892 if (mp->m_flag & MDELETED) 893 d++; 894 if (mp->m_flag & MSAVED) 895 s++; 896 } 897 ename = origname; 898 if (getfold(fname) >= 0) { 899 nstrcat(fname, sizeof (fname), "/"); 900 if (strncmp(fname, editfile, strlen(fname)) == 0) { 901 snprintf(zname, sizeof (zname), 902 "+%s", editfile + strlen(fname)); 903 ename = zname; 904 } 905 } 906 printf("\"%s\": ", ename); 907 if (msgCount == 1) 908 printf(gettext("1 message")); 909 else 910 printf(gettext("%d messages"), msgCount); 911 if (n > 0) 912 printf(gettext(" %d new"), n); 913 if (u-n > 0) 914 printf(gettext(" %d unread"), u); 915 if (d > 0) 916 printf(gettext(" %d deleted"), d); 917 if (s > 0) 918 printf(gettext(" %d saved"), s); 919 if (readonly) 920 printf(gettext(" [Read only]")); 921 printf("\n"); 922 return (mdot); 923 } 924 925 /* 926 * Print the current version number. 927 */ 928 929 int 930 #ifdef __cplusplus 931 pversion(char *) 932 #else 933 /* ARGSUSED */ 934 pversion(char *s) 935 #endif 936 { 937 printf("%s\n", version); 938 return (0); 939 } 940 941 /* 942 * Load a file of user definitions. 943 */ 944 void 945 load(char *name) 946 { 947 register FILE *in, *oldin; 948 949 if ((in = fopen(name, "r")) == NULL) 950 return; 951 oldin = input; 952 input = in; 953 loading = 1; 954 sourcing = 1; 955 commands(); 956 loading = 0; 957 sourcing = 0; 958 input = oldin; 959 fclose(in); 960 } 961 962 /* 963 * Incorporate any new mail into the current session. 964 * 965 * XXX - Since autoinc works on "edited" files as well as the 966 * system mailbox, this probably ought to as well. 967 */ 968 969 int 970 inc(void) 971 { 972 FILE *ibuf; 973 int mdot; 974 struct stat stbuf; 975 int firstnewmsg = msgCount + 1; 976 977 if (edit) { 978 fprintf(stderr, gettext("Not in system mailbox\n")); 979 return (-1); 980 } 981 if (((ibuf = fopen(mailname, "r")) == NULL) || 982 (fstat(fileno(ibuf), &stbuf) < 0) || stbuf.st_size == 0L || 983 stbuf.st_size == mailsize || (stbuf.st_mode&S_IFMT) != S_IFREG) { 984 if (strrchr(mailname, '/') == NOSTR) 985 fprintf(stderr, gettext("No new mail.\n")); 986 else 987 fprintf(stderr, gettext("No new mail for %s\n"), 988 strrchr(mailname, '/')+1); 989 if (ibuf != NULL) 990 fclose(ibuf); 991 return (-1); 992 } 993 994 fseek(otf, 0L, 2); 995 holdsigs(); 996 if (issysmbox) 997 lockmail(); 998 fseek(ibuf, mailsize, 0); 999 mailsize = fsize(ibuf); 1000 setptr(ibuf); 1001 setmsize(msgCount); 1002 fclose(ibuf); 1003 if (issysmbox) 1004 unlockmail(); 1005 relsesigs(); 1006 mdot = newfileinfo(firstnewmsg); 1007 dot = &message[mdot - 1]; 1008 sawcom = 0; 1009 return (0); 1010 } 1011