now uses signal holding facility to avoid losing signals when they must be
[unix-history] / usr / src / usr.bin / mail / lex.c
CommitLineData
ac72cd41
KS
1#
2
3#include "rcv.h"
4
5/*
6 * Mail -- a mail program
7 *
8 * Lexical processing of commands.
9 */
10
de274ec3 11static char *SccsId = "@(#)lex.c 1.17 %G%";
e5717bff
KS
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
20setfile(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[];
726c3356 28 int (*sigs[3])();
e5717bff
KS
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
726c3356 45 sigsave(sigs, SIG_IGN);
e5717bff
KS
46 if (shudclob) {
47 if (edit)
48 edstop();
49 else
50 quit();
51 }
52
53 /*
54 * Copy the messages into /tmp
55 * and set pointers.
56 */
57
58 readonly = 0;
59 if ((i = open(name, 1)) < 0)
60 readonly++;
61 else
62 close(i);
63 if (shudclob) {
64 fclose(itf);
65 fclose(otf);
66 }
67 shudclob = 1;
68 edit = isedit;
69 strncpy(efile, name, 128);
70 editfile = efile;
128ec005
KS
71 if (name != mailname)
72 strcpy(mailname, name);
e5717bff
KS
73 mailsize = fsize(ibuf);
74 if ((otf = fopen(tempMesg, "w")) == NULL) {
75 perror(tempMesg);
76 exit(1);
77 }
78 if ((itf = fopen(tempMesg, "r")) == NULL) {
79 perror(tempMesg);
80 exit(1);
81 }
82 remove(tempMesg);
83 setptr(ibuf);
84 setmsize(msgCount);
85 fclose(ibuf);
726c3356 86 sigret(sigs);
4d931f76 87 shudann = 1;
335704ca 88 sawcom = 0;
e5717bff
KS
89 return(0);
90}
ac72cd41
KS
91
92/*
93 * Interpret user commands one by one. If standard input is not a tty,
94 * print no prompt.
95 */
96
97int *msgvec;
98
99commands()
100{
101 int prompt, firstsw, stop();
102 register int n;
103 char linebuf[LINESIZE];
de274ec3 104 int hangup(), contin();
ac72cd41 105
de274ec3
KS
106 sigset(SIGCONT, contin);
107 sighold(SIGCONT);
726c3356
KS
108 if (rcvmode) {
109 if (sigset(SIGINT, SIG_IGN) != SIG_IGN)
110 sigset(SIGINT, stop);
111 if (sigset(SIGHUP, SIG_IGN) != SIG_IGN)
112 sigset(SIGHUP, hangup);
113 }
ac72cd41
KS
114 input = stdin;
115 prompt = 1;
116 if (!intty)
117 prompt = 0;
118 firstsw = 1;
119 for (;;) {
120 setexit();
121 if (firstsw > 0) {
122 firstsw = 0;
123 source1(mailrc);
124 if (!nosrc)
125 source1(MASTER);
126 }
127
128 /*
129 * How's this for obscure: after we
130 * finish sourcing for the first time,
131 * go off and print the headers!
132 */
133
4d931f76
KS
134 if (shudann && !sourcing) {
135 shudann = 0;
ac72cd41 136 if (rcvmode)
4d931f76 137 announce(edit);
ac72cd41
KS
138 }
139
140 /*
141 * Print the prompt, if needed. Clear out
142 * string space, and flush the output.
143 */
144
145 if (!rcvmode && !sourcing)
146 return;
73f94fab 147top:
de274ec3
KS
148 if (prompt && !sourcing) {
149 sigrelse(SIGCONT);
ac72cd41 150 printf("_\r");
de274ec3 151 }
ac72cd41
KS
152 flush();
153 sreset();
154
155 /*
156 * Read a line of commands from the current input
157 * and handle end of file specially.
158 */
159
160 n = 0;
161 for (;;) {
162 if (readline(input, &linebuf[n]) <= 0) {
163 if (n != 0)
164 break;
165 if (sourcing) {
166 unstack();
167 goto more;
168 }
73f94fab
KS
169 if (value("ignoreeof") != NOSTR && prompt) {
170 printf("Use \"quit\" to quit.\n");
171 goto top;
172 }
726c3356 173 if (!edit)
ac72cd41 174 return;
ac72cd41
KS
175 edstop();
176 return;
177 }
178 if ((n = strlen(linebuf)) == 0)
179 break;
180 n--;
181 if (linebuf[n] != '\\')
182 break;
183 linebuf[n++] = ' ';
184 }
de274ec3 185 sighold(SIGCONT);
2cd8e0a9 186 if (execute(linebuf, 0))
ac72cd41
KS
187 return;
188more: ;
189 }
190}
191
192/*
193 * Execute a single command. If the command executed
194 * is "quit," then return non-zero so that the caller
195 * will know to return back to main, if he cares.
2cd8e0a9 196 * Contxt is non-zero if called while composing mail.
ac72cd41
KS
197 */
198
2cd8e0a9 199execute(linebuf, contxt)
ac72cd41
KS
200 char linebuf[];
201{
202 char word[LINESIZE];
203 char *arglist[MAXARGC];
204 struct cmd *com;
205 register char *cp, *cp2;
206 register int c;
e5717bff 207 int muvec[2];
ac72cd41
KS
208 int edstop(), e;
209
210 /*
211 * Strip the white space away from the beginning
212 * of the command, then scan out a word, which
213 * consists of anything except digits and white space.
214 *
215 * Handle ! escapes differently to get the correct
216 * lexical conventions.
217 */
218
219 cp = linebuf;
220 while (any(*cp, " \t"))
221 cp++;
222 if (*cp == '!') {
223 if (sourcing) {
224 printf("Can't \"!\" while sourcing\n");
225 unstack();
226 return(0);
227 }
228 shell(cp+1);
229 return(0);
230 }
231 cp2 = word;
c0132ef0 232 while (*cp && !any(*cp, " \t0123456789$^.:/-+*'\""))
ac72cd41
KS
233 *cp2++ = *cp++;
234 *cp2 = '\0';
235
236 /*
237 * Look up the command; if not found, bitch.
238 * Normally, a blank command would map to the
239 * first command in the table; while sourcing,
240 * however, we ignore blank lines to eliminate
241 * confusion.
242 */
243
244 if (sourcing && equal(word, ""))
245 return(0);
246 com = lex(word);
247 if (com == NONE) {
248 printf("What?\n");
249 if (sourcing)
250 unstack();
251 return(0);
252 }
253
128ec005
KS
254 /*
255 * See if we should execute the command -- if a conditional
256 * we always execute it, otherwise, check the state of cond.
257 */
258
989e19a2 259 if ((com->c_argtype & F) == 0)
128ec005
KS
260 if (cond == CRCV && !rcvmode || cond == CSEND && rcvmode)
261 return(0);
262
ac72cd41
KS
263 /*
264 * Special case so that quit causes a return to
265 * main, who will call the quit code directly.
266 * If we are in a source file, just unstack.
267 */
268
269 if (com->c_func == edstop && sourcing) {
270 unstack();
271 return(0);
272 }
273 if (!edit && com->c_func == edstop) {
726c3356 274 sigset(SIGINT, SIG_IGN);
ac72cd41
KS
275 return(1);
276 }
277
278 /*
279 * Process the arguments to the command, depending
280 * on the type he expects. Default to an error.
281 * If we are sourcing an interactive command, it's
282 * an error.
283 */
284
285 if (!rcvmode && (com->c_argtype & M) == 0) {
286 printf("May not execute \"%s\" while sending\n",
287 com->c_name);
2cd8e0a9
KS
288 if (sourcing)
289 unstack();
ac72cd41
KS
290 return(0);
291 }
292 if (sourcing && com->c_argtype & I) {
293 printf("May not execute \"%s\" while sourcing\n",
294 com->c_name);
295 unstack();
296 return(0);
297 }
e5717bff
KS
298 if (readonly && com->c_argtype & W) {
299 printf("May not execute \"%s\" -- message file is read only\n",
300 com->c_name);
301 if (sourcing)
302 unstack();
303 return(0);
304 }
2cd8e0a9
KS
305 if (contxt && com->c_argtype & R) {
306 printf("Cannot recursively invoke \"%s\"\n", com->c_name);
307 return(0);
308 }
ac72cd41 309 e = 1;
2cd8e0a9 310 switch (com->c_argtype & ~(F|P|I|M|T|W|R)) {
ac72cd41
KS
311 case MSGLIST:
312 /*
313 * A message list defaulting to nearest forward
314 * legal message.
315 */
316 if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
317 break;
318 if (c == 0) {
319 *msgvec = first(com->c_msgflag,
320 com->c_msgmask);
321 msgvec[1] = NULL;
322 }
323 if (*msgvec == NULL) {
324 printf("No applicable messages\n");
325 break;
326 }
327 e = (*com->c_func)(msgvec);
328 break;
329
330 case NDMLIST:
331 /*
332 * A message list with no defaults, but no error
333 * if none exist.
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 && sourcing)
387 unstack();
388 if (com->c_func == edstop)
389 return(1);
390 if (value("autoprint") != NOSTR && com->c_argtype & P)
e5717bff
KS
391 if ((dot->m_flag & MDELETED) == 0) {
392 muvec[0] = dot - &message[0] + 1;
393 muvec[1] = 0;
394 type(muvec);
395 }
128ec005 396 if (!sourcing && (com->c_argtype & T) == 0)
ac72cd41
KS
397 sawcom = 1;
398 return(0);
399}
400
de274ec3
KS
401/*
402 * When we wake up after ^Z, reprint the prompt.
403 */
404contin(s)
405{
406
407 printf("_\r");
408 fflush(stdout);
409}
410
726c3356
KS
411/*
412 * Branch here on hangup signal and simulate quit.
413 */
414hangup()
415{
416 int (*sigs[3])();
417
418 sigsave(sigs);
419 if (edit) {
420 if (setexit())
421 exit(0);
422 edstop();
423 }
424 else
425 quit();
426 exit(0);
427}
428
e5717bff
KS
429/*
430 * Set the size of the message vector used to construct argument
431 * lists to message list functions.
432 */
433
434setmsize(sz)
435{
436
437 if (msgvec != (int *) 0)
438 cfree(msgvec);
439 msgvec = (int *) calloc((unsigned) (sz + 1), sizeof *msgvec);
440}
441
ac72cd41
KS
442/*
443 * Find the correct command in the command table corresponding
444 * to the passed command "word"
445 */
446
447struct cmd *
448lex(word)
449 char word[];
450{
451 register struct cmd *cp;
452 extern struct cmd cmdtab[];
453
454 for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++)
455 if (isprefix(word, cp->c_name))
456 return(cp);
457 return(NONE);
458}
459
460/*
461 * Determine if as1 is a valid prefix of as2.
462 * Return true if yep.
463 */
464
465isprefix(as1, as2)
466 char *as1, *as2;
467{
468 register char *s1, *s2;
469
470 s1 = as1;
471 s2 = as2;
472 while (*s1++ == *s2)
473 if (*s2++ == '\0')
474 return(1);
475 return(*--s1 == '\0');
476}
477
478/*
479 * The following gets called on receipt of a rubout. This is
480 * to abort printout of a command, mainly.
481 * Dispatching here when command() is inactive crashes rcv.
482 * Close all open files except 0, 1, 2, and the temporary.
483 * The special call to getuserid() is needed so it won't get
484 * annoyed about losing its open file.
485 * Also, unstack all source files.
486 */
487
e39b1d85
KS
488int inithdr; /* am printing startup headers */
489
726c3356 490stop(s)
ac72cd41
KS
491{
492 register FILE *fp;
493
494 noreset = 0;
e39b1d85
KS
495 if (!inithdr)
496 sawcom++;
497 inithdr = 0;
ac72cd41
KS
498 while (sourcing)
499 unstack();
500 getuserid((char *) -1);
501 for (fp = &_iob[0]; fp < &_iob[_NFILE]; fp++) {
502 if (fp == stdin || fp == stdout)
503 continue;
504 if (fp == itf || fp == otf)
505 continue;
506 if (fp == stderr)
507 continue;
e5717bff
KS
508 if (fp == pipef) {
509 pclose(pipef);
510 pipef = NULL;
511 continue;
512 }
ac72cd41
KS
513 fclose(fp);
514 }
515 if (image >= 0) {
516 close(image);
517 image = -1;
518 }
519 clrbuf(stdout);
520 printf("Interrupt\n");
726c3356 521 sigrelse(s);
ac72cd41
KS
522 reset(0);
523}
524
525/*
526 * Announce the presence of the current Mail version,
527 * give the message count, and print a header listing.
528 */
529
530char *greeting = "Mail version 2.0 %s. Type ? for help.\n";
531
e5717bff 532announce(pr)
ac72cd41 533{
f3bfa857 534 int vec[2], mdot;
ac72cd41 535 extern char *version;
74745497
KS
536
537 mdot = newfileinfo();
538 vec[0] = mdot;
539 vec[1] = 0;
540 if (pr && value("quiet") == NOSTR)
541 printf(greeting, version);
542 dot = &message[mdot - 1];
e39b1d85
KS
543 if (msgCount > 0 && !noheader) {
544 inithdr++;
74745497 545 headers(vec);
e39b1d85
KS
546 inithdr = 0;
547 }
74745497
KS
548}
549
550/*
551 * Announce information about the file we are editing.
552 * Return a likely place to set dot.
553 */
554
555newfileinfo()
556{
ac72cd41 557 register struct message *mp;
74745497 558 register int u, n, mdot;
ac72cd41 559
c52f0860
KS
560 for (mp = &message[0]; mp < &message[msgCount]; mp++)
561 if (mp->m_flag & MNEW)
562 break;
f3bfa857
KS
563 if (mp >= &message[msgCount])
564 for (mp = &message[0]; mp < &message[msgCount]; mp++)
565 if ((mp->m_flag & MREAD) == 0)
566 break;
c52f0860 567 if (mp < &message[msgCount])
f3bfa857 568 mdot = mp - &message[0] + 1;
c52f0860 569 else
f3bfa857 570 mdot = 1;
128ec005
KS
571 for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
572 if (mp->m_flag & MNEW)
573 n++;
574 if ((mp->m_flag & MREAD) == 0)
575 u++;
576 }
74745497
KS
577 printf("\"%s\": ", mailname);
578 if (msgCount == 1)
579 printf("1 message");
580 else
581 printf("%d messages", msgCount);
128ec005
KS
582 if (n > 0)
583 printf(" %d new", n);
584 if (u-n > 0)
585 printf(" %d unread", u);
74745497
KS
586 if (readonly)
587 printf(" [Read only]");
e5717bff 588 printf("\n");
74745497 589 return(mdot);
ac72cd41
KS
590}
591
592strace() {}
593
594/*
595 * Print the current version number.
596 */
597
598pversion(e)
599{
600 printf(greeting, version);
601 return(0);
602}