[comp.sources.unix] v19i095: Cnews production release, Part18/19

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

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

: ---CUT HERE---
echo 'rna/header.c':
sed 's/^X//' >'rna/header.c' <<'!'
X/*
X * extract/output headers
X */
X
X#include "defs.h"
X
X#if AUSAM
Xextern struct pwent pe;
X#else
Xextern struct passwd *pp;
X#endif
Xextern char systemid[];
Xextern long now;
X
Xchar tzone[]		 = TIMEZONE;
Xchar hform[]		 = "%s: %s\n";
X
X/* Mandatory Headers */
Xchar t_relayversion[]	 = "Relay-Version";
Xchar t_postversion[]	 = "Posting-Version";
Xchar t_from[]		 = "From";
Xchar t_date[]		 = "Date";
Xchar t_newsgroups[]	 = "Newsgroups";
Xchar t_subject[]	 = "Subject";
Xchar t_messageid[]	 = "Message-ID";
Xchar t_path[]		 = "Path";
X
X/* Optional Headers */
Xchar t_replyto[]	 = "Reply-To";
Xchar t_sender[]		 = "Sender";
Xchar t_followupto[]	 = "Followup-To";
Xchar t_datereceived[]	 = "Date-Received";
Xchar t_expires[]	 = "Expires";
Xchar t_references[]	 = "References";
Xchar t_control[]	 = "Control";
Xchar t_distribution[]	 = "Distribution";
Xchar t_organization[]	 = "Organization";
Xchar t_lines[]		 = "Lines";
X
Xtypedef enum ft
X{
X	f_control, f_date, f_datereceived, f_distribution,
X	f_expires, f_followupto, f_from, f_lines, f_messageid,
X	f_newsgroups, f_organization, f_path, f_postversion,
X	f_references, f_relayversion, f_replyto, f_sender,
X	f_subject
X}
X
X
Xftype;
X
Xtypedef struct field {
X	char *f_name;
X	ftype	f_type;
X} field;
X
Xstatic field fields[] = 
X{
X	{ t_control, 	f_control	 },
X	{ t_date, 		f_date		 },
X	{ t_datereceived, 	f_datereceived	 },
X	{ t_distribution, 	f_distribution	 },
X	{ t_expires, 	f_expires	 },
X	{ t_followupto, 	f_followupto	 },
X	{ t_from, 		f_from		 },
X	{ t_lines, 	f_lines		 },
X	{ t_messageid, 	f_messageid	 },
X	{ t_newsgroups, 	f_newsgroups	 },
X	{ t_organization, 	f_organization	 },
X	{ t_path, 		f_path		 },
X	{ t_postversion, 	f_postversion	 },
X	{ t_references, 	f_references	 },
X	{ t_relayversion, 	f_relayversion	 },
X	{ t_replyto, 	f_replyto	 },
X	{ t_sender, 	f_sender	 },
X	{ t_subject, 	f_subject	 }
X};
X
X
Xchar *weekdays[7] = 
X{
X	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
X};
X
X
Xchar *months[12] = 
X{
X	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
X	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
X};
X
X
Xstatic
Xfieldcmp(a, b)
Xfield *a, *b;
X{
X	return CMP(a->f_name, b->f_name);
X}
X
X
X/*
X * extract headers from file,
X * position file to start of body
X */
Xgethead(f, hp)
XFILE *f;
Xheader *hp;
X{
X	register char *colon, *space, *s;
X	register field	*fp;
X	field		af;
X	char buf[BUFLEN*2];
X
X	char *hfgets();
X
X	memset((char *) hp, 0, sizeof(header));
X	while (hfgets(buf, sizeof(buf), f)) {
X		if (buf[0] == '\n')
X			return;
X		if (isupper(buf[0]) && (colon = strchr(buf, ':')) && (space =
X		    strchr(buf, ' ')) && (colon + 1 == space)) {
X			*colon = '\0';
X			af.f_name = buf;
X			fp = (field * ) bsearch((char *) & af, (char *) fields,
X			     				sizeof(fields) / sizeof(fields[0]), sizeof(fields[0]),
X			     fieldcmp);
X			*colon = ':';
X		} else
X			fp = NIL(field);
X		if (!fp)
X			if (hp->h_others)
X				hp->h_others = catstr(hp->h_others, buf);
X			else
X				hp->h_others = newstr(buf);
X		else
X		 {
X			if (colon = strchr(space + 1, '\n'))
X				*colon = '\0';
X			s = newstr(space + 1);
X			switch (fp->f_type) {
X			case f_control:		
X				hp->h_control = s;	
X				break;
X			case f_date:		
X				hp->h_date = s;		
X				break;
X			case f_datereceived:	
X				hp->h_datereceived = s;	
X				break;
X			case f_distribution:	
X				hp->h_distribution = s;	
X				break;
X			case f_expires:		
X				hp->h_expires = s;	
X				break;
X			case f_followupto:	
X				hp->h_followupto = s;	
X				break;
X			case f_from:		
X				hp->h_from = s;		
X				break;
X			case f_lines:		
X				hp->h_lines = s;	
X				break;
X			case f_messageid:	
X				hp->h_messageid = s;	
X				break;
X			case f_newsgroups:	
X				hp->h_newsgroups = s;	
X				break;
X			case f_organization:	
X				hp->h_organisation = s;	
X				break;
X			case f_path:		
X				hp->h_path = s;		
X				break;
X			case f_postversion:	
X				hp->h_postversion = s;	
X				break;
X			case f_references:	
X				hp->h_references = s;	
X				break;
X			case f_relayversion:	
X				hp->h_relayversion = s;	
X				break;
X			case f_replyto:		
X				hp->h_replyto = s;	
X				break;
X			case f_sender:		
X				hp->h_sender = s;	
X				break;
X			case f_subject:		
X				hp->h_subject = s;	
X				break;
X			}
X		}
X	}
X}
X
X
X/*
X * put headers to file
X */
Xputhead(hp, f, com)
Xheader *hp;
XFILE *f;
Xpheadcom com;
X{
X	register char *s;
X	char *getunique();
X	extern char *getenv();
X
X	if (hp->h_relayversion && com == printing)
X		(void) fprintf(f, hform, t_relayversion, hp->h_relayversion);
X	else if (com != printing)
X		(void) fprintf(f, "%s: version %s; site %s.%s\n", t_relayversion, NEWSVERSION,
X		     systemid, MYDOMAIN);
X
X	if (hp->h_postversion)
X		(void) fprintf(f, hform, t_postversion, hp->h_postversion);
X	else if (com == making)
X		(void) fprintf(f, "%s: version %s; site %s.%s\n", t_postversion, NEWSVERSION,
X		     systemid, MYDOMAIN);
X
X
X	if (hp->h_from)
X		(void) fprintf(f, hform, t_from, hp->h_from);
X	else if(com == making) {
X		if(s = getenv("NAME"))
X			(void) fprintf(f, "%s: %s@%s.%s (%s)\n", t_from,
X#if AUSAM
X				pe.pw_strings[LNAME],
X#else
X				pp->pw_name,
X#endif
X				systemid, MYDOMAIN, s);
X		else
X			(void) fprintf(f,
X#if AUSAM
X				"%s: %s@%s.%s (%s %s)\n",
X#else
X				"%s: %s@%s.%s\n",
X#endif
X				t_from,
X#if AUSAM
X				pe.pw_strings[LNAME],
X#else
X				pp->pw_name,
X#endif
X				systemid, MYDOMAIN
X#if AUSAM
X				,
X				pe.pw_strings[FIRSTNAME],
X				pe.pw_strings[LASTNAME]
X#endif
X			);
X	}
X
X	if (hp->h_date)
X		(void) fprintf(f, hform, t_date, hp->h_date);
X	else if (com == making)
X		(void) fprintf(f, hform, t_date, ttoa(now));
X
X	if (hp->h_newsgroups)
X		(void) fprintf(f, hform, t_newsgroups, hp->h_newsgroups);
X	else if (com == making)
X		(void) fprintf(f, hform, t_newsgroups, DFLTGRP);
X
X	if (hp->h_subject)
X		(void) fprintf(f, hform, t_subject, hp->h_subject);
X	else if (com == making)
X		error("No subject field.");
X
X	if (hp->h_messageid)
X		(void) fprintf(f, hform, t_messageid, hp->h_messageid);
X	else if (com == making)
X		error("No messageid.");
X
X	if (hp->h_path && com == passing)
X		(void) fprintf(f, "%s: %s!%s\n", t_path, systemid, hp->h_path);
X	else if (hp->h_path)
X		(void) fprintf(f, hform, t_path, hp->h_path);
X	else if(com == making)
X		(void) fprintf(f, "%s: %s!%s\n", t_path, systemid,
X#if AUSAM
X			pe.pw_strings[LNAME]
X#else
X			pp->pw_name
X#endif
X		);
X
X	/* optional */
X
X	if (hp->h_replyto)
X		(void) fprintf(f, hform, t_replyto, hp->h_replyto);
X
X	if (hp->h_sender)
X		(void) fprintf(f, hform, t_sender, hp->h_sender);
X
X	if (hp->h_followupto)
X		(void) fprintf(f, hform, t_followupto, hp->h_followupto);
X
X	if (hp->h_datereceived && com == printing)
X		(void) fprintf(f, hform, t_datereceived, hp->h_datereceived);
X	else if (com != printing)
X		(void) fprintf(f, hform, t_datereceived, ttoa(now));
X
X	if (hp->h_expires)
X		(void) fprintf(f, hform, t_expires, hp->h_expires);
X
X	if (hp->h_references)
X		(void) fprintf(f, hform, t_references, hp->h_references);
X
X	if (hp->h_control)
X		(void) fprintf(f, hform, t_control, hp->h_control);
X
X	if (hp->h_distribution)
X		(void) fprintf(f, hform, t_distribution, hp->h_distribution);
X
X	if (hp->h_organisation)
X		(void) fprintf(f, hform, t_organization, hp->h_organisation);
X	else if (com == making)
X		(void) fprintf(f, hform, t_organization, (s = getenv("ORGANIZATION")) ?
X		    s : MYORG);
X
X	if (hp->h_lines)
X		(void) fprintf(f, hform, t_lines, hp->h_lines);
X
X	if (hp->h_others)
X		fputs(hp->h_others, f);
X}
X
X
X/*
X * free all strings allocated to header
X */
Xfreehead(hp)
Xregister header *hp;
X{
X	if (hp->h_relayversion)	
X		free(hp->h_relayversion);
X	if (hp->h_postversion)	
X		free(hp->h_postversion);
X	if (hp->h_from)		
X		free(hp->h_from);
X	if (hp->h_date)		
X		free(hp->h_date);
X	if (hp->h_newsgroups)	
X		free(hp->h_newsgroups);
X	if (hp->h_subject)	
X		free(hp->h_subject);
X	if (hp->h_messageid)	
X		free(hp->h_messageid);
X	if (hp->h_path)		
X		free(hp->h_path);
X	if (hp->h_replyto)	
X		free(hp->h_replyto);
X	if (hp->h_sender)	
X		free(hp->h_sender);
X	if (hp->h_followupto)	
X		free(hp->h_followupto);
X	if (hp->h_datereceived)	
X		free(hp->h_datereceived);
X	if (hp->h_expires)	
X		free(hp->h_expires);
X	if (hp->h_references)	
X		free(hp->h_references);
X	if (hp->h_control)	
X		free(hp->h_control);
X	if (hp->h_distribution)	
X		free(hp->h_distribution);
X	if (hp->h_organisation)	
X		free(hp->h_organisation);
X	if (hp->h_lines)		
X		free(hp->h_lines);
X	if (hp->h_others)	
X		free(hp->h_others);
X}
X
X
X/*
X * hfgets is like fgets, but deals with continuation lines.
X * It also ensures that even if a line that is too long is
X * received, the remainder of the line is thrown away
X * instead of treated like a second line.
X */
Xchar *
Xhfgets(buf, len, fp)
Xchar *buf;
Xint len;
XFILE *fp;
X{
X	register int c;
X	register char *cp, *tp;
X
X	if ((cp = fgets(buf, len, fp)) == NIL(char))
X		return NIL(char);
X
X	if (*cp == '\n')
X		return cp;
X
X	tp = cp + strlen(cp);
X	if (tp[-1] != '\n') {
X		/* Line too long - part read didn't fit into a newline */
X		while ((c = getc(fp)) != '\n' && c != EOF)
X			;
X	} else
X		*--tp = '\0';	/* clobber newline */
X
X	while ((c = getc(fp)) == ' ' || c == '\t') {
X		/* Continuation line. */
X		while ((c = getc(fp)) == ' ' || c == '\t')
X			;
X		if (tp - cp < len) {
X			*tp++ = ' ';
X			*tp++ = c;
X		}
X		while ((c = getc(fp)) != '\n' && c != EOF)
X			if (tp - cp < len)
X				*tp++ = c;
X	}
X	*tp++ = '\n';
X	*tp++ = '\0';
X	if (c != EOF)
X		ungetc(c, fp);	/* push back first char of next header */
X	return cp;
X}
X
X
X/*
X * time to ascii
X *	leave time in static var
X */
Xchar *
Xttoa(t)
Xlong t;
X{
X	static char buf[40];
X	struct tm *tp;
X	extern struct tm *localtime();
X
X	tp = localtime(&t);
X	sprintf(buf, "%s, %d %s %d %02d:%02d:%02d %s", weekdays[tp->tm_wday],
X	     tp->tm_mday, months[tp->tm_mon], tp->tm_year, tp->tm_hour, tp->tm_min,
X	     tp->tm_sec, tzone);
X	return buf;
X
X}
X
X
X/*
X * ascii to time
X * return 0L on error
X */
Xlong
Xatot(s)
Xchar *s;
X{
X	char *argv[4];
X	int day, year, hour, min, sec;
X	char month[10], sday[10], stime[10], syear[10];
X	extern long maketime();
X
X	if (sscanf(s, "%*s %d %*[ -] %9[^ -] %*[ -] %d %2d:%2d:%2d", &day, month,
X	     &year, &hour, &min, &sec) != 6)
X		return 0L;
X	sprintf(sday, "%d", day);
X	sprintf(stime, "%d:%d:%d", hour, min, sec);
X	sprintf(syear, "%d", 1900 + year);
X	argv[0] = sday;
X	argv[1] = month;
X	argv[2] = stime;
X	argv[3] = syear;
X	return maketime(4, argv, STIMES);
X}
X
X
!
echo 'rna/history.c':
sed 's/^X//' >'rna/history.c' <<'!'
X/*
X * History file
X * each line contains a message-id, install or expire time
X * names of linked files
X */
X
X#include "defs.h"
X
Xstatic char histname[]	 = HISTORY;
Xstatic char *histid;			/* messageid to save */
Xstatic char *histline;			/* list of linked files */
Xstatic long etime;			/* expire time */
X
Xtypedef enum stypes { 
X	chk, delete } stype;
X
X/*
X * do things with history file
X * chk - see if id present
X * delete - delete article with id
X */
Xstatic bool
Xsearchhist(id, type)
Xchar *id;
Xstype type;
X{
X	register FILE	*f;
X	register char *s, *name;
X	register bool	found;
X	char buf[BUFSIZ * 2];
X
X	extern char *newsdir;
X
X	f = fopenl(histname);
X
X	found = false;
X	while (fgets(buf, sizeof(buf), f)) {
X		if (s = strchr(buf, ' '))
X			*s = '\0';
X		else
X			error("Bad format: %s", histname);
X		if (CMP(buf, id) == 0) {
X			found = true;
X			break;
X		}
X	}
X	if (found && type == delete) {
X		if ((name = strchr(s + 1, ' ')) == NIL(char))
X			error("Bad format: %s", histname);
X		name++;
X		while (name && (s = strpbrk(name, " \n"))) {
X			*s = '\0';
X			name = newstr3(newsdir, "/", name);
X			remove(name);
X			free(name);
X			name = s + 1;
X		}
X	}
X	fclose(f);
X#if !AUSAM
X	unlock(histname);
X#endif
X	return found;
X}
X
X
X/*
X * delete files given id
X */
Xbool
Xcancel(id)
Xchar *id;
X{
X	bool searchhist();
X
X	return searchhist(id, delete);
X}
X
X
X/*
X * check if article has been recieved
X */
Xbool
Xchkhist(id)
Xchar *id;
X{
X	bool searchhist();
X
X	return searchhist(id, chk);
X}
X
X
X/*
X * scan history, clearing uflag list entry if id not seen
X */
Xscanhist(ulist, usize)
Xchar **ulist;
Xint usize;
X{
X	register FILE	*f;
X	register char *s, **found;
X	register int i;
X	char *key[1];
X	char buf[BUFSIZ * 2];
X	bool		 * seen;
X
X	extern char *newsdir;
X
X	seen = (bool * ) myalloc((int) sizeof(bool) * usize);
X	memset((char *)seen, 0, (int) sizeof(bool) * usize);
X
X	f = fopenf(histname, "r");
X	while (fgets(buf, sizeof(buf), f)) {
X		if (s = strchr(buf, ' '))
X			*s = '\0';
X		else
X			error("Bad format: %s", histname);
X		key[0] = buf;
X		found = (char **) bsearch((char *) key, (char *) ulist, (unsigned) usize,
X		     sizeof(char *), strpcmp);
X		if (found)
X			seen[found - ulist] = true;
X	}
X	fclose(f);
X
X	for (i = 0; i < usize; i++)
X		if (!seen[i]) {
X			free(ulist[i]);
X			ulist[i] = NIL(char);
X		}
X	free((char *)seen);
X}
X
X
X/*
X * open hist file, write id and time
X */
Xopenhist(hp)
Xheader *hp;
X{
X
X	histid = newstr(hp->h_messageid);
X	if (hp->h_expires)
X		etime = atot(hp->h_expires);
X	else
X		etime = 0L;
X	histline = NIL(char);
X}
X
X
X/*
X * write name of file article resides into history file
X */
Xwritehist(fname)
Xchar *fname;
X{
X	histline = (histline ? catstr2(histline, " ", fname) : newstr(fname));
X}
X
X
X/*
X * close history file
X */
Xclosehist()
X{
X	register FILE	*f;
X	extern long now;
X
X	f = fopenl(histname);
X	fseek(f, 0L, 2);
X	(void) fprintf(f, "%s %s%ld %s\n", histid, etime ? "E" : "", etime ? etime :
X	    now, histline);
X	fclose(f);
X#if !AUSAM
X	unlock(histname);
X#endif
X	free(histid); 
X	free(histline);
X}
X
X
X/*
X * remove a news item
X * check owner first
X */
Xstatic
Xremove(fname)
Xchar *fname;
X{
X	header			h;
X	FILE			 * f;
X	register char *s, *mname;
X
X#if AUSAM
X	extern struct pwent pe;
X#else
X	extern struct passwd *pp;
X#endif
X	extern char systemid[];
X	extern bool		su;
X	extern bool		pflag;
X
X	if (!su && !pflag) {
X		f = fopenf(fname, "r");
X		gethead(f, &h);
X		fclose(f);
X		if (s = strchr(h.h_from, ' '))
X			*s = '\0';
X		mname = newstr5(
X#if AUSAM
X				pe.pw_strings[LNAME],
X#else
X				pp->pw_name,
X#endif
X			"@", systemid, ".", MYDOMAIN);
X		if (CMP(mname, h.h_from) != 0)
X			error("Can't cancel articles you didn't write.");
X		free(mname);
X	}
X	if (unlink(fname) != 0)
X		error("Couldn't unlink %s", fname);
X
X}
!
echo 'rna/lib/bsearch.c':
sed 's/^X//' >'rna/lib/bsearch.c' <<'!'
X/*LINTLIBRARY*/
X/*
X * Binary search algorithm, generalized from Knuth (6.2.1) Algorithm B.
X *
X * Written by J. S. Rugaber; rewritten by L. Rosler, Dept. 45175, August, 1981.
X */
X
Xtypedef char *POINTER;
X
XPOINTER
Xbsearch(key, base, nel, width, compar)
XPOINTER	key;			/* Key to be located */
XPOINTER	base;			/* Beginning of table */
Xunsigned nel;			/* Number of elements in the table */
Xunsigned width;			/* Width of an element (bytes) */
Xint	(*compar)();		/* Comparison function */
X{
X	int two_width = width + width;
X	POINTER last = base + width * (nel - 1); /* Last element in table */
X
X	while (last >= base) {
X
X		register POINTER p = base + width * ((last - base)/two_width);
X		register int res = (*compar)(key, p);
X
X		if (res == 0)
X			return (p);	/* Key found */
X		if (res < 0)
X			last = p - width;
X		else
X			base = p + width;
X	}
X	return ((POINTER) 0);		/* Key not found */
X}
!
echo 'rna/lib/itoa.c':
sed 's/^X//' >'rna/lib/itoa.c' <<'!'
Xchar *
Xitoa(i)
Xint i;
X{
X	static char buf[30];
X
X	sprintf(buf, "%d", i);
X	return buf;
X}
!
echo 'rna/lib/makefile':
sed 's/^X//' >'rna/lib/makefile' <<'!'
X# makefile for Australian readnews library
XRANLIB=ranlib
X# workaround for System V make bug
XSHELL = /bin/sh
X
Xlib.a: bsearch.o itoa.o memset.o strpbrk.o
X	ar rv $@ bsearch.o itoa.o memset.o strpbrk.o
X	$(RANLIB) $@
Xclean:
X	rm -f *.o *.a
!
echo 'rna/lib/memset.c':
sed 's/^X//' >'rna/lib/memset.c' <<'!'
X/*LINTLIBRARY*/
X/*
X * Set an array of n chars starting at sp to the character c.
X * Return sp.
X */
Xchar *
Xmemset(sp, c, n)
Xregister char *sp, c;
Xregister int n;
X{
X	register char *sp0 = sp;
X
X	while (--n >= 0)
X		*sp++ = c;
X	return (sp0);
X}
!
echo 'rna/lib/strpbrk.c':
sed 's/^X//' >'rna/lib/strpbrk.c' <<'!'
X/*
X * Return ptr to first occurance of any character from `brkset'
X * in the character string `string'; NULL if none exists.
X */
X
X#define	NULL	(char *) 0
X
Xchar *
Xstrpbrk(string, brkset)
Xregister char *string, *brkset;
X{
X	register char *p;
X
X	do {
X		for(p=brkset; *p != '\0' && *p != *string; ++p)
X			;
X		if(*p != '\0')
X			return(string);
X	}
X	while(*string++);
X	return(NULL);
X}
!
echo 'rna/COVER.pd':
sed 's/^X//' >'rna/COVER.pd' <<'!'
XFrom decvax!mulga!michaelr:elecvax Mon Jul 16 04:25:19 1984
XFrom: decvax!mulga!michaelr:elecvax
XReceived: by decvax.UUCP (4.12/1.0)
X	id AA25742; Mon, 16 Jul 84 03:56:35 edt
XReceived: by mulga.OZ (4.3)
X	id AA06943; Mon, 16 Jul 84 14:19:46 EST
XTo: decvax!utcsstat!geoff:mulga
XSubject: Re: your news re-write
X
XYes, my news re-write is in the public domain.
XI couldn't sell it if I wanted to, since it was developed under an
Xeducational license.
X
XPlease distribute it freely as you wish.
X
XMichael Rourke
XUniversity of New South Wales, Kensington, N.S.W. 2033 AUSTRALIA
XPhone:	+61 2 662 2781	Netaddr: {decvax,vax135,sfjec}!mulga!michaelr:elecvax
X
X
!
echo 'rna/makefile':
sed 's/^X//' >'rna/makefile' <<'!'
X# australian readnews makefile
X# =()<NEWSARTS = @<NEWSARTS>@>()=
XNEWSARTS = /usr/spool/news
X# =()<NEWSBIN = @<NEWSBIN>@>()=
XNEWSBIN = /usr/lib/newsbin
X# =()<NEWSCTL = @<NEWSCTL>@>()=
XNEWSCTL = /usr/lib/news
X# workaround for System V make bug
XSHELL = /bin/sh
X
XDEFINES=
XCOPTS=-O
XCFLAGS=$(COPTS) $(DEFINES)
XINSTALL=:# install bigpdp
XNFLAG =
XLINT = lint -ha $(DEFINES)
XI=/usr/include
XC=/lib/libc.a
XLIBS=lib/lib.a ../libcnews.a
XBIN=/bin
XBINDIR=$(BIN)
X
XCOMMANDS = postnews uurec readnews uusend expire postgroup
XFILES = help
XLCOMMANDS = postnews.lint uurec.lint readnews.lint uusend.lint expire.lint
XPFILES = header.c postnews.c funcs.c active.c history.c maketime.c mtempnam.c
XRFILES = header.c readnews.c funcs.c active.c newsrc.c history.c maketime.c
XROFILES= header.o readnews.o funcs.o active.o newsrc.o history.o maketime.o
XEFILES = expire.c funcs.c active.c
X
X.c.lint:
X	$(LINT) $< > $@
X
X.c:
X	$(CC) $(CFLAGS) $*.c $(NFLAG) -o $(@F)
X	$(INSTALL) $* bin 711 $@
X
X.sh:
X	$(INSTALL) -c $*.sh bin 755 $@
X
Xall: readnews checknews
X	: warning, defs.h may not be right for your system
X
Xreadnews: $(ROFILES) defs.h $(LIBS)
X	$(CC) $(CFLAGS) $(ROFILES) $(LIBS) -o $@
X
Xnewsinstall:
X	: "install all control files; don't overwrite any!"
X	-if test ! -r $(NEWSCTL)/news.help; then cp news.help $(NEWSCTL); fi
X
X# bininstall: make directories, install programs
Xbininstall: install
Xinstall: $(BINDIR)/readnews $(BINDIR)/checknews
X$(BINDIR)/readnews: readnews
X	cp readnews $(BINDIR)
X	: "$(INSTALL) - readnews bin 711 $(BINDIR)/readnews $(BINDIR)/news"
X$(BINDIR)/checknews:	checknews
X	chmod +x checknews
X	cp checknews $(BINDIR)
X
Xdefs.h:	at.h # $C
X	touch defs.h
X
Xlint: $(LCOMMANDS)
X
Xhelp: $(NEWSCTL)/help
X$(NEWSCTL)/help: news.help
X	$(INSTALL) -c news.help news 644 $(NEWSCTL)/help
X
Xlib/lib.a:
X	: if you do not have ranlib, you will need to edit lib/makefile
X	(cd lib; make)
X
X$(ROFILES): defs.h
X
Xpostnews: $(BINDIR)/postnews
X$(BINDIR)/postnews: $(PFILES) defs.h
X	$(CC) $(CFLAGS) $(PFILES) -o postnews
X	$(INSTALL) - postnews news 6711 $(BINDIR)/postnews
X
Xpostgroup: $(BINDIR)/postgroup
X$(BINDIR)/postgroup: postgroup.sh
X
Xuurec: $(NEWSCTL)/uurec
X$(NEWSCTL)/uurec: uurec.c defs.h
X
Xuusend: $(NEWSCTL)/uusend
X$(NEWSCTL)/uusend: defs.h uusend.c
X
Xexpire: $(NEWSCTL)/expire
X$(NEWSCTL)/expire: $(EFILES) defs.h
X	$(CC) $(CFLAGS) $(EFILES) $(NFLAG) -o expire
X	$(INSTALL) expire news 700 $(NEWSCTL)/expire
X
Xpostnews.lint: $(PFILES)
X	$(LINT) $(PFILES) > postnews.lint
X
Xreadnews.lint: $(RFILES)
X	$(LINT) $(RFILES) > readnews.lint
X
Xexpire.lint: $(EFILES)
X	$(LINT) $(EFILES) > expire.lint
X
Xuurec.lint: uurec.c defs.h
Xuusend.lint: uusend.c defs.h
X
X.FINISH:
X	rm -s *.o
Xclean:
X	rm -f *.o core a.out readnews
X	(cd lib ; make clean)
!
echo 'rna/maketime.c':
sed 's/^X//' >'rna/maketime.c' <<'!'
X/*
X * long
X * maketime(argc, argv, type)
X *
X * A standard routine to convert a future time (in English) to seconds.
X * Arguments are order-independent (except for suffixes), and words
X * may be shortened to a non-ambiguous abbreviation.
X * As the time must be in the future, unspecified years, months and days default
X * to the "next" year, month or day if necessary; otherwise the current
X * month, day and hour are used.
X *
X * type is either TIMES in which days, times are recognised, or just DAYS.
X *
X * Tries hard to give meaningful messages, and make sure the user
X * gets the time she/he wanted!
X *
X * Return is in seconds or 0 if error.
X * Error messages to stderr.
X *
X * Michael Rourke (UNSW) Christmas 1982
X *
X * Syntax:
X *
X *	timespec ::= { time | day | month | year } .
X *	
X *	time ::= [ hour [ ":" min [ ":" second ] ] ] [ timemodifier ] .
X *	
X *	timemodifier ::= "am" | "pm" | "noon" | "midday" | "midnight" | "now" .
X *	
X *	day ::= ( dayofweek [ "week" ] ) | number .
X *	
X *	dayofweek ::= "sunday" | "monday" | "tuesday" | "wednesday" |
X *		      "thursday" | "friday" | "saturday" | "tomorrow" |
X *		      "today" .
X *	
X *	month ::= "january" | "february" | "march" | "april" | "may" | "june" |
X *		  "july" | "august" | "september" | "october" | "november" |
X *		  "december" .
X *	
X *	year ::= "19" number .
X *
X */
X
X#include "defs.h"
X
X#define	NOW 	-1
X
Xstatic timemod(), noonmid(), daymod(), weekday(), smonth();
X
Xstatic struct slist {
X	char *s_name;
X	int (*s_action)();
X	char s_val;
X	char s_type;
X} slist[] = 
X{
X	{ "am", 		timemod, 	0, 	TIMES, 	 },
X	{ "pm", 		timemod, 	12, 	TIMES, 	 },
X	{ "noon", 		noonmid, 	12, 	TIMES, 	 },
X	{ "midday", 	noonmid, 	12, 	TIMES, 	 },
X	{ "midnight", 	noonmid, 	0, 	TIMES, 	 },
X	{ "now", 		noonmid, 	NOW, 	TIMES, 	 },
X	{ "week", 		daymod, 		0, 	DAYS, 	 },
X	{ "sunday", 	weekday, 	0, 	DAYS, 	 },
X	{ "monday", 	weekday, 	1, 	DAYS, 	 },
X	{ "tuesday", 	weekday, 	2, 	DAYS, 	 },
X	{ "wednesday", 	weekday, 	3, 	DAYS, 	 },
X	{ "thursday", 	weekday, 	4, 	DAYS, 	 },
X	{ "friday", 	weekday, 	5, 	DAYS, 	 },
X	{ "saturday", 	weekday, 	6, 	DAYS, 	 },
X	{ "tomorrow", 	weekday, 	7, 	DAYS, 	 },
X	{ "today", 	weekday, 	8, 	DAYS, 	 },
X	{ "january", 	smonth, 		0, 	DAYS, 	 },
X	{ "february", 	smonth, 		1, 	DAYS, 	 },
X	{ "march", 	smonth, 		2, 	DAYS, 	 },
X	{ "april", 	smonth, 		3, 	DAYS, 	 },
X	{ "may", 		smonth, 		4, 	DAYS, 	 },
X	{ "june", 		smonth, 		5, 	DAYS, 	 },
X	{ "july", 		smonth, 		6, 	DAYS, 	 },
X	{ "august", 	smonth, 		7, 	DAYS, 	 },
X	{ "september", 	smonth, 		8, 	DAYS, 	 },
X	{ "october", 	smonth, 		9, 	DAYS, 	 },
X	{ "november", 	smonth, 		10, 	DAYS, 	 },
X	{ "december", 	smonth, 		11, 	DAYS, 	 },
X	{ "", 		0, 		0, 	0, 	 }
X};
X
X
Xstatic char daysinmonth[12] = 
X{
X	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
X};
X
X
Xstatic int hour, minute, second, day, year, dayofweek, month;
Xstatic int settime, setday, setyear, setdayofweek, setmonth;
Xstatic int setweek, err, setmod;
Xstatic char *curarg;
Xstatic struct tm *tim;
Xstatic int gtype;		/* global `type' arg */
Xstatic short silent;
X
Xlong
Xmaketime(argc, argv, type)
Xint argc;
Xchar **argv;
Xint type;
X{
X	struct tm *localtime();
X	long time(), construct(), now, then;
X
X	if (type == STIMES)
X		type = TIMES, silent = 1;
X	else
X		silent = 0;
X	gtype = type;
X	now = time((long *) 0);
X	tim = localtime(&now);
X
X	/*
X	 * set defaults 
X	 */
X	hour = tim->tm_hour;
X	minute = tim->tm_min;
X	second = tim->tm_sec;
X	day = tim->tm_mday;
X	year = tim->tm_year + 1900;
X	dayofweek = tim->tm_wday;
X	month = tim->tm_mon;
X
X	settime = setday = setyear = setdayofweek = setmonth = 0;
X	setweek = err = setmod = 0;
X
X	while (argc--)
X		timearg(curarg = *argv++);
X	if (err)
X		return (long) 0;
X
X	checktime();
X	if (err)
X		return (long) 0;
X
X	then = construct();
X	/*
X	if(now > then)
X	{
X		error("Time specified has passed.");
X		return (long) 0;
X	}
X*/
X	return then;
X}
X
X
Xstatic
Xtimearg(s)
Xchar *s;
X{
X	lower(s);
X	if (isdigit(*s))
X		numbers(s);
X	else
X		words(s);
X}
X
X
Xstatic
Xlower(s)
Xregister char *s;
X{
X	while (*s) {
X		*s = tolower(*s);
X		s++;
X	}
X}
X
X
Xstatic
Xnumbers(s)
Xregister char *s;
X{
X	register int val;
X
X	val = 0;
X	while (isdigit(*s))
X		val = val * 10 + *s++ - '0';
X	if (val > 1900)
X		if (setyear++)
X			reperror("year");
X		else
X			year = val;
X	else if (*s == '\0')
X		if (setday++)
X			reperror("day");
X		else
X			day = val;
X	else if (settime++)
X		reperror("time");
X	else
X	 {
X		hour = val;
X		if (*s == ':') {
X			s++;
X			val = 0;
X			while (isdigit(*s))
X				val = val * 10 + *s++ - '0';
X			minute = val;
X			if (*s == ':') {
X				s++;
X				val = 0;
X				while (isdigit(*s))
X					val = val * 10 + *s++ - '0';
X				second = val;
X			} else
X				second = 0;
X		} else
X			minute = second = 0;
X	}
X	if (*s)
X		words(curarg = s);
X}
X
X
Xstatic
Xreperror(s)
Xchar *s;
X{
X	error("Repeated %s argument: \"%s\"", s, curarg);
X}
X
X
X/* VARARGS1 */
Xstatic
Xerror(s, a1, a2, a3, a4)
Xchar *s;
Xint a1, a2, a3, a4;
X{
X	err++;
X	if (silent)
X		return;
X	(void) fprintf(stderr, "Error in time specification: ");
X	(void) fprintf(stderr, s, a1, a2, a3, a4);
X	(void) fprintf(stderr, "\n");
X}
X
X
Xstatic
Xwords(s)
Xchar *s;
X{
X	register struct slist *sp, *found;
X	register int size;
X	register char *wstart;
X
X	sp = slist;
X	wstart = s;
X	size = 0;
X	while (*s && !isdigit(*s))
X		size++, s++;
X	found = (struct slist *) 0;
X	while (*(sp->s_name)) {
X		if (sp->s_type <= gtype && CMPN(sp->s_name, wstart, size) ==
X		    0)
X			if (!found) {
X				found = sp;
X				if (strlen(sp->s_name) == size)
X					break;	/* otherwise an abbreviation */
X			}
X			else
X			 {
X				error("Ambiguous abbreviation: \"%.*s\"", size,
X				     wstart);
X				return;
X			}
X		sp++;
X	}
X	if (found)
X		(*(found->s_action))(found->s_val);
X	else
X		error("Unknown word: \"%.*s\"", size, wstart);
X	if (*s)
X		numbers(curarg = s);
X}
X
X
Xstatic
Xtimemod(val)
Xint val;
X{
X	if (!settime)
X		error("Can only use \"am\" or \"pm\" after a time.");
X	else if (setmod++)
X		reperror("time modifier");
X	else if (hour < 12)
X		hour += val;
X	else if (hour > 12)
X		error("Can't use \"am\" or \"pm\" with 24 hour clock.");
X	else if (val == 0) /* am && hour == 12 */
X		hour = 0;	/* 12am correction */
X}
X
X
Xstatic
Xnoonmid(val)
Xint val;
X{
X	if (val < 0)	/* NOW */ {
X		if (settime++)
X			reperror("time");
X		/* let defaults work */
X	} else if (setmod++) /* noon, midnight */
X		reperror("time modifier");
X	else
X	 {
X		if (!settime)
X			settime++;
X		else if (hour != 12 || minute != 0 || second != 0)
X			error("Illegal time: %02d:%02d:%02d %s", hour, minute,
X			     second, curarg);
X		hour = val;
X		minute = second = 0;
X	}
X}
X
X
Xstatic
Xdaymod()
X{
X	if (setweek++)
X		reperror("\b");
X	else if (!setdayofweek)
X		error("Can only use \"week\" after a weekday name.");
X	else
X		dayofweek += 7;
X}
X
X
Xstatic
Xweekday(val)
Xint val;
X{
X	if (setday++)
X		reperror("day");
X	else
X	 {
X		setdayofweek++;
X		if (val < 7) {
X			dayofweek = val - dayofweek;	/* now a displacement */
X			if (dayofweek <= 0)
X				dayofweek += 7;
X		} else if (val == 7) /* tomorrow */
X			dayofweek = 1;
X		else	/* today */
X			dayofweek = 0;
X	}
X}
X
X
Xstatic
Xsmonth(val)
Xint val;
X{
X	if (setmonth++)
X		reperror("day of month");
X	else
X		month = val;
X}
X
X
Xstatic
Xchecktime()
X{
X	register int dim;
X
X	if (gtype == DAYS && settime)
X		error("Times are not accepted.");
X	if (year < 1983 || year > 2038)
X		error("Year out of range.");
X	if (hour > 23 || minute > 59 || second > 59)
X		error("Illegal time: %02d:%02d:%02d", hour, minute, second);
X	if (!setdayofweek) {
X		dim = daysinmonth[month] + (month == 1 ? leapyear(year) : 0);
X		if (day > dim)
X			error("Month day out of range. (> %d)", dim);
X	}
X	if (setdayofweek && (setmonth || setyear))
X		error("Can't specify a weekday as well as a month or year.");
X}
X
X
Xstatic
Xleapyear(y)
Xint y;
X{
X	return ((y % 4) == 0 && (y % 100) != 0) || (y % 400 == 0);
X}
X
X
Xstatic long 
Xconstruct()
X{
X	register int i, days;
X
X	adjust();
X	days = DAYSTO1983;
X	for (i = 1983; i < year; i++)
X		days += 365 + leapyear(i);
X	for (i = 0; i < month; i++)
X		days += daysinmonth[i] + (i == 1 ? leapyear(year) : 0);
X	days += day - 1;	/* days since 1 Jan 1970 */
X	if (setdayofweek)
X		days += dayofweek;
X	return days * SECINDAY + hour * SECINHOUR + minute * SECINMIN + second;
X}
X
X
Xstatic
Xadjust()
X{
X	register int dim;
X
X	/*
X	 * make sure time defaults to the future
X	 */
X	if (setdayofweek || setyear || month > tim->tm_mon)
X		return;
X	if (month < tim->tm_mon) {
X		year++;
X		return;
X	}
X	/*
X	 * month == tim->tm_mon
X	 */
X	if (day > tim->tm_mday)
X		return;
X	if (day < tim->tm_mday) {
X		if (setmonth || ++month / 12)
X			year++, month %= 12;
X		return;
X	}
X	/*
X	 * month == tim->tm_mon && day == tim->tm_mday
X	 */
X	if ((long)(hour*SECINHOUR + minute*SECINMIN + second) <
X	    (long)(tim->tm_hour*SECINHOUR + tim->tm_min*SECINMIN + tim->tm_sec)) {
X		dim = daysinmonth[month] + (month == 1? leapyear(month): 0);
X		if (setday || ++day / dim) {
X			if (setmonth || ++month / 12)
X				year++, month %= 12;
X			day %= dim;
X		}
X		return;
X	}
X}
X
X
!
echo 'rna/news.help':
sed 's/^X//' >'rna/news.help' <<'!'
XCommands to read news:
X
XRETURN	    either print current article or go to next article and print header
X
X.	    print current article	+ or n	  go to next article
X- 	    go to previous article	s [file]  save current article
Xh	    print current header	H	  print current header in full
Xu	    unsubscribe from followups	U	  unsubscribe from current group
Xq or EOT    quit			x	  exit (as if no articles seen)
X<number>    go to article <number>	!cmd	  shell escape
XN [newsgrp] go to next newsgroup	DEL	  break to command level
X
XCommands to post/reply/cancel news:
X
Xc	    cancel article 		r	  reply to sender (by mail)
Xf	    post a followup article	p	  post new article
Xm person    mail to person
X
XCommands when posting/replying (p, r, f and m commands):
X
X. or EOT    terminate article		.e	  edit article
X.!cmd	    shell escape		.i	  interpolate current item
XDEL	    abort posting/mailing
!
echo 'rna/newsrc.c':
sed 's/^X//' >'rna/newsrc.c' <<'!'
X/*
X * newsrc file handling
X */
X
X#include "defs.h"
X
Xstatic char nrcname[]	 = NEWSRC;
X
Xstatic char *rcname;		/* full pathname of .newsrc */
Xnewsrc *rc;			/* internal .newsrc */
Xchar *rcgrps;			/* subscription from .newsrc */
Xstatic newsrc *lastrc;		/* last newsrc struct in list */
Xstatic int rclineno;		/* current lineno in .newsrc */
Xstatic bool sortrc;		/* if we should sort on output */
X
Xstatic newsrc *findnewsrc();
X
Xreadnewsrc()
X{
X	register FILE *f;
X	static char option[] = "options";
X	char word[BUFSIZ], rest[BUFSIZ];
X	extern char *getenv();
X
X	if ((rcname = getenv("HOME")) == NULL)
X		error("No $HOME in environment.");
X	rcname = newstr3(rcname, "/", nrcname);
X	if ((f = fopen(rcname, "r")) == NULL)
X		return;
X
X	rclineno = 0;
X	while (getline(f, word, rest))
X		if (CMP(word, option) == 0)
X			dooptions(rest);
X		else
X			dorcline(word, rest);
X	(void) fclose(f);
X}
X
X/*
X * Read a line from f, put first word into w and the rest into r.
X * Discard trailing newline instead of storing it.
X * This is a poor design, as w & r are unchecked for overrun.
X */
Xstatic
Xgetline(f, w, r)
Xregister FILE *f;
Xchar *w, *r;
X{
X	register int c;
X	register char *s;
X
X	rclineno++;
X	s = w;
X	while ((c = getc(f)) != EOF && c != ' ' && c != '\t')
X		*s++ = c;			/* stash first word */
X	*s = '\0';
X
X	if (c != EOF) {
X		s = r;
X		while ((c = getc(f)) != EOF && c != '\n')
X			*s++ = c;		/* stash the rest */
X		*s = '\0';
X	}
X
X	if (c != '\n' && c != EOF)
X		error("Bad format: %s line %d: %s", rcname, rclineno, w);
X
X	return c != EOF;
X}
X
X/*
X * Parse s into words and simulate command line arguments with them.
X */
Xstatic
Xdooptions(s)
Xchar *s;
X{
X	register char *cp;
X	register int argc;
X	register char **argv;
X
X	cp = s;
X	while (isspace(*cp))
X		cp++;
X	if (!*cp)
X		return;
X
X	argc = 1;
X	argv = (char **) myalloc(sizeof(char *));
X	argv[argc - 1] = cp;
X	while (*cp && (cp = strpbrk(cp, " \t")) != NULL) {
X		while (*cp == ' ' || *cp == '\t')
X			*cp++ = '\0';
X		if (*cp) {
X			argc++;
X			argv = (char **) myrealloc((char *) argv,
X				argc * (int)sizeof(char *));
X			argv[argc - 1] = cp;
X		}
X	}
X	if (options(argc, argv, false))
X		error("Bad options: %s line %d: %s", rcname, rclineno, s);
X	free((char *) argv);
X}
X
X/*
X * Parse w & r together as a .newsrc newsgroup line.
X */
Xstatic
Xdorcline(w, r)
Xchar *w, *r;
X{
X	register char lastw;
X	register int len;
X	register newsrc	*np;
X
X	len = strlen(w);
X	lastw = w[len - 1];			/* save presumed colon or bang */
X	w[len - 1] = '\0';			/* nuke presumed colon */
X	while (*r == ' ' || *r == '\t')
X		r++;				/* skip extra whitespace */
X
X	/* kludges, hacks, etc. for compatibility with other readers */
X	if (strncmp(r, "1-", sizeof "1-"-1) == 0)
X		r += sizeof "1-"-1;		/* skip usual `1-' */
X	if (*r == '\0')				/* rn's: `news.trash: ' */
X		r = "0";			/* fake a zero */
X
X	if (lastw != ':' && lastw != NEGCHAR || !isdigit(*r))
X		error("Bad line: %s line %d: %s", rcname, rclineno, w);
X
X	np = NEW(newsrc);
X	np->n_subscribe = (bool) (lastw == ':');	/* colon or bang? */
X	np->n_next = NIL(newsrc);
X	np->n_last = atoi(r);			/* stash first number only */
X	np->n_name = newstr(w);			/* stash n.g. name */
X
X	if (rc == 0)
X		rc = np;
X	else
X		lastrc->n_next = np;
X	lastrc = np;
X}
X
X/*
X * for every group in active list, which belongs to the specified subscription
X * list, and has messages to be read, call func
X * if no mention in newsrc file, make new entry
X */
Xapply(alist, group, func, dolast)
Xactive *alist;
Xchar *group;
Xapplycom (*func)();
Xbool dolast;
X{
X	register active *ap;
X	register newsrc *np;
X	register applycom act;
X	register bool donesome;
X
X	donesome = false;
X	do {
X		act = stop;
X		for (ap = alist; ap; ap = ap->a_next) {
X			if (ap->a_seq == 0 || ap->a_low > ap->a_seq)
X				continue;	/* empty group */
X			if (!ngmatch(ap->a_name, group))
X				continue;
X			if ((np = findnewsrc(ap->a_name)) == NIL(newsrc)) {
X				np = NEW(newsrc);
X				np->n_name = newstr(ap->a_name);
X				np->n_next = NIL(newsrc);
X				np->n_last = 0;
X				np->n_subscribe = true;
X				if (!rc)
X					rc = np;
X				else
X					lastrc->n_next = np;
X				lastrc = np;
X			}
X			if (!np->n_subscribe)
X				continue;
X			/*
X			 * if we haven't read any news for a while (or at all),
X			 * or somehow seq got smaller (active corrupted?),
X			 * set last read to oldest available article
X			 */
X			if (ap->a_low - 1 > np->n_last || ap->a_seq < np->n_last)
X				np->n_last = ap->a_low - 1;
X			while (np->n_last < ap->a_seq) {
X				donesome = true;
X				switch (act = (*func)(ap, np, false, false)) {
X				case stop:		
X					return;
X				case next:		
X					continue;
X				case nextgroup:		
X					break;
X				case searchgroup:	
X					break;
X				}
X				break;
X			}				/* while */
X			if (act == searchgroup)
X				break;
X		}					/* for */
X		if (act != searchgroup && dolast && donesome)
X			act = (*func)(NIL(active), NIL(newsrc), true, false);
X	} while (act == searchgroup);
X}
X
X/*
X * find if a newrc entry exists,
X * taking advantange of the fact that requests should be
X * in the same order
X *
X * detect when the newsrc gets out of order
X * so it can be sorted at the end of the session
X */
Xstatic newsrc *
Xfindnewsrc(name)
Xregister char *name;
X{
X	register newsrc *np, *start;
X	register bool found;
X	static newsrc *nextp;
X
X	if (!rc)
X		return NIL(newsrc);
X
X	found = false;
X	np = nextp ? nextp : rc;
X	nextp = start = np;
X	do {
X		if (CMP(np->n_name, name) == 0) {
X			found = true;
X			break;
X		}
X		np = np->n_next;
X		if (!np)
X			np = rc;
X	} while (np != nextp);
X
X	if (!found)
X		return NIL(newsrc);
X	nextp = np->n_next;
X	if (np != start)
X		sortrc = true;
X	return np;
X}
X
X/*
X * rewrite the newsrc file
X */
Xwritenewsrc(alist)
Xactive *alist;
X{
X	register FILE *f;
X	register active	*ap;
X	register newsrc	*np;
X	register int i;
X	extern char **uflag;
X	extern int usize;
X
X	if (!rc && !uflag && (!rcgrps || !*rcgrps))
X		return;
X
X	signal(SIGINT, SIG_IGN);
X	signal(SIGQUIT, SIG_IGN);
X
X	f = fopenf(rcname, "w");
X	if (rcgrps && *rcgrps)
X		(void) fprintf(f, "options -n %s\n", rcgrps);
X	if (uflag) {
X		scanhist(uflag, usize);		/* forget id's not in history */
X		for (i = 0; i < usize; i++)	/* print whats left */
X			if (uflag[i])
X				(void) fprintf(f, "options -u %s\n", uflag[i]);
X	}
X	if (sortrc) {
X		/*
X		 * sort newsrc so next time we use it,
X		 * history/newsrc comparisons will be faster
X		 */
X		for (ap = alist; ap; ap = ap->a_next)
X			if (np = findnewsrc(ap->a_name))
X				writengline(f, np);
X	} else
X		for (np = rc; np; np = np->n_next)
X			writengline(f, np);
X	(void) fclose(f);
X}
X
Xstatic
Xwritengline(f, np)		/* write .newsrc n.g. line in normal form on f */
Xregister newsrc *np;
X{
X	(void) fprintf(f, "%s%c 1-%d\n", np->n_name,
X		(np->n_subscribe? ':': NEGCHAR), np->n_last);
X}
!
echo 'rna/readnews.1':
sed 's/^X//' >'rna/readnews.1' <<'!'
X.TH READNEWS 1
X.SH NAME
Xnews, readnews \- read news articles
X.SH SYNOPSIS
X.B readnews
X.RB [ -n
Xnewsgroups]
X.RB [ -i ]
X.RB [ -clpC ]
X.RB [ -s [ -+?
X.RI [ group ]]]
X.RB [ -u
Xmessageid]
X.SH DESCRIPTION
X.I Readnews
Xwithout arguments enters command mode,
Xwhich allows printing of unread articles.
XThis is the normal way of using
X.IR readnews .
X.P
X.I Readnews
Xmaintains a
X.I .newsrc
Xfile in the user's home directory that specifies
Xall news articles already read.
XIt is updated at the end of each reading session.
X.P
XSome useful functions are available which don't use command mode.
XThe flags for these are:
X.TP
X.B -c
XCheck if there is news, and if so print `You have news.'.
XA line `readnews -c' is usually placed in the system
X.I .profile
X.RB (  /etc/profile ).
X.TP
X.B -C
XCheck if there is news, and print the groups and number of
Xarticles in each group to be read.
X.TP
X.B -l
XList the titles of available news articles.
X.TP
X.B -p
XPrint all articles on standard output,
Xand update
X.IR newsrc .
X.TP
X.B -s
XPrint the newsgroup subscription list.
X.TP
X.BI -s+ " group"
XAdd
X.I group
Xto the subscription list.
X.TP
X.BI -s- " group"
XSubtract
X.I group
Xfrom the subscription list.
X.TP
X.B -s?
XList currently active newsgroups.
X.P
XThe remaining flags determine article selection,
Xand may also appear in the
X.I .newsrc
Xfile.
XOptions may be specified in the
X.I .newsrc
Xfile by entering lines prefixed with the word `options',
Xfollowed by the options arguments.
XThis is most useful with the
X.B -n
Xflag, specifying the usual groups one wishes to subscribe to.
X.TP
X\fB-n \fInewsgroups\fR
XSelect all articles belonging to
X.IR newsgroups .
X.I newsgroups
Xis a comma separated list of newsgroup names.
XThe character `!' may be used to exclude certain groups,
Xand the word `all' can be used to match any group.
Xe.g. `-n all,!net.jokes'
X.TP
X.B -i
XIgnore
X.I .newsrc
Xfile. It is not read or updated.
XThis allows selection of articles that have already been read.
X.TP
X\fB-u \fImessageid\fR
XUnsubscribe to followup articles referring to
X.IR messageid .
X(This flag is usually only placed in the
X.I .newsrc
Xfile as a result of the `u' command.)
X.SH COMMANDS
XThis section details the commands available when
X.I readnews
Xis in command mode (no
X.B -clpsC
Xarguments).
XThe simplest way of using this mode, is to enter RETURN after every
Xprompt.
XThis will present to the user, a short heading for an article, then a prompt.
XTyping RETURN again will print the article body.
XTyping RETURN yet again will print the next heading, and so on.
XIf having read the heading, you don't wish to read the article, you may
Xtype `n' (or `+' or ';') which will take you directly to the next heading.
X.P
X.P
XAn article is treated as having been read, if either you have seen
Xthe article body, or typed `n' to skip over it.
X.P
XA number of commands operate on the `current' article.
XThis is defined as the article whose header you have most recently seen.
X.P
XThe commands to read news are:
X.TP
X.B RETURN
XEither print the current article,
Xor go to the next article and print its header.
X.TP
X\fBn\fR or \fB+\fR or \fB;\fR
XGo to the next article and print its header.
X.TP
X.B .
XPrint the current article.
X.TP
X.B -
XGo back to the previous article. This is a toggle, typing it
Xtwice returns you to the original article.
X.TP
X.I number
XGo to the article
X.I number
Xin the current newsgroup.
XLike the `-' command,
Xyou always return to the original article
Xafter reading the selected article.
X.TP
X\fBs \fR[\fIfile\fR]
XSave the current article, either in the specified file, or
Xin
X.BR $HOME/articles .
X.TP
X.B h
XPrint the current header (slightly more verbose than normal header).
X.TP
X.B H
XPrint the current header in full (very verbose).
X.TP
X\fBN \fR[\fInewsgroup\fR]
XGo to the next newsgroup, or to the specified newsgroup.
X.TP
X.B u
XUnsubscribe from all further followup articles on this topic.
X.TP
X.B U
XUnsubscribe from this newsgroup, and go to the next newsgroup.
X.TP
X\fB!\fIcommand\fB
XShell escape.
X.I Command
Xis executed.
XIf 
X.I command
Xis `!'
Xthe last escape command is executed.
X.TP
X\fBq\fR or \fBEOT\fR
XQuit.
XThe
X.I .newsrc
Xfile will be updated provided the flag
X.B -i
Xwas not specified.
X.TP
X.B x
XExit.
X.I .newsrc
Xis left unchanged (as if no articles had been read).
X.TP
X.B DEL
XAn interrupt will cause
X.I readnews
Xto terminate its current activity and return to command mode.
XAn interrupt in command mode will cause `Interrupt' to be printed,
Xand a subsequent interrupt will cause immediate exit (as in the `x' command).
X.P
XSome commands are available to send/reply or cancel news articles:
X.TP
X.B c
XCancel article. Only the author, or news administrator can do this.
X.IR postnews (1)
Xis called to do the actual cancelling.
X.TP
X.B r
XReply to sender of the current article by mail.
X.I Readnews
Xsets up the appropriate headers, and then calls
X.IR mail (1)
Xto send a reply to the sender.
X.TP
X.B f
XPost a followup to the current article.
X.I Readnews
Xsets up the appropriate headers, and then calls
X.IR postnews (1)
Xto post the followup article.
X.TP
X.B p
XPost an article on a new topic.
X.IR postnews (1)
Xis called to post the new article.
X.TP
X\fBm \fIperson\fB
XMail to
X.IR person .
X.P
XWhen replying by mail, or posting an article, the user
Xis prompted for certain headers, and then the text of the article or mail
Xitem is entered until a `.' or EOT is entered alone on a line.
XThen the article/mail is posted/mailed.
XOther commands are available:
X.TP
X\&\fB.e\fR
XEdit the message/article collected so far (see
X.IR ed (1)).
XThe 'To:' or 'cc:' fields may be changed if mailing.
XAfter editing further lines may be appended to the message.
X.TP
X\&\fB.i\fR
XInterpolate
Xthe current news article onto the end of the message.
XThe interpolated item
Xis indented by four spaces.
X.TP
X\&\fB.!\fIcmd\fR or \fB!\fIcmd\fR
XShell escape.
X.IR Cmd
Xis executed.
X.TP
X.B DEL
XCauses posting/mailing to be aborted, and the article entered so far
Xis saved in
X.B $HOME/dead.article
Xor
X.BR $HOME/dead.letter .
X.P
XIn order to permanently resubscribe to a newsgroup denied by `U',
Xor a series of followups denied by `u' it is necessary to understand
Xthe format of the
X.I .newsrc
Xfile.
XThe
X.I .newsrc
Xfile consists of two types of lines:
X.TP
Xoption lines
XThese start with the word `option' and contain the same arguments
Xas the
X.I readnews
Xcommand on the command line.
XFollowups are denied with `option -u <messageid>'.
XTo resubscribe to further followups, the correct options line must be deleted.
X.TP
Xread newsgroup lines
XThese have the format <newsgroup>`:' <number>, where
X<number> represents the last item number seen in that particular newsgroup.
XIf the newsgroup has been unsubscribed, the `:' is replaced by a `!'.
XTo resubscribe the `!' must be changed back to a `:'.
X.SH FILES
X.ta 24
X.nf
X$HOME/.newsrc	options and list of previously read articles
X%news	where the articles are kept
X/usr/lib/news/active	current newsgroups
X/usr/lib/news/help	help file
X.fi
X.SH SEE ALSO
Xpostnews(1), mail(1), ed(1), uusend(8), uurec(8).
X.SH BUGS
X.I Readnews
Xwith the
X.B -c
Xflag may say "You have news.", when the available article is a unsubscribed
Xfollowup article.
X.P
XYou may see followups, even if you have used the `u' command.
XThis is because many sites have faulty news programs, which do
Xnot follow the correct protocol, or the sender did not use the `r' command.
X.SH AUTHOR
XMichael Rourke, University of N.S.W (decvax!mulga!michaelr:elecvax)
!
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.