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