use the ENDIAN definitions
[unix-history] / usr / src / usr.bin / msgs / msgs.c
CommitLineData
5ff67f98
DF
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
46ebe0eb 7#ifndef lint
5ff67f98
DF
8char 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
46c68d92 14static char sccsid[] = "@(#)msgs.c 5.3 (Berkeley) %G%";
5ff67f98
DF
15#endif not lint
16
46ebe0eb
RH
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
695b2e09 35 * <num> print message number <num>
46ebe0eb
RH
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>
2e3d53b4 45#include <sys/param.h>
46ebe0eb 46#include <signal.h>
0588cc8c 47#include <sys/dir.h>
46ebe0eb
RH
48#include <sys/stat.h>
49#include <ctype.h>
50#include <pwd.h>
51#include <sgtty.h>
e7127e6a 52#include <setjmp.h>
46ebe0eb
RH
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
70typedef char bool;
71
72FILE *newmsg;
73char *sep = "-";
74char inbuf[BUFSIZ];
75char fname[128];
76char cmdbuf[128];
77char subj[128];
78char from[128];
79char date[128];
80char *ptr;
81char *in;
82bool local;
83bool ruptible;
84bool totty;
85bool seenfrom;
86bool seensubj;
87bool blankline;
88bool printing = NO;
89bool mailing = NO;
90bool quitit = NO;
91bool sending = NO;
92bool intrpflg = NO;
4bdc6489 93bool tstpflag = NO;
46ebe0eb
RH
94int uid;
95int msg;
96int prevmsg;
97int lct;
98int nlines;
686125cc 99int Lpp = 0;
46ebe0eb
RH
100time_t t;
101time_t keep;
102struct sgttyb otty;
103
104char *ctime();
105char *nxtfld();
106int onintr();
4bdc6489 107int onsusp();
46ebe0eb
RH
108off_t ftell();
109FILE *popen();
110struct passwd *getpwuid();
111
112extern int errno;
113
114/* option initialization */
115bool hdrs = NO;
116bool qopt = NO;
117bool hush = NO;
118bool send = NO;
119bool locomode = NO;
120bool pause = NO;
121bool clean = NO;
122bool lastcmd = NO;
e7127e6a 123jmp_buf tstpbuf;
46ebe0eb
RH
124
125main(argc, argv)
126int argc; char *argv[];
127{
128 bool newrc, already;
129 int rcfirst = 0; /* first message to print (from .rc) */
ef7d2615 130 int rcback = 0; /* amount to back off of rcfirst */
46ebe0eb
RH
131 int firstmsg, nextmsg, lastmsg = 0;
132 int blast = 0;
133 FILE *bounds, *msgsrc;
134
723391ff 135#ifdef UNBUFFERED
46ebe0eb
RH
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 */
2e3d53b4 222 struct direct *dp;
46ebe0eb
RH
223 struct stat stbuf;
224 bool seenany = NO;
2e3d53b4 225 DIR *dirp;
46ebe0eb 226
2e3d53b4
RH
227 dirp = opendir(USRMSGS);
228 if (dirp == NULL) {
46ebe0eb
RH
229 perror(USRMSGS);
230 exit(errno);
231 }
232
233 firstmsg = 32767;
234 lastmsg = 0;
235
2e3d53b4
RH
236 for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)){
237 register char *cp = dp->d_name;
46ebe0eb
RH
238 register int i = 0;
239
2e3d53b4
RH
240 if (dp->d_ino == 0)
241 continue;
242 if (dp->d_namlen == 0)
46ebe0eb
RH
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 }
2e3d53b4 269 closedir(dirp);
46ebe0eb
RH
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);
695b2e09
RC
365 if (nextmsg > lastmsg+1) {
366 printf("Warning: bounds have been reset (%d, %d)\n",
367 firstmsg, lastmsg);
46c68d92 368 truncate(fname, 0);
695b2e09
RC
369 newrc = YES;
370 }
371 else if (!rcfirst)
46ebe0eb
RH
372 rcfirst = nextmsg - rcback;
373 }
695b2e09 374 else
46ebe0eb 375 newrc = YES;
46ebe0eb
RH
376 msgsrc = fopen(fname, "a");
377 if (msgsrc == NULL) {
378 perror(fname);
379 exit(errno);
380 }
695b2e09
RC
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 }
46ebe0eb
RH
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) {
686125cc
JB
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 }
46ebe0eb
RH
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 */
4bdc6489 446again:
4bdc6489
CL
447 if (totty)
448 signal(SIGTSTP, onsusp);
e7127e6a 449 (void) setjmp(tstpbuf);
46ebe0eb
RH
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';
4bdc6489
CL
489 if (totty)
490 signal(SIGTSTP, SIG_DFL);
46ebe0eb
RH
491cmnd:
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";
af075600 507 prevmsg = msg;
46ebe0eb
RH
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
186baa0c
CL
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 }
46ebe0eb
RH
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
570prmesg(length)
571int length;
572{
573 FILE *outf, *inf;
574 int c;
575
576 if (pause && length > Lpp) {
00f74c2b
RC
577 signal(SIGPIPE, SIG_IGN);
578 signal(SIGQUIT, SIG_IGN);
46ebe0eb
RH
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
00f74c2b 592 while (fgets(inbuf, sizeof inbuf, newmsg)) {
46ebe0eb 593 fputs(inbuf, outf);
5276b9b8
RC
594 if (ferror(outf)) {
595 clearerr(outf);
00f74c2b 596 break;
5276b9b8 597 }
00f74c2b 598 }
46ebe0eb
RH
599
600 if (outf != stdout) {
601 pclose(outf);
00f74c2b
RC
602 signal(SIGPIPE, SIG_DFL);
603 signal(SIGQUIT, SIG_DFL);
46ebe0eb
RH
604 }
605 else {
606 fflush(stdout);
607 }
608
609 /* trick to force wait on output */
610 stty(fileno(stdout), &otty);
611}
612
613onintr()
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
4bdc6489
CL
634/*
635 * We have just gotten a susp. Suspend and prepare to resume.
636 */
637onsusp()
638{
e7127e6a 639
4bdc6489 640 signal(SIGTSTP, SIG_DFL);
21cbf623 641 sigsetmask(0);
4bdc6489 642 kill(0, SIGTSTP);
4bdc6489 643 signal(SIGTSTP, onsusp);
24a1a865
CL
644 if (!mailing)
645 longjmp(tstpbuf);
4bdc6489
CL
646}
647
46ebe0eb
RH
648linecnt(f)
649FILE *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
662next(buf)
663char *buf;
664{
665 int i;
666 sscanf(buf, "%d", &i);
667 sprintf(buf, "Goto %d", i);
668 return(--i);
669}
670
671ask(prompt)
672char *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
750gfrsub(infile)
751FILE *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 > ' ') {
41c894fb 772 if (*in == ':' || *in == '@' || *in == '!')
46ebe0eb
RH
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
826char *
827nxtfld(s)
828char *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}