reorganize window management to allow display routines to set
[unix-history] / usr / src / usr.bin / mail / lex.c
CommitLineData
2ae9f53f
SL
1#ifndef lint
2static char sccsid[] = "@(#)lex.c 2.14 (Berkeley) %G%";
3#endif
ac72cd41
KS
4
5#include "rcv.h"
6
7/*
8 * Mail -- a mail program
9 *
10 * Lexical processing of commands.
11 */
12
07c257b5 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) {
28bcd25d
SL
124 printf(prompt);
125 flush();
6c85fa2a 126# ifdef VMUNIX
d28ecfdf 127 sigset(SIGCONT, contin);
ea394d88 128# endif VMUNIX
28bcd25d
SL
129 } else
130 flush();
ac72cd41
KS
131 sreset();
132
133 /*
134 * Read a line of commands from the current input
135 * and handle end of file specially.
136 */
137
138 n = 0;
139 for (;;) {
140 if (readline(input, &linebuf[n]) <= 0) {
141 if (n != 0)
142 break;
7a05656e
KS
143 if (loading)
144 return;
ac72cd41
KS
145 if (sourcing) {
146 unstack();
147 goto more;
148 }
07c257b5 149 if (value("ignoreeof") != NOSTR && shudprompt) {
853e9d55
KS
150 if (++eofloop < 25) {
151 printf("Use \"quit\" to quit.\n");
152 goto top;
153 }
73f94fab 154 }
7a05656e
KS
155 if (edit)
156 edstop();
ac72cd41
KS
157 return;
158 }
159 if ((n = strlen(linebuf)) == 0)
160 break;
161 n--;
162 if (linebuf[n] != '\\')
163 break;
164 linebuf[n++] = ' ';
165 }
ea394d88 166# ifdef VMUNIX
d28ecfdf 167 sigset(SIGCONT, SIG_DFL);
ea394d88 168# endif VMUNIX
2cd8e0a9 169 if (execute(linebuf, 0))
ac72cd41
KS
170 return;
171more: ;
172 }
173}
174
175/*
176 * Execute a single command. If the command executed
177 * is "quit," then return non-zero so that the caller
178 * will know to return back to main, if he cares.
2cd8e0a9 179 * Contxt is non-zero if called while composing mail.
ac72cd41
KS
180 */
181
2cd8e0a9 182execute(linebuf, contxt)
ac72cd41
KS
183 char linebuf[];
184{
185 char word[LINESIZE];
186 char *arglist[MAXARGC];
187 struct cmd *com;
188 register char *cp, *cp2;
189 register int c;
e5717bff 190 int muvec[2];
ac72cd41
KS
191 int edstop(), e;
192
193 /*
194 * Strip the white space away from the beginning
195 * of the command, then scan out a word, which
196 * consists of anything except digits and white space.
197 *
198 * Handle ! escapes differently to get the correct
199 * lexical conventions.
200 */
201
202 cp = linebuf;
203 while (any(*cp, " \t"))
204 cp++;
205 if (*cp == '!') {
206 if (sourcing) {
207 printf("Can't \"!\" while sourcing\n");
208 unstack();
209 return(0);
210 }
211 shell(cp+1);
212 return(0);
213 }
214 cp2 = word;
c0132ef0 215 while (*cp && !any(*cp, " \t0123456789$^.:/-+*'\""))
ac72cd41
KS
216 *cp2++ = *cp++;
217 *cp2 = '\0';
218
219 /*
220 * Look up the command; if not found, bitch.
221 * Normally, a blank command would map to the
222 * first command in the table; while sourcing,
223 * however, we ignore blank lines to eliminate
224 * confusion.
225 */
226
227 if (sourcing && equal(word, ""))
228 return(0);
229 com = lex(word);
230 if (com == NONE) {
7a05656e
KS
231 printf("Unknown command: \"%s\"\n", word);
232 if (loading)
233 return(1);
ac72cd41
KS
234 if (sourcing)
235 unstack();
236 return(0);
237 }
238
128ec005
KS
239 /*
240 * See if we should execute the command -- if a conditional
241 * we always execute it, otherwise, check the state of cond.
242 */
243
989e19a2 244 if ((com->c_argtype & F) == 0)
128ec005
KS
245 if (cond == CRCV && !rcvmode || cond == CSEND && rcvmode)
246 return(0);
247
ac72cd41
KS
248 /*
249 * Special case so that quit causes a return to
250 * main, who will call the quit code directly.
251 * If we are in a source file, just unstack.
252 */
253
254 if (com->c_func == edstop && sourcing) {
7a05656e
KS
255 if (loading)
256 return(1);
ac72cd41
KS
257 unstack();
258 return(0);
259 }
260 if (!edit && com->c_func == edstop) {
726c3356 261 sigset(SIGINT, SIG_IGN);
ac72cd41
KS
262 return(1);
263 }
264
265 /*
266 * Process the arguments to the command, depending
267 * on the type he expects. Default to an error.
268 * If we are sourcing an interactive command, it's
269 * an error.
270 */
271
272 if (!rcvmode && (com->c_argtype & M) == 0) {
273 printf("May not execute \"%s\" while sending\n",
274 com->c_name);
7a05656e
KS
275 if (loading)
276 return(1);
2cd8e0a9
KS
277 if (sourcing)
278 unstack();
ac72cd41
KS
279 return(0);
280 }
281 if (sourcing && com->c_argtype & I) {
282 printf("May not execute \"%s\" while sourcing\n",
283 com->c_name);
7a05656e
KS
284 if (loading)
285 return(1);
ac72cd41
KS
286 unstack();
287 return(0);
288 }
e5717bff
KS
289 if (readonly && com->c_argtype & W) {
290 printf("May not execute \"%s\" -- message file is read only\n",
291 com->c_name);
7a05656e
KS
292 if (loading)
293 return(1);
e5717bff
KS
294 if (sourcing)
295 unstack();
296 return(0);
297 }
2cd8e0a9
KS
298 if (contxt && com->c_argtype & R) {
299 printf("Cannot recursively invoke \"%s\"\n", com->c_name);
300 return(0);
301 }
ac72cd41 302 e = 1;
2cd8e0a9 303 switch (com->c_argtype & ~(F|P|I|M|T|W|R)) {
ac72cd41
KS
304 case MSGLIST:
305 /*
306 * A message list defaulting to nearest forward
307 * legal message.
308 */
7a05656e
KS
309 if (msgvec == 0) {
310 printf("Illegal use of \"message list\"\n");
311 return(-1);
312 }
ac72cd41
KS
313 if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
314 break;
315 if (c == 0) {
316 *msgvec = first(com->c_msgflag,
317 com->c_msgmask);
318 msgvec[1] = NULL;
319 }
320 if (*msgvec == NULL) {
321 printf("No applicable messages\n");
322 break;
323 }
324 e = (*com->c_func)(msgvec);
325 break;
326
327 case NDMLIST:
328 /*
329 * A message list with no defaults, but no error
330 * if none exist.
331 */
7a05656e
KS
332 if (msgvec == 0) {
333 printf("Illegal use of \"message list\"\n");
334 return(-1);
335 }
ac72cd41
KS
336 if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
337 break;
338 e = (*com->c_func)(msgvec);
339 break;
340
341 case STRLIST:
342 /*
343 * Just the straight string, with
344 * leading blanks removed.
345 */
346 while (any(*cp, " \t"))
347 cp++;
348 e = (*com->c_func)(cp);
349 break;
350
351 case RAWLIST:
352 /*
353 * A vector of strings, in shell style.
354 */
355 if ((c = getrawlist(cp, arglist)) < 0)
356 break;
357 if (c < com->c_minargs) {
358 printf("%s requires at least %d arg(s)\n",
359 com->c_name, com->c_minargs);
360 break;
361 }
362 if (c > com->c_maxargs) {
363 printf("%s takes no more than %d arg(s)\n",
364 com->c_name, com->c_maxargs);
365 break;
366 }
367 e = (*com->c_func)(arglist);
368 break;
369
370 case NOLIST:
371 /*
372 * Just the constant zero, for exiting,
373 * eg.
374 */
375 e = (*com->c_func)(0);
376 break;
377
378 default:
379 panic("Unknown argtype");
380 }
381
382 /*
383 * Exit the current source file on
384 * error.
385 */
386
7a05656e
KS
387 if (e && loading)
388 return(1);
ac72cd41
KS
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
ea394d88
KS
496# ifndef VMUNIX
497 s = SIGINT;
498# endif VMUNIX
ac72cd41 499 noreset = 0;
e39b1d85
KS
500 if (!inithdr)
501 sawcom++;
502 inithdr = 0;
ac72cd41
KS
503 while (sourcing)
504 unstack();
505 getuserid((char *) -1);
506 for (fp = &_iob[0]; fp < &_iob[_NFILE]; fp++) {
507 if (fp == stdin || fp == stdout)
508 continue;
509 if (fp == itf || fp == otf)
510 continue;
511 if (fp == stderr)
512 continue;
e5717bff
KS
513 if (fp == pipef) {
514 pclose(pipef);
515 pipef = NULL;
516 continue;
517 }
ac72cd41
KS
518 fclose(fp);
519 }
520 if (image >= 0) {
521 close(image);
522 image = -1;
523 }
524 clrbuf(stdout);
525 printf("Interrupt\n");
4bc721c3 526# ifndef VMUNIX
ea394d88
KS
527 signal(s, stop);
528# endif
ac72cd41
KS
529 reset(0);
530}
531
532/*
533 * Announce the presence of the current Mail version,
534 * give the message count, and print a header listing.
535 */
536
a0aaf589 537char *greeting = "Mail version %s. Type ? for help.\n";
ac72cd41 538
e5717bff 539announce(pr)
ac72cd41 540{
f3bfa857 541 int vec[2], mdot;
ac72cd41 542 extern char *version;
74745497 543
125e424c
CL
544 if (pr && value("quiet") == NOSTR)
545 printf(greeting, version);
74745497
KS
546 mdot = newfileinfo();
547 vec[0] = mdot;
548 vec[1] = 0;
74745497 549 dot = &message[mdot - 1];
e39b1d85
KS
550 if (msgCount > 0 && !noheader) {
551 inithdr++;
74745497 552 headers(vec);
e39b1d85
KS
553 inithdr = 0;
554 }
74745497
KS
555}
556
557/*
558 * Announce information about the file we are editing.
559 * Return a likely place to set dot.
560 */
74745497
KS
561newfileinfo()
562{
ac72cd41 563 register struct message *mp;
83be1edb 564 register int u, n, mdot, d, s;
46d3d6df 565 char fname[BUFSIZ], zname[BUFSIZ], *ename;
ac72cd41 566
c52f0860
KS
567 for (mp = &message[0]; mp < &message[msgCount]; mp++)
568 if (mp->m_flag & MNEW)
569 break;
f3bfa857
KS
570 if (mp >= &message[msgCount])
571 for (mp = &message[0]; mp < &message[msgCount]; mp++)
572 if ((mp->m_flag & MREAD) == 0)
573 break;
c52f0860 574 if (mp < &message[msgCount])
f3bfa857 575 mdot = mp - &message[0] + 1;
c52f0860 576 else
f3bfa857 577 mdot = 1;
83be1edb 578 s = d = 0;
128ec005
KS
579 for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
580 if (mp->m_flag & MNEW)
581 n++;
582 if ((mp->m_flag & MREAD) == 0)
583 u++;
83be1edb
KS
584 if (mp->m_flag & MDELETED)
585 d++;
586 if (mp->m_flag & MSAVED)
587 s++;
128ec005 588 }
46d3d6df
KS
589 ename = mailname;
590 if (getfold(fname) >= 0) {
591 strcat(fname, "/");
592 if (strncmp(fname, mailname, strlen(fname)) == 0) {
593 sprintf(zname, "+%s", mailname + strlen(fname));
594 ename = zname;
595 }
596 }
597 printf("\"%s\": ", ename);
74745497
KS
598 if (msgCount == 1)
599 printf("1 message");
600 else
601 printf("%d messages", msgCount);
128ec005
KS
602 if (n > 0)
603 printf(" %d new", n);
604 if (u-n > 0)
605 printf(" %d unread", u);
83be1edb
KS
606 if (d > 0)
607 printf(" %d deleted", d);
608 if (s > 0)
609 printf(" %d saved", s);
74745497
KS
610 if (readonly)
611 printf(" [Read only]");
e5717bff 612 printf("\n");
74745497 613 return(mdot);
ac72cd41
KS
614}
615
616strace() {}
617
618/*
619 * Print the current version number.
620 */
621
622pversion(e)
623{
a0aaf589 624 printf("Version %s\n", version);
ac72cd41
KS
625 return(0);
626}
7a05656e
KS
627
628/*
629 * Load a file of user definitions.
630 */
631load(name)
632 char *name;
633{
634 register FILE *in, *oldin;
635
636 if ((in = fopen(name, "r")) == NULL)
637 return;
638 oldin = input;
639 input = in;
640 loading = 1;
641 sourcing = 1;
642 commands();
643 loading = 0;
644 sourcing = 0;
645 input = oldin;
646 fclose(in);
647}