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