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