Annotation of src/usr.bin/mail/lex.c, Revision 1.8
1.8 ! millert 1: /* $OpenBSD: lex.c,v 1.7 1997/07/13 23:54:00 millert Exp $ */
1.6 millert 2: /* $NetBSD: lex.c,v 1.10 1997/05/17 19:55:13 pk 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
1.6 millert 39: static char sccsid[] = "@(#)lex.c 8.2 (Berkeley) 4/20/95";
1.3 deraadt 40: #else
1.8 ! millert 41: static char rcsid[] = "$OpenBSD: lex.c,v 1.7 1997/07/13 23:54:00 millert Exp $";
1.3 deraadt 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:
1.8 ! millert 77: if ((name = expand(name)) == NULL)
1.6 millert 78: return(-1);
1.1 deraadt 79:
80: if ((ibuf = Fopen(name, "r")) == NULL) {
81: if (!isedit && errno == ENOENT)
82: goto nomail;
1.6 millert 83: warn(name);
1.1 deraadt 84: return(-1);
85: }
86:
87: if (fstat(fileno(ibuf), &stb) < 0) {
1.6 millert 88: warn("fstat");
89: (void)Fclose(ibuf);
90: return(-1);
1.1 deraadt 91: }
92:
93: switch (stb.st_mode & S_IFMT) {
94: case S_IFDIR:
1.6 millert 95: (void)Fclose(ibuf);
1.1 deraadt 96: errno = EISDIR;
1.6 millert 97: warn(name);
98: return(-1);
1.1 deraadt 99:
100: case S_IFREG:
101: break;
102:
103: default:
1.6 millert 104: (void)Fclose(ibuf);
1.1 deraadt 105: errno = EINVAL;
1.6 millert 106: warn(name);
107: return(-1);
1.1 deraadt 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
1.6 millert 130: (void)close(i);
1.1 deraadt 131: if (shudclob) {
1.6 millert 132: (void)fclose(itf);
133: (void)fclose(otf);
1.1 deraadt 134: }
135: shudclob = 1;
136: edit = isedit;
137: strcpy(prevfile, mailname);
138: if (name != mailname)
139: strcpy(mailname, name);
140: mailsize = fsize(ibuf);
1.6 millert 141: if ((otf = fopen(tempMesg, "w")) == NULL)
142: err(1, tempMesg);
1.7 millert 143: (void)fcntl(fileno(otf), F_SETFD, 1);
1.6 millert 144: if ((itf = fopen(tempMesg, "r")) == NULL)
145: err(1, tempMesg);
1.7 millert 146: (void)fcntl(fileno(itf), F_SETFD, 1);
1.1 deraadt 147: rm(tempMesg);
1.6 millert 148: setptr(ibuf, 0);
1.1 deraadt 149: setmsize(msgCount);
1.6 millert 150: /*
151: * New mail may have arrived while we were reading
152: * the mail file, so reset mailsize to be where
153: * we really are in the file...
154: */
155: mailsize = ftell(ibuf);
156: (void)Fclose(ibuf);
1.1 deraadt 157: relsesigs();
158: sawcom = 0;
159: if (!edit && msgCount == 0) {
160: nomail:
161: fprintf(stderr, "No mail for %s\n", who);
1.6 millert 162: return(-1);
1.1 deraadt 163: }
164: return(0);
165: }
166:
1.6 millert 167: /*
168: * Incorporate any new mail that has arrived since we first
169: * started reading mail.
170: */
171: int
172: incfile()
173: {
174: int newsize;
175: int omsgCount = msgCount;
176: FILE *ibuf;
177:
178: ibuf = Fopen(mailname, "r");
179: if (ibuf == NULL)
180: return(-1);
181: holdsigs();
182: newsize = fsize(ibuf);
183: if (newsize == 0)
184: return(-1); /* mail box is now empty??? */
185: if (newsize < mailsize)
186: return(-1); /* mail box has shrunk??? */
187: if (newsize == mailsize)
188: return(0); /* no new mail */
189: setptr(ibuf, mailsize);
190: setmsize(msgCount);
191: mailsize = ftell(ibuf);
192: (void)Fclose(ibuf);
193: relsesigs();
194: return(msgCount - omsgCount);
195: }
196:
197:
1.1 deraadt 198: int *msgvec;
199: int reset_on_stop; /* do a reset() if stopped */
200:
201: /*
202: * Interpret user commands one by one. If standard input is not a tty,
203: * print no prompt.
204: */
205: void
206: commands()
207: {
208: int eofloop = 0;
209: register int n;
210: char linebuf[LINESIZE];
1.3 deraadt 211: #if __GNUC__
1.8 ! millert 212: /* Avoid siglongjmp clobbering */
1.7 millert 213: (void)&eofloop;
1.3 deraadt 214: #endif
1.1 deraadt 215:
216: if (!sourcing) {
217: if (signal(SIGINT, SIG_IGN) != SIG_IGN)
218: signal(SIGINT, intr);
219: if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
220: signal(SIGHUP, hangup);
221: signal(SIGTSTP, stop);
222: signal(SIGTTOU, stop);
223: signal(SIGTTIN, stop);
224: }
225: setexit();
226: for (;;) {
227: /*
228: * Print the prompt, if needed. Clear out
229: * string space, and flush the output.
230: */
1.8 ! millert 231: if (!sourcing && value("interactive") != NULL) {
! 232: if ((value("autoinc") != NULL) && (incfile() > 0))
1.6 millert 233: puts("New mail has arrived.");
1.1 deraadt 234: reset_on_stop = 1;
235: printf(prompt);
236: }
237: fflush(stdout);
238: sreset();
239: /*
240: * Read a line of commands from the current input
241: * and handle end of file specially.
242: */
243: n = 0;
244: for (;;) {
245: if (readline(input, &linebuf[n], LINESIZE - n) < 0) {
246: if (n == 0)
247: n = -1;
248: break;
249: }
250: if ((n = strlen(linebuf)) == 0)
251: break;
252: n--;
253: if (linebuf[n] != '\\')
254: break;
255: linebuf[n++] = ' ';
256: }
257: reset_on_stop = 0;
258: if (n < 0) {
259: /* eof */
260: if (loading)
261: break;
262: if (sourcing) {
263: unstack();
264: continue;
265: }
1.8 ! millert 266: if (value("interactive") != NULL &&
! 267: value("ignoreeof") != NULL &&
1.1 deraadt 268: ++eofloop < 25) {
1.6 millert 269: puts("Use \"quit\" to quit.");
1.1 deraadt 270: continue;
271: }
272: break;
273: }
274: eofloop = 0;
275: if (execute(linebuf, 0))
276: break;
277: }
278: }
279:
280: /*
281: * Execute a single command.
282: * Command functions return 0 for success, 1 for error, and -1
283: * for abort. A 1 or -1 aborts a load or source. A -1 aborts
284: * the interactive command loop.
285: * Contxt is non-zero if called while composing mail.
286: */
287: int
288: execute(linebuf, contxt)
289: char linebuf[];
290: int contxt;
291: {
292: char word[LINESIZE];
293: char *arglist[MAXARGC];
1.3 deraadt 294: const struct cmd *com = NULL;
1.1 deraadt 295: register char *cp, *cp2;
296: register int c;
297: int muvec[2];
298: int e = 1;
299:
300: /*
301: * Strip the white space away from the beginning
302: * of the command, then scan out a word, which
303: * consists of anything except digits and white space.
304: *
305: * Handle ! escapes differently to get the correct
306: * lexical conventions.
307: */
308:
309: for (cp = linebuf; isspace(*cp); cp++)
310: ;
311: if (*cp == '!') {
312: if (sourcing) {
1.6 millert 313: puts("Can't \"!\" while sourcing");
1.1 deraadt 314: goto out;
315: }
316: shell(cp+1);
317: return(0);
318: }
319: cp2 = word;
1.8 ! millert 320: while (*cp && strchr(" \t0123456789$^.:/-+*'\"", *cp) == NULL)
1.1 deraadt 321: *cp2++ = *cp++;
322: *cp2 = '\0';
323:
324: /*
325: * Look up the command; if not found, bitch.
326: * Normally, a blank command would map to the
327: * first command in the table; while sourcing,
328: * however, we ignore blank lines to eliminate
329: * confusion.
330: */
331:
332: if (sourcing && *word == '\0')
333: return(0);
334: com = lex(word);
335: if (com == NONE) {
336: printf("Unknown command: \"%s\"\n", word);
337: goto out;
338: }
339:
340: /*
341: * See if we should execute the command -- if a conditional
342: * we always execute it, otherwise, check the state of cond.
343: */
344:
345: if ((com->c_argtype & F) == 0)
1.3 deraadt 346: if ((cond == CRCV && !rcvmode) || (cond == CSEND && rcvmode))
1.1 deraadt 347: return(0);
348:
349: /*
350: * Process the arguments to the command, depending
351: * on the type he expects. Default to an error.
352: * If we are sourcing an interactive command, it's
353: * an error.
354: */
355:
356: if (!rcvmode && (com->c_argtype & M) == 0) {
357: printf("May not execute \"%s\" while sending\n",
358: com->c_name);
359: goto out;
360: }
361: if (sourcing && com->c_argtype & I) {
362: printf("May not execute \"%s\" while sourcing\n",
363: com->c_name);
364: goto out;
365: }
366: if (readonly && com->c_argtype & W) {
367: printf("May not execute \"%s\" -- message file is read only\n",
368: com->c_name);
369: goto out;
370: }
371: if (contxt && com->c_argtype & R) {
372: printf("Cannot recursively invoke \"%s\"\n", com->c_name);
373: goto out;
374: }
375: switch (com->c_argtype & ~(F|P|I|M|T|W|R)) {
376: case MSGLIST:
377: /*
378: * A message list defaulting to nearest forward
379: * legal message.
380: */
381: if (msgvec == 0) {
1.6 millert 382: puts("Illegal use of \"message list\"");
1.1 deraadt 383: break;
384: }
385: if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
386: break;
387: if (c == 0) {
388: *msgvec = first(com->c_msgflag,
389: com->c_msgmask);
390: msgvec[1] = NULL;
391: }
392: if (*msgvec == NULL) {
1.6 millert 393: puts("No applicable messages");
1.1 deraadt 394: break;
395: }
396: e = (*com->c_func)(msgvec);
397: break;
398:
399: case NDMLIST:
400: /*
401: * A message list with no defaults, but no error
402: * if none exist.
403: */
404: if (msgvec == 0) {
1.6 millert 405: puts("Illegal use of \"message list\"");
1.1 deraadt 406: break;
407: }
408: if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
409: break;
410: e = (*com->c_func)(msgvec);
411: break;
412:
413: case STRLIST:
414: /*
415: * Just the straight string, with
416: * leading blanks removed.
417: */
418: while (isspace(*cp))
419: cp++;
420: e = (*com->c_func)(cp);
421: break;
422:
423: case RAWLIST:
424: /*
425: * A vector of strings, in shell style.
426: */
427: if ((c = getrawlist(cp, arglist,
1.6 millert 428: sizeof(arglist) / sizeof(*arglist))) < 0)
1.1 deraadt 429: break;
430: if (c < com->c_minargs) {
431: printf("%s requires at least %d arg(s)\n",
432: com->c_name, com->c_minargs);
433: break;
434: }
435: if (c > com->c_maxargs) {
436: printf("%s takes no more than %d arg(s)\n",
437: com->c_name, com->c_maxargs);
438: break;
439: }
440: e = (*com->c_func)(arglist);
441: break;
442:
443: case NOLIST:
444: /*
445: * Just the constant zero, for exiting,
446: * eg.
447: */
448: e = (*com->c_func)(0);
449: break;
450:
451: default:
452: panic("Unknown argtype");
453: }
454:
455: out:
456: /*
457: * Exit the current source file on
458: * error.
459: */
460: if (e) {
461: if (e < 0)
1.6 millert 462: return(1);
1.1 deraadt 463: if (loading)
1.6 millert 464: return(1);
1.1 deraadt 465: if (sourcing)
466: unstack();
1.6 millert 467: return(0);
1.1 deraadt 468: }
1.3 deraadt 469: if (com == NULL)
470: return(0);
1.8 ! millert 471: if (value("autoprint") != NULL && com->c_argtype & P)
1.1 deraadt 472: if ((dot->m_flag & MDELETED) == 0) {
473: muvec[0] = dot - &message[0] + 1;
474: muvec[1] = 0;
475: type(muvec);
476: }
477: if (!sourcing && (com->c_argtype & T) == 0)
478: sawcom = 1;
479: return(0);
480: }
481:
482: /*
483: * Set the size of the message vector used to construct argument
484: * lists to message list functions.
485: */
486: void
487: setmsize(sz)
488: int sz;
489: {
490:
491: if (msgvec != 0)
1.7 millert 492: (void)free(msgvec);
493: msgvec = (int *)calloc(sz + 1, sizeof(*msgvec));
1.1 deraadt 494: }
495:
496: /*
497: * Find the correct command in the command table corresponding
498: * to the passed command "word"
499: */
500:
1.2 niklas 501: const struct cmd *
1.1 deraadt 502: lex(word)
503: char word[];
504: {
1.2 niklas 505: extern const struct cmd cmdtab[];
506: register const struct cmd *cp;
1.1 deraadt 507:
1.8 ! millert 508: for (cp = &cmdtab[0]; cp->c_name != NULL; cp++)
1.1 deraadt 509: if (isprefix(word, cp->c_name))
510: return(cp);
511: return(NONE);
512: }
513:
514: /*
515: * Determine if as1 is a valid prefix of as2.
516: * Return true if yep.
517: */
518: int
519: isprefix(as1, as2)
520: char *as1, *as2;
521: {
522: register char *s1, *s2;
523:
524: s1 = as1;
525: s2 = as2;
526: while (*s1++ == *s2)
527: if (*s2++ == '\0')
528: return(1);
529: return(*--s1 == '\0');
530: }
531:
532: /*
533: * The following gets called on receipt of an interrupt. This is
534: * to abort printout of a command, mainly.
535: * Dispatching here when command() is inactive crashes rcv.
536: * Close all open files except 0, 1, 2, and the temporary.
537: * Also, unstack all source files.
538: */
539:
540: int inithdr; /* am printing startup headers */
541:
542: /*ARGSUSED*/
543: void
544: intr(s)
545: int s;
546: {
547:
548: noreset = 0;
549: if (!inithdr)
550: sawcom++;
551: inithdr = 0;
552: while (sourcing)
553: unstack();
554:
555: close_all_files();
556:
557: if (image >= 0) {
1.6 millert 558: (void)close(image);
1.1 deraadt 559: image = -1;
560: }
1.6 millert 561: fputs("Interrupt\n", stderr);
1.1 deraadt 562: reset(0);
563: }
564:
565: /*
566: * When we wake up after ^Z, reprint the prompt.
567: */
568: void
569: stop(s)
570: int s;
571: {
572: sig_t old_action = signal(s, SIG_DFL);
1.3 deraadt 573: sigset_t nset;
1.1 deraadt 574:
1.3 deraadt 575: sigemptyset(&nset);
576: sigaddset(&nset, s);
577: sigprocmask(SIG_UNBLOCK, &nset, NULL);
1.1 deraadt 578: kill(0, s);
1.3 deraadt 579: sigprocmask(SIG_BLOCK, &nset, NULL);
1.1 deraadt 580: signal(s, old_action);
581: if (reset_on_stop) {
582: reset_on_stop = 0;
583: reset(0);
584: }
585: }
586:
587: /*
588: * Branch here on hangup signal and simulate "exit".
589: */
590: /*ARGSUSED*/
591: void
592: hangup(s)
593: int s;
594: {
595:
596: /* nothing to do? */
597: exit(1);
598: }
599:
600: /*
601: * Announce the presence of the current Mail version,
602: * give the message count, and print a header listing.
603: */
604: void
605: announce()
606: {
607: int vec[2], mdot;
608:
1.6 millert 609: mdot = newfileinfo(0);
1.1 deraadt 610: vec[0] = mdot;
611: vec[1] = 0;
612: dot = &message[mdot - 1];
1.8 ! millert 613: if (msgCount > 0 && value("noheader") == NULL) {
1.1 deraadt 614: inithdr++;
615: headers(vec);
616: inithdr = 0;
617: }
618: }
619:
620: /*
621: * Announce information about the file we are editing.
622: * Return a likely place to set dot.
623: */
624: int
1.6 millert 625: newfileinfo(omsgCount)
626: int omsgCount;
1.1 deraadt 627: {
628: register struct message *mp;
629: register int u, n, mdot, d, s;
1.6 millert 630: char fname[PATHSIZE], zname[PATHSIZE], *ename;
1.1 deraadt 631:
1.6 millert 632: for (mp = &message[omsgCount]; mp < &message[msgCount]; mp++)
1.1 deraadt 633: if (mp->m_flag & MNEW)
634: break;
635: if (mp >= &message[msgCount])
1.6 millert 636: for (mp = &message[omsgCount]; mp < &message[msgCount]; mp++)
1.1 deraadt 637: if ((mp->m_flag & MREAD) == 0)
638: break;
639: if (mp < &message[msgCount])
640: mdot = mp - &message[0] + 1;
641: else
1.6 millert 642: mdot = omsgCount + 1;
1.1 deraadt 643: s = d = 0;
644: for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
645: if (mp->m_flag & MNEW)
646: n++;
647: if ((mp->m_flag & MREAD) == 0)
648: u++;
649: if (mp->m_flag & MDELETED)
650: d++;
651: if (mp->m_flag & MSAVED)
652: s++;
653: }
654: ename = mailname;
1.6 millert 655: if (getfold(fname, sizeof(fname)) >= 0) {
656: strncat(fname, "/", sizeof(fname) - strlen(fname) - 1);
1.1 deraadt 657: if (strncmp(fname, mailname, strlen(fname)) == 0) {
1.6 millert 658: snprintf(zname, sizeof(zname), "+%s",
659: mailname + strlen(fname));
1.1 deraadt 660: ename = zname;
661: }
662: }
663: printf("\"%s\": ", ename);
664: if (msgCount == 1)
1.6 millert 665: fputs("1 message", stdout);
1.1 deraadt 666: else
667: printf("%d messages", msgCount);
668: if (n > 0)
669: printf(" %d new", n);
670: if (u-n > 0)
671: printf(" %d unread", u);
672: if (d > 0)
673: printf(" %d deleted", d);
674: if (s > 0)
675: printf(" %d saved", s);
676: if (readonly)
1.6 millert 677: fputs(" [Read only]", stdout);
678: putchar('\n');
1.1 deraadt 679: return(mdot);
680: }
681:
682: /*
683: * Print the current version number.
684: */
685:
686: /*ARGSUSED*/
687: int
1.3 deraadt 688: pversion(v)
689: void *v;
1.1 deraadt 690: {
691: extern char *version;
692:
693: printf("Version %s\n", version);
694: return(0);
695: }
696:
697: /*
698: * Load a file of user definitions.
699: */
700: void
701: load(name)
702: char *name;
703: {
704: register FILE *in, *oldin;
705:
706: if ((in = Fopen(name, "r")) == NULL)
707: return;
708: oldin = input;
709: input = in;
710: loading = 1;
711: sourcing = 1;
712: commands();
713: loading = 0;
714: sourcing = 0;
715: input = oldin;
1.6 millert 716: (void)Fclose(in);
1.1 deraadt 717: }