[comp.sources.unix] v19i096: Cnews production release, Part19/19

rsalz@uunet.uu.net (Rich Salz) (06/30/89)

Submitted-by: utzoo!henry
Posting-number: Volume 19, Issue 96
Archive-name: cnews2/part19

: ---CUT HERE---
echo 'rna/readnews.c':
sed 's/^X//' >'rna/readnews.c' <<'!'
X/*
X * readnews
X *
X *	Michael Rourke (UNSW) April 1984
X */
X
X#include "defs.h"
X
X#define ARTSEP "/"
X
Xchar postnews[]	 = POSTNEWS;
Xchar admsub[]	 = ADMSUB;
Xchar dfltsub[]	 = DFLTSUB;
Xchar *mailpath	 = MAIL;
X
X#define	MAXARGV	10		/* used in building argv[]s below */
X
X#ifndef NETID
Xchar systemid[DIRSIZ];
X#else
Xchar systemid[] = NETID;
X#endif
X
Xbool iflag;		/* -i ignore .newsrc */
Xbool lflag;		/* -l print headers only */
Xbool cflag;		/* -c check for news only */
Xbool pflag;		/* -p print everything selected */
Xchar **uflag;		/* -u messagid (unsubscribe from followups) */
Xint usize;		/* number of uflag entries */
Xbool Cflag;		/* -C verbose -c */
Xbool sflag;		/* -s print newsgroup subscription list */
Xbool splus;		/* -s+ */
Xbool slistall;		/* -s? */
Xbool sminus;		/* -s- */
Xchar *sarg;		/* arg to -s[+-] */
Xchar *nflag;		/* -n newsgroups */
Xextern char *rcgrps;	/* -n newsgroups from newsrc file */
Xbool n_on_cline;	/* nflag set on command line */
X
Xextern newsrc	*rc;		/* internal .newsrc */
X
Xactive *alist;		/* internal active list */
X
X#if MANGRPS
Xchar *mangrps;	/* mandatory subsciption list */
X#endif
X
X#if AUSAM
Xstruct pwent pe;		/* current user passwd struct */
Xchar sbuf[SSIZ];	/* passwd strings */
X#else
Xstruct passwd *pp;		/* current user passwd struct */
X#endif
Xlong now;		/* current time */
Xbool interrupt;		/* if interrupt hit */
Xchar *newsdir;		/* %news */
Xuid_t newsuid;		/* %news uid (not used) */
Xbool su;		/* if super user (not used) */
X
Xapplycom list(), check(), commands();
Xvoid *onintr();
Xbool ureject(), seen(), subs(), subsub();
X
X#if MANGRPS
Xchar *getmangrps();
X#endif
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X	char buf[BUFSIZ], *p;
X
X	setbuf(stdout, buf);			/* TODO: remove this? */
X	if (options(--argc, ++argv, true)) {
X		(void) fprintf(stderr, "Usage: readnews [-n newsgroups] [-i] [-clpC] [-s[-+? [group]]] [-u messageid]\n");
X		exit(1);
X	}
X	now = time(&now);
X
X#if AUSAM
X	pe.pw_strings[LNAME] = NEWSROOT;
X	if (getpwuid(&pe, sbuf, sizeof(sbuf)) == PWERROR)
X		error("Password file error.");
X	newsdir = newstr(pe.pw_strings[DIRPATH]);
X	newsuid = pe.pw_limits.l_uid;
X#else
X	if ((pp = getpwnam(NEWSROOT)) == NULL)
X		error("Password file error.");
X	newsdir = newstr(pp->pw_dir);
X	newsuid = pp->pw_uid;
X#endif
X
X#if AUSAM
X#if MANGRPS
X	pe.pw_limits.l_uid = getuid();
X	if (getpwlog(&pe, NIL(char), 0) == PWERROR)	/* want pw_cmask */
X		error("Password file error.");
X#endif
X	pwclose();
X#else
X	pp = NIL(struct passwd );
X	endpwent();
X#endif
X
X#ifndef NETID
X	getaddr(G_SYSNAME, systemid);
X#endif
X
X	if (!iflag)
X		readnewsrc();
X
X	if (nflag)
X		convgrps(nflag);
X	else
X		nflag = dfltsub;
X	if (rcgrps)
X		convgrps(rcgrps);
X	if (!n_on_cline) {
X#if MANGRPS
X		int addsub();
X
X		if (mangrps = getmangrps(pe.pw_cmask))
X			applyng(mangrps, addsub, &nflag);
X#endif
X		if (!ngmatch(admsub, nflag))
X			nflag = newstr3(admsub, NGSEPS, nflag);
X	}
X	if ((int) sflag + (int) lflag + (int) cflag + (int) pflag > 1)
X		error("-clpsC flags are mutually exclusive.");
X	if (uflag)
X		qsort((char *) uflag, (unsigned) usize, sizeof(char *), strpcmp);
X
X	/* user has private mailer? */
X	if ((p = getenv("MAILER")) != NULL)
X		mailpath = newstr(p);
X
X	alist = readactive();
X
X	if (sflag) {
X		if (subs() && !iflag)
X			writenewsrc(alist);
X	} else if (lflag)
X		apply(alist, nflag, list, false);
X	else if (cflag)
X		apply(alist, nflag, check, false);
X	else {
X		if (!pflag) {
X			if (signal(SIGINT, SIG_IGN) != SIG_IGN)
X				(void) signal(SIGINT, onintr);
X			if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
X				(void) signal(SIGQUIT, onintr);
X		}
X		apply(alist, nflag, commands, true);
X		if (!iflag)
X			writenewsrc(alist);
X	}
X	exit(0);
X}
X
X#if MANGRPS
X/*
X * make a subscription list of all class groups the user belongs too
X * (mandatory subscription)
X */
Xchar *
Xgetmangrps(cmask)
Xchar *cmask;
X{
X	static char *weekday[] = { 
X		"mon", "tue", "wed", "thu", "fri" 	};
X	register char **classes;
X	register char *s, *end;
X	register char *grp;
X	register int i, size;
X	extern char **getclasses();
X
X	grp = NIL(char);
X	if ((classes = getclasses(cmask)) == NIL(char *))
X		error("Can't get classes.");
X	while (*classes) {
X		if (isdigit(**classes)) {
X			/*
X			 * trim string after numeric class
X			 * if it is a day of the week
X			 */
X			s = *classes;
X			while (isdigit(*s) || *s == '.')
X				s++;
X			if (*s) {
X				end = s;
X				while (isalpha(*end))
X					end++;
X				if (*end && end != s && end - s <= 3) {
X					size = end - s;
X					for (i = 0; i < 5; i++)
X						if (CMPN(s, weekday[i], size) == 0)
X							break;
X					if (i != 5)
X						*s = '\0';
X				}
X			}
X		}
X		grp = (grp? catstr2(grp, ",class.", *classes):
X			newstr2("class.", *classes));
X		classes++;
X	}
X	return grp;
X}
X
X/*
X * if newsgroup "ng" isn't subscribed to, add it to subscription list
X */
Xaddsub(ng, slist)
Xchar *ng;
Xchar **slist;
X{
X	if (!ngmatch(ng, *slist))
X		*slist = newstr3(ng, NGSEPS, *slist);
X}
X
X#endif
X
Xvoid *
Xonintr()
X{
X	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
X		(void) signal(SIGINT, onintr);
X	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
X		(void) signal(SIGQUIT, onintr);
X	interrupt = true;
X}
X
X/*
X * process options
X * can be called from readnewsrc()
X */
Xoptions(argc, argv, cline)
Xint argc;
Xchar *argv[];
Xbool cline;
X{
X	register char c;
X
X	/* TODO: use getopt(3) */
X	while (argc > 0) {
X		if (argv[0][0] != '-')
X			break;
X		while (c = *(++(argv[0]))) {
X			switch (c) {
X			case 'n':
X				if (cline)
X					nflag = argv[1], n_on_cline = true;
X				else {
X					if (!n_on_cline)
X						nflag = (nflag?
X							catstr2(nflag, NGSEPS, argv[1]):
X							newstr(argv[1]));
X					rcgrps = (rcgrps?
X						catstr2(rcgrps, NGSEPS, argv[1]):
X						newstr(argv[1]));
X				}
X				argc--, argv++; 
X				break;
X			case 'u':
X				usize++;
X				uflag = (uflag? (char **)myrealloc((char *)uflag,
X						(int)sizeof(char *) * usize):
X					(char **)myalloc((int)sizeof(char *)));
X				uflag[usize - 1] = newstr(argv[1]);
X				argc--, argv++; 
X				break;
X			case 'i':
X				iflag = true; 
X				continue;
X			case 's':
X				sflag = true;
X				switch (argv[0][1]) {
X				case '\0':
X					continue;
X				case '+':
X					splus = true; 
X					break;
X				case '?':
X					slistall = true, ++(argv[0]); 
X					continue;
X				case '-':
X					sminus = true; 
X					break;
X				default:
X					argc = -1; 
X					break;
X				}
X				if (argc > 0) {
X					sarg = newstr(argv[1]);
X					argc--, argv++;
X				}
X				break;
X			case 'p':
X				pflag = true; 
X				continue;
X			case 'l':
X				lflag = true; 
X				continue;
X			case 'c':
X				cflag = true; 
X				continue;
X			case 'C':
X				cflag = Cflag = true; 
X				continue;
X			default:
X				argc = -1; 
X				break;
X			}
X			break;
X		}
X		argc--, argv++;
X	}
X	return argc != 0;
X}
X
X/*
X * subscription list handling
X * return true if newsrc is to be re-written
X */
Xbool
Xsubs()
X{
X	register newsrc	*np;
X	register active	*ap;
X	register char *tmp, *com;
X	register FILE *f;
X
X	if (slistall) {
X		(void) printf("Active newsgroups:\n");
X		(void) fflush(stdout);
X#ifdef	MC			/* gok. probably a local paginator */
X		com = newstr2("exec ", MC);
X		if ((f = popen(com, "w")) == NULL)
X			f = stdout;
X		free(com);
X#else
X		f = stdout;
X		com = 0;		/* for lint */
X		com = com;		/* for lint */
X#endif
X		for (ap = alist; ap; ap = ap->a_next)
X			(void) fprintf(f, "%s\n", ap->a_name);
X#ifdef	MC
X		if (f != stdout)
X			pclose(f);
X#endif
X		return false;
X	} else if (splus || sminus) {
X		if (strpbrk(sarg, BADGRPCHARS)) {
X			(void) printf("%s: Illegal char in newsgroup.\n", sarg);
X			return false;
X		}
X		if (ngmatch(sarg, nflag)) {
X			/*
X			 * normally we subscribe, check for an exclusion
X			 */
X			for (np = rc; np; np = np->n_next)
X				if (CMP(sarg, np->n_name) == 0)
X					break;
X			if (np) {
X				/*
X				 * altering subscribe flag is all
X				 * we need to change
X				 */
X				np->n_subscribe = splus;
X				return true;
X			}
X			if (sminus) {
X				/*
X				 * try deleting from sub list
X				 */
X				if (subsub(sarg, rcgrps))
X					return true;
X				/*
X				 * add specific exclusion
X				 */
X				rcgrps = newstr4(rcgrps, NGSEPS, NEGS, sarg);
X				return true;
X			}
X		} else if (splus) {
X			/*
X			 * we don't subscribe,
X			 * try deleting !sarg first
X			 */
X			tmp = newstr2(NEGS, sarg);
X			subsub(tmp, rcgrps);
X			if (!ngmatch(sarg, rcgrps))
X				/*
X				 * didn't work, so add explicit subscription
X				 */
X				rcgrps = rcgrps? newstr3(rcgrps, NGSEPS, sarg):
X					newstr(sarg);
X			return true;
X		}
X	} else {
X		(void) printf("Subscription list: %s", nflag);
X		for (np = rc; np; np = np->n_next)
X			if (!np->n_subscribe && ngmatch(np->n_name, nflag))
X				(void) printf(",!%s", np->n_name);
X		(void) printf("\n");
X	}
X	return false;
X}
X
X
X/*
X * try and delete group from subscription list
X * return true if successful
X */
Xbool
Xsubsub(grp, slist)
Xchar *grp;
Xchar *slist;
X{
X	register char *delim;
X
X	while (*slist) {
X		if (delim = strchr(slist, NGSEPCHAR))
X			*delim = '\0';
X		if (CMP(grp, slist) == 0) {
X			if (delim)
X				(void) strcpy(slist, delim + 1);
X			else if (slist[-1] = ',')
X				slist[-1] = '\0';
X			else
X				slist[0] = '\0';
X			return true;
X		}
X		if (delim)
X			*delim = NGSEPCHAR, slist = delim + 1;
X		else
X			break;
X	}
X	return false;
X}
X
X/*
X * list titles command (-l)
X */
Xapplycom
Xlist(ap, np)
Xactive *ap;
Xnewsrc *np;
X{
X	static active *lastap;
X	static bool first = true;
X	register char *fname;
X	register FILE *f;
X	header h;
X	ino_t ino;
X
X	np->n_last++;
X	fname = convg(newstr5(newsdir, "/", ap->a_name, ARTSEP,
X		itoa(np->n_last)));
X	ino = 0;
X	f = fopen(fname, "r");
X	free(fname);
X	if (!f || seen(f, &ino))
X		return next;
X	gethead(f, &h);
X	if (uflag && h.h_references && ureject(&h)) {
X		freehead(&h);
X		return next;
X	}
X	if (first) {
X		(void) printf("News articles:\n");
X		first = false;
X	}
X	if (lastap != ap)
X		(void) printf("  %s:\n", ap->a_name);
X	lastap = ap;
X	(void) printf("    %-4d %s\n", np->n_last, h.h_subject);
X	(void) fclose(f);
X	freehead(&h);
X	if (ino)
X		seen(NIL(FILE), &ino);
X	return next;
X}
X
X/*
X * check command (-c or -C)
X */
Xapplycom
Xcheck(ap, np)
Xactive *ap;
Xnewsrc *np;
X{
X	static bool done;
X	register int num;
X
X	np->n_last++;
X	if (Cflag) {
X		if (!done)
X			(void) printf("You have news:\n");
X		done = true;
X		num = ap->a_seq - np->n_last + 1;
X		(void) printf("\t%s at most %d article%s\n",
X			ap->a_name, num, (num > 1? "s": ""));
X		return nextgroup;
X	} else {
X		(void) printf("You have news.\n");
X		exit(0);
X		/* NOTREACHED */
X	}
X}
X
X
X/*
X * normal command handler (or pflag)
X * commands:
X *
X * \n 		print current article
X * n 		go to next article
X * q		quit
X * c		cancel
X * r		reply
X * m [person]	mail
X * f 		followup
X * p 		postnews
X * N [newsgrp]	next newsgroup
X * s [file]	save
X * u		unsubscribe from followup
X * U		unsubscribe from group
X * !stuff	shell escape
X * number or .	go to number
X * - 		back to previous article (toggle)
X * x		quick exit
X * h		long header info
X * H		full header
X *
X * inside r, f or p:
X *	.e	edit
X *	.i	interpolate
X *	. or EOT terminate message
X *	.!comd	shell escape
X */
Xapplycom
Xcommands(ap, np, last, pushed)
Xactive *ap;
Xnewsrc *np;
Xbool last;
Xbool pushed;
X{
X	static char errmess[] = "Incorrect command; Type `?' for help.\n";
X	static char form[]    = "%s: %s\n";
X
X	static char savedsys[BUFSIZ / 2];
X	static active	*lastap, *rlastap;
X	static newsrc	lastn;
X	static char number[20];
X	static active	*wantap;
X
X	register char *com, *arg;
X	register int c, i, size;
X	register FILE 	*f;
X	char *fname;
X	header		h;
X	newsrc		ntmp;
X	ino_t		ino;
X	bool printed, pheader, verbose, hadinterrupt;
X	applycom	nextact;
X
X	extern char t_from[], t_subject[], t_date[];
X	extern char t_newsgroups[], t_path[], t_sender[];
X	extern char t_replyto[], t_organization[];
X	extern char *strncpy();
X	extern active	*activep();
X
X
X	if (last) {
X		/*
X		 * give user one last chance to
X		 * see this article
X		 */
X		ap = rlastap;
X		np = &lastn;
X		wantap = NIL(active);
X		if (!ap || pflag)
X			return stop;
X	} else if (wantap)
X		/*
X		 * doing an "n newsgroup" command
X		 */
X		if (wantap != ap)
X			return nextgroup;
X		else
X			wantap = NIL(active);
X
X	fname = convg(newstr5(newsdir, "/", ap->a_name, ARTSEP,
X		itoa(np->n_last + 1)));
X	f = fopen(fname, "r");
X	ino = 0;
X	if (!f || !last && !pushed && seen(f, &ino)) {
X		if (pushed)
X			(void) printf("Article %d (%s) no longer exists.\n",
X				np->n_last + 1, ap->a_name);
X		else
X			np->n_last++;
X		if (f)
X			(void) fclose(f);
X		free(fname);
X		return next;
X	}
X
X	gethead(f, &h);
X	if (!pushed && uflag && h.h_references && ureject(&h)) {
X		/* unsubscribed followup */
X		freehead(&h);
X		np->n_last++;
X		(void) fclose(f);
X		free(fname);
X		return next;
X	}
X
X	(void) printf("\n");
X	interrupt = hadinterrupt = verbose = false;
X	if (last) {
X		(void) printf("No more articles.\n");
X		printed = pheader = true;
X	} else
X		printed = pheader = false;
X
X	while (1) {
X		if (lastap != ap) {
X			size = strlen(ap->a_name) + sizeof("Newsgroup");
X			for (i = 0; i < size; i++)
X				(void) putc('-', stdout);
X			(void) printf("\nNewsgroup %s\n", ap->a_name);
X			for (i = 0; i < size; i++)
X				(void) putc('-', stdout);
X			(void) printf("\n\n");
X		}
X		lastap = ap;
X		if (!pheader) {
X			(void) printf("Article %d of %d (%s)",
X				np->n_last + 1, ap->a_seq, ap->a_name);
X			if (h.h_lines != 0)
X				(void) printf(" (%s lines)", h.h_lines);
X			(void) printf("\n");
X			(void) printf(form, t_subject, h.h_subject);
X			(void) printf(form, t_from, h.h_from);
X			if (verbose || pflag) {
X				(void) printf(form, t_date, h.h_date);
X				(void) printf(form, t_newsgroups, h.h_newsgroups);
X				(void) printf(form, t_path, h.h_path);
X				if (h.h_sender)
X					(void) printf(form, t_sender, h.h_sender);
X				if (h.h_replyto)
X					(void) printf(form, t_replyto, h.h_replyto);
X				if (h.h_organisation)
X					(void) printf(form, t_organization, h.h_organisation);
X				verbose = false;
X			}
X			pheader = true;
X		}
X		if (!pushed && number[0])
X			/*
X			 * just returned from a number command
X			 * and have another to do
X			 */
X			com = number;
X		else if (pflag)
X			/*
X			 * just print it
X			 */
X			com = "";
X		else
X		 {
X			(void) printf("? ");
X			if (fflush(stdout) == EOF) {
X				(void) printf("\n? ");
X				(void) fflush(stdout);
X			}
X			interrupt = false;
X			if ((com = mgets()) == NIL(char)) {
X				if (interrupt)
X					if (!hadinterrupt) {
X						clearerr(stdin);
X						(void) printf("Interrupt\n");
X						hadinterrupt = true;
X						interrupt = false;
X						continue;
X					}
X					else
X						exit(1);
X				nextact = stop;
X				break;
X			}
X			hadinterrupt = false;
X		}
X		if (*com == '!') {
X			if (com[1] == '!') {
X				(void) printf("!%s\n", savedsys);
X				com = savedsys;
X			} else
X				com++;
X			(void) fflush(stdout);
X#ifdef F_SETFD
X			(void) fcntl(fileno(f), F_SETFD, 1);	/* close on exec */
X#endif
X			(void) system(com);
X			if (com != savedsys)
X				strncpy(savedsys, com, sizeof(savedsys) - 1);
X			(void) printf("!\n");
X			if (!printed)
X				pheader = false;
X			continue;
X		}
X		/*
X		 * check command syntax
X		 */
X		if (*com && !isdigit(*com) && com[1] && (!isspace(com[1]) ||
X		    strchr("Nsm", *com) == NULL)) {
X			(void) printf(errmess);
X			continue;
X		}
X		if (c = *com) {
X			arg = com;
X			while (isspace(*++arg))
X				;
X		} else
X			arg = NULL;
X		switch (c) {
X		case 0:
X		case '.':
X			if (!printed || c == '.') {
X				if (pflag)
X					(void) printf("\n");
X				print(&h, f);
X				if (pflag) {
X					nextact = next;
X					break;
X				}
X				printed = true;
X				continue;
X			}
X		case 'n':			/* B compatible */
X		case '+':
X		case ';':
X			nextact = next;
X			break;
X		case '?':
X			help();
X			continue;
X		case 'c':
X			cancelarticle(&h);
X			continue;
X		case 'r':
X			reply(&h, fname);
X			continue;
X		case 'm':
X			if (!arg || !*arg)
X				(void) printf("Person argument missing.\n");
X			else
X				mail(&h, fname, arg);
X			continue;
X		case 'f':
X			followup(&h, fname);
X			continue;
X		case 'p':
X			pnews(fname);
X			continue;
X		case 'U':
X			if (ngmatch(np->n_name, ADMSUB)
X#if MANGRPS
X			 || ngmatch(np->n_name, mangrps))
X#else
X				)
X#endif
X				 {
X					(void) printf("Group \"%s\" can't be unsubscribed.\n",
X					     					 np->n_name);
X					continue;
X				}
X			np->n_subscribe = false;
X			nextact = nextgroup;
X			break;
X		case 'u':
X			unsubscribe(h.h_references);
X			nextact = next;
X			break;
X		case 'N':			/* B compatible */
X			if (!*arg) {
X				nextact = nextgroup;
X				break;
X			}
X			if ((wantap = activep(arg)) == NIL(active)) {
X				(void) printf("%s: non-existent newsgroup.\n", arg);
X				continue;
X			}
X			if (!ngmatch(arg, nflag)) {
X				(void) printf("%s: is not subscribed to!\n", arg);
X				wantap = NIL(active);
X				continue;
X			}
X			nextact = searchgroup;
X			break;
X		case 's':
X			save(&h, f, arg);
X			continue;
X		case 'q':
X			nextact = stop;
X			break;
X		case 'x':
X			exit(0);
X		case 'h':
X			verbose = true;
X			pheader = false;
X			continue;
X		case 'H':
X			puthead(&h, stdout, printing);
X			continue;
X		case '-':
X			if (pushed) {
X				nextact = next;
X				break;
X			}
X			if (!rlastap || !lastn.n_name) {
X				(void) printf("Can't go back!\n");
X				continue;
X			}
X			nextact = commands(rlastap, &lastn, false, true);
X			/*
X			 * number commands, after a "-" act on the
X			 * group of the "-" command
X			 */
X			while (number[0]) {
X				ntmp = lastn;
X				ntmp.n_last = atoi(number) - 1;
X				number[0] = '\0';
X				nextact = commands(rlastap, &ntmp, false, true);
X			}
X			if (nextact != next)
X				break;
X			(void) printf("\n");
X			pheader = false;
X			continue;
X		default:
X			if (isdigit(c)) {
X				i = c - '0';
X				while (isdigit(*arg))
X					i = i * 10 + *arg++ - '0';
X			}
X			if (!isdigit(c) || *arg != '\0') {
X				(void) printf(errmess);
X				continue;
X			}
X			number[0] = '\0';
X			if (i < ap->a_low || i > ap->a_seq) {
X				(void) printf("Articles in \"%s\" group range %d to %d.\n",
X				     					np->n_name, ap->a_low, ap->a_seq);
X				continue;
X			}
X			if (pushed) {
X				sprintf(number, "%d", i);
X				nextact = next;
X				break;
X			}
X			ntmp = *np;
X			ntmp.n_last = i - 1;
X			if ((nextact = commands(ap, &ntmp, false, true)) !=
X			    next)
X				break;
X			if (!number[0]) {
X				(void) printf("\n");
X				pheader = false;
X			}
X			continue;
X		}
X		break;
X	}
X	rlastap = ap;
X	lastn = *np;
X	if (!pushed && (nextact == next || printed)) {
X		np->n_last++;
X		if (ino)
X			seen(NIL(FILE), &ino);
X	}
X	freehead(&h);
X	(void) fclose(f);
X	free(fname);
X	return nextact;
X}
X
X
X/*
X * see if this is a followup we are ignoring
X */
Xbool
Xureject(hp)
Xregister header *hp;
X{
X	register bool found;
X	register char *rp, *ids, c;
X	char *key[1];
X
X	found = false;
X	rp = hp->h_references;
X	while (*rp && !found) {
X		if (ids = strpbrk(rp, " ,")) {
X			c = *ids;
X			*ids = '\0';
X		}
X		key[0] = rp;
X		found = (bool) (bsearch((char *) key, (char *) uflag, (unsigned) usize,
X		     sizeof(char *), strpcmp) != NIL(char));
X		if (ids)
X			*ids = c, rp = ids + 1;
X		else
X			break;
X		while (isspace(*rp) || *rp == ',')
X			rp++;
X	}
X	return found;
X}
X
X
X/*
X * see if the article has links, if so have we seen it?
X * close file if we return true
X *
X * called twice,
X *	first (with f set) to test (and possibly set *ino)
X *	again to put *ino in memory
X */
Xbool
Xseen(f, ino)
XFILE *f;
Xino_t *ino;
X{
X	static int num;
X	static ino_t	*ilist;
X	struct stat statb;
X	register int i;
X
X	if (f) {
X		if (fstat(fileno(f), &statb) != 0 || statb.st_nlink <= 1)
X			return false;
X		for (i = 0; i < num; i++)
X			if (ilist[i] == statb.st_ino) {
X				(void) fclose(f);
X				return true;
X			}
X		*ino = statb.st_ino;
X		return false;
X	} else if (*ino) {
X		num++;
X		ilist = (ino_t * ) (ilist ? myrealloc((char *) ilist, (int) sizeof(ino_t) *
X		    num) : myalloc((int) sizeof(ino_t)));
X		ilist[num - 1] = *ino;
X	}
X	return true;
X}
X
X
X/*
X * print out help file
X */
Xhelp()
X{
X	register FILE	*f;
X	register int c;
X	static char helppath[]	 = HELP;
X
X	if ((f = fopen(helppath, "r")) == NIL(FILE)) {
X		(void) printf("Can't open %s\n", helppath);
X		return;
X	}
X	while ((c = getc(f)) != EOF)
X		(void) putc(c, stdout);
X	(void) fclose(f);
X}
X
X/*
X * cancel an article.
X * inews does permission checking.
X */
Xcancelarticle(hp)
Xheader *hp;
X{
X	char *argv[MAXARGV];
X
X	argv[0] = strrchr(postnews, '/') + 1;
X	argv[1] = "-c";		/* TODO: no such option in C news */
X	argv[2] = newstr2("cancel ", hp->h_messageid);
X	argv[3] = "-n";
X	argv[4] = hp->h_newsgroups;
X	if (hp->h_distribution) {
X		argv[5] = "-d";
X		argv[6] = hp->h_distribution;
X		argv[7] = NIL(char);
X	} else
X		argv[5] = NIL(char);
X	run(postnews, argv, true);
X	free(argv[2]);
X}
X
X/*
X * reply to sender by mail
X */
X/* ARGSUSED fname */
Xreply(hp, fname)
Xheader *hp;
Xchar *fname;
X{
X	char *argv[MAXARGV];
X	register int argc;
X
X	argc = 0;
X	argv[argc++] = "mail";
X#ifdef UNSWMAIL
X	argv[argc++] = "-s";
X	if ((argv[argc++] = getsubject(hp)) == NIL(char))
X		return;
X	argv[argc++] = "-i";
X	argv[argc++] = fname;
X#endif
X
X	if ((argv[argc++] = getretaddr(hp)) == NIL(char)) {
X		(void) printf("Can't work out an address!\n");
X		return;
X	}
X
X	argv[argc++] = NIL(char);
X
X	run(mailpath, argv, false);
X
X	free(argv[argc - 2]);
X}
X
X
X/*
X * mail to person
X */
X/* ARGSUSED hp fname */
Xmail(hp, fname, person)
Xheader *hp;
Xchar *fname, *person;
X{
X	char *argv[MAXARGV];
X	register int argc;
X
X	argc = 0;
X	argv[argc++] = "mail";
X#ifdef UNSWMAIL
X	argv[argc++] = "-i";
X	argv[argc++] = fname;
X	argv[argc++] = "-s";
X	argv[argc++] = hp->h_subject;
X#endif
X	argv[argc++] = person;
X	argv[argc++] = NIL(char);
X
X	run(mailpath, argv, false);
X}
X
X
X/*
X * generate correct headers for a followup article
X * then call inews.
X */
Xfollowup(hp, fname)
Xheader *hp;
Xchar *fname;
X{
X	register int argc;
X	char *argv[10];
X
X	argc = 0;
X	argv[argc++] = strrchr(postnews, '/') + 1;
X	argv[argc++] = "-i";		/* TODO: what's this in B news? */
X	argv[argc++] = fname;
X	argv[argc++] = "-r";
X	if (hp->h_references && hp->h_messageid)
X		argv[argc++] = newstr3(hp->h_references, " ", hp->h_messageid);
X	else if (hp->h_messageid)
X		argv[argc++] = newstr(hp->h_messageid);
X	else
X		argc--;
X
X	argv[argc++] = "-s";
X	if ((argv[argc++] = getsubject(hp)) == NIL(char))
X		return;
X
X	argv[argc++] = "-n";
X	if (hp->h_followupto)
X		argv[argc++] = hp->h_followupto;
X	else
X		argv[argc++] = hp->h_newsgroups;
X	argv[argc++] = NIL(char);
X
X	run(postnews, argv, false);
X
X	if (argc == 10)
X		free(argv[4]);
X}
X
X/*
X * get correct "Subject: Re: .." line
X */
Xchar *
Xgetsubject(hp)
Xregister header *hp;
X{
X	register char *s;
X
X	if (!hp->h_subject) {
X		(void) printf("Subject: Re: ");
X		(void) fflush(stdout);
X		if ((s = mgets()) == NIL(char) || !*s) {
X			(void) printf("The Subject field is mandatory.\n");
X			return NIL(char);
X		}
X		return newstr2("Re: ", s);
X	} else if (CMPN(hp->h_subject, "Re: ", 4) != 0 && CMPN(hp->h_subject,
X	     "re: ", 4) != 0)
X		return newstr2("Re: ", hp->h_subject);
X	else
X		return hp->h_subject;
X}
X
X
X/*
X * run a command, optionally closing stdin
X */
Xrun(com, argv, closein)
Xchar *com;
Xchar *argv[];
Xbool closein;
X{
X	int pid, status, r;
X
X	switch (pid = fork()) {
X	default:
X		/* parent */
X		break;
X	case 0:
X		/* child */
X		if (closein)
X			close(fileno(stdin));
X		execvp(com, argv);
X		error("can't exec %s", com);
X		exit(1);
X
X	case -1:
X		error("can't fork");
X	}
X
X	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
X		(void) signal(SIGINT, SIG_IGN);
X	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
X		(void) signal(SIGQUIT, SIG_IGN);
X
X	while ((r = wait(&status)) != pid && r != -1)
X		;
X
X	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
X		(void) signal(SIGINT, onintr);
X	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
X		(void) signal(SIGQUIT, onintr);
X}
X
X/*
X * call postnews
X */
Xpnews(fname)
Xchar *fname;
X{
X	char *argv[MAXARGV];
X
X	argv[0] = strrchr(postnews, '/') + 1;
X	argv[1] = "-i";		/* TODO: what's this inews option? */
X	argv[2] = fname;
X	argv[3] = NIL(char);
X	run(postnews, argv, false);
X}
X
X/*
X * save an article
X */
Xsave(hp, f, s)
Xheader *hp;
XFILE *f;
Xchar *s;
X{
X	register long pos;
X	register int c;
X	register char *cp;
X	register FILE	*sf;
X	register char *aname;
X	long then;
X	extern char *getenv();
X
X	if (!*s) {
X		if ((aname = getenv("HOME")) == NIL(char)) {
X			(void) printf("No $HOME in environment.\n");
X			return;
X		}
X		s = aname = newstr3(aname, "/", ARTICLES);
X	} else
X		aname = NIL(char);
X	if ((sf = fopen(s, "a")) == NIL(FILE)) {
X		(void) fprintf(stderr, "readnews: can't open %s\n", s);
X		return;
X	}
X	if (aname)
X		free(aname);
X	pos = ftell(f);
X	rewind(f);
X	if (cp = strchr(hp->h_from, ' '))
X		*cp = '\0';
X	if (hp->h_date)
X		then = atot(hp->h_date);
X	else
X		then = 0L;
X	(void) fprintf(sf, "From %s %s", hp->h_from, ctime(then ? &then : &now));
X	if (cp)
X		*cp = ' ';
X	while ((c = getc(f)) != EOF)
X		(void) putc(c, sf);
X	(void) fclose(sf);
X	fseek(f, pos, 0);
X}
X
X
X/*
X * unsubscribe from all followup articles
X * on this topic
X */
Xunsubscribe(id)
Xchar *id;
X{
X	register char *s, c;
X
X	if (!id || !*id) {
X		(void) printf("no references! (warning)\n");
X		return;
X	}
X	while (*id) {
X		if (s = strpbrk(id, " ,")) {
X			c = *s;
X			*s = '\0';
X		}
X		usize++;
X		uflag = (uflag ? (char **) myrealloc((char *) uflag, (int) sizeof(char
X		    *) * usize) : (char **) myalloc((int) sizeof(char *)));
X		uflag[usize - 1] = newstr(id);
X		if (s)
X			*s = c, id = s + 1;
X		else
X			break;
X		while (isspace(*id) || *id == ',')
X			id++;
X	}
X	qsort((char *) uflag, (unsigned) usize, sizeof(char *), strpcmp);
X}
X
X
X/*
X * print an article, if it's long enough call page()
X */
Xprint(hp, f)
Xheader *hp;
XFILE *f;
X{
X	register int c;
X	register long pos;
X
X	pos = ftell(f);
X	if (!pflag
X#ifdef LINESHDRPRESENT
X	 && hp->h_lines && atoi(hp->h_lines) >= PAGESIZE - 4
X#endif
X	 )
X		page(f);
X	else
X		while ((c = getc(f)) != EOF)
X			(void) putchar(c);
X	fseek(f, pos, 0);
X}
X
X
X/*
X * copy article text to stdout, and break into pages
X */
Xpage(f)
XFILE *f;
X{
X	register int c;
X	register unsigned lineno;
X	char lbuf[80];
X	struct sgttyb ttybuf;
X
X#ifdef USG
X	/* TODO: fill in USG code to turn off echo */
X#else
X	gtty(fileno(stdin), &ttybuf);
X	if (ttybuf.sg_flags & ECHO) {
X		ttybuf.sg_flags &= ~ECHO;
X		stty(fileno(stdin), &ttybuf);
X		ttybuf.sg_flags |= ECHO;
X	}
X#endif
X	lineno = 1;
X	while (!interrupt) {
X		for (; lineno < PAGESIZE - 4 && !interrupt; lineno++) {
X			while ((c = getc(f)) != EOF && c != '\n')
X				(void) putchar(c);
X			if (c == EOF)
X				goto fastexit;
X			if (lineno < PAGESIZE - 5)
X				(void) putchar('\n');
X		}
X		if (interrupt)
X			break;
X#ifdef moremess
X		(void) printf(moremess);
X#endif
X		if (fflush(stdout) == EOF)
X			break;
X		if (read(fileno(stdin), lbuf, sizeof(lbuf)) <= 0)
X			break;
X#ifdef moremess
X		(void) printf("\r%*s\r", sizeof(moremess), " ");
X#else
X		putchar('\n');
X#endif
X		lineno = 0;
X	}
X	if (lineno)
X		(void) putchar('\n');
X	interrupt = false;
Xfastexit:
X#ifdef USG
X	;	/* TODO: fill in USG code to turn echo on */
X#else
X	if (ttybuf.sg_flags & ECHO)
X		stty(fileno(stdin), &ttybuf);
X#endif
X}
X
X/* VARARGS1 */
Xerror(s, a0, a1, a2, a3)
Xchar *s;
X{
X	(void) fprintf(stderr, "readnews: ");
X	(void) fprintf(stderr, s, a0, a1, a2, a3);
X	(void) fprintf(stderr, "\n");
X	exit(1);
X}
!
echo 'rna/checknews':
sed 's/^X//' >'rna/checknews' <<'!'
Xexec readnews -c
!
echo done


-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.