[comp.sources.unix] v19i094: Cnews production release, Part17/19

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

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

: ---CUT HERE---
echo 'relay/sh/postnews':
sed 's/^X//' >'relay/sh/postnews' <<'!'
X#! /bin/sh
X# postnews - post news article
X
X# =()<. ${NEWSCONFIG-@<NEWSCONFIG>@}>()=
X. ${NEWSCONFIG-/usr/lib/news/bin/config}
X
XPATH=$NEWSCTL/bin:$NEWSBIN:$NEWSPATH	# but do not export it
Xumask 077				# private
X
Xtmp=/tmp/pn$$
Xterm="rm -f $tmp ; exit 0"
Xtrap "$term" 0 1 2
X
Xif test " $VISUAL" != " "; then
X	edit="$VISUAL"
Xelif test " $EDITOR" != " "; then
X	edit="$EDITOR"
Xelse
X	edit=/bin/ed
Xfi
X
Xcase $# in
X0)
X	if test -r $NEWSCTL/postdefltgroup
X	then
X		defg="`cat $NEWSCTL/postdefltgroup`"
X		dprompt=" [$defg]"
X	else
X		defg=
X		dprompt=
X	fi
X	ans=
X	while test " $ans" = " "
X	do
X		echo "Newsgroup(s)$dprompt? " | tr -d '\012'
X		read ans
X		case "$ans" in
X		'')	if test " $defg" != " "
X			then
X				ans="$defg"
X			fi
X			;;
X		esac
X	done
X	echo "Newsgroups: $ans" >>$tmp
X	;;
X
X1)
X	echo "Newsgroups: $1" >>$tmp
X	;;
X
X*)
X	echo 'Usage: postnews [newsgroups]' >&2
X	exit 2
X	;;
Xesac
X
Xsubj=
Xwhile test " $subj" = " "
Xdo
X	echo 'Subject: ' | tr -d '\012'
X	read subj
Xdone
Xecho "Subject: $subj" >>$tmp
Xif test -r $NEWSCTL/postdefltdist
Xthen
X	echo "Distribution: `cat $NEWSCTL/postdefltdist`" >>$tmp
Xfi
X
Xecho >>$tmp
Xecho DELETE THIS LINE "(but DO NOT delete the blank line after the headers above)" >>$tmp
Xif test -r $NEWSCTL/postdefltdist
Xthen
X	echo 'DELETE THIS LINE (You may want to change the "Distribution" header)' >>$tmp
Xfi
Xecho REPLACE THIS LINE WITH YOUR TEXT >>$tmp
X
Xtrap : 2
X$edit $tmp
Xtrap "$term" 2
X
Xwhile egrep '^(DELETE|REPLACE) THIS LINE' $tmp >/dev/null
Xdo
X	echo 'This posting does not appear to have been edited properly.'
X	echo 'Abandon it [y] ? ' | tr -d '\012'
X	read ans
X	case "$ans" in
X	''|y*|Y*)
X		rm -f $tmp
X		exit 0
X		;;
X	esac
X
X	echo 'Editing again...  Please check it over carefully.'
X	trap : 2
X	$edit $tmp
X	trap "$term" 2
Xdone
X
Xecho 'Posting...'
Xinews -h <$tmp
!
echo 'relay/sh/tear':
sed 's/^X//' >'relay/sh/tear' <<'!'
X#! /bin/sh
X# tear prefix [file...] - tear RFC822 header and body apart
X#	output files are $1hdr and $1body
XPATH=/bin:/usr/bin; export PATH
X
Xcase $# in
X0)
X	echo "usage: tear prefix [file...]" >&2
X	exit 1
X	;;
Xesac
X
Xhdr="$1hdr"
Xbody="$1body"
Xshift
X
X>>$hdr					# create files just in case
X>>$body
Xcase $# in
X0)	args="-" ;;	# awk needs a filename due to cmd. line assignments
X*)	args="$@" ;;
Xesac
Xexec awk 'inbody == 0 && $0 ~ /^([ \t]|[^ \t]*:)/ { print >hdr; next }
X					{ inbody = 1; print >body }
X' hdr="$hdr" body="$body" $args
!
echo 'relay/sys.c':
sed 's/^X//' >'relay/sys.c' <<'!'
X/*
X * news sys file reading functions
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include "libc.h"
X#include "fgetmfs.h"
X#include "news.h"
X#include "config.h"
X#include "system.h"
X
X#define BTCHDIR "out.going/"	/* prefix of relative batch file name */
X#define BTCHPFX BTCHDIR		/* prefix of default batch file name */
X#define BTCHSFX "/togo"		/* suffix of same */
X#define CMDPFX "uux - -r -z "	/* prefix of default command */
X#define CMDSFX "!rnews"		/* suffix of same */
X
X/* private */
Xstatic FILE *fp = NULL;			/* stream for ctlfile(filerelname) */
Xstatic char filerelname[] = "sys";	/* filename relative to $NEWSCTL */
X
X/* forward decls */
XFORWARD char *parsecolon(), *reparse();
XFORWARD void readsys(), parsesysln(), parse(), parseflags();
X
X/* exports */
Xstruct system *firstsys = NULL;	/* cache: 1st sys of in-core sys file */
Xstruct system *currsys = NULL;	/* current system */
X
X/* imports */
Xextern boolean justone;
Xextern struct system *mysysincache();
Xextern void rewsys(), remmysys(), freecurrsys();
X
Xstruct system *
Xoursys()			/* return our sys entry */
X{
X	register struct system *sys = mysysincache();
X	static struct system fakesys;
X
X	if (sys == NULL) {
X		rewsys(fp);
X		while ((sys = nextsys()) != NULL &&
X		    !STREQ(sys->sy_name, hostname()))
X			;
X		if (sys == NULL) {
X			/* no entry: cook one up; no need to malloc members */
X			fakesys.sy_name = hostname();
X			fakesys.sy_excl = NULL;
X			fakesys.sy_ngs = "all";
X			fakesys.sy_distr = "all";
X			fakesys.sy_flags = 0;
X			fakesys.sy_lochops = 0;
X			fakesys.sy_cmd = "";
X			fakesys.sy_next = NULL;
X			sys = &fakesys;
X		}
X		remmysys(sys);			/* for future reference */
X	}
X	return sys;
X}
X
X/*
X * Return the next sys entry, which may span multiple lines.
X * Returned pointer points at a static struct whose members
X * point at static storage.
X *
X * It would be clearer to rewrite the justone/nextsys/readsys dance
X * to get rid of justone, but I haven't the energy.  Sorry.  Beware that
X * justone is set in either ../libbig/sys.fast.c or ../libsmall/sys.slow.c.
X */
Xstruct system *
Xnextsys()
X{
X	struct system *retsys;
X
X	if (firstsys == NULL && fp == NULL)
X		if ((fp = fopenwclex(ctlfile(filerelname), "r")) == NULL)
X			return NULL;
X	if (fp != NULL && firstsys == NULL)
X		readsys();
X	retsys = currsys;
X	if (currsys != NULL)
X		currsys = currsys->sy_next;
X	return retsys;
X}
X
X/*
X * If justone, read one entry; else read whole sys file (done once only).
X * Ignores '#' comments and blank lines; uses cfgetms to read possibly-
X * continued lines of arbitrary length.
X */
XSTATIC void
Xreadsys()
X{
X	register char *sysline;
X
X	if (justone)
X		freecurrsys();
X	else
X		rewind(fp);
X	while ((sysline = cfgetms(fp)) != NULL) {
X		if (sysline[0] != '#' && sysline[0] != '\n')
X			parsesysln(sysline);
X		free(sysline);
X		if (justone && firstsys != NULL) {	/* parsed an entry */
X			firstsys = NULL;
X			return;
X		}
X	}
X	(void) nfclose(fp);
X	fp = NULL;
X	rewsys(fp);
X}
X
Xstatic char *curr, *next;			/* parsing state */
X
X/*
X * Parse (and modify) sysline into *currsys, which is malloced here
X * and freed iff "justone", in readsys(), see freecursys().
X *
X * Side-effect: sysline has a trailing newline removed.
X */
XSTATIC void
Xparsesysln(sysline)
Xregister char *sysline;
X{
X	register struct system *sysp =(struct system *)nemalloc(sizeof *sysp);
X	char *flagstring;
X
X	trim(sysline);
X	next = sysline;
X	parse(&sysp->sy_name);
X	parse(&sysp->sy_ngs);
X	parse(&flagstring);
X	parse(&sysp->sy_cmd);
X	/* could check for extra fields here */
X
X	parseflags(flagstring, sysp);
X	free(flagstring);		/* malloced by parse */
X	sysp->sy_next = NULL;
X
X	/* reparse for embedded slashes */
X	sysp->sy_excl = reparse(sysp->sy_name, '/');
X	sysp->sy_distr = reparse(sysp->sy_ngs, '/');
X	if (sysp->sy_distr == NULL)	/* default distr is ngs... */
X		sysp->sy_distr = sysp->sy_ngs;
X
X	sysdeflt(sysp);			/* fill in any defaults */
X
X	/* stash *sysp away on the tail of the current list of systems */
X	if (firstsys == NULL)
X		firstsys = sysp;
X	else
X		currsys->sy_next = sysp;
X	currsys = sysp;
X}
X
X/*
X * fill in defaults in sysp.
X *
X * expand a name of "ME" to hostname().
X * If an empty batch file name was given, supply a default
X * ($NEWSARTS/BTCHPFX system BTCHSFX).
X * Prepend $NEWSARTS/BTCHDIR to relative file names.
X * If an empty command was given, supply a default (uux - -r -z system!rnews).
X * (This *is* yucky and uucp-version-dependent.)
X */
Xvoid
Xsysdeflt(sysp)
Xregister struct system *sysp;
X{
X	if (STREQ(sysp->sy_name, "ME")) {
X		free(sysp->sy_name);	/* malloced by parse */
X		sysp->sy_name = strsave(hostname());
X	}
X	if (sysp->sy_flags&FLG_BATCH && sysp->sy_cmd[0] == '\0') {
X		register char *deffile = nemalloc((unsigned) STRLEN(BTCHPFX) +
X			strlen(sysp->sy_name) + STRLEN(BTCHSFX) + 1);
X
X		(void) strcpy(deffile, BTCHPFX);
X		(void) strcat(deffile, sysp->sy_name);
X		(void) strcat(deffile, BTCHSFX);
X		free(sysp->sy_cmd);	/* malloced by parse */
X		sysp->sy_cmd = strsave(fullartfile(deffile));
X		free(deffile);
X	}
X	if (sysp->sy_flags&FLG_BATCH && sysp->sy_cmd[0] != FNDELIM) {
X		register char *absfile = nemalloc((unsigned) STRLEN(BTCHDIR) +
X			strlen(sysp->sy_cmd) + 1);
X
X		(void) strcpy(absfile, BTCHDIR);
X		(void) strcat(absfile, sysp->sy_cmd);
X		free(sysp->sy_cmd);	/* malloced by parse */
X		sysp->sy_cmd = strsave(artfile(absfile));
X		free(absfile);
X	}
X	if (!(sysp->sy_flags&FLG_BATCH) && sysp->sy_cmd[0] == '\0') {
X		free(sysp->sy_cmd);	/* malloced by parse */
X		sysp->sy_cmd = nemalloc((unsigned) STRLEN(CMDPFX) +
X			strlen(sysp->sy_name) + STRLEN(CMDSFX) + 1);
X		(void) strcpy(sysp->sy_cmd, CMDPFX);
X		(void) strcat(sysp->sy_cmd, sysp->sy_name);
X		(void) strcat(sysp->sy_cmd, CMDSFX);
X	}
X}
X
X/*
X * Parse "next" to colon into malloced storage, return its ptr via "into".
X * *into is freed iff "justone", in readsys(), see freecursys().
X */
XSTATIC void
Xparse(into)
Xregister char **into;
X{
X	curr = next;
X	if (curr == NULL)
X		*into = strsave("");
X	else {
X		next = parsecolon(curr);
X		*into = strsave(curr);
X	}
X}
X
XSTATIC char *
Xparsecolon(line)		/* return NULL or ptr. to byte after colon */
Xchar *line;
X{
X	register char *colon;
X
X	INDEX(line, ':', colon);
X	if (colon != NULL)
X		*colon++ = '\0';
X	return colon;
X}
X
X/*
X * replace "delim" in "field" with a NUL and return the address of the byte
X * after the NUL (the address of the second subfield), or NULL if no
X * "delim" was present.
X */
XSTATIC char *
Xreparse(field, delim)
Xchar *field;
Xint delim;
X{
X	register char *delimp = index(field, delim);
X
X	if (delimp != NULL)
X		*delimp++ = '\0';
X	return delimp;
X}
X
X/*
X * Parse sys file flags into sysp.
X */
XSTATIC void
Xparseflags(flags, sysp)
Xregister char *flags;
Xregister struct system *sysp;
X{
X	sysp->sy_flags = 0;
X	sysp->sy_lochops = 0;		/* default L value */
X	for (; *flags != '\0'; flags++)
X		switch (*flags) {
X		case 'A':
X			errunlock("A news format not supported", "");
X			/* NOTREACHED */
X		case 'B':		/* mostly harmless */
X			break;
X		case 'f':
X			sysp->sy_flags |= FLG_BATCH|FLG_SZBATCH;
X			break;
X		case 'F':
X			sysp->sy_flags |= FLG_BATCH;
X			break;
X		case 'I':		/* NNTP hook: write msgids, !files */
X			sysp->sy_flags |= FLG_BATCH|FLG_IHAVE;
X			break;
X		case 'L':		/* Ln */
X			sysp->sy_flags |= FLG_LOCAL;
X			sysp->sy_lochops = 0;
X			while (isascii(flags[1]) && isdigit(flags[1])) {
X				sysp->sy_lochops *= 10;
X				sysp->sy_lochops += *++flags - '0';
X			}
X			break;
X		case 'm':		/* send only moderated groups */
X			sysp->sy_flags |= FLG_MOD;
X			break;
X		case 'N':
X			errunlock(
X	"The N flag is a wasteful old kludge; see the I flag instead.", "");
X			/* NOTREACHED */
X		case 'n':		/* NNTP hook: write files+msgids */
X			sysp->sy_flags |= FLG_BATCH|FLG_NBATCH;
X			break;
X		case 'u':		/* send only unmoderated groups */
X			sysp->sy_flags |= FLG_UNMOD;
X			break;
X		case 'U':		/* mostly harmless */
X			break;
X		case 'H':		/* bugger off */
X		case 'S':		/* bugger off */
X		case 'M':		/* multicast: obs., see batcher */
X		case 'O':		/* multicast: obs., see batcher */
X		default:
X			errunlock("unknown sys flag `%s' given", flags);
X			/* NOTREACHED */
X		}
X}
X
Xvoid
Xrewndsys()
X{
X	rewsys(fp);
X}
!
echo 'relay/system.h':
sed 's/^X//' >'relay/system.h' <<'!'
X/*
X * parsed form of the "sys" file
X * Beware that in C++, struct system collides with system(3) in transmit.c
X * This can be fixed by using "::system(...)" or by renaming struct system.
X */
Xstruct system {
X	char *sy_name;		/* machine name */
X	char *sy_excl;		/* exclusion list of machines */
X	char *sy_ngs;		/* newsgroup subscription list */
X	char *sy_distr;		/* distribution list */
X	char *sy_cmd;		/* command to transmit articles */
X	unsigned sy_lochops;	/* flags Ln value: local hops */
X	char sy_flags;		/* ornaments, encoded as bits */
X	struct system *sy_next;	/* link to next system */
X};
X
X/* sy_flags bits */
X#define FLG_BATCH	(1<<0)		/* F: sy_cmd is batch filename */
X#define FLG_SZBATCH	(1<<1)		/* f: F, and include byte count */
X#define FLG_IHAVE	(1<<2)		/* I: NNTP ihave - F, write msg. ids */
X#define FLG_LOCAL	(1<<3)		/* L: send local articles only */
X#define FLG_MOD		(1<<4)		/* m: send moderated groups only */
X#define FLG_UNMOD	(1<<5)		/* u: send unmoderated groups only */
X#define FLG_NBATCH	(1<<6)		/* n: NNTP batch: filename & msg-id */
X
X/* imports from system.c */
Xextern struct system *oursys(), *nextsys();
Xextern void sysdeflt(), rewndsys();
!
echo 'relay/transmit.c':
sed 's/^X//' >'relay/transmit.c' <<'!'
X/*
X * transmit - transmit incoming articles to neighbouring machines
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include "libc.h"
X#include "news.h"
X#include "config.h"
X#include "headers.h"
X#include "active.h"
X#include "article.h"
X#include "msgs.h"
X#include "system.h"
X#include "trbatch.h"
X#include "transmit.h"
X
X/* forwards */
XFORWARD boolean oktransmit();
XFORWARD void ejaculate(), trappend();
X
X/* private */
Xstatic boolean debug = NO;
X
Xvoid
Xtransdebug(state)
Xboolean state;
X{
X	debug = state;
X}
X
X/*
X * For each system in "sys" other than this one,
X * transmit this article when its ng pattern matches
X * art->h.h_distr (which may be just a copy of art->h.h_ngs).
X */
Xvoid
Xtransmit(art, exclude)
Xregister struct article *art;
Xchar *exclude;					/* no copy to this site */
X{
X	register struct system *sys;
X	register int bsysno = 0;	/* ordinal # of batch sys entry */
X
X	rewndsys();
X	if (debug)
X		(void) fprintf(stderr, "just rewound sys file\n");
X	while ((sys = nextsys()) != NULL) {
X		if (debug)
X			(void) fprintf(stderr,
X				"sy_name=%s sy_ngs=%s sy_distr=%s\n",
X				sys->sy_name, sys->sy_ngs, sys->sy_distr);
X		if (oktransmit(art, sys, exclude))
X			ejaculate(art, sys, bsysno);
X		if (sys->sy_flags&FLG_BATCH)
X			++bsysno;
X	}
X	if (debug)
X		(void) fprintf(stderr, "just finished reading sys file\n");
X}
X
X/*
X * Is it okay to send the article corresponding to "art" to site "sys",
X * excluding site "exclude"?
X *
X * If L(n) flag is on, must have been posted within n hops of here.
X * Never send to this site, nor the "exclude" site, nor any site with a host
X * in sys->sy_excl named in Path:, nor any site named in Path:.
X *
X * Newsgroups: must match sys's subscription list.
X * Distribution: must match sys's distribution list.  (RFC 850 is wrong:
X * Distribution:s are *not* patterns, they are lists.  See RFC 1036.)
X *
X * If m flag is on, group(s) must be moderated; if u flag is on,
X * must be unmoderated.  (If both are on, act as if neither is on.)
X */
XSTATIC boolean
Xoktransmit(art, sys, exclude)
Xregister struct article *art;
Xregister struct system *sys;
Xchar *exclude;				/* no copy to him */
X{
X	register int flags = sys->sy_flags;
X	register char *site = sys->sy_name;
X	register char *path =
X		canonpath(art->h.h_path, art->h.h_approved, art->h.h_sender);
X	register int result;
X
X	if (flags&FLG_LOCAL && hopcount(path) > sys->sy_lochops ||
X	    STREQ(hostname(), site) ||
X	    exclude != NULL && STREQ(exclude, site) || hostin(site, path) ||
X	    sys->sy_excl != NULL && anyhostin(sys->sy_excl, path) ||
X	    !ngmatch(sys->sy_ngs, art->h.h_ngs) ||
X	    !ngmatch(sys->sy_distr, art->h.h_distr))
X		result = NO;
X	else if (flags&(FLG_MOD|FLG_UNMOD)) {	/* u, m flag selection */
X		if ((flags&(FLG_MOD|FLG_UNMOD)) == (FLG_MOD|FLG_UNMOD))
X			result = YES;		/* too silly */
X		else
X			result = (flags&FLG_MOD? moderated(art->h.h_ngs):
X						!moderated(art->h.h_ngs));
X	} else
X		result = YES;
X	free(path);
X	return result;
X}
X
X/*
X * Send the article denoted by art to the system denoted by sys.
X *
X * When a filename is needed, we use the first one in art->a_files
X * rather than art->a_tmpf because we want a permanent name, and
X * translate it to a full path name to avoid ambiguity.
X *
X * Side-effect: prints the system name on stdout for logging.
X */
XSTATIC void
Xejaculate(art, sys, bsysno)
Xregister struct article *art;
Xregister struct system *sys;
Xint bsysno;
X{
X	register char *fullname;	/* sometimes is a message-id */
X
X	if (debug)
X		(void) fprintf(stderr, "transmitting %s to %s\n",
X			art->h.h_msgid, sys->sy_name);
X    	(void) printf(" %s", sys->sy_name);	/* logging */
X	if (sys->sy_flags&FLG_IHAVE)
X		fullname = art->h.h_msgid;
X	else {
X		register char *filename = first(art->a_files);
X
X		mkfilenm(filename);
X	    	fullname = fullartfile(filename);
X		free(filename);
X	}
X#ifdef PARANOID
X	fullname = strsave(fullname);
X#endif
X	if (sys->sy_flags&FLG_BATCH)
X    		trbatch(art, sys, fullname, bsysno);
X	else
X		trcmd(art, sys, fullname);
X#ifdef PARANOID
X	free(fullname);
X#endif
X}
X
X/*
X * Execute sys->sy_cmd with the current article as stdin
X * and filename substituted for %s in sys->sy_cmd (if any).
X *
X * Search path includes $NEWSCTL/bin and $NEWSBIN/relay.
X * redirect stdin to prevent consuming my stdin & so cmd's stdin
X * is filename by default.
X *
X * We use strcat instead of sprintf if syscmd contains no %.
X * this avoids the 128-byte restriction on printf output
X * (see printf(3) BUGS, at least in V7).
X */
Xvoid
Xtrcmd(art, sys, filename)
Xstruct article *art;
Xstruct system *sys;
Xchar *filename;
X{
X	register char *cmd;
X	int exitstat;
X	char *syscmd = sys->sy_cmd, *percent;
X	static char *ctlcmd = NULL, *bincmd = NULL;
X
X	if (ctlcmd == NULL)
X		ctlcmd = strsave(ctlfile("bin"));
X	if (bincmd == NULL)
X		bincmd = strsave(binfile("relay"));
X	cmd = nemalloc((unsigned)(STRLEN("PATH=") + strlen(ctlcmd) +
X		STRLEN(":") + strlen(bincmd) + STRLEN(":") + strlen(newspath()) +
X		STRLEN(" <") + strlen(filename) + STRLEN(" ") +
X		strlen(syscmd) + strlen(filename) + 1));
X	(void) strcpy(cmd, "PATH=");
X	(void) strcat(cmd, ctlcmd);
X	(void) strcat(cmd, ":");
X	(void) strcat(cmd, bincmd);
X	(void) strcat(cmd, ":");
X	(void) strcat(cmd, newspath());
X	(void) strcat(cmd, " <");
X	(void) strcat(cmd, filename);
X	(void) strcat(cmd, " ");
X	percent = index(syscmd, '%');
X	if (percent == NULL)
X		(void) strcat(cmd, syscmd);
X	else {
X		char *pcent2;
X
X		++percent;
X		pcent2 = index(percent, '%');
X		if (pcent2 != NULL) {
X			art->a_status |= ST_DROPPED;
X			(void) fprintf(stderr, "%s: `%s' contains two %%'s\n",
X				progname, cmd);
X		} else if (*percent != 's' && *percent != '%') {
X			art->a_status |= ST_DROPPED;
X			(void) fprintf(stderr, "%s: `%s' contains %%%c, not %%s\n",
X				progname, cmd, *percent);
X		} else
X			(void) sprintf(cmd+strlen(cmd), syscmd, filename);
X	}
X	exitstat = system(cmd);
X	if (exitstat != 0) {
X		art->a_status |= ST_DROPPED;
X		(void) fprintf(stderr, "%s: `%s' returned exit status 0%o\n",
X			progname, cmd, exitstat);
X	}
X	free(cmd);
X}
X
X/*
X * Append "filename" to sys->sy_cmd.  bsysno is the ordinal # of this batch
X * sys line.  If bsysno is low enough, use the batchfile cache of batch file
X * descriptors.
X */
Xvoid
Xtrbatch(art, sys, filename, bsysno)
Xregister struct article *art;
Xstruct system *sys;
Xchar *filename;
Xregister int bsysno;
X{
X	register struct batchfile *bf = bfopen(sys->sy_cmd, bsysno);
X
X	if (bf == NULL || bf->bf_str == NULL)
X		art->a_status |= ST_DROPPED;
X	else {
X		trappend(art, sys, bf, filename);
X		art->a_status |= bffkclose(bsysno);
X	}
X}
X
X/*
X * write filename, message-id or size on batch file "bf".
X * under the 'f' flag (FLG_SZBATCH), include the size in bytes of the article
X * after "name" to assist the C news batcher.  under the 'n' flag (FLG_NBATCH),
X * write the article's message-id.  afterward, flush "bf" in case
X * the machine crashes before the stream is closed.
X */
XSTATIC void
Xtrappend(art, sys, bf, name)
Xregister struct article *art;
Xregister struct system *sys;
Xregister struct batchfile *bf;
Xchar *name;
X{
X	if (fputs(name, bf->bf_str) == EOF)
X		fulldisk(art, bf->bf_name);
X	if (sys->sy_flags&FLG_SZBATCH &&
X	    fprintf(bf->bf_str, " %ld", art->a_charswritten) == EOF)
X		fulldisk(art, bf->bf_name);
X	if (sys->sy_flags&FLG_NBATCH &&
X	    fprintf(bf->bf_str, " %s", art->h.h_msgid) == EOF)
X		fulldisk(art, bf->bf_name);
X
X	/* don't check putc return value for portability; use ferror */
X	(void) putc('\n', bf->bf_str);
X	if (ferror(bf->bf_str) || bfflush(bf) == EOF)
X		fulldisk(art, bf->bf_name);		
X}
X
X/*
X * really close all the open batch files
X */
Xstatust
Xtrclose()
X{
X	return bfrealclose();
X}
!
echo 'relay/transmit.h':
sed 's/^X//' >'relay/transmit.h' <<'!'
X/* imports from transmit.c */
Xextern statust trclose();
Xextern void transdebug(), transmit(), trcmd(), trbatch();
!
echo 'relay/trbatch.c':
sed 's/^X//' >'relay/trbatch.c' <<'!'
X/* 
X * transmit batch file management
X */
X#include <stdio.h>
X#include <sys/types.h>
X#include "libc.h"
X#include "news.h"
X#include "msgs.h"
X#include "trbatch.h"
X
X/* tunable parameters */
X#ifndef FLUSHEVERY
X#define FLUSHEVERY 1	/* fflush batch files every this many lines */
X#endif			/* FLUSHEVERY */
X#ifndef NOPENBFS
X#define NOPENBFS 10	/* # batchfiles kept open for batching (arbitrary) */
X#endif			/* NOPENBFS */
X
Xstatic struct batchfile batchfile[NOPENBFS];	/* try to keep open always */
X#define lastbf &batchfile[NOPENBFS-1]
X/*
X * More than one pointer in ordtobfs may point at a given batchfile,
X * to permit sharing of open batch files among multiple sys entries.
X * ordtobfs[ordinal # of batch sys entry] -> (usually open) batch file,
X * if the index is in range.
X */
Xstatic struct batchfile *ordtobfs[NOPENBFS];
Xstatic struct batchfile fakebatf;	/* for non-cached batch files */
X
X/* forwards */
XFORWARD statust bfclose(), bfrclose();
XFORWARD struct batchfile *bfincache(), *fakebf();
X
X/*
X * open "name" for appending, for batch sys entry with ordinal # "ord".
X *
X * if ord is too big, see if any batchfile has been assigned to "name" yet;
X * if not, set up a fake batchfile for temporary use.  if ord is in range,
X * ensure that (name, ord) are mapped to a batchfile.
X *
X * if an attempt to open the batchfile's stream fails, close a random
X * batchfile stream and retry the open.
X */
Xstruct batchfile *
Xbfopen(name, ord)
Xregister char *name;
Xregister int ord;
X{
X	register struct batchfile *bf;
X
X	if (ord >= NOPENBFS) {			/* no mapping possible */
X		bf = bfisopen(name);
X		if (bf == NULL)
X			bf = fakebf((FILE *)NULL, name);
X	} else
X		bf = bfincache(name, ord);
X
X	if (bf->bf_str == NULL)
X		bf->bf_str = fopenclex(name, "a");
X	if (bf->bf_str == NULL) {
X		if (bfrclose() != ST_OKAY)
X			return NULL;
X		bf->bf_str = fopenwclex(name, "a");	/* retry, may bitch */
X	}
X	return bf;
X}
X
X/*
X * returns a batchfile, never NULL, corresponding to name and ord.
X * if ord isn't mapped, search the batchfile cache for name;
X * if missing, initialise batchfile[ord] and map ord to it.
X * if ord wasn't mapped, but name was in the cache, map ord to the cache hit.
X */
XSTATIC struct batchfile *
Xbfincache(name, ord)
Xchar *name;
Xint ord;
X{
X	register struct batchfile *bf = ordtobfs[ord];
X
X	if (bf == NULL) {
X		bf = bfisopen(name);
X		if (bf == NULL) {
X			/* establish new mapping for a new file */
X			bf = &batchfile[ord];
X			bf->bf_name = strsave(name);
X			bf->bf_str = NULL;	/* paranoia */
X#ifdef notdef
X			bf->bf_ref = 0;
X#endif
X			bf->bf_lines = FLUSHEVERY;
X		}
X		ordtobfs[ord] = bf;
X	}
X	/* mapping is now set (ord -> bf) */
X	return bf;
X}
X
Xstatust
Xbffkclose(ord)				/* close ord's batchfile, if fake */
Xint ord;
X{
X	register statust status = ST_OKAY;
X
X	if (ord >= NOPENBFS)
X		status |= bfclose(&fakebatf);
X	return status;
X}
X
XSTATIC statust
Xbfclose(bf)
Xregister struct batchfile *bf;
X{
X	register statust status = ST_OKAY;
X
X	if (nfclose(bf->bf_str) == EOF)
X		status = prfulldisk(bf->bf_name);
X	bf->bf_str = NULL;	/* prevent accidents; mark as closed */
X	return status;
X}
X
XSTATIC struct batchfile *
Xfakebf(stream, name)
XFILE *stream;
Xchar *name;
X{
X	fakebatf.bf_name = name;
X	fakebatf.bf_str = stream;
X	return &fakebatf;
X}
X
X/*
X * search the batchfile cache for "name"; return the hit, if any.
X */
Xstruct batchfile *
Xbfisopen(name)
Xregister char *name;
X{
X	register struct batchfile *bf;
X
X	for (bf = batchfile; bf <= lastbf; bf++)
X		if (bf->bf_name != NULL && STREQ(name, bf->bf_name))
X			return bf;
X	return NULL;
X}
X
X/*
X * a performance hack: only fflush bf->bf_str every FLUSHEVERY calls.
X */
Xint
Xbfflush(bf)
Xregister struct batchfile *bf;
X{
X	register int ret = 0;
X
X	if (--bf->bf_lines <= 0) {
X		bf->bf_lines = FLUSHEVERY;
X		ret = fflush(bf->bf_str);
X	}
X	return ret;
X}
X
XSTATIC statust
Xbfrclose()				/* close a random batchfile */
X{
X	register struct batchfile *bf;
X	register statust status = ST_OKAY;
X
X	for (bf = batchfile; bf <= lastbf; bf++)
X		if (bf->bf_str != NULL) {
X			status |= bfclose(bf);
X			break;
X		}
X	return status;
X}
X
Xstatust
Xbfrealclose()				/* close all open batch files */
X{
X	register struct batchfile *bf;
X	register statust status = ST_OKAY;
X
X	for (bf = batchfile; bf <= lastbf; bf++) {
X		if (bf->bf_str != NULL)		/* batch file stream open */
X			status |= bfclose(bf);
X		nnfree(&bf->bf_name);
X#ifdef notdef
X		bf->bf_ref = 0;
X#endif
X		ordtobfs[bf - batchfile] = NULL;	/* unmap batch file */
X	}
X	return status;
X}
!
echo 'relay/trbatch.h':
sed 's/^X//' >'relay/trbatch.h' <<'!'
X/*
X * interface to the transmit batch files
X */
X
Xstruct batchfile {
X#ifdef notdef
X	int bf_ref;			/* reference count */
X#endif
X	FILE *bf_str;			/* stream */
X	char *bf_name;			/* file name */
X	int bf_lines;			/* until fflush */
X};
X
X/* imports from trbatch.c */
Xextern struct batchfile *bfopen(), *bfisopen();
Xextern statust bffkclose(), bfrealclose();
Xextern int bfflush();
!
echo 'relay/ihave.not.c':
sed 's/^X//' >'relay/ihave.not.c' <<'!'
X/*
X * Reject the Usenet ihave/sendme control messages.
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X
X#include "news.h"
X#include "headers.h"
X#include "article.h"
X
Xstatic void
Xignore(cmd, args)
Xchar *cmd, *args;
X{
X	(void) fprintf(stderr, "%s: `%s %s' control ignored\n", progname, cmd, args);
X}
X
X/* ARGSUSED art */
Xvoid
Xihave(args, art)
Xchar *args;
Xstruct article *art;
X{
X	ignore("ihave", args);
X}
X
X/* ARGSUSED art */
Xvoid
Xsendme(args, art)
Xchar *args;
Xstruct article *art;
X{
X	ignore("sendme", args);
X}
!
echo 'relay/README.relay':
sed 's/^X//' >'relay/README.relay' <<'!'
X``yer about to be boarded, ye scurvy network news dogs! har har ...''
X		-- Oliver Wendell Jones, Bloom County Hacker & Cracker
X
X``No news is good news.''
X``When bigger machines are built, netnews will saturate them.''
X``USENET -- All the news that's fit to `N'.''
X		-- /usr/games/fortune
X
X``Net news is the television of computing.''
X		-- Geoff Collyer
X
XOn older systems, you will to also install a small program, setnewids,
Xsetuid-root.  If this worries you, read setnewsids.c; all it does is
Xexecute setgid(), setuid() to the "news" group and user if they exist,
Xotherwise relaynews's real ids.  Setnewsids can be found in ../conf.
X
XYou can test relaynews by giving NEWSCTL, NEWSBIN or NEWSARTS
Xenvironment variables to change the library, binary or spool
Xdirectories and I encourage this.
X
XIf you plan to run rn, you'll need the rn patches to allow Xref: to
Xwork without Relay-Version:, which has been banished.
X
XYou will need to put your site name in /usr/lib/news/mailname (../conf/build
Xlooks after all this).  No upper case letters in your name
Xplease, there is no call for it and it just looks ugly.
X
XYou must only permit relaynews to run on file servers since newsboot clears
Xall locks in /usr/lib/news.
X
XYou'll need compress for compressing or uncompressing batches of news.
XSee the contact person of your news feed or the moderator of the
Xnewsgroup comp.sources.unix (try uunet!sources).
X
XSee the anews directory for conversion filters from A to B and back.
X
XYou'll need to install /usr/lib/newsbin/gngp (see ../misc) before inews
Xwill work.
X
XB-2.11-isms.  Your /usr/lib/news/mailpaths file must be updated to
Xpoint at your nearest backbone site.  A 5th sys file field for
XDistribution:  patterns is available (add them in sys after the
Xsubscription list, separated by "/"), and a 6th field for excluded
Xhosts is also, separated by "/" from the system name.
X
XGood Luck.
X
X				Geoff Collyer, 8 June 1989
!
echo 'rna/README':
sed 's/^X//' >'rna/README' <<'!'
XThis is the "Australian readnews", written by Michael Rourke at UNSW.
XIt's a simple and reasonably well-put-together news reader suitable for
Xgiving to naive users who aren't going to be reading news much.
X
XYou'll need to fiddle defs.h for your machine, then type "make".
X
XNote that README.aus assumes you are installing the entire UNSW news
Xsystem, not just readnews; some of it is inapplicable.
!
echo 'rna/README.aus':
sed 's/^X//' >'rna/README.aus' <<'!'
XThe files in this distribution are:
X
X	Makefile
X	README
X	active.c
X	at.h
X	defs.h
X	expire.c
X	funcs.c
X	header.c
X	history.c
X	lib
X	lib/bsearch.c
X	lib/memset.c
X	lib/strpbrk.c
X	lib/tmpfile.c
X	lib/tmpnam.c
X	maketime.c
X	man
X	man/postnews.1
X	man/readnews.1
X	man/uurec.8
X	man/uusend.8
X	mtempnam.c
X	news.help
X	newsrc.c
X	postnews.c
X	readnews.c
X	sample.sys
X	uurec.c
X	uusend.c
X
XThis news system is modelled on the USENET news system
Xby Mark Horton (and others).
X
XApart from some minor programs the system has been completely re-written.
XThe aim of re-writing was to produce a system that was:
X	1. smaller
X	2. cleaner
X	3. faster
X	4. was compatible at the site <--> site level with USENET
X	5. had a better user interface ("readnews" and "postnews")
X
XThese goals have been met.
XThe programs "readnews" and "postnews" are 1/3 the previous size, and
Xdoes not require separate I/D space to run on pdp11/70's.
XAlso far fewer processes are needed to use "postnews".
X
XThis system is compatible with USENET at the site <--> site level, provided
Xcommunication is done with Version B format messages (the current 'standard').
XThe messages meet the Standard for the format of ARPA Internet Text messages
X(RFC 822).
X
X"postnews" methods of editing messages is compatible with our local "mail"
Xprogram (also re-written locally).
X
XTo aid someone familiar with USENET to find his/her way around the source:
X    Program changes:
X	"checknews" has become a function of "readnews" (readnews -cC)
X	"postnews" and "inews" are combined into "postnews"
X	"readnews" has the same function (simplified user interface)
X	"expire" has the same function (simplified arguments)
X	"recnews" is not needed
X	"sendnews" has been renamed "uusend" (and simplified)
X	"uurec" has the same function
X    Files:
X	The layout of the news database is the same, except that articles
X	are named #<number> rather than <number>, so that numbers can
X	be a valid newsgroup (like class.6.621).
X
X	"/usr/lib/news/active" has an extra field - the lowest numbered article
X	present in a newsgroup.
X	"/usr/lib/news/history" has a sightly different format.
X	"/usr/lib/news/sys" is compatible, except that the third field
X	is ignored (always expects format B site); colons are allowed in
X	the last field.
X
XTo setup the news system:
X	1. edit the "defs.h" file and make any changes necessary
X	   in particular: MYDOMAIN, MYORG and the paths of SEQ, SYS etc.
X	   MANGRPS should not be defined without making suitable
X	   modifications to getmangrps() in readnews.c
X	   UNSWMAIL is set if you have the version of mail from UNSW,
X	   in particular it allows arguments "-s subject -i include_file"
X	   to specify the subject, and make include_file available to
X	   a ".i" command (like postnews).
X	   AUSAM should not be set unless you have the hashed passwd file,
X	   and locked file facilities of AUSAM.
X	1a. edit "Makefile" for the pathnames of LIBDIR, BINDIR and NETDIR.
X	2. create the account NEWSROOT (defined in defs.h) (this is where
X	   the messages are kept).
X	3. Run the makefile. If you don't have the routines found in
X	   lib/* (bsearch, memset etc.) these can be compiled and
X	   linked in as required.
X	4. Create any groups (using "postnews -c 'newgroup <name>'"),
X	   that require immediate local posting, otherwise groups will
X	   be created automatically when news is received from other sites.
X	   Root and NEWSROOT can also mail to non-existent groups, and
X	   will be asked whether or not to create the new group.
X	5. Set up a pseudo user "rnews" to direct received news into
X	   "postnews -p" (with uid set to NEWSROOT).
X	   How this is done will depend on your network implementation.
X	   It may require a deamon emptying a mail box regularly
X	   (see rnews.sh in this case).
X	   If a mail interface is required, series of messages can be
X	   piped into /usr/lib/news/uurec instead.
X	6. Set up "/usr/lib/news/sys". See sample.sys for an example.
X	   Each line in the "sys" file specifies:
X		host name
X		distribution newsgroups
X		(empty field (system assumes type B interchange))
X		the command needed to send the item to the host.
X	   Note the current host must have the first two fields also.
X	   News transmission can be via "mail" or directly as a
X	   network file transfer.
X	7. Test the system by posting to "to.mysite".
X	8. Arrange for "expire" to be run periodically (via "cron" or "at").
X
XIf you had an existing (old) news system, and wish to transfer the
Xarticles. The best way to do it is run the command:
X
X	find oldnewsdir -type f -a -print ^
X	while read F
X	do
X		postnews -p < $F
X	done
X
XMichael Rourke
XUniversity of New South Wales, Australia	13 June 1984
X(decvax!mulga!michaelr:elecvax)
X(vax135!mulga!michaelr:elecvax)
!
echo 'rna/active.c':
sed 's/^X//' >'rna/active.c' <<'!'
X/*
X * active file handling routines
X *
X * format of file:
X *	<groupname> ' ' <5 digit #> ' ' <5 digit #> ' ' flag '\n'
X *			  (seq)		  (low)
X */
X
X#include "defs.h"
X
Xstatic char actname[]	 = ACTIVE;
Xstatic int lineno;
Xstatic active	*alist;
X
X/*
X * getseq - Get next sequence number for this group
X *	    and update active file.
X *	    If group missing append to file.
X */
Xchar *
Xgetseq(group)
Xchar *group;
X{
X	register FILE	*f;
X	register int i;
X	char gbuf[BUFSIZ / 2], dbuf[BUFSIZ / 4], dbuf2[BUFSIZ / 4];
X	extern char *itoa();
X
X	f = fopenl(actname);
X	lineno = 0;
X	while (getline(f, gbuf, dbuf, dbuf2))
X		if (CMP(gbuf, group) == 0) {
X			i = atoi(dbuf);
X			i++;
X			fseek(f, -12L, 1);
X			(void) fprintf(f, "%05d", i);
X			fclose(f);
X#if !AUSAM
X			unlock(actname);
X#endif
X			return itoa(i);
X		}
X	(void) fprintf(f, "%s 00001 00001 y\n", group);
X	fclose(f);
X#if !AUSAM
X	unlock(actname);
X#endif
X	return itoa(1);
X}
X
X
Xstatic
Xgetline(f, g, d, d2)
Xregister FILE *f;
Xchar *g, *d, *d2;
X{
X	register int c;
X	register char *s;
X
X	lineno++;
X	s = g;
X	while ((c = getc(f)) != ' ' && c != EOF)
X		*s++ = c;
X	*s = '\0';
X
X	if (c != EOF) {
X		s = d;
X		while ((c = getc(f)) != EOF && isdigit(c))
X			*s++ = c;
X		*s = '\0';
X
X		s = d2;
X		if (c == ' ')
X			while ((c = getc(f)) != EOF && isdigit(c))
X				*s++ = c;
X		*s = '\0';
X
X		if (c == ' ')
X			while ((c = getc(f)) != EOF && c != '\n')
X				;		/* eat flag */
X	}
X
X	if (c != EOF && (c != '\n' || !*d || !*d2))
X		error("%s: bad format: line %d", actname, lineno);
X	return c != EOF;
X}
X
X
X/*
X * build internal active file structure
X */
Xactive *
Xreadactive()
X{
X	register FILE	*f;
X	register active	*ap, *last;
X	char gbuf[BUFSIZ / 2], dbuf[BUFSIZ / 4], dbuf2[BUFSIZ / 4];
X
X	alist = last = NIL(active);
X	f = fopenf(actname, "r");
X	lineno = 0;
X	while (getline(f, gbuf, dbuf, dbuf2)) {
X		ap = NEW(active);
X		ap->a_name = newstr(gbuf);
X		ap->a_seq = atoi(dbuf);
X		ap->a_low = atoi(dbuf2);
X		ap->a_next = NIL(active);
X		if (!alist)
X			alist = ap;
X		else
X			last->a_next = ap;
X		last = ap;
X	}
X	fclose(f);
X	return alist;
X}
X
X
X/*
X * return pointer to named group
X */
Xactive *
Xactivep(grp)
Xregister char *grp;
X{
X	register active	*ap;
X
X	for (ap = alist; ap; ap = ap->a_next)
X		if (CMP(grp, ap->a_name) == 0)
X			break;
X	return ap;
X}
X
X
X/*
X * setlow - set the low number for this group
X */
Xsetlow(group, low)
Xchar *group;
Xint low;
X{
X	register FILE	*f;
X	char gbuf[BUFSIZ / 2], dbuf[BUFSIZ / 4], dbuf2[BUFSIZ / 4];
X	extern char *itoa();
X
X	f = fopenl(actname);
X	lineno = 0;
X	while (getline(f, gbuf, dbuf, dbuf2))
X		if (CMP(gbuf, group) == 0) {
X			fseek(f, -6L, 1);
X			(void) fprintf(f, "%05d", low);
X			break;
X		}
X	fclose(f);
X#if !AUSAM
X	unlock(actname);
X#endif
X}
X
X
X
X/*
X * initgrp - initialise an entry for this group
X */
Xinitgrp(group)
Xchar *group;
X{
X	register FILE	*f;
X	char gbuf[BUFSIZ / 2], dbuf[BUFSIZ / 4], dbuf2[BUFSIZ / 4];
X
X	f = fopenl(actname);
X	lineno = 0;
X	while (getline(f, gbuf, dbuf, dbuf2))
X		if (CMP(gbuf, group) == 0) {
X#if !AUSAM
X			unlock(actname);
X#endif
X			return;
X		}
X	(void) fprintf(f, "%s 00000 00001\n", group);
X
X}
!
echo 'rna/at.h':
sed 's/^X//' >'rna/at.h' <<'!'
X#define SECINWEEK	604800L
X#define SECINDAY	 86400L
X#define SECINHOUR	  3600L
X#define SECINMIN	    60L
X#define	DAYSTO1983	(10*365 + 3*366)
X#define MAXTIME		0x7fffffffL
X
X/*
X * frequencies
X */
X#define HOURLY		1
X#define DAILY		2
X#define WEEKLY		3
X#define MONTHLY		4
X#define BOOT		5
X#define BATCHTIME	6	/* not really a frequency - just looks like one */
X
X/*
X * time types recognised
X */
X#define DAYS		0	/* days only */
X#define TIMES		1	/* days, times */
X#define FULL		2	/* days, times, frequencies */
X#define STIMES		3	/* days, times - be silent about errors */
!
echo 'rna/defs.h':
sed 's/^X//' >'rna/defs.h' <<'!'
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/dir.h>
X#include <stdio.h>
X#include <ctype.h>
X#include <time.h>
X#ifdef USG
X#include <fcntl.h>
X#endif
X#include <signal.h>
X#include <sgtty.h>
X#include "at.h"
X
X#define NEWSVERSION	 "B UNSW 1.1 19 Sep 1984"
X
X/* Things that very well may require local configuration */
X
X#define TIMEZONE	"EST"		/* name of time zone */
X
X#define DFLTSUB "general,general.all"	/* default subscription list	*/
X#define	ADMSUB	"general"		/* Mandatory subscription list */
X#define MODGROUPS "mod.all,all.mod,all.announce"	/* Moderated groups */
X#define DFLTGRP	"general"		/* default newsgroup (for postnews) */
X/* #define MANGRPS	 1		/* if you have mandatory subscriptions
X					   tailored per-person (uses
X					   getclasses()) */
X/*#define OZ	1*/			/* if on Australian network, used
X					   in readnews to get correct return
X					   address */
X/*#define AUSAM	1*/			/* hashed passwd file, locked files */
X#if AUSAM
X#include <passwd.h>
X#else
X#include <pwd.h>
X#endif
X
X#ifdef vax
X/* #define NETPATH	1		/* if you have path finding program
X					   /bin/netpath */
X#endif
X/*#define UNSWMAIL 1*/			/* if you have UNSW "mail" which
X					   allows "-s subject -i include_file"
X					   arguments */
X#define NETID "utzoo"
X#ifndef NETID
X#define NETID "utstat"			/* else define it here */
X#endif
X
X#ifndef NETID
X#include <table.h>			/* UNSW only */
X#endif
X
X/* #define MC "/usr/bin/p"			/* pager */
X#define UUNAME "/usr/bin/uuname"
X#define RNEWS	"exec rnews"		/* rnews for uurec to fork */
X#define POSTNEWS "/usr/bin/inews"
X#define CHOWN	"/etc/chown"		/* pathname of chown command */
X#define SHELL	"/bin/sh"		/* if not bourne shell see postnews.c */
X#define MKDIR	"/bin/mkdir"
X#define MAIL	"/bin/mail"
X#if UNSWMAIL
X#define FASTMAIL	"/bin/mail"
X#else
X#define FASTMAIL	MAIL
X#endif
X
X#define HELP	"/usr/lib/news/help.readnews"		/* Help text */
X#define SEQ	"/usr/lib/news/seq"		/* Next sequence number */
X#define SYS	"/usr/lib/news/sys"		/* System subscription lists */
X#define ACTIVE	"/usr/lib/news/active"		/* Active newsgroups */
X#define HISTORY "/usr/lib/news/history"		/* Current articles */
X
X#define MYDOMAIN "uucp"			/* Local domain */
X#define MYORG	"U of Toronto Zoology" /* My organization */
X#define NEWSROOT "news"			/* news editor */
X
X/* Things you might want to change */
X
X#define NEWSRC  ".newsrc"		/* name of .newsrc file */
X#define	PAGESIZE 24			/* lines on screen */
X#define ARTICLES "articles"		/* default place to save articles */
X#define NEGCHAR	'!'			/* newsgroup negation character	*/
X#define NEGS	"!"			/* ditto (string) */
X#define BADGRPCHARS "/#!"		/* illegal chars in group name */
X#define BUFLEN	256			/* standard buffer size */
X#define ED	"/bin/ed"		/* default, if $EDITOR not set */
X
X/* Things you probably won't want to change */
X
X#define	NGSEPCHAR ','	/* delimit character in news group line		*/
X#define NGSEPS	","	/* ditto */
X#define PSEPS "!"	/* separator in Path: */
X#define PSEPCHAR '!'	/* ditto */
X#define PATHPREF "..!"	/* prefix for addresses worked out from Path: */
X#define TRUE	1
X#define FALSE	0
X
X#ifndef F_SETFD
X#ifdef F_SETFL
X#define F_SETFD F_SETFL		/* SETFL becomes SETFD (close on exec arg
X				   to fcntl) */
X#endif
X#endif
X
Xtypedef enum booltype { false = 0, true } bool;
Xtypedef enum applytype { stop, next, nextgroup, searchgroup } applycom;
Xtypedef applycom (*apcmfunc)();
Xtypedef enum pheadtype { printing, passing, making } pheadcom;
X
X/*
X * header structure
X */
Xtypedef struct header {
X	/* mandatory fields */
X	char	*h_relayversion;
X	char	*h_postversion;
X	char	*h_from;
X	char	*h_date;
X	char	*h_newsgroups;
X	char	*h_subject;
X	char	*h_messageid;
X	char	*h_path;
X	/* optional fields */
X	char	*h_replyto;
X	char	*h_sender;
X	char	*h_followupto;
X	char	*h_datereceived;
X	char	*h_expires;
X	char	*h_references;
X	char	*h_control;
X	char	*h_distribution;
X	char	*h_organisation;
X	char	*h_lines;
X	/* any we don't recognise */
X	char	*h_others;
X} header;
X
X/*
X * internal structure for active file
X */
Xtypedef struct active active;
Xstruct active {
X	char	*a_name;
X	short	a_seq;
X	short	a_low;
X	active	*a_next;
X};
X
X/*
X * internal struct for newsrc file
X */
Xtypedef struct newsrc newsrc;
Xstruct newsrc {
X	char	*n_name;
X	bool	n_subscribe;
X	short	n_last;
X	newsrc	*n_next;
X};
X
Xchar	*strrchr(), *strchr(), *strcat(), *strcpy(), *strpbrk();
Xchar	*itoa(), *convg(), *ngsquash(), *ttoa(), *mgets(), *rconvg();
Xchar	*newstr(), *newstr2(), *newstr3(), *newstr4(), *newstr5(), *catstr();
Xchar	*catstr2(), *bsearch(), *mtempnam(), *newstr6();
Xchar	*getunique(), *getretaddr(), *getsubject();
XFILE	*fopenl(), *fopenf();
Xchar	*memset(), *myalloc(), *myrealloc();
Xlong	time(), atol(), atot();
Xint	strpcmp();
Xactive	*readactive();
Xchar *getenv();
X
X#define NIL(type)	((type *) 0)
X#define NEW(type)	((type *) myalloc(sizeof(type)))
X#define CMP(a, b)	(*(a) != *(b) ? *(a) - *(b) : strcmp(a, b))
X#define CMPN(a, b, n)	(*(a) != *(b) ? *(a) - *(b) : strncmp(a, b, n))
X
X/* bw 9/15/84 */
X#define uid_t int
X#define index strchr
X#define rindex strrchr
!
echo 'rna/funcs.c':
sed 's/^X//' >'rna/funcs.c' <<'!'
X#include "defs.h"
X
X/*
X * string handling functions
X */
Xchar *
Xmyalloc(size)
Xint size;
X{
X	register char *cp;
X
X	extern char *malloc();
X
X	if ((cp = malloc((unsigned) size)) == NIL(char))
X		error("No more memory.");
X	return cp;
X}
X
X
Xchar *
Xmyrealloc(ptr, size)
Xchar *ptr;
Xint size;
X{
X	register char *cp;
X
X	extern char *realloc();
X
X	if ((cp = realloc(ptr, (unsigned) size)) == NIL(char))
X		error("No more memory.");
X	return cp;
X}
X
X
Xchar *
Xnewstr(s)
Xchar *s;
X{
X	return strcpy(myalloc(strlen(s) + 1), s);
X}
X
X
Xchar *
Xnewstr2(s1, s2)
Xchar *s1, *s2;
X{
X	return strcat(strcpy(myalloc(strlen(s1) + strlen(s2) + 1), s1), s2);
X}
X
X
Xchar *
Xnewstr3(s1, s2, s3)
Xchar *s1, *s2, *s3;
X{
X	return strcat(strcat(strcpy(myalloc(strlen(s1) + strlen(s2) + strlen(s3) +
X	    1), s1), s2), s3);
X}
X
X
Xchar *
Xnewstr4(s1, s2, s3, s4)
Xchar *s1, *s2, *s3, *s4;
X{
X	return strcat(strcat(strcat(strcpy(myalloc(strlen(s1) + strlen(s2) +
X	    strlen(s3) + strlen(s4) + 1), s1), s2), s3), s4);
X}
X
X
Xchar *
Xnewstr5(s1, s2, s3, s4, s5)
Xchar *s1, *s2, *s3, *s4, *s5;
X{
X	return strcat(strcat(strcat(strcat(strcpy(myalloc(strlen(s1) + strlen(s2) +
X	    strlen(s3) + strlen(s4) + strlen(s5) + 1), s1), s2), s3), s4), s5);
X}
X
X
Xchar *
Xnewstr6(s1, s2, s3, s4, s5, s6)
Xchar *s1, *s2, *s3, *s4, *s5, *s6;
X{
X	return strcat(strcat(strcat(strcat(strcat(strcpy(myalloc(strlen(s1) +
X	    strlen(s2) + strlen(s3) + strlen(s4) + strlen(s5) + strlen(s6) + 1),
X	     s1), s2), s3), s4), s5), s6);
X}
X
X
Xchar *
Xcatstr(old, s)
Xchar *old, *s;
X{
X	return strcat(myrealloc(old, strlen(old) + strlen(s) + 1), s);
X}
X
X
Xchar *
Xcatstr2(old, s1, s2)
Xchar *old, *s1, *s2;
X{
X	return strcat(strcat(myrealloc(old, strlen(old) + strlen(s1) + strlen(s2) +
X	    1), s1), s2);
X}
X
X
X/*
X * News group matching.
X *
X * nglist is a list of newsgroups.
X * sublist is a list of subscriptions.
X * sublist may have "meta newsgroups" in it.
X * All fields are NGSEPCHAR separated.
X *
X * sublist uses "all" like shell uses "*", and "." like shell uses "/"
X * if subscription X matches Y, it also matches Y.anything
X */
Xngmatch(nglist, sublist)
Xchar *nglist, *sublist;
X{
X	register char *n, *s, *nd, *sd;
X	register int rc;
X
X	rc = 0;
X	n = nglist;
X	while (*n && rc == 0) {
X		if (nd = strchr(n, NGSEPCHAR))
X			*nd = '\0';
X		s = sublist;
X		while (*s) {
X			if (sd = strchr(s, NGSEPCHAR))
X				*sd = '\0';
X			if (*s != NEGCHAR)
X				rc |= ptrncmp(s, n);
X			else
X				rc &= ~ptrncmp(s + 1, n);
X			if (sd)
X				*sd = NGSEPCHAR, s = sd + 1;
X			else
X				break;
X		}
X		if (nd)
X			*nd = NGSEPCHAR, n = nd + 1;
X		else
X			break;
X	}
X	return rc;
X}
X
X
X/*
X * Compare two newsgroups for equality.
X * The first one may be a "meta" newsgroup.
X */
Xstatic
Xptrncmp(ng1, ng2)
Xregister char *ng1, *ng2;
X{
X
X	while (1) {
X		if (ng1[0] == 'a' && ng1[1] == 'l' && ng1[2] == 'l' && (ng1[3] ==
X		    '\0' || ng1[3] == '.')) {
X			if (ng1[3] == '\0')	/* "all" matches anything */
X				return 1;
X			while (*ng2 && *ng2 != '.')
X				ng2++;
X			if (*ng2 != '.')		/* "all." doesn't match "xx" */
X				return 0;
X			ng1 += 4, ng2++;
X			continue;
X		}
X		while (*ng1 && *ng1 != '.' && *ng1 == *ng2)
X			ng1++, ng2++;
X		if (*ng1 == '.') {
X			if (*ng2 != '.' && *ng2 != '\0')
X				return 0;	/* "."'s don't line up */
X			if (*ng2)
X				ng2++;
X			ng1++;			/* "."'s line up - keep going */
X		} else if (*ng1 == '\0')
X			return (*ng2 == '\0' || *ng2 == '.');
X			/* full match or X matching X.thing */
X		else
X			return 0;
X	}
X	/* NOTREACHED */
X}
X
X
X/*
X * return new newsgroup composed of only those from 'nglist'
X * subscribed to by 'sublist'
X * return NULL for empty list
X */
Xchar *
Xngsquash(nglist, sublist)
Xregister char *nglist, *sublist;
X{
X	register char *delim;
X	register char *newg;
X
X	newg = NIL(char);
X	while (*nglist) {
X		if (delim = strchr(nglist, NGSEPCHAR))
X			*delim = '\0';
X		if (ngmatch(nglist, sublist))
X			newg = (newg ? catstr2(newg, NGSEPS, nglist) : newstr(nglist));
X		if (delim)
X			*delim = NGSEPCHAR, nglist = delim + 1;
X		else
X			break;
X	}
X	return newg;
X}
X
X
X/*
X * get unique sequence number from SEQ
X */
Xchar *
Xgetunique()
X{
X	register long number;
X	register FILE	*f;
X	static char buf[12];
X
X	f = fopenl(SEQ);
X	if (fread(buf, 1, sizeof(buf), f) > 0)
X		number = atol(buf);
X	else
X		number = 1;
X
X	rewind(f);
X	(void) fprintf(f, "%ld\n", number + 1);
X	fclose(f);
X#if !AUSAM
X	unlock(SEQ);
X#endif
X
X	sprintf(buf, "%ld", number);
X	return buf;
X}
X
X
X/*
X * open a locked file (or create) for reading and writing
X */
XFILE *
Xfopenl(fname)
Xchar *fname;
X{
X	register FILE	*f;
X#ifdef AUSAM
X	struct stat sbuf;
X#endif
X
X	extern uid_t	newsuid;
X
X	if ((f = fopen(fname, "r+")) == NIL(FILE) && (f = fopen(fname, "w+")) ==
X	    NIL(FILE))
X		error("Can't open %s", fname);
X
X#if AUSAM
X	if (fstat(fileno(f), &sbuf) != 0)
X		error("Can't stat %s", fname);
X	if ((sbuf.st_mode & S_IFMT) != S_IFALK && (chmod(fname, (int) (sbuf.st_mode
X	    &~S_IFMT) | S_IFALK) != 0 || chown(fname, (int) newsuid, (int) newsuid) !=
X	    0 || fclose(f) == EOF || (f = fopen(fname, "r+")) == NIL(FILE)))
X		error("Can't create %s", fname);
X#else
X	chown(fname, (int) newsuid, (int) newsuid);
X	lock(fname);
X#endif
X
X	return f;
X}
X
X
X#if !AUSAM
X
X#define LSUFFIX	".lock"		/* suffix for lock files */
X
Xlock(fname)
Xchar *fname;
X{
X	register char *lname;
X	register int i, f;
X
X	lname = newstr2(fname, LSUFFIX);
X	for (i = 0; i < 10; i++) {
X		if ((f = creat(lname, 0)) != -1) {
X			close(f);
X			free(lname);
X			return;
X		}
X		sleep(2);
X	}
X	error("Can't creat %s after %d tries", lname, i);
X}
X
X
Xunlock(fname)
Xchar *fname;
X{
X	register char *lname;
X
X	lname = newstr2(fname, LSUFFIX);
X	unlink(lname);
X	free(lname);
X}
X
X
X#endif
X
X/*
X * open a file
X */
XFILE *
Xfopenf(name, mode)
Xchar *name, *mode;
X{
X	register FILE	*f;
X
X	if ((f = fopen(name, mode)) == NIL(FILE))
X		error("Can't %s %s", *mode == 'r' ? "open" : "create", name);
X	return f;
X}
X
X
X/*
X * replace all '.''s with '/'
X */
Xchar *
Xconvg(s)
Xregister char *s;
X{
X	register char *sav;
X
X	sav = s;
X	while (s = strchr(s, '.'))
X		*s = '/';
X	return sav;
X}
X
X
X/*
X * replace all '/''s with '.'
X */
Xchar *
Xrconvg(s)
Xregister char *s;
X{
X	register char *sav;
X
X	sav = s;
X	while (s = strchr(s, '/'))
X		*s = '.';
X	return sav;
X}
X
X
X/*
X * get a line from stdin
X * trim leading and trailing blanks
X */
Xchar *
Xmgets()
X{
X	register char *s;
X	static char buf[BUFSIZ];
X
X	fflush(stdout);
X	if (fgets(buf, sizeof(buf), stdin) == NIL(char)) {
X		(void) printf("\n");
X		return NIL(char);
X	}
X	if (s = strchr(buf, '\n'))
X		while (isspace(*s) && s > buf)
X			*s-- = '\0';
X	else
X	 {
X		(void) printf("Input line too long.\n");
X		return NIL(char);
X	}
X	s = buf;
X	while (isspace(*s))
X		s++;
X	return s;
X}
X
X
Xreadln(f)
XFILE *f;
X{
X	register int c;
X
X	if (feof(f) || ferror(f))
X		return;
X	while ((c = getc(f)) != '\n' && c != EOF)
X		;
X}
X
X
X/*
X * compare string pointers
X */
Xstrpcmp(a, b)
Xchar **a, **b;
X{
X	return CMP(*a, *b);
X}
X
X
X/*
X * apply the given function to each member in the newsgroup
X */
X/* VARARGS2 */
Xapplyng(ng, func, arg1)
Xregister char *ng;
Xregister int (*func)();
Xchar *arg1;
X{
X	register char *delim;
X	register int err;
X
X	err = 0;
X	while (*ng) {
X		if (delim = strchr(ng, NGSEPCHAR))
X			*delim = '\0';
X		err += (*func)(ng, arg1);
X		if (delim)
X			*delim = NGSEPCHAR, ng = delim + 1;
X		else
X			break;
X	}
X	return err;
X}
X
X
X/*
X * generate a return address
X */
Xchar *
Xgetretaddr(hp)
Xheader *hp;
X{
X	register char *ra;
X
X	extern char *getpath(), *exaddress();
X#ifdef NETPATH
X	extern char *getnetpath();
X#endif
X
X	if (hp->h_replyto)
X		ra = exaddress(hp->h_replyto);
X	else if (hp->h_from)
X		ra = exaddress(hp->h_from);
X	else
X		ra = NIL(char);
X	if (hp->h_path && !ra)
X		ra = getpath(hp->h_path);
X#ifdef NETPATH
X	if (CMPN(ra, PATHPREF, sizeof(PATHPREF) - 1) == 0)
X		ra = getnetpath(ra);
X#endif
X	return ra;
X}
X
X
X/*
X * try and make a proper address
X */
Xchar *
Xexaddress(addr)
Xchar *addr;
X{
X	register char *space, *dot, *at;
X	register char *raddr;
X
X	raddr = NIL(char);
X	if (space = strchr(addr, ' '))
X		*space = '\0';
X	if (at = strchr(addr, '@')) {
X		*at = '\0';
X		if (dot = strchr(at + 1, '.')) {
X			*dot = '\0';
X#if OZ
X			if (CMP(dot + 1, MYDOMAIN) == 0)
X				raddr = newstr3(addr, ":", at + 1);
X			else
X#endif
X				raddr = newstr4(PATHPREF, at + 1, PSEPS, addr);
X			*dot = '.';
X		}
X		*at = '@';
X	}
X	if (space)
X		*space = ' ';
X	return raddr;
X
X}
X
X
X/*
X * return the last two components of the path
X */
Xchar *
Xgetpath(path)
Xchar *path;
X{
X	register char *exlast, *ex;
X	register char *raddr;
X
X	if (exlast = strrchr(path, PSEPCHAR)) {
X		*exlast = '\0';
X		if (ex = strrchr(path, PSEPCHAR))
X			raddr = newstr4(PATHPREF, ex + 1, PSEPS, exlast + 1);
X		else
X			raddr = newstr3(path, PSEPS, exlast + 1);
X		*exlast = PSEPCHAR;
X	} else
X		raddr = NIL(char);
X	return raddr;
X}
X
X
X#ifdef NETPATH
X/*
X * try and work out a path from our "netpath" database
X */
Xchar *
Xgetnetpath(path)
Xchar *path;
X{
X	FILE		 * f;
X	register char *ex1, *ex2, *com, *new;
X	char buf[BUFSIZ];
X
X	if ((ex1 = strchr(path, PSEPCHAR)) && (ex2 = strchr(ex1 + 1, PSEPCHAR))) {
X		*ex2 = '\0';
X		com = newstr4("exec ", NETPATH, " mulga ", ex1 + 1);
X		if ((f = popen(com, "r")) == NIL(FILE))
X			(void) printf("Couldn't run \"%s\"\n", com);
X		else
X		 {
X			fread(buf, sizeof(buf), 1, f);
X			if (pclose(f) != 0) {
X				(void) printf("Error in running \"%s\"\n", com);
X				fflush(stdout);
X			} else if (CMPN(buf, "mulga!", 6) == 0) {
X				if (ex1 = strchr(buf, '\n'))
X					*ex1 = '\0';
X				new = newstr4(buf + 6, PSEPS, ex2 + 1, ":mulga");
X				free(path);
X				path = new;
X			}
X		}
X		free(com);
X		*ex2 = PSEPCHAR;
X	}
X	return path;
X
X}
X
X
X#endif
X
X/*
X * remove extra spaces, and insert separators if necessary in
X * newsgroups specification
X */
Xconvgrps(sp)
Xregister char *sp;
X{
X	register char *sep;
X
X	sep = NIL(char);
X	while (*sp) {
X		if (sep)
X			sp++;
X		while (*sp && (isspace(*sp) || *sp == NGSEPCHAR))
X			strcpy(sp, sp + 1);
X		if (sep)
X			*sep = (*sp ? NGSEPCHAR : '\0');
X		while (*sp && !isspace(*sp) && *sp != NGSEPCHAR)
X			sp++;
X		sep = sp;
X	}
X}
X
X
!
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.