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