[net.sources] Fixes for bugs in 2.10 readnews

smk@linus.UUCP (Steven M. Kramer) (06/06/83)

I have modified this fix to readr.c to work with the -ldir stuff.
You have to compile readnews with the -ldir flag in the Makefile and
have to enable the -DMITRE stuff.  Also in readr.c are all the current
fixes to now, so I'll post the whole thing.  If you don't enable -DMITRE,
you get the original source.  Remember, the -ldir lib that has
been posted a few times must be used or opendir and readdir will be
undefined.

readr.c:
--------------------------
/*
 * readr - /bin/mail and msgs interface and associated functions.
 */

static char	*SccsId = "@(#)readr.c	2.26	5/3/83";

#include "rparams.h"
#ifdef MITRE
#include <ndir.h>
#endif

static char	lbuf[BUFLEN*2];

#define	saveart	oobit = bit;strcpy(ofilename1, filename);strcpy(ogroupdir, groupdir);hbufcp(&hbuf1, &h);ongsize = pngsize
#define NLINES(h, fp) (h.numlines[0] ? h.intnumlines : (h.intnumlines=linecnt(fp),sprintf(h.numlines, "%d", h.intnumlines), h.intnumlines))

#ifdef MITRE
/*	Some systems don't have a /usr/tmp because it is a nuisance to
	have two publically writable directories to clear out
	periodically and /tmp is a file system of adequate size.
*/
char *tft = "/tmp/folXXXXXX";
#else
char *tft = "/usr/tmp/folXXXXXX";
#endif

static int	hascaught = 0;
static catchintr()
{
	hascaught = 1;
	printf("\n");
	fflush(stdout);
}

/*
 * These were made static for u370 with its buggy cc.
 * I judged it better to have one copy with no ifdefs than
 * to conditionally compile them as automatic variables
 * in readr (which they originally were).  Performance
 * considerations might warrent moving some of the simple
 * things into register variables, but I don't know what
 * breaks the u370 cc.
 */
static char goodone[BUFLEN];		/* last decent article		*/
static char ogroupdir[BUFLEN];		/* last groupdir		*/
static char address[PATHLEN];		/* for reply copy		*/
static char edcmdbuf[128];
static char folbuf[160];
static int rfq = 0;			/* for last article		*/
static long ongsize;			/* Previous ngsize		*/
static long pngsize;			/* Printing ngsize		*/
static char *bptr;			/* temp pointer.		*/
static struct srec srec;		/* srec for sys file entries	*/
static char *tfilename;			/* temporary file name 		*/
static char ofilename1[BUFLEN];		/* previous file name		*/
static struct hbuf hbuf1, hbuf2, *hptr;	/* for minusing			*/
static char *ptr1, *ptr2, *ptr3;	/* for reply manipulation	*/
static int  news = 0;
static int  abs = FALSE;		/* TRUE if we asked absolutely	*/
static char *ed, tf[100];
static struct hbuf h;			/* ditto.			*/
static int i;
static int oobit;			/* last bit, really		*/
static char *oldsig;
static int dgest = 0;
static FILE *ofp;			/* Current output file to terminal*/
static FILE *fp;			/* current article to be printed*/
static int holdup;			/* 1 iff should stop before hdr */
static int ignorenews;			/* 1 iff readnews -p > /dev/null*/
static long timelastsaved;		/* time newsrc last written out */

int catchcont();

readr()
{

#ifdef DEBUG
	fprintf(stderr, "readr()\n");
#endif
	if (aflag) {
		if (*datebuf) {
			if ((atime = cgtdate(datebuf)) == -1)
				xerror("Cannot parse date string");
		} else 
			atime = 0L;
	}

	if (pflag && ignoring())
		ignorenews = TRUE;

	if (uflag)
		time(&timelastsaved);

	ofp = stdout;
	if (cflag && coptbuf[0] != '\0') {
		umask(022);
		mktemp(outfile);	/* get "unique" file name */
		ofp = xfopen(outfile, "w");
		umask(N_UMASK);
		cflag = FALSE;
		pflag = TRUE;
	}

	/* loop reading articles. */
	fp = NULL;
	obit = -1;
	nextng();
	for ( ;; ) {
		if (getnextart(FALSE))
			break;
#ifdef DEBUG
		printf("after getnextart, fp %x, pos %d, bit %d, group '%s', filename '%s'\n",
			fp, ftell(fp), bit, groupdir, filename);
#endif
		strcpy(goodone, filename);
		if (pflag || lflag || eflag) {
			/* This code should be gotten rid of */
			if (sigtrap) {
				qfflush(ofp);
				fprintf(ofp, "\n");
				cdump(ofp);
				_exit(0); /* kludge! drop when qfflush works */
				return;
			}
			clear(bit);
			nextbit();
			if (fp) {
				fclose(fp);
				fp = NULL;
			}
			continue;
		}
		for ( ;; ) {
			char *pp;
#ifdef	SIGCONT
			int (*ocont)();
#endif
			sigtrap = FALSE;
			if (!cflag) {
				if (rfq)
					fprintf(ofp, "Last article.  [qfr] ");
				else
					fprintf(ofp, "(%d lines) More? [ynq] ", NLINES(h, fp));
			} else
				fprintf(ofp, "? ");
			fflush(ofp);
			bptr = lbuf;
#ifdef SIGCONT
			ocont = signal(SIGCONT, catchcont);
#endif
			pp = fgets(bptr, BUFLEN, stdin);
#ifdef SIGCONT
			signal(SIGCONT, ocont);
#endif
			if (pp != NULL)
				break;
			if (!sigtrap)
				return;
#ifdef SIGCONT
			if (sigtrap != SIGCONT)
#endif
				fprintf(ofp, "\n");
		}
		nstrip(bptr);
		while (*bptr == ' ' || *bptr == '\t')
			bptr++;
		i = command();
		if (i)
			break;
	}

	if (!news)
		fprintf(stderr, "No news.\n");
	cout(ofp);
}


#define EOL() if (*bptr != '\0') { fprintf(ofp, "? for commands.\n"); return FALSE; }
/*
 * Process one command, which has already been typed in.
 */
command()
{
	char *findhist();

	switch (i = *bptr++) {

	/* No.  Go on to next article. */
	case 'n':
		EOL();
		itsbeenseen(h.ident);
		readmode = NEXT;
		if (!cflag) {
			fclose(fp);
			fp = NULL;
		}
		fprintf(ofp, "\n");
		clear(bit);
		saveart;
		nextbit();
		break;

	/* Undigestify the article. */
	case 'd':
		dgest = 1;
		/* fall through */

	/* yes: print this article, go on. */
	case 'y':
		EOL();
		/* fall through. */

	/* The user hit return.  Default is 'y' unless rfq, then it's 'q'. */
	case '\0':
		if (!bptr[-1] && rfq)
			return;
		readmode = NEXT;
		showtail(fp);
		clear(bit);
		saveart;
		nextbit();
		break;

	/*
	 * Unsubscribe to the newsgroup and go on to next group
	 */
	case 'u':
		fprintf(ofp, "To unsubscribe, use 'U'\n");
		break;

	case 'U':
		fprintf(ofp, "Unsubscribing to newsgroup: %s\n", groupdir);
		obit = -1;
		if (fp != NULL) {
			fclose(fp);
			fp = NULL;
		}
		if (cflag)
			clear(bit);
		else
			putc('\n', ofp);
		rfq = 0;
		zapng = TRUE;
		saveart;
		if (nextng()) {
			if (actdirect == BACKWARD)
				fprintf(ofp, "Can't back up.\n");
			else
				return TRUE;
		}
		break;

		/* Print the current version of news */
	case 'v':
		fprintf(ofp, "News version: %s\n", news_version);
		break;

		/* reprint the article */
	case 'p':
		EOL();
		if (!cflag)
			goto minus;
		readmode = NEXT;
		if (!cflag) {
			fclose(fp);
			fp = NULL;
			bit = last;
			putc('\n', ofp);
		}
		obit = -1;
		break;

		/* decrypt joke */
	case 'D': 
		caesar_command();
		readmode = NEXT;
		clear(bit);
		saveart;
		nextbit();
		break;

		/* write out the article someplace */
	case 's':
	case 'w':
		{
		char *grn = groupdir;
		tfilename = filename;
		if (*bptr == '-') {
			bptr++;
			grn = ogroupdir;
			if (*ofilename1)
				tfilename = ofilename1;
		}
		if (*bptr != '\0' && *bptr != ' ') {
			fprintf(ofp, "Bad file name.\n");
			break;
		}
		while (*bptr == ' ')
			bptr++;
		if (*bptr != '|' && *bptr != '/') {
			char	hetyped[BUFLEN];
			char	*boxptr;
			strcpy(hetyped, bptr);
			if (boxptr = getenv("NEWSBOX"))
				if (index(boxptr, '%'))
					sprintf(bptr, boxptr, grn);
				else
					strcpy(bptr, boxptr);
			else if (hetyped[0] == '~' && hetyped[1] == '/') {
				strcpy(hetyped, bptr+2);
				strcpy(bptr, userhome);
			} else
				strcpy(bptr, ".");
			strcat(bptr, "/");
			if (hetyped[0] != '\0')
				strcat(bptr, hetyped);
			else
				strcat(bptr, "Articles");
		}
		fwait(fsubr(save, tfilename, bptr));
		}
		break;

		/* back up  */
	case '-':
minus:
		rfq = 0;
		abs = TRUE;
		if (!*ofilename1) {
			fprintf(ofp, "Can't back up.\n");
			break;
		}
		if (cflag)
			clear(bit);
		else {
			fclose(fp);
			fp = NULL;
			putc('\n', ofp);
		}
		hbufcp(&hbuf2, &h);
		hbufcp(&h, &hbuf1);
		hbufcp(&hbuf1, &hbuf2);
		strcpy(bfr, filename);
		strcpy(filename, ofilename1);
		strcpy(ofilename1, bfr);
		obit = bit;
		if (strcmp(groupdir, ogroupdir)) {
			strcpy(bfr, groupdir);
			selectng(ogroupdir);
			strcpy(groupdir, ogroupdir);
			strcpy(ogroupdir, bfr);
			ngrp = 1;
			back();
		}
		bit = oobit;
		oobit = obit;
		obit = -1;
		getnextart(TRUE);
		return FALSE;

		/* skip forwards */
	case '+':
caseplus:
		if (*bptr == '\0')
			strcat(bptr, "1");
		rfq = 0;
		if (cflag)
			clear(bit);
		saveart;
		last = bit;
		for (i = 0; i < atoi(bptr); i++) {
			nextbit();
			if ((bit > pngsize) || (rflag && bit < 1))
				break;
		}
		if (!cflag) {
			putc('\n', ofp);
			fclose(fp);
			fp = NULL;
		}
		obit = -1;
		break;

	/* exit - time updated to that of most recently read article */
	case 'q':
		EOL();
		return TRUE;

	/* exit - no time update. */
	case 'x':
		EOL();
		xxit(0);

	/* cancel the article. */
	case 'c':
		cancel_command();
		break;

	/* escape to shell */
	case '!':
		fwait(fsubr(ushell, bptr, (char *)NULL));
		fprintf(ofp, "\n");
		hdr();
		break;

	/* mail reply */
	case 'r':
		reply_command();
		break;

	/* send to some system */
	case 'X':
		xmit_command();
		break;

	/* next newsgroup */
	case 'P':
		*bptr = '-';
	case 'N':
		if (fp != NULL) {
			fclose(fp);
			fp = NULL;
		}
		if (next_ng_command())
			return TRUE;
		break;

	/* specific no. */
	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
		sscanf(--bptr, "%d", &i);
		if (i == 0) {
			fprintf(ofp, "Bad article no.\n");
			break;
		}
		if (i > pngsize) {
			fprintf(ofp, "Not that many articles.\n");
			break;
		}
		readmode = SPEC;
		abs = TRUE;
		bit = i;
		obit = -1;
		if (!cflag) {
			putc('\n', ofp);
			fclose(fp);
			fp = NULL;
		}
		rfq = 0;
		break;

	/* specific message ID. */
	case '<':
		ptr1 = findhist(--bptr);
		if (ptr1 == NULL) {
			fprintf(ofp, "No such article: %s.\n", bptr);
			break;
		}
		ptr2 = index(ptr1, '\t');
		ptr3 = index(++ptr2, '\t');
		ptr2 = index(++ptr3, ' ');
		if (ptr2)
			*ptr2 = '\0';
		ptr2 = index(ptr3, '/');
		*ptr2++ = '\0';
		abs = TRUE;
		if (cflag)
			clear(bit);
		else {
			fclose(fp);
			fp = NULL;
			putc('\n', ofp);
		}
		hbufcp(&hbuf1, &h);
		saveart;
		strcpy(ogroupdir, ptr3);
		if (strcmp(groupdir, ogroupdir)) {
			strcpy(bfr, groupdir);
			selectng(ogroupdir);
			strcpy(groupdir, ogroupdir);
			strcpy(ogroupdir, bfr);
			back();
		}
		sscanf(ptr2, "%d", &bit);
		oobit = obit;
		obit = -1;
		getnextart(TRUE);
		rfq = 0;
		break;

	/* follow-up article */
	case 'f':
		followup_command();
		break;

	/* erase - pretend we haven't seen this article. */
	case 'e':
		if (rfq || *bptr == '-') {
			if (strcmp(groupdir, ogroupdir)) {
				i = bit;
				strcpy(bfr, groupdir);
				selectng(ogroupdir);
				set(oobit);
				printf("Holding article %d newsgroup %s\n", oobit, ogroupdir),
				strcpy(groupdir, ogroupdir);
				selectng(bfr);
				bit = i;
			} else {
				printf("Holding article %d\n", oobit),
				set(oobit);
			}
		} else {
			printf("Holding article %d\n", bit),
			set(bit);
			goto caseplus;	/* skip this article for now */
		}
		break;

	case 'H':
	case 'h':
		if (!hflag)
			dash(8, ofp);
		if (*bptr == '-') {
			if (oobit > 0)
				fprintf(ofp, "Article %d:\n", oobit);
			hprint(&hbuf1, ofp, 1 + (i=='H'));
		} else {
			fprintf(ofp, "Article %d of %ld: %s\n",
				rfq ? oobit : bit, pngsize, h.ident);
			hprint(&h, ofp, 1 + (i=='H'));
		}
		if (!hflag)
			dash(8, ofp);
		break;

	case '#':
		fprintf(ofp, "Article %d of %ld: newsgroup %s\n",
			rfq ? oobit : bit, pngsize, rfq ? ogroupdir : groupdir);
		break;

		/* error */
	case '?':
		help(ofp);
		break;
	default:
		fprintf(ofp, "? for commands.\n");
		break;
	}

	return FALSE;
}

cancel_command()
{
	tfilename = filename;
	hptr = &h;
	if (*bptr == '-') {
		if (*ofilename1) {
			tfilename = ofilename1;
			hptr = &hbuf1;
		}
		bptr++;
	}
	EOL();
	readmode = SPEC;
	strcpy(rcbuf, hptr->path);
	ptr1 = index(rcbuf, ' ');
	if (ptr1)
		*ptr1 = 0;
	if (uid == ROOTID)
		i = 0;		/* root gets to cancel */
	else
		i = strcmp(username, rcbuf);
	if (i != 0) {
		fprintf(ofp, "Can't cancel what you didn't write.\n");
		return;
	}
	if (!cancel(ofp, hptr, i) && hptr == &h) {
		clear(bit);
		saveart;
		nextbit();
		obit = -1;
		fp = NULL;
		if (!cflag)
			putc('\n', ofp);
	}
	if (fp != NULL)
		fclose(fp);
	fp = NULL;
}

reply_command()
{
	register char	*pathptr, *ptr;
	int edit = 1;
	char *ed;
	FILE *tfp;
	char	curberk[BUFLEN];
	char *replyname();
	char subj[100];
	char folbuf[100];
	extern char MAILPARSER[];

	hptr = &h;
	while (*bptr && index("d-", *bptr)) {
		switch (*bptr) {
		/* Followup the previous article. */
		case '-':
			hptr = &hbuf1;
			break;

		/* Don't edit the headers */
		case 'd':
			edit = 0;
			break;
		}
		bptr++;
	}
	EOL();
	if (edit && access(MAILPARSER, 1)) {
#ifdef IHCC
		fprintf(stderr, "Can't edit headers, 'recmail' missing.\n");
#else
		fprintf(stderr, "Can't edit headers without %s\n", MAILPARSER);
#endif
		edit = 0;
	}

	*rcbuf = '\0';
	*curberk = '\0';
	pathptr = replyname(hptr);;
	ptr = pathptr - 1;
	i = 0;
	for (ptr1 = address, ptr2 = pathptr; *ptr2; ptr1++, ptr2++) {
		if (index("\"\\$", *ptr2))
			*ptr1++ = '\\';
		*ptr1 = *ptr2;
	}
	*ptr1 = '\0';

	folbuf[0] = 0;				/* References */
	if (hptr->followid[0]) {
		strcpy(folbuf, hptr->followid);
#ifdef MITRE
		/*	The usenet standards document (doc/standard
			in the 2.10 distribution) specifies that
			followups should use blanks to separate the
			references in the header.	*/
		strcat(folbuf, " ");
#else
		strcat(folbuf, ", ");
#endif
	}
	strcat(folbuf, hptr->ident);

	strcpy(subj, hptr->title);	/* Subject */
	while (isspace(*bptr))
		bptr++;
	if (*bptr != '\0')
		strcpy(subj, bptr);
	if (!prefix(subj, "Re:") && !prefix(subj, "re:")) {
		strcpy(bfr, subj);
		sprintf(subj, "Re: %s", bfr);
	}
	if (!edit) {
		fprintf(ofp, "To: %s\n", pathptr);
		ed = index(MAILER, '%');
		if (ed && ed[1] == 's')
			fprintf(ofp, "Subject: %s\n", subj);
		fflush(ofp);
	}

	/* Put the user in the editor to create the body of the followup. */
	if (edit) {
		strcpy(tf, tft);
		mktemp(tf);

		ed = getenv("EDITOR");
		if (ed == NULL)
			ed = DFTEDITOR;

#ifdef MITRE
		/*	The original code failed to test the value
			returned by fopen when using the template
			tft.  This is a bad practice, since fopen
			for a file could fail due to kernel table
			exhaustion or an existing file of the same
			name with no permission, etc.
		*/
		if ((tfp = fopen(tf, "w")) == NULL)
			perror(tf);
		else {
			fprintf(tfp, "To: %s\n", pathptr);
			fprintf(tfp, "Subject: %s\n", subj);
			fprintf(tfp, "References: %s\n\n", folbuf);
			fclose(tfp);
		}
#else
		tfp = fopen(tf, "w");
		fprintf(tfp, "To: %s\n", pathptr);
		fprintf(tfp, "Subject: %s\n", subj);
		fprintf(tfp, "References: %s\n\n", folbuf);
		fclose(tfp);
#endif

		sprintf(edcmdbuf, "%s %s", ed, tf);
		system(edcmdbuf);
		strcpy(rcbuf, MAILPARSER);
		strcat(rcbuf, " -t");
		strcat(rcbuf, " < ");
		strcat(rcbuf, tf);
		if (access(tf, 4)) {
			fprintf(stderr, "Reply not sent: no input file.\n");
			return;
		}
		printf("Sending reply.\n");
		fflush(stdout);
		if (fork() == 0) {
			system(rcbuf);
			unlink(tf);
			_exit(0);
		}
	} else {
		sprintf(rcbuf, MAILER, hptr->title);
		sprintf(bfr, "%s %s", rcbuf, address);
		system(bfr);
	}
	hdr();
}

xmit_command()
{
	tfilename = filename;
	if (*bptr == '-') {
		if (*ofilename1)
			tfilename = ofilename1;
		bptr++;
	}
	if (*bptr != '\0' && *bptr != ' ') {
		fprintf(ofp, "Bad system name.\n");
		return;
	}
	while (*bptr == ' ')
		bptr++;
	if (*bptr == '\0') {
		fprintf(ofp, "Missing system name.\n");
		return;
	}
	if (s_find(&srec, bptr) == NULL) {
		fprintf(ofp, "%s not in SYSFILE\n", bptr);
		return;
	}
	transmit(&srec, tfilename);
}

next_ng_command()
{
	if (!*bptr || *bptr == '-') {
		obit = -1;
		if (cflag)
			clear(bit);
		else
			putc('\n', ofp);
		if (*bptr)
			actdirect = BACKWARD;
		rfq = 0;
		saveart;
		if (nextng()) {
			if (actdirect == BACKWARD)
				fprintf(ofp, "Can't back up.\n");
			else
				return TRUE;
		}
		return FALSE;
	}
	while (isspace(*bptr))
		bptr++;
	if (!validng(bptr)) {
		fprintf(ofp, "No such group.\n");
		return FALSE;
	}
	obit = -1;
	if (cflag)
		clear(bit);
	else
		putc('\n', ofp);
	readmode = SPEC;
	rfq = 0;
	saveart;
	back();
	selectng(bptr);
	return FALSE;
}

followup_command()
{
	int edit = 1;
	char subj[100];
	char folbuf[100];
	char *ng;
	FILE *tfp;

	hptr = &h;

	while (*bptr && index("d-", *bptr)) {
		switch (*bptr) {
		/* Followup the previous article. */
		case '-':
			hptr = &hbuf1;
			break;

		/* Don't edit the headers */
		case 'd':
			edit = 0;
			break;
		}
		bptr++;
	}

	/* Figure out the subject, newsgroups, and references for the followup. */
	ng = hptr->nbuf;			/* Newsgroups */
	if (hptr->followto[0])
		ng = hptr->followto;
	launder(ng);

	folbuf[0] = 0;				/* References */
	if (hptr->followid[0]) {
		strcpy(folbuf, hptr->followid);
#ifdef MITRE
		/*	The usenet standards document (doc/standard
			in the 2.10 distribution) specifies that
			followups should use blanks to separate the
			references in the header.	*/
		strcat(folbuf, " ");
#else
		strcat(folbuf, ", ");
#endif
	}
	strcat(folbuf, hptr->ident);

	strcpy(subj, hptr->title);	/* Subject */
	while (isspace(*bptr))
		bptr++;
	if (*bptr != '\0')
		strcpy(subj, bptr);
	if (!prefix(subj, "Re:") && !prefix(subj, "re:")) {
		strcpy(bfr, subj);
		sprintf(subj, "Re: %s", bfr);
	}

	/* Determine the command line for the shell. */
	if (edit) {
		sprintf(bfr, "%s -h -D", FOLLOWUP);
	} else {
		sprintf(bfr, "%s -D -F '%s' -n %s -t \'", FOLLOWUP, folbuf, ng);
		strqcat(bfr, subj);
		strcat(bfr, "\'");
	}

	/* backslash special characters */
	for (ptr1 = rcbuf, ptr2 = bfr; *ptr2; ptr1++, ptr2++) {
		if (index("\\", *ptr2))
			*ptr1++ = '\\';
		*ptr1 = *ptr2;
	}
	*ptr1 = '\0';

	/* Let the user know what's going on. */
	fprintf(ofp, "Posting followup article to network.  Please use\n");
	fprintf(ofp, "reply ('r') instead unless your article is of general\n");
	fprintf(ofp, "interest.  (To abort press BREAK.)\n");
	fprintf(ofp, "Subject: %s\n", subj);
	fprintf(ofp, "Newsgroups: %s\n", ng);
	fprintf(ofp, "Hit <return> to continue, BREAK to abort: ");
	fflush(ofp);

	/* Give the user a chance to hit BREAK and back out. */
	hascaught = 0;
	oldsig = (char *) signal(SIGINT, catchintr);
	gets(edcmdbuf);
	signal(SIGINT, oldsig);
	if (hascaught)
		return;

	/* Play obnoxious warnings, if necessary. */
	if (recording(hptr->nbuf, 0))
		return;

	/* Put the user in the editor to create the body of the followup. */
	ed = getenv("EDITOR");
	if (ed == NULL || *ed == '\0')
		ed = DFTEDITOR;
	if (ed) {
		strcpy(tf, tft);
		mktemp(tf);

#ifdef MITRE
		/*	The original code failed to test the value
			returned by fopen when using the template
			tft.  This is a bad practice, since fopen
			for a file could fail due to kernel table
			exhaustion or an existing file of the same
			name with no permission, etc.
		*/

		if ((tfp = fopen(tf, "w")) == NULL)
			perror(tf);
		else {
			if (edit) {
				fprintf(tfp, "Newsgroups: %s\n", ng);
				fprintf(tfp, "Subject: %s\n", subj);
				fprintf(tfp, "References: %s\n", folbuf);
				if (hptr->keywords[0])
					fprintf(tfp, "Keywords: %s\n",
						hptr->keywords);
				fprintf(tfp, "\n");
			}
			fclose(tfp);
		}
#else
		tfp = fopen(tf, "w");
		if (edit) {
			fprintf(tfp, "Newsgroups: %s\n", ng);
			fprintf(tfp, "Subject: %s\n", subj);
			fprintf(tfp, "References: %s\n", folbuf);
			if (hptr->keywords[0])
				fprintf(tfp, "Keywords: %s\n", hptr->keywords);
			fprintf(tfp, "\n");
		}
		fclose(tfp);
#endif

		sprintf(edcmdbuf, "%s %s", ed, tf);
		system(edcmdbuf);
		strcat(rcbuf, "< ");
		strcat(rcbuf, tf);
		if (access(tf, 4)) {
			fprintf(stderr, "Article not posted: no input file.\n");
			return;
		}
		printf("Posting article.\n");
		fflush(stdout);
		if (fork() == 0) {
			system(rcbuf);
			unlink(tf);
			_exit(0);
		}
	} else {
		printf("%s\n", rcbuf);
		system(rcbuf);
	}
	hdr();
}

caesar_command()
{
	char	temp[100];
	char	*pp = bptr;
	FILE	*pfp, *popen();

	fprintf(stderr, "Caesar decoding:\n");
	strcpy(temp, CAESAR);
	if (*bptr) {
		strcat(temp, " ");
		strcat(temp, bptr);
	}
	if (NLINES(h, fp) > LNCNT && *PAGER) {
		strcat(temp, " | ");
		strcat(temp, PAGER);
	}
	pfp = popen(temp, "w");
	tprint(fp, pfp, FALSE);
	itsbeenseen(h.ident);
	fclose(fp);
	fp = NULL;
	pclose(pfp);
}

/*
 * Show the user the tail, if any, of the message on file
 * descriptor fd, and close fd.  The digester is considered,
 * and the pager is used if appropriate.
 */
showtail(fd)
FILE *fd;
{
	if (fd == NULL)
		return;

	if (dgest) {
		digest(fd, ofp, &h);
	} else if (!lflag && !pflag && !eflag) {
#ifdef PAGE
		/* Filter the tail of long messages through PAGER. */
		if (NLINES(h, fd) > LNCNT && *PAGER) {
			if (!index(PAGER, FMETA)) {
				FILE *pfp, *popen();
				int	cnt;

				pfp = popen(PAGER, "w");
				if (pfp == NULL)
					pfp = ofp;
				/*
				 * What follows is an attempt to prevent the
				 * next message from scrolling part of this
				 * message off the top of the screen before
				 * the poor luser can read it.
				 */
				tprint(fd, pfp, FALSE);
				itsbeenseen(h.ident);
				pclose(pfp);
			} 
			else
				pout(ofp);
			holdup = TRUE;
#ifndef MITRE
			fprintf(ofp, ":");
			fflush(ofp);
#endif
		}
		else
#endif
			tprint(fd, ofp, FALSE), itsbeenseen(h.ident);
	}
	fclose(fd);
}

/*
 * Find the next article we want to consider, if we're done with
 * the last one, and show the header.
 */
getnextart(minus)
int minus;
{
#ifdef MITRE
	int noaccess = 0;
	DIR *dfp;
	struct direct *entry;
	long nextnum, tnum;
	long atol();
#endif
	if (minus)
		goto nextart2;	/* Kludge for "-" command. */

	if (bit == obit)	/* Return if still on same article as last time */
		return 0;

	sigtrap = FALSE;

nextart:
	dgest = 0;
	if (bit < 1 && !rflag)
		bit = 1;

	/* If done with this newsgroup, find the next one. */
	while (((long) bit > ngsize) || (rflag && bit < 1)) {
		int i;
		if (i=nextng()) {
			if (actdirect == BACKWARD) {
				fprintf(ofp, "Can't back up.\n");
				actdirect = FORWARD;
				continue;
			} 
			else if (rfq++ || pflag || cflag)
				return 1;
		}
		if (rflag)
			bit = ngsize + 1L;
		else
			bit = -1;
		if (uflag) {
			long now;
			time(&now);
			if (now - timelastsaved > 5*60 /* 5 minutes */) {
				printf("[Saving .newsrc]\n");
				fflush(stdout);
				writeoutrc();
				timelastsaved = now;
			}
		}
	}

nextart2:
#ifdef DEBUG
	fprintf(stderr, "article: %s/%d\n", groupdir, bit);
#endif
	if (rcreadok)
		rcreadok = 2;	/* have seen >= 1 article */
	sprintf(filename, "%s/%d", dirname(groupdir), bit);
	if (rfq && goodone[0])
		strcpy(filename, goodone);
	if (sigtrap) {
		if (sigtrap == SIGHUP)
			return 1;
		if (!rcreadok)
			xxit(0);
		fprintf(ofp, "Abort (n)?  ");
		fflush(ofp);
		gets(bfr);
		if (*bfr == 'y' || *bfr == 'Y')
			xxit(0);
		sigtrap = FALSE;
	}
#ifdef DEBUG
	fprintf(stderr, "filename = '%s'\n", filename);
#endif
	/* Decide if we want to show this article. */
#ifdef MITRE
	/*	This modification is to speed up readnews when you
		first start reading a group for the first time.
		On sites that expire articles, you eventually get
		lots of articles that don't exist.  You start
		reading news for the first time, and you get to a
		popular group with current articles in the 1000's,
		and readnews says "Oh, an new group! Let's see,
		is article 1 there? nope. Is article 2 there? nope.
		Is article 3 there? nope..." and so on and so on,
		so it does over 1000 calls to access(2), which is
		really a waste.  This modification solves this.
		When readnews finds 5 consecutive articles that
		don't exist, it hauls off and opens up the directory,
		and reads it in and finds the next article to show.
	*/
	if (access(filename, 4)) {
		/* since there can be holes in legal article numbers, */
		/* we wait till we hit 5 consecutive bad articles */
		/* before we haul off and scan the directory */
		if (++noaccess < 5)
			goto badart;
		dfp = opendir(dirname(groupdir));
		if (dfp == (DIR *) NULL) {
#ifdef	DEBUG
	fprintf(stderr, "can't open groupdir (%s)\n", dirname(groupdir));
#endif
			noaccess = 0;
			goto badart;
		}
		nextnum = rflag ? 0 : ngsize;
		while ((entry = readdir (dfp)) != NULL) {
			tnum = atol(entry->d_name);
#ifdef	DEBUG
	fprintf(stderr, "art %s (%ld) next %ld\n",
		entry->d_name, tnum, nextnum);
#endif	DEBUG
			if (tnum <= 0)
				continue;
			if (rflag ? (tnum > nextnum && tnum < bit)
				  : (tnum < nextnum && tnum > bit))
				nextnum = tnum;
		}
		closedir(dfp);
		if (rflag ? (nextnum >= bit) : (nextnum <= bit))
			goto badart;
		do {
			clear(bit);
			nextbit();
		} while (rflag ? (nextnum < bit) : (nextnum > bit));
		obit = -1;
		abs = FALSE;
		goto nextart;
#ifdef	DEBUG
	fprintf(stderr, "bit %d nextnum %ld\n", bit, nextnum);
#endif	DEBUG
	} else
		noaccess = 0;
#endif
	if (ignorenews
#ifndef MITRE
	|| access(filename, 4)
#endif
	|| ((fp = fopen(filename, "r")) == NULL)
	|| (hread(&h, fp, TRUE) == NULL)
	|| (!rfq && !select(&h, abs))) {
#ifdef MITRE
	badart:
#endif
#ifdef DEBUG
		fprintf(stderr, "Bad article '%s'\n", filename);
#endif
		if (fp != NULL) {
			fclose(fp);
			fp = NULL;
		}
		clear(bit);
		obit = -1;
		nextbit();
		abs = FALSE;
		goto nextart;
	}
	abs = FALSE;
	actdirect = FORWARD;
	news = TRUE;
	hdr();
	if ((cflag && !lflag && !eflag) || pflag)
		tprint(fp, ofp, FALSE);
#ifdef MITRE
	/*	Readnews with the -c, -l, and -e options will list
		each article posted to multiple news groups once
		for each group, rather than just once for the
		session.	*/
	if (cflag || lflag || eflag || pflag) {
#else
	if (cflag && lflag && eflag || pflag) {
#endif
		itsbeenseen(h.ident);
		sigtrap = FALSE;
		fclose(fp);
		fp = NULL;
	}
	obit = bit;
	return 0;
}

/*
 * Print out whatever the appropriate header is
 */
hdr()
{
	if (rfq)
		return;

	/* Wait for user to read previous article. */
	if (holdup) {
		holdup = FALSE;
#ifdef MITRE
		/*	If the last article is piped through the pager,
			it prints a ':', but does not wait for any input.
		*/
		fprintf(ofp, ":");
		fflush(ofp);
#endif
		gets(bfr);
		if (bfr[0])
			explaincolon();
	}

	if (lflag || eflag) {
		hprint(&h, ofp, 0);
		return;
	}

	/* Print out a header */
	if (ngrp) {
		pngsize = ngsize;
		ngrp--;
		nghprint(groupdir);
	}
	if (!hflag)
#ifdef MITRE
		/*	Trivial fix to allow header line to show the
			current newsgroup (which can be forgotten in
			long newsgroups).	*/
		fprintf(ofp, "Article %d of %ld, %s; %s.\n",
			bit, pngsize, groupdir, briefdate(h.subdate));
#else
		fprintf(ofp, "Article %d of %ld, %s.\n",
			bit, pngsize, briefdate(h.subdate));
#endif
	hprint(&h, ofp, pflag ? 1 : 0);
}

explaincolon()
{
	fprintf(ofp, "\n'%s' ignored.\n", bfr);
	fprintf(ofp, "The colon is to give you a chance to finish reading the\n");
	fprintf(ofp, "previous article before the next header scrolls it off\n");
	fprintf(ofp, "the top of the screen.  You should hit `return' or `newline'\n");
	fprintf(ofp, "when you are ready to go on to the next article.\n\n");
	fflush(ofp);
}

nghprint(title)
char *title;
{
	char *tstr = "Newsgroup ";
	int l = strlen(title) + strlen(tstr);

	fprintf(ofp, "\n");
	if (!hflag) {
		dash(l, ofp);
		fprintf(ofp, "%s%s\n", tstr, title);
		dash(l, ofp);
	} else {
		fprintf(ofp, "%s%s, ", tstr, title);
		if (bit == pngsize)
#ifdef MITRE
			/*	The variable pngsize is long, yet
				this prints it via %d instead of %ld.
			*/
			fprintf(ofp, "%ld\n", pngsize);
#else
			fprintf(ofp, "%d\n", pngsize);
#endif
		else
#ifdef MITRE
			/*	The variable pngsize is long, yet
				this prints it via %d instead of %ld.
			*/
			fprintf(ofp, "%d-%ld\n", bit, pngsize);
#else
			fprintf(ofp, "%d-%d\n", bit, pngsize);
#endif
	}
	fprintf(ofp, "\n");
}

/*
 * Routine to catch a continue signal.
 */
catchcont()
{
#ifdef SIGCONT
	signal(SIGCONT, catchcont);
	sigtrap = SIGCONT;
#endif
	hdr();
}