|
|
1.1 ! root 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 are permitted ! 6: * provided that this notice is preserved and that due credit is given ! 7: * to the University of California at Berkeley. The name of the University ! 8: * may not be used to endorse or promote products derived from this ! 9: * software without specific prior written permission. This software ! 10: * is provided ``as is'' without express or implied warranty. ! 11: */ ! 12: ! 13: #ifdef notdef ! 14: static char sccsid[] = "@(#)lex.c 5.8 (Berkeley) 4/12/88"; ! 15: #endif /* notdef */ ! 16: ! 17: #include "rcv.h" ! 18: #include <sys/stat.h> ! 19: #include <errno.h> ! 20: ! 21: /* ! 22: * Mail -- a mail program ! 23: * ! 24: * Lexical processing of commands. ! 25: */ ! 26: ! 27: char *prompt = "& "; ! 28: ! 29: /* ! 30: * Set up editing on the given file name. ! 31: * If isedit is true, we are considered to be editing the file, ! 32: * otherwise we are reading our mail which has signficance for ! 33: * mbox and so forth. ! 34: */ ! 35: ! 36: setfile(name, isedit) ! 37: char *name; ! 38: { ! 39: FILE *ibuf; ! 40: int i; ! 41: struct stat stb; ! 42: static int shudclob; ! 43: static char efile[128]; ! 44: extern char tempMesg[]; ! 45: extern int errno; ! 46: ! 47: if ((ibuf = fopen(name, "r")) == NULL) ! 48: return(-1); ! 49: ! 50: if (fstat(fileno(ibuf), &stb) < 0) { ! 51: fclose(ibuf); ! 52: return (-1); ! 53: } ! 54: ! 55: switch (stb.st_mode & S_IFMT) { ! 56: case S_IFDIR: ! 57: fclose(ibuf); ! 58: errno = EISDIR; ! 59: return (-1); ! 60: ! 61: case S_IFREG: ! 62: break; ! 63: ! 64: default: ! 65: fclose(ibuf); ! 66: errno = EINVAL; ! 67: return (-1); ! 68: } ! 69: ! 70: if (!edit && stb.st_size == 0) { ! 71: fclose(ibuf); ! 72: return(-1); ! 73: } ! 74: ! 75: /* ! 76: * Looks like all will be well. We must now relinquish our ! 77: * hold on the current set of stuff. Must hold signals ! 78: * while we are reading the new file, else we will ruin ! 79: * the message[] data structure. ! 80: */ ! 81: ! 82: holdsigs(); ! 83: if (shudclob) { ! 84: if (edit) ! 85: edstop(); ! 86: else ! 87: quit(); ! 88: } ! 89: ! 90: /* ! 91: * Copy the messages into /tmp ! 92: * and set pointers. ! 93: */ ! 94: ! 95: readonly = 0; ! 96: if ((i = open(name, 1)) < 0) ! 97: readonly++; ! 98: else ! 99: close(i); ! 100: if (shudclob) { ! 101: fclose(itf); ! 102: fclose(otf); ! 103: } ! 104: shudclob = 1; ! 105: edit = isedit; ! 106: strncpy(efile, name, 128); ! 107: editfile = efile; ! 108: if (name != mailname) ! 109: strcpy(mailname, name); ! 110: mailsize = fsize(ibuf); ! 111: if ((otf = fopen(tempMesg, "w")) == NULL) { ! 112: perror(tempMesg); ! 113: exit(1); ! 114: } ! 115: if ((itf = fopen(tempMesg, "r")) == NULL) { ! 116: perror(tempMesg); ! 117: exit(1); ! 118: } ! 119: remove(tempMesg); ! 120: setptr(ibuf); ! 121: setmsize(msgCount); ! 122: fclose(ibuf); ! 123: relsesigs(); ! 124: sawcom = 0; ! 125: return(0); ! 126: } ! 127: ! 128: /* ! 129: * Interpret user commands one by one. If standard input is not a tty, ! 130: * print no prompt. ! 131: */ ! 132: ! 133: int *msgvec; ! 134: jmp_buf commjmp; ! 135: ! 136: commands() ! 137: { ! 138: int eofloop, shudprompt, stop(); ! 139: register int n; ! 140: char linebuf[LINESIZE]; ! 141: int hangup(), contin(); ! 142: ! 143: signal(SIGCONT, SIG_DFL); ! 144: if (rcvmode && !sourcing) { ! 145: if (signal(SIGINT, SIG_IGN) != SIG_IGN) ! 146: signal(SIGINT, stop); ! 147: if (signal(SIGHUP, SIG_IGN) != SIG_IGN) ! 148: signal(SIGHUP, hangup); ! 149: } ! 150: shudprompt = intty && !sourcing; ! 151: for (;;) { ! 152: setexit(); ! 153: ! 154: /* ! 155: * Print the prompt, if needed. Clear out ! 156: * string space, and flush the output. ! 157: */ ! 158: ! 159: if (!rcvmode && !sourcing) ! 160: return; ! 161: eofloop = 0; ! 162: top: ! 163: if (shudprompt) { ! 164: setjmp(commjmp); ! 165: signal(SIGCONT, contin); ! 166: printf(prompt); ! 167: } ! 168: fflush(stdout); ! 169: sreset(); ! 170: ! 171: /* ! 172: * Read a line of commands from the current input ! 173: * and handle end of file specially. ! 174: */ ! 175: ! 176: n = 0; ! 177: for (;;) { ! 178: if (readline(input, &linebuf[n]) < 0) { ! 179: if (n != 0) ! 180: break; ! 181: if (loading) ! 182: return; ! 183: if (sourcing) { ! 184: unstack(); ! 185: goto more; ! 186: } ! 187: if (value("ignoreeof") != NOSTR && shudprompt) { ! 188: if (++eofloop < 25) { ! 189: printf("Use \"quit\" to quit.\n"); ! 190: goto top; ! 191: } ! 192: } ! 193: if (edit) ! 194: edstop(); ! 195: return; ! 196: } ! 197: if ((n = strlen(linebuf)) == 0) ! 198: break; ! 199: n--; ! 200: if (linebuf[n] != '\\') ! 201: break; ! 202: linebuf[n++] = ' '; ! 203: } ! 204: signal(SIGCONT, SIG_DFL); ! 205: if (execute(linebuf, 0)) ! 206: return; ! 207: more: ; ! 208: } ! 209: } ! 210: ! 211: /* ! 212: * Execute a single command. If the command executed ! 213: * is "quit," then return non-zero so that the caller ! 214: * will know to return back to main, if he cares. ! 215: * Contxt is non-zero if called while composing mail. ! 216: */ ! 217: ! 218: execute(linebuf, contxt) ! 219: char linebuf[]; ! 220: { ! 221: char word[LINESIZE]; ! 222: char *arglist[MAXARGC]; ! 223: struct cmd *com; ! 224: register char *cp, *cp2; ! 225: register int c; ! 226: int muvec[2]; ! 227: int edstop(), e; ! 228: ! 229: /* ! 230: * Strip the white space away from the beginning ! 231: * of the command, then scan out a word, which ! 232: * consists of anything except digits and white space. ! 233: * ! 234: * Handle ! escapes differently to get the correct ! 235: * lexical conventions. ! 236: */ ! 237: ! 238: for (cp = linebuf; isspace(*cp); cp++) ! 239: ; ! 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 && *word == '\0') ! 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: signal(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 (isspace(*cp)) ! 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: /*ARGSUSED*/ ! 444: contin(s) ! 445: { ! 446: ! 447: longjmp(commjmp, 1); ! 448: } ! 449: ! 450: /* ! 451: * Branch here on hangup signal and simulate "exit". ! 452: */ ! 453: /*ARGSUSED*/ ! 454: hangup(s) ! 455: { ! 456: ! 457: /* nothing to do? */ ! 458: exit(0); ! 459: } ! 460: ! 461: /* ! 462: * Set the size of the message vector used to construct argument ! 463: * lists to message list functions. ! 464: */ ! 465: ! 466: setmsize(sz) ! 467: { ! 468: ! 469: if (msgvec != 0) ! 470: cfree((char *) msgvec); ! 471: msgvec = (int *) calloc((unsigned) (sz + 1), sizeof *msgvec); ! 472: } ! 473: ! 474: /* ! 475: * Find the correct command in the command table corresponding ! 476: * to the passed command "word" ! 477: */ ! 478: ! 479: struct cmd * ! 480: lex(word) ! 481: char word[]; ! 482: { ! 483: register struct cmd *cp; ! 484: extern struct cmd cmdtab[]; ! 485: ! 486: for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++) ! 487: if (isprefix(word, cp->c_name)) ! 488: return(cp); ! 489: return(NONE); ! 490: } ! 491: ! 492: /* ! 493: * Determine if as1 is a valid prefix of as2. ! 494: * Return true if yep. ! 495: */ ! 496: ! 497: isprefix(as1, as2) ! 498: char *as1, *as2; ! 499: { ! 500: register char *s1, *s2; ! 501: ! 502: s1 = as1; ! 503: s2 = as2; ! 504: while (*s1++ == *s2) ! 505: if (*s2++ == '\0') ! 506: return(1); ! 507: return(*--s1 == '\0'); ! 508: } ! 509: ! 510: /* ! 511: * The following gets called on receipt of an interrupt. This is ! 512: * to abort printout of a command, mainly. ! 513: * Dispatching here when command() is inactive crashes rcv. ! 514: * Close all open files except 0, 1, 2, and the temporary. ! 515: * Also, unstack all source files. ! 516: */ ! 517: ! 518: int inithdr; /* am printing startup headers */ ! 519: ! 520: #ifdef _NFILE ! 521: static ! 522: _fwalk(function) ! 523: register int (*function)(); ! 524: { ! 525: register FILE *iop; ! 526: ! 527: for (iop = _iob; iop < _iob + _NFILE; iop++) ! 528: (*function)(iop); ! 529: } ! 530: #endif ! 531: ! 532: static ! 533: xclose(iop) ! 534: register FILE *iop; ! 535: { ! 536: if (iop == stdin || iop == stdout || ! 537: iop == stderr || iop == itf || iop == otf) ! 538: return; ! 539: ! 540: if (iop != pipef) ! 541: fclose(iop); ! 542: else { ! 543: pclose(pipef); ! 544: pipef = NULL; ! 545: } ! 546: } ! 547: ! 548: /*ARGSUSED*/ ! 549: stop(s) ! 550: { ! 551: ! 552: noreset = 0; ! 553: if (!inithdr) ! 554: sawcom++; ! 555: inithdr = 0; ! 556: while (sourcing) ! 557: unstack(); ! 558: ! 559: /* ! 560: * Walk through all the open FILEs, applying xclose() to them ! 561: */ ! 562: _fwalk(xclose); ! 563: ! 564: if (image >= 0) { ! 565: close(image); ! 566: image = -1; ! 567: } ! 568: fprintf(stderr, "Interrupt\n"); ! 569: reset(0); ! 570: } ! 571: ! 572: /* ! 573: * Announce the presence of the current Mail version, ! 574: * give the message count, and print a header listing. ! 575: */ ! 576: ! 577: char *greeting = "Mail version %s. Type ? for help.\n"; ! 578: ! 579: announce(pr) ! 580: { ! 581: int vec[2], mdot; ! 582: extern char *version; ! 583: ! 584: if (pr && value("quiet") == NOSTR) ! 585: printf(greeting, version); ! 586: mdot = newfileinfo(); ! 587: vec[0] = mdot; ! 588: vec[1] = 0; ! 589: dot = &message[mdot - 1]; ! 590: if (msgCount > 0 && !noheader) { ! 591: inithdr++; ! 592: headers(vec); ! 593: inithdr = 0; ! 594: } ! 595: } ! 596: ! 597: /* ! 598: * Announce information about the file we are editing. ! 599: * Return a likely place to set dot. ! 600: */ ! 601: newfileinfo() ! 602: { ! 603: register struct message *mp; ! 604: register int u, n, mdot, d, s; ! 605: char fname[BUFSIZ], zname[BUFSIZ], *ename; ! 606: ! 607: for (mp = &message[0]; mp < &message[msgCount]; mp++) ! 608: if (mp->m_flag & MNEW) ! 609: break; ! 610: if (mp >= &message[msgCount]) ! 611: for (mp = &message[0]; mp < &message[msgCount]; mp++) ! 612: if ((mp->m_flag & MREAD) == 0) ! 613: break; ! 614: if (mp < &message[msgCount]) ! 615: mdot = mp - &message[0] + 1; ! 616: else ! 617: mdot = 1; ! 618: s = d = 0; ! 619: for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) { ! 620: if (mp->m_flag & MNEW) ! 621: n++; ! 622: if ((mp->m_flag & MREAD) == 0) ! 623: u++; ! 624: if (mp->m_flag & MDELETED) ! 625: d++; ! 626: if (mp->m_flag & MSAVED) ! 627: s++; ! 628: } ! 629: ename = mailname; ! 630: if (getfold(fname) >= 0) { ! 631: strcat(fname, "/"); ! 632: if (strncmp(fname, mailname, strlen(fname)) == 0) { ! 633: sprintf(zname, "+%s", mailname + strlen(fname)); ! 634: ename = zname; ! 635: } ! 636: } ! 637: printf("\"%s\": ", ename); ! 638: if (msgCount == 1) ! 639: printf("1 message"); ! 640: else ! 641: printf("%d messages", msgCount); ! 642: if (n > 0) ! 643: printf(" %d new", n); ! 644: if (u-n > 0) ! 645: printf(" %d unread", u); ! 646: if (d > 0) ! 647: printf(" %d deleted", d); ! 648: if (s > 0) ! 649: printf(" %d saved", s); ! 650: if (readonly) ! 651: printf(" [Read only]"); ! 652: printf("\n"); ! 653: return(mdot); ! 654: } ! 655: ! 656: /* ! 657: * Print the current version number. ! 658: */ ! 659: ! 660: /*ARGSUSED*/ ! 661: pversion(e) ! 662: { ! 663: extern char *version; ! 664: ! 665: printf("Version %s\n", version); ! 666: return(0); ! 667: } ! 668: ! 669: /* ! 670: * Load a file of user definitions. ! 671: */ ! 672: load(name) ! 673: char *name; ! 674: { ! 675: register FILE *in, *oldin; ! 676: ! 677: if ((in = fopen(name, "r")) == NULL) ! 678: return; ! 679: oldin = input; ! 680: input = in; ! 681: loading = 1; ! 682: sourcing = 1; ! 683: commands(); ! 684: loading = 0; ! 685: sourcing = 0; ! 686: input = oldin; ! 687: fclose(in); ! 688: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.