[comp.sources.unix] v12i035: C News alpha release, Part10/14

rsalz@uunet.UU.NET (Rich Salz) (10/23/87)

Submitted-by: utzoo!henry (Henry Spencer)
Posting-number: Volume 12, Issue 35
Archive-name: cnews/part10


#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 10 (of 14)."
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'rna/funcs.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rna/funcs.c'\"
else
echo shar: Extracting \"'rna/funcs.c'\" \(9767 characters\)
sed "s/^X//" >'rna/funcs.c' <<'END_OF_FILE'
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
END_OF_FILE
if test 9767 -ne `wc -c <'rna/funcs.c'`; then
    echo shar: \"'rna/funcs.c'\" unpacked with wrong size!
fi
# end of 'rna/funcs.c'
fi
if test -f 'rna/header.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rna/header.c'\"
else
echo shar: Extracting \"'rna/header.c'\" \(10079 characters\)
sed "s/^X//" >'rna/header.c' <<'END_OF_FILE'
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
END_OF_FILE
if test 10079 -ne `wc -c <'rna/header.c'`; then
    echo shar: \"'rna/header.c'\" unpacked with wrong size!
fi
# end of 'rna/header.c'
fi
if test -f 'rna/maketime.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rna/maketime.c'\"
else
echo shar: Extracting \"'rna/maketime.c'\" \(8785 characters\)
sed "s/^X//" >'rna/maketime.c' <<'END_OF_FILE'
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
END_OF_FILE
if test 8785 -ne `wc -c <'rna/maketime.c'`; then
    echo shar: \"'rna/maketime.c'\" unpacked with wrong size!
fi
# end of 'rna/maketime.c'
fi
if test -f 'rnews/relaynews.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rnews/relaynews.c'\"
else
echo shar: Extracting \"'rnews/relaynews.c'\" \(9783 characters\)
sed "s/^X//" >'rnews/relaynews.c' <<'END_OF_FILE'
X/*
X * relaynews - relay Usenet news (version C)
X * See the file COPYRIGHT for the copyright notice.
X *
X * relaynews should be setuid-news, setgid-news.  You'll need to install
X * setnewsids setuid-root if setuid(geteuid()) doesn't work on your
X * machine (e.g. on V7 and possibly SystemIII).
X *
X * Written by Geoff Collyer, 15-20 November 1985 and revised periodically
X * since.
X *
X * relaynews parses Newsgroups: headers, rejects articles by newsgroup &
X * message-id, files articles, updates the active & history files,
X * transmits articles, and honours (infrequent) control messages, which do
X * all sorts of varied and rococo things.  Control messages are implemented
X * by separate programs.  relaynews reads a "sys" file to control the
X * transmission of articles but can function as a promiscuous leaf node
X * without one.
X *
X * A truly radical notion: people may over-ride via environment variables
X * the compiled-in default directories so IHCC kludges are not needed and
X * testing is possible (and encouraged) in alternate directories.  This
X * does cause a loss of privilege, to avoid spoofing.
X *
X * The disused I-have/send-me protocol is going to work; it's been broken
X * in B news for ages but no one has noticed because it's essentially
X * useless on the uucp network, especially when batching news articles,
X * but NNTP may breathe new life into it.
X *
X * Portability vs SystemV.  relaynews uses dbm(3) and makes no apologies
X * for so doing.  Imitation UNIX (registered trademark of AT&T in the
X * United States) brand operating systems that lack dbm are going to
X * have to use my incredibly slow dbm simulation.
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <errno.h>
X#include <signal.h>		/* to make locking safe */
X#include <sys/types.h>
X
X#include "news.h"
X#include "newspaths.h"
X#include "active.h"
X#include "cpu.h"
X#include "headers.h"
X#include "system.h"
X
X/*
X * setuid-root program to set ids to news/news & rexec rnews with
X * NEWSPERMS in the environment to break loops.
X */
X#ifndef SETNEWSIDS
X#define SETNEWSIDS "setnewsids"
X#endif
X
X#ifndef NEWSUSER
X#define NEWSUSER "news"
X#endif
X#ifndef NEWSGROUP
X#define NEWSGROUP "news"
X#endif
X
Xchar *progname;
X
Xint remote = NO;			/* articles are being relayed? */
X
Xchar *exclude = NULL;			/* site to exclude, for erik */
Xstatic int userealids = NO;
X
X/*
X * main - parse arguments and handle options, lock & unlock news system.
X */
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X	int c, errflg = 0;
X	int status = ST_OKAY;
X	char origdir[MAXFILE];		/* current directory at start */
X	char *newpath;
X	extern int optind;
X	extern char *optarg;
X
X	progname = argv[0];
X
X	/* setuid daemon prelude; various precautions */
X	(void) umask(newsumask());	/* undo silly umasks */
X	(void) alarm(0);		/* cancel any pending alarm */
X	/*
X	 * Reset certain environment variables to sane values.
X	 */
X	newpath = malloc(STRLEN("PATH=") + STRLEN(STDPATH) + 1);
X	if (newpath == NULL)
X		exit(1);		/* no chatter until stdfdopen */
X	(void) strcpy(newpath, "PATH=");
X	(void) strcat(newpath, STDPATH);
X	if (!putenv(newpath) ||
X	    !putenv("IFS= \t\n"))
X		exit(1);		/* no chatter until stdfdopen */
X	closeall(1);			/* closes all but std descriptors */
X	stdfdopen();			/* ensure standard descriptors are open */
X
X	setids(argv);			/* change of real and effective ids */
X	/* we are now running as news, so you can all relax */
X
X	/* ignore signals (for locking). relaynews runs quickly, so don't worry. */
X	(void) signal(SIGINT, (sigarg_t)SIG_IGN);
X	(void) signal(SIGQUIT, (sigarg_t)SIG_IGN);
X	(void) signal(SIGHUP, (sigarg_t)SIG_IGN);
X	(void) signal(SIGTERM, (sigarg_t)SIG_IGN);
X
X	/* parse options & set flags */
X	while ((c = getopt(argc, argv, "pd:x:")) != EOF)
X		switch (c) {
X		case 'p':		/* "rnews" mode: */
X			++remote;	/* just relay, don't fuck about */
X			break;
X		/* all options below are new in C news */
X		case 'd':		/* -d debug-options; thanks, henry */
X			if (!debugon(optarg))
X				errflg++;	/* debugon already complained */
X			break;
X		case 'x':			/* -x site: don't send to site */
X			/* you're welcome, erik */
X			if (exclude != NULL) {
X				(void) fprintf(stderr,
X					"%s: more than one -x site (%s)\n",
X					progname, optarg);
X				errflg++;
X			} else
X				exclude = optarg;
X			break;
X		default:
X			errflg++;
X			break;
X		}
X	if (errflg) {
X		(void) fprintf(stderr, "usage: %s [-p][-d fhlmt][-x site]\n",
X			progname);
X		exit(2);
X	}
X
X	/* lock the news system, set up log files */
X	newslock();			/* done here due to dbm internal cacheing */
X	if (remote) {		/* TODO: test this some other way */
X		redirectlogs();		/* rnews daemon: redirect to logs */
X#ifdef MANYERRORS
X		(void) putc('\n', stderr);	/* leave a blank line */
X		/* prints "Jun  5 12:34:56" */
X		timestamp(stderr, (time_t *)NULL, (char **)NULL);
X		(void) putc('\n', stderr);
X#endif
X	}
X
X	/* process file name arguments */
X#ifdef RELATIVE_FILES_ALLOWED
X	if (getwd(origdir) == 0)
X#endif
X		(void) strncpy(origdir, "/dunno/man/like/somewhere.", MAXFILE);
X	cd(fullartfile((char *)NULL));		/* move to spool directory */
X
X	if (optind == argc)
X		status |= process(stdin, "stdin");
X	else
X		for (; optind < argc; optind++)
X			status |= relnmprocess(argv[optind], origdir);
X
X	trclose();			/* close open batch files */
X	status |= synccaches();		/* just being cautious */
X	newsunlock();			/* unlock the news system */
X	exit(status);
X}
X
Xsetids(argv)				/* change of real and effective ids */
Xchar **argv;
X{
X	int newsuid = getuid(), newsgid = getgid();	/* default to real ids */
X
X	(void) ctlfile((char *)NULL);	/* trigger unprivileged(), set userealids */
X	if (!userealids) {
X		/*
X		 * If setuid(geteuid()) fails, try execing a small,
X		 * setuid-root program to just do getpwnam(), getgrnam()
X		 * (with NEWSPERMS set), setgid(), setuid(),
X		 * and exec this program again.  If NEWSPERMS is set,
X		 * the failure is a fatal error (recursive loop).
X		 * Then this program can be setuid-news.
X		 */
X		(void) setgid(getegid());
X		if (setuid(geteuid()) < 0) {
X			if (getenv("NEWSPERMS") != 0)
X				error("recursive loop setting ids", "");
X			execv(libfile(SETNEWSIDS), argv);
X			error("can't exec %s to set ids", libfile(SETNEWSIDS));
X			/* NOTREACHED */
X		}
X		/* you can relax, we are now running as news */
X	} else {
X		(void) setgid(newsgid);
X		(void) setuid(newsuid);
X	}
X	/* we are now running as news, so you can all relax */
X}
X
Xvoid
Xunprivileged()			/* called if NEWSARTS, NEWSCTL or NEWSBIN present */
X{
X	userealids = YES;
X}
X
Xint					/* YES/NO */
Xdebugon(dbopt)
Xregister char *dbopt;
X{
X	int status = YES;
X
X	for (; *dbopt != '\0'; dbopt++)
X		switch (*dbopt) {
X		case 'f':
X			filedebug(YES);
X			break;
X		case 'h':
X			hdrdebug(YES);
X			break;
X		case 'l':
X			lockdebug(YES);
X			break;
X		case 'm':
X			matchdebug(YES);
X			break;
X		case 't':
X			transdebug(YES);
X			break;
X		default:
X			status = NO;	/* unknown debugging option */
X			(void) fprintf(stderr, "%s: bad -d %c\n",
X				progname, *dbopt);
X			break;
X		}
X	return status;
X}
X
X/*
X * Redirect stdout and stderr into log files at known locations.
X */
Xredirectlogs()
X{
X	logfile(stdout, libfile("log"));
X	logfile(stderr, libfile("errlog"));
X}
X
Xlogfile(stream, name)			/* redirect stream into name */
XFILE *stream;
Xchar *name;
X{
X	if (freopen(name, "a", stream) == NULL)
X		errunlock("can't redirect standard stream to %s", name);
X}
X
Xint					/* status */
Xrelnmprocess(name, origdir)		/* process a (relative) file name */
Xchar *name, *origdir;
X{
X	register int status = ST_OKAY;
X	register FILE *in;
X	register char *fullname;
X
X	fullname = emalloc((unsigned)strlen(origdir) + STRLEN(SFNDELIM) +
X		strlen(name) + 1);
X	fullname[0] = '\0';
X
X	if (name[0] != FNDELIM) {	/* relative path */
X		(void) strcat(fullname, origdir);
X		(void) strcat(fullname, SFNDELIM);
X	}
X	(void) strcat(fullname, name);
X
X	in = fopen(fullname, "r");
X	if (in == NULL)
X		warning("can't open argument `%s'", fullname);
X	else {
X		status |= process(in, fullname);
X		(void) fclose(in);
X	}
X	free(fullname);
X	return status;
X}
X
X/*
X * process - process input file
X * If it starts with '#', assume it's a batch and unravel it,
X * else it's a single article, so just inject it.
X */
Xint
Xprocess(in, inname)
XFILE *in;
Xchar *inname;
X{
X	register int c;
X
X	if ((c = getc(in)) == EOF)
X		return ST_OKAY; 		/* normal EOF */
X	(void) ungetc(c, in);
X	if (c == '#')
X		return unbatch(in, inname);
X	else
X		/* ST_SHORT should always come on with a count of MAXLONG */
X		return cpinsart(in, inname, MAXLONG) & ~ST_SHORT;
X}
X
X/*
X * Unwind in and insert each article.
X * For each article, call cpinsart to copy the article
X * from in into a temporary file and rename the temp file
X * into the news spool directory.
X */
Xint
Xunbatch(in, inname)
XFILE *in;
Xchar *inname;
X{
X	register int c;
X	long charcnt;
X	int status = ST_OKAY;
X	char line[MAXLINE];
X
X	while (!(status&ST_DISKFULL) && (c = getc(in)) != EOF) {
X		(void) ungetc(c, in);
X		/*
X		 * While out of sync, eat input lines,
X		 * then eat the tail end of the "#! rnews" line.
X		 */
X		while (fgets(line, sizeof line, in) != NULL &&
X		    !batchln(line, &charcnt)) {
X			status |= ST_DROPPED;		/* argh! a bad batch */
X			(void) fprintf(stderr, "%s: unbatcher out of synch, tossing: ",
X				progname);
X		    	(void) fputs(line, stderr);
X		}
X		if (!feof(in))
X			status |= cpinsart(in, inname, charcnt);
X	}
X	return status;
X}
X
X/*
X * Is line a batcher-produced line (#! rnews count)?
X * If so, return the count through charcntp.
X * This is slightly less convenient than sscanf, but a lot smaller.
X */
Xint					/* YES/NO */
Xbatchln(line, charcntp)
Xregister char *line;
Xregister long *charcntp;
X{
X	register char *countp;
X	static char batchtext[] = "#! rnews ";
X
X	countp = line + STRLEN(batchtext);
X	if (STREQN(line, batchtext, STRLEN(batchtext)) &&
X	    isascii(*countp) && isdigit(*countp)) {
X		*charcntp = atol(countp);
X		return YES;
X	} else {
X		*charcntp = 0;
X		return NO;
X	}
X}
END_OF_FILE
if test 9783 -ne `wc -c <'rnews/relaynews.c'`; then
    echo shar: \"'rnews/relaynews.c'\" unpacked with wrong size!
fi
# end of 'rnews/relaynews.c'
fi
if test -f 'rnews/transmit.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rnews/transmit.c'\"
else
echo shar: Extracting \"'rnews/transmit.c'\" \(7923 characters\)
sed "s/^X//" >'rnews/transmit.c' <<'END_OF_FILE'
X/*
X * transmit - transmit incoming articles to neighbouring machines
X * TODO: deal with multiple references to the same batch file.
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <sys/types.h>
X#include "news.h"
X#include "newspaths.h"
X#include "headers.h"
X#include "system.h"
X
X#define NOPENTFS 10	/* # of file descriptors kept open for batching */
X
Xstatic FILE *tfs[NOPENTFS];			/* keep these open always */
Xstatic int debug = NO;
X
Xtransdebug(state)
Xint 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 * hdrs->h_distr (which may be just a copy of hdrs->h_ngs).
X */
Xint
Xtransmit(hdrs, rmt, exclude)
Xregister struct headers *hdrs;
Xint rmt;					/* inews/rnews flag */
Xchar *exclude;					/* no copy to him */
X{
X	register struct system *sys;
X	register int fno = 0;
X	int status = 0;
X
X	rewsys();
X	if (debug)
X		(void) fprintf(stderr, "just rewound sys file\n");
X	while ((sys = nextsys()) != NULL) {
X		if (debug) {
X			(void) fprintf(stderr, "hdrs->h_distr=%s\n",
X				hdrs->h_distr);
X			(void) fprintf(stderr, "sy_name=%s sy_ngs=%s\n",
X				sys->sy_name, sys->sy_ngs);
X		}
X		if (oktransmit(hdrs, sys, sys->sy_flags, rmt, exclude))
X			status |= ejaculate(hdrs, sys, fno);
X		if (sys->sy_flags&(FLG_BATCH|FLG_SZBATCH))
X			++fno;		/* count lines with F or f flag */
X	}
X	if (debug)
X		(void) fprintf(stderr, "just finished reading sys file\n");
X	return status; 			
X}
X
X/*
X * Is it okay to send the article corresponding to hdrs to sys,
X * given flags (derived from sys) and rmt?
X *
X * Never send to this host, nor any host named in Path:.
X * Newsgroups must match sys's subscription list.
X * Also, Distribution: must match sys's distribution list.
X * If L flag is on, must be a local posting.
X *
X * TODO: implement Ln restriction:
X *	forward articles generated within sysp->sy_lochops hops of here.
X * TODO: implement exclusions by site, from sy_excl (can be NULL).
X */
Xstatic int
Xoktransmit(hdrs, sys, flags, rmt, exclude)
Xregister struct headers *hdrs;
Xregister struct system *sys;
Xint flags, rmt;
Xchar *exclude;				/* no copy to him */
X{
X	return (!(flags&FLG_LOCAL) || !rmt) &&		/* local & */
X	    !STREQ(hostname(), sys->sy_name) &&		/* not to ME & */
X	    (exclude == NULL || !STREQ(exclude, sys->sy_name)) &&	/* not excluded & */
X	    (hdrs->h_path == NULL || !hostin(hdrs->h_path, sys->sy_name)) &&	/* not been here & */
X	    ngmatch(sys->sy_ngs, hdrs->h_ngs) &&	/* ngs match & */
X	    /* RFC 850 is wrong, yea, verily: Distribution:s are *not* patterns */
X	    ngmatch(sys->sy_distr, hdrs->h_distr);	/* distrs match! */
X}
X
X/*
X * send the article denoted by hdrs to the system denoted by sys.
X */
XSTATIC int				/* status */
Xejaculate(hdrs, sys, fno)		/* kick the article to its reward */
Xregister struct headers *hdrs;
Xregister struct system *sys;
Xint fno;
X{
X	int status = ST_OKAY;
X	char filename[MAXLINE];
X	char *fullname;
X
X    	/* can't use hdrs->h_tmpf because we need a permanent name */
X	first(hdrs->h_files, filename);
X	mkfilenm(filename);
X    	(void) printf(" %s", sys->sy_name);	/* logging */
X	if (debug)
X		(void) fprintf(stderr, "transmitting %s to %s\n",
X			hdrs->h_msgid, sys->sy_name);
X
X    	/* must supply a full pathname to the outside world */
X    	fullname = fullspoolfile(filename);
X	if (sys->sy_flags&(FLG_BATCH|FLG_SZBATCH))
X    		status |= trbatch(sys, hdrs, fullname, fno);
X	else
X    		status |= trcmd(sys, fullname);
X	return status;
X}
X
X/*
X * Append filename to sys->sy_cmd. fno is the ordinal number of this sys line.
X * If fno is low enough, use the tfs cache of batch file descriptors.
X */
Xint
Xtrbatch(sys, hdrs, filename, fno)
Xstruct system *sys;
Xstruct headers *hdrs;
Xchar *filename;
Xregister int fno;
X{
X	register int status = 0;
X	char *batfile = sys->sy_cmd;
X
X	if (fno >= NOPENTFS) {				/* not cachable */
X		register FILE *batchf = fopenclex(batfile, "a");
X
X		if (batchf == NULL) {
X			warning("can't open batch file %s", batfile);
X			status |= ST_DROPPED;
X		} else {
X			status |= trappend(batchf, sys, hdrs, filename);
X			if (fclose(batchf) == EOF)
X				status = fulldisk(status|ST_DROPPED, batfile);
X		}
X	} else {					/* cachable */
X		if (tfs[fno] == NULL) {			/* closed */
X			tfs[fno] = fopenclex(batfile, "a");
X			if (tfs[fno] == NULL) {		/* didn't open */
X				register int openf;
X
X				/*
X				 * Assume open failed due to lack of file
X				 * descriptors.  Find an open one and close it,
X				 * then retry the open.  Honk at someone too?
X				 */
X				for (openf = 0; openf < NOPENTFS; openf++)
X					if (tfs[openf] != NULL)	/* open */
X						break;
X				if (openf < NOPENTFS && tfs[openf] != NULL) {
X					if (fclose(tfs[openf]) == EOF)
X						status = fulldisk(status|ST_DROPPED,
X							"some batch file");
X					tfs[openf] = NULL;	/* mark closed */
X					tfs[fno] = fopenclex(batfile, "a");
X				}
X			}
X		}
X		if (tfs[fno] == NULL) {			/* still closed! */
X			warning("can't open batch file %s", batfile);
X			status |= ST_DROPPED;
X		} else
X			status |= trappend(tfs[fno], sys, hdrs, filename);
X	}
X	return status;
X}
X
Xstatic int
Xtrappend(fp, sys, hdrs, name)			/* write name\n on fp */
Xregister FILE *fp;
Xregister struct system *sys;
Xregister struct headers *hdrs;
Xchar *name;
X{
X	int status = ST_OKAY;
X
X	if (fputs(name, fp) == EOF)	/* append to batch file */
X		status = fulldisk(status|ST_DROPPED, "some batch file");
X	/* for Henry's new batcher */
X	if (sys->sy_flags&FLG_SZBATCH &&
X	    fprintf(fp, " %ld", hdrs->h_charswritten) == EOF)
X		status = fulldisk(status|ST_DROPPED, "some batch file");
X	/* don't check putc return value for portability; use ferror */
X	(void) putc('\n', fp);
X	(void) fflush(fp);		/* for crash-proofness */
X	if (ferror(fp))
X		status = fulldisk(status|ST_DROPPED, "some batch file");
X	return status;
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 */
Xint
Xtrcmd(sys, filename)
Xstruct system *sys;
Xchar *filename;
X{
X	int status = ST_OKAY, exitstat;
X	char *cmd;
X	char *syscmd = sys->sy_cmd;
X
X	cmd = emalloc((unsigned)STRLEN("PATH=") + STRLEN(STDPATH) + STRLEN(" <") +
X		strlen(filename) + STRLEN(" ") + strlen(syscmd) +
X		strlen(filename) + 1);
X	(void) strcpy(cmd, "PATH=");
X	(void) strcat(cmd, STDPATH);
X	(void) strcat(cmd, " <");
X	/*
X	 * redirect stdin to prevent consuming my stdin & so cmd's stdin
X	 * is filename by default.
X	 */
X	(void) strcat(cmd, filename);
X	(void) strcat(cmd, " ");
X	(void) sprintf(cmd+strlen(cmd), syscmd, filename);
X	exitstat = system(cmd);
X	if (exitstat != 0) {
X		extern char *progname;
X
X		status |= ST_DROPPED;
X		(void) fprintf(stderr, "%s: `%s' returned exit status 0%o\n",
X			progname, cmd, exitstat);
X	}
X	free(cmd);
X	return status;
X}
X
Xtrclose()
X{
X	register int fno;
X
X	for (fno = 0; fno < NOPENTFS; fno++)
X		if (tfs[fno] != NULL) {
X			(void) fclose(tfs[fno]);
X			tfs[fno] = NULL;
X		}
X}
X
X/*
X * Return true iff host appears in s, with no characters from the alphabet
X * of legal hostname characters immediately adjacent.
X * This function is a profiling hot spot, so it has been optimised.
X */
Xint
Xhostin(s, host)
Xregister char *s, *host;
X{
X	register int hostlen = strlen(host);
X	register int ch;		/* use by hostchar macro */
X
X/* If c is NUL, hostchar will be false, so don't test (optimisation: ==). */
X#define nothostchar(c) (!hostchar(c) /* || (c) == '\0' */ )	/* ! or EOS */
X/*
X * True if c can be part of a hostname. RFC 850 allows letters, digits, periods,
X * and hyphens and specifically disallows blanks. False may mean c is NUL.
X */
X#define hostchar(c) (ch = (c) , \
X	(isascii(ch) && (isalnum(ch) || (ch) == '.' || (ch) == '-')))
X
X	/*
X	 * Special case: match host!path or host.
X	 */
X	if (STREQN(s, host, hostlen) && nothostchar(s[hostlen]))
X		return YES;
X	/*
X	 * Match path2!host!path or path2!host.
X	 */
X	while (*s != '\0')
X		if (hostchar(s[0]))		/* can't start after here */
X			++s;
X		else if ((++s, STREQN(s, host, hostlen)) &&
X		    nothostchar(s[hostlen]))
X			return YES;
X	return NO;
X}
END_OF_FILE
if test 7923 -ne `wc -c <'rnews/transmit.c'`; then
    echo shar: \"'rnews/transmit.c'\" unpacked with wrong size!
fi
# end of 'rnews/transmit.c'
fi
echo shar: End of archive 10 \(of 14\).
##  End of shell archive.
exit 0