fixup in BIG_ENDIAN macro
[unix-history] / usr / src / usr.bin / mail / lex.c
CommitLineData
9552e6b8
DF
1/*
2 * Copyright (c) 1980 Regents of the University of California.
0c5f72fb
KB
3 * All rights reserved.
4 *
f15db449 5 * %sccs.include.redist.c%
9552e6b8
DF
6 */
7
acfc7e9b 8#ifndef lint
a0d23834 9static char sccsid[] = "@(#)lex.c 5.24 (Berkeley) %G%";
acfc7e9b 10#endif /* not lint */
ac72cd41
KS
11
12#include "rcv.h"
c17bf5b6 13#include <errno.h>
a0d23834
KB
14#include <fcntl.h>
15#include "extern.h"
ac72cd41
KS
16
17/*
18 * Mail -- a mail program
19 *
20 * Lexical processing of commands.
21 */
22
07c257b5 23char *prompt = "& ";
e5717bff
KS
24
25/*
26 * Set up editing on the given file name.
8421b6a6
EW
27 * If the first character of name is %, we are considered to be
28 * editing the file, otherwise we are reading our mail which has
29 * signficance for mbox and so forth.
e5717bff 30 */
a0d23834 31int
8421b6a6 32setfile(name)
e5717bff
KS
33 char *name;
34{
35 FILE *ibuf;
36 int i;
fb4186f8 37 struct stat stb;
8421b6a6
EW
38 char isedit = *name != '%';
39 char *who = name[1] ? name + 1 : myname;
e5717bff 40 static int shudclob;
e5717bff 41 extern char tempMesg[];
c17bf5b6 42 extern int errno;
e5717bff 43
8421b6a6
EW
44 if ((name = expand(name)) == NOSTR)
45 return -1;
46
07b0d286 47 if ((ibuf = Fopen(name, "r")) == NULL) {
8421b6a6
EW
48 if (!isedit && errno == ENOENT)
49 goto nomail;
2ee3bce2 50 perror(name);
e5717bff 51 return(-1);
2ee3bce2 52 }
c17bf5b6
S
53
54 if (fstat(fileno(ibuf), &stb) < 0) {
2ee3bce2 55 perror("fstat");
07b0d286 56 Fclose(ibuf);
c17bf5b6
S
57 return (-1);
58 }
59
60 switch (stb.st_mode & S_IFMT) {
61 case S_IFDIR:
07b0d286 62 Fclose(ibuf);
c17bf5b6 63 errno = EISDIR;
2ee3bce2 64 perror(name);
c17bf5b6
S
65 return (-1);
66
67 case S_IFREG:
68 break;
69
70 default:
07b0d286 71 Fclose(ibuf);
c17bf5b6 72 errno = EINVAL;
2ee3bce2 73 perror(name);
c17bf5b6
S
74 return (-1);
75 }
76
e5717bff
KS
77 /*
78 * Looks like all will be well. We must now relinquish our
177b7a73 79 * hold on the current set of stuff. Must hold signals
e5717bff
KS
80 * while we are reading the new file, else we will ruin
81 * the message[] data structure.
82 */
83
177b7a73 84 holdsigs();
2ee3bce2
EW
85 if (shudclob)
86 quit();
e5717bff
KS
87
88 /*
89 * Copy the messages into /tmp
90 * and set pointers.
91 */
92
93 readonly = 0;
94 if ((i = open(name, 1)) < 0)
95 readonly++;
96 else
97 close(i);
98 if (shudclob) {
99 fclose(itf);
100 fclose(otf);
101 }
102 shudclob = 1;
103 edit = isedit;
8421b6a6 104 strcpy(prevfile, mailname);
128ec005
KS
105 if (name != mailname)
106 strcpy(mailname, name);
e5717bff
KS
107 mailsize = fsize(ibuf);
108 if ((otf = fopen(tempMesg, "w")) == NULL) {
109 perror(tempMesg);
110 exit(1);
111 }
112 if ((itf = fopen(tempMesg, "r")) == NULL) {
113 perror(tempMesg);
114 exit(1);
115 }
f82cf0f0 116 rm(tempMesg);
e5717bff
KS
117 setptr(ibuf);
118 setmsize(msgCount);
07b0d286 119 Fclose(ibuf);
177b7a73 120 relsesigs();
335704ca 121 sawcom = 0;
8421b6a6
EW
122 if (!edit && msgCount == 0) {
123nomail:
124 fprintf(stderr, "No mail for %s\n", who);
125 return -1;
126 }
e5717bff
KS
127 return(0);
128}
ac72cd41 129
2ee3bce2
EW
130int *msgvec;
131int reset_on_stop; /* do a reset() if stopped */
132
ac72cd41
KS
133/*
134 * Interpret user commands one by one. If standard input is not a tty,
135 * print no prompt.
136 */
a0d23834 137void
ac72cd41
KS
138commands()
139{
2ee3bce2 140 int eofloop = 0;
ac72cd41
KS
141 register int n;
142 char linebuf[LINESIZE];
a6714963 143 void intr(), stop(), hangup();
ac72cd41 144
2ee3bce2 145 if (!sourcing) {
828615a1 146 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
2ee3bce2 147 signal(SIGINT, intr);
828615a1
EW
148 if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
149 signal(SIGHUP, hangup);
2ee3bce2
EW
150 signal(SIGTSTP, stop);
151 signal(SIGTTOU, stop);
152 signal(SIGTTIN, stop);
726c3356 153 }
2ee3bce2 154 setexit();
ac72cd41 155 for (;;) {
ac72cd41
KS
156 /*
157 * Print the prompt, if needed. Clear out
158 * string space, and flush the output.
159 */
686f6134 160 if (!sourcing && value("interactive") != NOSTR) {
2ee3bce2 161 reset_on_stop = 1;
28bcd25d 162 printf(prompt);
828615a1
EW
163 }
164 fflush(stdout);
f674e088 165 sreset();
ac72cd41
KS
166 /*
167 * Read a line of commands from the current input
168 * and handle end of file specially.
169 */
ac72cd41
KS
170 n = 0;
171 for (;;) {
2ee3bce2
EW
172 if (readline(input, &linebuf[n], LINESIZE - n) < 0) {
173 if (n == 0)
174 n = -1;
175 break;
ac72cd41
KS
176 }
177 if ((n = strlen(linebuf)) == 0)
178 break;
179 n--;
180 if (linebuf[n] != '\\')
181 break;
182 linebuf[n++] = ' ';
183 }
2ee3bce2
EW
184 reset_on_stop = 0;
185 if (n < 0) {
186 /* eof */
187 if (loading)
188 break;
189 if (sourcing) {
190 unstack();
191 continue;
192 }
193 if (value("interactive") != NOSTR &&
194 value("ignoreeof") != NOSTR &&
195 ++eofloop < 25) {
196 printf("Use \"quit\" to quit.\n");
197 continue;
198 }
199 break;
200 }
201 eofloop = 0;
2cd8e0a9 202 if (execute(linebuf, 0))
2ee3bce2 203 break;
ac72cd41
KS
204 }
205}
206
207/*
2ee3bce2
EW
208 * Execute a single command.
209 * Command functions return 0 for success, 1 for error, and -1
210 * for abort. A 1 or -1 aborts a load or source. A -1 aborts
211 * the interactive command loop.
2cd8e0a9 212 * Contxt is non-zero if called while composing mail.
ac72cd41 213 */
a0d23834 214int
2cd8e0a9 215execute(linebuf, contxt)
ac72cd41 216 char linebuf[];
a0d23834 217 int contxt;
ac72cd41
KS
218{
219 char word[LINESIZE];
220 char *arglist[MAXARGC];
221 struct cmd *com;
222 register char *cp, *cp2;
223 register int c;
e5717bff 224 int muvec[2];
2ee3bce2 225 int e = 1;
ac72cd41
KS
226
227 /*
228 * Strip the white space away from the beginning
229 * of the command, then scan out a word, which
230 * consists of anything except digits and white space.
231 *
232 * Handle ! escapes differently to get the correct
233 * lexical conventions.
234 */
235
828615a1
EW
236 for (cp = linebuf; isspace(*cp); cp++)
237 ;
ac72cd41
KS
238 if (*cp == '!') {
239 if (sourcing) {
240 printf("Can't \"!\" while sourcing\n");
2ee3bce2 241 goto out;
ac72cd41
KS
242 }
243 shell(cp+1);
244 return(0);
245 }
246 cp2 = word;
470c33f3 247 while (*cp && index(" \t0123456789$^.:/-+*'\"", *cp) == NOSTR)
ac72cd41
KS
248 *cp2++ = *cp++;
249 *cp2 = '\0';
250
251 /*
252 * Look up the command; if not found, bitch.
253 * Normally, a blank command would map to the
254 * first command in the table; while sourcing,
255 * however, we ignore blank lines to eliminate
256 * confusion.
257 */
258
828615a1 259 if (sourcing && *word == '\0')
ac72cd41
KS
260 return(0);
261 com = lex(word);
262 if (com == NONE) {
7a05656e 263 printf("Unknown command: \"%s\"\n", word);
2ee3bce2 264 goto out;
ac72cd41
KS
265 }
266
128ec005
KS
267 /*
268 * See if we should execute the command -- if a conditional
269 * we always execute it, otherwise, check the state of cond.
270 */
271
989e19a2 272 if ((com->c_argtype & F) == 0)
128ec005
KS
273 if (cond == CRCV && !rcvmode || cond == CSEND && rcvmode)
274 return(0);
275
ac72cd41
KS
276 /*
277 * Process the arguments to the command, depending
278 * on the type he expects. Default to an error.
279 * If we are sourcing an interactive command, it's
280 * an error.
281 */
282
283 if (!rcvmode && (com->c_argtype & M) == 0) {
284 printf("May not execute \"%s\" while sending\n",
285 com->c_name);
2ee3bce2 286 goto out;
ac72cd41
KS
287 }
288 if (sourcing && com->c_argtype & I) {
289 printf("May not execute \"%s\" while sourcing\n",
290 com->c_name);
2ee3bce2 291 goto out;
ac72cd41 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);
2ee3bce2 296 goto out;
e5717bff 297 }
2cd8e0a9
KS
298 if (contxt && com->c_argtype & R) {
299 printf("Cannot recursively invoke \"%s\"\n", com->c_name);
2ee3bce2 300 goto out;
2cd8e0a9 301 }
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");
2ee3bce2 310 break;
7a05656e 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");
2ee3bce2 333 break;
7a05656e 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 */
828615a1 345 while (isspace(*cp))
ac72cd41
KS
346 cp++;
347 e = (*com->c_func)(cp);
348 break;
349
350 case RAWLIST:
351 /*
352 * A vector of strings, in shell style.
353 */
fee4667a
S
354 if ((c = getrawlist(cp, arglist,
355 sizeof arglist / sizeof *arglist)) < 0)
ac72cd41
KS
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
2ee3bce2 382out:
ac72cd41
KS
383 /*
384 * Exit the current source file on
385 * error.
386 */
2ee3bce2
EW
387 if (e) {
388 if (e < 0)
389 return 1;
390 if (loading)
391 return 1;
392 if (sourcing)
393 unstack();
394 return 0;
395 }
ac72cd41 396 if (value("autoprint") != NOSTR && com->c_argtype & P)
e5717bff
KS
397 if ((dot->m_flag & MDELETED) == 0) {
398 muvec[0] = dot - &message[0] + 1;
399 muvec[1] = 0;
400 type(muvec);
401 }
128ec005 402 if (!sourcing && (com->c_argtype & T) == 0)
ac72cd41
KS
403 sawcom = 1;
404 return(0);
405}
406
e5717bff
KS
407/*
408 * Set the size of the message vector used to construct argument
409 * lists to message list functions.
410 */
a0d23834 411void
e5717bff 412setmsize(sz)
a0d23834 413 int sz;
e5717bff
KS
414{
415
828615a1
EW
416 if (msgvec != 0)
417 cfree((char *) msgvec);
e5717bff
KS
418 msgvec = (int *) calloc((unsigned) (sz + 1), sizeof *msgvec);
419}
420
ac72cd41
KS
421/*
422 * Find the correct command in the command table corresponding
423 * to the passed command "word"
424 */
425
426struct cmd *
427lex(word)
428 char word[];
429{
430 register struct cmd *cp;
431 extern struct cmd cmdtab[];
432
433 for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++)
434 if (isprefix(word, cp->c_name))
435 return(cp);
436 return(NONE);
437}
438
439/*
440 * Determine if as1 is a valid prefix of as2.
441 * Return true if yep.
442 */
a0d23834 443int
ac72cd41
KS
444isprefix(as1, as2)
445 char *as1, *as2;
446{
447 register char *s1, *s2;
448
449 s1 = as1;
450 s2 = as2;
451 while (*s1++ == *s2)
452 if (*s2++ == '\0')
453 return(1);
454 return(*--s1 == '\0');
455}
456
457/*
828615a1 458 * The following gets called on receipt of an interrupt. This is
ac72cd41
KS
459 * to abort printout of a command, mainly.
460 * Dispatching here when command() is inactive crashes rcv.
461 * Close all open files except 0, 1, 2, and the temporary.
ac72cd41
KS
462 * Also, unstack all source files.
463 */
464
e39b1d85
KS
465int inithdr; /* am printing startup headers */
466
828615a1 467/*ARGSUSED*/
a6714963 468void
2ee3bce2 469intr(s)
a0d23834 470 int s;
ac72cd41 471{
ac72cd41
KS
472
473 noreset = 0;
e39b1d85
KS
474 if (!inithdr)
475 sawcom++;
476 inithdr = 0;
ac72cd41
KS
477 while (sourcing)
478 unstack();
46053c99 479
07b0d286 480 close_all_files();
46053c99 481
ac72cd41
KS
482 if (image >= 0) {
483 close(image);
484 image = -1;
485 }
80187484 486 fprintf(stderr, "Interrupt\n");
ac72cd41
KS
487 reset(0);
488}
489
2ee3bce2
EW
490/*
491 * When we wake up after ^Z, reprint the prompt.
492 */
a6714963 493void
2ee3bce2 494stop(s)
a0d23834 495 int s;
2ee3bce2 496{
e62a1467 497 sig_t old_action = signal(s, SIG_DFL);
2ee3bce2
EW
498
499 sigsetmask(sigblock(0) & ~sigmask(s));
500 kill(0, s);
501 sigblock(sigmask(s));
502 signal(s, old_action);
503 if (reset_on_stop) {
504 reset_on_stop = 0;
505 reset(0);
506 }
507}
508
509/*
510 * Branch here on hangup signal and simulate "exit".
511 */
512/*ARGSUSED*/
a6714963 513void
2ee3bce2 514hangup(s)
a0d23834 515 int s;
2ee3bce2
EW
516{
517
518 /* nothing to do? */
519 exit(1);
520}
521
ac72cd41
KS
522/*
523 * Announce the presence of the current Mail version,
524 * give the message count, and print a header listing.
525 */
a0d23834 526void
4d20fb09 527announce()
ac72cd41 528{
f3bfa857 529 int vec[2], mdot;
74745497
KS
530
531 mdot = newfileinfo();
532 vec[0] = mdot;
533 vec[1] = 0;
74745497 534 dot = &message[mdot - 1];
209a87d1 535 if (msgCount > 0 && value("noheader") == NOSTR) {
e39b1d85 536 inithdr++;
74745497 537 headers(vec);
e39b1d85
KS
538 inithdr = 0;
539 }
74745497
KS
540}
541
542/*
543 * Announce information about the file we are editing.
544 * Return a likely place to set dot.
545 */
a0d23834 546int
74745497
KS
547newfileinfo()
548{
ac72cd41 549 register struct message *mp;
83be1edb 550 register int u, n, mdot, d, s;
46d3d6df 551 char fname[BUFSIZ], zname[BUFSIZ], *ename;
ac72cd41 552
c52f0860
KS
553 for (mp = &message[0]; mp < &message[msgCount]; mp++)
554 if (mp->m_flag & MNEW)
555 break;
f3bfa857
KS
556 if (mp >= &message[msgCount])
557 for (mp = &message[0]; mp < &message[msgCount]; mp++)
558 if ((mp->m_flag & MREAD) == 0)
559 break;
c52f0860 560 if (mp < &message[msgCount])
f3bfa857 561 mdot = mp - &message[0] + 1;
c52f0860 562 else
f3bfa857 563 mdot = 1;
83be1edb 564 s = d = 0;
128ec005
KS
565 for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
566 if (mp->m_flag & MNEW)
567 n++;
568 if ((mp->m_flag & MREAD) == 0)
569 u++;
83be1edb
KS
570 if (mp->m_flag & MDELETED)
571 d++;
572 if (mp->m_flag & MSAVED)
573 s++;
128ec005 574 }
46d3d6df
KS
575 ename = mailname;
576 if (getfold(fname) >= 0) {
577 strcat(fname, "/");
578 if (strncmp(fname, mailname, strlen(fname)) == 0) {
579 sprintf(zname, "+%s", mailname + strlen(fname));
580 ename = zname;
581 }
582 }
583 printf("\"%s\": ", ename);
74745497
KS
584 if (msgCount == 1)
585 printf("1 message");
586 else
587 printf("%d messages", msgCount);
128ec005
KS
588 if (n > 0)
589 printf(" %d new", n);
590 if (u-n > 0)
591 printf(" %d unread", u);
83be1edb
KS
592 if (d > 0)
593 printf(" %d deleted", d);
594 if (s > 0)
595 printf(" %d saved", s);
74745497
KS
596 if (readonly)
597 printf(" [Read only]");
e5717bff 598 printf("\n");
74745497 599 return(mdot);
ac72cd41
KS
600}
601
ac72cd41
KS
602/*
603 * Print the current version number.
604 */
605
828615a1 606/*ARGSUSED*/
a0d23834 607int
ac72cd41 608pversion(e)
a0d23834 609 int e;
ac72cd41 610{
001d60ad 611 extern char *version;
828615a1 612
a0aaf589 613 printf("Version %s\n", version);
ac72cd41
KS
614 return(0);
615}
7a05656e
KS
616
617/*
618 * Load a file of user definitions.
619 */
a0d23834 620void
7a05656e
KS
621load(name)
622 char *name;
623{
624 register FILE *in, *oldin;
625
07b0d286 626 if ((in = Fopen(name, "r")) == NULL)
7a05656e
KS
627 return;
628 oldin = input;
629 input = in;
630 loading = 1;
631 sourcing = 1;
632 commands();
633 loading = 0;
634 sourcing = 0;
635 input = oldin;
07b0d286 636 Fclose(in);
7a05656e 637}