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