|
|
1.1 root 1: #ident "@(#)lex.c 1.5 'attmail mail(1) command'"
2: #ident "@(#)mailx:lex.c 1.21.3.1"
3: /* Copyright (c) 1984 AT&T */
4: /* All Rights Reserved */
5:
6: /* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
7: /* The copyright notice above does not evidence any */
8: /* actual or intended publication of such source code. */
9:
10: #ident "@(#)mailx:lex.c 1.21.1.1"
11:
12: #include "rcv.h"
13:
14: /*
15: * mailx -- a modified version of a University of California at Berkeley
16: * mail program
17: *
18: * Lexical processing of commands.
19: */
20:
21: #ifdef SIGCONT
22: static void contin();
23: #endif
24: static int isprefix();
25: static struct cmd *lex();
26: static int Passeren();
27: static void setmsize();
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: int isedit;
39: {
40: FILE *ibuf;
41: int i;
42: static int shudclob;
43: static char efile[PATHSIZE];
44: char fortest[128];
45: struct stat stbuf;
46:
47: if ((ibuf = fopen(name, "r")) == NULL) {
48: if (exitflg)
49: exit(1); /* no mail, return error */
50: if (isedit)
51: perror(name);
52: else if (!Hflag) {
53: if (strrchr(name,'/') == NOSTR)
54: fprintf(stderr, "No mail.\n");
55: else
56: fprintf(stderr, "No mail for %s\n", strrchr(name,'/')+1);
57: }
58: return(-1);
59: }
60: fstat(fileno(ibuf), &stbuf);
61: if (stbuf.st_size == 0L) {
62: if (exitflg)
63: exit(1); /* no mail, return error */
64: if (isedit)
65: fprintf(stderr, "%s: empty file\n", name);
66: else if (!Hflag) {
67: if (strrchr(name,'/') == NOSTR)
68: fprintf(stderr, "No mail.\n");
69: else
70: fprintf(stderr, "No mail for %s\n", strrchr(name,'/')+1);
71: }
72: fclose(ibuf);
73: return(-1);
74: }
75:
76: fgets(fortest, sizeof fortest, ibuf);
77: fseek(ibuf, 0L, 0);
78: if (strncmp(fortest, "Forward to ", 11) == 0) {
79: if (exitflg)
80: exit(1); /* no mail, return error */
81: fprintf(stderr, "Your mail is being forwarded to %s", fortest+11);
82: fclose(ibuf);
83: return (-1);
84: }
85: if (exitflg)
86: exit(0); /* there is mail, return success */
87:
88: /*
89: * Looks like all will be well. Must hold signals
90: * while we are reading the new file, else we will ruin
91: * the message[] data structure.
92: * Copy the messages into /tmp and set pointers.
93: */
94:
95: if (shudclob) {
96: holdsigs();
97: if (edit)
98: edstop();
99: else {
100: quit();
101: Verhogen();
102: }
103: relsesigs();
104: }
105:
106: readonly = 0;
107: if (!isedit)
108: readonly = Passeren()==-1;
109: lock(name); /* adb */
110: fstat(fileno(ibuf), &stbuf);
111: utimep->actime = stbuf.st_atime;
112: utimep->modtime = stbuf.st_mtime;
113:
114: holdsigs();
115: if (!readonly)
116: if ((i = open(name, 1)) < 0) /* adb */
117: readonly++;
118: else
119: close(i);
120: if (shudclob) {
121: fclose(itf);
122: fclose(otf);
123: free(message);
124: space=0;
125: }
126: shudclob = 1;
127: edit = isedit;
128: strncpy(efile, name, PATHSIZE);
129: editfile = efile;
130: if (name != mailname)
131: strcpy(mailname, name);
132: mailsize = fsize(ibuf);
133: if ((otf = fopen(tempMesg, "w")) == NULL) {
134: perror(tempMesg);
135: if (!edit)
136: Verhogen();
137: exit(1);
138: }
139: if ((itf = fopen(tempMesg, "r")) == NULL) {
140: perror(tempMesg);
141: if (!edit)
142: Verhogen();
143: exit(1);
144: }
145: removefile(tempMesg);
146: setptr(ibuf);
147: setmsize(msgCount);
148: unlock(); /* adb */
149: fclose(ibuf);
150: relsesigs();
151: sawcom = 0;
152: return(0);
153: }
154:
155: /* semaphore stuff is changed in V9/V10. adb */
156: /* global to semaphores */
157: static char semaphore[128];
158:
159: /*
160: * return -1 if file is already being read, 0 otherwise
161: */
162: static
163: Passeren()
164: {
165: char file[128];
166: struct stat sbuf;
167: FILE *fp;
168: int pid;
169: extern char *getenv();
170: char *home = getenv("HOME");
171:
172: if (home == NULL)
173: return 0;
174: (void)strcpy(file, home);
175: (void)strcat(file, "/.Maillock");
176: if (stat(file, &sbuf) >= 0) {
177:
178: /* lock file exists */
179: fp = fopen(file, "r");
180: if (fp != NULL) {
181: fscanf(fp, "%d", &pid);
182: fclose(fp);
183: }
184: if (fp == NULL || kill(pid, 0)==0) {
185: fprintf(stderr,"WARNING: You are already reading mail.\n");
186: fprintf(stderr, "\tThis instance of mail is read only.\n");
187: return -1;
188: }
189: }
190:
191: /* create a semaphore file */
192: strcpy(semaphore, file);
193: Verhogen();
194: fp = fopen(semaphore, "w");
195: if (fp == NULL)
196: return 0; /* nothing else we can do */
197: fprintf(fp, "%d somewhere", getpid());
198: fclose(fp);
199: return 0;
200: }
201:
202: void
203: Verhogen()
204: {
205: unlink(semaphore);
206: }
207:
208: /*
209: * Interpret user commands one by one. If standard input is not a tty,
210: * print no prompt.
211: */
212:
213: static int *msgvec;
214: static int shudprompt;
215:
216: void
217: commands()
218: {
219: int eofloop;
220: register int n;
221: char linebuf[LINESIZE];
222: struct stat minfo;
223: FILE *ibuf;
224: FILE *lockopen(); /* adb */
225:
226: #ifdef SIGCONT
227: # ifdef preSVr4
228: sigset(SIGCONT, SIG_DFL);
229: # else
230: {
231: struct sigaction nsig;
232: nsig.sa_handler = SIG_DFL;
233: sigemptyset(&nsig.sa_mask);
234: nsig.sa_flags = SA_RESTART;
235: (void) sigaction(SIGCONT, &nsig, (struct sigaction*)0);
236: }
237: # endif
238: #endif
239: if (rcvmode && !sourcing) {
240: if (sigset(SIGINT, SIG_IGN) != (void (*)())SIG_IGN) /* adb */
241: sigset(SIGINT, stop);
242: if (sigset(SIGHUP, SIG_IGN) != (void (*)())SIG_IGN) /* adb */
243: sigset(SIGHUP, hangup);
244: }
245: for (;;) {
246: setjmp(srbuf);
247:
248: /*
249: * Print the prompt, if needed. Clear out
250: * string space, and flush the output.
251: */
252:
253: if (!rcvmode && !sourcing)
254: return;
255: eofloop = 0;
256: top:
257: if ((shudprompt = (intty && !sourcing)) != 0) {
258: if (prompt==NOSTR)
259: prompt = "? ";
260: #ifdef SIGCONT
261: # ifdef preSVr4
262: sigset(SIGCONT, contin);
263: # else
264: {
265: struct sigaction nsig;
266: nsig.sa_handler = contin;
267: sigemptyset(&nsig.sa_mask);
268: nsig.sa_flags = SA_RESTART;
269: (void) sigaction(SIGCONT, &nsig, (struct sigaction*)0);
270: }
271: # endif
272: #endif
273: if (stat(mailname, &minfo) >=0 && minfo.st_size > mailsize) {
274: int OmsgCount, i;
275: OmsgCount = msgCount;
276: fseek(otf, 0L, 2);
277: holdsigs();
278: if ( (ibuf = lockopen(mailname, "r", 0666, -1, -1)) == NULL ) { /* adb */
279: fprintf(stderr,"Can't reopen %s\n",mailname);
280: exit(1);
281: /* NOTREACHED */
282: }
283: fseek(ibuf, mailsize, 0);
284: setptr(ibuf);
285: setmsize(msgCount);
286: stat(mailname, &minfo);
287: mailsize = minfo.st_size;
288: lockclose(ibuf); /* adb */
289: /* unlockmail(); adb */
290: if (msgCount-OmsgCount > 0) {
291: printf("New mail has arrived.\n");
292: printf("Loaded %d new message%s\n",
293: msgCount-OmsgCount,
294: plural(msgCount-OmsgCount));
295: if (value("header") != NOSTR)
296: for (i = OmsgCount+1;
297: i <= msgCount; i++) {
298: printhead(i);
299: sreset();
300: }
301: }
302: relsesigs();
303: }
304: printf("%s", prompt);
305: }
306: flush();
307: sreset();
308:
309: /*
310: * Read a line of commands from the current input
311: * and handle end of file specially.
312: */
313:
314: n = 0;
315: for (;;) {
316: if (readline(input, &linebuf[n]) <= 0) {
317: if (n != 0)
318: break;
319: if (loading)
320: return;
321: if (sourcing) {
322: unstack();
323: goto more;
324: }
325: if (value("ignoreeof") != NOSTR && shudprompt) {
326: if (++eofloop < 25) {
327: printf("Use \"quit\" to quit.\n");
328: goto top;
329: }
330: }
331: if (edit)
332: edstop();
333: return;
334: }
335: if ((n = strlen(linebuf)) == 0)
336: break;
337: n--;
338: if (linebuf[n] != '\\')
339: break;
340: linebuf[n++] = ' ';
341: }
342: #ifdef SIGCONT
343: # ifdef preSVr4
344: sigset(SIGCONT, SIG_DFL);
345: # else
346: {
347: struct sigaction nsig;
348: nsig.sa_handler = SIG_DFL;
349: sigemptyset(&nsig.sa_mask);
350: nsig.sa_flags = SA_RESTART;
351: (void) sigaction(SIGCONT, &nsig, (struct sigaction*)0);
352: }
353: # endif
354: #endif
355: if (execute(linebuf, 0))
356: return;
357: more: ;
358: }
359: }
360:
361: /*
362: * Execute a single command. If the command executed
363: * is "quit," then return non-zero so that the caller
364: * will know to return back to main, if he cares.
365: * Contxt is non-zero if called while composing mail.
366: */
367:
368: execute(linebuf, contxt)
369: char linebuf[];
370: {
371: char word[LINESIZE];
372: char *arglist[MAXARGC];
373: struct cmd *com;
374: register char *cp, *cp2;
375: register int c, e;
376: int muvec[2];
377:
378: /*
379: * Strip the white space away from the beginning
380: * of the command, then scan out a word, which
381: * consists of anything except digits and white space.
382: *
383: * Handle |, ! and # differently to get the correct
384: * lexical conventions.
385: */
386:
387: cp = linebuf;
388: while (any(*cp, " \t"))
389: cp++;
390: cp2 = word;
391: if (any(*cp, "!|#"))
392: *cp2++ = *cp++;
393: else
394: while (*cp && !any(*cp, " \t0123456789$^.:/-+*'\""))
395: *cp2++ = *cp++;
396: *cp2 = '\0';
397:
398: /*
399: * Look up the command; if not found, complain.
400: * Normally, a blank command would map to the
401: * first command in the table; while sourcing,
402: * however, we ignore blank lines to eliminate
403: * confusion.
404: */
405:
406: if (sourcing && equal(word, ""))
407: return(0);
408: com = lex(word);
409: if (com == NONE) {
410: printf("Unknown command: \"%s\"\n", word);
411: if (loading) {
412: cond = CANY;
413: return(1);
414: }
415: if (sourcing) {
416: cond = CANY;
417: unstack();
418: }
419: return(0);
420: }
421:
422: /*
423: * See if we should execute the command -- if a conditional
424: * we always execute it, otherwise, check the state of cond.
425: */
426:
427: if ((com->c_argtype & F) == 0)
428: if (cond == CRCV && !rcvmode || cond == CSEND && rcvmode)
429: return(0);
430:
431: /*
432: * Special case so that quit causes a return to
433: * main, who will call the quit code directly.
434: * If we are in a source file, just unstack.
435: */
436:
437: if (com->c_func == edstop && sourcing) {
438: if (loading)
439: return(1);
440: unstack();
441: return(0);
442: }
443: if (!edit && com->c_func == edstop) {
444: sigset(SIGINT, SIG_IGN);
445: return(1);
446: }
447:
448: /*
449: * Process the arguments to the command, depending
450: * on the type he expects. Default to an error.
451: * If we are sourcing an interactive command, it's
452: * an error.
453: */
454:
455: if (!rcvmode && (com->c_argtype & M) == 0) {
456: printf("May not execute \"%s\" while sending\n",
457: com->c_name);
458: if (loading)
459: return(1);
460: if (sourcing)
461: unstack();
462: return(0);
463: }
464: if (sourcing && com->c_argtype & I) {
465: printf("May not execute \"%s\" while sourcing\n",
466: com->c_name);
467: if (loading)
468: return(1);
469: unstack();
470: return(0);
471: }
472: if (readonly && com->c_argtype & W) {
473: printf("May not execute \"%s\" -- message file is read only\n",
474: com->c_name);
475: if (loading)
476: return(1);
477: if (sourcing)
478: unstack();
479: return(0);
480: }
481: if (contxt && com->c_argtype & R) {
482: printf("Cannot recursively invoke \"%s\"\n", com->c_name);
483: return(0);
484: }
485: e = 1;
486: switch (com->c_argtype & ~(F|P|I|M|T|W|R)) {
487: case MSGLIST:
488: /*
489: * A message list defaulting to nearest forward
490: * legal message.
491: */
492: if (msgvec == 0) {
493: printf("Illegal use of \"message list\"\n");
494: return(-1);
495: }
496: if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
497: break;
498: if (c == 0)
499: if (msgCount == 0)
500: *msgvec = NULL;
501: else {
502: *msgvec = first(com->c_msgflag,
503: com->c_msgmask);
504: msgvec[1] = NULL;
505: }
506: if (*msgvec == NULL) {
507: printf("No applicable messages\n");
508: break;
509: }
510: e = (*com->c_func)(msgvec);
511: break;
512:
513: case NDMLIST:
514: /*
515: * A message list with no defaults, but no error
516: * if none exist.
517: */
518: if (msgvec == 0) {
519: printf("Illegal use of \"message list\"\n");
520: return(-1);
521: }
522: if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
523: break;
524: e = (*com->c_func)(msgvec);
525: break;
526:
527: case STRLIST:
528: /*
529: * Just the straight string, with
530: * leading blanks removed.
531: */
532: while (any(*cp, " \t"))
533: cp++;
534: e = (*com->c_func)(cp);
535: break;
536:
537: case RAWLIST:
538: /*
539: * A vector of strings, in shell style.
540: */
541: if ((c = getrawlist(cp, arglist)) < 0)
542: break;
543: if (c < com->c_minargs) {
544: printf("%s requires at least %d arg(s)\n",
545: com->c_name, com->c_minargs);
546: break;
547: }
548: if (c > com->c_maxargs) {
549: printf("%s takes no more than %d arg(s)\n",
550: com->c_name, com->c_maxargs);
551: break;
552: }
553: e = (*com->c_func)(arglist);
554: break;
555:
556: case NOLIST:
557: /*
558: * Just the constant zero, for exiting,
559: * eg.
560: */
561: e = (*com->c_func)(0);
562: break;
563:
564: default:
565: panic("Unknown argtype");
566: }
567:
568: /*
569: * Exit the current source file on
570: * error.
571: */
572:
573: if (e && loading)
574: return(1);
575: if (e && sourcing)
576: unstack();
577: if (com->c_func == edstop)
578: return(1);
579: if (value("autoprint") != NOSTR && com->c_argtype & P)
580: if ((dot->m_flag & MDELETED) == 0) {
581: muvec[0] = dot - &message[0] + 1;
582: muvec[1] = 0;
583: type(muvec);
584: }
585: if (!sourcing && (com->c_argtype & T) == 0)
586: sawcom = 1;
587: return(0);
588: }
589:
590: #ifdef SIGCONT
591: /*
592: * When we wake up after ^Z, reprint the prompt.
593: */
594: static void
595: contin()
596: {
597: if (shudprompt)
598: printf("%s", prompt);
599: fflush(stdout);
600: }
601: #endif
602:
603: /*
604: * Branch here on hangup signal and simulate quit.
605: */
606: void
607: hangup()
608: {
609:
610: holdsigs();
611: sigignore(SIGHUP);
612: if (edit) {
613: if (setjmp(srbuf))
614: exit(0);
615: edstop();
616: } else {
617: Verhogen();
618: if (value("exit") != NOSTR)
619: exit(1);
620: else
621: quit();
622: }
623: exit(0);
624: }
625:
626: /*
627: * Set the size of the message vector used to construct argument
628: * lists to message list functions.
629: */
630:
631: static void
632: setmsize(sz)
633: {
634:
635: if (msgvec != (int *) 0)
636: free(msgvec);
637: if (sz < 1)
638: sz = 1; /* need at least one cell for terminating 0 */
639: msgvec = (int *) calloc((unsigned) (sz + 1), sizeof *msgvec);
640: }
641:
642: /*
643: * Find the correct command in the command table corresponding
644: * to the passed command "word"
645: */
646:
647: static struct cmd *
648: lex(word)
649: char word[];
650: {
651: register struct cmd *cp;
652:
653: for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++)
654: if (isprefix(word, cp->c_name))
655: return(cp);
656: return(NONE);
657: }
658:
659: /*
660: * Determine if as1 is a valid prefix of as2.
661: */
662: static int
663: isprefix(as1, as2)
664: char *as1, *as2;
665: {
666: register char *s1, *s2;
667:
668: s1 = as1;
669: s2 = as2;
670: while (*s1++ == *s2)
671: if (*s2++ == '\0')
672: return(1);
673: return(*--s1 == '\0');
674: }
675:
676: /*
677: * The following gets called on receipt of a rubout. This is
678: * to abort printout of a command, mainly.
679: * Dispatching here when command() is inactive crashes rcv.
680: * Close all open files except 0, 1, 2, and the temporary.
681: * The special call to getuserid() is needed so it won't get
682: * annoyed about losing its open file.
683: * Also, unstack all source files.
684: */
685:
686: static int inithdr; /* am printing startup headers */
687:
688: void
689: stop(s)
690: {
691: register NODE *head;
692:
693: noreset = 0;
694: if (!inithdr)
695: sawcom++;
696: inithdr = 0;
697: while (sourcing)
698: unstack();
699: getuserid((char *) 0);
700: if ( !maxfiles )
701: if ( (maxfiles=ulimit(4, 0)) < 0 )
702: maxfiles = _NFILE;
703: for (head = fplist; head != (NODE *)NULL; head = head->next) {
704: if (head->fp == stdin || head->fp == stdout)
705: continue;
706: if (head->fp == itf || head->fp == otf)
707: continue;
708: if (head->fp == stderr)
709: continue;
710: if (head->fp == pipef) {
711: npclose(pipef);
712: pipef = NULL;
713: continue;
714: }
715: fclose(head->fp);
716: }
717: if (image >= 0) {
718: close(image);
719: image = -1;
720: }
721: clrbuf(stdout);
722: if (s) {
723: printf("Interrupt\n");
724: sigrelse(s);
725: }
726: longjmp(srbuf, 1);
727: }
728:
729: /*
730: * Announce the presence of the current mailx version,
731: * give the message count, and print a header listing.
732: */
733:
734: static char *greeting = "%s Type ? for help.\n";
735:
736: void
737: announce()
738: {
739: int vec[2], mdot;
740:
741: if (!Hflag && value("quiet")==NOSTR)
742: printf(greeting, version);
743: mdot = newfileinfo();
744: vec[0] = mdot;
745: vec[1] = 0;
746: dot = &message[mdot - 1];
747: if (msgCount > 0 && !noheader) {
748: inithdr++;
749: headers(vec);
750: inithdr = 0;
751: }
752: }
753:
754: /*
755: * Announce information about the file we are editing.
756: * Return a likely place to set dot.
757: */
758: newfileinfo()
759: {
760: register struct message *mp;
761: register int u, n, mdot, d, s;
762: char fname[BUFSIZ], zname[BUFSIZ], *ename;
763:
764: if (Hflag)
765: return(1);
766: for (mp = &message[0]; mp < &message[msgCount]; mp++)
767: if (mp->m_flag & MNEW)
768: break;
769: if (mp >= &message[msgCount])
770: for (mp = &message[0]; mp < &message[msgCount]; mp++)
771: if ((mp->m_flag & MREAD) == 0)
772: break;
773: if (mp < &message[msgCount])
774: mdot = mp - &message[0] + 1;
775: else
776: mdot = 1;
777: s = d = 0;
778: for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
779: if (mp->m_flag & MNEW)
780: n++;
781: if ((mp->m_flag & MREAD) == 0)
782: u++;
783: if (mp->m_flag & MDELETED)
784: d++;
785: if (mp->m_flag & MSAVED)
786: s++;
787: }
788: ename=origname;
789: if (getfold(fname) >= 0) {
790: strcat(fname, "/");
791: if (strncmp(fname, mailname, strlen(fname)) == 0) {
792: sprintf(zname, "+%s", mailname + strlen(fname));
793: ename = zname;
794: }
795: }
796: printf("\"%s\": ", ename);
797: if (msgCount == 1)
798: printf("1 message");
799: else
800: printf("%d messages", msgCount);
801: if (n > 0)
802: printf(" %d new", n);
803: if (u-n > 0)
804: printf(" %d unread", u);
805: if (d > 0)
806: printf(" %d deleted", d);
807: if (s > 0)
808: printf(" %d saved", s);
809: if (readonly)
810: printf(" [Read only]");
811: printf("\n");
812: return(mdot);
813: }
814:
815: /*
816: * Print the current version number.
817: */
818:
819: pversion(e)
820: char *e;
821: {
822: (void) e;
823: printf("%s\n", version);
824: return(0);
825: }
826:
827: /*
828: * Load a file of user definitions.
829: */
830: void
831: load(name)
832: char *name;
833: {
834: register FILE *in, *oldin;
835:
836: if ((in = fopen(name, "r")) == NULL)
837: return;
838: oldin = input;
839: input = in;
840: loading = 1;
841: sourcing = 1;
842: commands();
843: loading = 0;
844: sourcing = 0;
845: input = oldin;
846: fclose(in);
847: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.