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