Add define for Kirk Smith's USR Courier driver. Change default baud
[unix-history] / usr / src / usr.bin / mail / lex.c
CommitLineData
9552e6b8
DF
1/*
2 * Copyright (c) 1980 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
2ae9f53f 7#ifndef lint
fee4667a 8static char *sccsid = "@(#)lex.c 5.4 (Berkeley) %G%";
9552e6b8 9#endif not lint
ac72cd41
KS
10
11#include "rcv.h"
fb4186f8 12#include <sys/stat.h>
c17bf5b6 13#include <errno.h>
ac72cd41
KS
14
15/*
16 * Mail -- a mail program
17 *
18 * Lexical processing of commands.
19 */
20
07c257b5 21char *prompt = "& ";
e5717bff
KS
22
23/*
24 * Set up editing on the given file name.
25 * If isedit is true, we are considered to be editing the file,
26 * otherwise we are reading our mail which has signficance for
27 * mbox and so forth.
28 */
29
30setfile(name, isedit)
31 char *name;
32{
33 FILE *ibuf;
34 int i;
fb4186f8 35 struct stat stb;
e5717bff
KS
36 static int shudclob;
37 static char efile[128];
38 extern char tempMesg[];
c17bf5b6 39 extern int errno;
e5717bff 40
531fa87e 41 if ((ibuf = fopen(name, "r")) == NULL)
e5717bff 42 return(-1);
c17bf5b6
S
43
44 if (fstat(fileno(ibuf), &stb) < 0) {
45 fclose(ibuf);
46 return (-1);
47 }
48
49 switch (stb.st_mode & S_IFMT) {
50 case S_IFDIR:
51 fclose(ibuf);
52 errno = EISDIR;
53 return (-1);
54
55 case S_IFREG:
56 break;
57
58 default:
59 fclose(ibuf);
60 errno = EINVAL;
61 return (-1);
62 }
63
64 if (!edit && stb.st_size == 0) {
65 fclose(ibuf);
66 return(-1);
fb4186f8 67 }
e5717bff
KS
68
69 /*
70 * Looks like all will be well. We must now relinquish our
177b7a73 71 * hold on the current set of stuff. Must hold signals
e5717bff
KS
72 * while we are reading the new file, else we will ruin
73 * the message[] data structure.
74 */
75
177b7a73 76 holdsigs();
e5717bff
KS
77 if (shudclob) {
78 if (edit)
79 edstop();
80 else
81 quit();
82 }
83
84 /*
85 * Copy the messages into /tmp
86 * and set pointers.
87 */
88
89 readonly = 0;
90 if ((i = open(name, 1)) < 0)
91 readonly++;
92 else
93 close(i);
94 if (shudclob) {
95 fclose(itf);
96 fclose(otf);
97 }
98 shudclob = 1;
99 edit = isedit;
100 strncpy(efile, name, 128);
101 editfile = efile;
128ec005
KS
102 if (name != mailname)
103 strcpy(mailname, name);
e5717bff
KS
104 mailsize = fsize(ibuf);
105 if ((otf = fopen(tempMesg, "w")) == NULL) {
106 perror(tempMesg);
107 exit(1);
108 }
109 if ((itf = fopen(tempMesg, "r")) == NULL) {
110 perror(tempMesg);
111 exit(1);
112 }
113 remove(tempMesg);
114 setptr(ibuf);
115 setmsize(msgCount);
116 fclose(ibuf);
177b7a73 117 relsesigs();
335704ca 118 sawcom = 0;
e5717bff
KS
119 return(0);
120}
ac72cd41
KS
121
122/*
123 * Interpret user commands one by one. If standard input is not a tty,
124 * print no prompt.
125 */
126
127int *msgvec;
128
129commands()
130{
7a05656e 131 int eofloop, shudprompt, stop();
ac72cd41
KS
132 register int n;
133 char linebuf[LINESIZE];
de274ec3 134 int hangup(), contin();
ac72cd41 135
ea394d88 136# ifdef VMUNIX
d28ecfdf 137 sigset(SIGCONT, SIG_DFL);
ea394d88 138# endif VMUNIX
7a05656e 139 if (rcvmode && !sourcing) {
726c3356
KS
140 if (sigset(SIGINT, SIG_IGN) != SIG_IGN)
141 sigset(SIGINT, stop);
142 if (sigset(SIGHUP, SIG_IGN) != SIG_IGN)
143 sigset(SIGHUP, hangup);
144 }
7a05656e 145 shudprompt = intty && !sourcing;
ac72cd41
KS
146 for (;;) {
147 setexit();
ac72cd41
KS
148
149 /*
150 * Print the prompt, if needed. Clear out
151 * string space, and flush the output.
152 */
153
154 if (!rcvmode && !sourcing)
155 return;
853e9d55 156 eofloop = 0;
73f94fab 157top:
7a05656e 158 if (shudprompt) {
28bcd25d 159 printf(prompt);
80187484 160 fflush(stdout);
6c85fa2a 161# ifdef VMUNIX
d28ecfdf 162 sigset(SIGCONT, contin);
ea394d88 163# endif VMUNIX
28bcd25d 164 } else
80187484 165 fflush(stdout);
ac72cd41
KS
166 sreset();
167
168 /*
169 * Read a line of commands from the current input
170 * and handle end of file specially.
171 */
172
173 n = 0;
174 for (;;) {
175 if (readline(input, &linebuf[n]) <= 0) {
176 if (n != 0)
177 break;
7a05656e
KS
178 if (loading)
179 return;
ac72cd41
KS
180 if (sourcing) {
181 unstack();
182 goto more;
183 }
07c257b5 184 if (value("ignoreeof") != NOSTR && shudprompt) {
853e9d55
KS
185 if (++eofloop < 25) {
186 printf("Use \"quit\" to quit.\n");
187 goto top;
188 }
73f94fab 189 }
7a05656e
KS
190 if (edit)
191 edstop();
ac72cd41
KS
192 return;
193 }
194 if ((n = strlen(linebuf)) == 0)
195 break;
196 n--;
197 if (linebuf[n] != '\\')
198 break;
199 linebuf[n++] = ' ';
200 }
ea394d88 201# ifdef VMUNIX
d28ecfdf 202 sigset(SIGCONT, SIG_DFL);
ea394d88 203# endif VMUNIX
2cd8e0a9 204 if (execute(linebuf, 0))
ac72cd41
KS
205 return;
206more: ;
207 }
208}
209
210/*
211 * Execute a single command. If the command executed
212 * is "quit," then return non-zero so that the caller
213 * will know to return back to main, if he cares.
2cd8e0a9 214 * Contxt is non-zero if called while composing mail.
ac72cd41
KS
215 */
216
2cd8e0a9 217execute(linebuf, contxt)
ac72cd41
KS
218 char linebuf[];
219{
220 char word[LINESIZE];
221 char *arglist[MAXARGC];
222 struct cmd *com;
223 register char *cp, *cp2;
224 register int c;
e5717bff 225 int muvec[2];
ac72cd41
KS
226 int edstop(), e;
227
228 /*
229 * Strip the white space away from the beginning
230 * of the command, then scan out a word, which
231 * consists of anything except digits and white space.
232 *
233 * Handle ! escapes differently to get the correct
234 * lexical conventions.
235 */
236
237 cp = linebuf;
238 while (any(*cp, " \t"))
239 cp++;
240 if (*cp == '!') {
241 if (sourcing) {
242 printf("Can't \"!\" while sourcing\n");
243 unstack();
244 return(0);
245 }
246 shell(cp+1);
247 return(0);
248 }
249 cp2 = word;
c0132ef0 250 while (*cp && !any(*cp, " \t0123456789$^.:/-+*'\""))
ac72cd41
KS
251 *cp2++ = *cp++;
252 *cp2 = '\0';
253
254 /*
255 * Look up the command; if not found, bitch.
256 * Normally, a blank command would map to the
257 * first command in the table; while sourcing,
258 * however, we ignore blank lines to eliminate
259 * confusion.
260 */
261
262 if (sourcing && equal(word, ""))
263 return(0);
264 com = lex(word);
265 if (com == NONE) {
7a05656e
KS
266 printf("Unknown command: \"%s\"\n", word);
267 if (loading)
268 return(1);
ac72cd41
KS
269 if (sourcing)
270 unstack();
271 return(0);
272 }
273
128ec005
KS
274 /*
275 * See if we should execute the command -- if a conditional
276 * we always execute it, otherwise, check the state of cond.
277 */
278
989e19a2 279 if ((com->c_argtype & F) == 0)
128ec005
KS
280 if (cond == CRCV && !rcvmode || cond == CSEND && rcvmode)
281 return(0);
282
ac72cd41
KS
283 /*
284 * Special case so that quit causes a return to
285 * main, who will call the quit code directly.
286 * If we are in a source file, just unstack.
287 */
288
289 if (com->c_func == edstop && sourcing) {
7a05656e
KS
290 if (loading)
291 return(1);
ac72cd41
KS
292 unstack();
293 return(0);
294 }
295 if (!edit && com->c_func == edstop) {
726c3356 296 sigset(SIGINT, SIG_IGN);
ac72cd41
KS
297 return(1);
298 }
299
300 /*
301 * Process the arguments to the command, depending
302 * on the type he expects. Default to an error.
303 * If we are sourcing an interactive command, it's
304 * an error.
305 */
306
307 if (!rcvmode && (com->c_argtype & M) == 0) {
308 printf("May not execute \"%s\" while sending\n",
309 com->c_name);
7a05656e
KS
310 if (loading)
311 return(1);
2cd8e0a9
KS
312 if (sourcing)
313 unstack();
ac72cd41
KS
314 return(0);
315 }
316 if (sourcing && com->c_argtype & I) {
317 printf("May not execute \"%s\" while sourcing\n",
318 com->c_name);
7a05656e
KS
319 if (loading)
320 return(1);
ac72cd41
KS
321 unstack();
322 return(0);
323 }
e5717bff
KS
324 if (readonly && com->c_argtype & W) {
325 printf("May not execute \"%s\" -- message file is read only\n",
326 com->c_name);
7a05656e
KS
327 if (loading)
328 return(1);
e5717bff
KS
329 if (sourcing)
330 unstack();
331 return(0);
332 }
2cd8e0a9
KS
333 if (contxt && com->c_argtype & R) {
334 printf("Cannot recursively invoke \"%s\"\n", com->c_name);
335 return(0);
336 }
ac72cd41 337 e = 1;
2cd8e0a9 338 switch (com->c_argtype & ~(F|P|I|M|T|W|R)) {
ac72cd41
KS
339 case MSGLIST:
340 /*
341 * A message list defaulting to nearest forward
342 * legal message.
343 */
7a05656e
KS
344 if (msgvec == 0) {
345 printf("Illegal use of \"message list\"\n");
346 return(-1);
347 }
ac72cd41
KS
348 if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
349 break;
350 if (c == 0) {
351 *msgvec = first(com->c_msgflag,
352 com->c_msgmask);
353 msgvec[1] = NULL;
354 }
355 if (*msgvec == NULL) {
356 printf("No applicable messages\n");
357 break;
358 }
359 e = (*com->c_func)(msgvec);
360 break;
361
362 case NDMLIST:
363 /*
364 * A message list with no defaults, but no error
365 * if none exist.
366 */
7a05656e
KS
367 if (msgvec == 0) {
368 printf("Illegal use of \"message list\"\n");
369 return(-1);
370 }
ac72cd41
KS
371 if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
372 break;
373 e = (*com->c_func)(msgvec);
374 break;
375
376 case STRLIST:
377 /*
378 * Just the straight string, with
379 * leading blanks removed.
380 */
381 while (any(*cp, " \t"))
382 cp++;
383 e = (*com->c_func)(cp);
384 break;
385
386 case RAWLIST:
387 /*
388 * A vector of strings, in shell style.
389 */
fee4667a
S
390 if ((c = getrawlist(cp, arglist,
391 sizeof arglist / sizeof *arglist)) < 0)
ac72cd41
KS
392 break;
393 if (c < com->c_minargs) {
394 printf("%s requires at least %d arg(s)\n",
395 com->c_name, com->c_minargs);
396 break;
397 }
398 if (c > com->c_maxargs) {
399 printf("%s takes no more than %d arg(s)\n",
400 com->c_name, com->c_maxargs);
401 break;
402 }
403 e = (*com->c_func)(arglist);
404 break;
405
406 case NOLIST:
407 /*
408 * Just the constant zero, for exiting,
409 * eg.
410 */
411 e = (*com->c_func)(0);
412 break;
413
414 default:
415 panic("Unknown argtype");
416 }
417
418 /*
419 * Exit the current source file on
420 * error.
421 */
422
7a05656e
KS
423 if (e && loading)
424 return(1);
ac72cd41
KS
425 if (e && sourcing)
426 unstack();
427 if (com->c_func == edstop)
428 return(1);
429 if (value("autoprint") != NOSTR && com->c_argtype & P)
e5717bff
KS
430 if ((dot->m_flag & MDELETED) == 0) {
431 muvec[0] = dot - &message[0] + 1;
432 muvec[1] = 0;
433 type(muvec);
434 }
128ec005 435 if (!sourcing && (com->c_argtype & T) == 0)
ac72cd41
KS
436 sawcom = 1;
437 return(0);
438}
439
de274ec3
KS
440/*
441 * When we wake up after ^Z, reprint the prompt.
442 */
443contin(s)
444{
445
07c257b5 446 printf(prompt);
de274ec3
KS
447 fflush(stdout);
448}
449
726c3356
KS
450/*
451 * Branch here on hangup signal and simulate quit.
452 */
453hangup()
454{
726c3356 455
177b7a73 456 holdsigs();
726c3356
KS
457 if (edit) {
458 if (setexit())
459 exit(0);
460 edstop();
461 }
462 else
463 quit();
464 exit(0);
465}
466
e5717bff
KS
467/*
468 * Set the size of the message vector used to construct argument
469 * lists to message list functions.
470 */
471
472setmsize(sz)
473{
474
475 if (msgvec != (int *) 0)
476 cfree(msgvec);
477 msgvec = (int *) calloc((unsigned) (sz + 1), sizeof *msgvec);
478}
479
ac72cd41
KS
480/*
481 * Find the correct command in the command table corresponding
482 * to the passed command "word"
483 */
484
485struct cmd *
486lex(word)
487 char word[];
488{
489 register struct cmd *cp;
490 extern struct cmd cmdtab[];
491
492 for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++)
493 if (isprefix(word, cp->c_name))
494 return(cp);
495 return(NONE);
496}
497
498/*
499 * Determine if as1 is a valid prefix of as2.
500 * Return true if yep.
501 */
502
503isprefix(as1, as2)
504 char *as1, *as2;
505{
506 register char *s1, *s2;
507
508 s1 = as1;
509 s2 = as2;
510 while (*s1++ == *s2)
511 if (*s2++ == '\0')
512 return(1);
513 return(*--s1 == '\0');
514}
515
516/*
517 * The following gets called on receipt of a rubout. This is
518 * to abort printout of a command, mainly.
519 * Dispatching here when command() is inactive crashes rcv.
520 * Close all open files except 0, 1, 2, and the temporary.
521 * The special call to getuserid() is needed so it won't get
522 * annoyed about losing its open file.
523 * Also, unstack all source files.
524 */
525
e39b1d85
KS
526int inithdr; /* am printing startup headers */
527
46053c99
S
528#ifdef _NFILE
529static
530_fwalk(function)
531 register int (*function)();
532{
533 register FILE *iop;
534
535 for (iop = _iob; iop < _iob + _NFILE; iop++)
536 (*function)(iop);
537}
538#endif
539
540static
541xclose(iop)
542 register FILE *iop;
543{
544 if (iop == stdin || iop == stdout ||
545 iop == stderr || iop == itf || iop == otf)
546 return;
547
548 if (iop != pipef)
549 fclose(iop);
550 else {
551 pclose(pipef);
552 pipef = NULL;
553 }
554}
555
726c3356 556stop(s)
ac72cd41
KS
557{
558 register FILE *fp;
559
ea394d88
KS
560# ifndef VMUNIX
561 s = SIGINT;
562# endif VMUNIX
ac72cd41 563 noreset = 0;
e39b1d85
KS
564 if (!inithdr)
565 sawcom++;
566 inithdr = 0;
ac72cd41
KS
567 while (sourcing)
568 unstack();
569 getuserid((char *) -1);
46053c99
S
570
571 /*
572 * Walk through all the open FILEs, applying xclose() to them
573 */
574 _fwalk(xclose);
575
ac72cd41
KS
576 if (image >= 0) {
577 close(image);
578 image = -1;
579 }
80187484 580 fprintf(stderr, "Interrupt\n");
4bc721c3 581# ifndef VMUNIX
ea394d88
KS
582 signal(s, stop);
583# endif
ac72cd41
KS
584 reset(0);
585}
586
587/*
588 * Announce the presence of the current Mail version,
589 * give the message count, and print a header listing.
590 */
591
a0aaf589 592char *greeting = "Mail version %s. Type ? for help.\n";
ac72cd41 593
e5717bff 594announce(pr)
ac72cd41 595{
f3bfa857 596 int vec[2], mdot;
ac72cd41 597 extern char *version;
74745497 598
125e424c
CL
599 if (pr && value("quiet") == NOSTR)
600 printf(greeting, version);
74745497
KS
601 mdot = newfileinfo();
602 vec[0] = mdot;
603 vec[1] = 0;
74745497 604 dot = &message[mdot - 1];
e39b1d85
KS
605 if (msgCount > 0 && !noheader) {
606 inithdr++;
74745497 607 headers(vec);
e39b1d85
KS
608 inithdr = 0;
609 }
74745497
KS
610}
611
612/*
613 * Announce information about the file we are editing.
614 * Return a likely place to set dot.
615 */
74745497
KS
616newfileinfo()
617{
ac72cd41 618 register struct message *mp;
83be1edb 619 register int u, n, mdot, d, s;
46d3d6df 620 char fname[BUFSIZ], zname[BUFSIZ], *ename;
ac72cd41 621
c52f0860
KS
622 for (mp = &message[0]; mp < &message[msgCount]; mp++)
623 if (mp->m_flag & MNEW)
624 break;
f3bfa857
KS
625 if (mp >= &message[msgCount])
626 for (mp = &message[0]; mp < &message[msgCount]; mp++)
627 if ((mp->m_flag & MREAD) == 0)
628 break;
c52f0860 629 if (mp < &message[msgCount])
f3bfa857 630 mdot = mp - &message[0] + 1;
c52f0860 631 else
f3bfa857 632 mdot = 1;
83be1edb 633 s = d = 0;
128ec005
KS
634 for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
635 if (mp->m_flag & MNEW)
636 n++;
637 if ((mp->m_flag & MREAD) == 0)
638 u++;
83be1edb
KS
639 if (mp->m_flag & MDELETED)
640 d++;
641 if (mp->m_flag & MSAVED)
642 s++;
128ec005 643 }
46d3d6df
KS
644 ename = mailname;
645 if (getfold(fname) >= 0) {
646 strcat(fname, "/");
647 if (strncmp(fname, mailname, strlen(fname)) == 0) {
648 sprintf(zname, "+%s", mailname + strlen(fname));
649 ename = zname;
650 }
651 }
652 printf("\"%s\": ", ename);
74745497
KS
653 if (msgCount == 1)
654 printf("1 message");
655 else
656 printf("%d messages", msgCount);
128ec005
KS
657 if (n > 0)
658 printf(" %d new", n);
659 if (u-n > 0)
660 printf(" %d unread", u);
83be1edb
KS
661 if (d > 0)
662 printf(" %d deleted", d);
663 if (s > 0)
664 printf(" %d saved", s);
74745497
KS
665 if (readonly)
666 printf(" [Read only]");
e5717bff 667 printf("\n");
74745497 668 return(mdot);
ac72cd41
KS
669}
670
671strace() {}
672
673/*
674 * Print the current version number.
675 */
676
677pversion(e)
678{
a0aaf589 679 printf("Version %s\n", version);
ac72cd41
KS
680 return(0);
681}
7a05656e
KS
682
683/*
684 * Load a file of user definitions.
685 */
686load(name)
687 char *name;
688{
689 register FILE *in, *oldin;
690
691 if ((in = fopen(name, "r")) == NULL)
692 return;
693 oldin = input;
694 input = in;
695 loading = 1;
696 sourcing = 1;
697 commands();
698 loading = 0;
699 sourcing = 0;
700 input = oldin;
701 fclose(in);
702}