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