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