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