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