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