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 1.14 02/06/81"; 12 13 /* 14 * Set up editing on the given file name. 15 * If isedit is true, we are considered to be editing the file, 16 * otherwise we are reading our mail which has signficance for 17 * mbox and so forth. 18 */ 19 20 setfile(name, isedit) 21 char *name; 22 { 23 FILE *ibuf; 24 int i; 25 static int shudclob; 26 static char efile[128]; 27 extern char tempMesg[]; 28 int (*sigs[2])(); 29 30 if ((ibuf = fopen(name, "r")) == NULL) { 31 if (isedit) 32 perror(name); 33 else 34 printf("No mail for %s\n", myname); 35 return(-1); 36 } 37 38 /* 39 * Looks like all will be well. We must now relinquish our 40 * hold on the current set of stuff. Must ignore signals 41 * while we are reading the new file, else we will ruin 42 * the message[] data structure. 43 */ 44 45 for (i = SIGINT; i <= SIGQUIT; i++) 46 sigs[i - SIGINT] = signal(i, SIG_IGN); 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 for (i = SIGINT; i <= SIGQUIT; i++) 88 signal(i, sigs[i - SIGINT]); 89 shudann = 1; 90 sawcom = 0; 91 return(0); 92 } 93 94 /* 95 * Interpret user commands one by one. If standard input is not a tty, 96 * print no prompt. 97 */ 98 99 int *msgvec; 100 101 commands() 102 { 103 int prompt, firstsw, stop(); 104 register int n; 105 char linebuf[LINESIZE]; 106 107 if (rcvmode) 108 if (signal(SIGINT, SIG_IGN) == SIG_DFL) 109 signal(SIGINT, stop); 110 input = stdin; 111 prompt = 1; 112 if (!intty) 113 prompt = 0; 114 firstsw = 1; 115 for (;;) { 116 setexit(); 117 if (firstsw > 0) { 118 firstsw = 0; 119 source1(mailrc); 120 if (!nosrc) 121 source1(MASTER); 122 } 123 124 /* 125 * How's this for obscure: after we 126 * finish sourcing for the first time, 127 * go off and print the headers! 128 */ 129 130 if (shudann && !sourcing) { 131 shudann = 0; 132 if (rcvmode) 133 announce(edit); 134 } 135 136 /* 137 * Print the prompt, if needed. Clear out 138 * string space, and flush the output. 139 */ 140 141 if (!rcvmode && !sourcing) 142 return; 143 top: 144 if (prompt && !sourcing) 145 printf("_\r"); 146 flush(); 147 sreset(); 148 149 /* 150 * Read a line of commands from the current input 151 * and handle end of file specially. 152 */ 153 154 n = 0; 155 for (;;) { 156 if (readline(input, &linebuf[n]) <= 0) { 157 if (n != 0) 158 break; 159 if (sourcing) { 160 unstack(); 161 goto more; 162 } 163 if (value("ignoreeof") != NOSTR && prompt) { 164 printf("Use \"quit\" to quit.\n"); 165 goto top; 166 } 167 if (!edit) { 168 signal(SIGINT, SIG_IGN); 169 return; 170 } 171 edstop(); 172 return; 173 } 174 if ((n = strlen(linebuf)) == 0) 175 break; 176 n--; 177 if (linebuf[n] != '\\') 178 break; 179 linebuf[n++] = ' '; 180 } 181 if (execute(linebuf, 0)) 182 return; 183 more: ; 184 } 185 } 186 187 /* 188 * Execute a single command. If the command executed 189 * is "quit," then return non-zero so that the caller 190 * will know to return back to main, if he cares. 191 * Contxt is non-zero if called while composing mail. 192 */ 193 194 execute(linebuf, contxt) 195 char linebuf[]; 196 { 197 char word[LINESIZE]; 198 char *arglist[MAXARGC]; 199 struct cmd *com; 200 register char *cp, *cp2; 201 register int c; 202 int muvec[2]; 203 int edstop(), e; 204 205 /* 206 * Strip the white space away from the beginning 207 * of the command, then scan out a word, which 208 * consists of anything except digits and white space. 209 * 210 * Handle ! escapes differently to get the correct 211 * lexical conventions. 212 */ 213 214 cp = linebuf; 215 while (any(*cp, " \t")) 216 cp++; 217 if (*cp == '!') { 218 if (sourcing) { 219 printf("Can't \"!\" while sourcing\n"); 220 unstack(); 221 return(0); 222 } 223 shell(cp+1); 224 return(0); 225 } 226 cp2 = word; 227 while (*cp && !any(*cp, " \t0123456789$^.:/-+*'\"")) 228 *cp2++ = *cp++; 229 *cp2 = '\0'; 230 231 /* 232 * Look up the command; if not found, bitch. 233 * Normally, a blank command would map to the 234 * first command in the table; while sourcing, 235 * however, we ignore blank lines to eliminate 236 * confusion. 237 */ 238 239 if (sourcing && equal(word, "")) 240 return(0); 241 com = lex(word); 242 if (com == NONE) { 243 printf("What?\n"); 244 if (sourcing) 245 unstack(); 246 return(0); 247 } 248 249 /* 250 * See if we should execute the command -- if a conditional 251 * we always execute it, otherwise, check the state of cond. 252 */ 253 254 if ((com->c_argtype & F) == 0) 255 if (cond == CRCV && !rcvmode || cond == CSEND && rcvmode) 256 return(0); 257 258 /* 259 * Special case so that quit causes a return to 260 * main, who will call the quit code directly. 261 * If we are in a source file, just unstack. 262 */ 263 264 if (com->c_func == edstop && sourcing) { 265 unstack(); 266 return(0); 267 } 268 if (!edit && com->c_func == edstop) { 269 signal(SIGINT, SIG_IGN); 270 return(1); 271 } 272 273 /* 274 * Process the arguments to the command, depending 275 * on the type he expects. Default to an error. 276 * If we are sourcing an interactive command, it's 277 * an error. 278 */ 279 280 if (!rcvmode && (com->c_argtype & M) == 0) { 281 printf("May not execute \"%s\" while sending\n", 282 com->c_name); 283 if (sourcing) 284 unstack(); 285 return(0); 286 } 287 if (sourcing && com->c_argtype & I) { 288 printf("May not execute \"%s\" while sourcing\n", 289 com->c_name); 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 (sourcing) 297 unstack(); 298 return(0); 299 } 300 if (contxt && com->c_argtype & R) { 301 printf("Cannot recursively invoke \"%s\"\n", com->c_name); 302 return(0); 303 } 304 e = 1; 305 switch (com->c_argtype & ~(F|P|I|M|T|W|R)) { 306 case MSGLIST: 307 /* 308 * A message list defaulting to nearest forward 309 * legal message. 310 */ 311 if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0) 312 break; 313 if (c == 0) { 314 *msgvec = first(com->c_msgflag, 315 com->c_msgmask); 316 msgvec[1] = NULL; 317 } 318 if (*msgvec == NULL) { 319 printf("No applicable messages\n"); 320 break; 321 } 322 e = (*com->c_func)(msgvec); 323 break; 324 325 case NDMLIST: 326 /* 327 * A message list with no defaults, but no error 328 * if none exist. 329 */ 330 if (getmsglist(cp, msgvec, com->c_msgflag) < 0) 331 break; 332 e = (*com->c_func)(msgvec); 333 break; 334 335 case STRLIST: 336 /* 337 * Just the straight string, with 338 * leading blanks removed. 339 */ 340 while (any(*cp, " \t")) 341 cp++; 342 e = (*com->c_func)(cp); 343 break; 344 345 case RAWLIST: 346 /* 347 * A vector of strings, in shell style. 348 */ 349 if ((c = getrawlist(cp, arglist)) < 0) 350 break; 351 if (c < com->c_minargs) { 352 printf("%s requires at least %d arg(s)\n", 353 com->c_name, com->c_minargs); 354 break; 355 } 356 if (c > com->c_maxargs) { 357 printf("%s takes no more than %d arg(s)\n", 358 com->c_name, com->c_maxargs); 359 break; 360 } 361 e = (*com->c_func)(arglist); 362 break; 363 364 case NOLIST: 365 /* 366 * Just the constant zero, for exiting, 367 * eg. 368 */ 369 e = (*com->c_func)(0); 370 break; 371 372 default: 373 panic("Unknown argtype"); 374 } 375 376 /* 377 * Exit the current source file on 378 * error. 379 */ 380 381 if (e && sourcing) 382 unstack(); 383 if (com->c_func == edstop) 384 return(1); 385 if (value("autoprint") != NOSTR && com->c_argtype & P) 386 if ((dot->m_flag & MDELETED) == 0) { 387 muvec[0] = dot - &message[0] + 1; 388 muvec[1] = 0; 389 type(muvec); 390 } 391 if (!sourcing && (com->c_argtype & T) == 0) 392 sawcom = 1; 393 return(0); 394 } 395 396 /* 397 * Set the size of the message vector used to construct argument 398 * lists to message list functions. 399 */ 400 401 setmsize(sz) 402 { 403 404 if (msgvec != (int *) 0) 405 cfree(msgvec); 406 msgvec = (int *) calloc((unsigned) (sz + 1), sizeof *msgvec); 407 } 408 409 /* 410 * Find the correct command in the command table corresponding 411 * to the passed command "word" 412 */ 413 414 struct cmd * 415 lex(word) 416 char word[]; 417 { 418 register struct cmd *cp; 419 extern struct cmd cmdtab[]; 420 421 for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++) 422 if (isprefix(word, cp->c_name)) 423 return(cp); 424 return(NONE); 425 } 426 427 /* 428 * Determine if as1 is a valid prefix of as2. 429 * Return true if yep. 430 */ 431 432 isprefix(as1, as2) 433 char *as1, *as2; 434 { 435 register char *s1, *s2; 436 437 s1 = as1; 438 s2 = as2; 439 while (*s1++ == *s2) 440 if (*s2++ == '\0') 441 return(1); 442 return(*--s1 == '\0'); 443 } 444 445 /* 446 * The following gets called on receipt of a rubout. This is 447 * to abort printout of a command, mainly. 448 * Dispatching here when command() is inactive crashes rcv. 449 * Close all open files except 0, 1, 2, and the temporary. 450 * The special call to getuserid() is needed so it won't get 451 * annoyed about losing its open file. 452 * Also, unstack all source files. 453 */ 454 455 stop() 456 { 457 register FILE *fp; 458 459 noreset = 0; 460 signal(SIGINT, SIG_IGN); 461 sawcom++; 462 while (sourcing) 463 unstack(); 464 getuserid((char *) -1); 465 for (fp = &_iob[0]; fp < &_iob[_NFILE]; fp++) { 466 if (fp == stdin || fp == stdout) 467 continue; 468 if (fp == itf || fp == otf) 469 continue; 470 if (fp == stderr) 471 continue; 472 if (fp == pipef) { 473 pclose(pipef); 474 pipef = NULL; 475 continue; 476 } 477 fclose(fp); 478 } 479 if (image >= 0) { 480 close(image); 481 image = -1; 482 } 483 clrbuf(stdout); 484 printf("Interrupt\n"); 485 signal(SIGINT, stop); 486 reset(0); 487 } 488 489 /* 490 * Announce the presence of the current Mail version, 491 * give the message count, and print a header listing. 492 */ 493 494 char *greeting = "Mail version 2.0 %s. Type ? for help.\n"; 495 496 announce(pr) 497 { 498 int vec[2], mdot; 499 extern char *version; 500 501 mdot = newfileinfo(); 502 vec[0] = mdot; 503 vec[1] = 0; 504 if (pr && value("quiet") == NOSTR) 505 printf(greeting, version); 506 dot = &message[mdot - 1]; 507 if (msgCount > 0 && !noheader) 508 headers(vec); 509 } 510 511 /* 512 * Announce information about the file we are editing. 513 * Return a likely place to set dot. 514 */ 515 516 newfileinfo() 517 { 518 register struct message *mp; 519 register int u, n, mdot; 520 521 for (mp = &message[0]; mp < &message[msgCount]; mp++) 522 if (mp->m_flag & MNEW) 523 break; 524 if (mp >= &message[msgCount]) 525 for (mp = &message[0]; mp < &message[msgCount]; mp++) 526 if ((mp->m_flag & MREAD) == 0) 527 break; 528 if (mp < &message[msgCount]) 529 mdot = mp - &message[0] + 1; 530 else 531 mdot = 1; 532 for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) { 533 if (mp->m_flag & MNEW) 534 n++; 535 if ((mp->m_flag & MREAD) == 0) 536 u++; 537 } 538 printf("\"%s\": ", mailname); 539 if (msgCount == 1) 540 printf("1 message"); 541 else 542 printf("%d messages", msgCount); 543 if (n > 0) 544 printf(" %d new", n); 545 if (u-n > 0) 546 printf(" %d unread", u); 547 if (readonly) 548 printf(" [Read only]"); 549 printf("\n"); 550 return(mdot); 551 } 552 553 strace() {} 554 555 /* 556 * Print the current version number. 557 */ 558 559 pversion(e) 560 { 561 printf(greeting, version); 562 return(0); 563 } 564