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.2 10/08/80"; 12 13 /* 14 * Interpret user commands one by one. If standard input is not a tty, 15 * print no prompt. 16 */ 17 18 int *msgvec; 19 20 commands() 21 { 22 int prompt, firstsw, stop(); 23 register int n; 24 char linebuf[LINESIZE]; 25 26 msgvec = (int *) calloc((unsigned) (msgCount + 1), sizeof *msgvec); 27 if (rcvmode) 28 if (signal(SIGINT, SIG_IGN) == SIG_DFL) 29 signal(SIGINT, stop); 30 input = stdin; 31 prompt = 1; 32 if (!intty) 33 prompt = 0; 34 firstsw = 1; 35 for (;;) { 36 setexit(); 37 if (firstsw > 0) { 38 firstsw = 0; 39 source1(mailrc); 40 if (!nosrc) 41 source1(MASTER); 42 } 43 44 /* 45 * How's this for obscure: after we 46 * finish sourcing for the first time, 47 * go off and print the headers! 48 */ 49 50 if (firstsw == 0 && !sourcing) { 51 firstsw = -1; 52 if (rcvmode) 53 announce(); 54 } 55 56 /* 57 * Print the prompt, if needed. Clear out 58 * string space, and flush the output. 59 */ 60 61 if (!rcvmode && !sourcing) 62 return; 63 top: 64 if (prompt && !sourcing) 65 printf("_\r"); 66 flush(); 67 sreset(); 68 69 /* 70 * Read a line of commands from the current input 71 * and handle end of file specially. 72 */ 73 74 n = 0; 75 for (;;) { 76 if (readline(input, &linebuf[n]) <= 0) { 77 if (n != 0) 78 break; 79 if (sourcing) { 80 unstack(); 81 goto more; 82 } 83 if (value("ignoreeof") != NOSTR && prompt) { 84 printf("Use \"quit\" to quit.\n"); 85 goto top; 86 } 87 if (!edit) { 88 signal(SIGINT, SIG_IGN); 89 return; 90 } 91 edstop(); 92 return; 93 } 94 if ((n = strlen(linebuf)) == 0) 95 break; 96 n--; 97 if (linebuf[n] != '\\') 98 break; 99 linebuf[n++] = ' '; 100 } 101 if (execute(linebuf)) 102 return; 103 more: ; 104 } 105 } 106 107 /* 108 * Execute a single command. If the command executed 109 * is "quit," then return non-zero so that the caller 110 * will know to return back to main, if he cares. 111 */ 112 113 execute(linebuf) 114 char linebuf[]; 115 { 116 char word[LINESIZE]; 117 char *arglist[MAXARGC]; 118 struct cmd *com; 119 register char *cp, *cp2; 120 register int c; 121 int edstop(), e; 122 123 /* 124 * Strip the white space away from the beginning 125 * of the command, then scan out a word, which 126 * consists of anything except digits and white space. 127 * 128 * Handle ! escapes differently to get the correct 129 * lexical conventions. 130 */ 131 132 cp = linebuf; 133 while (any(*cp, " \t")) 134 cp++; 135 if (*cp == '!') { 136 if (sourcing) { 137 printf("Can't \"!\" while sourcing\n"); 138 unstack(); 139 return(0); 140 } 141 shell(cp+1); 142 return(0); 143 } 144 cp2 = word; 145 while (*cp && !any(*cp, " \t0123456789$^.-+*'\"")) 146 *cp2++ = *cp++; 147 *cp2 = '\0'; 148 149 /* 150 * Look up the command; if not found, bitch. 151 * Normally, a blank command would map to the 152 * first command in the table; while sourcing, 153 * however, we ignore blank lines to eliminate 154 * confusion. 155 */ 156 157 if (sourcing && equal(word, "")) 158 return(0); 159 com = lex(word); 160 if (com == NONE) { 161 printf("What?\n"); 162 if (sourcing) 163 unstack(); 164 return(0); 165 } 166 167 /* 168 * Special case so that quit causes a return to 169 * main, who will call the quit code directly. 170 * If we are in a source file, just unstack. 171 */ 172 173 if (com->c_func == edstop && sourcing) { 174 unstack(); 175 return(0); 176 } 177 if (!edit && com->c_func == edstop) { 178 signal(SIGINT, SIG_IGN); 179 return(1); 180 } 181 182 /* 183 * Process the arguments to the command, depending 184 * on the type he expects. Default to an error. 185 * If we are sourcing an interactive command, it's 186 * an error. 187 */ 188 189 if (!rcvmode && (com->c_argtype & M) == 0) { 190 printf("May not execute \"%s\" while sending\n", 191 com->c_name); 192 unstack(); 193 return(0); 194 } 195 if (sourcing && com->c_argtype & I) { 196 printf("May not execute \"%s\" while sourcing\n", 197 com->c_name); 198 unstack(); 199 return(0); 200 } 201 e = 1; 202 switch (com->c_argtype & ~(P|I|M)) { 203 case MSGLIST: 204 /* 205 * A message list defaulting to nearest forward 206 * legal message. 207 */ 208 if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0) 209 break; 210 if (c == 0) { 211 *msgvec = first(com->c_msgflag, 212 com->c_msgmask); 213 msgvec[1] = NULL; 214 } 215 if (*msgvec == NULL) { 216 printf("No applicable messages\n"); 217 break; 218 } 219 e = (*com->c_func)(msgvec); 220 break; 221 222 case NDMLIST: 223 /* 224 * A message list with no defaults, but no error 225 * if none exist. 226 */ 227 if (getmsglist(cp, msgvec, com->c_msgflag) < 0) 228 break; 229 e = (*com->c_func)(msgvec); 230 break; 231 232 case STRLIST: 233 /* 234 * Just the straight string, with 235 * leading blanks removed. 236 */ 237 while (any(*cp, " \t")) 238 cp++; 239 e = (*com->c_func)(cp); 240 break; 241 242 case RAWLIST: 243 /* 244 * A vector of strings, in shell style. 245 */ 246 if ((c = getrawlist(cp, arglist)) < 0) 247 break; 248 if (c < com->c_minargs) { 249 printf("%s requires at least %d arg(s)\n", 250 com->c_name, com->c_minargs); 251 break; 252 } 253 if (c > com->c_maxargs) { 254 printf("%s takes no more than %d arg(s)\n", 255 com->c_name, com->c_maxargs); 256 break; 257 } 258 e = (*com->c_func)(arglist); 259 break; 260 261 case NOLIST: 262 /* 263 * Just the constant zero, for exiting, 264 * eg. 265 */ 266 e = (*com->c_func)(0); 267 break; 268 269 default: 270 panic("Unknown argtype"); 271 } 272 273 /* 274 * Exit the current source file on 275 * error. 276 */ 277 278 if (e && sourcing) 279 unstack(); 280 if (com->c_func == edstop) 281 return(1); 282 if (value("autoprint") != NOSTR && com->c_argtype & P) 283 if ((dot->m_flag & MDELETED) == 0) 284 print(dot); 285 if (!sourcing) 286 sawcom = 1; 287 return(0); 288 } 289 290 /* 291 * Find the correct command in the command table corresponding 292 * to the passed command "word" 293 */ 294 295 struct cmd * 296 lex(word) 297 char word[]; 298 { 299 register struct cmd *cp; 300 extern struct cmd cmdtab[]; 301 302 for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++) 303 if (isprefix(word, cp->c_name)) 304 return(cp); 305 return(NONE); 306 } 307 308 /* 309 * Determine if as1 is a valid prefix of as2. 310 * Return true if yep. 311 */ 312 313 isprefix(as1, as2) 314 char *as1, *as2; 315 { 316 register char *s1, *s2; 317 318 s1 = as1; 319 s2 = as2; 320 while (*s1++ == *s2) 321 if (*s2++ == '\0') 322 return(1); 323 return(*--s1 == '\0'); 324 } 325 326 /* 327 * The following gets called on receipt of a rubout. This is 328 * to abort printout of a command, mainly. 329 * Dispatching here when command() is inactive crashes rcv. 330 * Close all open files except 0, 1, 2, and the temporary. 331 * The special call to getuserid() is needed so it won't get 332 * annoyed about losing its open file. 333 * Also, unstack all source files. 334 */ 335 336 stop() 337 { 338 register FILE *fp; 339 340 noreset = 0; 341 signal(SIGINT, SIG_IGN); 342 sawcom++; 343 while (sourcing) 344 unstack(); 345 getuserid((char *) -1); 346 for (fp = &_iob[0]; fp < &_iob[_NFILE]; fp++) { 347 if (fp == stdin || fp == stdout) 348 continue; 349 if (fp == itf || fp == otf) 350 continue; 351 if (fp == stderr) 352 continue; 353 fclose(fp); 354 } 355 if (image >= 0) { 356 close(image); 357 image = -1; 358 } 359 clrbuf(stdout); 360 printf("Interrupt\n"); 361 signal(SIGINT, stop); 362 reset(0); 363 } 364 365 /* 366 * Announce the presence of the current Mail version, 367 * give the message count, and print a header listing. 368 */ 369 370 char *greeting = "Mail version 2.0 %s. Type ? for help.\n"; 371 372 announce() 373 { 374 int vec[2]; 375 extern char *version; 376 register struct message *mp; 377 378 if (value("hold") != NOSTR) 379 for (mp = &message[0]; mp < &message[msgCount]; mp++) 380 mp->m_flag |= MPRESERVE; 381 vec[0] = 1; 382 vec[1] = 0; 383 if (value("quiet") == NOSTR) 384 printf(greeting, version); 385 if (msgCount == 1) 386 printf("1 message:\n"); 387 else 388 printf("%d messages:\n", msgCount); 389 headers(vec); 390 } 391 392 strace() {} 393 394 /* 395 * Print the current version number. 396 */ 397 398 pversion(e) 399 { 400 printf(greeting, version); 401 return(0); 402 } 403