[net.sources] news 2.10.2 src part 5

rick@seismo.UUCP (Rick Adams) (09/05/84)

if test ! -d src
then
	mkdir src
	echo mkdir src
fi
echo x - src/process.c
sed 's/^X//' >src/process.c <<'*-*-END-of-src/process.c-*-*'
X/*
X * process - process options for readnews
X */
X
Xstatic char *SccsId = "@(#)process.c	2.9	4/20/84";
X
X#include "rparams.h"
X
X#define OPTION	0	/* pick up an option string */
X#define STRING	1	/* pick up a string of arguments */
X
Xstruct optable *optpt, options[] = { /*
Xoptlet	filchar	flag	newstate oldmode	newmode	buf	*/
X'p',	'\0',	FALSE,	OPTION,	UNKNOWN,	UNKNOWN,(char *)NULL,
X't',	'\0',	FALSE,	STRING,	ANY,		UNKNOWN,header.title,
X'a',	' ',	FALSE,	STRING,	ANY,		UNKNOWN,datebuf,
X'n',   NGDELIM,	FALSE,	STRING,	ANY,		UNKNOWN,header.nbuf,
X'c',	' ',	FALSE,	STRING,	UNKNOWN,	UNKNOWN,coptbuf,
X'l',	' ',	FALSE,	OPTION,	UNKNOWN,	UNKNOWN,(char *)NULL,
X'r',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,(char *)NULL,
X's',   NGDELIM,	FALSE,	STRING,	ANY,		UNKNOWN,header.nbuf,
X'x',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,(char *)NULL,
X'h',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,(char *)NULL,
X'M',	'\0',	FALSE,	OPTION,	UNKNOWN,	MAIL,	(char *)NULL,
X'f',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,(char *)NULL,
X'u',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,(char *)NULL,
X'e',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,(char *)NULL,
X'\0',	'\0',	0,	0,	0,		0,	(char *)NULL
X};
X
Xprocess(argc,argv)
Xregister int argc;
Xregister char **argv;
X{
X	register int state = OPTION;
X	register char *ptr;
X	char filchar;
X	int len, tlen;
X
X	/* loop once per arg. */
X
X	while (--argc) {
X	    if (state == OPTION) {
X		if (**argv != '-') {
X			sprintf(bfr, "Bad option string \"%s\"", *argv);
X			xerror(bfr);
X		}
X		while (*++*argv != '\0') {
X			for (optpt = options; optpt->optlet != '\0'; ++optpt) {
X				if (optpt->optlet == **argv)
X					goto found;
X			}
X			/* unknown option letter */
X			fprintf(stderr, "Usage: readnews [ -a [ date ]] [ -n newsgroups ] [ -t titles ] [ -lprxhfuM ]\n");
X			fprintf(stderr, "\t[ -c [ ``mailer'' ]]\n\n");
X			fprintf(stderr, "       readnews -s\n");
X			exit(1);
X
X		    found:;
X			if (mode != UNKNOWN && (mode&optpt->oldmode) == 0) {
X				sprintf(bfr, "Bad %c option", **argv);
X				xerror(bfr);
X			}
X			if (mode == UNKNOWN)
X				mode = optpt->newmode;
X			filchar = optpt->filchar;
X			optpt->flag = TRUE;
X			state = optpt->newstate;
X			ptr = optpt->buf;
X			len = LBUFLEN;
X		}
X
X		argv++;		/* done with this option arg. */
X
X	    } else {
X
X		/*
X		 * Pick up a piece of a string and put it into
X		 * the appropriate buffer.
X		 */
X		if (**argv == '-') {
X			state = OPTION;
X			argc++;	/* uncount this arg. */
X			continue;
X		}
X
X		if ((tlen = strlen(*argv)) >= len)
X			xerror("Argument string too long");
X		strcpy(ptr, *argv++);
X		ptr += tlen;
X		if (*(ptr-1) != filchar)
X			*ptr++ = filchar;
X		len -= tlen + 1;
X		*ptr = '\0';
X	    }
X	}
X	return;
X}
*-*-END-of-src/process.c-*-*
echo x - src/readnews.c
sed 's/^X//' >src/readnews.c <<'*-*-END-of-src/readnews.c-*-*'
X/*
X * readnews - read news articles.
X */
X
X#ifndef lint
Xstatic char	*SccsId = "@(#)readnews.c	2.19	8/28/84";
X#endif !lint
X
X#include "rparams.h"
X
X/*
X * readnews - article reading program
X */
X
X#ifndef SYSBUF
Xchar	SYSBUF[BUFSIZ];	/* to buffer std out */
X#endif
X
X#define OPTION	0	/* pick up an option string */
X#define STRING	1	/* pick up a string of arguments */
X
Xint onsig();
X
X/*
X *	Authors:
X *		Matt Glickman	ucbvax!glickman
X *		Mark Horton	cbosg!mark
X *		Stephen Daniels	duke!swd
X *		Tom Truscott	duke!trt
X */
X
Xmain(argc, argv)
Xint	argc;
Xregister char	**argv;
X{
X	register char	*ptr;	/* pointer to rest of buffer		*/
X	char	*user, *home;
X	int	optflag = FALSE, space = FALSE;
X	struct utsname ubuf;
X	char	*myrc;
X
X	/* set up defaults and initialize. */
X	pathinit();
X	mode = UNKNOWN;
X	header.title[0] = header.nbuf[0] = '\0';
X	titlebuf[0] = coptbuf[0] = datebuf[0] = '\0';
X	uname(&ubuf);
X	strcpy(FULLSYSNAME, ubuf.nodename);
X
X	savmask = umask(N_UMASK);	/* set up mask */
X	uid = getuid();
X	gid = getgid();
X	duid = 0;
X	dgid = 0;
X
X#ifndef V6
X#ifndef SHELL
X	if ((SHELL = getenv("SHELL")) == NULL)
X		SHELL = "/bin/sh";
X#endif
X#ifndef IHCC
X	/*
X	 * IHCC does not allow use of $LOGNAME to prevent forgery.
X	 * Note that this shouldn't matter in readnews, since inews
X	 * does all the actual posting of news.
X	 */
X	if ((user = getenv("USER")) == NULL)
X		user = getenv("LOGNAME");
X	if ((home = getenv("HOME")) == NULL)
X		home = getenv("LOGDIR");
X#endif
X	if (user == NULL || home == NULL)
X		getuser();
X	else {
X		username = AllocCpy(user);
X		strcpy(header.path, username);
X		userhome = AllocCpy(home);
X	}
X
X	getuser();
X	if (!(MAILER = getenv("MAILER")))
X		MAILER = "mail";	/* was /bin/mail */
X
X#ifdef PAGE
X	if (myrc = getenv("PAGER"))
X		strcpy(PAGER, myrc);
X	else
X# ifdef IHCC
X		sprintf(PAGER,"%s/bin/%s",logdir(HOME),PAGE);
X# else
X		strcpy(PAGER, PAGE);
X# endif
X#else
X	strcpy(PAGER, "");
X#endif
X
X	if (ptr = getenv("NEWSOPTS"))
X		strcpy(rcbuf, ptr);
X	else
X		*rcbuf = '\0';
X	if (*rcbuf) {
X		strcat(rcbuf, " \1");
X		ptr = rcbuf;
X		while (*++ptr)
X			if (isspace(*ptr))
X				*ptr = '\0';
X		for (ptr = rcbuf; ; ptr++) {
X			if (!*ptr)
X				continue;
X			if (*ptr == '\1')
X				break;
X			if (++line > LINES)
X				xerror("Too many options.");
X			if ((rcline[line] = malloc((unsigned)(strlen(ptr) + 1))) == NULL)
X				xerror("Not enough memory.");
X			argvrc[line] = rcline[line];
X			strcpy(rcline[line], ptr);
X			while (*ptr)
X				ptr++;
X		}
X	}
X#else
X	getuser();
X#endif
X	myrc = getenv("NEWSRC");
X	if (myrc == NULL) {
X		myrc = NEWSRC;
X		sprintf(newsrc, "%s/%s", userhome, myrc);
X	} else {
X		strcpy(newsrc, myrc);
X	}
X	if (access(newsrc, 0))
X		newrc(newsrc);
X	if ((rcfp = fopen(newsrc, "r")) != NULL) {
X		rcreadok = FALSE;
X		while (fgets(rcbuf, LBUFLEN, rcfp) != NULL) {
X			if (!(space = isspace(*rcbuf)))
X				optflag = FALSE;
X			if (!strncmp(rcbuf, "options ", 8))
X				optflag = TRUE;
X			if (optflag) {
X				strcat(rcbuf, "\1");
X				if (space)
X					ptr = rcbuf - 1;
X				else
X					ptr = &rcbuf[7];
X				while (*++ptr)
X					if (isspace(*ptr))
X						*ptr = '\0';
X				if (space)
X					ptr = rcbuf;
X				else
X					ptr = &rcbuf[8];
X				for (; ; ptr++) {
X					if (!*ptr)
X						continue;
X					if (*ptr == '\1')
X						break;
X					if (++line > LINES)
X						xerror("Too many options.");
X					if ((rcline[line] = malloc((unsigned)(strlen(ptr) + 1))) == NULL)
X						xerror("Not enough memory.");
X					argvrc[line] = rcline[line];
X					strcpy(rcline[line], ptr);
X					while (*ptr)
X						ptr++;
X				}
X			}
X		}
X		fclose(rcfp);
X		rcreadok = TRUE;
X	}
X	if (line != -1) {
X#ifdef DEBUG
X		for (i = 0; i <= line; i++)
X			fprintf(stderr, "options:  %s\n", rcline[i]);
X#endif
X		process(line + 2, argvrc);
X		do {
X#ifdef DEBUG
X			fprintf(stderr, "Freeing %d\n", line);
X#endif
X			free(rcline[line]);
X		} while (line--);
X	}
X
X	argv++;
X	strcat(header.nbuf, ADMSUB);
X	ngcat(header.nbuf);
X	process(argc, argv);
X	if (!nflag) {
X		strcpy(header.nbuf, DFLTSUB);
X		ngcat(header.nbuf);
X		strcat(header.nbuf, ADMSUB);
X		ngcat(header.nbuf);
X	}
X	if (*header.nbuf)
X		lcase(header.nbuf);
X	makehimask(header.nbuf, "junk");
X	makehimask(header.nbuf, "control");
X	makehimask(header.nbuf, "test");
X
X	setbuf(stdout, SYSBUF);
X	sigtrap = FALSE;	/* true if a signal has been caught */
X	if (!pflag && !lflag && !eflag) {
X		signal(SIGQUIT, SIG_IGN);
X		signal(SIGHUP, onsig);
X		signal(SIGINT, onsig);
X		signal(SIGPIPE, onsig);
X	}
X
X	/*
X	 * ALL of the command line has now been processed. (!)
X	 */
X
X	if (!*header.nbuf)
X		ngcat(strcpy(header.nbuf, DFLTSUB));
X	if (sflag) {
X		ngdel(header.nbuf);
X		printf("Subscription list:  %s\n", header.nbuf);
X		xxit(0);
X	}
X	if (xflag)
X		line = -1;
X	rcfp = xfopen(newsrc, "r");
X	while (fgets(rcbuf, LBUFLEN, rcfp) != NULL) {
X		if (!nstrip(rcbuf))
X			xerror(".newsrc line too long");
X		if (++line >= LINES)
X			xerror("Too many .newsrc lines");
X		if ((rcline[line] = malloc((unsigned)(strlen(rcbuf) + 1))) == NULL)
X			xerror("Not enough memory");
X		strcpy(rcline[line], rcbuf);
X	}
X	fclose(rcfp);
X
X	if (sigtrap) {
X		if (sigtrap == SIGHUP || !rcreadok)
X			xxit(0);
X		fprintf(stdout, "Abort (n)?  ");
X		fflush(stdout);
X		if (gets(bfr) == NULL || *bfr == 'y' || *bfr == 'Y')
X			xxit(0);
X		sigtrap = FALSE;
X	}
X#ifdef SORTACTIVE
X	sortactive();
X#endif
X	actfp = xfopen(ACTIVE, "r");
X
X#ifdef DEBUG
X	fprintf(stderr, "header.nbuf = %s\n", header.nbuf);
X#endif
X	switch (mode) {
X	case UNKNOWN:
X		readr();
X		break;
X#ifdef TMAIL
X	case MAIL:
X		Mail();
X		break;
X#endif
X	}
X	fflush(stdout);
X	if (xflag || lflag || tflag)
X		xxit(0);
X	if (*groupdir && mode != MAIL)
X		updaterc();
X	writeoutrc();
X	xxit(0);
X
X	/* Camel, R.O.H. */
X}
X
X/*
X * Write out the .newsrc file.  We sort it into "active" file order,
X * for speed in future invocations, and to get rid of junk.
X */
Xwriteoutrc()
X{
X	FILE *wrcfp, *afp;
X	char aline[BUFLEN];
X	register int i, c;
X	register char *p;
X
X	if (!rcreadok)
X		return;
X#ifdef VMS
X	unlink(newsrc);
X#endif
X
X	wrcfp = xfopen(newsrc, "w");
X	afp = xfopen(ACTIVE, "r");
X
X	/* Write out options line, continuations, and comments. */
X	for (i=0;rcline[i];i++) {
X		c = rcline[i][0];
X		if (c != 'o' && c != '#' && c != ' ' && c != '\t')
X			break;
X		if (c == 'o' && strncmp(rcline[i], "options", 7) != 0)
X			break;
X		fprintf(wrcfp, "%s\n", rcline[i]);
X	}
X
X	/* For each newsgroup in active, find that newsrc line and write it out. */
X	while (fgets(aline, sizeof aline, afp)) {
X		p = index(aline, ' ');
X		if (p)
X			*p = 0;
X		i = findrcline(aline);
X		if (i >= 0)
X			fprintf(wrcfp, "%s\n", rcline[i]);
X	}
X	fclose(wrcfp);
X	fclose(afp);
X}
X
X/*
X * Forbid newsgroup ng, unless he asked for it in nbuf.
X */
Xmakehimask(nbuf, ng)
Xchar *nbuf, *ng;
X{
X	if (!findex(nbuf, ng)) {
X		ngcat(nbuf);
X		strcat(nbuf, "!");
X		strcat(nbuf, ng);
X		ngcat(nbuf);
X	}
X}
X
X/*
X * Return true if the string searchfor is in string, but not if preceeded by !.
X */
Xfindex(string, searchfor)
Xchar *string, *searchfor;
X{
X	register char first;
X	register char *p;
X
X	first = *searchfor;
X	for (p=index(string, first); p; p = index(p+1, first)) {
X		if (((p==string) || (p[-1]!='!')) && strncmp(p, searchfor, strlen(searchfor)) == 0)
X			return TRUE;
X	}
X	return FALSE;
X}
*-*-END-of-src/readnews.c-*-*
echo x - src/readr.c
sed 's/^X//' >src/readr.c <<'*-*-END-of-src/readr.c-*-*'
X/*
X * readr - /bin/mail and msgs interface and associated functions.
X */
X
X#ifndef lint
Xstatic char	*SccsId = "@(#)readr.c	2.45	9/3/84";
X#endif !lint
X
X#include "rparams.h"
X#if defined(BSD4_2) || defined(BSD4_1C)
X#include <sys/dir.h>
X#else
X#include "ndir.h"
X#endif !BSD4_2 && !BSD4_1C
X#include <setjmp.h>
X#include <errno.h>
X
Xextern int errno;
X
Xchar *Progname = "readnews";	/* used by xerror to identify failing program */
X
Xstatic char	lbuf[BUFLEN*2];
Xlong atol();
X
X#define	saveart	oobit = bit;strcpy(ofilename1, filename);strcpy(ogroupdir, groupdir);hptr = h;h = hold;hold = hptr;ongsize = pngsize
X#define NLINES(h, fp) (h->numlines[0] ? h->intnumlines : (h->intnumlines=linecnt(fp),sprintf(h->numlines, "%d", h->intnumlines), h->intnumlines))
X
Xchar *tft = "/tmp/folXXXXXX";
X
X/*
X * These were made static for u370 with its buggy cc.
X * I judged it better to have one copy with no ifdefs than
X * to conditionally compile them as automatic variables
X * in readr (which they originally were).  Performance
X * considerations might warrent moving some of the simple
X * things into register variables, but I don't know what
X * breaks the u370 cc.
X */
Xstatic char goodone[BUFLEN];		/* last decent article		*/
Xstatic char ogroupdir[BUFLEN];		/* last groupdir		*/
Xstatic char address[PATHLEN];		/* for reply copy		*/
Xstatic char edcmdbuf[128];
Xstatic int rfq = 0;			/* for last article		*/
Xstatic long ongsize;			/* Previous ngsize		*/
Xstatic long pngsize;			/* Printing ngsize		*/
Xstatic char *bptr;			/* temp pointer.		*/
Xstatic struct srec srec;		/* srec for sys file entries	*/
Xstatic char *tfilename;			/* temporary file name 		*/
Xstatic char ofilename1[BUFLEN];		/* previous file name		*/
Xstatic struct hbuf hbuf1, hbuf2,	/* for minusing			*/
X		*h = &hbuf1,		/* current header		*/
X		*hold = &hbuf2,		/* previous header		*/
X		*hptr;			/* temporary 			*/
Xstatic char *ptr1, *ptr2, *ptr3;	/* for reply manipulation	*/
Xstatic int  news = 0;
Xstatic int  abs = FALSE;		/* TRUE if we asked absolutely	*/
Xstatic char tf[100];
Xstatic long oobit;			/* last bit, really		*/
Xstatic int dgest = 0;
Xstatic FILE *ofp;			/* Current output file to terminal*/
Xstatic FILE *fp;			/* current article to be printed*/
Xstatic int holdup;			/* 1 iff should stop before hdr */
Xstatic int ignorenews;			/* 1 iff readnews -p > /dev/null*/
Xstatic time_t timelastsaved;		/* time newsrc last written out */
Xstatic jmp_buf sigjmpbuf;		/* for signal processing */
Xstatic int canlongjmp;			/* TRUE if setjmp on sigjmp valid */
X
Xint catchcont();
X
Xreadr()
X{
X
X#ifdef DEBUG
X	fprintf(stderr, "readr()\n");
X#endif
X	if (aflag) {
X		if (*datebuf) {
X			if ((atime = cgtdate(datebuf)) == -1)
X				xerror("Cannot parse date string");
X		} else
X			atime = 0;
X	}
X
X	if (pflag && ignoring())
X		ignorenews = TRUE;
X
X	if (xflag)
X		uflag = 0;
X	if (uflag)
X		(void) time(&timelastsaved);
X
X	ofp = stdout;
X	if (cflag && coptbuf[0] != '\0') {
X		umask(022);
X		mktemp(outfile);	/* get "unique" file name */
X		close(creat(outfile,0666));
X		ofp = xfopen(outfile, "w");
X		umask(N_UMASK);
X		cflag = FALSE;
X		pflag = TRUE;
X	}
X
X	/* loop reading articles. */
X	fp = NULL;
X	obit = -1;
X	nextng();
X	for ( ;; ) {
X		if (getnextart(FALSE))
X			break;
X#ifdef DEBUG
X		fprintf(stderr,"after getnextart, fp %x, pos %ld, bit %ld, group '%s', filename '%s'\n",
X			fp, ftell(fp), bit, groupdir, filename);
X#endif
X		strcpy(goodone, filename);
X		if (pflag || lflag || eflag) {
X			/* This code should be gotten rid of */
X			if (sigtrap) {
X				qfflush(ofp);
X				fprintf(ofp, "\n");
X				cdump(ofp);
X				xxit(0); /* kludge! drop when qfflush works */
X				return;
X			}
X			clear(bit);
X			nextbit();
X			FCLOSE(fp);
X			continue;
X		}
X		for ( ;; ) {
X			char *pp;
X			int nlines;
X			int (*ointr)();
X#ifdef	SIGCONT
X			int (*ocont)();
X#endif
X			setjmp(sigjmpbuf);
X			canlongjmp = TRUE;
X
X			sigtrap = FALSE;
X			if (!cflag) {
X				if (rfq)
X					sprintf(bfr, "Last article.  [qfr] ");
X				else {
X					nlines = NLINES(h, fp);
X					if (nlines <= 0) {
X						sprintf(bfr, "(0 lines) Next? [nqfr] ");
X						FCLOSE(fp);
X					} else {
X						sprintf(bfr, "(%d lines) More? [ynq] ", nlines);
X					}
X				}
X			} else
X				sprintf(bfr, "? ");
X			fprintf(ofp, "%s", bfr);
X			fflush(ofp);
X			bptr = lbuf;
X			ointr = signal(SIGINT, catchcont);
X#ifdef SIGCONT
X			ocont = signal(SIGCONT, catchcont);
X#endif
X			pp = fgets(bptr, BUFLEN, stdin);
X			canlongjmp = FALSE;
X			signal(SIGINT, ointr);
X#ifdef SIGCONT
X			signal(SIGCONT, ocont);
X#endif
X			if (pp != NULL)
X				break;
X			if (!sigtrap)
X				return;
X#ifdef SIGCONT
X			if (sigtrap != SIGCONT)
X#endif
X				fprintf(ofp, "\n");
X		}
X		nstrip(bptr);
X		while (*bptr == ' ' || *bptr == '\t')
X			bptr++;
X		if (command())
X			break;
X	}
X
X	if (!news)
X		fprintf(stderr, "No news.\n");
X	cout(ofp);
X}
X
X
X#define EOL() if (*bptr != '\0') { fprintf(ofp, "? for commands.\n"); return FALSE; }
X/*
X * Process one command, which has already been typed in.
X */
Xcommand()
X{
X	char *findhist();
X	long i;
X
X	switch (*bptr++) {
X
X	/* No.  Go on to next article. */
X	case 'n':
X		EOL();
X		readmode = NEXT;
X		if (!cflag)
X			FCLOSE(fp);
X		fprintf(ofp, "\n");
X		clear(bit);
X		saveart;
X		nextbit();
X		break;
X
X	/* Undigestify the article. */
X	case 'd':
X		dgest = 1;
X		/* fall through */
X
X	/* yes: print this article, go on. */
X	case 'y':
X		EOL();
X		/* fall through. */
X
X	/* The user hit return.  Default is 'y' unless rfq, then it's 'q'. */
X	case '\0':
X		if (!bptr[-1] && rfq)
X			return TRUE;
X		readmode = NEXT;
X		showtail(fp);
X		clear(bit);
X		saveart;
X		nextbit();
X		break;
X
X	/*
X	 * Unsubscribe to the newsgroup and go on to next group
X	 */
X	case 'u':
X		fprintf(ofp, "To unsubscribe, use 'U'\n");
X		break;
X
X	case 'U':
X		fprintf(ofp, "Unsubscribing to newsgroup: %s\n", groupdir);
X		obit = -1;
X		FCLOSE(fp);
X		if (cflag)
X			clear(bit);
X		else
X			putc('\n', ofp);
X		rfq = 0;
X		zapng = TRUE;
X		saveart;
X		if (nextng()) {
X			if (actdirect == BACKWARD)
X				fprintf(ofp, "Can't back up.\n");
X			else
X				return TRUE;
X		}
X		break;
X
X		/* Print the current version of news */
X	case 'v':
X		fprintf(ofp, "News version: %s\n", news_version);
X		break;
X
X		/* reprint the article */
X	case 'p':
X		EOL();
X		if (!cflag)
X			goto minus;
X		readmode = NEXT;
X		if (!cflag) {
X			FCLOSE(fp);
X			bit = last;
X			putc('\n', ofp);
X		}
X		obit = -1;
X		break;
X
X		/* decrypt joke */
X	case 'D':
X		caesar_command();
X		readmode = NEXT;
X		clear(bit);
X		saveart;
X		nextbit();
X		break;
X
X		/* write out the article someplace */
X	case 's':
X	case 'w':
X		{
X		char *grn = groupdir;
X		tfilename = filename;
X		if (*bptr == '-') {
X			bptr++;
X			grn = ogroupdir;
X			if (*ofilename1)
X				tfilename = ofilename1;
X		}
X		if (*bptr != '\0' && *bptr != ' ') {
X			fprintf(ofp, "Bad file name.\n");
X			break;
X		}
X		while (*bptr == ' ')
X			bptr++;
X		if (*bptr != '|' && *bptr != '/') {
X			char	hetyped[BUFLEN];
X			char	*boxptr;
X			strcpy(hetyped, bptr);
X			if (boxptr = getenv("NEWSBOX"))
X				if (index(boxptr, '%'))
X					sprintf(bptr, boxptr, grn);
X				else
X					strcpy(bptr, boxptr);
X			else if (hetyped[0] == '~' && hetyped[1] == '/') {
X				strcpy(hetyped, bptr+2);
X				strcpy(bptr, userhome);
X			} else
X				strcpy(bptr, ".");
X			strcat(bptr, "/");
X			if (hetyped[0] != '\0')
X				strcat(bptr, hetyped);
X			else
X				strcat(bptr, "Articles");
X		}
X		fwait(fsubr(save, tfilename, bptr));
X		}
X		break;
X
X		/* back up  */
X	case '-':
Xminus:
X		rfq = 0;
X		abs = TRUE;
X		if (!*ofilename1) {
X			fprintf(ofp, "Can't back up.\n");
X			break;
X		}
X		if (cflag)
X			clear(bit);
X		else {
X			FCLOSE(fp);
X			putc('\n', ofp);
X		}
X		hptr = h;
X		h = hold;
X		hold = hptr;
X		strcpy(bfr, filename);
X		strcpy(filename, ofilename1);
X		strcpy(ofilename1, bfr);
X		obit = bit;
X		if (strcmp(groupdir, ogroupdir)) {
X			strcpy(bfr, groupdir);
X			selectng(ogroupdir, TRUE);
X			strcpy(groupdir, ogroupdir);
X			strcpy(ogroupdir, bfr);
X			ngrp = 1;
X			back();
X		}
X		bit = oobit;
X		oobit = obit;
X		obit = -1;
X		(void) getnextart(TRUE);
X		return FALSE;
X
X		/* skip forwards */
X	case '+':
Xcaseplus:
X		if (*bptr == '\0')
X			strcat(bptr, "1");
X		rfq = 0;
X		if (cflag)
X			clear(bit);
X		saveart;
X		last = bit;
X		for (i = 0; i < atol(bptr); i++) {
X			nextbit();
X			if ((bit > pngsize) || (rflag && bit < 1))
X				break;
X		}
X		if (!cflag) {
X			putc('\n', ofp);
X			FCLOSE(fp);
X		}
X		obit = -1;
X		break;
X
X	/* exit - time updated to that of most recently read article */
X	case 'q':
X		EOL();
X		return TRUE;
X
X	/* exit - no time update. */
X	case 'x':
X		EOL();
X		xxit(0);
X
X	/* cancel the article. */
X	case 'c':
X		(void) cancel_command();
X		break;
X
X	/* escape to shell */
X	case '!':
X		fwait(fsubr(ushell, bptr, (char *)NULL));
X		fprintf(ofp, "\n");
X		hdr();
X		break;
X
X	/* mail reply */
X	case 'r':
X		(void) reply_command();
X		break;
X
X	/* send to some system */
X	case 'X':
X		xmit_command();
X		break;
X	/* mark the rest of the articles in this group as read */
X	case 'K':
X		saveart;
X		while (bit <= pngsize && bit >= minartno) {
X			clear(bit);
X			nextbit();
X		}
X		FCLOSE(fp);
X		break;
X
X	/* next newsgroup */
X	case 'P':
X		*bptr = '-';
X	case 'N':
X		FCLOSE(fp);
X		if (next_ng_command())
X			return TRUE;
X		break;
X
X	case 'b':	/* back up 1 article */
X		i = bit - 1;
X		goto tryartnum;
X	case '0':	/* specific no. */
X	case '1':
X	case '2':
X	case '3':
X	case '4':
X	case '5':
X	case '6':
X	case '7':
X	case '8':
X	case '9':
X		sscanf(--bptr, "%ld", &i);
X		if (i == 0) {
X			fprintf(ofp, "Bad article no.\n");
X			break;
X		}
X		if (i > pngsize) {
X			fprintf(ofp, "Not that many articles.\n");
X			break;
X		}
Xtryartnum:
X		readmode = SPEC;
X		abs = TRUE;
X		bit = i;
X		obit = -1;
X		if (!cflag) {
X			putc('\n', ofp);
X			FCLOSE(fp);
X		}
X		rfq = 0;
X		break;
X
X	/* specific message ID. */
X	case '<':
X		ptr1 = findhist(--bptr);
X		if (ptr1 == NULL) {
X			fprintf(ofp, "No such article: %s.\n", bptr);
X			break;
X		}
X		ptr2 = index(ptr1, '\t');
X		ptr3 = index(++ptr2, '\t');
X		ptr2 = index(++ptr3, ' ');
X		if (ptr2)
X			*ptr2 = '\0';
X		ptr2 = index(ptr3, '/');
X		*ptr2++ = '\0';
X		abs = TRUE;
X		if (cflag)
X			clear(bit);
X		else {
X			FCLOSE(fp);
X			putc('\n', ofp);
X		}
X		saveart;
X		strcpy(ogroupdir, ptr3);
X		if (strcmp(groupdir, ogroupdir)) {
X			strcpy(bfr, groupdir);
X			selectng(ogroupdir, TRUE);
X			strcpy(groupdir, ogroupdir);
X			strcpy(ogroupdir, bfr);
X			back();
X		}
X		sscanf(ptr2, "%ld", &bit);
X		oobit = obit;
X		obit = -1;
X		(void) getnextart(TRUE);
X		rfq = 0;
X		break;
X
X	/* follow-up article */
X	case 'f':
X		if (*bptr == '-')
X			tfilename = ofilename1;
X		else
X			tfilename = filename;
X		sprintf(bfr,"%s/%s %s", BIN, "postnews", tfilename);
X		system(bfr);
X		break;
X
X	/* erase - pretend we haven't seen this article. */
X	case 'e':
X		if (rfq || *bptr == '-') {
X			if (strcmp(groupdir, ogroupdir)) {
X				i = bit;
X				strcpy(bfr, groupdir);
X				selectng(ogroupdir, FALSE);
X				set(oobit);
X				fprintf(ofp,"Holding article %ld newsgroup %s\n", oobit, ogroupdir),
X				strcpy(groupdir, ogroupdir);
X				selectng(bfr, FALSE);
X				bit = i;
X			} else {
X				fprintf(ofp,"Holding article %ld\n", oobit);
X				set(oobit);
X			}
X		} else {
X			fprintf(ofp,"Holding article %ld\n", bit);
X			set(bit);
X			goto caseplus;	/* skip this article for now */
X		}
X		break;
X
X	case 'H':
X	case 'h':
X		if (!hflag)
X			dash(8, ofp);
X		if (*bptr == '-') {
X			if (oobit > 0)
X				fprintf(ofp, "Article %ld:\n", oobit);
X			hprint(hold, ofp, 1 + (bptr[-1]=='H'));
X		} else {
X			fprintf(ofp, "Article %ld of %ld: %s\n",
X				rfq ? oobit : bit, pngsize, h->ident);
X			hprint(h, ofp, 1 + (bptr[-1]=='H'));
X		}
X		if (!hflag)
X			dash(8, ofp);
X		break;
X
X	case '#':
X		fprintf(ofp, "Article %ld of %ld: newsgroup %s\n",
X			rfq ? oobit : bit, pngsize, rfq ? ogroupdir : groupdir);
X		break;
X
X		/* error */
X	case '?':
X		help(ofp);
X		break;
X	default:
X		fprintf(ofp, "? for commands.\n");
X		break;
X	}
X
X	return FALSE;
X}
X
Xcancel_command()
X{
X	tfilename = filename;
X	hptr = h;
X	if (*bptr == '-') {
X		if (*ofilename1) {
X			tfilename = ofilename1;
X			hptr = hold;
X		}
X		bptr++;
X	}
X	EOL();
X	readmode = SPEC;
X	strcpy(rcbuf, hptr->path);
X	ptr1 = index(rcbuf, ' ');
X	if (ptr1)
X		*ptr1 = 0;
X	if (uid != ROOTID && strcmp(username, rcbuf)) {
X		fprintf(ofp, "Can't cancel what you didn't write.\n");
X		return FALSE;
X	}
X	if (!cancel(ofp, hptr, 0) && hptr == h) {
X		clear(bit);
X		saveart;
X		nextbit();
X		obit = -1;
X		if (!cflag)
X			putc('\n', ofp);
X		FCLOSE(fp);
X	}
X	return TRUE;
X}
X
Xreply_command()
X{
X	register char	*pathptr;
X	int edit = 1;
X	char *ed;
X	FILE *tfp;
X	char	curberk[BUFLEN];
X	char *replyname();
X	char subj[BUFLEN];
X	char folbuf[BUFLEN];
X	extern char MAILPARSER[];
X	struct stat statb;
X	long creatm;
X
X	hptr = h;
X	while (*bptr && index("d-", *bptr)) {
X		switch (*bptr) {
X		/* Followup the previous article. */
X		case '-':
X			hptr = hold;
X			break;
X
X		/* Don't edit the headers */
X		case 'd':
X			edit = 0;
X			break;
X		}
X		bptr++;
X	}
X	EOL();
X	if (edit && access(MAILPARSER, 1)) {
X#ifdef IHCC
X		fprintf(stderr, "Can't edit headers, 'recmail' missing.\n");
X#else
X		fprintf(stderr, "Can't edit headers without %s\n", MAILPARSER);
X#endif
X		edit = 0;
X	}
X
X	*rcbuf = '\0';
X	*curberk = '\0';
X	pathptr = replyname(hptr);;
X	for (ptr1 = address, ptr2 = pathptr; *ptr2; ptr1++, ptr2++) {
X		if (index("\"\\$", *ptr2))
X			*ptr1++ = '\\';
X		*ptr1 = *ptr2;
X	}
X	*ptr1 = '\0';
X
X	folbuf[0] = '\0';		/* References */
X	if (hptr->followid[0]) {
X		strcpy(folbuf, hptr->followid);
X		strcat(folbuf, ", ");
X	}
X	strcat(folbuf, hptr->ident);
X
X	strcpy(subj, hptr->title);	/* Subject */
X	while (isspace(*bptr))
X		bptr++;
X	if (*bptr != '\0')
X		strcpy(subj, bptr);
X	if (!prefix(subj, "Re:")){
X		strcpy(bfr, subj);
X		sprintf(subj, "Re: %s", bfr);
X	}
X	if (!edit) {
X		fprintf(ofp, "To: %s\n", pathptr);
X		ed = index(MAILER, '%');
X		if (ed && ed[1] == 's')
X			fprintf(ofp, "Subject: %s\n", subj);
X		fflush(ofp);
X	}
X
X	/* Put the user in the editor to create the body of the followup. */
X	if (edit) {
X		strcpy(tf, tft);
X		mktemp(tf);
X
X		ed = getenv("EDITOR");
X		if (ed == NULL)
X			ed = DFTEDITOR;
X
X		if ((tfp = fopen(tf, "w")) == NULL) {
X			perror(tf);
X			creatm = 0L;
X		} else {
X			fprintf(tfp, "To: %s\n", pathptr);
X			fprintf(tfp, "Subject: %s\n", subj);
X			fprintf(tfp, "References: %s\n\n", folbuf);
X			fstat(fileno(tfp), &statb);
X			creatm = statb.st_mtime;
X			fclose(tfp);
X		}
X
X		sprintf(edcmdbuf, "%s %s", ed, tf);
X		system(edcmdbuf);
X		strcpy(rcbuf, MAILPARSER);
X		strcat(rcbuf, " -t");
X		strcat(rcbuf, " < ");
X		strcat(rcbuf, tf);
X		if (access(tf, 4) || stat(tf, &statb)) {
X			fprintf(stderr, "Reply not sent: no input file.\n");
X			return FALSE;
X		}
X		if (statb.st_mtime == creatm) {
X			fprintf(stderr, "Reply not sent: cancelled.\n");
X			unlink(tf);
X			return FALSE;
X		}
X		fprintf(ofp,"Sending reply.\n");
X		fflush(stdout);
X		if (fork() == 0) {
X			system(rcbuf);
X			unlink(tf);
X			_exit(0);
X		}
X	} else {
X		sprintf(rcbuf, MAILER, hptr->title);
X		sprintf(bfr, "%s %s", rcbuf, address);
X		system(bfr);
X	}
X	hdr();
X	return TRUE;
X}
X
Xxmit_command()
X{
X	tfilename = filename;
X	if (*bptr == '-') {
X		if (*ofilename1)
X			tfilename = ofilename1;
X		bptr++;
X	}
X	if (*bptr != '\0' && *bptr != ' ') {
X		fprintf(ofp, "Bad system name.\n");
X		return;
X	}
X	while (*bptr == ' ')
X		bptr++;
X	if (*bptr == '\0') {
X		fprintf(ofp, "Missing system name.\n");
X		return;
X	}
X	if (s_find(&srec, bptr) == NULL) {
X		fprintf(ofp, "%s not in SYSFILE\n", bptr);
X		return;
X	}
X	transmit(&srec, tfilename);
X}
X
Xnext_ng_command()
X{
X	obit = -1;
X	if (!*bptr || *bptr == '-') {
X		if (cflag)
X			clear(bit);
X		else
X			putc('\n', ofp);
X		if (*bptr)
X			actdirect = BACKWARD;
X		rfq = 0;
X		saveart;
X		if (nextng()) {
X			if (actdirect == BACKWARD)
X				fprintf(ofp, "Can't back up.\n");
X			else
X				return TRUE;
X		}
X		return FALSE;
X	}
X	while (isspace(*bptr))
X		bptr++;
X	if (!validng(bptr)) {
X		fprintf(ofp, "No such group.\n");
X		return FALSE;
X	}
X	if (cflag)
X		clear(bit);
X	else
X		putc('\n', ofp);
X	readmode = SPEC;
X	rfq = 0;
X	saveart;
X	back();
X	selectng(bptr, TRUE);
X	return FALSE;
X}
X
Xcaesar_command()
X{
X	char	temp[BUFLEN];
X	FILE	*pfp, *popen();
X
X	fprintf(stderr, "Caesar decoding:\n");
X	sprintf(temp, "%s/%s", LIB, "caesar");
X	if (*bptr) {
X		strcat(temp, " ");
X		strcat(temp, bptr);
X	}
X	if (NLINES(h, fp) > LNCNT && *PAGER) {
X		strcat(temp, " | ");
X		strcat(temp, PAGER);
X	}
X	pfp = popen(temp, "w");
X	tprint(fp, pfp, FALSE);
X	FCLOSE(fp);
X	pclose(pfp);
X}
X
X/*
X * Show the user the tail, if any, of the message on file
X * descriptor fd, and close fd.  The digester is considered,
X * and the pager is used if appropriate.
X */
Xshowtail(fd)
XFILE *fd;
X{
X	if (fd == NULL)
X		return;
X
X	if (dgest) {
X		digest(fd, ofp, h);
X	} else if (!lflag && !pflag && !eflag) {
X		pprint(fd);
X	}
X	fclose(fd);
X}
X
X/*
X * Print out the rest of the article through the pager.
X */
Xpprint(fd)
XFILE *fd;
X{
X#ifdef PAGE
X	/* Filter the tail of long messages through PAGER. */
X	if (NLINES(h, fd) > LNCNT && *PAGER) {
X		if (!index(PAGER, FMETA)) {
X			FILE *pfp, *popen();
X
X			pfp = popen(PAGER, "w");
X			if (pfp == NULL)
X				pfp = ofp;
X			/*
X			 * What follows is an attempt to prevent the
X			 * next message from scrolling part of this
X			 * message off the top of the screen before
X			 * the poor luser can read it.
X			 */
X			tprint(fd, pfp, FALSE);
X			pclose(pfp);
X		}
X		else
X			pout(ofp);
X		holdup = TRUE;
X	}
X	else
X#endif
X		tprint(fd, ofp, FALSE);
X}
X
X/*
X * Find the next article we want to consider, if we're done with
X * the last one, and show the header.
X */
Xgetnextart(minus)
Xint minus;
X{
X 	int noaccess;
X 	register DIR *dirp;
X 	register struct direct *dir;
X 	long nextnum, tnum;
X
X 	noaccess = 0;
X
X	if (minus)
X		goto nextart2;	/* Kludge for "-" command. */
X
X	if (bit == obit)	/* Return if still on same article as last time */
X		return 0;
X
X	sigtrap = FALSE;
X
Xnextart:
X#ifdef DEBUG
X	fprintf(stderr,"nextart:\n");
X#endif DEBUG
X	dgest = 0;
X
X	if (bit < minartno && !rflag)
X		bit = minartno;
X
X	/* If done with this newsgroup, find the next one. */
X	while (ngsize <= 0 || ((long) bit > ngsize) || (rflag && bit < minartno)) {
X		if (nextng()) {
X			if (actdirect == BACKWARD) {
X				fprintf(ofp, "Can't back up.\n");
X				actdirect = FORWARD;
X				continue;
X			} else
X				if (rfq++ || pflag || cflag)
X					return 1;
X		}
X		if (rflag)
X			bit = ngsize + 1;
X		else
X			bit = minartno - 1;
X		if (uflag) {
X			time_t now;
X			(void) time(&now);
X			if (now - timelastsaved > 5*60 /* 5 minutes */) {
X				fprintf(stderr,"[Saving .newsrc]\n");
X				writeoutrc();
X				timelastsaved = now;
X			}
X		}
X	}
X
Xnextart2:
X#ifdef DEBUG
X	fprintf(stderr, "article: %s/%ld\n", groupdir, bit);
X#endif
X	if (rcreadok)
X		rcreadok = 2;	/* have seen >= 1 article */
X	sprintf(filename, "%s/%ld", dirname(groupdir), bit);
X	if (rfq && goodone[0])
X		strcpy(filename, goodone);
X	if (sigtrap) {
X		if (sigtrap == SIGHUP)
X			return 1;
X		if (!rcreadok)
X			xxit(0);
X		fprintf(ofp, "Abort (n)?  ");
X		fflush(ofp);
X		gets(bfr);
X		if (*bfr == 'y' || *bfr == 'Y')
X			xxit(0);
X		sigtrap = FALSE;
X	}
X#ifdef DEBUG
X	fprintf(stderr, "filename = '%s'\n", filename);
X#endif
X	/* Decide if we want to show this article. */
X 	if ((fp = fopen(filename, "r")) == NULL) {
X 		/* since there can be holes in legal article numbers, */
X 		/* we wait till we hit 5 consecutive bad articles */
X 		/* before we haul off and scan the directory */
X 		if (++noaccess < 5)
X 			goto badart;
X		noaccess = 0;
X 		dirp = opendir(dirname(groupdir));
X 		if (dirp == NULL) {
X			if (errno != EACCES)
X				fprintf(stderr,"Can't open %s", dirname(groupdir));
X 			goto badart;
X 		}
X 		nextnum = rflag ? minartno - 1 : ngsize + 1;
X 		while ((dir = readdir(dirp)) != NULL) {
X 			tnum = atol(dir->d_name);
X 			if (tnum <= 0)
X 				continue;
X 			if (rflag ? (tnum > nextnum && tnum < bit)
X 				  : (tnum < nextnum && tnum > bit))
X 				nextnum = tnum;
X 		}
X 		closedir(dirp);
X 		if (rflag ? (nextnum >= bit) : (nextnum <= bit))
X 			goto badart;
X#ifdef DEBUG
X		fprintf(stderr,"nextnum = %ld\n",nextnum);
X#endif DEBUG
X 		do {
X 			clear(bit);
X 			nextbit();
X 		} while (rflag ? (nextnum < bit) : (nextnum > bit));
X 		obit = -1;
X 		abs = FALSE;
X 		goto nextart;
X 	} else
X 		noaccess = 0;
X
X 	if (ignorenews || hread(h, fp, TRUE) == NULL
X		|| (!rfq && !aselect(h, abs))) {
X badart:
X#ifdef DEBUG
X		fprintf(stderr, "Bad article '%s'\n", filename);
X#endif
X		FCLOSE(fp);
X		clear(bit);
X		obit = -1;
X		nextbit();
X		abs = FALSE;
X		goto nextart;
X	}
X	abs = FALSE;
X	actdirect = FORWARD;
X	news = TRUE;
X	hdr();
X	if (pflag)
X		tprint(fp, ofp, FALSE);
X	else if (cflag && !lflag && !eflag) {
X		fflush(ofp);
X		pprint(fp);
X	}
X	if (cflag || lflag || eflag || pflag) {
X		sigtrap = FALSE;
X		FCLOSE(fp);
X	}
X	obit = bit;
X	return 0;
X}
X
X/*
X * Print out whatever the appropriate header is
X */
Xhdr()
X{
X	char *briefdate();
X
X	if (rfq)
X		return;
X
X	if (lflag || eflag) {
X		hprint(h, ofp, 0);
X		return;
X	}
X
X	/* Print out a header */
X	if (ngrp) {
X		pngsize = ngsize;
X		ngrp--;
X		nghprint(groupdir);
X	}
X	if (!hflag)
X		fprintf(ofp, "Article %ld of %ld, %s.\n",
X			bit, pngsize, briefdate(h->subdate));
X	hprint(h, ofp, pflag ? 1 : 0);
X}
X
Xnghprint(title)
Xchar *title;
X{
X	char *tstr = "Newsgroup ";
X	int l = strlen(title) + strlen(tstr);
X
X	fprintf(ofp, "\n");
X	if (!hflag) {
X		dash(l, ofp);
X		fprintf(ofp, "%s%s\n", tstr, title);
X		dash(l, ofp);
X	} else {
X		fprintf(ofp, "%s%s, ", tstr, title);
X		if (bit == pngsize)
X			fprintf(ofp, "%ld\n", pngsize);
X		else
X			fprintf(ofp, "%ld-%ld\n", bit, pngsize);
X	}
X	fprintf(ofp, "\n");
X}
X
X/*
X * Routine to catch a continue signal.
X */
Xcatchcont(sig)
Xint sig;
X{
X	signal(sig, catchcont);
X	sigtrap = sig;
X	fflush(ofp);
X#ifdef SIGCONT
X	if (fp && sig == SIGCONT)
X		hdr();
X	if (sig != SIGCONT)
X#endif SIGCONT
X		putc('\n', ofp);
X	if (canlongjmp)
X		longjmp(sigjmpbuf,1);
X}
*-*-END-of-src/readr.c-*-*
echo x - src/recmail.c
sed 's/^X//' >src/recmail.c <<'*-*-END-of-src/recmail.c-*-*'
X/*
X * recmail: read a mail message on stdin, grab all addresses in To and Cc
X * lines, and pass the full message to all addressees.  This is useful to
X * send the output of a recently edited mail message (with headers edited too).
X * It is similar to sendmail -t, but only assumes /bin/mail.
X * To use your own mailer, e. g. nmail, compile with -DMAILER=my_mailer.
X */
X
X#ifndef lint
Xstatic char	*SccsId = "@(#)recmail.c	1.8	8/14/84";
X#endif !lint
X
X#include <stdio.h>
X#include <ctype.h>
X#include <pwd.h>
X#ifdef USG
Xstruct passwd *getpwent(), *getpwuid(), *getpwnam();
X#endif USG
X
X#ifndef MAILER
X#define MAILER "/bin/mail"
X#endif
Xchar mailer[] = MAILER;
X
X#define MAXRECIPS 100
Xchar *recips[MAXRECIPS];
Xint nrecips = 0;
X
Xmain()
X{
X	FILE *fd;
X	char *tmpf;
X	FILE *errfd;
X	char *errf;
X	char linebuf[1024];
X	int i, pid, wpid;
X	int exstat;
X	char *mypath;
X	int goodcnt, badcnt;
X	char *mktemp(), *getenv();
X
X	tmpf = mktemp("/tmp/rmXXXXXX");
X	close(creat(tmpf,0666));
X	fd = fopen(tmpf, "w");
X	errf = mktemp("/tmp/rmXXXXXX");
X	close(creat(errf,0666));
X	errfd = fopen(errf, "w");
X	fprintf(errfd, "Subject: Returned mail\n");
X	fprintf(errfd, "\n  ----- Transcript of session follows -----\n");
X	fflush(errfd);
X	goodcnt = badcnt = 0;
X
X	while (fgets(linebuf, sizeof linebuf, stdin) != NULL) {
X		if (fputs(linebuf, fd) == EOF)
X			goto werror;
X		if (linebuf[0] == '\n')
X			break;
X		if (strncmp(linebuf, "To: ", 4) == 0 ||
X		    strncmp(linebuf, "to: ", 4) == 0 ||
X		    strncmp(linebuf, "TO: ", 4) == 0 ||
X		    strncmp(linebuf, "Cc: ", 4) == 0 ||
X		    strncmp(linebuf, "cc: ", 4) == 0 ||
X		    strncmp(linebuf, "CC: ", 4) == 0)
X			addrecips(linebuf+4);
X	}
X	if (!feof(stdin)) {
X		while (fgets(linebuf, sizeof linebuf, stdin) != NULL) {
X			if (fputs(linebuf, fd) == EOF) {
Xwerror:
X				printf("write error on temp file\n");
X				exit(2);
X			}
X		}
X	}
X	fclose(fd);
X
X	/*
X	 * Force the path to only consider /bin and /usr/bin, since
X	 * that's the version of mail we want (not /usr/ucb/mail)
X	 * This code will probably cause a core dump some day.
X	 */
X	mypath = getenv("PATH");
X	if (mypath)
X		strcpy(mypath, "/bin:/usr/bin");
X
X	/*
X	 * We send the copies out separately, because of a bug in
X	 * USG's /bin/mail which will generate ANOTHER To: line,
X	 * even though we already have one, if there are at least
X	 * two recipients.
X	 */
X	for (i=0; i<nrecips; i++) {
X		/*
X		 * mail recips[i] < tmpf
X		 */
X		pid = mailto(tmpf, errfd, recips[i]);
X		exstat = -1;
X		while ((wpid = wait(&exstat)) >= 0 && wpid != pid)
X			;
X		if (exstat == 0)
X			goodcnt++;
X		else
X			badcnt++;
X	}
X	if (badcnt) {
X		mailback(errfd, tmpf, errf);
X		unlink(tmpf);
X		unlink(errf);
X		exit(1);
X	} else if (goodcnt == 0) {
X		fprintf(errfd, "recmail: no 'To:' line\n");
X		mailback(errfd, tmpf, errf);
X		unlink(tmpf);
X		unlink(errf);
X		exit (1);
X	}
X	unlink(tmpf);
X	unlink(errf);
X	exit (0);
X}
X
X#define isok(c) (isprint(c) && (c) != ' ' && c != ',')
Xaddrecips(line)
Xchar *line;
X{
X	char *front, *back, *tail;
X	char *malloc();
X
X	tail = line + strlen(line);
X	for (front=line; front < tail; ) {
X		while (!isok(*front) && front < tail)
X			front++;
X		for (back=front; isok(*back); back++)
X			;
X		*back=0;
X		if (nrecips >= MAXRECIPS) {
X			printf("Too many destinations\n");
X			exit(2);
X		}
X		if ((recips[nrecips] = malloc(strlen(front) + 1)) == NULL) {
X			printf("Out of space\n");
X			exit(2);
X		}
X		strcpy(recips[nrecips], front);
X		nrecips++;
X		front = back+1;
X	}
X}
X
Xint
Xmailto(tmpf, errfd, recip)
Xchar *tmpf;
XFILE *errfd;
Xchar *recip;
X{
X	register int pid;
X
X	/*
X	 * mail recips < tmpf
X	 */
X	while ((pid = fork()) == -1) {
X		fprintf(stderr, "fork failed, waiting...\r\n");
X		sleep(60);
X	}
X	if (pid == 0) {
X		close(0);
X		open(tmpf, 0);
X		if (errfd != NULL) {
X			close(1);
X			dup(fileno(errfd));
X			fclose(errfd);
X			close(2);
X			dup(1);
X		}
X		execlp(mailer, mailer, recip, (char *)0);
X		perror(mailer);
X		exit(1);
X	}
X	return(pid);
X}
X
Xmailback(errfd, tmpf, errf)
Xregister FILE *errfd;
Xchar *tmpf;
Xchar *errf;
X{
X	register FILE *fd;
X	register int c;
X	int exstat;
X	register int pid, wpid;
X	char *logn;
X	char *getlogin(), *getenv();
X	register struct passwd *pwd;
X
X	if ((fd = fopen(tmpf, "r")) != NULL) {
X		fprintf(errfd, "\n   ----- Unsent message follows -----\n");
X		while ((c = getc(fd)) != EOF)
X			putc(c, errfd);
X		fclose(fd);
X	}
X	fclose(errfd);
X	if ((logn = getlogin()) == NULL && (logn = getenv("USER")) == NULL) {
X		if ((pwd = getpwent(getuid())) == NULL)
X			return;
X		logn = pwd->pw_name;
X	}
X	pid = mailto(errf, (FILE *)NULL, logn);
X	while ((wpid = wait(&exstat)) >= 0 && wpid != pid)
X		;
X}
*-*-END-of-src/recmail.c-*-*
echo x - src/recnews.c
sed 's/^X//' >src/recnews.c <<'*-*-END-of-src/recnews.c-*-*'
X/*
X * recnews [to newsgroup] [from user]
X *
X * Process a news article which has been mailed to some group like msgs.
X * Such articles are in normal mail format and have never seen the insides
X * of netnews.  If the "to newsgroup" is included, the article is posted
X * to this newsgroup instead of trying to intuit it from the headers.
X * If the "from user" is included, the return address is forged to look
X * like that user instead of what getuid or a from line says.
X *
X * It is recommended that you always include the to newsgroup, since the
X * intution code is flakey and out of date.  The from user is probably
X * appropriate for arpanet mailing lists being funnelled at ucbvax but
X * not otherwise.  Sample lines in /usr/lib/aliases (if you run delivermail):
X *	worldnews: "|/usr/lib/news/recnews net.general"
X *		Allows you to mail to worldnews rather than using inews.
X *		Intended for humans to mail to.
X *	post-unix-wizards: "|/usr/lib/news/recnews fa.unix-wizards unix-wizards"
X *		Causes mail to post-unix-wizards to be fed into fa.unix-wizards
X *		and the return address forged as unix-wizards on the local
X *		machine.  post-unix-wizards (on the local machine) should
X *		be part of the master mailing list somewhere (on a different
X *		machine.)
X *
X * Recnews is primarily useful in remote places on the usenet which collect
X * mail from mailing lists and funnel them into the network.  It is also
X * useful if you like to send mail to some user instead of invoking
X * inews -t .. -n .. when you want to submit an article.  (Many mailers give
X * you nice facilities like editing the message.)  It is not, however,
X * essential to use recnews to be able to join usenet.
X *
X * WARNING: recnews disables the "recording" check - it has to because
X * by the time inews is run, it's in the background and too late to
X * ask permission.  If you depend heavily on recordings you probably
X * should not allow recnews (and thus the mail interface) to be used.
X */
X
X#ifndef lint
Xstatic char	*SccsId = "@(#)recnews.c	2.9	9/3/84";
X#endif !lint
X
X#include "defs.h"
X
X#include <stdio.h>
X#include <ctype.h>
X
X/*
X * Note: we assume there are 2 kinds of hosts using recnews:
X * Those that have delivermail (and hence this program will never
X * have to deal with more than one message at a time) and those on the arpanet
X * that do not (and hence all messages end with a sentenel).  It is
X * supposed that regular v7 type systems without delivermail or some
X * other automatic forwarding device will just use rnews.  We do
X * not attempt to tell where a message ends on all systems due to the
X * diff

rick@seismo.UUCP (Rick Adams) (09/08/84)

cat <<'E_O_F'
	This was received truncated at a lot of places
E_O_F
if test ! -d src
then
	mkdir src
	echo mkdir src
fi
echo x - src/process.c
sed 's/^X//' >src/process.c <<'*-*-END-of-src/process.c-*-*'
X/*
X * process - process options for readnews
X */
X
Xstatic char *SccsId = "@(#)process.c	2.9	4/20/84";
X
X#include "rparams.h"
X
X#define OPTION	0	/* pick up an option string */
X#define STRING	1	/* pick up a string of arguments */
X
Xstruct optable *optpt, options[] = { /*
Xoptlet	filchar	flag	newstate oldmode	newmode	buf	*/
X'p',	'\0',	FALSE,	OPTION,	UNKNOWN,	UNKNOWN,(char *)NULL,
X't',	'\0',	FALSE,	STRING,	ANY,		UNKNOWN,header.title,
X'a',	' ',	FALSE,	STRING,	ANY,		UNKNOWN,datebuf,
X'n',   NGDELIM,	FALSE,	STRING,	ANY,		UNKNOWN,header.nbuf,
X'c',	' ',	FALSE,	STRING,	UNKNOWN,	UNKNOWN,coptbuf,
X'l',	' ',	FALSE,	OPTION,	UNKNOWN,	UNKNOWN,(char *)NULL,
X'r',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,(char *)NULL,
X's',   NGDELIM,	FALSE,	STRING,	ANY,		UNKNOWN,header.nbuf,
X'x',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,(char *)NULL,
X'h',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,(char *)NULL,
X'M',	'\0',	FALSE,	OPTION,	UNKNOWN,	MAIL,	(char *)NULL,
X'f',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,(char *)NULL,
X'u',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,(char *)NULL,
X'e',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,(char *)NULL,
X'\0',	'\0',	0,	0,	0,		0,	(char *)NULL
X};
X
Xprocess(argc,argv)
Xregister int argc;
Xregister char **argv;
X{
X	register int state = OPTION;
X	register char *ptr;
X	char filchar;
X	int len, tlen;
X
X	/* loop once per arg. */
X
X	while (--argc) {
X	    if (state == OPTION) {
X		if (**argv != '-') {
X			sprintf(bfr, "Bad option string \"%s\"", *argv);
X			xerror(bfr);
X		}
X		while (*++*argv != '\0') {
X			for (optpt = options; optpt->optlet != '\0'; ++optpt) {
X				if (optpt->optlet == **argv)
X					goto found;
X			}
X			/* unknown option letter */
X			fprintf(stderr, "Usage: readnews [ -a [ date ]] [ -n newsgroups ] [ -t titles ] [ -lprxhfuM ]\n");
X			fprintf(stderr, "\t[ -c [ ``mailer'' ]]\n\n");
X			fprintf(stderr, "       readnews -s\n");
X			exit(1);
X
X		    found:;
X			if (mode != UNKNOWN && (mode&optpt->oldmode) == 0) {
X				sprintf(bfr, "Bad %c option", **argv);
X				xerror(bfr);
X			}
X			if (mode == UNKNOWN)
X				mode = optpt->newmode;
X			filchar = optpt->filchar;
X			optpt->flag = TRUE;
X			state = optpt->newstate;
X			ptr = optpt->buf;
X			len = LBUFLEN;
X		}
X
X		argv++;		/* done with this option arg. */
X
X	    } else {
X
X		/*
X		 * Pick up a piece of a string and put it into
X		 * the appropriate buffer.
X		 */
X		if (**argv == '-') {
X			state = OPTION;
X			argc++;	/* uncount this arg. */
X			continue;
X		}
X
X		if ((tlen = strlen(*argv)) >= len)
X			xerror("Argument string too long");
X		strcpy(ptr, *argv++);
X		ptr += tlen;
X		if (*(ptr-1) != filchar)
X			*ptr++ = filchar;
X		len -= tlen + 1;
X		*ptr = '\0';
X	    }
X	}
X	return;
X}
*-*-END-of-src/process.c-*-*
echo x - src/readnews.c
sed 's/^X//' >src/readnews.c <<'*-*-END-of-src/readnews.c-*-*'
X/*
X * readnews - read news articles.
X */
X
X#ifndef lint
Xstatic char	*SccsId = "@(#)readnews.c	2.19	8/28/84";
X#endif !lint
X
X#include "rparams.h"
X
X/*
X * readnews - article reading program
X */
X
X#ifndef SYSBUF
Xchar	SYSBUF[BUFSIZ];	/* to buffer std out */
X#endif
X
X#define OPTION	0	/* pick up an option string */
X#define STRING	1	/* pick up a string of arguments */
X
Xint onsig();
X
X/*
X *	Authors:
X *		Matt Glickman	ucbvax!glickman
X *		Mark Horton	cbosg!mark
X *		Stephen Daniels	duke!swd
X *		Tom Truscott	duke!trt
X */
X
Xmain(argc, argv)
Xint	argc;
Xregister char	**argv;
X{
X	register char	*ptr;	/* pointer to rest of buffer		*/
X	char	*user, *home;
X	int	optflag = FALSE, space = FALSE;
X	struct utsname ubuf;
X	char	*myrc;
X
X	/* set up defaults and initialize. */
X	pathinit();
X	mode = UNKNOWN;
X	header.title[0] = header.nbuf[0] = '\0';
X	titlebuf[0] = coptbuf[0] = datebuf[0] = '\0';
X	uname(&ubuf);
X	strcpy(FULLSYSNAME, ubuf.nodename);
X
X	savmask = umask(N_UMASK);	/* set up mask */
X	uid = getuid();
X	gid = getgid();
X	duid = 0;
X	dgid = 0;
X
X#ifndef V6
X#ifndef SHELL
X	if ((SHELL = getenv("SHELL")) == NULL)
X		SHELL = "/bin/sh";
X#endif
X#ifndef IHCC
X	/*
X	 * IHCC does not allow use of $LOGNAME to prevent forgery.
X	 * Note that this shouldn't matter in readnews, since inews
X	 * does all the actual posting of news.
X	 */
X	if ((user = getenv("USER")) == NULL)
X		user = getenv("LOGNAME");
X	if ((home = getenv("HOME")) == NULL)
X		home = getenv("LOGDIR");
X#endif
X	if (user == NULL || home == NULL)
X		getuser();
X	else {
X		username = AllocCpy(user);
X		strcpy(header.path, username);
X		userhome = AllocCpy(home);
X	}
X
X	getuser();
X	if (!(MAILER = getenv("MAILER")))
X		MAILER = "mail";	/* was /bin/mail */
X
X#ifdef PAGE
X	if (myrc = getenv("PAGER"))
X		strcpy(PAGER, myrc);
X	else
X# ifdef IHCC
X		sprintf(PAGER,"%s/bin/%s",logdir(HOME),PAGE);
X# else
X		strcpy(PAGER, PAGE);
X# endif
X#else
X	strcpy(PAGER, "");
X#endif
X
X	if (ptr = getenv("NEWSOPTS"))
X		strcpy(rcbuf, ptr);
X	else
X		*rcbuf = '\0';
X	if (*rcbuf) {
X		strcat(rcbuf, " \1");
X		ptr = rcbuf;
X		while (*++ptr)
X			if (isspace(*ptr))
X				*ptr = '\0';
X		for (ptr = rcbuf; ; ptr++) {
X			if (!*ptr)
X				continue;
X			if (*ptr == '\1')
X				break;
X			if (++line > LINES)
X				xerror("Too many options.");
X			if ((rcline[line] = malloc((unsigned)(strlen(ptr) + 1))) == NULL)
X				xerror("Not enough memory.");
X			argvrc[line] = rcline[line];
X			strcpy(rcline[line], ptr);
X			while (*ptr)
X				ptr++;
X		}
X	}
X#else
X	getuser();
X#endif
X	myrc = getenv("NEWSRC");
X	if (myrc == NULL) {
X		myrc = NEWSRC;
X		sprintf(newsrc, "%s/%s", userhome, myrc);
X	} else {
X		strcpy(newsrc, myrc);
X	}
X	if (access(newsrc, 0))
X		newrc(newsrc);
X	if ((rcfp = fopen(newsrc, "r")) != NULL) {
X		rcreadok = FALSE;
X		while (fgets(rcbuf, LBUFLEN, rcfp) != NULL) {
X			if (!(space = isspace(*rcbuf)))
X				optflag = FALSE;
X			if (!strncmp(rcbuf, "options ", 8))
X				optflag = TRUE;
X			if (optflag) {
X				strcat(rcbuf, "\1");
X				if (space)
X					ptr = rcbuf - 1;
X				else
X					ptr = &rcbuf[7];
X				while (*++ptr)
X					if (isspace(*ptr))
X						*ptr = '\0';
X				if (space)
X					ptr = rcbuf;
X				else
X					ptr = &rcbuf[8];
X				for (; ; ptr++) {
X					if (!*ptr)
X						continue;
X					if (*ptr == '\1')
X						break;
X					if (++line > LINES)
X						xerror("Too many options.");
X					if ((rcline[line] = malloc((unsigned)(strlen(ptr) + 1))) == NULL)
X						xerror("Not enough memory.");
X					argvrc[line] = rcline[line];
X					strcpy(rcline[line], ptr);
X					while (*ptr)
X						ptr++;
X				}
X			}
X		}
X		fclose(rcfp);
X		rcreadok = TRUE;
X	}
X	if (line != -1) {
X#ifdef DEBUG
X		for (i = 0; i <= line; i++)
X			fprintf(stderr, "options:  %s\n", rcline[i]);
X#endif
X		process(line + 2, argvrc);
X		do {
X#ifdef DEBUG
X			fprintf(stderr, "Freeing %d\n", line);
X#endif
X			free(rcline[line]);
X		} while (line--);
X	}
X
X	argv++;
X	strcat(header.nbuf, ADMSUB);
X	ngcat(header.nbuf);
X	process(argc, argv);
X	if (!nflag) {
X		strcpy(header.nbuf, DFLTSUB);
X		ngcat(header.nbuf);
X		strcat(header.nbuf, ADMSUB);
X		ngcat(header.nbuf);
X	}
X	if (*header.nbuf)
X		lcase(header.nbuf);
X	makehimask(header.nbuf, "junk");
X	makehimask(header.nbuf, "control");
X	makehimask(header.nbuf, "test");
X
X	setbuf(stdout, SYSBUF);
X	sigtrap = FALSE;	/* true if a signal has been caught */
X	if (!pflag && !lflag && !eflag) {
X		signal(SIGQUIT, SIG_IGN);
X		signal(SIGHUP, onsig);
X		signal(SIGINT, onsig);
X		signal(SIGPIPE, onsig);
X	}
X
X	/*
X	 * ALL of the command line has now been processed. (!)
X	 */
X
X	if (!*header.nbuf)
X		ngcat(strcpy(header.nbuf, DFLTSUB));
X	if (sflag) {
X		ngdel(header.nbuf);
X		printf("Subscription list:  %s\n", header.nbuf);
X		xxit(0);
X	}
X	if (xflag)
X		line = -1;
X	rcfp = xfopen(newsrc, "r");
X	while (fgets(rcbuf, LBUFLEN, rcfp) != NULL) {
X		if (!nstrip(rcbuf))
X			xerror(".newsrc line too long");
X		if (++line >= LINES)
X			xerror("Too many .newsrc lines");
X		if ((rcline[line] = malloc((unsigned)(strlen(rcbuf) + 1))) == NULL)
X			xerror("Not enough memory");
X		strcpy(rcline[line], rcbuf);
X	}
X	fclose(rcfp);
X
X	if (sigtrap) {
X		if (sigtrap == SIGHUP || !rcreadok)
X			xxit(0);
X		fprintf(stdout, "Abort (n)?  ");
X		fflush(stdout);
X		if (gets(bfr) == NULL || *bfr == 'y' || *bfr == 'Y')
X			xxit(0);
X		sigtrap = FALSE;
X	}
X#ifdef SORTACTIVE
X	sortactive();
X#endif
X	actfp = xfopen(ACTIVE, "r");
X
X#ifdef DEBUG
X	fprintf(stderr, "header.nbuf = %s\n", header.nbuf);
X#endif
X	switch (mode) {
X	case UNKNOWN:
X		readr();
X		break;
X#ifdef TMAIL
X	case MAIL:
X		Mail();
X		break;
X#endif
X	}
X	fflush(stdout);
X	if (xflag || lflag || tflag)
X		xxit(0);
X	if (*groupdir && mode != MAIL)
X		updaterc();
X	writeoutrc();
X	xxit(0);
X
X	/* Camel, R.O.H. */
X}
X
X/*
X * Write out the .newsrc file.  We sort it into "active" file order,
X * for speed in future invocations, and to get rid of junk.
X */
Xwriteoutrc()
X{
X	FILE *wrcfp, *afp;
X	char aline[BUFLEN];
X	register int i, c;
X	register char *p;
X
X	if (!rcreadok)
X		return;
X#ifdef VMS
X	unlink(newsrc);
X#endif
X
X	wrcfp = xfopen(newsrc, "w");
X	afp = xfopen(ACTIVE, "r");
X
X	/* Write out options line, continuations, and comments. */
X	for (i=0;rcline[i];i++) {
X		c = rcline[i][0];
X		if (c != 'o' && c != '#' && c != ' ' && c != '\t')
X			break;
X		if (c == 'o' && strncmp(rcline[i], "options", 7) != 0)
X			break;
X		fprintf(wrcfp, "%s\n", rcline[i]);
X	}
X
X	/* For each newsgroup in active, find that newsrc line and write it out. */
X	while (fgets(aline, sizeof aline, afp)) {
X		p = index(aline, ' ');
X		if (p)
X			*p = 0;
X		i = findrcline(aline);
X		if (i >= 0)
X			fprintf(wrcfp, "%s\n", rcline[i]);
X	}
X	fclose(wrcfp);
X	fclose(afp);
X}
X
X/*
X * Forbid newsgroup ng, unless he asked for it in nbuf.
X */
Xmakehimask(nbuf, ng)
Xchar *nbuf, *ng;
X{
X	if (!findex(nbuf, ng)) {
X		ngcat(nbuf);
X		strcat(nbuf, "!");
X		strcat(nbuf, ng);
X		ngcat(nbuf);
X	}
X}
X
X/*
X * Return true if the string searchfor is in string, but not if preceeded by !.
X */
Xfindex(string, searchfor)
Xchar *string, *searchfor;
X{
X	register char first;
X	register char *p;
X
X	first = *searchfor;
X	for (p=index(string, first); p; p = index(p+1, first)) {
X		if (((p==string) || (p[-1]!='!')) && strncmp(p, searchfor, strlen(searchfor)) == 0)
X			return TRUE;
X	}
X	return FALSE;
X}
*-*-END-of-src/readnews.c-*-*
echo x - src/readr.c
sed 's/^X//' >src/readr.c <<'*-*-END-of-src/readr.c-*-*'
X/*
X * readr - /bin/mail and msgs interface and associated functions.
X */
X
X#ifndef lint
Xstatic char	*SccsId = "@(#)readr.c	2.45	9/3/84";
X#endif !lint
X
X#include "rparams.h"
X#if defined(BSD4_2) || defined(BSD4_1C)
X#include <sys/dir.h>
X#else
X#include "ndir.h"
X#endif !BSD4_2 && !BSD4_1C
X#include <setjmp.h>
X#include <errno.h>
X
Xextern int errno;
X
Xchar *Progname = "readnews";	/* used by xerror to identify failing program */
X
Xstatic char	lbuf[BUFLEN*2];
Xlong atol();
X
X#define	saveart	oobit = bit;strcpy(ofilename1, filename);strcpy(ogroupdir, groupdir);hptr = h;h = hold;hold = hptr;ongsize = pngsize
X#define NLINES(h, fp) (h->numlines[0] ? h->intnumlines : (h->intnumlines=linecnt(fp),sprintf(h->numlines, "%d", h->intnumlines), h->intnumlines))
X
Xchar *tft = "/tmp/folXXXXXX";
X
X/*
X * These were made static for u370 with its buggy cc.
X * I judged it better to have one copy with no ifdefs than
X * to conditionally compile them as automatic variables
X * in readr (which they originally were).  Performance
X * considerations might warrent moving some of the simple
X * things into register variables, but I don't know what
X * breaks the u370 cc.
X */
Xstatic char goodone[BUFLEN];		/* last decent article		*/
Xstatic char ogroupdir[BUFLEN];		/* last groupdir		*/
Xstatic char address[PATHLEN];		/* for reply copy		*/
Xstatic char edcmdbuf[128];
Xstatic int rfq = 0;			/* for last article		*/
Xstatic long ongsize;			/* Previous ngsize		*/
Xstatic long pngsize;			/* Printing ngsize		*/
Xstatic char *bptr;			/* temp pointer.		*/
Xstatic struct srec srec;		/* srec for sys file entries	*/
Xstatic char *tfilename;			/* temporary file name 		*/
Xstatic char ofilename1[BUFLEN];		/* previous file name		*/
Xstatic struct hbuf hbuf1, hbuf2,	/* for minusing			*/
X		*h = &hbuf1,		/* current header		*/
X		*hold = &hbuf2,		/* previous header		*/
X		*hptr;			/* temporary 			*/
Xstatic char *ptr1, *ptr2, *ptr3;	/* for reply manipulation	*/
Xstatic int  news = 0;
Xstatic int  abs = FALSE;		/* TRUE if we asked absolutely	*/
Xstatic char tf[100];
Xstatic long oobit;			/* last bit, really		*/
Xstatic int dgest = 0;
Xstatic FILE *ofp;			/* Current output file to terminal*/
Xstatic FILE *fp;			/* current article to be printed*/
Xstatic int holdup;			/* 1 iff should stop before hdr */
Xstatic int ignorenews;			/* 1 iff readnews -p > /dev/null*/
Xstatic time_t timelastsaved;		/* time newsrc last written out */
Xstatic jmp_buf sigjmpbuf;		/* for signal processing */
Xstatic int canlongjmp;			/* TRUE if setjmp on sigjmp valid */
X
Xint catchcont();
X
Xreadr()
X{
X
X#ifdef DEBUG
X	fprintf(stderr, "readr()\n");
X#endif
X	if (aflag) {
X		if (*datebuf) {
X			if ((atime = cgtdate(datebuf)) == -1)
X				xerror("Cannot parse date string");
X		} else
X			atime = 0;
X	}
X
X	if (pflag && ignoring())
X		ignorenews = TRUE;
X
X	if (xflag)
X		uflag = 0;
X	if (uflag)
X		(void) time(&timelastsaved);
X
X	ofp = stdout;
X	if (cflag && coptbuf[0] != '\0') {
X		umask(022);
X		mktemp(outfile);	/* get "unique" file name */
X		close(creat(outfile,0666));
X		ofp = xfopen(outfile, "w");
X		umask(N_UMASK);
X		cflag = FALSE;
X		pflag = TRUE;
X	}
X
X	/* loop reading articles. */
X	fp = NULL;
X	obit = -1;
X	nextng();
X	for ( ;; ) {
X		if (getnextart(FALSE))
X			break;
X#ifdef DEBUG
X		fprintf(stderr,"after getnextart, fp %x, pos %ld, bit %ld, group '%s', filename '%s'\n",
X			fp, ftell(fp), bit, groupdir, filename);
X#endif
X		strcpy(goodone, filename);
X		if (pflag || lflag || eflag) {
X			/* This code should be gotten rid of */
X			if (sigtrap) {
X				qfflush(ofp);
X				fprintf(ofp, "\n");
X				cdump(ofp);
X				xxit(0); /* kludge! drop when qfflush works */
X				return;
X			}
X			clear(bit);
X			nextbit();
X			FCLOSE(fp);
X			continue;
X		}
X		for ( ;; ) {
X			char *pp;
X			int nlines;
X			int (*ointr)();
X#ifdef	SIGCONT
X			int (*ocont)();
X#endif
X			setjmp(sigjmpbuf);
X			canlongjmp = TRUE;
X
X			sigtrap = FALSE;
X			if (!cflag) {
X				if (rfq)
X					sprintf(bfr, "Last article.  [qfr] ");
X				else {
X					nlines = NLINES(h, fp);
X					if (nlines <= 0) {
X						sprintf(bfr, "(0 lines) Next? [nqfr] ");
X						FCLOSE(fp);
X					} else {
X						sprintf(bfr, "(%d lines) More? [ynq] ", nlines);
X					}
X				}
X			} else
X				sprintf(bfr, "? ");
X			fprintf(ofp, "%s", bfr);
X			fflush(ofp);
X			bptr = lbuf;
X			ointr = signal(SIGINT, catchcont);
X#ifdef SIGCONT
X			ocont = signal(SIGCONT, catchcont);
X#endif
X			pp = fgets(bptr, BUFLEN, stdin);
X			canlongjmp = FALSE;
X			signal(SIGINT, ointr);
X#ifdef SIGCONT
X			signal(SIGCONT, ocont);
X#endif
X			if (pp != NULL)
X				break;
X			if (!sigtrap)
X				return;
X#ifdef SIGCONT
X			if (sigtrap != SIGCONT)
X#endif
X				fprintf(ofp, "\n");
X		}
X		nstrip(bptr);
X		while (*bptr == ' ' || *bptr == '\t')
X			bptr++;
X		if (command())
X			break;
X	}
X
X	if (!news)
X		fprintf(stderr, "No news.\n");
X	cout(ofp);
X}
X
X
X#define EOL() if (*bptr != '\0') { fprintf(ofp, "? for commands.\n"); return FALSE; }
X/*
X * Process one command, which has already been typed in.
X */
Xcommand()
X{
X	char *findhist();
X	long i;
X
X	switch (*bptr++) {
X
X	/* No.  Go on to next article. */
X	case 'n':
X		EOL();
X		readmode = NEXT;
X		if (!cflag)
X			FCLOSE(fp);
X		fprintf(ofp, "\n");
X		clear(bit);
X		saveart;
X		nextbit();
X		break;
X
X	/* Undigestify the article. */
X	case 'd':
X		dgest = 1;
X		/* fall through */
X
X	/* yes: print this article, go on. */
X	case 'y':
X		EOL();
X		/* fall through. */
X
X	/* The user hit return.  Default is 'y' unless rfq, then it's 'q'. */
X	case '\0':
X		if (!bptr[-1] && rfq)
X			return TRUE;
X		readmode = NEXT;
X		showtail(fp);
X		clear(bit);
X		saveart;
X		nextbit();
X		break;
X
X	/*
X	 * Unsubscribe to the newsgroup and go on to next group
X	 */
X	case 'u':
X		fprintf(ofp, "To unsubscribe, use 'U'\n");
X		break;
X
X	case 'U':
X		fprintf(ofp, "Unsubscribing to newsgroup: %s\n", groupdir);
X		obit = -1;
X		FCLOSE(fp);
X		if (cflag)
X			clear(bit);
X		else
X			putc('\n', ofp);
X		rfq = 0;
X		zapng = TRUE;
X		saveart;
X		if (nextng()) {
X			if (actdirect == BACKWARD)
X				fprintf(ofp, "Can't back up.\n");
X			else
X				return TRUE;
X		}
X		break;
X
X		/* Print the current version of news */
X	case 'v':
X		fprintf(ofp, "News version: %s\n", news_version);
X		break;
X
X		/* reprint the article */
X	case 'p':
X		EOL();
X		if (!cflag)
X			goto minus;
X		readmode = NEXT;
X		if (!cflag) {
X			FCLOSE(fp);
X			bit = last;
X			putc('\n', ofp);
X		}
X		obit = -1;
X		break;
X
X		/* decrypt joke */
X	case 'D':
X		caesar_command();
X		readmode = NEXT;
X		clear(bit);
X		saveart;
X		nextbit();
X		break;
X
X		/* write out the article someplace */
X	case 's':
X	case 'w':
X		{
X		char *grn = groupdir;
X		tfilename = filename;
X		if (*bptr == '-') {
X			bptr++;
X			grn = ogroupdir;
X			if (*ofilename1)
X				tfilename = ofilename1;
X		}
X		if (*bptr != '\0' && *bptr != ' ') {
X			fprintf(ofp, "Bad file name.\n");
X			break;
X		}
X		while (*bptr == ' ')
X			bptr++;
X		if (*bptr != '|' && *bptr != '/') {
X			char	hetyped[BUFLEN];
X			char	*boxptr;
X			strcpy(hetyped, bptr);
X			if (boxptr = getenv("NEWSBOX"))
X				if (index(boxptr, '%'))
X					sprintf(bptr, boxptr, grn);
X				else
X					strcpy(bptr, boxptr);
X			else if (hetyped[0] == '~' && hetyped[1] == '/') {
X				strcpy(hetyped, bptr+2);
X				strcpy(bptr, userhome);
X			} else
X				strcpy(bptr, ".");
X			strcat(bptr, "/");
X			if (hetyped[0] != '\0')
X				strcat(bptr, hetyped);
X			else
X				strcat(bptr, "Articles");
X		}
X		fwait(fsubr(save, tfilename, bptr));
X		}
X		break;
X
X		/* back up  */
X	case '-':
Xminus:
X		rfq = 0;
X		abs = TRUE;
X		if (!*ofilename1) {
X			fprintf(ofp, "Can't back up.\n");
X			break;
X		}
X		if (cflag)
X			clear(bit);
X		else {
X			FCLOSE(fp);
X			putc('\n', ofp);
X		}
X		hptr = h;
X		h = hold;
X		hold = hptr;
X		strcpy(bfr, filename);
X		strcpy(filename, ofilename1);
X		strcpy(ofilename1, bfr);
X		obit = bit;
X		if (strcmp(groupdir, ogroupdir)) {
X			strcpy(bfr, groupdir);
X			selectng(ogroupdir, TRUE);
X			strcpy(groupdir, ogroupdir);
X			strcpy(ogroupdir, bfr);
X			ngrp = 1;
X			back();
X		}
X		bit = oobit;
X		oobit = obit;
X		obit = -1;
X		(void) getnextart(TRUE);
X		return FALSE;
X
X		/* skip forwards */
X	case '+':
Xcaseplus:
X		if (*bptr == '\0')
X			strcat(bptr, "1");
X		rfq = 0;
X		if (cflag)
X			clear(bit);
X		saveart;
X		last = bit;
X		for (i = 0; i < atol(bptr); i++) {
X			nextbit();
X			if ((bit > pngsize) || (rflag && bit < 1))
X				break;
X		}
X		if (!cflag) {
X			putc('\n', ofp);
X			FCLOSE(fp);
X		}
X		obit = -1;
X		break;
X
X	/* exit - time updated to that of most recently read article */
X	case 'q':
X		EOL();
X		return TRUE;
X
X	/* exit - no time update. */
X	case 'x':
X		EOL();
X		xxit(0);
X
X	/* cancel the article. */
X	case 'c':
X		(void) cancel_command();
X		break;
X
X	/* escape to shell */
X	case '!':
X		fwait(fsubr(ushell, bptr, (char *)NULL));
X		fprintf(ofp, "\n");
X		hdr();
X		break;
X
X	/* mail reply */
X	case 'r':
X		(void) reply_command();
X		break;
X
X	/* send to some system */
X	case 'X':
X		xmit_command();
X		break;
X	/* mark the rest of the articles in this group as read */
X	case 'K':
X		saveart;
X		while (bit <= pngsize && bit >= minartno) {
X			clear(bit);
X			nextbit();
X		}
X		FCLOSE(fp);
X		break;
X
X	/* next newsgroup */
X	case 'P':
X		*bptr = '-';
X	case 'N':
X		FCLOSE(fp);
X		if (next_ng_command())
X			return TRUE;
X		break;
X
X	case 'b':	/* back up 1 article */
X		i = bit - 1;
X		goto tryartnum;
X	case '0':	/* specific no. */
X	case '1':
X	case '2':
X	case '3':
X	case '4':
X	case '5':
X	case '6':
X	case '7':
X	case '8':
X	case '9':
X		sscanf(--bptr, "%ld", &i);
X		if (i == 0) {
X			fprintf(ofp, "Bad article no.\n");
X			break;
X		}
X		if (i > pngsize) {
X			fprintf(ofp, "Not that many articles.\n");
X			break;
X		}
Xtryartnum:
X		readmode = SPEC;
X		abs = TRUE;
X		bit = i;
X		obit = -1;
X		if (!cflag) {
X			putc('\n', ofp);
X			FCLOSE(fp);
X		}
X		rfq = 0;
X		break;
X
X	/* specific message ID. */
X	case '<':
X		ptr1 = findhist(--bptr);
X		if (ptr1 == NULL) {
X			fprintf(ofp, "No such article: %s.\n", bptr);
X			break;
X		}
X		ptr2 = index(ptr1, '\t');
X		ptr3 = index(++ptr2, '\t');
X		ptr2 = index(++ptr3, ' ');
X		if (ptr2)
X			*ptr2 = '\0';
X		ptr2 = index(ptr3, '/');
X		*ptr2++ = '\0';
X		abs = TRUE;
X		if (cflag)
X			clear(bit);
X		else {
X			FCLOSE(fp);
X			putc('\n', ofp);
X		}
X		saveart;
X		strcpy(ogroupdir, ptr3);
X		if (strcmp(groupdir, ogroupdir)) {
X			strcpy(bfr, groupdir);
X			selectng(ogroupdir, TRUE);
X			strcpy(groupdir, ogroupdir);
X			strcpy(ogroupdir, bfr);
X			back();
X		}
X		sscanf(ptr2, "%ld", &bit);
X		oobit = obit;
X		obit = -1;
X		(void) getnextart(TRUE);
X		rfq = 0;
X		break;
X
X	/* follow-up article */
X	case 'f':
X		if (*bptr == '-')
X			tfilename = ofilename1;
X		else
X			tfilename = filename;
X		sprintf(bfr,"%s/%s %s", BIN, "postnews", tfilename);
X		system(bfr);
X		break;
X
X	/* erase - pretend we haven't seen this article. */
X	case 'e':
X		if (rfq || *bptr == '-') {
X			if (strcmp(groupdir, ogroupdir)) {
X				i = bit;
X				strcpy(bfr, groupdir);
X				selectng(ogroupdir, FALSE);
X				set(oobit);
X				fprintf(ofp,"Holding article %ld newsgroup %s\n", oobit, ogroupdir),
X				strcpy(groupdir, ogroupdir);
X				selectng(bfr, FALSE);
X				bit = i;
X			} else {
X				fprintf(ofp,"Holding article %ld\n", oobit);
X				set(oobit);
X			}
X		} else {
X			fprintf(ofp,"Holding article %ld\n", bit);
X			set(bit);
X			goto caseplus;	/* skip this article for now */
X		}
X		break;
X
X	case 'H':
X	case 'h':
X		if (!hflag)
X			dash(8, ofp);
X		if (*bptr == '-') {
X			if (oobit > 0)
X				fprintf(ofp, "Article %ld:\n", oobit);
X			hprint(hold, ofp, 1 + (bptr[-1]=='H'));
X		} else {
X			fprintf(ofp, "Article %ld of %ld: %s\n",
X				rfq ? oobit : bit, pngsize, h->ident);
X			hprint(h, ofp, 1 + (bptr[-1]=='H'));
X		}
X		if (!hflag)
X			dash(8, ofp);
X		break;
X
X	case '#':
X		fprintf(ofp, "Article %ld of %ld: newsgroup %s\n",
X			rfq ? oobit : bit, pngsize, rfq ? ogroupdir : groupdir);
X		break;
X
X		/* error */
X	case '?':
X		help(ofp);
X		break;
X	default:
X		fprintf(ofp, "? for commands.\n");
X		break;
X	}
X
X	return FALSE;
X}
X
Xcancel_command()
X{
X	tfilename = filename;
X	hptr = h;
X	if (*bptr == '-') {
X		if (*ofilename1) {
X			tfilename = ofilename1;
X			hptr = hold;
X		}
X		bptr++;
X	}
X	EOL();
X	readmode = SPEC;
X	strcpy(rcbuf, hptr->path);
X	ptr1 = index(rcbuf, ' ');
X	if (ptr1)
X		*ptr1 = 0;
X	if (uid != ROOTID && strcmp(username, rcbuf)) {
X		fprintf(ofp, "Can't cancel what you didn't write.\n");
X		return FALSE;
X	}
X	if (!cancel(ofp, hptr, 0) && hptr == h) {
X		clear(bit);
X		saveart;
X		nextbit();
X		obit = -1;
X		if (!cflag)
X			putc('\n', ofp);
X		FCLOSE(fp);
X	}
X	return TRUE;
X}
X
Xreply_command()
X{
X	register char	*pathptr;
X	int edit = 1;
X	char *ed;
X	FILE *tfp;
X	char	curberk[BUFLEN];
X	char *replyname();
X	char subj[BUFLEN];
X	char folbuf[BUFLEN];
X	extern char MAILPARSER[];
X	struct stat statb;
X	long creatm;
X
X	hptr = h;
X	while (*bptr && index("d-", *bptr)) {
X		switch (*bptr) {
X		/* Followup the previous article. */
X		case '-':
X			hptr = hold;
X			break;
X
X		/* Don't edit the headers */
X		case 'd':
X			edit = 0;
X			break;
X		}
X		bptr++;
X	}
X	EOL();
X	if (edit && access(MAILPARSER, 1)) {
X#ifdef IHCC
X		fprintf(stderr, "Can't edit headers, 'recmail' missing.\n");
X#else
X		fprintf(stderr, "Can't edit headers without %s\n", MAILPARSER);
X#endif
X		edit = 0;
X	}
X
X	*rcbuf = '\0';
X	*curberk = '\0';
X	pathptr = replyname(hptr);;
X	for (ptr1 = address, ptr2 = pathptr; *ptr2; ptr1++, ptr2++) {
X		if (index("\"\\$", *ptr2))
X			*ptr1++ = '\\';
X		*ptr1 = *ptr2;
X	}
X	*ptr1 = '\0';
X
X	folbuf[0] = '\0';		/* References */
X	if (hptr->followid[0]) {
X		strcpy(folbuf, hptr->followid);
X		strcat(folbuf, ", ");
X	}
X	strcat(folbuf, hptr->ident);
X
X	strcpy(subj, hptr->title);	/* Subject */
X	while (isspace(*bptr))
X		bptr++;
X	if (*bptr != '\0')
X		strcpy(subj, bptr);
X	if (!prefix(subj, "Re:")){
X		strcpy(bfr, subj);
X		sprintf(subj, "Re: %s", bfr);
X	}
X	if (!edit) {
X		fprintf(ofp, "To: %s\n", pathptr);
X		ed = index(MAILER, '%');
X		if (ed && ed[1] == 's')
X			fprintf(ofp, "Subject: %s\n", subj);
X		fflush(ofp);
X	}
X
X	/* Put the user in the editor to create the body of the followup. */
X	if (edit) {
X		strcpy(tf, tft);
X		mktemp(tf);
X
X		ed = getenv("EDITOR");
X		if (ed == NULL)
X			ed = DFTEDITOR;
X
X		if ((tfp = fopen(tf, "w")) == NULL) {
X			perror(tf);
X			creatm = 0L;
X		} else {
X			fprintf(tfp, "To: %s\n", pathptr);
X			fprintf(tfp, "Subject: %s\n", subj);
X			fprintf(tfp, "References: %s\n\n", folbuf);
X			fstat(fileno(tfp), &statb);
X			creatm = statb.st_mtime;
X			fclose(tfp);
X		}
X
X		sprintf(edcmdbuf, "%s %s", ed, tf);
X		system(edcmdbuf);
X		strcpy(rcbuf, MAILPARSER);
X		strcat(rcbuf, " -t");
X		strcat(rcbuf, " < ");
X		strcat(rcbuf, tf);
X		if (access(tf, 4) || stat(tf, &statb)) {
X			fprintf(stderr, "Reply not sent: no input file.\n");
X			return FALSE;
X		}
X		if (statb.st_mtime == creatm) {
X			fprintf(stderr, "Reply not sent: cancelled.\n");
X			unlink(tf);
X			return FALSE;
X		}
X		fprintf(ofp,"Sending reply.\n");
X		fflush(stdout);
X		if (fork() == 0) {
X			system(rcbuf);
X			unlink(tf);
X			_exit(0);
X		}
X	} else {
X		sprintf(rcbuf, MAILER, hptr->title);
X		sprintf(bfr, "%s %s", rcbuf, address);
X		system(bfr);
X	}
X	hdr();
X	return TRUE;
X}
X
Xxmit_command()
X{
X	tfilename = filename;
X	if (*bptr == '-') {
X		if (*ofilename1)
X			tfilename = ofilename1;
X		bptr++;
X	}
X	if (*bptr != '\0' && *bptr != ' ') {
X		fprintf(ofp, "Bad system name.\n");
X		return;
X	}
X	while (*bptr == ' ')
X		bptr++;
X	if (*bptr == '\0') {
X		fprintf(ofp, "Missing system name.\n");
X		return;
X	}
X	if (s_find(&srec, bptr) == NULL) {
X		fprintf(ofp, "%s not in SYSFILE\n", bptr);
X		return;
X	}
X	transmit(&srec, tfilename);
X}
X
Xnext_ng_command()
X{
X	obit = -1;
X	if (!*bptr || *bptr == '-') {
X		if (cflag)
X			clear(bit);
X		else
X			putc('\n', ofp);
X		if (*bptr)
X			actdirect = BACKWARD;
X		rfq = 0;
X		saveart;
X		if (nextng()) {
X			if (actdirect == BACKWARD)
X				fprintf(ofp, "Can't back up.\n");
X			else
X				return TRUE;
X		}
X		return FALSE;
X	}
X	while (isspace(*bptr))
X		bptr++;
X	if (!validng(bptr)) {
X		fprintf(ofp, "No such group.\n");
X		return FALSE;
X	}
X	if (cflag)
X		clear(bit);
X	else
X		putc('\n', ofp);
X	readmode = SPEC;
X	rfq = 0;
X	saveart;
X	back();
X	selectng(bptr, TRUE);
X	return FALSE;
X}
X
Xcaesar_command()
X{
X	char	temp[BUFLEN];
X	FILE	*pfp, *popen();
X
X	fprintf(stderr, "Caesar decoding:\n");
X	sprintf(temp, "%s/%s", LIB, "caesar");
X	if (*bptr) {
X		strcat(temp, " ");
X		strcat(temp, bptr);
X	}
X	if (NLINES(h, fp) > LNCNT && *PAGER) {
X		strcat(temp, " | ");
X		strcat(temp, PAGER);
X	}
X	pfp = popen(temp, "w");
X	tprint(fp, pfp, FALSE);
X	FCLOSE(fp);
X	pclose(pfp);
X}
X
X/*
X * Show the user the tail, if any, of the message on file
X * descriptor fd, and close fd.  The digester is considered,
X * and the pager is used if appropriate.
X */
Xshowtail(fd)
XFILE *fd;
X{
X	if (fd == NULL)
X		return;
X
X	if (dgest) {
X		digest(fd, ofp, h);
X	} else if (!lflag && !pflag && !eflag) {
X		pprint(fd);
X	}
X	fclose(fd);
X}
X
X/*
X * Print out the rest of the article through the pager.
X */
Xpprint(fd)
XFILE *fd;
X{
X#ifdef PAGE
X	/* Filter the tail of long messages through PAGER. */
X	if (NLINES(h, fd) > LNCNT && *PAGER) {
X		if (!index(PAGER, FMETA)) {
X			FILE *pfp, *popen();
X
X			pfp = popen(PAGER, "w");
X			if (pfp == NULL)
X				pfp = ofp;
X			/*
X			 * What follows is an attempt to prevent the
X			 * next message from scrolling part of this
X			 * message off the top of the screen before
X			 * the poor luser can read it.
X			 */
X			tprint(fd, pfp, FALSE);
X			pclose(pfp);
X		}
X		else
X			pout(ofp);
X		holdup = TRUE;
X	}
X	else
X#endif
X		tprint(fd, ofp, FALSE);
X}
X
X/*
X * Find the next article we want to consider, if we're done with
X * the last one, and show the header.
X */
Xgetnextart(minus)
Xint minus;
X{
X 	int noaccess;
X 	register DIR *dirp;
X 	register struct direct *dir;
X 	long nextnum, tnum;
X
X 	noaccess = 0;
X
X	if (minus)
X		goto nextart2;	/* Kludge for "-" command. */
X
X	if (bit == obit)	/* Return if still on same article as last time */
X		return 0;
X
X	sigtrap = FALSE;
X
Xnextart:
X#ifdef DEBUG
X	fprintf(stderr,"nextart:\n");
X#endif DEBUG
X	dgest = 0;
X
X	if (bit < minartno && !rflag)
X		bit = minartno;
X
X	/* If done with this newsgroup, find the next one. */
X	while (ngsize <= 0 || ((long) bit > ngsize) || (rflag && bit < minartno)) {
X		if (nextng()) {
X			if (actdirect == BACKWARD) {
X				fprintf(ofp, "Can't back up.\n");
X				actdirect = FORWARD;
X				continue;
X			} else
X				if (rfq++ || pflag || cflag)
X					return 1;
X		}
X		if (rflag)
X			bit = ngsize + 1;
X		else
X			bit = minartno - 1;
X		if (uflag) {
X			time_t now;
X			(void) time(&now);
X			if (now - timelastsaved > 5*60 /* 5 minutes */) {
X				fprintf(stderr,"[Saving .newsrc]\n");
X				writeoutrc();
X				timelastsaved = now;
X			}
X		}
X	}
X
Xnextart2:
X#ifdef DEBUG
X	fprintf(stderr, "article: %s/%ld\n", groupdir, bit);
X#endif
X	if (rcreadok)
X		rcreadok = 2;	/* have seen >= 1 article */
X	sprintf(filename, "%s/%ld", dirname(groupdir), bit);
X	if (rfq && goodone[0])
X		strcpy(filename, goodone);
X	if (sigtrap) {
X		if (sigtrap == SIGHUP)
X			return 1;
X		if (!rcreadok)
X			xxit(0);
X		fprintf(ofp, "Abort (n)?  ");
X		fflush(ofp);
X		gets(bfr);
X		if (*bfr == 'y' || *bfr == 'Y')
X			xxit(0);
X		sigtrap = FALSE;
X	}
X#ifdef DEBUG
X	fprintf(stderr, "filename = '%s'\n", filename);
X#endif
X	/* Decide if we want to show this article. */
X 	if ((fp = fopen(filename, "r")) == NULL) {
X 		/* since there can be holes in legal article numbers, */
X 		/* we wait till we hit 5 consecutive bad articles */
X 		/* before we haul off and scan the directory */
X 		if (++noaccess < 5)
X 			goto badart;
X		noaccess = 0;
X 		dirp = opendir(dirname(groupdir));
X 		if (dirp == NULL) {
X			if (errno != EACCES)
X				fprintf(stderr,"Can't open %s", dirname(groupdir));
X 			goto badart;
X 		}
X 		nextnum = rflag ? minartno - 1 : ngsize + 1;
X 		while ((dir = readdir(dirp)) != NULL) {
X 			tnum = atol(dir->d_name);
X 			if (tnum <= 0)
X 				continue;
X 			if (rflag ? (tnum > nextnum && tnum < bit)
X 				  : (tnum < nextnum && tnum > bit))
X 				nextnum = tnum;
X 		}
X 		closedir(dirp);
X 		if (rflag ? (nextnum >= bit) : (nextnum <= bit))
X 			goto badart;
X#ifdef DEBUG
X		fprintf(stderr,"nextnum = %ld\n",nextnum);
X#endif DEBUG
X 		do {
X 			clear(bit);
X 			nextbit();
X 		} while (rflag ? (nextnum < bit) : (nextnum > bit));
X 		obit = -1;
X 		abs = FALSE;
X 		goto nextart;
X 	} else
X 		noaccess = 0;
X
X 	if (ignorenews || hread(h, fp, TRUE) == NULL
X		|| (!rfq && !aselect(h, abs))) {
X badart:
X#ifdef DEBUG
X		fprintf(stderr, "Bad article '%s'\n", filename);
X#endif
X		FCLOSE(fp);
X		clear(bit);
X		obit = -1;
X		nextbit();
X		abs = FALSE;
X		goto nextart;
X	}
X	abs = FALSE;
X	actdirect = FORWARD;
X	news = TRUE;
X	hdr();
X	if (pflag)
X		tprint(fp, ofp, FALSE);
X	else if (cflag && !lflag && !eflag) {
X		fflush(ofp);
X		pprint(fp);
X	}
X	if (cflag || lflag || eflag || pflag) {
X		sigtrap = FALSE;
X		FCLOSE(fp);
X	}
X	obit = bit;
X	return 0;
X}
X
X/*
X * Print out whatever the appropriate header is
X */
Xhdr()
X{
X	char *briefdate();
X
X	if (rfq)
X		return;
X
X	if (lflag || eflag) {
X		hprint(h, ofp, 0);
X		return;
X	}
X
X	/* Print out a header */
X	if (ngrp) {
X		pngsize = ngsize;
X		ngrp--;
X		nghprint(groupdir);
X	}
X	if (!hflag)
X		fprintf(ofp, "Article %ld of %ld, %s.\n",
X			bit, pngsize, briefdate(h->subdate));
X	hprint(h, ofp, pflag ? 1 : 0);
X}
X
Xnghprint(title)
Xchar *title;
X{
X	char *tstr = "Newsgroup ";
X	int l = strlen(title) + strlen(tstr);
X
X	fprintf(ofp, "\n");
X	if (!hflag) {
X		dash(l, ofp);
X		fprintf(ofp, "%s%s\n", tstr, title);
X		dash(l, ofp);
X	} else {
X		fprintf(ofp, "%s%s, ", tstr, title);
X		if (bit == pngsize)
X			fprintf(ofp, "%ld\n", pngsize);
X		else
X			fprintf(ofp, "%ld-%ld\n", bit, pngsize);
X	}
X	fprintf(ofp, "\n");
X}
X
X/*
X * Routine to catch a continue signal.
X */
Xcatchcont(sig)
Xint sig;
X{
X	signal(sig, catchcont);
X	sigtrap = sig;
X	fflush(ofp);
X#ifdef SIGCONT
X	if (fp && sig == SIGCONT)
X		hdr();
X	if (sig != SIGCONT)
X#endif SIGCONT
X		putc('\n', ofp);
X	if (canlongjmp)
X		longjmp(sigjmpbuf,1);
X}
*-*-END-of-src/readr.c-*-*
echo x - src/recmail.c
sed 's/^X//' >src/recmail.c <<'*-*-END-of-src/recmail.c-*-*'
X/*
X * recmail: read a mail message on stdin, grab all addresses in To and Cc
X * lines, and pass the full message to all addressees.  This is useful to
X * send the output of a recently edited mail message (with headers edited too).
X * It is similar to sendmail -t, but only assumes /bin/mail.
X * To use your own mailer, e. g. nmail, compile with -DMAILER=my_mailer.
X */
X
X#ifndef lint
Xstatic char	*SccsId = "@(#)recmail.c	1.8	8/14/84";
X#endif !lint
X
X#include <stdio.h>
X#include <ctype.h>
X#include <pwd.h>
X#ifdef USG
Xstruct passwd *getpwent(), *getpwuid(), *getpwnam();
X#endif USG
X
X#ifndef MAILER
X#define MAILER "/bin/mail"
X#endif
Xchar mailer[] = MAILER;
X
X#define MAXRECIPS 100
Xchar *recips[MAXRECIPS];
Xint nrecips = 0;
X
Xmain()
X{
X	FILE *fd;
X	char *tmpf;
X	FILE *errfd;
X	char *errf;
X	char linebuf[1024];
X	int i, pid, wpid;
X	int exstat;
X	char *mypath;
X	int goodcnt, badcnt;
X	char *mktemp(), *getenv();
X
X	tmpf = mktemp("/tmp/rmXXXXXX");
X	close(creat(tmpf,0666));
X	fd = fopen(tmpf, "w");
X	errf = mktemp("/tmp/rmXXXXXX");
X	close(creat(errf,0666));
X	errfd = fopen(errf, "w");
X	fprintf(errfd, "Subject: Returned mail\n");
X	fprintf(errfd, "\n  ----- Transcript of session follows -----\n");
X	fflush(errfd);
X	goodcnt = badcnt = 0;
X
X	while (fgets(linebuf, sizeof linebuf, stdin) != NULL) {
X		if (fputs(linebuf, fd) == EOF)
X			goto werror;
X		if (linebuf[0] == '\n')
X			break;
X		if (strncmp(linebuf, "To: ", 4) == 0 ||
X		    strncmp(linebuf, "to: ", 4) == 0 ||
X		    strncmp(linebuf, "TO: ", 4) == 0 ||
X		    strncmp(linebuf, "Cc: ", 4) == 0 ||
X		    strncmp(linebuf, "cc: ", 4) == 0 ||
X		    strncmp(linebuf, "CC: ", 4) == 0)
X			addrecips(linebuf+4);
X	}
X	if (!feof(stdin)) {
X		while (fgets(linebuf, sizeof linebuf, stdin) != NULL) {
X			if (fputs(linebuf, fd) == EOF) {
Xwerror:
X				printf("write error on temp file\n");
X				exit(2);
X			}
X		}
X	}
X	fclose(fd);
X
X	/*
X	 * Force the path to only consider /bin and /usr/bin, since
X	 * that's the version of mail we want (not /usr/ucb/mail)
X	 * This code will probably cause a core dump some day.
X	 */
X	mypath = getenv("PATH");
X	if (mypath)
X		strcpy(mypath, "/bin:/usr/bin");
X
X	/*
X	 * We send the copies out separately, because of a bug in
X	 * USG's /bin/mail which will generate ANOTHER To: line,
X	 * even though we already have one, if there are at least
X	 * two recipients.
X	 */
X	for (i=0; i<nrecips; i++) {
X		/*
X		 * mail recips[i] < tmpf
X		 */
X		pid = mailto(tmpf, errfd, recips[i]);
X		exstat = -1;
X		while ((wpid = wait(&exstat)) >= 0 && wpid != pid)
X			;
X		if (exstat == 0)
X			goodcnt++;
X		else
X			badcnt++;
X	}
X	if (badcnt) {
X		mailback(errfd, tmpf, errf);
X		unlink(tmpf);
X		unlink(errf);
X		exit(1);
X	} else if (goodcnt == 0) {
X		fprintf(errfd, "recmail: no 'To:' line\n");
X		mailback(errfd, tmpf, errf);
X		unlink(tmpf);
X		unlink(errf);
X		exit (1);
X	}
X	unlink(tmpf);
X	unlink(errf);
X	exit (0);
X}
X
X#define isok(c) (isprint(c) && (c) != ' ' && c != ',')
Xaddrecips(line)
Xchar *line;
X{
X	char *front, *back, *tail;
X	char *malloc();
X
X	tail = line + strlen(line);
X	for (front=line; front < tail; ) {
X		while (!isok(*front) && front < tail)
X			front++;
X		for (back=front; isok(*back); back++)
X			;
X		*back=0;
X		if (nrecips >= MAXRECIPS) {
X			printf("Too many destinations\n");
X			exit(2);
X		}
X		if ((recips[nrecips] = malloc(strlen(front) + 1)) == NULL) {
X			printf("Out of space\n");
X			exit(2);
X		}
X		strcpy(recips[nrecips], front);
X		nrecips++;
X		front = back+1;
X	}
X}
X
Xint
Xmailto(tmpf, errfd, recip)
Xchar *tmpf;
XFILE *errfd;
Xchar *recip;
X{
X	register int pid;
X
X	/*
X	 * mail recips < tmpf
X	 */
X	while ((pid = fork()) == -1) {
X		fprintf(stderr, "fork failed, waiting...\r\n");
X		sleep(60);
X	}
X	if (pid == 0) {
X		close(0);
X		open(tmpf, 0);
X		if (errfd != NULL) {
X			close(1);
X			dup(fileno(errfd));
X			fclose(errfd);
X			close(2);
X			dup(1);
X		}
X		execlp(mailer, mailer, recip, (char *)0);
X		perror(mailer);
X		exit(1);
X	}
X	return(pid);
X}
X
Xmailback(errfd, tmpf, errf)
Xregister FILE *errfd;
Xchar *tmpf;
Xchar *errf;
X{
X	register FILE *fd;
X	register int c;
X	int exstat;
X	register int pid, wpid;
X	char *logn;
X	char *getlogin(), *getenv();
X	register struct passwd *pwd;
X
X	if ((fd = fopen(tmpf, "r")) != NULL) {
X		fprintf(errfd, "\n   ----- Unsent message follows -----\n");
X		while ((c = getc(fd)) != EOF)
X			putc(c, errfd);
X		fclose(fd);
X	}
X	fclose(errfd);
X	if ((logn = getlogin()) == NULL && (logn = getenv("USER")) == NULL) {
X		if ((pwd = getpwent(getuid())) == NULL)
X			return;
X		logn = pwd->pw_name;
X	}
X	pid = mailto(errf, (FILE *)NULL, logn);
X	while ((wpid = wait(&exstat)) >= 0 && wpid != pid)
X		;
X}
*-*-END-of-src/recmail.c-*-*
echo x - src/recnews.c
sed 's/^X//' >src/recnews.c <<'*-*-END-of-src/recnews.c-*-*'
X/*
X * recnews [to newsgroup] [from user]
X *
X * Process a news article which has been mailed to some group like msgs.
X * Such articles are in normal mail format and have never seen the insides
X * of netnews.  If the "to newsgroup" is included, the article is posted
X * to this newsgroup instead of trying to intuit it from the headers.
X * If the "from user" is included, the return address is forged to look
X * like that user instead of what getuid or a from line says.
X *
X * It is recommended that you always include the to newsgroup, since the
X * intution code is flakey and out of date.  The from user is probably
X * appropriate for arpanet mailing lists being funnelled at ucbvax but
X * not otherwise.  Sample lines in /usr/lib/aliases (if you run delivermail):
X *	worldnews: "|/usr/lib/news/recnews net.general"
X *		Allows you to mail to worldnews rather than using inews.
X *		Intended for humans to mail to.
X *	post-unix-wizards: "|/usr/lib/news/recnews fa.unix-wizards unix-wizards"
X *		Causes mail to post-unix-wizards to be fed into fa.unix-wizards
X *		and the return address forged as unix-wizards on the local
X *		machine.  post-unix-wizards (on the local machine) should
X *		be part of the master mailing list somewhere (on a different
X *		machine.)
X *
X * Recnews is primarily useful in remote places on the usenet which collect
X * mail from mailing lists and funnel them into the network.  It is also
X * useful if you like to send mail to some user instead of invoking
X * inews -t .. -n .. when you want to submit an article.  (Many mailers give
X * you nice facilities like editing the message.)  It is not, however,
X * essential to use recnews to be able to join usenet.
X *
X * WARNING: recnews disables the "recording" check - it has to because
X * by the time inews is run, it's in the background and too late to
X * ask permission.  If you depend heavily on recordings you probably
X * should not allow recnews (and thus the mail interface) to be used.
X */
X
X#ifndef lint
Xstatic char	*SccsId = "@(#)recnews.c	2.9	9/3/84";
X#endif !lint
X
X#include "defs.h"
X
X#include <stdio.h>
X#include <ctype.h>
X
X/*
X * Note: we assume there are 2 kinds of hosts using recnews:
X * Those that have delivermail (and hence this program will never
X * have to deal with more than one message at a time) and those on the arpanet
X * that do not (and hence all messages end with a sentenel).  It is
X * supposed that regular v7 type systems without delivermail or some
X * other automatic forwarding device will just use rnews.  We do
X * not attempt to tell where a message ends on all systems due to the
X * different conventions in effect.  (This COULD be fixed, I suppose.)
X */
X
X/*
X * Kinds of lines in a message.
X */
X#define FROM	001		/* From line */
X#define SUBJ	002		/* Subject */
X#define TO	003		/* To (newgroup based on this) */
X#define BLANK	004		/* blank line */
X#define EOM	005		/* End of message (4 ctrl A's) */
X#define HEADER	006		/* any unrecognized header */
X#define TEXT	007		/* anything unrecognized */
X#define INCLUSIVE 010		/* newsgroup is already in header */
X
X/*
X * Possible states program can be in.
X */
X#define SKIPPING	0100	/* In header of message */
X#define READING		0200	/* In body of message */
X
X#define BFSZ 250
X
X#define EOT	'\004'
X
Xchar	from[BFSZ];		/* mailing address for replies */
Xchar	sender[BFSZ];		/* mailing address of author, if different */
Xchar	to[BFSZ];		/* Destination of mail (msgs, etc) */
Xchar	subject[BFSZ];		/* subject of message */
Xchar	newsgroup[BFSZ];	/* newsgroups of message */
Xchar	cmdbuf[BFSZ];		/* command to popen */
X
Xextern	char	*strcat(), *strcpy();
Xextern	FILE	*popen();
Xchar	*any();
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	char buf[BFSZ], inews[BFSZ];
X	register char *p, *q;
X	register FILE *pipe = NULL;
X	register int state;
X
X	/* build inews command */
X#ifdef IHCC
X	sprintf(inews, "%s/%s/%s", logdir(HOME), LIBDIR, "inews");
X#else
X	sprintf(inews, "%s/%s", LIBDIR, "inews");
X#endif
X
X	if (argc > 1)
X		strcpy(to, argv[1]);
X	if (argc > 2)
X		strcpy(from, argv[2]);
X#ifdef debug
X	printf("argv[0] is <%s>, argv[1] is <%s>, argv[2] is <%s>\n",
X		argv[0], argv[1], argv[2]);
X#endif
X	state = SKIPPING;
X	while (fgets(buf, BFSZ, stdin) != NULL) {
X		if (state == READING) {
X			fputs(buf,pipe);
X			continue;
X		}
X		switch (type(buf)) {
X
X		case FROM:
X			frombreak(buf, from);
X			break;
X
X		case SUBJ:
X			p = any(buf, " \t");
X			if (p == NULL)
X				p = buf + 8;
X			q = subject;
X			while (*++p) {
X				if (*p == '"')
X					*q++ = '\\';
X				*q++ = *p;
X			}
X			q[-1] = '\0';
X			break;
X
X		case TO:
X			if (to[0])
X				break;		/* already have one */
X			p = any(buf, " \t");
X			if (p == NULL)
X				p = buf + 3;
X			q = to;
X			while (*++p) {
X				if (*p == '"')
X					*q++ = '\\';
X				*q++ = *p;
X			}
X			q[-1] = '\0';
X			break;
X
X		case INCLUSIVE:
X			sprintf(cmdbuf,"exec %s -p", inews);
X			pipe = popen(cmdbuf,"w");
X			if (pipe == NULL){
X				perror("recnews: open failed");
X				exit(1);
X			}
X			state = READING;
X			fputs(buf,pipe);
X			break;
X
X		/*
X		 * Kludge to compensate for messages without real headers
X		 */
X		case HEADER:
X			break;
X
X		case BLANK:
X			state = READING;
X			findgroup(to, newsgroup);
X			sprintf(cmdbuf, "exec %s -t \"%s\" -n \"%s\" -f \"%s\"",
X				inews, subject, newsgroup, from);
X#ifdef debug
X			pipe = stdout;
X			printf("BLANK: %s\n", cmdbuf);
X#else
X			pipe = popen(cmdbuf, "w");
X			if (pipe == NULL) {
X				perror("recnews: popen failed");
X				exit(1);
X			}
X#endif
X			if (sender[0]) {
X				fputs(sender, pipe);
X				putc('\n', pipe);
X			}
X			break;
X
X		case TEXT:
X			findgroup(to, newsgroup);
X			state = READING;
X			if (subject[0] == 0) {
X				strcpy(subject, buf);
X				if (subject[strlen(subject)-1] == '\n')
X					subject[strlen(subject)-1] = '\0';
X			}
X			sprintf(cmdbuf, "exec \"%s\" -t \"%s\" -n \"%s\" -f \"%s\"",
X				inews, subject, newsgroup, from);
X#ifdef debug
X			pipe = stdout;
X			printf("TEXT: %s\n", cmdbuf);
X#else
X			pipe = popen(cmdbuf, "w");
X			if (pipe == NULL) {
X				perror("pipe failed");
X				exit(1);
X			}
X#endif
X			if (sender[0]){
X				fputs(sender, pipe);
X				putc('\n',pipe);
X			}
X			break;
X		}
X	}
X	exit(0);
X}
X
Xtype(p)
Xregister char *p;
X{
X	char *firstbl;
X	static char lasthdr;		/* prev line was a header */
X
X	lasthdr = 1;
X	if ((*p == ' ' || *p == '\t') && lasthdr)
X		return HEADER;		/* continuation line */
X	firstbl = any(p, " \t");
X	while (*p == ' ' || *p == '?' || *p == '\t')
X		++p;
X
X	if (*p == '\n' || *p == 0)
X		return BLANK;
X	if (strncmp(p, ">From", 5) == 0 || strncmp(p, "From", 4) == 0)
X		return FROM;
X	if (strncmp(p, "Subj", 4)==0 || strncmp(p, "Re:", 3)==0 ||
X		strncmp(p, "re:", 3)==0)
X		return SUBJ;
X	if (strncmp(p, "To", 2)==0)
X		return TO;
X	if (strncmp(p, "\1\1\1\1", 4)==0)
X		return EOM;
X	if (firstbl && firstbl[-1] == ':' && isalpha(*p))
X		return HEADER;
X	lasthdr = 0;
X	return TEXT;
X}
X
X/*
X * Figure out who a message is from.
X */
Xfrombreak(buf, fbuf)
Xregister char *buf, *fbuf;
X{
X	char wordfrom[BFSZ], uname[BFSZ], at[BFSZ], site[BFSZ];
X
X	if (fbuf[0]) {	/* we already know who it's from */
X		if (sender[0] == 0 || buf[4] == ':') {
X#ifdef debug
X			printf("sender set to: %s", buf);
X#endif
X			strcpy(sender, buf);
X		}
X		return;
X	}
X	/* break the line into tokens. */
X	sscanf(buf, "%s %s %s %s", wordfrom, uname, at, site);
X	if (isat(at))
X		/*
X		 * Some arpanet mail comes from "joe at mit-dms"
X		 * instead of "joe@mit-dms", so handle it here.
X		 */
X		sprintf(fbuf, "%s@%s", uname, site);
X	else
X		strcpy(fbuf, uname);
X}
X
Xisat(str)
Xchar *str;
X{
X	if (strcmp(str, "@")==0) return TRUE;
X	if (strcmp(str, "at")==0) return TRUE;
X	if (strcmp(str, "AT")==0) return TRUE;
X	return FALSE;
X}
X
Xfindgroup(dest, group)
Xchar *dest ;
Xchar *group;
X{
X#ifdef debug
X	printf("findgroup(%s)\n", dest);
X#endif
X#ifdef fussy
X	/*
X	 * Default unknown "to" fields to "general".  This gives you
X	 * tight control over which newsgroups exist.
X	 */
X	if (strcmp(dest, "msgs")==0)
X		strcpy(group, "msgs");
X	else if (strcmp(dest, "allmsgs")==0)
X		strcpy(group, "NET.allmsgs");
X	else if (strcmp(dest, "csmsgs")==0)
X		strcpy(group, "NET.csmsgs");
X	else
X		strcpy(group, "general");
X#else
X	/*
X	 * Allow any newsgroup.  This way you don't have to recompile
X	 * recnews everytime you add a newsgroup.
X	 */
X	strcpy(group, dest);
X#endif
X}
X
X/*
X * Return the ptr in sp at which a character in sq appears;
X * NULL if not found
X *
X */
X
Xchar *
Xany(sp, sq)
Xchar *sp, *sq;
X{
X	register c1, c2;
X	register char *q;
X
X	while (c1 = *sp++) {
X		q = sq;
X		while (c2 = *q++)
X			if (c1 == c2)
X				return(--sp);
X	}
X	return(NULL);
X}
*-*-END-of-src/recnews.c-*-*
echo x - src/rextern.c
sed 's/^X//' >src/rextern.c <<'*-*-END-of-src/rextern.c-*-*'
X/*
X * rextern - external definitions for readnews
X */
X
X#ifndef lint
Xstatic char	*SccsId = "@(#)rextern.c	2.12	8/28/84";
X#endif !lint
X
X#include "rparams.h"
X
Xint	uid, gid;			/* real user/group I.D.		*/
Xint	duid, dgid;			/* effective user/group I.D.	*/
Xint	sigtrap;			/* set if signal trapped	*/
Xint	savmask;			/* old umask			*/
Xint	mode;				/* mode of news program		*/
Xstruct hbuf header;			/* general-use header structure	*/
Xchar	bfr[LBUFLEN];			/* general-use scratch area	*/
X
X#ifndef ROOTID
Xint	ROOTID;				/* special users id #		*/
X#endif
X
Xchar	*outfile = "/tmp/M1XXXXXX";	/* output file for -M and -c	*/
Xchar	*infile = "/tmp/M2XXXXXX";	/* -T output from Mail		*/
Xint	ngrp, line = -1;
X
Xchar	filename[BUFLEN], coptbuf[BUFLEN], datebuf[BUFLEN];
Xchar	titlebuf[BUFLEN];
Xchar	afline[BUFLEN];
XFILE	*rcfp, *actfp;
Xtime_t	atime;
Xchar	newsrc[BUFLEN], groupdir[BUFLEN], *rcline[LINES], rcbuf[LBUFLEN];
Xchar	bitmap[BITMAPSIZE/8], *argvrc[LINES];
Xlong	bit, obit, last;
Xint	readmode = NEXT;
Xint	actdirect = FORWARD;	/* read direction in ACTIVE file */
Xint	rcreadok = FALSE;	/* NEWSRC has been read OK */
Xint	zapng = FALSE;		/* ! out this newsgroup on next updaterc */
Xlong	ngsize;			/* max article # in this newsgroup */
Xlong	minartno;		/* min article # in this newsgroup */
X
X#ifndef SHELL
Xchar	*SHELL;
X#endif
X
X#ifndef MAILER
Xchar	*MAILER;
X#endif
X
Xchar	PAGER[BUFLEN];
*-*-END-of-src/rextern.c-*-*
exit