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