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: }

Defined functions

_fwalk defined in line 529; used 1 times
announce defined in line 594; used 2 times
commands defined in line 129; used 2 times
contin defined in line 443; used 2 times
execute defined in line 217; used 2 times
hangup defined in line 453; used 3 times
isprefix defined in line 503; used 1 times
lex defined in line 485; used 2 times
load defined in line 686; used 2 times
newfileinfo defined in line 616; used 2 times
pversion defined in line 677; used 2 times
setfile defined in line 30; used 2 times
setmsize defined in line 472; used 1 times
stop defined in line 556; used 4 times
strace defined in line 671; used 1 times
xclose defined in line 540; used 1 times

Defined variables

greeting defined in line 592; used 1 times
inithdr defined in line 526; used 4 times
msgvec defined in line 127; used 13 times
prompt defined in line 21; used 2 times
sccsid defined in line 8; never used
Last modified: 1985-11-03
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 1830
Valid CSS Valid XHTML 1.0 Strict