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