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