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