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