/*
 * readnews
 *
 *	Michael Rourke (UNSW) April 1984
 */

#include "defs.h"

#define ARTSEP "/"

char admsub[BUFLEN]	 = "general";
char dfltsub[BUFLEN]	 = "general";
char mailvia[BUFLEN]	 = "";
char *mailpath	 = MAIL;

#define	MAXARGV	10		/* used in building argv[]s below */

bool iflag;		/* -i ignore .newsrc */
bool lflag;		/* -l print headers only */
bool cflag;		/* -c check for news only */
bool pflag;		/* -p print everything selected */
bool Cflag;		/* -C verbose -c */
bool sflag;		/* -s print newsgroup subscription list */
bool splus;		/* -s+ */
bool sminus;		/* -s- */
char *sarg;		/* arg to -s[+-] */
char *nflag;		/* -n newsgroups */
extern char *rcgrps;	/* -n newsgroups from newsrc file */
bool n_on_cline;	/* nflag set on command line */
char *disable;		/* -d disabled commands */

extern newsrc	*rc;		/* internal .newsrc */

active *alist;		/* internal active list */

long now;		/* current time */
bool interrupt;		/* if interrupt hit */
char *newsdir;		/* %news */
bool su;		/* if super user (not used) */

applycom list(), check(), commands();
void onintr();
bool ureject(), seen(), subs(), subsub();

char *progname;

main(argc, argv)
int argc;
char *argv[];
{
	char buf[BUFSIZ], *p;

	progname = argv[0];
	setbuf(stdout, buf);
	if (options(argc, argv, true) < 0) {
		(void) fprintf(stderr,
	"Usage: readnews [-n newsgroups] [-i] [-clpCL] [-Agroup] [-Dgroup]\n");
		exit(1);
	}
	now = time(&now);

	newsdir = newstr(fullartfile((char *)NULL));
	getctl();

	if (!iflag)
		readnewsrc();

	if (nflag)
		convgrps(nflag);
	else
		nflag = dfltsub;
	if (rcgrps)
		convgrps(rcgrps);
	if (!n_on_cline && !ngmatch(admsub, nflag))
		nflag = newstr3(admsub, NGSEPS, nflag);
	if ((int) sflag + (int) lflag + (int) cflag + (int) pflag > 1)
		error("-clpsC flags are mutually exclusive.");

	/* user has private mailer? */
	if ((p = getenv("MAILER")) != NULL)
		mailpath = newstr(p);

	alist = readactive();

	if (sflag) {
		if (subs() && !iflag)
			writenewsrc(alist);
	} else if (lflag)
		apply(alist, nflag, list, false);
	else if (cflag)
		apply(alist, nflag, check, false);
	else {
		if (!pflag) {
			if (signal(SIGINT, SIG_IGN) != SIG_IGN)
				(void) signal(SIGINT, onintr);
			if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
				(void) signal(SIGQUIT, onintr);
		}
		apply(alist, nflag, commands, true);
		if (!iflag)
			writenewsrc(alist);
	}
	fflush(stdout);
	exit(0);
}

#if MANGRPS
/*
 * if newsgroup "ng" isn't subscribed to, add it to subscription list
 */
addsub(ng, slist)
char *ng;
char **slist;
{
	if (!ngmatch(ng, *slist))
		*slist = newstr3(ng, NGSEPS, *slist);
}
#endif

/* ARGSUSED */
void
onintr(dummy)
int dummy;
{
	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
		(void) signal(SIGINT, onintr);
	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
		(void) signal(SIGQUIT, onintr);
	interrupt = true;
}

/*
 * process options
 * can be called from readnewsrc()
 */
int				/* < 0 failure, otherwise success */
options(argc, argv, cline)
int argc;
char *argv[];
bool cline;
{
	int c;
	extern int optind;
	extern char *optarg;

	optind = 1;			/* reset scan, if necessary */
	while ((c = getopt(argc, argv, "n:d:iLA:D:plcC")) != EOF)
		switch (c) {
		case 'n':
			if (cline)
				nflag = optarg, n_on_cline = true;
			else {
				if (!n_on_cline)
					nflag = (nflag?
						catstr2(nflag, NGSEPS, optarg):
						newstr(optarg));
				rcgrps = (rcgrps?
					catstr2(rcgrps, NGSEPS, optarg):
					newstr(optarg));
			}
			break;
		case 'd':
			disable = (disable?
					catstr2(disable, "", optarg):
					newstr(optarg));
			break;
		case 'i':
			iflag = true; 
			break;
		case 'L':
			sflag = true;
			break;
		case 'A':
			sflag = true;
			splus = true;
			sarg = optarg;
			break;
		case 'D':
			sflag = true;
			sminus = true;
			sarg = optarg;
			break;
		case 'p':
			pflag = true; 
			break;
		case 'l':
			lflag = true; 
			break;
		case 'c':
			cflag = true; 
			break;
		case 'C':
			cflag = Cflag = true; 
			break;
		case '?':
		default:
			return(-1);
			break;
		}

	return(0);
}

/*
 * subscription list handling
 * return true if newsrc is to be re-written
 */
bool
subs()
{
	register newsrc	*np;
	register active	*ap;
	register char *tmp, *com;
	register FILE *f;

	if (splus || sminus) {
		if (strpbrk(sarg, BADGRPCHARS)) {
			(void) printf("%s: Illegal char in newsgroup.\n", sarg);
			return false;
		}
		if (ngmatch(sarg, nflag)) {
			/*
			 * normally we subscribe, check for an exclusion
			 */
			for (np = rc; np; np = np->n_next)
				if (CMP(sarg, np->n_name) == 0)
					break;
			if (np) {
				/*
				 * altering subscribe flag is all
				 * we need to change
				 */
				np->n_subscribe = splus;
				return true;
			}
			if (sminus) {
				/*
				 * try deleting from sub list
				 */
				if (subsub(sarg, rcgrps))
					return true;
				/*
				 * add specific exclusion
				 */
				rcgrps = newstr4(rcgrps, NGSEPS, NEGS, sarg);
				return true;
			}
		} else if (splus) {
			/*
			 * we don't subscribe,
			 * try deleting !sarg first
			 */
			tmp = newstr2(NEGS, sarg);
			subsub(tmp, rcgrps);
			if (!ngmatch(sarg, rcgrps))
				/*
				 * didn't work, so add explicit subscription
				 */
				rcgrps = rcgrps? newstr3(rcgrps, NGSEPS, sarg):
					newstr(sarg);
			return true;
		}
	} else {
		(void) printf("Subscription list: %s", nflag);
		for (np = rc; np; np = np->n_next)
			if (!np->n_subscribe && ngmatch(np->n_name, nflag))
				(void) printf(",!%s", np->n_name);
		(void) printf("\n");
	}
	return false;
}


/*
 * try and delete group from subscription list
 * return true if successful
 */
bool
subsub(grp, slist)
char *grp;
char *slist;
{
	register char *delim;

	while (*slist) {
		if (delim = strchr(slist, NGSEPCHAR))
			*delim = '\0';
		if (CMP(grp, slist) == 0) {
			if (delim)
				(void) strcpy(slist, delim + 1);
			else if (slist[-1] == ',')
				slist[-1] = '\0';
			else
				slist[0] = '\0';
			return true;
		}
		if (delim)
			*delim = NGSEPCHAR, slist = delim + 1;
		else
			break;
	}
	return false;
}

char *
ltoa(l)
long l;
{
	static char buf[30];

	sprintf(buf, "%ld", l);
	return buf;
}

/*
 * list titles command (-l)
 */
applycom
list(ap, np)
active *ap;
newsrc *np;
{
	static active *lastap;
	static bool first = true;
	register char *fname;
	register FILE *f;
	header h;
	ino_t ino;

	np->n_last++;
	fname = convg(newstr5(newsdir, "/", ap->a_name, ARTSEP,
		ltoa(np->n_last)));
	ino = 0;
	f = fopen(fname, "r");
	free(fname);
	if (!f || seen(f, &ino))
		return next;
	gethead(f, &h);
	if (first) {
		(void) printf("News articles:\n");
		first = false;
	}
	if (lastap != ap)
		(void) printf("  %s:\n", ap->a_name);
	lastap = ap;
	(void) printf("    %-4d %s\n", np->n_last, h.h_subject);
	(void) fclose(f);
	freehead(&h);
	if (ino)
		seen(NIL(FILE), &ino);
	return next;
}

/*
 * check command (-c or -C)
 */
applycom
check(ap, np)
active *ap;
newsrc *np;
{
	static bool done;

	np->n_last++;
	if (Cflag) {
		register long num;

		if (!done)
			(void) printf("You have news:\n");
		done = true;
		num = ap->a_seq - np->n_last + 1;
		(void) printf("\t%s at most %ld article%s\n",
			ap->a_name, num, (num > 1? "s": ""));
		return nextgroup;
	} else {
		(void) printf("You have news.\n");
		fflush(stdout);
		exit(0);
		/* NOTREACHED */
	}
}

/*
 * normal command handler (or pflag)
 * commands:
 *
 * \n 		print current article
 * n 		go to next article
 * q		quit
 * r		reply
 * f 		followup
 * p 		postnews
 * N [newsgrp]	next newsgroup
 * s [file]	save
 * U		unsubscribe from group
 * !stuff	shell escape
 * number or .	go to number
 * - 		back to previous article (toggle)
 * x		quick exit
 * h		long header info
 * H		full header
 *
 * inside r, f or p:
 *	.e	edit
 *	.i	interpolate
 *	. or EOT terminate message
 *	.!comd	shell escape
 */
applycom
commands(ap, np, last, pushed)
active *ap;
newsrc *np;
bool last;
bool pushed;
{
	register char *com, *arg;
	register int c, size;
	register long i;
	register FILE *f;
	char *fname;
	header		h;
	newsrc		ntmp;
	ino_t		ino;
	bool printed, pheader, verbose, hadinterrupt;
	applycom	nextact;
	static char errmess[] = "Unknown command; type `?' for help.\n";
	static char form[]    = "%s: %s\n";
	static char savedsys[BUFSIZ / 2];
	static active	*lastap, *rlastap;
	static newsrc	lastn;
	static char number[20];
	static active	*wantap;
	extern char t_from[], t_subject[], t_date[];
	extern char t_newsgroups[], t_path[], t_sender[];
	extern char t_replyto[], t_organization[];
	extern active	*activep();

	if (last) {
		/*
		 * give user one last chance to
		 * see this article
		 */
		ap = rlastap;
		np = &lastn;
		wantap = NIL(active);
		if (!ap || pflag)
			return stop;
	} else if (wantap)
		/*
		 * doing an "n newsgroup" command
		 */
		if (wantap != ap)
			return nextgroup;
		else
			wantap = NULL;

	fname = convg(newstr5(newsdir, "/", ap->a_name, ARTSEP,
		ltoa(np->n_last + 1)));
	f = fopen(fname, "r");
	ino = 0;
	if (!f || !last && !pushed && seen(f, &ino)) {
		if (pushed)
			(void) printf("Article %ld (%s) no longer exists.\n",
				np->n_last + 1, ap->a_name);
		else
			np->n_last++;
		if (f)
			(void) fclose(f);
		free(fname);
		return next;
	}

	gethead(f, &h);

	(void) printf("\n");
	interrupt = hadinterrupt = verbose = false;
	if (last) {
		(void) printf("No more articles (press RETURN again to quit).\n");
		printed = pheader = true;
	} else
		printed = pheader = false;

	while (1) {
		if (lastap != ap) {
			size = strlen(ap->a_name) + sizeof("Newsgroup");
			for (i = 0; i < size; i++)
				(void) putc('-', stdout);
			(void) printf("\nNewsgroup %s\n", ap->a_name);
			for (i = 0; i < size; i++)
				(void) putc('-', stdout);
			(void) printf("\n\n");
		}
		lastap = ap;
		if (!pheader) {
			time_t itsdate;
			(void) printf("Article %ld of %ld (%s)",
				np->n_last + 1, ap->a_seq, ap->a_name);
			if (h.h_lines != 0)
				(void) printf(" (%s lines)", h.h_lines);
			if (h.h_date != NULL) {
				itsdate = atot(h.h_date);
				(void) printf(" %s", ctime(&itsdate));
			} else
				(void) printf(" %s", "<no date!>\n");
			(void) printf(form, t_from, h.h_from);
			(void) printf(form, t_subject, h.h_subject);
			if (verbose || pflag) {
				(void) printf(form, t_date, h.h_date);
				(void) printf(form, t_newsgroups, h.h_newsgroups);
				(void) printf(form, t_path, h.h_path);
				if (h.h_sender)
					(void) printf(form, t_sender, h.h_sender);
				if (h.h_replyto)
					(void) printf(form, t_replyto, h.h_replyto);
				if (h.h_organisation)
					(void) printf(form, t_organization, h.h_organisation);
				verbose = false;
			}
			pheader = true;
		}
		if (!pushed && number[0])
			/*
			 * just returned from a number command
			 * and have another to do
			 */
			com = number;
		else if (pflag)
			/*
			 * just print it
			 */
			com = "";
		else {
			(void) printf("? ");
			if (fflush(stdout) == EOF) {
				(void) printf("\n? ");
				(void) fflush(stdout);
			}
			interrupt = false;
			if ((com = mgets()) == NIL(char)) {
				if (interrupt)
					if (!hadinterrupt) {
						clearerr(stdin);
						(void) printf("Interrupt\n");
						hadinterrupt = true;
						interrupt = false;
						continue;
					}
					else
						exit(1);
				nextact = stop;
				break;
			}
			hadinterrupt = false;
		}
		if (disable && *com && strchr(disable, *com) != NULL) {
			(void) printf("Command `%c' disabled.\n", *com);
			continue;
		}
		if (*com == '!') {
			if (com[1] == '!') {
				(void) printf("!%s\n", savedsys);
				com = savedsys;
			} else
				com++;
			(void) fflush(stdout);
			(void) fcntl(fileno(f), F_SETFD, 1);	/* close on exec */
			(void) system(com);
			if (com != savedsys)
				strncpy(savedsys, com, sizeof(savedsys) - 1);
			(void) printf("!\n");
			if (!printed)
				pheader = false;
			continue;
		}
		/*
		 * check command syntax
		 */
		if (*com && !isdigit(*com) && com[1] && (!isspace(com[1]) ||
		    strchr("Nsm", *com) == NULL)) {
			(void) printf(errmess);
			continue;
		}
		if (c = *com) {
			arg = com;
			while (isspace(*++arg))
				;
		} else
			arg = NULL;
		switch (c) {
		case 0:
		case '.':
			if (!printed || c == '.') {
				if (pflag)
					(void) printf("\n");
				print(&h, f);
				if (pflag) {
					nextact = next;
					break;
				}
				printed = true;
				continue;
			}
		case 'n':			/* B compatible */
		case '+':
		case ';':
			nextact = next;
			break;
		case '?':
			help();
			continue;
		case 'r':
			reply(&h, fname);
			continue;
		case 'f':
			followup(&h, fname);
			continue;
		case 'p':
			pnews(ap->a_name);
			continue;
		case 'U':
			if (ngmatch(np->n_name, admsub)) {
				(void) printf(
					"Group \"%s\" can't be unsubscribed.\n",
					np->n_name);
				continue;
			}
			np->n_subscribe = false;
			nextact = nextgroup;
			break;
		case 'N':			/* B compatible */
			if (!*arg) {
				nextact = nextgroup;
				break;
			}
			if ((wantap = activep(arg)) == NIL(active)) {
				(void) printf("%s: non-existent newsgroup.\n", arg);
				continue;
			}
			if (!ngmatch(arg, nflag)) {
				(void) printf("%s: is not subscribed to!\n", arg);
				wantap = NULL;
				continue;
			}
			nextact = searchgroup;
			break;
		case 's':
			save(&h, f, arg);
			continue;
		case 'q':
			nextact = stop;
			break;
		case 'x':
			fflush(stdout);
			exit(0);
		case 'h':
			verbose = true;
			pheader = false;
			continue;
		case 'H':
			puthead(&h, stdout, printing);
			continue;
		case '-':
			if (pushed) {
				nextact = next;
				break;
			}
			if (!rlastap || !lastn.n_name) {
				(void) printf("Can't go back!\n");
				continue;
			}
			nextact = commands(rlastap, &lastn, false, true);
			/*
			 * number commands, after a "-" act on the
			 * group of the "-" command
			 */
			while (number[0]) {
				ntmp = lastn;
				ntmp.n_last = atol(number) - 1;
				number[0] = '\0';
				nextact = commands(rlastap, &ntmp, false, true);
			}
			if (nextact != next)
				break;
			(void) printf("\n");
			pheader = false;
			continue;
		default:
			if (isdigit(c)) {
/*				i = atol(arg);		*/
				i = c - '0';
				while (isdigit(*arg))
					i = i * 10 + *arg++ - '0';
			}
			if (!isdigit(c) || *arg != '\0') {
				(void) printf(errmess);
				continue;
			}
			number[0] = '\0';
			if (i < ap->a_low || i > ap->a_seq) {
				(void) printf(
				    "Articles in \"%s\" group range %ld to %ld.\n",
				    np->n_name, ap->a_low, ap->a_seq);
				continue;
			}
			if (pushed) {
				sprintf(number, "%ld", i);
				nextact = next;
				break;
			}
			ntmp = *np;
			ntmp.n_last = i - 1;
			if ((nextact = commands(ap, &ntmp, false, true)) != next)
				break;
			if (!number[0]) {
				(void) printf("\n");
				pheader = false;
			}
			continue;
		}
		break;
	}
	rlastap = ap;
	lastn = *np;
	if (!pushed && (nextact == next || printed)) {
		np->n_last++;
		if (ino)
			seen(NIL(FILE), &ino);
	}
	freehead(&h);
	(void) fclose(f);
	free(fname);
	return nextact;
}


/*
 * see if the article has links, if so have we seen it?
 * close file if we return true
 *
 * called twice,
 *	first (with f set) to test (and possibly set *ino)
 *	again to put *ino in memory
 */
bool
seen(f, ino)
FILE *f;
ino_t *ino;
{
	static int num;
	static ino_t	*ilist;
	struct stat statb;
	register int i;

	if (f) {
		if (fstat(fileno(f), &statb) != 0 || statb.st_nlink <= 1)
			return false;
		for (i = 0; i < num; i++)
			if (ilist[i] == statb.st_ino) {
				(void) fclose(f);
				return true;
			}
		*ino = statb.st_ino;
		return false;
	} else if (*ino) {
		num++;
		ilist = (ino_t * ) (ilist ? myrealloc((char *) ilist, (int) sizeof(ino_t) *
		    num) : myalloc((int) sizeof(ino_t)));
		ilist[num - 1] = *ino;
	}
	return true;
}


/*
 * print out help file
 */
help()
{
	register FILE	*f;
	register int c;
	register char *helppath;

	helppath = ctlfile("readnews.help");
	if ((f = fopen(helppath, "r")) == NIL(FILE)) {
		(void) printf("Can't open %s\n", helppath);
		return;
	}
	while ((c = getc(f)) != EOF)
		(void) putc(c, stdout);
	(void) fclose(f);
}

/*
 * reply to sender by mail
 */
/* ARGSUSED fname */
reply(hp, fname)
header *hp;
char *fname;
{
	char *argv[MAXARGV];
	register int argc;

	argc = 0;
	argv[argc++] = "mail";
#ifdef UNSWMAIL
	argv[argc++] = "-s";
	if ((argv[argc++] = getsubject(hp)) == NIL(char))
		return;
	argv[argc++] = "-i";
	argv[argc++] = fname;
#endif

	if ((argv[argc++] = getretaddr(hp)) == NIL(char)) {
		(void) printf("Can't work out an address!\n");
		return;
	}

	argv[argc++] = NIL(char);

	run(mailpath, argv, false);

	free(argv[argc - 2]);
}



/*
 * generate correct headers for a followup article
 * then call postnews.
 */
followup(hp, fname)
header *hp;
char *fname;
{
	char tmpf[50];
	register FILE *fo;
	char *s = getsubject(hp);

	if (s == NULL)
		return;
	(void) strcpy(tmpf, "/tmp/rfXXXXXX");
	(void) mktemp(tmpf);
	fo = fopen(tmpf, "w");
	if (fo == NULL)
		error("can't create `%s'", tmpf);
	fprintf(fo, "Newsgroups: %s\n", (hp->h_followupto) ? hp->h_followupto :
							hp->h_newsgroups);
	fprintf(fo, "Subject: %s\n", s);
	free(s);
	if (hp->h_references && hp->h_messageid)
		fprintf(fo, "References: %s %s\n", hp->h_references, hp->h_messageid);
	else if (hp->h_messageid)
		fprintf(fo, "References: %s\n", hp->h_messageid);
	(void) fclose(fo);

	s = newstr3(binfile("inject/postnews"), " -h ", tmpf);
	system(s);
	free(s);

	(void) unlink(tmpf);
}

/*
 * get correct "Subject: Re: .." line
 */
char *
getsubject(hp)
register header *hp;
{
	register char *s;

	if (!hp->h_subject) {
		(void) printf("Subject: Re: ");
		(void) fflush(stdout);
		if ((s = mgets()) == NIL(char) || !*s) {
			(void) printf("The Subject field is mandatory.\n");
			return NIL(char);
		}
		return newstr2("Re: ", s);
	} else if (CMPN(hp->h_subject, "Re: ", 4) != 0 && CMPN(hp->h_subject,
	     "re: ", 4) != 0)
		return newstr2("Re: ", hp->h_subject);
	else
		return newstr(hp->h_subject);
}


/*
 * run a command, optionally closing stdin
 */
run(com, argv, closein)
char *com;
char *argv[];
bool closein;
{
	int pid, status, r;

	switch (pid = fork()) {
	default:
		/* parent */
		break;
	case 0:
		/* child */
		if (closein)
			close(fileno(stdin));
		execvp(com, argv);
		error("can't exec %s", com);
		exit(1);

	case -1:
		error("can't fork");
	}

	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
		(void) signal(SIGINT, SIG_IGN);
	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
		(void) signal(SIGQUIT, SIG_IGN);

	while ((r = wait(&status)) != pid && r != -1)
		;

	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
		(void) signal(SIGINT, onintr);
	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
		(void) signal(SIGQUIT, onintr);
}

/*
 * call postnews
 */
pnews(group)
char *group;
{
	register char *s = newstr3(binfile("inject/postnews"), " ", group);
	system(s);
	free(s);
}

/*
 * save an article
 */
save(hp, f, s)
header *hp;
FILE *f;
char *s;
{
	register long pos;
	register int c;
	register char *cp;
	register FILE	*sf;
	register char *aname;
	long then;
	extern char *getenv();

	if (!*s) {
		if ((aname = getenv("HOME")) == NIL(char)) {
			(void) printf("No $HOME in environment.\n");
			return;
		}
		s = aname = newstr3(aname, "/", ARTICLES);
	} else
		aname = NIL(char);
	if ((sf = fopen(s, "a")) == NIL(FILE)) {
		(void) fprintf(stderr, "readnews: can't open %s\n", s);
		return;
	}
	if (aname)
		free(aname);
	pos = ftell(f);
	rewind(f);
	if (cp = strchr(hp->h_from, ' '))
		*cp = '\0';
	if (hp->h_date)
		then = atot(hp->h_date);
	else
		then = 0L;
	(void) fprintf(sf, "From %s %s", hp->h_from, ctime(then ? &then : &now));
	if (cp)
		*cp = ' ';
	while ((c = getc(f)) != EOF)
		(void) putc(c, sf);
	(void) putc('\n', sf);
	(void) fclose(sf);
	fseek(f, pos, 0);
}

/*
 * put - like putchar, but filtering control characters
 */
int				/* like putchar */
put(c)
int c;
{
	register int trimc = c & 0177;		/* trim off top bit */
	register int newc;

	if (iscntrl(trimc) && trimc != '\n' && trimc != '\b' && trimc != '\t')
		newc = '#';
	else
		newc = c;
	return(putchar(newc));
}



/*
 * print an article, if it's long enough call page()
 */
/* ARGSUSED */
print(hp, f)
header *hp;
FILE *f;
{
	register int c;
	register long pos;

	pos = ftell(f);
	if (!pflag)
		page(f);
	else
		while ((c = getc(f)) != EOF)
			(void) put(c);
	(void) fseek(f, pos, 0);
}


/*
 * copy article text to stdout, and break into pages
 */
page(f)
FILE *f;
{
	register int c;
	register unsigned lineno;
	char lbuf[80];

	lineno = 1;
	while (!interrupt) {
		for (; lineno < PAGESIZE - 4 && !interrupt; lineno++) {
			while ((c = getc(f)) != EOF && c != '\n')
				(void) put(c);
			if (c == EOF)
				goto fastexit;
			if (lineno < PAGESIZE - 5)
				(void) put('\n');
		}
		if (interrupt)
			break;
		if (fflush(stdout) == EOF)
			break;
		if (read(fileno(stdin), lbuf, sizeof(lbuf)) <= 0)
			break;
		lineno = 0;
	}
	if (lineno)
		(void) put('\n');
	interrupt = false;
fastexit:	;
}

/* VARARGS1 */
error(s, a0, a1, a2, a3)
char *s;
{
	(void) fprintf(stderr, "readnews: ");
	(void) fprintf(stderr, s, a0, a1, a2, a3);
	(void) fprintf(stderr, "\n");
	fflush(stdout);		/* just on principle */
	exit(1);
}

/*
 - getctl - pick up control file for subscriptions etc.
 */
getctl()
{
	register FILE *f;
	register char *fname;
	char line[BUFLEN];
	register char *p;

	fname = ctlfile("readnews.ctl");
	f = fopen(fname, "r");
	if (f == NULL)
		return;

	while (fgets(line, sizeof(line), f) != NULL) {
		line[strlen(line)-1] = '\0';	/* dispose of newline */
		p = strchr(line, '\t');
		if (p == NULL)
			p = strchr(line, ' ');
		if (line[0] != '#' && p != NULL) {
			while (*p == ' ' || *p == '\t')
				*p++ = '\0';
			if (strcmp(line, "defsub") == 0)
				(void) strcpy(dfltsub, p);
			else if (strcmp(line, "mustsub") == 0)
				(void) strcpy(admsub, p);
			else if (strcmp(line, "mailvia") == 0)
				(void) strcpy(mailvia, p);
		}
	}

	(void) fclose(f);
}


syntax highlighted by Code2HTML, v. 0.9.1