msgs.c revision 94504
11590Srgrimes/*- 21590Srgrimes * Copyright (c) 1980, 1993 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 71590Srgrimes * are met: 81590Srgrimes * 1. Redistributions of source code must retain the above copyright 91590Srgrimes * notice, this list of conditions and the following disclaimer. 101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111590Srgrimes * notice, this list of conditions and the following disclaimer in the 121590Srgrimes * documentation and/or other materials provided with the distribution. 131590Srgrimes * 3. All advertising materials mentioning features or use of this software 141590Srgrimes * must display the following acknowledgement: 151590Srgrimes * This product includes software developed by the University of 161590Srgrimes * California, Berkeley and its contributors. 171590Srgrimes * 4. Neither the name of the University nor the names of its contributors 181590Srgrimes * may be used to endorse or promote products derived from this software 191590Srgrimes * without specific prior written permission. 201590Srgrimes * 211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311590Srgrimes * SUCH DAMAGE. 321590Srgrimes */ 331590Srgrimes 341590Srgrimes#ifndef lint 3527751Scharnierstatic const char copyright[] = 361590Srgrimes"@(#) Copyright (c) 1980, 1993\n\ 371590Srgrimes The Regents of the University of California. All rights reserved.\n"; 381590Srgrimes#endif /* not lint */ 391590Srgrimes 401590Srgrimes#ifndef lint 4127751Scharnier#if 0 4227751Scharnierstatic char sccsid[] = "@(#)msgs.c 8.2 (Berkeley) 4/28/95"; 4327751Scharnier#endif 441590Srgrimes#endif /* not lint */ 451590Srgrimes 4694504Scharnier#include <sys/cdefs.h> 4794504Scharnier__FBSDID("$FreeBSD: head/usr.bin/msgs/msgs.c 94504 2002-04-12 13:11:42Z charnier $"); 4894504Scharnier 491590Srgrimes/* 501590Srgrimes * msgs - a user bulletin board program 511590Srgrimes * 521590Srgrimes * usage: 531590Srgrimes * msgs [fhlopq] [[-]number] to read messages 541590Srgrimes * msgs -s to place messages 551590Srgrimes * msgs -c [-days] to clean up the bulletin board 561590Srgrimes * 571590Srgrimes * prompt commands are: 581590Srgrimes * y print message 591590Srgrimes * n flush message, go to next message 601590Srgrimes * q flush message, quit 611590Srgrimes * p print message, turn on 'pipe thru more' mode 621590Srgrimes * P print message, turn off 'pipe thru more' mode 631590Srgrimes * - reprint last message 641590Srgrimes * s[-][<num>] [<filename>] save message 651590Srgrimes * m[-][<num>] mail with message in temp mbox 661590Srgrimes * x exit without flushing this message 671590Srgrimes * <num> print message number <num> 681590Srgrimes */ 691590Srgrimes 701590Srgrimes#define V7 /* will look for TERM in the environment */ 711590Srgrimes#define OBJECT /* will object to messages without Subjects */ 728382Srgrimes/* #define REJECT */ /* will reject messages without Subjects 731590Srgrimes (OBJECT must be defined also) */ 748382Srgrimes/* #define UNBUFFERED *//* use unbuffered output */ 751590Srgrimes 761590Srgrimes#include <sys/param.h> 771590Srgrimes#include <sys/stat.h> 781590Srgrimes#include <ctype.h> 7918485Sbde#include <dirent.h> 8027751Scharnier#include <err.h> 811590Srgrimes#include <errno.h> 8237534Sghelmer#include <fcntl.h> 8312809Sache#include <locale.h> 841590Srgrimes#include <pwd.h> 851590Srgrimes#include <setjmp.h> 8627751Scharnier#include <termcap.h> 876591Swollman#include <termios.h> 881590Srgrimes#include <signal.h> 891590Srgrimes#include <stdio.h> 901590Srgrimes#include <stdlib.h> 911590Srgrimes#include <string.h> 921590Srgrimes#include <time.h> 931590Srgrimes#include <unistd.h> 941590Srgrimes#include "pathnames.h" 951590Srgrimes 9612390Sache#define CMODE 0644 /* bounds file creation mode */ 971590Srgrimes#define NO 0 981590Srgrimes#define YES 1 991590Srgrimes#define SUPERUSER 0 /* superuser uid */ 1001590Srgrimes#define DAEMON 1 /* daemon uid */ 1011590Srgrimes#define NLINES 24 /* default number of lines/crt screen */ 1021590Srgrimes#define NDAYS 21 /* default keep time for messages */ 1031590Srgrimes#define DAYS *24*60*60 /* seconds/day */ 1041590Srgrimes#define MSGSRC ".msgsrc" /* user's rc file */ 1051590Srgrimes#define BOUNDS "bounds" /* message bounds file */ 1061590Srgrimes#define NEXT "Next message? [yq]" 1071590Srgrimes#define MORE "More? [ynq]" 1081590Srgrimes#define NOMORE "(No more) [q] ?" 1091590Srgrimes 1101590Srgrimestypedef char bool; 1111590Srgrimes 11277245SkrisFILE *msgsrc; 11377245SkrisFILE *newmsg; 11477245Skrisconst char *sep = "-"; 11577245Skrischar inbuf[BUFSIZ]; 11677245Skrischar fname[MAXPATHLEN]; 11777245Skrischar cmdbuf[MAXPATHLEN + MAXPATHLEN]; 11877245Skrischar subj[128]; 11977245Skrischar from[128]; 12077245Skrischar date[128]; 12177245Skrischar *ptr; 12277245Skrischar *in; 12377245Skrisbool local; 12477245Skrisbool ruptible; 12577245Skrisbool totty; 12677245Skrisbool seenfrom; 12777245Skrisbool seensubj; 12877245Skrisbool blankline; 12977245Skrisbool printing = NO; 13077245Skrisbool mailing = NO; 13177245Skrisbool quitit = NO; 13277245Skrisbool sending = NO; 13377245Skrisbool intrpflg = NO; 13494504Scharnieruid_t uid; 13577245Skrisint msg; 13677245Skrisint prevmsg; 13777245Skrisint lct; 13877245Skrisint nlines; 13977245Skrisint Lpp = 0; 14077245Skristime_t t; 14177245Skristime_t keep; 1421590Srgrimes 1431590Srgrimes/* option initialization */ 1441590Srgrimesbool hdrs = NO; 1451590Srgrimesbool qopt = NO; 1461590Srgrimesbool hush = NO; 1471590Srgrimesbool send_msg = NO; 1481590Srgrimesbool locomode = NO; 1491590Srgrimesbool use_pager = NO; 1501590Srgrimesbool clean = NO; 1511590Srgrimesbool lastcmd = NO; 1521590Srgrimesjmp_buf tstpbuf; 1531590Srgrimes 15427751Scharnier 15592921Simpvoid ask(const char *); 15692921Simpvoid gfrsub(FILE *); 15792921Simpint linecnt(FILE *); 15892921Simpint main(int, char *[]); 15992921Simpint next(char *); 16092921Simpchar *nxtfld(unsigned char *); 16192921Simpvoid onsusp(int); 16292921Simpvoid onintr(int); 16392921Simpvoid prmesg(int); 16492921Simpstatic void usage(void); 16527751Scharnier 16627751Scharnierint 1671590Srgrimesmain(argc, argv) 1681590Srgrimesint argc; char *argv[]; 1691590Srgrimes{ 1701590Srgrimes bool newrc, already; 1711590Srgrimes int rcfirst = 0; /* first message to print (from .rc) */ 1721590Srgrimes int rcback = 0; /* amount to back off of rcfirst */ 17312646Sdg int firstmsg = 0, nextmsg = 0, lastmsg = 0; 1741590Srgrimes int blast = 0; 17537476Sjkh struct stat buf; /* stat to check access of bounds */ 1761590Srgrimes FILE *bounds; 1771590Srgrimes 1781590Srgrimes#ifdef UNBUFFERED 1791590Srgrimes setbuf(stdout, NULL); 1801590Srgrimes#endif 18112809Sache setlocale(LC_ALL, ""); 1821590Srgrimes 1831590Srgrimes time(&t); 18437645Sghelmer setuid(uid = getuid()); 1851590Srgrimes ruptible = (signal(SIGINT, SIG_IGN) == SIG_DFL); 1861590Srgrimes if (ruptible) 1871590Srgrimes signal(SIGINT, SIG_DFL); 1881590Srgrimes 1891590Srgrimes argc--, argv++; 1901590Srgrimes while (argc > 0) { 1911590Srgrimes if (isdigit(argv[0][0])) { /* starting message # */ 1921590Srgrimes rcfirst = atoi(argv[0]); 1931590Srgrimes } 1941590Srgrimes else if (isdigit(argv[0][1])) { /* backward offset */ 1951590Srgrimes rcback = atoi( &( argv[0][1] ) ); 1961590Srgrimes } 1971590Srgrimes else { 1981590Srgrimes ptr = *argv; 1991590Srgrimes while (*ptr) switch (*ptr++) { 2001590Srgrimes 2011590Srgrimes case '-': 2021590Srgrimes break; 2031590Srgrimes 2041590Srgrimes case 'c': 20594504Scharnier if (uid != SUPERUSER && uid != DAEMON) 20694504Scharnier errx(1, 20794504Scharnier "only the super-user can use the c flag"); 2081590Srgrimes clean = YES; 2091590Srgrimes break; 2101590Srgrimes 2111590Srgrimes case 'f': /* silently */ 2121590Srgrimes hush = YES; 2131590Srgrimes break; 2141590Srgrimes 2151590Srgrimes case 'h': /* headers only */ 2161590Srgrimes hdrs = YES; 2171590Srgrimes break; 2181590Srgrimes 2191590Srgrimes case 'l': /* local msgs only */ 2201590Srgrimes locomode = YES; 2211590Srgrimes break; 2221590Srgrimes 2231590Srgrimes case 'o': /* option to save last message */ 2241590Srgrimes lastcmd = YES; 2251590Srgrimes break; 2261590Srgrimes 2271590Srgrimes case 'p': /* pipe thru 'more' during long msgs */ 2281590Srgrimes use_pager = YES; 2291590Srgrimes break; 2301590Srgrimes 2311590Srgrimes case 'q': /* query only */ 2321590Srgrimes qopt = YES; 2331590Srgrimes break; 2341590Srgrimes 2351590Srgrimes case 's': /* sending TO msgs */ 2361590Srgrimes send_msg = YES; 2371590Srgrimes break; 2381590Srgrimes 2391590Srgrimes default: 24027751Scharnier usage(); 2411590Srgrimes } 2421590Srgrimes } 2431590Srgrimes argc--, argv++; 2441590Srgrimes } 2451590Srgrimes 2461590Srgrimes /* 2471590Srgrimes * determine current message bounds 2481590Srgrimes */ 24927751Scharnier snprintf(fname, sizeof(fname), "%s/%s", _PATH_MSGS, BOUNDS); 25049061Smjacob 25149061Smjacob /* 25249061Smjacob * Test access rights to the bounds file 25349061Smjacob * This can be a little tricky. if(send_msg), then 25449061Smjacob * we will create it. We assume that if(send_msg), 25549061Smjacob * then you have write permission there. 25649061Smjacob * Else, it better be there, or we bail. 25749061Smjacob */ 25849061Smjacob if (send_msg != YES) { 25949061Smjacob if (stat(fname, &buf) < 0) { 26049061Smjacob if (hush != YES) { 26149061Smjacob err(errno, "%s", fname); 26249061Smjacob } else { 26349061Smjacob exit(1); 26449061Smjacob } 26549061Smjacob } 26649061Smjacob } 2671590Srgrimes bounds = fopen(fname, "r"); 2681590Srgrimes 2691590Srgrimes if (bounds != NULL) { 2701590Srgrimes fscanf(bounds, "%d %d\n", &firstmsg, &lastmsg); 2711590Srgrimes fclose(bounds); 2721590Srgrimes blast = lastmsg; /* save upper bound */ 2731590Srgrimes } 2741590Srgrimes 2751590Srgrimes if (clean) 2761590Srgrimes keep = t - (rcback? rcback : NDAYS) DAYS; 2771590Srgrimes 2781590Srgrimes if (clean || bounds == NULL) { /* relocate message bounds */ 27918485Sbde struct dirent *dp; 2801590Srgrimes struct stat stbuf; 2811590Srgrimes bool seenany = NO; 2821590Srgrimes DIR *dirp; 2831590Srgrimes 2841590Srgrimes dirp = opendir(_PATH_MSGS); 28527751Scharnier if (dirp == NULL) 28627751Scharnier err(errno, "%s", _PATH_MSGS); 2871590Srgrimes 2881590Srgrimes firstmsg = 32767; 2891590Srgrimes lastmsg = 0; 2901590Srgrimes 2911590Srgrimes for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)){ 2921590Srgrimes register char *cp = dp->d_name; 2931590Srgrimes register int i = 0; 2941590Srgrimes 2951590Srgrimes if (dp->d_ino == 0) 2961590Srgrimes continue; 2971590Srgrimes if (dp->d_namlen == 0) 2981590Srgrimes continue; 2991590Srgrimes 3001590Srgrimes if (clean) 30127751Scharnier snprintf(inbuf, sizeof(inbuf), "%s/%s", _PATH_MSGS, cp); 3021590Srgrimes 3031590Srgrimes while (isdigit(*cp)) 3041590Srgrimes i = i * 10 + *cp++ - '0'; 3051590Srgrimes if (*cp) 3061590Srgrimes continue; /* not a message! */ 3071590Srgrimes 3081590Srgrimes if (clean) { 3091590Srgrimes if (stat(inbuf, &stbuf) != 0) 3101590Srgrimes continue; 3111590Srgrimes if (stbuf.st_mtime < keep 3121590Srgrimes && stbuf.st_mode&S_IWRITE) { 3131590Srgrimes unlink(inbuf); 3141590Srgrimes continue; 3151590Srgrimes } 3161590Srgrimes } 3171590Srgrimes 3181590Srgrimes if (i > lastmsg) 3191590Srgrimes lastmsg = i; 3201590Srgrimes if (i < firstmsg) 3211590Srgrimes firstmsg = i; 3221590Srgrimes seenany = YES; 3231590Srgrimes } 3241590Srgrimes closedir(dirp); 3251590Srgrimes 3261590Srgrimes if (!seenany) { 3271590Srgrimes if (blast != 0) /* never lower the upper bound! */ 3281590Srgrimes lastmsg = blast; 3291590Srgrimes firstmsg = lastmsg + 1; 3301590Srgrimes } 3311590Srgrimes else if (blast > lastmsg) 3321590Srgrimes lastmsg = blast; 3331590Srgrimes 3341590Srgrimes if (!send_msg) { 3351590Srgrimes bounds = fopen(fname, "w"); 33627751Scharnier if (bounds == NULL) 33727751Scharnier err(errno, "%s", fname); 3381590Srgrimes chmod(fname, CMODE); 3391590Srgrimes fprintf(bounds, "%d %d\n", firstmsg, lastmsg); 3401590Srgrimes fclose(bounds); 3411590Srgrimes } 3421590Srgrimes } 3431590Srgrimes 3441590Srgrimes if (send_msg) { 3451590Srgrimes /* 3461590Srgrimes * Send mode - place msgs in _PATH_MSGS 3471590Srgrimes */ 3481590Srgrimes bounds = fopen(fname, "w"); 34927751Scharnier if (bounds == NULL) 35027751Scharnier err(errno, "%s", fname); 3511590Srgrimes 3521590Srgrimes nextmsg = lastmsg + 1; 35327751Scharnier snprintf(fname, sizeof(fname), "%s/%d", _PATH_MSGS, nextmsg); 3541590Srgrimes newmsg = fopen(fname, "w"); 35527751Scharnier if (newmsg == NULL) 35627751Scharnier err(errno, "%s", fname); 35712390Sache chmod(fname, CMODE); 3581590Srgrimes 3591590Srgrimes fprintf(bounds, "%d %d\n", firstmsg, nextmsg); 3601590Srgrimes fclose(bounds); 3611590Srgrimes 3621590Srgrimes sending = YES; 3631590Srgrimes if (ruptible) 3641590Srgrimes signal(SIGINT, onintr); 3651590Srgrimes 3661590Srgrimes if (isatty(fileno(stdin))) { 3671590Srgrimes ptr = getpwuid(uid)->pw_name; 3681590Srgrimes printf("Message %d:\nFrom %s %sSubject: ", 3691590Srgrimes nextmsg, ptr, ctime(&t)); 3701590Srgrimes fflush(stdout); 3711590Srgrimes fgets(inbuf, sizeof inbuf, stdin); 3721590Srgrimes putchar('\n'); 3731590Srgrimes fflush(stdout); 3741590Srgrimes fprintf(newmsg, "From %s %sSubject: %s\n", 3751590Srgrimes ptr, ctime(&t), inbuf); 3761590Srgrimes blankline = seensubj = YES; 3771590Srgrimes } 3781590Srgrimes else 3791590Srgrimes blankline = seensubj = NO; 3801590Srgrimes for (;;) { 3811590Srgrimes fgets(inbuf, sizeof inbuf, stdin); 3821590Srgrimes if (feof(stdin) || ferror(stdin)) 3831590Srgrimes break; 3841590Srgrimes blankline = (blankline || (inbuf[0] == '\n')); 3851590Srgrimes seensubj = (seensubj || (!blankline && (strncmp(inbuf, "Subj", 4) == 0))); 3861590Srgrimes fputs(inbuf, newmsg); 3871590Srgrimes } 3881590Srgrimes#ifdef OBJECT 3891590Srgrimes if (!seensubj) { 3901590Srgrimes printf("NOTICE: Messages should have a Subject field!\n"); 3911590Srgrimes#ifdef REJECT 3921590Srgrimes unlink(fname); 3931590Srgrimes#endif 3941590Srgrimes exit(1); 3951590Srgrimes } 3961590Srgrimes#endif 3971590Srgrimes exit(ferror(stdin)); 3981590Srgrimes } 3991590Srgrimes if (clean) 4001590Srgrimes exit(0); 4011590Srgrimes 4021590Srgrimes /* 4031590Srgrimes * prepare to display messages 4041590Srgrimes */ 4051590Srgrimes totty = (isatty(fileno(stdout)) != 0); 4061590Srgrimes use_pager = use_pager && totty; 4071590Srgrimes 40827751Scharnier snprintf(fname, sizeof(fname), "%s/%s", getenv("HOME"), MSGSRC); 4091590Srgrimes msgsrc = fopen(fname, "r"); 4101590Srgrimes if (msgsrc) { 4111590Srgrimes newrc = NO; 4121590Srgrimes fscanf(msgsrc, "%d\n", &nextmsg); 4131590Srgrimes fclose(msgsrc); 4141590Srgrimes if (nextmsg > lastmsg+1) { 4151590Srgrimes printf("Warning: bounds have been reset (%d, %d)\n", 4161590Srgrimes firstmsg, lastmsg); 4171590Srgrimes truncate(fname, (off_t)0); 4181590Srgrimes newrc = YES; 4191590Srgrimes } 4201590Srgrimes else if (!rcfirst) 4211590Srgrimes rcfirst = nextmsg - rcback; 4221590Srgrimes } 4231590Srgrimes else 4241590Srgrimes newrc = YES; 4251590Srgrimes msgsrc = fopen(fname, "r+"); 4261590Srgrimes if (msgsrc == NULL) 4271590Srgrimes msgsrc = fopen(fname, "w"); 42827751Scharnier if (msgsrc == NULL) 42927751Scharnier err(errno, "%s", fname); 4301590Srgrimes if (rcfirst) { 4311590Srgrimes if (rcfirst > lastmsg+1) { 4321590Srgrimes printf("Warning: the last message is number %d.\n", 4331590Srgrimes lastmsg); 4341590Srgrimes rcfirst = nextmsg; 4351590Srgrimes } 4361590Srgrimes if (rcfirst > firstmsg) 4371590Srgrimes firstmsg = rcfirst; /* don't set below first msg */ 4381590Srgrimes } 4391590Srgrimes if (newrc) { 4401590Srgrimes nextmsg = firstmsg; 44182846Sache rewind(msgsrc); 4421590Srgrimes fprintf(msgsrc, "%d\n", nextmsg); 4431590Srgrimes fflush(msgsrc); 4441590Srgrimes } 4451590Srgrimes 4461590Srgrimes#ifdef V7 4471590Srgrimes if (totty) { 4481590Srgrimes struct winsize win; 4491590Srgrimes if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1) 4501590Srgrimes Lpp = win.ws_row; 4511590Srgrimes if (Lpp <= 0) { 4521590Srgrimes if (tgetent(inbuf, getenv("TERM")) <= 0 45377245Skris || (Lpp = tgetnum(__DECONST(char *, "li"))) <= 0) { 4541590Srgrimes Lpp = NLINES; 4551590Srgrimes } 4561590Srgrimes } 4571590Srgrimes } 4581590Srgrimes#endif 4591590Srgrimes Lpp -= 6; /* for headers, etc. */ 4601590Srgrimes 4611590Srgrimes already = NO; 4621590Srgrimes prevmsg = firstmsg; 4631590Srgrimes printing = YES; 4641590Srgrimes if (ruptible) 4651590Srgrimes signal(SIGINT, onintr); 4661590Srgrimes 4671590Srgrimes /* 4681590Srgrimes * Main program loop 4691590Srgrimes */ 4701590Srgrimes for (msg = firstmsg; msg <= lastmsg; msg++) { 4711590Srgrimes 47227751Scharnier snprintf(fname, sizeof(fname), "%s/%d", _PATH_MSGS, msg); 4731590Srgrimes newmsg = fopen(fname, "r"); 4741590Srgrimes if (newmsg == NULL) 4751590Srgrimes continue; 4761590Srgrimes 4771590Srgrimes gfrsub(newmsg); /* get From and Subject fields */ 4781590Srgrimes if (locomode && !local) { 4791590Srgrimes fclose(newmsg); 4801590Srgrimes continue; 4811590Srgrimes } 4821590Srgrimes 4831590Srgrimes if (qopt) { /* This has to be located here */ 4841590Srgrimes printf("There are new messages.\n"); 4851590Srgrimes exit(0); 4861590Srgrimes } 4871590Srgrimes 4881590Srgrimes if (already && !hdrs) 4891590Srgrimes putchar('\n'); 4901590Srgrimes 4911590Srgrimes /* 4921590Srgrimes * Print header 4931590Srgrimes */ 4941590Srgrimes if (totty) 4951590Srgrimes signal(SIGTSTP, onsusp); 4961590Srgrimes (void) setjmp(tstpbuf); 4971590Srgrimes already = YES; 4981590Srgrimes nlines = 2; 4991590Srgrimes if (seenfrom) { 5001590Srgrimes printf("Message %d:\nFrom %s %s", msg, from, date); 5011590Srgrimes nlines++; 5021590Srgrimes } 5031590Srgrimes if (seensubj) { 5041590Srgrimes printf("Subject: %s", subj); 5051590Srgrimes nlines++; 5061590Srgrimes } 5071590Srgrimes else { 5081590Srgrimes if (seenfrom) { 5091590Srgrimes putchar('\n'); 5101590Srgrimes nlines++; 5111590Srgrimes } 5121590Srgrimes while (nlines < 6 5131590Srgrimes && fgets(inbuf, sizeof inbuf, newmsg) 5141590Srgrimes && inbuf[0] != '\n') { 5151590Srgrimes fputs(inbuf, stdout); 5161590Srgrimes nlines++; 5171590Srgrimes } 5181590Srgrimes } 5191590Srgrimes 5201590Srgrimes lct = linecnt(newmsg); 5211590Srgrimes if (lct) 5221590Srgrimes printf("(%d%slines) ", lct, seensubj? " " : " more "); 5231590Srgrimes 5241590Srgrimes if (hdrs) { 5251590Srgrimes printf("\n-----\n"); 5261590Srgrimes fclose(newmsg); 5271590Srgrimes continue; 5281590Srgrimes } 5291590Srgrimes 5301590Srgrimes /* 5311590Srgrimes * Ask user for command 5321590Srgrimes */ 5331590Srgrimes if (totty) 5341590Srgrimes ask(lct? MORE : (msg==lastmsg? NOMORE : NEXT)); 5351590Srgrimes else 5361590Srgrimes inbuf[0] = 'y'; 5371590Srgrimes if (totty) 5381590Srgrimes signal(SIGTSTP, SIG_DFL); 5391590Srgrimescmnd: 5401590Srgrimes in = inbuf; 5411590Srgrimes switch (*in) { 5421590Srgrimes case 'x': 54394504Scharnier /* FALLTHROUGH */ 5441590Srgrimes case 'X': 5451590Srgrimes exit(0); 54694504Scharnier /* NOTREACHED */ 5471590Srgrimes case 'q': 54894504Scharnier /* FALLTHROUGH */ 5491590Srgrimes case 'Q': 5501590Srgrimes quitit = YES; 5511590Srgrimes printf("--Postponed--\n"); 5521590Srgrimes exit(0); 55394504Scharnier /* NOTREACHED */ 5541590Srgrimes case 'n': 55594504Scharnier /* FALLTHROUGH */ 5561590Srgrimes case 'N': 5571590Srgrimes if (msg >= nextmsg) sep = "Flushed"; 5581590Srgrimes prevmsg = msg; 5591590Srgrimes break; 5601590Srgrimes 5611590Srgrimes case 'p': 56294504Scharnier /* FALLTHROUGH */ 5631590Srgrimes case 'P': 5641590Srgrimes use_pager = (*in++ == 'p'); 56594504Scharnier /* FALLTHROUGH */ 5661590Srgrimes case '\n': 56794504Scharnier /* FALLTHROUGH */ 5681590Srgrimes case 'y': 5691590Srgrimes default: 5701590Srgrimes if (*in == '-') { 5711590Srgrimes msg = prevmsg-1; 5721590Srgrimes sep = "replay"; 5731590Srgrimes break; 5741590Srgrimes } 5751590Srgrimes if (isdigit(*in)) { 5761590Srgrimes msg = next(in); 5771590Srgrimes sep = in; 5781590Srgrimes break; 5791590Srgrimes } 5801590Srgrimes 5811590Srgrimes prmesg(nlines + lct + (seensubj? 1 : 0)); 5821590Srgrimes prevmsg = msg; 5831590Srgrimes 5841590Srgrimes } 5851590Srgrimes 5861590Srgrimes printf("--%s--\n", sep); 5871590Srgrimes sep = "-"; 5881590Srgrimes if (msg >= nextmsg) { 5891590Srgrimes nextmsg = msg + 1; 59082846Sache rewind(msgsrc); 5911590Srgrimes fprintf(msgsrc, "%d\n", nextmsg); 5921590Srgrimes fflush(msgsrc); 5931590Srgrimes } 5941590Srgrimes if (newmsg) 5951590Srgrimes fclose(newmsg); 5961590Srgrimes if (quitit) 5971590Srgrimes break; 5981590Srgrimes } 5991590Srgrimes 6001590Srgrimes /* 6011590Srgrimes * Make sure .rc file gets updated 6021590Srgrimes */ 6031590Srgrimes if (--msg >= nextmsg) { 6041590Srgrimes nextmsg = msg + 1; 60582846Sache rewind(msgsrc); 6061590Srgrimes fprintf(msgsrc, "%d\n", nextmsg); 6071590Srgrimes fflush(msgsrc); 6081590Srgrimes } 6091590Srgrimes if (already && !quitit && lastcmd && totty) { 6101590Srgrimes /* 6111590Srgrimes * save or reply to last message? 6121590Srgrimes */ 6131590Srgrimes msg = prevmsg; 6141590Srgrimes ask(NOMORE); 6151590Srgrimes if (inbuf[0] == '-' || isdigit(inbuf[0])) 6161590Srgrimes goto cmnd; 6171590Srgrimes } 6181590Srgrimes if (!(already || hush || qopt)) 6191590Srgrimes printf("No new messages.\n"); 6201590Srgrimes exit(0); 62194504Scharnier /* NOTREACHED */ 6221590Srgrimes} 6231590Srgrimes 62427751Scharnierstatic void 62527751Scharnierusage() 62627751Scharnier{ 62727751Scharnier fprintf(stderr, "usage: msgs [fhlopq] [[-]number]\n"); 62827751Scharnier exit(1); 62927751Scharnier} 63027751Scharnier 63127751Scharniervoid 6321590Srgrimesprmesg(length) 6331590Srgrimesint length; 6341590Srgrimes{ 6351590Srgrimes FILE *outf; 63637534Sghelmer char *env_pager; 6371590Srgrimes 6381590Srgrimes if (use_pager && length > Lpp) { 6391590Srgrimes signal(SIGPIPE, SIG_IGN); 6401590Srgrimes signal(SIGQUIT, SIG_IGN); 64137534Sghelmer if ((env_pager = getenv("PAGER")) == NULL) { 64237534Sghelmer snprintf(cmdbuf, sizeof(cmdbuf), _PATH_PAGER, Lpp); 64337534Sghelmer } else { 64462726Skris snprintf(cmdbuf, sizeof(cmdbuf), "%s", env_pager); 64537534Sghelmer } 6461590Srgrimes outf = popen(cmdbuf, "w"); 6471590Srgrimes if (!outf) 6481590Srgrimes outf = stdout; 6491590Srgrimes else 6501590Srgrimes setbuf(outf, (char *)NULL); 6511590Srgrimes } 6521590Srgrimes else 6531590Srgrimes outf = stdout; 6541590Srgrimes 6551590Srgrimes if (seensubj) 6561590Srgrimes putc('\n', outf); 6571590Srgrimes 6581590Srgrimes while (fgets(inbuf, sizeof inbuf, newmsg)) { 6591590Srgrimes fputs(inbuf, outf); 6601590Srgrimes if (ferror(outf)) { 6611590Srgrimes clearerr(outf); 6621590Srgrimes break; 6631590Srgrimes } 6641590Srgrimes } 6651590Srgrimes 6661590Srgrimes if (outf != stdout) { 6671590Srgrimes pclose(outf); 6681590Srgrimes signal(SIGPIPE, SIG_DFL); 6691590Srgrimes signal(SIGQUIT, SIG_DFL); 6701590Srgrimes } 6711590Srgrimes else { 6721590Srgrimes fflush(stdout); 6731590Srgrimes } 6741590Srgrimes 6756591Swollman /* force wait on output */ 6766591Swollman tcdrain(fileno(stdout)); 6771590Srgrimes} 6781590Srgrimes 6791590Srgrimesvoid 68027751Scharnieronintr(unused) 68177245Skris int unused __unused; 6821590Srgrimes{ 6831590Srgrimes signal(SIGINT, onintr); 6841590Srgrimes if (mailing) 6851590Srgrimes unlink(fname); 6861590Srgrimes if (sending) { 6871590Srgrimes unlink(fname); 6881590Srgrimes puts("--Killed--"); 6891590Srgrimes exit(1); 6901590Srgrimes } 6911590Srgrimes if (printing) { 6921590Srgrimes putchar('\n'); 6931590Srgrimes if (hdrs) 6941590Srgrimes exit(0); 6951590Srgrimes sep = "Interrupt"; 6961590Srgrimes if (newmsg) 69782846Sache fseeko(newmsg, (off_t)0, SEEK_END); 6981590Srgrimes intrpflg = YES; 6991590Srgrimes } 7001590Srgrimes} 7011590Srgrimes 7021590Srgrimes/* 7031590Srgrimes * We have just gotten a susp. Suspend and prepare to resume. 7041590Srgrimes */ 7051590Srgrimesvoid 70627751Scharnieronsusp(unused) 70777245Skris int unused __unused; 7081590Srgrimes{ 7091590Srgrimes signal(SIGTSTP, SIG_DFL); 7101590Srgrimes sigsetmask(0); 7111590Srgrimes kill(0, SIGTSTP); 7121590Srgrimes signal(SIGTSTP, onsusp); 7131590Srgrimes if (!mailing) 7141590Srgrimes longjmp(tstpbuf, 0); 7151590Srgrimes} 7161590Srgrimes 71727751Scharnierint 7181590Srgrimeslinecnt(f) 7191590SrgrimesFILE *f; 7201590Srgrimes{ 72182846Sache off_t oldpos = ftello(f); 7221590Srgrimes int l = 0; 7231590Srgrimes char lbuf[BUFSIZ]; 7241590Srgrimes 7251590Srgrimes while (fgets(lbuf, sizeof lbuf, f)) 7261590Srgrimes l++; 7271590Srgrimes clearerr(f); 72882846Sache fseeko(f, oldpos, SEEK_SET); 7291590Srgrimes return (l); 7301590Srgrimes} 7311590Srgrimes 73227751Scharnierint 7331590Srgrimesnext(buf) 7341590Srgrimeschar *buf; 7351590Srgrimes{ 7361590Srgrimes int i; 7371590Srgrimes sscanf(buf, "%d", &i); 7381590Srgrimes sprintf(buf, "Goto %d", i); 7391590Srgrimes return(--i); 7401590Srgrimes} 7411590Srgrimes 74227751Scharniervoid 7431590Srgrimesask(prompt) 74477245Skrisconst char *prompt; 7451590Srgrimes{ 7461590Srgrimes char inch; 74737534Sghelmer int n, cmsg, fd; 7481590Srgrimes off_t oldpos; 7491590Srgrimes FILE *cpfrom, *cpto; 7501590Srgrimes 7511590Srgrimes printf("%s ", prompt); 7521590Srgrimes fflush(stdout); 7531590Srgrimes intrpflg = NO; 7541590Srgrimes (void) fgets(inbuf, sizeof inbuf, stdin); 7551590Srgrimes if ((n = strlen(inbuf)) > 0 && inbuf[n - 1] == '\n') 7561590Srgrimes inbuf[n - 1] = '\0'; 7571590Srgrimes if (intrpflg) 7581590Srgrimes inbuf[0] = 'x'; 7591590Srgrimes 7601590Srgrimes /* 7611590Srgrimes * Handle 'mail' and 'save' here. 7621590Srgrimes */ 7631590Srgrimes if ((inch = inbuf[0]) == 's' || inch == 'm') { 7641590Srgrimes if (inbuf[1] == '-') 7651590Srgrimes cmsg = prevmsg; 7661590Srgrimes else if (isdigit(inbuf[1])) 7671590Srgrimes cmsg = atoi(&inbuf[1]); 7681590Srgrimes else 7691590Srgrimes cmsg = msg; 77027751Scharnier snprintf(fname, sizeof(fname), "%s/%d", _PATH_MSGS, cmsg); 7711590Srgrimes 77282846Sache oldpos = ftello(newmsg); 7731590Srgrimes 7741590Srgrimes cpfrom = fopen(fname, "r"); 7751590Srgrimes if (!cpfrom) { 7761590Srgrimes printf("Message %d not found\n", cmsg); 7771590Srgrimes ask (prompt); 7781590Srgrimes return; 7791590Srgrimes } 7801590Srgrimes 7811590Srgrimes if (inch == 's') { 7821590Srgrimes in = nxtfld(inbuf); 7831590Srgrimes if (*in) { 7841590Srgrimes for (n=0; in[n] > ' '; n++) { /* sizeof fname? */ 7851590Srgrimes fname[n] = in[n]; 7861590Srgrimes } 7871590Srgrimes fname[n] = NULL; 7881590Srgrimes } 7891590Srgrimes else 7901590Srgrimes strcpy(fname, "Messages"); 79137534Sghelmer fd = open(fname, O_RDWR|O_EXCL|O_CREAT|O_APPEND); 7921590Srgrimes } 7931590Srgrimes else { 7941590Srgrimes strcpy(fname, _PATH_TMP); 79537534Sghelmer fd = mkstemp(fname); 79637534Sghelmer if (fd != -1) { 79737534Sghelmer snprintf(cmdbuf, sizeof(cmdbuf), _PATH_MAIL, 79837534Sghelmer fname); 79937534Sghelmer mailing = YES; 80037534Sghelmer } 8011590Srgrimes } 80237534Sghelmer if (fd == -1 || (cpto = fdopen(fd, "a")) == NULL) { 80337534Sghelmer if (fd != -1) 80437534Sghelmer close(fd); 80527751Scharnier warn("%s", fname); 8061590Srgrimes mailing = NO; 80782846Sache fseeko(newmsg, oldpos, SEEK_SET); 8081590Srgrimes ask(prompt); 8091590Srgrimes return; 8101590Srgrimes } 8111590Srgrimes 81227751Scharnier while ((n = fread(inbuf, 1, sizeof inbuf, cpfrom))) 8131590Srgrimes fwrite(inbuf, 1, n, cpto); 8141590Srgrimes 8151590Srgrimes fclose(cpfrom); 8161590Srgrimes fclose(cpto); 81782846Sache fseeko(newmsg, oldpos, SEEK_SET);/* reposition current message */ 8181590Srgrimes if (inch == 's') 8191590Srgrimes printf("Message %d saved in \"%s\"\n", cmsg, fname); 8201590Srgrimes else { 8211590Srgrimes system(cmdbuf); 8221590Srgrimes unlink(fname); 8231590Srgrimes mailing = NO; 8241590Srgrimes } 8251590Srgrimes ask(prompt); 8261590Srgrimes } 8271590Srgrimes} 8281590Srgrimes 82927751Scharniervoid 8301590Srgrimesgfrsub(infile) 8311590SrgrimesFILE *infile; 8321590Srgrimes{ 8331590Srgrimes off_t frompos; 83437534Sghelmer int count; 8351590Srgrimes 8361590Srgrimes seensubj = seenfrom = NO; 8371590Srgrimes local = YES; 8381590Srgrimes subj[0] = from[0] = date[0] = NULL; 8391590Srgrimes 8401590Srgrimes /* 8411590Srgrimes * Is this a normal message? 8421590Srgrimes */ 8431590Srgrimes if (fgets(inbuf, sizeof inbuf, infile)) { 8441590Srgrimes if (strncmp(inbuf, "From", 4)==0) { 8451590Srgrimes /* 8461590Srgrimes * expected form starts with From 8471590Srgrimes */ 8481590Srgrimes seenfrom = YES; 84982846Sache frompos = ftello(infile); 8501590Srgrimes ptr = from; 8511590Srgrimes in = nxtfld(inbuf); 85237534Sghelmer if (*in) { 85337534Sghelmer count = sizeof(from) - 1; 85437534Sghelmer while (*in && *in > ' ' && count-- > 0) { 85537534Sghelmer if (*in == ':' || *in == '@' || 85637534Sghelmer *in == '!') 85737534Sghelmer local = NO; 85837534Sghelmer *ptr++ = *in++; 85937534Sghelmer } 8601590Srgrimes } 8611590Srgrimes *ptr = NULL; 8621590Srgrimes if (*(in = nxtfld(in))) 8631590Srgrimes strncpy(date, in, sizeof date); 8641590Srgrimes else { 8651590Srgrimes date[0] = '\n'; 8661590Srgrimes date[1] = NULL; 8671590Srgrimes } 8681590Srgrimes } 8691590Srgrimes else { 8701590Srgrimes /* 8711590Srgrimes * not the expected form 8721590Srgrimes */ 87382846Sache rewind(infile); 8741590Srgrimes return; 8751590Srgrimes } 8761590Srgrimes } 8771590Srgrimes else 8781590Srgrimes /* 8791590Srgrimes * empty file ? 8801590Srgrimes */ 8811590Srgrimes return; 8821590Srgrimes 8831590Srgrimes /* 8841590Srgrimes * look for Subject line until EOF or a blank line 8851590Srgrimes */ 8861590Srgrimes while (fgets(inbuf, sizeof inbuf, infile) 8871590Srgrimes && !(blankline = (inbuf[0] == '\n'))) { 8881590Srgrimes /* 8891590Srgrimes * extract Subject line 8901590Srgrimes */ 8911590Srgrimes if (!seensubj && strncmp(inbuf, "Subj", 4)==0) { 8921590Srgrimes seensubj = YES; 89382846Sache frompos = ftello(infile); 8941590Srgrimes strncpy(subj, nxtfld(inbuf), sizeof subj); 8951590Srgrimes } 8961590Srgrimes } 8971590Srgrimes if (!blankline) 8981590Srgrimes /* 8991590Srgrimes * ran into EOF 9001590Srgrimes */ 90182846Sache fseeko(infile, frompos, SEEK_SET); 9021590Srgrimes 9031590Srgrimes if (!seensubj) 9041590Srgrimes /* 9051590Srgrimes * for possible use with Mail 9061590Srgrimes */ 9071590Srgrimes strncpy(subj, "(No Subject)\n", sizeof subj); 9081590Srgrimes} 9091590Srgrimes 9101590Srgrimeschar * 9111590Srgrimesnxtfld(s) 91212809Sacheunsigned char *s; 9131590Srgrimes{ 91412809Sache if (*s) while (*s && !isspace(*s)) s++; /* skip over this field */ 91512809Sache if (*s) while (*s && isspace(*s)) s++; /* find start of next field */ 9161590Srgrimes return (s); 9171590Srgrimes} 918