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