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