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