Annotation of src/usr.bin/mail/lex.c, Revision 1.3
1.3 ! deraadt 1: /* $OpenBSD: lex.c,v 1.7 1996/06/08 19:48:28 christos Exp $ */
! 2: /* $NetBSD: lex.c,v 1.7 1996/06/08 19:48:28 christos Exp $ */
1.2 niklas 3:
1.1 deraadt 4: /*
5: * Copyright (c) 1980, 1993
6: * The Regents of the University of California. All rights reserved.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
14: * notice, this list of conditions and the following disclaimer in the
15: * documentation and/or other materials provided with the distribution.
16: * 3. All advertising materials mentioning features or use of this software
17: * must display the following acknowledgement:
18: * This product includes software developed by the University of
19: * California, Berkeley and its contributors.
20: * 4. Neither the name of the University nor the names of its contributors
21: * may be used to endorse or promote products derived from this software
22: * without specific prior written permission.
23: *
24: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34: * SUCH DAMAGE.
35: */
36:
37: #ifndef lint
1.3 ! deraadt 38: #if 0
! 39: static char sccsid[] = "@(#)lex.c 8.1 (Berkeley) 6/6/93";
! 40: #else
! 41: static char rcsid[] = "$OpenBSD: lex.c,v 1.7 1996/06/08 19:48:28 christos Exp $";
! 42: #endif
1.1 deraadt 43: #endif /* not lint */
44:
45: #include "rcv.h"
46: #include <errno.h>
47: #include <fcntl.h>
48: #include "extern.h"
49:
50: /*
51: * Mail -- a mail program
52: *
53: * Lexical processing of commands.
54: */
55:
56: char *prompt = "& ";
57:
58: /*
59: * Set up editing on the given file name.
60: * If the first character of name is %, we are considered to be
61: * editing the file, otherwise we are reading our mail which has
62: * signficance for mbox and so forth.
63: */
64: int
65: setfile(name)
66: char *name;
67: {
68: FILE *ibuf;
69: int i;
70: struct stat stb;
71: char isedit = *name != '%';
72: char *who = name[1] ? name + 1 : myname;
73: static int shudclob;
74: extern char *tempMesg;
75: extern int errno;
76:
77: if ((name = expand(name)) == NOSTR)
78: return -1;
79:
80: if ((ibuf = Fopen(name, "r")) == NULL) {
81: if (!isedit && errno == ENOENT)
82: goto nomail;
83: perror(name);
84: return(-1);
85: }
86:
87: if (fstat(fileno(ibuf), &stb) < 0) {
88: perror("fstat");
89: Fclose(ibuf);
90: return (-1);
91: }
92:
93: switch (stb.st_mode & S_IFMT) {
94: case S_IFDIR:
95: Fclose(ibuf);
96: errno = EISDIR;
97: perror(name);
98: return (-1);
99:
100: case S_IFREG:
101: break;
102:
103: default:
104: Fclose(ibuf);
105: errno = EINVAL;
106: perror(name);
107: return (-1);
108: }
109:
110: /*
111: * Looks like all will be well. We must now relinquish our
112: * hold on the current set of stuff. Must hold signals
113: * while we are reading the new file, else we will ruin
114: * the message[] data structure.
115: */
116:
117: holdsigs();
118: if (shudclob)
119: quit();
120:
121: /*
122: * Copy the messages into /tmp
123: * and set pointers.
124: */
125:
126: readonly = 0;
127: if ((i = open(name, 1)) < 0)
128: readonly++;
129: else
130: close(i);
131: if (shudclob) {
132: fclose(itf);
133: fclose(otf);
134: }
135: shudclob = 1;
136: edit = isedit;
137: strcpy(prevfile, mailname);
138: if (name != mailname)
139: strcpy(mailname, name);
140: mailsize = fsize(ibuf);
141: if ((otf = fopen(tempMesg, "w")) == NULL) {
142: perror(tempMesg);
143: exit(1);
144: }
145: (void) fcntl(fileno(otf), F_SETFD, 1);
146: if ((itf = fopen(tempMesg, "r")) == NULL) {
147: perror(tempMesg);
148: exit(1);
149: }
150: (void) fcntl(fileno(itf), F_SETFD, 1);
151: rm(tempMesg);
152: setptr(ibuf);
153: setmsize(msgCount);
154: Fclose(ibuf);
155: relsesigs();
156: sawcom = 0;
157: if (!edit && msgCount == 0) {
158: nomail:
159: fprintf(stderr, "No mail for %s\n", who);
160: return -1;
161: }
162: return(0);
163: }
164:
165: int *msgvec;
166: int reset_on_stop; /* do a reset() if stopped */
167:
168: /*
169: * Interpret user commands one by one. If standard input is not a tty,
170: * print no prompt.
171: */
172: void
173: commands()
174: {
175: int eofloop = 0;
176: register int n;
177: char linebuf[LINESIZE];
1.3 ! deraadt 178: #if __GNUC__
! 179: /* Avoid longjmp clobbering */
! 180: (void) &eofloop;
! 181: #endif
1.1 deraadt 182:
183: if (!sourcing) {
184: if (signal(SIGINT, SIG_IGN) != SIG_IGN)
185: signal(SIGINT, intr);
186: if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
187: signal(SIGHUP, hangup);
188: signal(SIGTSTP, stop);
189: signal(SIGTTOU, stop);
190: signal(SIGTTIN, stop);
191: }
192: setexit();
193: for (;;) {
194: /*
195: * Print the prompt, if needed. Clear out
196: * string space, and flush the output.
197: */
198: if (!sourcing && value("interactive") != NOSTR) {
199: reset_on_stop = 1;
200: printf(prompt);
201: }
202: fflush(stdout);
203: sreset();
204: /*
205: * Read a line of commands from the current input
206: * and handle end of file specially.
207: */
208: n = 0;
209: for (;;) {
210: if (readline(input, &linebuf[n], LINESIZE - n) < 0) {
211: if (n == 0)
212: n = -1;
213: break;
214: }
215: if ((n = strlen(linebuf)) == 0)
216: break;
217: n--;
218: if (linebuf[n] != '\\')
219: break;
220: linebuf[n++] = ' ';
221: }
222: reset_on_stop = 0;
223: if (n < 0) {
224: /* eof */
225: if (loading)
226: break;
227: if (sourcing) {
228: unstack();
229: continue;
230: }
231: if (value("interactive") != NOSTR &&
232: value("ignoreeof") != NOSTR &&
233: ++eofloop < 25) {
234: printf("Use \"quit\" to quit.\n");
235: continue;
236: }
237: break;
238: }
239: eofloop = 0;
240: if (execute(linebuf, 0))
241: break;
242: }
243: }
244:
245: /*
246: * Execute a single command.
247: * Command functions return 0 for success, 1 for error, and -1
248: * for abort. A 1 or -1 aborts a load or source. A -1 aborts
249: * the interactive command loop.
250: * Contxt is non-zero if called while composing mail.
251: */
252: int
253: execute(linebuf, contxt)
254: char linebuf[];
255: int contxt;
256: {
257: char word[LINESIZE];
258: char *arglist[MAXARGC];
1.3 ! deraadt 259: const struct cmd *com = NULL;
1.1 deraadt 260: register char *cp, *cp2;
261: register int c;
262: int muvec[2];
263: int e = 1;
264:
265: /*
266: * Strip the white space away from the beginning
267: * of the command, then scan out a word, which
268: * consists of anything except digits and white space.
269: *
270: * Handle ! escapes differently to get the correct
271: * lexical conventions.
272: */
273:
274: for (cp = linebuf; isspace(*cp); cp++)
275: ;
276: if (*cp == '!') {
277: if (sourcing) {
278: printf("Can't \"!\" while sourcing\n");
279: goto out;
280: }
281: shell(cp+1);
282: return(0);
283: }
284: cp2 = word;
285: while (*cp && index(" \t0123456789$^.:/-+*'\"", *cp) == NOSTR)
286: *cp2++ = *cp++;
287: *cp2 = '\0';
288:
289: /*
290: * Look up the command; if not found, bitch.
291: * Normally, a blank command would map to the
292: * first command in the table; while sourcing,
293: * however, we ignore blank lines to eliminate
294: * confusion.
295: */
296:
297: if (sourcing && *word == '\0')
298: return(0);
299: com = lex(word);
300: if (com == NONE) {
301: printf("Unknown command: \"%s\"\n", word);
302: goto out;
303: }
304:
305: /*
306: * See if we should execute the command -- if a conditional
307: * we always execute it, otherwise, check the state of cond.
308: */
309:
310: if ((com->c_argtype & F) == 0)
1.3 ! deraadt 311: if ((cond == CRCV && !rcvmode) || (cond == CSEND && rcvmode))
1.1 deraadt 312: return(0);
313:
314: /*
315: * Process the arguments to the command, depending
316: * on the type he expects. Default to an error.
317: * If we are sourcing an interactive command, it's
318: * an error.
319: */
320:
321: if (!rcvmode && (com->c_argtype & M) == 0) {
322: printf("May not execute \"%s\" while sending\n",
323: com->c_name);
324: goto out;
325: }
326: if (sourcing && com->c_argtype & I) {
327: printf("May not execute \"%s\" while sourcing\n",
328: com->c_name);
329: goto out;
330: }
331: if (readonly && com->c_argtype & W) {
332: printf("May not execute \"%s\" -- message file is read only\n",
333: com->c_name);
334: goto out;
335: }
336: if (contxt && com->c_argtype & R) {
337: printf("Cannot recursively invoke \"%s\"\n", com->c_name);
338: goto out;
339: }
340: switch (com->c_argtype & ~(F|P|I|M|T|W|R)) {
341: case MSGLIST:
342: /*
343: * A message list defaulting to nearest forward
344: * legal message.
345: */
346: if (msgvec == 0) {
347: printf("Illegal use of \"message list\"\n");
348: break;
349: }
350: if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
351: break;
352: if (c == 0) {
353: *msgvec = first(com->c_msgflag,
354: com->c_msgmask);
355: msgvec[1] = NULL;
356: }
357: if (*msgvec == NULL) {
358: printf("No applicable messages\n");
359: break;
360: }
361: e = (*com->c_func)(msgvec);
362: break;
363:
364: case NDMLIST:
365: /*
366: * A message list with no defaults, but no error
367: * if none exist.
368: */
369: if (msgvec == 0) {
370: printf("Illegal use of \"message list\"\n");
371: break;
372: }
373: if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
374: break;
375: e = (*com->c_func)(msgvec);
376: break;
377:
378: case STRLIST:
379: /*
380: * Just the straight string, with
381: * leading blanks removed.
382: */
383: while (isspace(*cp))
384: cp++;
385: e = (*com->c_func)(cp);
386: break;
387:
388: case RAWLIST:
389: /*
390: * A vector of strings, in shell style.
391: */
392: if ((c = getrawlist(cp, arglist,
393: sizeof arglist / sizeof *arglist)) < 0)
394: break;
395: if (c < com->c_minargs) {
396: printf("%s requires at least %d arg(s)\n",
397: com->c_name, com->c_minargs);
398: break;
399: }
400: if (c > com->c_maxargs) {
401: printf("%s takes no more than %d arg(s)\n",
402: com->c_name, com->c_maxargs);
403: break;
404: }
405: e = (*com->c_func)(arglist);
406: break;
407:
408: case NOLIST:
409: /*
410: * Just the constant zero, for exiting,
411: * eg.
412: */
413: e = (*com->c_func)(0);
414: break;
415:
416: default:
417: panic("Unknown argtype");
418: }
419:
420: out:
421: /*
422: * Exit the current source file on
423: * error.
424: */
425: if (e) {
426: if (e < 0)
427: return 1;
428: if (loading)
429: return 1;
430: if (sourcing)
431: unstack();
432: return 0;
433: }
1.3 ! deraadt 434: if (com == NULL)
! 435: return(0);
1.1 deraadt 436: if (value("autoprint") != NOSTR && com->c_argtype & P)
437: if ((dot->m_flag & MDELETED) == 0) {
438: muvec[0] = dot - &message[0] + 1;
439: muvec[1] = 0;
440: type(muvec);
441: }
442: if (!sourcing && (com->c_argtype & T) == 0)
443: sawcom = 1;
444: return(0);
445: }
446:
447: /*
448: * Set the size of the message vector used to construct argument
449: * lists to message list functions.
450: */
451: void
452: setmsize(sz)
453: int sz;
454: {
455:
456: if (msgvec != 0)
457: free((char *) msgvec);
458: msgvec = (int *) calloc((unsigned) (sz + 1), sizeof *msgvec);
459: }
460:
461: /*
462: * Find the correct command in the command table corresponding
463: * to the passed command "word"
464: */
465:
1.2 niklas 466: const struct cmd *
1.1 deraadt 467: lex(word)
468: char word[];
469: {
1.2 niklas 470: extern const struct cmd cmdtab[];
471: register const struct cmd *cp;
1.1 deraadt 472:
473: for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++)
474: if (isprefix(word, cp->c_name))
475: return(cp);
476: return(NONE);
477: }
478:
479: /*
480: * Determine if as1 is a valid prefix of as2.
481: * Return true if yep.
482: */
483: int
484: isprefix(as1, as2)
485: char *as1, *as2;
486: {
487: register char *s1, *s2;
488:
489: s1 = as1;
490: s2 = as2;
491: while (*s1++ == *s2)
492: if (*s2++ == '\0')
493: return(1);
494: return(*--s1 == '\0');
495: }
496:
497: /*
498: * The following gets called on receipt of an interrupt. This is
499: * to abort printout of a command, mainly.
500: * Dispatching here when command() is inactive crashes rcv.
501: * Close all open files except 0, 1, 2, and the temporary.
502: * Also, unstack all source files.
503: */
504:
505: int inithdr; /* am printing startup headers */
506:
507: /*ARGSUSED*/
508: void
509: intr(s)
510: int s;
511: {
512:
513: noreset = 0;
514: if (!inithdr)
515: sawcom++;
516: inithdr = 0;
517: while (sourcing)
518: unstack();
519:
520: close_all_files();
521:
522: if (image >= 0) {
523: close(image);
524: image = -1;
525: }
526: fprintf(stderr, "Interrupt\n");
527: reset(0);
528: }
529:
530: /*
531: * When we wake up after ^Z, reprint the prompt.
532: */
533: void
534: stop(s)
535: int s;
536: {
537: sig_t old_action = signal(s, SIG_DFL);
1.3 ! deraadt 538: sigset_t nset;
1.1 deraadt 539:
1.3 ! deraadt 540: sigemptyset(&nset);
! 541: sigaddset(&nset, s);
! 542: sigprocmask(SIG_UNBLOCK, &nset, NULL);
1.1 deraadt 543: kill(0, s);
1.3 ! deraadt 544: sigprocmask(SIG_BLOCK, &nset, NULL);
1.1 deraadt 545: signal(s, old_action);
546: if (reset_on_stop) {
547: reset_on_stop = 0;
548: reset(0);
549: }
550: }
551:
552: /*
553: * Branch here on hangup signal and simulate "exit".
554: */
555: /*ARGSUSED*/
556: void
557: hangup(s)
558: int s;
559: {
560:
561: /* nothing to do? */
562: exit(1);
563: }
564:
565: /*
566: * Announce the presence of the current Mail version,
567: * give the message count, and print a header listing.
568: */
569: void
570: announce()
571: {
572: int vec[2], mdot;
573:
574: mdot = newfileinfo();
575: vec[0] = mdot;
576: vec[1] = 0;
577: dot = &message[mdot - 1];
578: if (msgCount > 0 && value("noheader") == NOSTR) {
579: inithdr++;
580: headers(vec);
581: inithdr = 0;
582: }
583: }
584:
585: /*
586: * Announce information about the file we are editing.
587: * Return a likely place to set dot.
588: */
589: int
590: newfileinfo()
591: {
592: register struct message *mp;
593: register int u, n, mdot, d, s;
594: char fname[BUFSIZ], zname[BUFSIZ], *ename;
595:
596: for (mp = &message[0]; mp < &message[msgCount]; mp++)
597: if (mp->m_flag & MNEW)
598: break;
599: if (mp >= &message[msgCount])
600: for (mp = &message[0]; mp < &message[msgCount]; mp++)
601: if ((mp->m_flag & MREAD) == 0)
602: break;
603: if (mp < &message[msgCount])
604: mdot = mp - &message[0] + 1;
605: else
606: mdot = 1;
607: s = d = 0;
608: for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
609: if (mp->m_flag & MNEW)
610: n++;
611: if ((mp->m_flag & MREAD) == 0)
612: u++;
613: if (mp->m_flag & MDELETED)
614: d++;
615: if (mp->m_flag & MSAVED)
616: s++;
617: }
618: ename = mailname;
619: if (getfold(fname) >= 0) {
620: strcat(fname, "/");
621: if (strncmp(fname, mailname, strlen(fname)) == 0) {
622: sprintf(zname, "+%s", mailname + strlen(fname));
623: ename = zname;
624: }
625: }
626: printf("\"%s\": ", ename);
627: if (msgCount == 1)
628: printf("1 message");
629: else
630: printf("%d messages", msgCount);
631: if (n > 0)
632: printf(" %d new", n);
633: if (u-n > 0)
634: printf(" %d unread", u);
635: if (d > 0)
636: printf(" %d deleted", d);
637: if (s > 0)
638: printf(" %d saved", s);
639: if (readonly)
640: printf(" [Read only]");
641: printf("\n");
642: return(mdot);
643: }
644:
645: /*
646: * Print the current version number.
647: */
648:
649: /*ARGSUSED*/
650: int
1.3 ! deraadt 651: pversion(v)
! 652: void *v;
1.1 deraadt 653: {
654: extern char *version;
655:
656: printf("Version %s\n", version);
657: return(0);
658: }
659:
660: /*
661: * Load a file of user definitions.
662: */
663: void
664: load(name)
665: char *name;
666: {
667: register FILE *in, *oldin;
668:
669: if ((in = Fopen(name, "r")) == NULL)
670: return;
671: oldin = input;
672: input = in;
673: loading = 1;
674: sourcing = 1;
675: commands();
676: loading = 0;
677: sourcing = 0;
678: input = oldin;
679: Fclose(in);
680: }