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 ((uintptr_t)com->c_func == (uintptr_t)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 ((uintptr_t)com->c_func == (uintptr_t)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 contin(int s __unused) 688 { 689 if (shudprompt) 690 printf("%s", prompt); 691 fflush(stdout); 692 } 693 #endif 694 695 /* 696 * Branch here on hangup signal and simulate quit. 697 */ 698 void 699 hangup(int s __unused) 700 { 701 702 holdsigs(); 703 #ifdef OLD_BSD_SIGS 704 sigignore(SIGHUP); 705 #endif 706 if (edit) { 707 if (setjmp(srbuf)) 708 exit(rpterr); 709 edstop(0); 710 } else { 711 if (issysmbox) 712 Verhogen(); 713 if (value("exit") != NOSTR) 714 exit(1); 715 else 716 quit(0); 717 } 718 exit(rpterr); 719 } 720 721 /* 722 * Set the size of the message vector used to construct argument 723 * lists to message list functions. 724 */ 725 726 static void 727 setmsize(int sz) 728 { 729 730 if (msgvec != (int *)0) 731 free(msgvec); 732 if (sz < 1) 733 sz = 1; /* need at least one cell for terminating 0 */ 734 if ((msgvec = (int *) 735 calloc((unsigned)(sz + 1), sizeof (*msgvec))) == NULL) 736 panic("Failed to allocate memory for message vector"); 737 } 738 739 /* 740 * Find the correct command in the command table corresponding 741 * to the passed command "word" 742 */ 743 744 static const struct cmd * 745 lex(char word[]) 746 { 747 const struct cmd *cp; 748 749 for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++) 750 if (isprefix(word, cp->c_name)) 751 return (cp); 752 return (NONE); 753 } 754 755 /* 756 * Determine if as1 is a valid prefix of as2. 757 */ 758 static int 759 isprefix(char *as1, char *as2) 760 { 761 char *s1, *s2; 762 763 s1 = as1; 764 s2 = as2; 765 while (*s1++ == *s2) 766 if (*s2++ == '\0') 767 return (1); 768 return (*--s1 == '\0'); 769 } 770 771 /* 772 * The following gets called on receipt of a rubout. This is 773 * to abort printout of a command, mainly. 774 * Dispatching here when command() is inactive crashes rcv. 775 * Close all open files except 0, 1, 2, and the temporary. 776 * The special call to getuserid() is needed so it won't get 777 * annoyed about losing its open file. 778 * Also, unstack all source files. 779 */ 780 781 static int inithdr; /* am printing startup headers */ 782 783 void 784 stop(int s) 785 { 786 NODE *head; 787 788 noreset = 0; 789 if (!inithdr) 790 sawcom++; 791 inithdr = 0; 792 while (sourcing) 793 unstack(); 794 (void) getuserid((char *)0); 795 for (head = fplist; head != (NODE *)NULL; head = head->next) { 796 if (head->fp == stdin || head->fp == stdout) 797 continue; 798 if (head->fp == itf || head->fp == otf) 799 continue; 800 if (head->fp == stderr) 801 continue; 802 if (head->fp == semfp) 803 continue; 804 if (head->fp == pipef) { 805 npclose(pipef); 806 pipef = NULL; 807 continue; 808 } 809 fclose(head->fp); 810 } 811 if (image >= 0) { 812 close(image); 813 image = -1; 814 } 815 if (s) { 816 fprintf(stderr, gettext("Interrupt\n")); 817 fflush(stderr); 818 #ifdef OLD_BSD_SIGS 819 sigrelse(s); 820 #endif 821 } 822 longjmp(srbuf, 1); 823 } 824 825 /* 826 * Announce the presence of the current mailx version, 827 * give the message count, and print a header listing. 828 */ 829 830 #define GREETING "%s Type ? for help.\n" 831 832 void 833 announce(void) 834 { 835 int vec[2], mdot; 836 extern const char *const version; 837 838 if (!Hflag && value("quiet") == NOSTR) 839 printf(gettext(GREETING), version); 840 mdot = newfileinfo(1); 841 vec[0] = mdot; 842 vec[1] = 0; 843 dot = &message[mdot - 1]; 844 if (msgCount > 0 && !noheader) { 845 inithdr++; 846 headers(vec); 847 inithdr = 0; 848 } 849 } 850 851 /* 852 * Announce information about the file we are editing. 853 * Return a likely place to set dot. 854 */ 855 int 856 newfileinfo(int start) 857 { 858 struct message *mp; 859 int u, n, mdot, d, s; 860 char fname[BUFSIZ], zname[BUFSIZ], *ename; 861 862 if (Hflag) 863 return (1); /* fake it--return message 1 */ 864 for (mp = &message[start - 1]; mp < &message[msgCount]; mp++) 865 if ((mp->m_flag & (MNEW|MREAD)) == MNEW) 866 break; 867 if (mp >= &message[msgCount]) 868 for (mp = &message[start - 1]; mp < &message[msgCount]; mp++) 869 if ((mp->m_flag & MREAD) == 0) 870 break; 871 if (mp < &message[msgCount]) 872 mdot = mp - &message[0] + 1; 873 else 874 mdot = 1; 875 n = u = d = s = 0; 876 for (mp = &message[start - 1]; mp < &message[msgCount]; mp++) { 877 if (mp->m_flag & MNEW) 878 n++; 879 if ((mp->m_flag & MREAD) == 0) 880 u++; 881 if (mp->m_flag & MDELETED) 882 d++; 883 if (mp->m_flag & MSAVED) 884 s++; 885 } 886 ename = origname; 887 if (getfold(fname) >= 0) { 888 nstrcat(fname, sizeof (fname), "/"); 889 if (strncmp(fname, editfile, strlen(fname)) == 0) { 890 snprintf(zname, sizeof (zname), 891 "+%s", editfile + strlen(fname)); 892 ename = zname; 893 } 894 } 895 printf("\"%s\": ", ename); 896 if (msgCount == 1) 897 printf(gettext("1 message")); 898 else 899 printf(gettext("%d messages"), msgCount); 900 if (n > 0) 901 printf(gettext(" %d new"), n); 902 if (u-n > 0) 903 printf(gettext(" %d unread"), u); 904 if (d > 0) 905 printf(gettext(" %d deleted"), d); 906 if (s > 0) 907 printf(gettext(" %d saved"), s); 908 if (readonly) 909 printf(gettext(" [Read only]")); 910 printf("\n"); 911 return (mdot); 912 } 913 914 /* 915 * Print the current version number. 916 */ 917 918 int 919 pversion(char *s __unused) 920 { 921 printf("%s\n", version); 922 return (0); 923 } 924 925 /* 926 * Load a file of user definitions. 927 */ 928 void 929 load(char *name) 930 { 931 FILE *in, *oldin; 932 933 if ((in = fopen(name, "r")) == NULL) 934 return; 935 oldin = input; 936 input = in; 937 loading = 1; 938 sourcing = 1; 939 commands(); 940 loading = 0; 941 sourcing = 0; 942 input = oldin; 943 fclose(in); 944 } 945 946 /* 947 * Incorporate any new mail into the current session. 948 * 949 * XXX - Since autoinc works on "edited" files as well as the 950 * system mailbox, this probably ought to as well. 951 */ 952 953 int 954 inc(void) 955 { 956 FILE *ibuf; 957 int mdot; 958 struct stat stbuf; 959 int firstnewmsg = msgCount + 1; 960 961 if (edit) { 962 fprintf(stderr, gettext("Not in system mailbox\n")); 963 return (-1); 964 } 965 if (((ibuf = fopen(mailname, "r")) == NULL) || 966 (fstat(fileno(ibuf), &stbuf) < 0) || stbuf.st_size == 0L || 967 stbuf.st_size == mailsize || (stbuf.st_mode&S_IFMT) != S_IFREG) { 968 if (strrchr(mailname, '/') == NOSTR) 969 fprintf(stderr, gettext("No new mail.\n")); 970 else 971 fprintf(stderr, gettext("No new mail for %s\n"), 972 strrchr(mailname, '/')+1); 973 if (ibuf != NULL) 974 fclose(ibuf); 975 return (-1); 976 } 977 978 fseek(otf, 0L, 2); 979 holdsigs(); 980 if (issysmbox) 981 lockmail(); 982 fseek(ibuf, mailsize, 0); 983 mailsize = fsize(ibuf); 984 setptr(ibuf); 985 setmsize(msgCount); 986 fclose(ibuf); 987 if (issysmbox) 988 unlockmail(); 989 relsesigs(); 990 mdot = newfileinfo(firstnewmsg); 991 dot = &message[mdot - 1]; 992 sawcom = 0; 993 return (0); 994 } 995