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