1: /* 2: * Copyright (c) 1980 Regents of the University of California. 3: * All rights reserved. 4: * 5: * Redistribution and use in source and binary forms, with or without 6: * modification, are permitted provided that the following conditions 7: * are met: 8: * 1. Redistributions of source code must retain the above copyright 9: * notice, this list of conditions and the following disclaimer. 10: * 2. Redistributions in binary form must reproduce the above copyright 11: * notice, this list of conditions and the following disclaimer in the 12: * documentation and/or other materials provided with the distribution. 13: * 3. All advertising materials mentioning features or use of this software 14: * must display the following acknowledgement: 15: * This product includes software developed by the University of 16: * California, Berkeley and its contributors. 17: * 4. Neither the name of the University nor the names of its contributors 18: * may be used to endorse or promote products derived from this software 19: * without specific prior written permission. 20: * 21: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31: * SUCH DAMAGE. 32: */ 33: 34: #if !defined(lint) && defined(DOSCCS) 35: static char sccsid[] = "@(#)lex.c 5.23 (Berkeley) 4/1/91"; 36: #endif 37: 38: #include "rcv.h" 39: #include <sys/stat.h> 40: #include <errno.h> 41: 42: /* 43: * Mail -- a mail program 44: * 45: * Lexical processing of commands. 46: */ 47: 48: char *prompt = "& "; 49: 50: /* 51: * Set up editing on the given file name. 52: * If the first character of name is %, we are considered to be 53: * editing the file, otherwise we are reading our mail which has 54: * signficance for mbox and so forth. 55: */ 56: setfile(name) 57: char *name; 58: { 59: FILE *ibuf; 60: int i; 61: struct stat stb; 62: char isedit = *name != '%'; 63: char *who = name[1] ? name + 1 : myname; 64: static int shudclob; 65: extern char tempMesg[]; 66: extern int errno; 67: 68: if ((name = expand(name)) == NOSTR) 69: return -1; 70: 71: if ((ibuf = Fopen(name, "r")) == NULL) { 72: if (!isedit && errno == ENOENT) 73: goto nomail; 74: perror(name); 75: return(-1); 76: } 77: 78: if (fstat(fileno(ibuf), &stb) < 0) { 79: perror("fstat"); 80: Fclose(ibuf); 81: return (-1); 82: } 83: 84: switch (stb.st_mode & S_IFMT) { 85: case S_IFDIR: 86: Fclose(ibuf); 87: errno = EISDIR; 88: perror(name); 89: return (-1); 90: 91: case S_IFREG: 92: break; 93: 94: default: 95: Fclose(ibuf); 96: errno = EINVAL; 97: perror(name); 98: return (-1); 99: } 100: 101: /* 102: * Looks like all will be well. We must now relinquish our 103: * hold on the current set of stuff. Must hold signals 104: * while we are reading the new file, else we will ruin 105: * the message[] data structure. 106: */ 107: 108: holdsigs(); 109: if (shudclob) 110: quit(); 111: 112: /* 113: * Copy the messages into /tmp 114: * and set pointers. 115: */ 116: 117: readonly = 0; 118: if ((i = open(name, 1)) < 0) 119: readonly++; 120: else 121: close(i); 122: if (shudclob) { 123: fclose(itf); 124: fclose(otf); 125: } 126: shudclob = 1; 127: edit = isedit; 128: strcpy(prevfile, mailname); 129: if (name != mailname) 130: strcpy(mailname, name); 131: mailsize = fsize(ibuf); 132: if ((otf = fopen(tempMesg, "w")) == NULL) { 133: perror(tempMesg); 134: exit(1); 135: } 136: if ((itf = fopen(tempMesg, "r")) == NULL) { 137: perror(tempMesg); 138: exit(1); 139: } 140: rm(tempMesg); 141: setptr(ibuf); 142: setmsize(msgCount); 143: Fclose(ibuf); 144: relsesigs(); 145: sawcom = 0; 146: if (!edit && msgCount == 0) { 147: nomail: 148: fprintf(stderr, "No mail for %s\n", who); 149: return -1; 150: } 151: return(0); 152: } 153: 154: int *msgvec; 155: int reset_on_stop; /* do a reset() if stopped */ 156: 157: /* 158: * Interpret user commands one by one. If standard input is not a tty, 159: * print no prompt. 160: */ 161: commands() 162: { 163: int eofloop = 0; 164: register int n; 165: char linebuf[LINESIZE]; 166: void intr(), stop(), hangup(); 167: 168: if (!sourcing) { 169: if (signal(SIGINT, SIG_IGN) != SIG_IGN) 170: signal(SIGINT, intr); 171: if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 172: signal(SIGHUP, hangup); 173: signal(SIGTSTP, stop); 174: signal(SIGTTOU, stop); 175: signal(SIGTTIN, stop); 176: } 177: setexit(); 178: for (;;) { 179: /* 180: * Print the prompt, if needed. Clear out 181: * string space, and flush the output. 182: */ 183: if (!sourcing && value("interactive") != NOSTR) { 184: reset_on_stop = 1; 185: printf(prompt); 186: } 187: fflush(stdout); 188: sreset(); 189: /* 190: * Read a line of commands from the current input 191: * and handle end of file specially. 192: */ 193: n = 0; 194: for (;;) { 195: if (readline(input, &linebuf[n], LINESIZE - n) < 0) { 196: if (n == 0) 197: n = -1; 198: break; 199: } 200: if ((n = strlen(linebuf)) == 0) 201: break; 202: n--; 203: if (linebuf[n] != '\\') 204: break; 205: linebuf[n++] = ' '; 206: } 207: reset_on_stop = 0; 208: if (n < 0) { 209: /* eof */ 210: if (loading) 211: break; 212: if (sourcing) { 213: unstack(); 214: continue; 215: } 216: if (value("interactive") != NOSTR && 217: value("ignoreeof") != NOSTR && 218: ++eofloop < 25) { 219: printf("Use \"quit\" to quit.\n"); 220: continue; 221: } 222: break; 223: } 224: eofloop = 0; 225: if (execute(linebuf, 0)) 226: break; 227: } 228: } 229: 230: /* 231: * Execute a single command. 232: * Command functions return 0 for success, 1 for error, and -1 233: * for abort. A 1 or -1 aborts a load or source. A -1 aborts 234: * the interactive command loop. 235: * Contxt is non-zero if called while composing mail. 236: */ 237: execute(linebuf, contxt) 238: char linebuf[]; 239: { 240: char word[LINESIZE]; 241: char *arglist[MAXARGC]; 242: struct cmd *com; 243: register char *cp, *cp2; 244: register int c; 245: int muvec[2]; 246: int e = 1; 247: 248: /* 249: * Strip the white space away from the beginning 250: * of the command, then scan out a word, which 251: * consists of anything except digits and white space. 252: * 253: * Handle ! escapes differently to get the correct 254: * lexical conventions. 255: */ 256: 257: for (cp = linebuf; isspace(*cp); cp++) 258: ; 259: if (*cp == '!') { 260: if (sourcing) { 261: printf("Can't \"!\" while sourcing\n"); 262: goto out; 263: } 264: shell(cp+1); 265: return(0); 266: } 267: cp2 = word; 268: while (*cp && index(" \t0123456789$^.:/-+*'\"", *cp) == NOSTR) 269: *cp2++ = *cp++; 270: *cp2 = '\0'; 271: 272: /* 273: * Look up the command; if not found, bitch. 274: * Normally, a blank command would map to the 275: * first command in the table; while sourcing, 276: * however, we ignore blank lines to eliminate 277: * confusion. 278: */ 279: 280: if (sourcing && *word == '\0') 281: return(0); 282: com = lex(word); 283: if (com == NONE) { 284: printf("Unknown command: \"%s\"\n", word); 285: goto out; 286: } 287: 288: /* 289: * See if we should execute the command -- if a conditional 290: * we always execute it, otherwise, check the state of cond. 291: */ 292: 293: if ((com->c_argtype & F) == 0) 294: if (cond == CRCV && !rcvmode || cond == CSEND && rcvmode) 295: return(0); 296: 297: /* 298: * Process the arguments to the command, depending 299: * on the type he expects. Default to an error. 300: * If we are sourcing an interactive command, it's 301: * an error. 302: */ 303: 304: if (!rcvmode && (com->c_argtype & M) == 0) { 305: printf("May not execute \"%s\" while sending\n", 306: com->c_name); 307: goto out; 308: } 309: if (sourcing && com->c_argtype & I) { 310: printf("May not execute \"%s\" while sourcing\n", 311: com->c_name); 312: goto out; 313: } 314: if (readonly && com->c_argtype & W) { 315: printf("May not execute \"%s\" -- message file is read only\n", 316: com->c_name); 317: goto out; 318: } 319: if (contxt && com->c_argtype & R) { 320: printf("Cannot recursively invoke \"%s\"\n", com->c_name); 321: goto out; 322: } 323: switch (com->c_argtype & ~(F|P|I|M|T|W|R)) { 324: case MSGLIST: 325: /* 326: * A message list defaulting to nearest forward 327: * legal message. 328: */ 329: if (msgvec == 0) { 330: printf("Illegal use of \"message list\"\n"); 331: break; 332: } 333: if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0) 334: break; 335: if (c == 0) { 336: *msgvec = first(com->c_msgflag, 337: com->c_msgmask); 338: msgvec[1] = NULL; 339: } 340: if (*msgvec == NULL) { 341: printf("No applicable messages\n"); 342: break; 343: } 344: e = (*com->c_func)(msgvec); 345: break; 346: 347: case NDMLIST: 348: /* 349: * A message list with no defaults, but no error 350: * if none exist. 351: */ 352: if (msgvec == 0) { 353: printf("Illegal use of \"message list\"\n"); 354: break; 355: } 356: if (getmsglist(cp, msgvec, com->c_msgflag) < 0) 357: break; 358: e = (*com->c_func)(msgvec); 359: break; 360: 361: case STRLIST: 362: /* 363: * Just the straight string, with 364: * leading blanks removed. 365: */ 366: while (isspace(*cp)) 367: cp++; 368: e = (*com->c_func)(cp); 369: break; 370: 371: case RAWLIST: 372: /* 373: * A vector of strings, in shell style. 374: */ 375: if ((c = getrawlist(cp, arglist, 376: sizeof arglist / sizeof *arglist)) < 0) 377: break; 378: if (c < com->c_minargs) { 379: printf("%s requires at least %d arg(s)\n", 380: com->c_name, com->c_minargs); 381: break; 382: } 383: if (c > com->c_maxargs) { 384: printf("%s takes no more than %d arg(s)\n", 385: com->c_name, com->c_maxargs); 386: break; 387: } 388: e = (*com->c_func)(arglist); 389: break; 390: 391: case NOLIST: 392: /* 393: * Just the constant zero, for exiting, 394: * eg. 395: */ 396: e = (*com->c_func)(0); 397: break; 398: 399: default: 400: panic("Unknown argtype"); 401: } 402: 403: out: 404: /* 405: * Exit the current source file on 406: * error. 407: */ 408: if (e) { 409: if (e < 0) 410: return 1; 411: if (loading) 412: return 1; 413: if (sourcing) 414: unstack(); 415: return 0; 416: } 417: if (value("autoprint") != NOSTR && com->c_argtype & P) 418: if ((dot->m_flag & MDELETED) == 0) { 419: muvec[0] = dot - &message[0] + 1; 420: muvec[1] = 0; 421: type(muvec); 422: } 423: if (!sourcing && (com->c_argtype & T) == 0) 424: sawcom = 1; 425: return(0); 426: } 427: 428: /* 429: * Set the size of the message vector used to construct argument 430: * lists to message list functions. 431: */ 432: 433: setmsize(sz) 434: { 435: 436: if (msgvec != 0) 437: cfree((char *) msgvec); 438: msgvec = (int *) calloc((unsigned) (sz + 1), sizeof *msgvec); 439: } 440: 441: /* 442: * Find the correct command in the command table corresponding 443: * to the passed command "word" 444: */ 445: 446: struct cmd * 447: lex(word) 448: char word[]; 449: { 450: register struct cmd *cp; 451: extern struct cmd cmdtab[]; 452: 453: for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++) 454: if (isprefix(word, cp->c_name)) 455: return(cp); 456: return(NONE); 457: } 458: 459: /* 460: * Determine if as1 is a valid prefix of as2. 461: * Return true if yep. 462: */ 463: 464: isprefix(as1, as2) 465: char *as1, *as2; 466: { 467: register char *s1, *s2; 468: 469: s1 = as1; 470: s2 = as2; 471: while (*s1++ == *s2) 472: if (*s2++ == '\0') 473: return(1); 474: return(*--s1 == '\0'); 475: } 476: 477: /* 478: * The following gets called on receipt of an interrupt. This is 479: * to abort printout of a command, mainly. 480: * Dispatching here when command() is inactive crashes rcv. 481: * Close all open files except 0, 1, 2, and the temporary. 482: * Also, unstack all source files. 483: */ 484: 485: int inithdr; /* am printing startup headers */ 486: 487: /*ARGSUSED*/ 488: void 489: intr(s) 490: { 491: 492: noreset = 0; 493: if (!inithdr) 494: sawcom++; 495: inithdr = 0; 496: while (sourcing) 497: unstack(); 498: 499: close_all_files(); 500: 501: if (image >= 0) { 502: close(image); 503: image = -1; 504: } 505: fprintf(stderr, "Interrupt\n"); 506: reset(0); 507: } 508: 509: /* 510: * When we wake up after ^Z, reprint the prompt. 511: */ 512: void 513: stop(s) 514: { 515: sig_t old_action = signal(s, SIG_DFL); 516: 517: sigsetmask(sigblock(0L) & ~sigmask(s)); 518: kill(0, s); 519: sigblock(sigmask(s)); 520: signal(s, old_action); 521: if (reset_on_stop) { 522: reset_on_stop = 0; 523: reset(0); 524: } 525: } 526: 527: /* 528: * Branch here on hangup signal and simulate "exit". 529: */ 530: /*ARGSUSED*/ 531: void 532: hangup(s) 533: { 534: 535: /* nothing to do? */ 536: exit(1); 537: } 538: 539: /* 540: * Announce the presence of the current Mail version, 541: * give the message count, and print a header listing. 542: */ 543: 544: announce() 545: { 546: int vec[2], mdot; 547: 548: mdot = newfileinfo(); 549: vec[0] = mdot; 550: vec[1] = 0; 551: dot = &message[mdot - 1]; 552: if (msgCount > 0 && value("noheader") == NOSTR) { 553: inithdr++; 554: headers(vec); 555: inithdr = 0; 556: } 557: } 558: 559: /* 560: * Announce information about the file we are editing. 561: * Return a likely place to set dot. 562: */ 563: newfileinfo() 564: { 565: register struct message *mp; 566: register int u, n, mdot, d, s; 567: char fname[BUFSIZ], zname[BUFSIZ], *ename; 568: 569: for (mp = &message[0]; mp < &message[msgCount]; mp++) 570: if (mp->m_flag & MNEW) 571: break; 572: if (mp >= &message[msgCount]) 573: for (mp = &message[0]; mp < &message[msgCount]; mp++) 574: if ((mp->m_flag & MREAD) == 0) 575: break; 576: if (mp < &message[msgCount]) 577: mdot = mp - &message[0] + 1; 578: else 579: mdot = 1; 580: s = d = 0; 581: for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) { 582: if (mp->m_flag & MNEW) 583: n++; 584: if ((mp->m_flag & MREAD) == 0) 585: u++; 586: if (mp->m_flag & MDELETED) 587: d++; 588: if (mp->m_flag & MSAVED) 589: s++; 590: } 591: ename = mailname; 592: if (getfold(fname) >= 0) { 593: strcat(fname, "/"); 594: if (strncmp(fname, mailname, strlen(fname)) == 0) { 595: sprintf(zname, "+%s", mailname + strlen(fname)); 596: ename = zname; 597: } 598: } 599: printf("\"%s\": ", ename); 600: if (msgCount == 1) 601: printf("1 message"); 602: else 603: printf("%d messages", msgCount); 604: if (n > 0) 605: printf(" %d new", n); 606: if (u-n > 0) 607: printf(" %d unread", u); 608: if (d > 0) 609: printf(" %d deleted", d); 610: if (s > 0) 611: printf(" %d saved", s); 612: if (readonly) 613: printf(" [Read only]"); 614: printf("\n"); 615: return(mdot); 616: } 617: 618: /* 619: * Print the current version number. 620: */ 621: 622: /*ARGSUSED*/ 623: pversion(e) 624: { 625: extern char *version; 626: 627: printf("Version %s\n", version); 628: return(0); 629: } 630: 631: /* 632: * Load a file of user definitions. 633: */ 634: load(name) 635: char *name; 636: { 637: register FILE *in, *oldin; 638: 639: if ((in = Fopen(name, "r")) == NULL) 640: return; 641: oldin = input; 642: input = in; 643: loading = 1; 644: sourcing = 1; 645: commands(); 646: loading = 0; 647: sourcing = 0; 648: input = oldin; 649: Fclose(in); 650: }