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