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