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.4 10/10/80"; 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 mailsize = fsize(ibuf); 73 if ((otf = fopen(tempMesg, "w")) == NULL) { 74 perror(tempMesg); 75 exit(1); 76 } 77 if ((itf = fopen(tempMesg, "r")) == NULL) { 78 perror(tempMesg); 79 exit(1); 80 } 81 remove(tempMesg); 82 setptr(ibuf); 83 setmsize(msgCount); 84 fclose(ibuf); 85 for (i = SIGINT; i <= SIGQUIT; i++) 86 signal(i, sigs[i - SIGINT]); 87 printf("%s: ", name); 88 announce(!edit); 89 sawcom = 0; 90 return(0); 91 } 92 93 /* 94 * Interpret user commands one by one. If standard input is not a tty, 95 * print no prompt. 96 */ 97 98 int *msgvec; 99 100 commands() 101 { 102 int prompt, firstsw, stop(); 103 register int n; 104 char linebuf[LINESIZE]; 105 106 if (rcvmode) 107 if (signal(SIGINT, SIG_IGN) == SIG_DFL) 108 signal(SIGINT, stop); 109 input = stdin; 110 prompt = 1; 111 if (!intty) 112 prompt = 0; 113 firstsw = 1; 114 for (;;) { 115 setexit(); 116 if (firstsw > 0) { 117 firstsw = 0; 118 source1(mailrc); 119 if (!nosrc) 120 source1(MASTER); 121 } 122 123 /* 124 * How's this for obscure: after we 125 * finish sourcing for the first time, 126 * go off and print the headers! 127 */ 128 129 #ifdef CRAZYWOW 130 if (firstsw == 0 && !sourcing) { 131 firstsw = -1; 132 if (rcvmode) 133 announce(1); 134 } 135 #endif 136 137 /* 138 * Print the prompt, if needed. Clear out 139 * string space, and flush the output. 140 */ 141 142 if (!rcvmode && !sourcing) 143 return; 144 top: 145 if (prompt && !sourcing) 146 printf("_\r"); 147 flush(); 148 sreset(); 149 150 /* 151 * Read a line of commands from the current input 152 * and handle end of file specially. 153 */ 154 155 n = 0; 156 for (;;) { 157 if (readline(input, &linebuf[n]) <= 0) { 158 if (n != 0) 159 break; 160 if (sourcing) { 161 unstack(); 162 goto more; 163 } 164 if (value("ignoreeof") != NOSTR && prompt) { 165 printf("Use \"quit\" to quit.\n"); 166 goto top; 167 } 168 if (!edit) { 169 signal(SIGINT, SIG_IGN); 170 return; 171 } 172 edstop(); 173 return; 174 } 175 if ((n = strlen(linebuf)) == 0) 176 break; 177 n--; 178 if (linebuf[n] != '\\') 179 break; 180 linebuf[n++] = ' '; 181 } 182 if (execute(linebuf)) 183 return; 184 more: ; 185 } 186 } 187 188 /* 189 * Execute a single command. If the command executed 190 * is "quit," then return non-zero so that the caller 191 * will know to return back to main, if he cares. 192 */ 193 194 execute(linebuf) 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 * Special case so that quit causes a return to 251 * main, who will call the quit code directly. 252 * If we are in a source file, just unstack. 253 */ 254 255 if (com->c_func == edstop && sourcing) { 256 unstack(); 257 return(0); 258 } 259 if (!edit && com->c_func == edstop) { 260 signal(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 unstack(); 275 return(0); 276 } 277 if (sourcing && com->c_argtype & I) { 278 printf("May not execute \"%s\" while sourcing\n", 279 com->c_name); 280 unstack(); 281 return(0); 282 } 283 if (readonly && com->c_argtype & W) { 284 printf("May not execute \"%s\" -- message file is read only\n", 285 com->c_name); 286 if (sourcing) 287 unstack(); 288 return(0); 289 } 290 e = 1; 291 switch (com->c_argtype & ~(P|I|M|W)) { 292 case MSGLIST: 293 /* 294 * A message list defaulting to nearest forward 295 * legal message. 296 */ 297 if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0) 298 break; 299 if (c == 0) { 300 *msgvec = first(com->c_msgflag, 301 com->c_msgmask); 302 msgvec[1] = NULL; 303 } 304 if (*msgvec == NULL) { 305 printf("No applicable messages\n"); 306 break; 307 } 308 e = (*com->c_func)(msgvec); 309 break; 310 311 case NDMLIST: 312 /* 313 * A message list with no defaults, but no error 314 * if none exist. 315 */ 316 if (getmsglist(cp, msgvec, com->c_msgflag) < 0) 317 break; 318 e = (*com->c_func)(msgvec); 319 break; 320 321 case STRLIST: 322 /* 323 * Just the straight string, with 324 * leading blanks removed. 325 */ 326 while (any(*cp, " \t")) 327 cp++; 328 e = (*com->c_func)(cp); 329 break; 330 331 case RAWLIST: 332 /* 333 * A vector of strings, in shell style. 334 */ 335 if ((c = getrawlist(cp, arglist)) < 0) 336 break; 337 if (c < com->c_minargs) { 338 printf("%s requires at least %d arg(s)\n", 339 com->c_name, com->c_minargs); 340 break; 341 } 342 if (c > com->c_maxargs) { 343 printf("%s takes no more than %d arg(s)\n", 344 com->c_name, com->c_maxargs); 345 break; 346 } 347 e = (*com->c_func)(arglist); 348 break; 349 350 case NOLIST: 351 /* 352 * Just the constant zero, for exiting, 353 * eg. 354 */ 355 e = (*com->c_func)(0); 356 break; 357 358 default: 359 panic("Unknown argtype"); 360 } 361 362 /* 363 * Exit the current source file on 364 * error. 365 */ 366 367 if (e && sourcing) 368 unstack(); 369 if (com->c_func == edstop) 370 return(1); 371 if (value("autoprint") != NOSTR && com->c_argtype & P) 372 if ((dot->m_flag & MDELETED) == 0) { 373 muvec[0] = dot - &message[0] + 1; 374 muvec[1] = 0; 375 type(muvec); 376 } 377 if (!sourcing) 378 sawcom = 1; 379 return(0); 380 } 381 382 /* 383 * Set the size of the message vector used to construct argument 384 * lists to message list functions. 385 */ 386 387 setmsize(sz) 388 { 389 390 if (msgvec != (int *) 0) 391 cfree(msgvec); 392 msgvec = (int *) calloc((unsigned) (sz + 1), sizeof *msgvec); 393 } 394 395 /* 396 * Find the correct command in the command table corresponding 397 * to the passed command "word" 398 */ 399 400 struct cmd * 401 lex(word) 402 char word[]; 403 { 404 register struct cmd *cp; 405 extern struct cmd cmdtab[]; 406 407 for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++) 408 if (isprefix(word, cp->c_name)) 409 return(cp); 410 return(NONE); 411 } 412 413 /* 414 * Determine if as1 is a valid prefix of as2. 415 * Return true if yep. 416 */ 417 418 isprefix(as1, as2) 419 char *as1, *as2; 420 { 421 register char *s1, *s2; 422 423 s1 = as1; 424 s2 = as2; 425 while (*s1++ == *s2) 426 if (*s2++ == '\0') 427 return(1); 428 return(*--s1 == '\0'); 429 } 430 431 /* 432 * The following gets called on receipt of a rubout. This is 433 * to abort printout of a command, mainly. 434 * Dispatching here when command() is inactive crashes rcv. 435 * Close all open files except 0, 1, 2, and the temporary. 436 * The special call to getuserid() is needed so it won't get 437 * annoyed about losing its open file. 438 * Also, unstack all source files. 439 */ 440 441 stop() 442 { 443 register FILE *fp; 444 445 noreset = 0; 446 signal(SIGINT, SIG_IGN); 447 sawcom++; 448 while (sourcing) 449 unstack(); 450 getuserid((char *) -1); 451 for (fp = &_iob[0]; fp < &_iob[_NFILE]; fp++) { 452 if (fp == stdin || fp == stdout) 453 continue; 454 if (fp == itf || fp == otf) 455 continue; 456 if (fp == stderr) 457 continue; 458 if (fp == pipef) { 459 pclose(pipef); 460 pipef = NULL; 461 continue; 462 } 463 fclose(fp); 464 } 465 if (image >= 0) { 466 close(image); 467 image = -1; 468 } 469 clrbuf(stdout); 470 printf("Interrupt\n"); 471 signal(SIGINT, stop); 472 reset(0); 473 } 474 475 /* 476 * Announce the presence of the current Mail version, 477 * give the message count, and print a header listing. 478 */ 479 480 char *greeting = "Mail version 2.0 %s. Type ? for help.\n"; 481 482 announce(pr) 483 { 484 int vec[2]; 485 extern char *version; 486 register struct message *mp; 487 488 if (value("hold") != NOSTR) 489 for (mp = &message[0]; mp < &message[msgCount]; mp++) 490 mp->m_flag |= MPRESERVE; 491 vec[0] = 1; 492 vec[1] = 0; 493 if (pr && value("quiet") == NOSTR) 494 printf(greeting, version); 495 if (msgCount == 1) 496 printf("1 message"); 497 else 498 printf("%d messages", msgCount); 499 if (readonly) 500 printf(" [Read only]"); 501 printf("\n"); 502 headers(vec); 503 } 504 505 strace() {} 506 507 /* 508 * Print the current version number. 509 */ 510 511 pversion(e) 512 { 513 printf(greeting, version); 514 return(0); 515 } 516