|
|
1.1 root 1: /*-
2: * Copyright (c) 1980 The 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: (1) source distributions retain this entire copyright
7: * notice and comment, and (2) distributions including binaries display
8: * the following acknowledgement: ``This product includes software
9: * developed by the University of California, Berkeley and its contributors''
10: * in the documentation or other materials provided with the distribution
11: * and in all advertising materials mentioning features or use of this
12: * software. Neither the name of the University nor the names of its
13: * contributors may be used to endorse or promote products derived
14: * from this software without specific prior written permission.
15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16: * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18: */
19:
20: #ifndef lint
21: char copyright[] =
22: "@(#) Copyright (c) 1980 The Regents of the University of California.\n\
23: All rights reserved.\n";
24: #endif /* not lint */
25:
26: #ifndef lint
27: static char sccsid[] = "@(#)msgs.c 5.6 (Berkeley) 5/29/90";
28: #endif /* not lint */
29:
30: /*
31: * msgs - a user bulletin board program
32: *
33: * usage:
34: * msgs [fhlopq] [[-]number] to read messages
35: * msgs -s to place messages
36: * msgs -c [-days] to clean up the bulletin board
37: *
38: * prompt commands are:
39: * y print message
40: * n flush message, go to next message
41: * q flush message, quit
42: * p print message, turn on 'pipe thru more' mode
43: * P print message, turn off 'pipe thru more' mode
44: * - reprint last message
45: * s[-][<num>] [<filename>] save message
46: * m[-][<num>] mail with message in temp mbox
47: * x exit without flushing this message
48: * <num> print message number <num>
49: */
50:
51: #define V7 /* will look for TERM in the environment */
52: #define OBJECT /* will object to messages without Subjects */
53: /* #define REJECT /* will reject messages without Subjects
54: (OBJECT must be defined also) */
55: /* #define UNBUFFERED /* use unbuffered output */
56:
57: #include <sys/param.h>
58: #include <signal.h>
59: #include <sys/dir.h>
60: #include <sys/stat.h>
61: #include <ctype.h>
62: #include <pwd.h>
63: #include <sgtty.h>
64: #include <setjmp.h>
65: #include <stdio.h>
66: #include "pathnames.h"
67:
68: #define CMODE 0666 /* bounds file creation mode */
69: #define NO 0
70: #define YES 1
71: #define SUPERUSER 0 /* superuser uid */
72: #define DAEMON 1 /* daemon uid */
73: #define NLINES 24 /* default number of lines/crt screen */
74: #define NDAYS 21 /* default keep time for messages */
75: #define DAYS *24*60*60 /* seconds/day */
76: #define MSGSRC ".msgsrc" /* user's rc file */
77: #define BOUNDS "bounds" /* message bounds file */
78: #define NEXT "Next message? [yq]"
79: #define MORE "More? [ynq]"
80: #define NOMORE "(No more) [q] ?"
81:
82: typedef char bool;
83:
84: FILE *newmsg;
85: char *sep = "-";
86: char inbuf[BUFSIZ];
87: char fname[128];
88: char cmdbuf[128];
89: char subj[128];
90: char from[128];
91: char date[128];
92: char *ptr;
93: char *in;
94: bool local;
95: bool ruptible;
96: bool totty;
97: bool seenfrom;
98: bool seensubj;
99: bool blankline;
100: bool printing = NO;
101: bool mailing = NO;
102: bool quitit = NO;
103: bool sending = NO;
104: bool intrpflg = NO;
105: bool tstpflag = NO;
106: int uid;
107: int msg;
108: int prevmsg;
109: int lct;
110: int nlines;
111: int Lpp = 0;
112: time_t t;
113: time_t keep;
114: struct sgttyb otty;
115:
116: char *ctime();
117: char *nxtfld();
118: int onintr();
119: int onsusp();
120: off_t ftell();
121: FILE *popen();
122: struct passwd *getpwuid();
123:
124: extern int errno;
125:
126: /* option initialization */
127: bool hdrs = NO;
128: bool qopt = NO;
129: bool hush = NO;
130: bool send = NO;
131: bool locomode = NO;
132: bool pause = NO;
133: bool clean = NO;
134: bool lastcmd = NO;
135: jmp_buf tstpbuf;
136:
137: main(argc, argv)
138: int argc; char *argv[];
139: {
140: bool newrc, already;
141: int rcfirst = 0; /* first message to print (from .rc) */
142: int rcback = 0; /* amount to back off of rcfirst */
143: int firstmsg, nextmsg, lastmsg = 0;
144: int blast = 0;
145: FILE *bounds, *msgsrc;
146:
147: #ifdef UNBUFFERED
148: setbuf(stdout, NULL);
149: #endif
150:
151: gtty(fileno(stdout), &otty);
152: time(&t);
153: setuid(uid = getuid());
154: ruptible = (signal(SIGINT, SIG_IGN) == SIG_DFL);
155: if (ruptible)
156: signal(SIGINT, SIG_DFL);
157:
158: argc--, argv++;
159: while (argc > 0) {
160: if (isdigit(argv[0][0])) { /* starting message # */
161: rcfirst = atoi(argv[0]);
162: }
163: else if (isdigit(argv[0][1])) { /* backward offset */
164: rcback = atoi( &( argv[0][1] ) );
165: }
166: else {
167: ptr = *argv;
168: while (*ptr) switch (*ptr++) {
169:
170: case '-':
171: break;
172:
173: case 'c':
174: if (uid != SUPERUSER && uid != DAEMON) {
175: fprintf(stderr, "Sorry\n");
176: exit(1);
177: }
178: clean = YES;
179: break;
180:
181: case 'f': /* silently */
182: hush = YES;
183: break;
184:
185: case 'h': /* headers only */
186: hdrs = YES;
187: break;
188:
189: case 'l': /* local msgs only */
190: locomode = YES;
191: break;
192:
193: case 'o': /* option to save last message */
194: lastcmd = YES;
195: break;
196:
197: case 'p': /* pipe thru 'more' during long msgs */
198: pause = YES;
199: break;
200:
201: case 'q': /* query only */
202: qopt = YES;
203: break;
204:
205: case 's': /* sending TO msgs */
206: send = YES;
207: break;
208:
209: default:
210: fprintf(stderr,
211: "usage: msgs [fhlopq] [[-]number]\n");
212: exit(1);
213: }
214: }
215: argc--, argv++;
216: }
217:
218: /*
219: * determine current message bounds
220: */
221: sprintf(fname, "%s/%s", _PATH_MSGS, BOUNDS);
222: bounds = fopen(fname, "r");
223:
224: if (bounds != NULL) {
225: fscanf(bounds, "%d %d\n", &firstmsg, &lastmsg);
226: fclose(bounds);
227: blast = lastmsg; /* save upper bound */
228: }
229:
230: if (clean)
231: keep = t - (rcback? rcback : NDAYS) DAYS;
232:
233: if (clean || bounds == NULL) { /* relocate message bounds */
234: struct direct *dp;
235: struct stat stbuf;
236: bool seenany = NO;
237: DIR *dirp;
238:
239: dirp = opendir(_PATH_MSGS);
240: if (dirp == NULL) {
241: perror(_PATH_MSGS);
242: exit(errno);
243: }
244:
245: firstmsg = 32767;
246: lastmsg = 0;
247:
248: for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)){
249: register char *cp = dp->d_name;
250: register int i = 0;
251:
252: if (dp->d_ino == 0)
253: continue;
254: if (dp->d_namlen == 0)
255: continue;
256:
257: if (clean)
258: sprintf(inbuf, "%s/%s", _PATH_MSGS, cp);
259:
260: while (isdigit(*cp))
261: i = i * 10 + *cp++ - '0';
262: if (*cp)
263: continue; /* not a message! */
264:
265: if (clean) {
266: if (stat(inbuf, &stbuf) != 0)
267: continue;
268: if (stbuf.st_mtime < keep
269: && stbuf.st_mode&S_IWRITE) {
270: unlink(inbuf);
271: continue;
272: }
273: }
274:
275: if (i > lastmsg)
276: lastmsg = i;
277: if (i < firstmsg)
278: firstmsg = i;
279: seenany = YES;
280: }
281: closedir(dirp);
282:
283: if (!seenany) {
284: if (blast != 0) /* never lower the upper bound! */
285: lastmsg = blast;
286: firstmsg = lastmsg + 1;
287: }
288: else if (blast > lastmsg)
289: lastmsg = blast;
290:
291: if (!send) {
292: bounds = fopen(fname, "w");
293: if (bounds == NULL) {
294: perror(fname);
295: exit(errno);
296: }
297: chmod(fname, CMODE);
298: fprintf(bounds, "%d %d\n", firstmsg, lastmsg);
299: fclose(bounds);
300: }
301: }
302:
303: if (send) {
304: /*
305: * Send mode - place msgs in _PATH_MSGS
306: */
307: bounds = fopen(fname, "w");
308: if (bounds == NULL) {
309: perror(fname);
310: exit(errno);
311: }
312:
313: nextmsg = lastmsg + 1;
314: sprintf(fname, "%s/%d", _PATH_MSGS, nextmsg);
315: newmsg = fopen(fname, "w");
316: if (newmsg == NULL) {
317: perror(fname);
318: exit(errno);
319: }
320: chmod(fname, 0644);
321:
322: fprintf(bounds, "%d %d\n", firstmsg, nextmsg);
323: fclose(bounds);
324:
325: sending = YES;
326: if (ruptible)
327: signal(SIGINT, onintr);
328:
329: if (isatty(fileno(stdin))) {
330: ptr = getpwuid(uid)->pw_name;
331: printf("Message %d:\nFrom %s %sSubject: ",
332: nextmsg, ptr, ctime(&t));
333: fflush(stdout);
334: fgets(inbuf, sizeof inbuf, stdin);
335: putchar('\n');
336: fflush(stdout);
337: fprintf(newmsg, "From %s %sSubject: %s\n",
338: ptr, ctime(&t), inbuf);
339: blankline = seensubj = YES;
340: }
341: else
342: blankline = seensubj = NO;
343: for (;;) {
344: fgets(inbuf, sizeof inbuf, stdin);
345: if (feof(stdin) || ferror(stdin))
346: break;
347: blankline = (blankline || (inbuf[0] == '\n'));
348: seensubj = (seensubj || (!blankline && (strncmp(inbuf, "Subj", 4) == 0)));
349: fputs(inbuf, newmsg);
350: }
351: #ifdef OBJECT
352: if (!seensubj) {
353: printf("NOTICE: Messages should have a Subject field!\n");
354: #ifdef REJECT
355: unlink(fname);
356: #endif
357: exit(1);
358: }
359: #endif
360: exit(ferror(stdin));
361: }
362: if (clean)
363: exit(0);
364:
365: /*
366: * prepare to display messages
367: */
368: totty = (isatty(fileno(stdout)) != 0);
369: pause = pause && totty;
370:
371: sprintf(fname, "%s/%s", getenv("HOME"), MSGSRC);
372: msgsrc = fopen(fname, "r");
373: if (msgsrc) {
374: newrc = NO;
375: fscanf(msgsrc, "%d\n", &nextmsg);
376: fclose(msgsrc);
377: if (nextmsg > lastmsg+1) {
378: printf("Warning: bounds have been reset (%d, %d)\n",
379: firstmsg, lastmsg);
380: truncate(fname, 0);
381: newrc = YES;
382: }
383: else if (!rcfirst)
384: rcfirst = nextmsg - rcback;
385: }
386: else
387: newrc = YES;
388: msgsrc = fopen(fname, "a");
389: if (msgsrc == NULL) {
390: perror(fname);
391: exit(errno);
392: }
393: if (rcfirst) {
394: if (rcfirst > lastmsg+1) {
395: printf("Warning: the last message is number %d.\n",
396: lastmsg);
397: rcfirst = nextmsg;
398: }
399: if (rcfirst > firstmsg)
400: firstmsg = rcfirst; /* don't set below first msg */
401: }
402: if (newrc) {
403: nextmsg = firstmsg;
404: fseek(msgsrc, 0L, 0);
405: fprintf(msgsrc, "%d\n", nextmsg);
406: fflush(msgsrc);
407: }
408:
409: #ifdef V7
410: if (totty) {
411: struct winsize win;
412: if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1)
413: Lpp = win.ws_row;
414: if (Lpp <= 0) {
415: if (tgetent(inbuf, getenv("TERM")) <= 0
416: || (Lpp = tgetnum("li")) <= 0) {
417: Lpp = NLINES;
418: }
419: }
420: }
421: #endif
422: Lpp -= 6; /* for headers, etc. */
423:
424: already = NO;
425: prevmsg = firstmsg;
426: printing = YES;
427: if (ruptible)
428: signal(SIGINT, onintr);
429:
430: /*
431: * Main program loop
432: */
433: for (msg = firstmsg; msg <= lastmsg; msg++) {
434:
435: sprintf(fname, "%s/%d", _PATH_MSGS, msg);
436: newmsg = fopen(fname, "r");
437: if (newmsg == NULL)
438: continue;
439:
440: gfrsub(newmsg); /* get From and Subject fields */
441: if (locomode && !local) {
442: fclose(newmsg);
443: continue;
444: }
445:
446: if (qopt) { /* This has to be located here */
447: printf("There are new messages.\n");
448: exit(0);
449: }
450:
451: if (already && !hdrs)
452: putchar('\n');
453: already = YES;
454:
455: /*
456: * Print header
457: */
458: again:
459: if (totty)
460: signal(SIGTSTP, onsusp);
461: (void) setjmp(tstpbuf);
462: nlines = 2;
463: if (seenfrom) {
464: printf("Message %d:\nFrom %s %s", msg, from, date);
465: nlines++;
466: }
467: if (seensubj) {
468: printf("Subject: %s", subj);
469: nlines++;
470: }
471: else {
472: if (seenfrom) {
473: putchar('\n');
474: nlines++;
475: }
476: while (nlines < 6
477: && fgets(inbuf, sizeof inbuf, newmsg)
478: && inbuf[0] != '\n') {
479: fputs(inbuf, stdout);
480: nlines++;
481: }
482: }
483:
484: lct = linecnt(newmsg);
485: if (lct)
486: printf("(%d%slines) ", lct, seensubj? " " : " more ");
487:
488: if (hdrs) {
489: printf("\n-----\n");
490: fclose(newmsg);
491: continue;
492: }
493:
494: /*
495: * Ask user for command
496: */
497: if (totty)
498: ask(lct? MORE : (msg==lastmsg? NOMORE : NEXT));
499: else
500: inbuf[0] = 'y';
501: if (totty)
502: signal(SIGTSTP, SIG_DFL);
503: cmnd:
504: in = inbuf;
505: switch (*in) {
506: case 'x':
507: case 'X':
508: exit(0);
509:
510: case 'q':
511: case 'Q':
512: quitit = YES;
513: printf("--Postponed--\n");
514: exit(0);
515: /* intentional fall-thru */
516: case 'n':
517: case 'N':
518: if (msg >= nextmsg) sep = "Flushed";
519: prevmsg = msg;
520: break;
521:
522: case 'p':
523: case 'P':
524: pause = (*in++ == 'p');
525: /* intentional fallthru */
526: case '\n':
527: case 'y':
528: default:
529: if (*in == '-') {
530: msg = prevmsg-1;
531: sep = "replay";
532: break;
533: }
534: if (isdigit(*in)) {
535: msg = next(in);
536: sep = in;
537: break;
538: }
539:
540: prmesg(nlines + lct + (seensubj? 1 : 0));
541: prevmsg = msg;
542:
543: }
544:
545: printf("--%s--\n", sep);
546: sep = "-";
547: if (msg >= nextmsg) {
548: nextmsg = msg + 1;
549: fseek(msgsrc, 0L, 0);
550: fprintf(msgsrc, "%d\n", nextmsg);
551: fflush(msgsrc);
552: }
553: if (newmsg)
554: fclose(newmsg);
555: if (quitit)
556: break;
557: }
558:
559: /*
560: * Make sure .rc file gets updated
561: */
562: if (--msg >= nextmsg) {
563: nextmsg = msg + 1;
564: fseek(msgsrc, 0L, 0);
565: fprintf(msgsrc, "%d\n", nextmsg);
566: fflush(msgsrc);
567: }
568: if (already && !quitit && lastcmd && totty) {
569: /*
570: * save or reply to last message?
571: */
572: msg = prevmsg;
573: ask(NOMORE);
574: if (inbuf[0] == '-' || isdigit(inbuf[0]))
575: goto cmnd;
576: }
577: if (!(already || hush || qopt))
578: printf("No new messages.\n");
579: exit(0);
580: }
581:
582: prmesg(length)
583: int length;
584: {
585: FILE *outf, *inf;
586: int c;
587:
588: if (pause && length > Lpp) {
589: signal(SIGPIPE, SIG_IGN);
590: signal(SIGQUIT, SIG_IGN);
591: sprintf(cmdbuf, _PATH_PAGER, Lpp);
592: outf = popen(cmdbuf, "w");
593: if (!outf)
594: outf = stdout;
595: else
596: setbuf(outf, NULL);
597: }
598: else
599: outf = stdout;
600:
601: if (seensubj)
602: putc('\n', outf);
603:
604: while (fgets(inbuf, sizeof inbuf, newmsg)) {
605: fputs(inbuf, outf);
606: if (ferror(outf)) {
607: clearerr(outf);
608: break;
609: }
610: }
611:
612: if (outf != stdout) {
613: pclose(outf);
614: signal(SIGPIPE, SIG_DFL);
615: signal(SIGQUIT, SIG_DFL);
616: }
617: else {
618: fflush(stdout);
619: }
620:
621: /* trick to force wait on output */
622: stty(fileno(stdout), &otty);
623: }
624:
625: onintr()
626: {
627: signal(SIGINT, onintr);
628: if (mailing)
629: unlink(fname);
630: if (sending) {
631: unlink(fname);
632: puts("--Killed--");
633: exit(1);
634: }
635: if (printing) {
636: putchar('\n');
637: if (hdrs)
638: exit(0);
639: sep = "Interrupt";
640: if (newmsg)
641: fseek(newmsg, 0L, 2);
642: intrpflg = YES;
643: }
644: }
645:
646: /*
647: * We have just gotten a susp. Suspend and prepare to resume.
648: */
649: onsusp()
650: {
651:
652: signal(SIGTSTP, SIG_DFL);
653: sigsetmask(0);
654: kill(0, SIGTSTP);
655: signal(SIGTSTP, onsusp);
656: if (!mailing)
657: longjmp(tstpbuf);
658: }
659:
660: linecnt(f)
661: FILE *f;
662: {
663: off_t oldpos = ftell(f);
664: int l = 0;
665: char lbuf[BUFSIZ];
666:
667: while (fgets(lbuf, sizeof lbuf, f))
668: l++;
669: clearerr(f);
670: fseek(f, oldpos, 0);
671: return (l);
672: }
673:
674: next(buf)
675: char *buf;
676: {
677: int i;
678: sscanf(buf, "%d", &i);
679: sprintf(buf, "Goto %d", i);
680: return(--i);
681: }
682:
683: ask(prompt)
684: char *prompt;
685: {
686: char inch;
687: int n, cmsg;
688: off_t oldpos;
689: FILE *cpfrom, *cpto;
690:
691: printf("%s ", prompt);
692: fflush(stdout);
693: intrpflg = NO;
694: (void) fgets(inbuf, sizeof inbuf, stdin);
695: if ((n = strlen(inbuf)) > 0 && inbuf[n - 1] == '\n')
696: inbuf[n - 1] = '\0';
697: if (intrpflg)
698: inbuf[0] = 'x';
699:
700: /*
701: * Handle 'mail' and 'save' here.
702: */
703: if ((inch = inbuf[0]) == 's' || inch == 'm') {
704: if (inbuf[1] == '-')
705: cmsg = prevmsg;
706: else if (isdigit(inbuf[1]))
707: cmsg = atoi(&inbuf[1]);
708: else
709: cmsg = msg;
710: sprintf(fname, "%s/%d", _PATH_MSGS, cmsg);
711:
712: oldpos = ftell(newmsg);
713:
714: cpfrom = fopen(fname, "r");
715: if (!cpfrom) {
716: printf("Message %d not found\n", cmsg);
717: ask (prompt);
718: return;
719: }
720:
721: if (inch == 's') {
722: in = nxtfld(inbuf);
723: if (*in) {
724: for (n=0; in[n] > ' '; n++) { /* sizeof fname? */
725: fname[n] = in[n];
726: }
727: fname[n] = NULL;
728: }
729: else
730: strcpy(fname, "Messages");
731: }
732: else {
733: strcpy(fname, _PATH_TMP);
734: mktemp(fname);
735: sprintf(cmdbuf, _PATH_MAIL, fname);
736: mailing = YES;
737: }
738: cpto = fopen(fname, "a");
739: if (!cpto) {
740: perror(fname);
741: mailing = NO;
742: fseek(newmsg, oldpos, 0);
743: ask(prompt);
744: return;
745: }
746:
747: while (n = fread(inbuf, 1, sizeof inbuf, cpfrom))
748: fwrite(inbuf, 1, n, cpto);
749:
750: fclose(cpfrom);
751: fclose(cpto);
752: fseek(newmsg, oldpos, 0); /* reposition current message */
753: if (inch == 's')
754: printf("Message %d saved in \"%s\"\n", cmsg, fname);
755: else {
756: system(cmdbuf);
757: unlink(fname);
758: mailing = NO;
759: }
760: ask(prompt);
761: }
762: }
763:
764: gfrsub(infile)
765: FILE *infile;
766: {
767: off_t frompos;
768:
769: seensubj = seenfrom = NO;
770: local = YES;
771: subj[0] = from[0] = date[0] = NULL;
772:
773: /*
774: * Is this a normal message?
775: */
776: if (fgets(inbuf, sizeof inbuf, infile)) {
777: if (strncmp(inbuf, "From", 4)==0) {
778: /*
779: * expected form starts with From
780: */
781: seenfrom = YES;
782: frompos = ftell(infile);
783: ptr = from;
784: in = nxtfld(inbuf);
785: if (*in) while (*in && *in > ' ') {
786: if (*in == ':' || *in == '@' || *in == '!')
787: local = NO;
788: *ptr++ = *in++;
789: /* what about sizeof from ? */
790: }
791: *ptr = NULL;
792: if (*(in = nxtfld(in)))
793: strncpy(date, in, sizeof date);
794: else {
795: date[0] = '\n';
796: date[1] = NULL;
797: }
798: }
799: else {
800: /*
801: * not the expected form
802: */
803: fseek(infile, 0L, 0);
804: return;
805: }
806: }
807: else
808: /*
809: * empty file ?
810: */
811: return;
812:
813: /*
814: * look for Subject line until EOF or a blank line
815: */
816: while (fgets(inbuf, sizeof inbuf, infile)
817: && !(blankline = (inbuf[0] == '\n'))) {
818: /*
819: * extract Subject line
820: */
821: if (!seensubj && strncmp(inbuf, "Subj", 4)==0) {
822: seensubj = YES;
823: frompos = ftell(infile);
824: strncpy(subj, nxtfld(inbuf), sizeof subj);
825: }
826: }
827: if (!blankline)
828: /*
829: * ran into EOF
830: */
831: fseek(infile, frompos, 0);
832:
833: if (!seensubj)
834: /*
835: * for possible use with Mail
836: */
837: strncpy(subj, "(No Subject)\n", sizeof subj);
838: }
839:
840: char *
841: nxtfld(s)
842: char *s;
843: {
844: if (*s) while (*s && *s > ' ') s++; /* skip over this field */
845: if (*s) while (*s && *s <= ' ') s++; /* find start of next field */
846: return (s);
847: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.