|
|
1.1 ! root 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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.