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