Vnews part 2 (of 7)

sources-request at genrad.UUCP sources-request at genrad.UUCP
Fri Jan 25 09:04:24 AEST 1985


# From: ka at hou3c
#
# Welcome to vnews release 2.11-B 1/17/85.
# This is part 2 out of 7.
# Feed me into sh (NOT csh).

if test ! -d artfile
then	mkdir artfile
fi

if test ! -d lib
then	mkdir lib
fi

cat > artfile/ifuncs.c.10.2 <<\!E!O!F!
/*
 * ifuncs - functions used by inews.
 */

#ifndef lint
static char	*SccsId = "@(#)ifuncs.c	2.35	9/12/84";
#endif !lint

#include "iparams.h"
#include <errno.h>

/*
 * Transmit this article to all interested systems.
 */

#ifdef u370
static struct srec srec;
#endif

static struct hbuf h, hh;

broadcast()
{
	register char *nptr, *hptr;
	register FILE *fp;
#ifndef u370
	struct srec srec;
#endif
	char sentbuf[BUFLEN];
	int nsent = 0;

	/* h is a local copy of the header we can scribble on */
	fp = xfopen(ARTICLE, "r");
	if (hread(&h, fp, TRUE) == NULL)
		xerror("Cannot reread article");
	fclose(fp);

	strcpy(sentbuf, h.ident);
	strcat(sentbuf, " sent to ");
	nsent = 0;
	/* break path into list of systems. */
	hptr = nptr = h.path;
	while (*hptr != '\0') {
		if (index(NETCHRS, *hptr)) {
			*hptr++ = '\0';
			nptr = hptr;
		} else
			hptr++;
	}
	*nptr = '\0';

	/* loop once per system. */
	lock();
	s_openr();
	while (s_read(&srec)) {
		if (strncmp(srec.s_name, FULLSYSNAME, SNLN) == 0)
			continue;
		hptr = h.path;
		while (*hptr != '\0') {
			if (strncmp(srec.s_name, hptr, SNLN) == 0)
				goto contin;
			while (*hptr++ != '\0')
				;
		}
		if (ngmatch(h.nbuf, srec.s_nbuf)) {
			if (h.distribution[0] == '\0' ||
				ngmatch(h.distribution, srec.s_nbuf) ||
				ngmatch(h.nbuf, h.distribution)) {
				if (transmit(&srec, xfopen(ARTICLE, "r"), 1)) {
					if (nsent)
						strcat(sentbuf, ", ");
					strcat(sentbuf, srec.s_name);
					nsent++;
				}
			}
		}
	contin:;
	}
	if (nsent)
		log(sentbuf);
	s_close();
	unlock();
}

/*
 * Transmit file to system.
 */
#define PROC 0004
transmit(sp, ifp, maynotify)
register struct srec *sp;
register FILE *ifp;
int maynotify;
{
	register FILE *ofp;
	register int c;
	register char *ptr;
	char TRANS[BUFLEN];
	char *argv[20];
	register int pid;
	extern char firstbufname[];

/* A:	afmt: the other machine runs an A news, so we xmit in A format */
	int afmt = (index(sp->s_flags, 'A') != NULL);
/* B:	use B format (this is the default - don't use this letter elsewise). */
/* F:	append name to file */
	int appfile = (index(sp->s_flags, 'F') != NULL);
/* H:	history: expand %s into the history line (KSA) */
	int history = (index(sp->s_flags, 'H') != NULL);  /* (6/18/84 KSA) */
/* L:	local: don't send the article unless it was generated locally */
	int local = (index(sp->s_flags, 'L') != NULL);
/* N:	notify: don't send the article, just tell him we have it */
	int notify = maynotify && (index(sp->s_flags, 'N') != NULL);
/* S:	noshell: don't fork a shell to execute the xmit command */
	int noshell = (index(sp->s_flags, 'S') != NULL);
/* U:	useexist: use the -c option to uux to use the existing copy */
	int useexist = (index(sp->s_flags, 'U') != NULL);

	if (local && mode == PROC) {
		fclose(ifp);
		return FALSE;
	}
#ifdef DEBUG
	printf("Transmitting to '%s'\n", sp->s_name);
#endif
	if (!appfile && !useexist && !history) {	/* (6/18/84 KSA) */
		if (hread(&hh, ifp, TRUE) == NULL) {
			logerr("Bad header, not transmitting %s re %s to %s",
				hh.ident, hh.title, sp->s_name);
			fclose(ifp);
			return FALSE;
		}
		if (hh.nbuf[0] == '\0') {
			fprintf(stderr, "Article not subscribed to by %s\n", sp->s_name);
			fclose(ifp);
			return FALSE;
		}
		sprintf(TRANS, "%s/trXXXXXX", SPOOL);
	}

	if (notify) {
		char oldid[50];
		sprintf(hh.title, "ihave %s %s", hh.ident, FULLSYSNAME);
		sprintf(hh.nbuf, "to.%s.ctl", sp->s_name);
		strcpy(oldid, hh.ident);
		getident(&hh);
		log("tell %s about %s, notif. id %s",
			sp->s_name, oldid, hh.ident);
	}

	if (appfile) {
		if (firstbufname[0] == '\0') {
			extern char histline[];
			localize("junk");
			savehist(histline);
			xerror("No file name to xmit from");
		}
#ifdef IHCC
		sprintf(TRANS, "%s/%s/%s", logdir(HOME), BATCHDIR, sp->s_xmit);
		ofp = fopen(TRANS, "a");
#else
		ofp = fopen(sp->s_xmit, "a");
#endif
		if (ofp == NULL)
			xerror("Cannot append to %s", sp->s_xmit);
		fprintf(ofp, "%s\n", firstbufname);
		fclose(ofp);
		fclose(ifp);
		return TRUE;
	}
	else
#ifdef UXMIT
	if (useexist) {
		if (firstbufname[0] == '\0')
			xerror("No file name to xmit from");
		if (*sp->s_xmit == '\0')
			sprintf(bfr, UXMIT, sp->s_name, firstbufname);
		else
			sprintf(bfr, sp->s_xmit, firstbufname);
		fclose(ifp);
	} else
#endif
	if (history) {		/* (6/18/84 KSA) */
		extern char histline[];
		if (*sp->s_xmit == '\0')
			xerror("no xmit command with H flag");
		sprintf(bfr, sp->s_xmit, histline);
	} else
	{
		ofp = xfopen(mktemp(TRANS), "w");
		if (afmt)
			ohwrite(&hh, ofp);
		else
			hwrite(&hh, ofp);
		if (!notify)
			while ((c = getc(ifp)) != EOF)
				putc(c, ofp);
		fclose(ifp);
		fclose(ofp);
		if (*sp->s_xmit == '\0')
			sprintf(bfr, DFTXMIT, sp->s_name, TRANS);
		else
			sprintf(bfr, "(%s) < %s", sp->s_xmit, TRANS);
	}

	/* At this point, the command to be executed is in bfr. */
	if (noshell) {
		if (pid = fork())
			fwait(pid);
		else {
			close(0);
			open(TRANS, 0);
			if (*sp->s_xmit == '\0')
				ptr = bfr;
			else
				ptr = sp->s_xmit;
			for (pid = 0; pid < 19; pid++) {
				while (isspace(*ptr))
					*ptr++ = 0;
				argv[pid] = ptr;
				while (!isspace(*++ptr) && *ptr)
					;
				if (!*ptr)
					break;
			}
			argv[++pid] = 0;
			execv(argv[0], argv);
			xerror("Can't execv %s", argv[0]);
		}
	} else
		system(bfr);
	if (!appfile && !useexist && !history)	/* 6/18/84 KSA) */
		unlink(TRANS);
	fclose(ifp);
	return TRUE;
}

typedef struct {
	char *dptr;
	int dsize;
} datum;

/*
 * Return TRUE if we have seen this file before, else FALSE.
 */
history(hp)
struct hbuf *hp;
{
#ifdef DBM
	datum lhs, rhs;
	datum fetch();
#else !DBM
	register FILE *hfp;
#endif !DBM
	register char *p;
	char lcident[BUFLEN];

#ifdef DEBUG
	fprintf(stderr,"history(%s)\n", hp->ident);
#endif
	/*
	 * Make the article ID case insensitive.
	 */
	strcpy(lcident, hp->ident);
	p = lcident;
	while (*++p)
		if (isupper(*p))
			*p = tolower(*p);

	idlock(lcident);
#ifdef DBM
	dbminit(ARTFILE);
	lhs.dptr = lcident;
	lhs.dsize = strlen(lhs.dptr) + 1;
	rhs = fetch(lhs);
	if (rhs.dptr)
		return(TRUE);
#else
	hfp = xfopen(ARTFILE, "r");
	while (fgets(bfr, BUFLEN, hfp) != NULL) {
		p = index(bfr, '\t');
		if (p == NULL)
			p = index(bfr, '\n');
		if (p != NULL)	/* can happen if nulls in file */
			*p = 0;
		p = bfr;
		while (*++p)
			if (isupper(*p))
				*p = tolower(*p);
		
		if (strcmp(bfr, lcident)==0 ||
				hp->oident[0] && strcmp(bfr, hp->oident)==0) {
			fclose(hfp);
			idunlock();
#ifdef DEBUG
			fprintf(stderr,"history returns true\n");
#endif
			return(TRUE);
		}
	}
	fclose(hfp);
#endif
	addhist(hp->ident);
	addhist("\t");
#ifdef DEBUG
	fprintf(stderr,"history returns false\n");
#endif
	return(FALSE);
}

char histline[256];	/* Assumed initially zero */

addhist(msg)
char *msg;
{
	strcat(histline, msg);
}

savehist(hline)
char *hline;
{
	register FILE *hfp;
	datum lhs, rhs;
	long fpos;
	register char *p;

	hfp = xfopen(ARTFILE, "a");
	fpos = ftell(hfp);
	fprintf(hfp, "%s\n", hline);
	fclose(hfp);
#ifdef DBM
	/* We assume that history has already been called, calling dbminit. */
	p = index(hline, '\t');
	if (p)
		*p = 0;
	p = hline;
	while(*++p)
		if(isupper(*p))
			*p = tolower(*p);
	lhs.dptr = hline;
	lhs.dsize = strlen(lhs.dptr) + 1;
	rhs.dptr = (char *) &fpos;
	rhs.dsize = sizeof fpos;
	store(lhs, rhs);
#endif
	hline[0] = 0;
	idunlock();
}

/*
 * Save partial news.
 */
newssave(fd, dummy)
FILE *fd;
char *dummy;
{
	register FILE *tofd, *fromfd;
	char sfname[BUFLEN];
	register int c;
	time_t tim;

#ifdef lint
	c = *dummy;
#endif lint

	if (fd == NULL)
		fromfd = xfopen(INFILE, "r");
	else
		fromfd = fd;
	umask(savmask);
	setgid(gid);
	setuid(uid);

	sprintf(sfname, "%s/%s", userhome, PARTIAL);
	if ((tofd = fopen(sfname, "a")) == NULL)
		xerror("Cannot save partial news in %s", sfname);
	time(&tim);
	fprintf(tofd, "----- News saved at %s\n", arpadate(&tim));
	while ((c = getc(fromfd)) != EOF)
		putc(c, tofd);
	fclose(fromfd);
	fclose(tofd);
	printf("News saved in %s\n", sfname);
	xxit(0);
}

/*
 * Handle dates in header.
 */

dates(hp)
struct hbuf *hp;
{
	time_t edt;

	time(&hp->rectime);
	strcpy(hp->recdate, arpadate(&hp->rectime));
	nstrip(hp->recdate);
	if (*hp->subdate) {
		if (cgtdate(hp->subdate) < 0) {
			xerror("Cannot parse submittal date '%s'", hp->subdate);
		}
	} else
		strcpy(hp->subdate, hp->recdate);
	if (*hp->expdate) {
		if ((edt = cgtdate(hp->expdate)) < 0)
			xerror("Cannot parse expiration date '%s'",hp->expdate);
		nstrip(strcpy(hp->expdate, arpadate(&edt)));
	} else {
		defexp = TRUE;
		/*
		 * Default is now applied in expire.c
		hp->exptime = hp->rectime + DFLTEXP;
		nstrip(strcpy(hp->expdate, arpadate(&hp->exptime)));
		*/
	}
}

char lockname[80];
idlock(str)
char *str;
{
	register int i;
	char tempname[80];
	time_t now;
	struct stat sbuf;
#ifdef	VMS
	int fd;

	sprintf(lockname, "/tmp/%s.l.1", str);
	if ((fd = creat(lockname, 0444)) < 0) {
#else	!VMS
	sprintf(tempname, "/tmp/LTMP.%d", getpid());
	sprintf(lockname, "/tmp/L%s", str);
#ifdef FOURTEENMAX
	lockname[5 /* /tmp/ */ + 14] = '\0';
#endif
	close(creat(tempname, 0666));
	while (link(tempname, lockname)) {
#endif	!VMS
		(void) time(&now);
		i = stat(lockname, &sbuf);
		if (i < 0) {
			xerror("Directory permission problem in /tmp");
		}
		if (sbuf.st_mtime + 10*60 < now) {
			unlink(lockname);
			logerr("Article %s locked up", str);
			continue;
		}
		log("waiting on lock for %s", lockname);
		sleep((unsigned)60);
	}
#ifdef VMS
	close(fd);
#else
	unlink(tempname);
#endif
	unlink(tempname);
}

idunlock()
{
	unlink(lockname);
}

/*
 * Put a unique name into header.ident.
 */
getident(hp)
struct hbuf *hp;
{
	long seqn;
	register FILE *fp;

	lock();
	fp = xfopen(SEQFILE, "r");
	fgets(bfr, BUFLEN, fp);
	fclose(fp);
	seqn = atol(bfr) + 1;
#ifdef	VMS
	unlink(SEQFILE);
#endif	VMS
	fp = xfopen(SEQFILE, "w");
	fprintf(fp, "%ld\n", seqn);
	fclose(fp);
	unlock();
	sprintf(hp->ident, "<%ld@%s%s>", seqn, FULLSYSNAME, MYDOMAIN);
}

/*
 * Check that header.nbuf contains only valid newsgroup names;
 * exit with error if not valid.
 *
 */

ngfcheck(isproc)
int isproc;
{
	register char *s1, *s2;
	register FILE *f;
	register char *os1;
	int ngroups = 1;
	char tbuf[BUFLEN], ngcheck[AFSIZ];

	f = xfopen(ACTIVE, "r");
	s1 = ngcheck;
	while (fgets(bfr, BUFLEN, f) != NULL) {
		os1 = s1;
		for(s2 = bfr; *s2 != '\0' && *s2 != ' ';) {
			if (s1 >= &ngcheck[AFSIZ-2])
				xerror("ACTIVE file too long");
			*s1++ = *s2++;
		}
		*s1++ = '\0';
		if (isproc) /* don't check to see if can post to this group */
			continue;	
		while (*s2++ != '\0' && *s2 != ' ')
			;	/* skip max article number */
		while (*s2++ != '\0' && *s2 != ' ')
			;	/* skip min article number */
		if (*s2++ != '\0' && *s2 == 'n')
			s1 = os1;	/* can't post to this group */
	}
	*s1++ = '\0';
	*s1 = '\0';
	fclose(f);
		
	s1 = header.nbuf;
	s2 = nbuf;
	while (*s1 == NGDELIM || *s1 == ' ')
		s1++;	/* skip leading junk */
	do {
		/* there shouldn't be blanks, but give the jerk a break */
		if (*s1 == NGDELIM || *s1 == ' ') {
			*s2++ = '\0';
			ngroups++;
			while (*++s1 == NGDELIM || *s1 == ' ')
				;	/* remove extra commas */
		} else
			*s2++ = *s1++;
	} while (*s1 != '\0');
	*s2 = '\0';

	s1 = nbuf;
	while (*s1 != '\0') {	/* for each newsgroup in header */
		s2 = ngcheck;
		while (*s2 != '\0') { /* for each newsgroup in active file */
			if (strcmp(s1, s2) == 0)
				break;
			while (*s2++ != '\0')
				;
		}
		if (*s2 == '\0') {	/* not found. remove it */
			if (!isproc) {
				logerr("Invalid news group '%s'", s1);
				newssave(stdin, (char *)NULL);
			}
			/* See if it's in our alias list */
			f = xfopen(ALIASES,"r");
			while (fscanf(f,"%s %s", tbuf, bfr) == 2
				&& strcmp(s1, tbuf))
				;
			fclose(f);
			if (strcmp(s1, tbuf) == 0) {
				logerr("Aliased newsgroup '%s' to '%s'", s1, bfr);
				os1 = s1;
				s1 = nbuf;
				s2 = tbuf;
				while (s1 < os1) /* copy left part */
					*s2++ = *s1++;
				s1 = bfr;
				while (*s1 != '\0') /* copy alias */
					*s2++ = *s1++;
				*s2++ = '\0';
				s1 = os1;
				os1 = nbuf + (s2 - tbuf);
				while (*s1++ != '\0') /* skip old group */
					;
				/* copy right part */
				tbufcpy(s2, s1);
				/* copy back to original buffer */
				tbufcpy(nbuf, tbuf);
				s1 = os1;
			} else {
				logerr("Unknown newsgroup '%s' removed", s1);
				s2 = s1;
				while (*s2++ != '\0')	/* skip the bad one */
					;
				tbufcpy(s1, s2);
			}
		} else { /* It's in our active file */
			os1 = s1;
			while (*s1++ != '\0')
				;
			/* check for local only distribution on incoming
			   newsgroups. This might occur if someone posted to
			   general,net.unix */
			if(isproc && ngroups > 1 && index(os1, '.') == NULL) {
				logerr("Local group '%s' removed", os1);
				tbufcpy(os1, s1);
				s1 = os1;
			}
		}
	}
	/*  remove any duplicates */
	os1 = s1 = nbuf;
	for(;;) {
		if (*s1++ == '\0') {
			if (*s1 == '\0')
				break;
			s2 = s1;
			while (*s2 != '\0') {
				if (strcmp(os1, s2) == 0) {
					logerr("Duplicate '%s' removed",os1);
					os1 = s2;
					while (*s2++ != '\0') /* skip it */
						;
					tbufcpy(os1, s2);
				} else
					while (*s2++ != '\0')
						;
			}
			os1 = s1;
			s1[-1] = '\0';
		}
	}
	if (nbuf[0] != '\0') {
		s1 = header.nbuf;
		s2 = nbuf;
		do {
			while (*s2 != '\0')
				*s1++ = *s2++;
			*s1++ = NGDELIM;
		} while (*++s2 != '\0');
		*s1 = '\0';
		return FALSE;
	}
	return TRUE;
}

tbufcpy(s1, s2)
register char *s1, *s2;
{
	do {
		while (*s2 != '\0')
			*s1++ = *s2++;
		*s1++ = '\0';
	} while (*++s2 != '\0');
	*s1 = '\0';
}


/*
 * Figure out who posted the article (which is locally entered).
 * The results are placed in the header structure hp.
 */
gensender(hp, logname)
struct hbuf *hp;
char *logname;
{
	register char *fn, *p;
	char buf[BUFLEN];
	char *fullname(), *getenv();
	int fd;

	fn = getenv("NAME");

	if (fn == NULL) {
		sprintf(buf, "%s/%s", userhome, ".name");
		fd = open(buf, 0);
		if (fd >= 0) {
			read(fd, buf, sizeof buf);
			close(fd);
			if (buf[0] >= 'A') {
				fn = buf;
				for (p=fn; *p; p++)
					if (*p < ' ')
						*p = '\0';
			}
		}
	}

	if (fn == NULL)
		fn = fullname(logname);

	sprintf(hp->path, "%s", logname);
	sprintf(hp->from, "%s@%s%s (%s)", logname, FULLSYSNAME, MYDOMAIN, fn);
}

/*
 * Trap interrupts.
 */
onsig(n)
int n;
{
	static int numsigs = 0;
	/*
	 * Most UNIX systems reset caught signals to SIG_DFL.
	 * This bad design requires that the trap be set again here.
	 * Unfortunately, if the signal recurs before the trap is set,
	 * the program will die, possibly leaving the lock in place.
	 */
	if (++numsigs > 100) {
		logerr("inews ran away looping on signal %d", n);
		xxit(1);
	}
	signal(n, onsig);
	sigtrap = n;
}

#ifdef BATCH
/*
 * If the stdin begins with "#", we assume we have been fed a batched
 * shell script which looks like this:
 *	#! rnews 1234
 *	article with 1234 chars
 *	#! rnews 4321
 *	article with 4321 chars
 *
 * In this case we just exec the unbatcher and let it unpack and call us back.
 *
 * Note that there is a potential security hole here.  If the batcher is
 * /bin/sh, someone could ship you arbitrary stuff to run as shell commands.
 * The main protection you have is that the effective uid will be news, not
 * uucp and not the super user.  (That, plus the fact that BATCH is set to
 * "unbatch" as the system is distributed.)  If you want to run a batched link
 * and you are security concious, do not use /bin/sh as the unbatcher.
 * the thing to do is to change BATCH in your localize.sh file from /bin/sh
 * to some restricted shell which can only run rnews.
 */
checkbatch()
{
	int c;

	c = getc(stdin);
	if (c != EOF)
		ungetc(c, stdin);
	clearerr(stdin);
	if (c == '#') {
		char unbatcher[BUFLEN];

		sprintf(unbatcher, "%s/%s", LIB, BATCH);
		reset_stdin();
		execl(unbatcher, "news-unpack", (char *)0);
		xerror("Unable to exec shell to unpack news.");
	}
}

/*
 * We've already done a read on stdin, and we want to seek back to the
 * beginning.  We want the real file descriptor (beyond buffers) to
 * reflect the true beginning.  Do whatever is necessary.
 */
reset_stdin()
{
	register FILE *ofd;
	register int c;
	char *ofdname;
	long lseek();

	/* First try to seek back - if so, it's a cheap way back. */
	if (lseek(0, 0L, 0) == 0L)
		return;

	/* Can't seek, so have to copy input to a file and use that. */
	ofdname = "/tmp/inewsXXXXX";
	mktemp(ofdname);
	ofd = fopen(ofdname, "w");
	while ((c=getc(stdin)) != EOF)
		putc(c, ofd);
	fclose(stdin);
	fclose(ofd);

	/* Now for a few lower level hacks to reopen stdin and make
	 * absolutely sure that the right fd's are done for the exec.
	 */
	(void) close(0);		/* make sure stdin is really closed. */
	(void) open(ofdname, 0);	/* should return zero */
	(void) unlink(ofdname);		/* to avoid cleaning it up later. */
}
#endif BATCH

/*
 *	Exit and cleanup.
 */
xxit(status)
int status;
{
	unlink(INFILE);
	unlink(ARTICLE);
	while (lockcount > 0)
		unlock();
	idunlock();
	exit(status);
}

rwaccess(fname)
char *fname;
{
	int fd;

	fd = open(fname, 2);
	if (fd < 0)
		return 0;
	close(fd);
	return 1;
}

exists(fname)
char *fname;
{
	int fd;

	fd = open(fname, 0);
	if (fd < 0)
		return 0;
	close(fd);
	return 1;
}

int	lockcount = 0;			/* no. of times we've called lock */

#ifdef	VMS

#define	SUBLOCK	"/tmp/netnews.lck.1"

/*
 * Newsystem locking.
 * These routines are different for VMS because we can not
 * effectively simulate links, and VMS supports multiple
 * version numbers of files
 */
lock()
{
	register int i;
	register int fd;

	if (lockcount++ == 0) {
		i = DEADTIME;
		while ((fd = creat(SUBLOCK, 0444)) < 0) {
			if (--i < 0) {
				unlink(SUBLOCK);
				fprintf(stderr,"%s: %s\n", Progname, msg);
				logerr("News system locked up");
			}
			if (i < -3)
				xerror("Unable to unlock news system");
			sleep((unsigned)1);
		}
		close(fd);
	}
}

unlock()
{
	if (--lockcount == 0)
		unlink(SUBLOCK);
}

#else	!VMS

/*
 * Newsystem locking.
 */

lock()
{
	register int i;
	extern int errno;

	if (lockcount++ == 0) {
		i = DEADTIME;
		while (link(SUBFILE, LOCKFILE)) {
			if (errno != EEXIST)
				break;
			if (--i < 0)
				xerror("News system locked up");
			sleep((unsigned)1);
		}
	}
}

unlock()
{
	if (--lockcount == 0)
		unlink(LOCKFILE);
}
#endif	VMS

/*
 * Generate the name of the person responsible for posting this article,
 * in order to check that two articles were posted by the same person.
 */
char *
senderof(hp)
struct hbuf *hp;
{
	char *q, *tp;
	char *tailpath();

	if (hp->sender[0])
		tp = hp->sender;
	else if (hp->from[0])
		tp = hp->from;
	else
		tp = tailpath(hp);

	/* Remove full name */
	q = index(tp, ' ');
	if (q)
		*q = '\0';

	q = malloc((unsigned)(strlen(tp) + 1));
	strcpy(q, tp);
	return q;
}
!E!O!F!

cat > lib/addrc.c <<\!E!O!F!
/*
 * Add a newsgroup onto the end of .newsrc.
 */

#include <stdio.h>
#include "defs.h"
#include "newsrc.h"


addrc(ngp)
	struct ngentry *ngp;
	{
	if (lastng != NULL) {
		lastng->ng_next = ngp;
		lastng = ngp;
	} else {
		lastng = firstng = ngp;
	}
}
!E!O!F!

cat > lib/af.h <<\!E!O!F!
#include "artfile.h"
#include "stroff.h"

/* length of section of artrec that can be written directly to artfile */
#define A_WRTLEN (int)(stroff(a_nkwords, artrec) + 1)

/* offsets */
#define PARENTOFF	(stroff(a_parent, artrec) + 1)
#define CHILDRENOFF	(stroff(a_children, artrec) + 1)
#define CHILDCHOFF	(stroff(a_childchain, artrec) + 1)
#define GROUPOFF	(stroff(a_group[0], artrec) + 1)


#define A_PREFIX 0210		/* this byte precedes each article record */
#define AF_MAGIC 0431		/* magic number to indicate artfile */
#define AF_VERSION 1		/* version number */


struct afheader {
      short af_magic;		/* magic number (0431) */
      short af_version;		/* version number */
      DPTR  af_idtab;		/* offset of message id hash table */
      DPTR  af_nglist;		/* pointer to newsgroup chain table */
      DPTR  af_titletab;	/* title hash table */
      DPTR  af_records;		/* start of article records */
      DPTR  af_free;		/* add next record here */
      short af_idtlen;		/* # elements in message id hash table */
      short af_maxng;		/* # elements in newsgroup chain table */
      short af_ttlen;		/* # elements in title hash table */
};


extern struct afheader afhd ;
extern int affd ;

#if BUFSIZ == 1024
#define BSIZE 1024
#else
#define BSIZE 512
#endif


DPTR readptr() ;

#define ngchain(ngnum)	(afhd.af_nglist + ngnum * (int)sizeof(DPTR))
#define hashid(msgid)	(afhd.af_idtab + hash(msgid, afhd.af_idtlen) * (int)sizeof(DPTR))

#define equal(s1, s2)	(strcmp(s1, s2) == 0)
!E!O!F!

cat > lib/aflock.c <<\!E!O!F!
/*
 * Lock and unlock the article data base.
 */

#include <stdio.h>
#include <errno.h>
#include "af.h"
#include <sys/types.h>
#include <sys/stat.h>
#include "defs.h"
#include "libextern.h"


static int islocked ;


/*
 * Lock the data base.  The data base is only locked to avoid multiple
 * writers, so the data base may be read and written simultaneously.
 */

aflock() {
      char lockfile[FPATHLEN] ;
      int ntries ;
      struct stat st ;
      int fd ;
      char pid[10] ;
      long time() ;

      sprintf(lockfile, "%s/artfile.lck", LIB) ;
      ntries = 0 ;
      while (islocked++, (fd = creat(lockfile, 0444)) < 0) {
            islocked-- ;
            if (++ntries == 1 || ntries == 31) {
                  if (stat(lockfile, &st) >= 0 && st.st_mtime < time((long *)0) - 60) {
                        unlink(lockfile) ;
                        continue ;
                  }
            } else if (ntries > 60) {
                  xerror("Can't lock data base") ;
            }
            sleep(2) ;
      }
      sprintf(pid, "%d\n", getpid()) ;
      write(fd, pid, strlen(pid)) ;
      close(fd) ;
#ifdef notdef /* forget about stdio due to BSD bug */
      fseek(affp, 0L, 0) ;
      fread((char *)&afhd, sizeof(afhd), 1, affp) ;
#else
      lseek(affd, 0L, 0) ;
      read(affd, (char *)&afhd, sizeof afhd) ;
#endif
}


afunlock() {
      char lockfile[FPATHLEN] ;

      if (islocked) {
            sprintf(lockfile, "%s/artfile.lck", LIB) ;
            unlink(lockfile) ;
            islocked-- ;
      }
}
!E!O!F!

cat > lib/afopen.c <<\!E!O!F!
#include "defs.h"
#include "libextern.h"


afopen() {
      char fname[FPATHLEN] ;

      sprintf(fname, "%s/artfile", LIB) ;
      genafopen(fname, "r") ;
}
!E!O!F!

cat > lib/allgroups.c <<\!E!O!F!
#include <stdio.h>
#include "config.h"
#include "ng.h"

char *index() ;


nginit() {
      int c ;

      fseek(ngfp, 0L, 0) ;
      while ((c = getc(ngfp)) != '\n' && c != EOF) ;
}


ngread(g)
      register struct ngrec *g ;
      {
      char line[100] ;
      register char *p ;

      if (fgets(line, 100, ngfp) == NULL)
            return 0 ;
      if ((p = index(line, ' ')) == NULL)
            xerror("corrupted newsgroup file") ;
      *p++ = '\0' ;
      scopyn(line, g->g_name, sizeof(g->g_name)) ;
      g->g_num = atoi(p) ;
      g->g_flags = 0 ;
      if (index(p, 'm'))
            g->g_flags |= G_MOD ;
      return 1 ;
}
!E!O!F!

cat > lib/artfile.h <<\!E!O!F!
/*
 * Defines for the article data base file
 */

/* flags in artrec structure */
#define A_DUMMY 01		/* placeholder; article not yet arrived */
#define A_EXPIRED 02		/* article has been expired */
#define A_CANCELLED 04		/* article has been cancelled */
#define A_NOFILE 07		/* no file for this article */


typedef long DPTR ;		/* file address */
typedef int ARTNO ;		/* article number */
#define DNULL 0L


#define MAXNG	5		/* max # newsgroups per article */
#define A_MAXKW	6		/* max # keywords on an article */
#define A_SPACE 576		/* space for storing strings */


struct artgroup {
      short a_ngnum;		/* number of this newsgroup */
      long  a_artno;		/* number of article within newsgroup */
      DPTR  a_ngchain;		/* pointer to previous article in newsgroup */
};


struct artrec {
      long  a_subtime;		/* when article was posted */
      long  a_rectime;		/* when article was received */
      long  a_exptime;		/* when this article expires (0 if not specified) */
      DPTR  a_parent;		/* article this is a followup to */
      DPTR  a_children;		/* linked list of followups */
      DPTR  a_childchain;	/* link for followup chain */
      DPTR  a_idchain;		/* chain for article id hash */
      DPTR  a_titlechain;	/* title hash chain */
      short a_flags;		/* various flags */
      short a_nlines;		/* length of article */
      char  a_ngroups;		/* number of newsgroups this article posted to */
      char  a_nkwords;		/* number of keywords on article */
      struct artgroup a_group[MAXNG];	/* list of groups article posted to */
      char *a_ident;		/* message id */
      char *a_title;		/* article subject line */
      char *a_from;		/* author of article */
      char *a_file;		/* file containing article */
      char *a_kword[A_MAXKW];	/* keywords */
      char  a_space[A_SPACE];	/* space to store strings */
};


extern DPTR nglnext ;

DPTR nglfirst() ;
ARTNO ngltest() ;
DPTR lookart() ;


#define BKWD_GROUP(ngnum, artno, dp, a)	for (dp = nglfirst(ngnum) ; (artno = ngltest(ngnum, &(a))) >= 0 ; dp = nglnext)

#define ainit(a)
#define afree(a)
!E!O!F!

cat > lib/arthead.h <<\!E!O!F!
/*
 * header.h - Article header format
 */

#define NUNREC 50
#define H_SPACE 1024
#define NHDNAME 24

/* article header */
struct	arthead {
	char	*h_relayversion;	/* Relay-Version:	*/
	char	*h_postversion;		/* Posting-Version:	*/
	char	*h_path;		/* Path:		*/
	char	*h_from;		/* From:		*/
	char	*h_nbuf;		/* Newsgroups:		*/
	char	*h_title;		/* Subject:		*/
	char	*h_ident;		/* Message-ID:		*/
	char	*h_subdate;		/* Date: (submission)	*/
	char	*h_oident;		/* Article-I.D.:	*/
	char	*h_postdate;		/* Posted:		*/
	char	*h_recdate;		/* Date-Received:	*/
	char	*h_expdate;		/* Expires:		*/
	char	*h_references;		/* References:		*/
	char	*h_ctlmsg;		/* Control:		*/
	char	*h_sender;		/* Sender:		*/
	char	*h_replyto;		/* Reply-To:		*/
	char	*h_followto;		/* Followup-To:		*/
	char	*h_distribution;	/* Distribution:	*/
	char	*h_organization;	/* Organization:	*/
	char	*h_numlines;		/* Lines:		*/
	char	*h_keywords;		/* Keywords:		*/
	char	*h_approved;		/* Approved:		*/
	char	*h_summary;		/* Summary:		*/
	char	*h_priority;		/* Priority:		*/
	char	*h_unrec[NUNREC];	/* unrecognized lines	*/
	time_t	h_subtime;		/* subdate in secs	*/
	time_t	h_rectime;		/* recdate in secs	*/
	time_t	h_exptime;		/* expdate in secs	*/
	int	h_intnumlines;		/* Integer version	*/
	int	h_intpriority;		/* Integer version	*/
	char	*h_space[8];		/* string space		*/
};

#define hset(hdrline)	((hdrline) != NULL)

FILE *gethead();
!E!O!F!

cat > lib/bcopy.c <<\!E!O!F!
#include "config.h"

#if BSDREL < 42

bcopy(from, to, n)
      register char *from, *to ;
      {
      register int i ;

      if ((i = n) != 0) {
            do *to++ = *from++ ;
            while (--i != 0) ;
      }
}

#endif
!E!O!F!

cat > lib/bcopy.pdp <<\!E!O!F!
/ BCOPY(FROM, TO, N)  CHAR *FROM, *TO ;
/
/	Copy "n" bytes from "from" to "to".
/
.globl	_bcopy
.globl	csav, cret

.text
_bcopy:	jsr  r0, csav		/ save registers
	mov  r5, r4		/ get arguments...
	cmp  (r4)+, (r4)+	/
	mov  (r4)+, r0		/ from
	mov  (r4)+, r1		/ to
	mov  (r4), r2		/ byte count
	beq  ret		/ if zero then return
	bit  $1, r1		/ is "to" odd
	beq  1f			/ yes...
	  movb (r0)+, (r1)+	/   copy one byte to make it even
	  dec  r2		/   and adjust byte count
1:	mov  r2, r3		/ save (low bit of) count
	asr  r2			/ convert from bytes to words
	beq  3f			/ if any words to copy...
	  bit  $1, r0		/   is "from" even now?
	  beq  2f		/   if not ...
1:	    movb (r0)+, (r1)+	/     copy bytes...
	    movb (r0)+, (r1)+	/     ...
	    sob  r2, 1b		/     ...
	  br   3f		/   else ...
2:	    mov  (r0)+, (r1)+	/     copy words...
	    sob  r2, 2b		/     ...
3:	ror  r3			/ is byte count odd?
	bcc  ret		/ if so...
	   movb (r0)+, (r1)+	/   copy odd byte.
ret:	jmp  cret		/ return
!E!O!F!

cat > lib/bcopy.u3b <<\!E!O!F!
	.file "bcopy.s"		# silly assembler wants this line
	.globl	bcopy
	.align	4
bcopy:	save	&0		# set up stack frame
	movw	0(%ap), %r0	# get source
	movw	4(%ap), %r1	# and destination
	movw	8(%ap), %r2	# get count
	movblb			# this instruction does it all
	ret	&0		# return
!E!O!F!

cat > lib/bcopy.vax <<\!E!O!F!
# bcopy (from, to, count) char *from, *to; int count;
#
# Copy "count" bytes from "from" to "to"; not guaranteed to
# work if "from" and "to" overlap.

	.align	2
	.globl	_bcopy
_bcopy:
	.word	0
	movl	4(ap), r1		# r1 = from
	movl	8(ap), r3		# r3 = to
L1:
	movzwl	$65535, r0		# while more than 65535 bytes to move
	cmpl	12(ap), r0
	jleq	L2			# if <= 65535, break
	subl2	r0, 12(ap)		# count-=65535 (bytes moved this time)
	movc3	r0, (r1), (r3)		# r1, r3 magically point to next 65K
	brb	L1
L2:
	movc3	12(ap), (r1), (r3)	# move up to 65535 bytes
	ret
!E!O!F!

cat > lib/bfr.c <<\!E!O!F!
#include "defs.h"

char bfr[LBUFLEN];	/* general purpose buffer */
!E!O!F!

cat > lib/bzero.c <<\!E!O!F!
/*
 * Set nc bytes, starting at cp, to zero.
 */

#include "config.h"

#if BSDREL < 42

bzero(cp, nc)
register char	*cp;
register int	nc;
{
	while (--nc >= 0)
		*cp++ = 0;
}

#endif
!E!O!F!

cat > lib/bzero.pdp <<\!E!O!F!
/ BZERO(MEM, N)  CHAR *MEM;
/
/	Clear "n" bytes of memory starting at "mem".
/
.globl	_bzero
.globl	csav, cret

.text
_bzero:	jsr	r0, csav	/ save registers
	mov	4(r5), r0	/ get memory pointer...
	mov	6(r5), r1	/ ...and count
	beq	ret		/ return if zero
	bit	$1, r0		/ is address odd?
	beq	1f		/ yes...
	  clrb	  (r0)+		/   clear one byte to make it even
	  dec	  r1		/   and adjust byte count
1:	mov	r1, r3		/ save (low bit of) count
	clr	r2		/ get zero into a register and clear carry
	ror	r1		/ convert count to words.
	asr	r1		/ convert count to double words
	bcc	1f		/ if word count was odd
	  mov	  r2, (r0)+	/   clear an extra word
	  tst	  r1		/   reset condition codes on r1
1:	beq	3f		/ while r1 ~= 0
2:	  mov	  r2, (r0)+	/   clear a word...
	  mov	  r2, (r0)+	/
	  sob	  r1, 2b	/   and update count
3:	asr	r3		/ if count was odd
	bcc	ret		/
	  movb	  r2, (r0)+	/   clear an extra byte
ret:	jmp	cret		/ return
!E!O!F!

cat > lib/bzero.u3b <<\!E!O!F!
	.file	"bzero.u3b"	# assembler wants this

# This code relies on the fact that bzero(mem, n) is equivalent to
# strncpy("", mem, n).

	.globl	bzero
	.align	4
bzero:	save	&1		# set up stack frame and save r8
	movw	0(%ap), %r0	# get address
	movw	4(%ap), %r1	# and count
	movw	&0, %r2		# various parameters are zero
	movaw	zero, %r8	# null string
	movccep	%r8,%r1,%r0,%r2,%r2	# strncpy insruction
	ret	&1		# return

	.data
zero:	.byte	0
!E!O!F!

cat > lib/cancel.c <<\!E!O!F!
/*
 * Cancel the article whose header is in hp, by posting a control message
 * to cancel it.  The scope of the control message depends on who would
 * really be willing to cancel it.  It is sent as far as it will do any good.
 * notauthor is true iff the person posting this article is not the
 * real author of the article being cancelled.
 */

#include <stdio.h>
#include <sys/types.h>
#include "config.h"
#include "arthead.h"
#include "libextern.h"

cancel(ofp, hp, notauthor)
FILE *ofp;
struct arthead *hp;
int	notauthor;
{
	FILE	*inews;
	char	distgroup[64];
	int	pid;
	char	bfr[512];
	char	*index();
	FILE	*popen();

	/* fflush(stdout); */
	pid = fork();
	if (pid > 0)
		return 0;
	strcpy(distgroup, hp->h_nbuf);
	if (notauthor)
		sprintf(distgroup, "to.%s", FULLSYSNAME);
	else
		sprintf(distgroup, "%s", hp->h_nbuf);
	sprintf(bfr, "%s -t 'cmsg cancel %s' -n %s < /dev/null",
	    XINEWS, hp->h_ident, distgroup);
	if ((inews = popen(bfr, "w")) == NULL)
		fprintf(ofp, "Can't fork %s\n", XINEWS);
	else
		pclose(inews);
	if (pid == 0)
		_exit(0);
	return 0;
}
!E!O!F!

cat > lib/cgtdate.c <<\!E!O!F!
#include <stdio.h>
#include <sys/types.h>
#include "config.h"
#ifndef BSDREL >= 7
#include <sys/timeb.h>
#else
struct timeb
{
	time_t	time;
	unsigned short millitm;
	short	timezone;
	short	dstflag;
};
#endif

time_t getdate();
long getadate();


time_t
cgtdate(datestr)
char *datestr;
{
	time_t	i;
	char	junk[40],month[40],day[30],time[60],year[50];
	char	bfr[181];

	if ((i = getadate(datestr)) != -1L)
		return i;
	if ((i = getdate(datestr, (struct timeb *) NULL)) >= 0)
		return i;
	sscanf(datestr, "%s %s %s %s %s", junk, month, day, time, year);
	sprintf(bfr, "%s %s, %s %s", month, day, year, time);
	return getdate(bfr, (struct timeb *) NULL);
}
!E!O!F!

cat > lib/cgtdatecmd.c <<\!E!O!F!
/*
 * This program takes a date as its argument and writes the UN*X internal
 * value on the standard output.  The primary purpose of this program is
 * to make it unnecessary to load the cgtdate routine as part of readnews
 * and/or vnews.
 */

#include <stdio.h>

main(argc, argv)
      char **argv ;
      {
      char buf[1024] ;
      register int i ;
      long tim ;
      long cgtdate() ;

      buf[0] = '\0' ;
      for (i = 1 ; i < argc ; i++) {
            strcat(buf, " ") ;
            strcat(buf, argv[i]) ;
      }
      tim = cgtdate(buf) ;
      if (tim == -1L)
            return 1 ;
      printf("%ld\n", tim) ;
      return 0 ;
}
!E!O!F!

cat > lib/ckfopen.c <<\!E!O!F!
/*
 * Open file, calling xerror on failure.
 */

#include <stdio.h>
#include "config.h"

FILE *
ckfopen(name, mode)
register char *name, *mode;
{
	register FILE *fp;
	extern int errno;

	if ((fp = fopen(name, mode)) == NULL) {
#ifdef IHCC
		char	*fname, *rindex();
		/*
		 * IHCC users only see the "filename" that was in trouble,
		 * not the whole path.  (for security!)
		 */
		if ((fname = rindex(name, '/') != NULL)
			fname++;
		else
			fname = name;
		xerror("Cannot open %s (%s), errno=%d", fname, mode, errno);
#else
		xerror("Cannot open %s (%s), errno=%d", name, mode, errno);
#endif
	}
#ifdef notdef /* this doesn't make sense for readnews */
	/* kludge for setuid not being honored for root */
	if ((uid == 0) && (duid != 0) && ((mode == "a") || (mode == "w")))
		chown(name, duid, dgid);
#endif
	return(fp);
}
!E!O!F!

cat > lib/ckmalloc.c <<\!E!O!F!
char *
ckmalloc(nbytes)
      unsigned nbytes ;
      {
      register char *p ;
      char *malloc() ;

      if ((p = malloc(nbytes)) == (char *)0)
            xerror("out of space") ;
      return p ;
}
!E!O!F!

cat > lib/config.hou3c <<\!E!O!F!
# This is the config file I use on hou3c.

newsusr netnews
newsgrp other
myorg	AT&T Bell Labs, Holmdel
mydomain .UUCP
path	/bin:/usr/bin:/usr/lbin
spool	/tools/netnews/spool
admin ka
notify
admsub
!E!O!F!

cat > lib/config.sample <<\!E!O!F!
#This is a sample config file (with more comments than a real config file
#would contain, of course).  First, we list all the possible entries.
#
#Lines in the setup file consist of a name followed by a value.  The various
#types of values are string, indicated by "str", integer, indicated by "int",
#and boolean, indicated by "bool".  The default value, if any, is indicated
#by DFT.  In certain cases, there is a default which is assigned when a null
#value is given for the value.  If the value is boolean, the default is "yes",
#other defaults for null values are indicated by NDFT.
#
#
#NAME	   TYPE	DESCRTIPTION
#admin	   str	Netnews administrator.  (DFT is your login name)
#admsub	   str	Mandatory subscription list.  DFT general,all.announce
#bin	   str	Directory where programs (e. g. inews) placed.  DFT /usr/bin
#buflen	   int	Length of various bufs.  DFT 128 if small, else 256
#compadmin bool	Recompute the numeric admin id each time program run.  DFT no
#datelen   int	Max length of date.  DFT 48
#dfteditor str	Default editor program.  DFT /bin/ed
#dftexp    int	Default expiration time in days.  DFT 14
#dftsub	   str	Default subscription list.  DFT all
#fpathlen  int	Max length of file path name.  DFT 64
#ghname	   bool	True if system has gethostname call.  DFT yes if 4.2bsd.
#home	   str	Lib and spool are relative to this user's home directory.
#internet  bool True if mailer understands internet addresses.  DFT yes
#lbuflen   int	Length of large buffer.  DFT 1024
#lib	   str	Directory for misc netnews files.  DFT /usr/lib/news
#mailer	   str	Mail program.  DFT /bin/mail
#manually  bool	Require that admin remove groups manually.  DFT yes
#maxgroups int	Maximum number of newsgroups.
#mydomain  str	Appended to sys name to get domain name.  This entry mandatory.
#myname	   str	System name.  Computed at run time if omitted.
#myorg	   str	Organization name.  This entry mandatory.
#namelen   int	Max length of internet address or messge ID.  DFT 64
#newsgrp   str	Group of news software.  This entry is mandatory.
#newsrc	   str	Name of newsrc file.  DFT .newsrc
#newsusr   str	Owner of news software.  This entry is mandatory.
#notify	   str	Inform person about certain control messages.  NDFT admin
#page	   str	Pager program.  NDFT /usr/ucb/more
#path	   str	Path use by setup to find various programs.  DFT $PATH
#pathlen   int	Max length of return path.  DFT 512
#small	   bool	True if 16 bit address space.  DFT yes if on pdp11.
#spool	   str	Directory where articles stored.  DFT /usr/spool/news
#sys	   str	Operating system version.  (Guessed by default)
#termcap   str	Name of termcap library.  (Guessed by default)
#tmail	   str	Mailer that understands -t option.  NDFT /usr/lib/sendmail
#umask	   int	Umask value; use 022 for secure system.  DFT 000
#uname	   bool	True if system has uname system call.  DFT yes if USG system.
#v7mail	   bool	True if use Version 7 mail format.  DFT yes


# OK, now to give our config entries.
# First we give the required ones.

# The login and group owning the netnews software.
newsusr netnews
newsgrp other

# The name of my organization.
myorg	AT&T Bell Labs, Holmdel

# My domain.
mydomain .UUCP

# The remaining entries are not required by the setup program.

# Use a standard path for the rest of setup
path	/bin:/usr/bin:/usr/lbin

# We don't have room in the /usr file system for the spool directory,
# so we place it elsewhere.
spool /tools/netnews/spool

# Since I am running the setup program, this is unnecessary, but it is
# safer to include it in case somebody else recompiles the news software
# for some reason
admin ka

# I want to be told about create and rmgrp control messages.  Since I am
# the person running the config program, they will be sent to me by default.
notify

# Mandatory subscripts are fascist, so no mandatory subscriptions on hou3c.
admsub

# End of sample config file.
!E!O!F!

cat > lib/cpu.c <<\!E!O!F!
/* This file is run through cpp to determine the cpu type */
#ifdef pdp11
Xpdp11
#endif
#ifdef vax
Xvax
#endif
#ifdef u3b
Xu3b
#endif
!E!O!F!

cat > lib/defs.h <<\!E!O!F!
/*
 * defs.h - defines for news-related programs.
 *
 * By convention, the version of the software you are running is taken
 * to be news_version below.
 */

#define news_version "B 2.11-B 1/17/84"
/* SCCS ID @(#)defs.dist	2.23	5/3/83 */

#include "newsdefs.h"	/* this gets most of the definitions */

#define FOLLOWUP INEWS
#define NEWSRC	".newsrc"
/* #define BERKNAME "ARPAVAX"	/* name of local host on Berknet	*/

#define LINES	512	/* maximum no. of lines in .newsrc		*/
#define NEGCHAR	'!'	/* newsgroup negation character			*/
#define DEADTIME 45	/* no. of seconds to wait on deadlock		*/
#define FMETA	'%'	/* file meta-character for c option		*/
#define SYSPATH	"PATH=/local/bin:/bin:/usr/bin"	/* default, secure, vanilla path */
#define LNCNT	16	/* Articles with > LNCNT lines go through pager */

/* Things you probably won't want to change */
#define	SNLN	8	/* max significant characters in sysname	*/
#define	PROTO	'A'	/* old protocol name				*/
#define NETCHRS	"!:.@^%"/* Punct. chars used for various networks	*/
#define	TRUE	1	/* boolean true					*/
#define	FALSE	0	/* boolean false				*/
#define NGFSIZ  5000	/* legal newsgroup file size			*/
#define	NGDELIM	','	/* delimit character in news group line		*/
!E!O!F!

cat > lib/dir.c <<\!E!O!F!
#include "config.h"
#if BSDREL < 42
#include <sys/types.h>
#include <sys/param.h>
#include "ndir.h"

/*
 * open a directory.
 */
DIR *
opendir(name)
	char *name;
{
	register DIR *dirp;
	register int fd;
	char *malloc();

	if ((fd = open(name, 0)) == -1)
		return NULL;
	if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
		close (fd);
		return NULL;
	}
	dirp->dd_fd = fd;
	dirp->dd_nleft = 0;
	return dirp;
}



/*
 * close a directory.
 */
void
closedir(dirp)
	register DIR *dirp;
{
	close(dirp->dd_fd);
	free(dirp);
}



/*
 * read an old style directory entry and present it as a new one
 */
#define	ODIRSIZ	14

struct	olddirect {
	ino_t	od_ino;
	char	od_name[ODIRSIZ];
};

/*
 * get next entry in a directory.
 */
struct direct *
readdir(dirp)
	register DIR *dirp;
{
	register struct olddirect *dp;
	static struct direct dir;

	for (;;) {
		if ((dirp->dd_nleft -= sizeof(struct olddirect)) < 0) {
			dirp->dd_nleft = read(dirp->dd_fd, dirp->dd_buf, 
			    DIRBLKSIZ);
			if (dirp->dd_nleft <= 0)
				return NULL;
			dirp->dd_nextc = dirp->dd_buf;
		}
		dp = (struct olddirect *) dirp->dd_nextc;
		dirp->dd_nextc += sizeof(struct olddirect);
		if (dp->od_ino == 0)
			continue;
		dir.d_ino = dp->od_ino;
		strncpy(dir.d_name, dp->od_name, ODIRSIZ + 1);
		dir.d_namlen = strlen(dir.d_name);
		return (&dir);
	}
}
#endif
!E!O!F!

cat > lib/dirname.c <<\!E!O!F!
#include "defs.h"
#include "libextern.h"


char *
dirname(ngname, rbuf)
char *ngname;
char rbuf[FPATHLEN];
{
	register char *p;

	sprintf(rbuf, "%s/%s", SPOOL, ngname);
	for (p=rbuf+strlen(SPOOL); *p; p++)
		if (*p == '.')
			*p = '/';
	return rbuf;
}
!E!O!F!

cat > lib/doc.h <<\!E!O!F!
.na
Newsgroups may be refered to by name or my number.
The name is identical across all machines,
the number is specific to a particular machine.

Articles are refered to by message id, by number in newsgroup,
or by offset into data base.
The message id is the same on all machines.
The number is specific to a given machine.
The offset into the data base is specific to a particular
machine and a particular open call to the data base.
(When expire runs, it moves the article records.
A program which has the data base open will continue
to use the old data base, however, so that the same offsets
will still work.)

The article data base contains a record for each article.
It consists of a single file divided into several sections.
First is the header, which gives the size of the other sections.
Second is a hash table for message ids.
Third is a hash table of reference lines which is used to deal with
orphaned articles.
Fourth is a table of pointers to newsgroup chains.
All the articles in a newsgroup are connected by a singly linked list.
Finally, the article records appear.

There is one article record for each article.
!E!O!F!

cat > lib/findgroup.c <<\!E!O!F!
#include "defs.h"
#include "str.h"
#include "newsrc.h"


struct ngentry *
findgroup(name)
      register char *name ;
      {
      register struct ngentry *ngp ;
      extern int maxng ;

      for (ngp = ngtable; ngp < ngtable + MAXGROUPS ; ngp++) {
            if (ngp->ng_name && equal(ngp->ng_name, name))
                  return ngp ;
      }
      return 0 ;
}
!E!O!F!

cat > lib/ftime.c <<\!E!O!F!
#include "config.h"

#if BSDREL < 7
static char *SccsId = "@(#)ftime.c	2.3	3/3/83";

#include <sys/types.h>
struct timeb
{
	time_t	time;
	unsigned short millitm;
	short	timezone;
	short	dstflag;
};

extern long timezone;
extern int  daylight;

ftime(tp)
struct timeb *tp;
{
	long t;

	tzset();		/* be sure time zone info set */
	time(&t);
	tp->time = t;
	tp->millitm = 0;
	tp->timezone = timezone/60;
	tp->dstflag = daylight;
}
#endif
!E!O!F!

cat > lib/genafopen.c <<\!E!O!F!
#include <stdio.h>
#include "af.h"


int affd ;			/* artfile file descriptor */
struct afheader afhd ;		/* artfile header */


genafopen(file, mode)
      char *file, *mode ;
      {
      int omode = mode[1] ? 2 : 0 ;

      if ((affd = open(file, omode)) < 0
       && (sleep(2), affd = open(file, omode)) < 0)
            xerror("can't open %s", file) ;
      if (read(affd, (char *)&afhd, sizeof(afhd)) != sizeof afhd)
            xerror("can't read data base header") ;
      if (afhd.af_magic != AF_MAGIC)
            xerror("bad data base file") ;
      if (afhd.af_version != AF_VERSION)
            xerror("Version of article data base is wrong") ;
}
!E!O!F!

cat > lib/genmakefile <<\!E!O!F!
. makedefs
exec > makefile
cat <<\!
# makefile for rlib	1/4/85

!
cat makedefs
cat <<\!

CFLAGS = $(DEBUG)
DEBUG = -O
MAKE = make

RLIB = aflock.o afopen.o cancel.o cgtdate.o dir.o dirname.o\
genafopen.o getadate.o getdate.o ftime.o gethead.o getopt.o\
getuser.o hfgets.o hfree.o hxchg.o isadmin.o isre.o launder.o\
lookup.o ngchain.o ngmatch.o openrc.o pgetuser.o read.o\
replyname.o rewinddir.o rmnf.o readinrc.o roptions.o titmat.o\
write.o setupgrp.o addrc.o allgroups.o findgroup.o gfopen.o\
makehimask.o nextgrp.o prevgrp.o bfr.o bcopy.o bzero.o ckfopen.o\
getaddr.o hash.o lcase.o nsavestr.o nstrip.o prefix.o process.o\
rename.o savestr.o ckmalloc.o scopyn.o strpbrk.o strcspn.o\
strspn.o strtok.o tomsgid.o


all: setuptime makefile rlib.a cgtdate rpathinit.o newer

install: all
	/bin/cp cgtdate $(LIBDIR)/cgtdate

setuptime: setup config
	sh setup

makefile: genmakefile makedefs
	genmakefile	
	@echo 'Makefile changed.  Restart make program.'
	@sh -c 'exit 22'

rlib.a: $(RLIB)
	rm -f $@
	ar rc $@ $(RLIB)
!
if test $BSDREL -gt 7
then	echo "	ranlib \$@"
fi
: Add more assembly language routines when they have been tested.
case $CPU in
pdp11)	files="" suffix=pdp ;;
vax)	files="scopyn" suffix=vax ;;
u3b)	files="bcopy bzero scopyn strpbrk" suffix=u3b ;;
*)	files=
esac
for x in $files
do	cat <<!

$x.o: $x.$suffix
	\$(AS) -o $x.o $x.$suffix
!
done
cat <<\!

rpathinit.o: mypathinit.c defs.h newsdefs.h $(FRC)
	$(CC) -c $(CFLAGS) mypathinit.c
	mv mypathinit.o rpathinit.o

genafopen.o lock.o lookup.o ngchain.o read.o: af.h artfile.h
readinrc.o setupgrp.o: artfile.h
addrc.o nextgrp.o prevgrp.o readinrc.o setupgrp.o: newsrc.h
allgroups.o readinrc.o: ng.h
cancel.o gethead.o hfree.o hxchg.o: arthead.h
$(RLIB): $(FRC)

cgtdate: cgtdatecmd.o cgtdate.o getadate.o getdate.o
	$(CC) $(LFLAGS) -o $@ cgtdatecmd.o rlib.a

newer: newer.c
	$(CC) -o $@ newer.c

FRC:
!
!E!O!F!
chmod +x lib/genmakefile

cat > lib/getadate.c <<\!E!O!F!
/*
 * getadate - get arpa date.
 *
 * This routine takes a character character string containing a
 * date-time in the format specified in RFC 822 and converts it
 * to UNIX internal format.  The components of the date are left
 * in the global tm structure arpadtm.  Getarpad returns -1L if
 * the argument is syntactically incorrect.
 */

#include "config.h"
#if BSDREL == 42
#include <sys/time.h>
#else
#include <time.h>
#endif
#include <ctype.h>

#define HR 3600
#define DAY (24L*HR)

struct tm adatetm ;

static int lookup(), getdig(), skipbl() ;
static char *d ;
static char *days[] = {"sun", "mon", "tue", "wed", "thu", "fri", "sat", 0} ;
static char *months[] = {"jan", "feb", "mar", "apr", "may", "jun",
			 "jul", "aug", "sep", "oct", "nov", "dec", 0} ;
static char *zones[] =
       {"ut", "gmt", "est", "edt", "cst", "cdt", "mst", "mdt", "pst", "pdt",
	"a", "b", "c", "d", "e", "f", "g", "h", "i", "k", "l", "m",
	"n",   "o",   "p",   "q",   "r",   "s",
	"t",   "u",   "v",   "w",   "x",   "y",  "z",
	/* the remaining time zones are not legal according to RFC822 */
	"yst", "ydt", "hst", "hdt",
	"aest", "aesst", "acst",    "acsst",    "awst", 0} ;
static long zoffset[] =
       {0,    0,      5*HR,  4*HR,  6*HR,  5*HR,  7*HR,  6*HR,  8*HR,  7*HR,
	1*HR,2*HR,3*HR,4*HR,5*HR,6*HR,7*HR,8*HR,9*HR,10*HR,11*HR,12*HR,
	-1*HR, -2*HR, -3*HR, -4*HR, -5*HR, -6*HR,
	-7*HR, -8*HR, -9*HR,-10*HR,-11*HR,-12*HR, 0,
	/* the remaining time zones are not legal according to RFC822 */
	9*HR,  8*HR,  10*HR, 9*HR,
	-10*HR, -11*HR,  -9*HR-HR/2,-10*HR-HR/2,-8*HR,  0} ;
static int mon[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334} ;


long
getadate(date)
      register char *date ;
      {
      int i ;
      int oh ;
      long tim ;
      long offset ;

      d = date ;
      if ((adatetm.tm_wday = lookup(days)) >= 0) {
            if (getch(',') < 0)
bad:              return -1L ;
      }
      skipbl() ;
      if (! isdigit(*d))
            goto bad ;
      adatetm.tm_mday = *d++ - '0' ;
      if (isdigit(*d))
            adatetm.tm_mday = adatetm.tm_mday * 10 + *d++ - '0' ;
      if (*d == '-')		/* ARPA spec doesn't permit this */
            d++ ;
      if ((adatetm.tm_mon = lookup(months)) < 0)
            goto bad ;
      getch('-') ;		/* ARPA spec doesn't permit this */
      if ((adatetm.tm_year = getdig()) < 0)
            goto bad ;
      skipbl() ;
      if ((adatetm.tm_hour = getdig()) < 0)
            goto bad ;
      if (getch(':') < 0)
            goto bad ;
      if ((adatetm.tm_min = getdig()) < 0)
            goto bad ;
      if (getch(':') < 0)
            adatetm.tm_sec = 0 ;
      else if ((adatetm.tm_sec = getdig()) < 0)
            goto bad ;
      if (getch('+') >= 0) {
            offset = 60 ;
numtz:      if ((oh = getdig()) < 0
             || (i = getdig()) < 0)
                  goto bad ;
            offset *= 60 * oh + i ;
      } else if (getch('-') >= 0) {
            offset = -60 ;
            goto numtz ;
      } else if ((i = lookup(zones)) >= 0)
            offset = zoffset[i] ;
      else
            goto bad ;

      /* now calculate the time */
      adatetm.tm_yday = mon[adatetm.tm_mon] + adatetm.tm_mday - 1 ;
      if ((adatetm.tm_year & 03) == 0 && adatetm.tm_mon >= 2)
            adatetm.tm_yday++ ;
      tim = adatetm.tm_sec + 60 * adatetm.tm_min + 3600L * adatetm.tm_hour
          + offset + DAY * adatetm.tm_yday ;
      for (i = adatetm.tm_year ; --i >= 70 ; )
            tim += (i & 03) == 0?  366 * DAY : 365 * DAY ;
      return tim ;
}



static int
lookup(ltab)
      char **ltab ;
      {
      char tok[20] ;
      register char *dp ;
      register char *tp ;
      register char **lp ;

      dp = d ;
      tp = tok ;
      while (isspace(*dp))
            dp++ ;
      for (;;) {
            if (isupper(*dp))
                  *tp++ = tolower(*dp) ;
            else if (islower(*dp))
                  *tp++ = *dp ;
            else
                  break ;
            if (tp >= &tok[20])
                  return -1 ;
            dp++ ;
      }
      if (dp == d)
            return -1 ;
      *tp = '\0' ;
      for (lp = ltab ; *lp ; lp++) {
            if (strcmp(*lp, tok) == 0) {
                  d = dp ;
                  return lp - ltab ;
            }
      }
      return -1 ;
}


static int
getdig() {
      register char *p ;

      p = d ;
      if (! isdigit(*p) || ! isdigit(*++p))
            return -1 ;
      d += 2 ;
      return 10 * p[-1] + p[0] - (10 * '0' + '0') ;
}


static
skipbl() {
      while (isspace(*d))
            d++ ;
}


static int
getch(c) {
      skipbl() ;
      if (*d != c)
            return -1 ;
      d++ ;
      skipbl() ;
      return 0 ;
}
!E!O!F!

cat > lib/getaddr.c <<\!E!O!F!
/*
 * Get the machine address of a person, stripping off the real name.
 */

#include <stdio.h>
#include "config.h"
#include "defs.h"

char *
getaddr(full, mach)
      char full[] ;
      char mach[NAMELEN] ;
      {
      register char *p, *q, *r ;
      char *index(), *rindex();

      p = full ;
      if ((q = index(p, '(')) == NULL)
            q = p + strlen(p) ;
      if ((p = index(full, '<')) != NULL && p < q && (r = rindex(p, '>')) != NULL) {
            p++ ;
            q = r ;
      } else
            p = full ;
      while (*p == ' ')
            p++ ;
      while (--q > p && *q == ' ') ;
      if (q > p + NAMELEN - 1)
            q = p + NAMELEN - 1 ;
      r = mach ;
      while (p <= q)
            *r++ = *p++ ;
      *r = '\0' ;
      return mach ;
}
!E!O!F!

cat > lib/getdate.y <<\!E!O!F!
%token ID MONTH DAY MERIDIAN NUMBER UNIT MUNIT SUNIT ZONE DAYZONE AGO
%{
	/* 	Steven M. Bellovin (unc!smb)			*/
	/*	Dept. of Computer Science			*/
	/*	University of North Carolina at Chapel Hill	*/
	/*	@(#)getdate.y	2.6	4/20/84	*/

#include <sys/types.h>
#include "config.h"
#if BSDREL < 7
struct timeb
{
	time_t	time;
	unsigned short millitm;
	short	timezone;
	short	dstflag;
};
#else
#include <sys/timeb.h>
#endif
#include <ctype.h>
#include <time.h>

#define	NULL	0
#define daysec (24L*60L*60L)
	static int timeflag, zoneflag, dateflag, dayflag, relflag;
	static time_t relsec, relmonth;
	static int hh, mm, ss, merid, daylight;
	static int dayord, dayreq;
	static int month, day, year;
	static int ourzone;
#define AM 1
#define PM 2
#define DAYLIGHT 1
#define STANDARD 2
#define MAYBE    3
%}

%%
timedate: 		/* empty */
	| timedate item;

item:	tspec =
		{timeflag++;}
	| zone =
		{zoneflag++;}
	| dtspec =
		{dateflag++;}
	| dyspec =
		{dayflag++;}
	| rspec =
		{relflag++;}
	| nspec;

nspec:	NUMBER =
		{if (timeflag && dateflag && !relflag) year = $1;
		else {timeflag++;hh = $1/100;mm = $1%100;ss = 0;merid = 24;}};

tspec:	NUMBER MERIDIAN =
		{hh = $1; mm = 0; ss = 0; merid = $2;}
	| NUMBER ':' NUMBER =
		{hh = $1; mm = $3; merid = 24;}
	| NUMBER ':' NUMBER MERIDIAN =
		{hh = $1; mm = $3; merid = $4;}
	| NUMBER ':' NUMBER ':' NUMBER =
		{hh = $1; mm = $3; ss = $5; merid = 24;}
	| NUMBER ':' NUMBER ':' NUMBER MERIDIAN =
		{hh = $1; mm = $3; ss = $5; merid = $6;};

zone:	ZONE =
		{ourzone = $1; daylight = STANDARD;}
	| DAYZONE =
		{ourzone = $1; daylight = DAYLIGHT;};

dyspec:	DAY =
		{dayord = 1; dayreq = $1;}
	| DAY ',' =
		{dayord = 1; dayreq = $1;}
	| NUMBER DAY =
		{dayord = $1; dayreq = $2;};

dtspec:	NUMBER '/' NUMBER =
		{month = $1; day = $3;}
	| NUMBER '/' NUMBER '/' NUMBER =
		{month = $1; day = $3; year = $5;}
	| MONTH NUMBER =
		{month = $1; day = $2;}
	| MONTH NUMBER ',' NUMBER =
		{month = $1; day = $2; year = $4;}
	| NUMBER MONTH =
		{month = $2; day = $1;}
	| NUMBER MONTH NUMBER =
		{month = $2; day = $1; year = $3;};


rspec:	NUMBER UNIT =
		{relsec +=  60L * $1 * $2;}
	| NUMBER MUNIT =
		{relmonth += $1 * $2;}
	| NUMBER SUNIT =
		{relsec += $1;}
	| UNIT =
		{relsec +=  60L * $1;}
	| MUNIT =
		{relmonth += $1;}
	| SUNIT =
		{relsec++;}
	| rspec AGO =
		{relsec = -relsec; relmonth = -relmonth;};
%%

static int mdays[12] =
	{31, 0, 31,  30, 31, 30,  31, 31, 30,  31, 30, 31};
#define epoch 1970

extern struct tm *localtime();
time_t dateconv(mm, dd, yy, h, m, s, mer, zone, dayflag)
int mm, dd, yy, h, m, s, mer, zone, dayflag;
{
	time_t tod, jdate;
	register int i;
	time_t timeconv();

	if (yy < 0) yy = -yy;
	if (yy < 100) yy += 1900;
	mdays[1] = 28 + (yy%4 == 0);
	if (yy < epoch || yy > 1999 || mm < 1 || mm > 12 ||
		dd < 1 || dd > mdays[--mm]) return (-1);
	jdate = dd-1;
        for (i=0; i<mm; i++) jdate += mdays[i];
	for (i = epoch; i < yy; i++) jdate += 365 + (i%4 == 0);
	jdate *= daysec;
	jdate += zone * 60L;
	if ((tod = timeconv(h, m, s, mer)) < 0) return (-1);
	jdate += tod;
	if (dayflag==DAYLIGHT || (dayflag==MAYBE&&localtime(&jdate)->tm_isdst))
		jdate += -1*60*60;
	return (jdate);
}

time_t dayconv(ord, day, now) int ord, day; time_t now;
{
	register struct tm *loctime;
	time_t tod;
	time_t daylcorr();

	tod = now;
	loctime = localtime(&tod);
	tod += daysec * ((day - loctime->tm_wday + 7) % 7);
	tod += 7*daysec*(ord<=0?ord:ord-1);
	return daylcorr(tod, now);
}

time_t timeconv(hh, mm, ss, mer) register int hh, mm, ss, mer;
{
	if (mm < 0 || mm > 59 || ss < 0 || ss > 59) return (-1);
	switch (mer) {
		case AM: if (hh < 1 || hh > 12) return(-1);
			 return (60L * ((hh%12)*60L + mm)+ss);
		case PM: if (hh < 1 || hh > 12) return(-1);
			 return (60L * ((hh%12 +12)*60L + mm)+ss);
		case 24: if (hh < 0 || hh > 23) return (-1);
			 return (60L * (hh*60L + mm)+ss);
		default: return (-1);
	}
}
time_t monthadd(sdate, relmonth) time_t sdate, relmonth;
{
	struct tm *ltime;
	time_t dateconv();
	time_t daylcorr();
	int mm, yy;

	if (relmonth == 0) return 0;
	ltime = localtime(&sdate);
	mm = 12*ltime->tm_year + ltime->tm_mon + relmonth;
	yy = mm/12;
	mm = mm%12 + 1;
	return daylcorr(dateconv(mm, ltime->tm_mday, yy, ltime->tm_hour,
		ltime->tm_min, ltime->tm_sec, 24, ourzone, MAYBE), sdate);
}

time_t daylcorr(future, now) time_t future, now;
{
	int fdayl, nowdayl;

	nowdayl = (localtime(&now)->tm_hour+1) % 24;
	fdayl = (localtime(&future)->tm_hour+1) % 24;
	return (future-now) + 60L*60L*(nowdayl-fdayl);
}

static char *lptr;

yylex()
{
	extern int yylval;
	int sign;
	register char c;
	register char *p;
	char idbuf[20];
	int pcnt;

	for (;;) {
		while (isspace(*lptr)) lptr++;

		if (isdigit(c = *lptr) || c == '-' || c == '+') {
			if (c== '-' || c == '+') {
				if (c=='-') sign = -1;
				else sign = 1;
				if (!isdigit(*++lptr)) {
					/* yylval = sign; return (NUMBER); */
					return yylex();	/* skip the '-' sign */
				}
			} else sign = 1;
			yylval = 0;
			while (isdigit(c = *lptr++)) yylval = 10*yylval + c - '0';
			yylval *= sign;
			lptr--;
			return (NUMBER);

		} else if (isalpha(c)) {
			p = idbuf;
			while (isalpha(c = *lptr++) || c=='.')
				*p++ = c;
			*p = '\0';
			lptr--;
			return (lookup(idbuf));
		}

		else if (c == '(') {
			pcnt = 0;
			do {
				c = *lptr++;
				if (c == '\0') return(c);
				else if (c == '(') pcnt++;
				else if (c == ')') pcnt--;
			} while (pcnt > 0);
		}

		else return (*lptr++);
	}
}

struct table {
	char *name;
	int type, value;
};

struct table mdtab[] = {
	{"January", MONTH, 1},
	{"February", MONTH, 2},
	{"March", MONTH, 3},
	{"April", MONTH, 4},
	{"May", MONTH, 5},
	{"June", MONTH, 6},
	{"July", MONTH, 7},
	{"August", MONTH, 8},
	{"September", MONTH, 9},
	{"Sept", MONTH, 9},
	{"October", MONTH, 10},
	{"November", MONTH, 11},
	{"December", MONTH, 12},

	{"Sunday", DAY, 0},
	{"Monday", DAY, 1},
	{"Tuesday", DAY, 2},
	{"Tues", DAY, 2},
	{"Wednesday", DAY, 3},
	{"Wednes", DAY, 3},
	{"Thursday", DAY, 4},
	{"Thur", DAY, 4},
	{"Thurs", DAY, 4},
	{"Friday", DAY, 5},
	{"Saturday", DAY, 6},
	{0, 0, 0}};

#define HRS *60
#define HALFHR 30
struct table mztab[] = {
	{"a.m.", MERIDIAN, AM},
	{"am", MERIDIAN, AM},
	{"p.m.", MERIDIAN, PM},
	{"pm", MERIDIAN, PM},
	{"nst", ZONE, 3 HRS + HALFHR},		/* Newfoundland */
	{"n.s.t.", ZONE, 3 HRS + HALFHR},
	{"ast", ZONE, 4 HRS},		/* Atlantic */
	{"a.s.t.", ZONE, 4 HRS},
	{"adt", DAYZONE, 4 HRS},
	{"a.d.t.", DAYZONE, 4 HRS},
	{"est", ZONE, 5 HRS},		/* Eastern */
	{"e.s.t.", ZONE, 5 HRS},
	{"edt", DAYZONE, 5 HRS},
	{"e.d.t.", DAYZONE, 5 HRS},
	{"cst", ZONE, 6 HRS},		/* Central */
	{"c.s.t.", ZONE, 6 HRS},
	{"cdt", DAYZONE, 6 HRS},
	{"c.d.t.", DAYZONE, 6 HRS},
	{"mst", ZONE, 7 HRS},		/* Mountain */
	{"m.s.t.", ZONE, 7 HRS},
	{"mdt", DAYZONE, 7 HRS},
	{"m.d.t.", DAYZONE, 7 HRS},
	{"pst", ZONE, 8 HRS},		/* Pacific */
	{"p.s.t.", ZONE, 8 HRS},
	{"pdt", DAYZONE, 8 HRS},
	{"p.d.t.", DAYZONE, 8 HRS},
	{"yst", ZONE, 9 HRS},		/* Yukon */
	{"y.s.t.", ZONE, 9 HRS},
	{"ydt", DAYZONE, 9 HRS},
	{"y.d.t.", DAYZONE, 9 HRS},
	{"hst", ZONE, 10 HRS},		/* Hawaii */
	{"h.s.t.", ZONE, 10 HRS},
	{"hdt", DAYZONE, 10 HRS},
	{"h.d.t.", DAYZONE, 10 HRS},
	{"bst", ZONE, 11 HRS},		/* Bering */
	{"b.s.t.", ZONE, 11 HRS},
	{"bdt", DAYZONE, 11 HRS},
	{"b.d.t.", DAYZONE, 11 HRS},

	{"gmt", ZONE, 0 HRS},
	{"g.m.t.", ZONE, 0 HRS},

	{"aest", ZONE, -10 HRS},	/* Australian Eastern Time */
	{"a.e.s.t.", ZONE, -10 HRS},
	{"aesst", DAYZONE, -10 HRS},	/* Australian Eastern Summer Time */
	{"a.e.s.s.t.", DAYZONE, -10 HRS},
	{"acst", ZONE, -(9 HRS + HALFHR)},	/* Australian Central Time */
	{"a.c.s.t.", ZONE, -(9 HRS + HALFHR)},
	{"acsst", DAYZONE, -(9 HRS + HALFHR)},	/* Australian Central Summer */
	{"a.c.s.s.t.", DAYZONE, -(9 HRS + HALFHR)},
	{"awst", ZONE, -8 HRS},		/* Australian Western Time */
	{"a.w.s.t.", ZONE, -8 HRS},	/* (no daylight time there, I'm told */
	{0, 0, 0}};

struct table unittb[] = {
	{"year", MUNIT, 12},
	{"month", MUNIT, 1},
	{"fortnight", UNIT, 14*24*60},
	{"week", UNIT, 7*24*60},
	{"day", UNIT, 1*24*60},
	{"hour", UNIT, 60},
	{"minute", UNIT, 1},
	{"min", UNIT, 1},
	{"second", SUNIT, 1},
	{"sec", SUNIT, 1},
	{0, 0, 0}};

struct table othertb[] = {
	{"tomorrow", UNIT, 1*24*60},
	{"yesterday", UNIT, -1*24*60},
	{"today", UNIT, 0},
	{"now", UNIT, 0},
	{"last", NUMBER, -1},
	{"this", UNIT, 0},
	{"next", NUMBER, 2},
	{"first", NUMBER, 1},
	/* {"second", NUMBER, 2}, */
	{"third", NUMBER, 3},
	{"fourth", NUMBER, 4},
	{"fifth", NUMBER, 5},
	{"sixth", NUMBER, 6},
	{"seventh", NUMBER, 7},
	{"eigth", NUMBER, 8},
	{"ninth", NUMBER, 9},
	{"tenth", NUMBER, 10},
	{"eleventh", NUMBER, 11},
	{"twelfth", NUMBER, 12},
	{"ago", AGO, 1},
	{0, 0, 0}};

struct table milzone[] = {
	{"a", ZONE, 1 HRS},
	{"b", ZONE, 2 HRS},
	{"c", ZONE, 3 HRS},
	{"d", ZONE, 4 HRS},
	{"e", ZONE, 5 HRS},
	{"f", ZONE, 6 HRS},
	{"g", ZONE, 7 HRS},
	{"h", ZONE, 8 HRS},
	{"i", ZONE, 9 HRS},
	{"k", ZONE, 10 HRS},
	{"l", ZONE, 11 HRS},
	{"m", ZONE, 12 HRS},
	{"n", ZONE, -1 HRS},
	{"o", ZONE, -2 HRS},
	{"p", ZONE, -3 HRS},
	{"q", ZONE, -4 HRS},
	{"r", ZONE, -5 HRS},
	{"s", ZONE, -6 HRS},
	{"t", ZONE, -7 HRS},
	{"u", ZONE, -8 HRS},
	{"v", ZONE, -9 HRS},
	{"w", ZONE, -10 HRS},
	{"x", ZONE, -11 HRS},
	{"y", ZONE, -12 HRS},
	{"z", ZONE, 0 HRS},
	{0, 0, 0}};

lookup(id) char *id;
{
#define gotit (yylval=i->value,  i->type)
#define getid for(j=idvar, k=id; *j++ = *k++; )

	char idvar[20];
	register char *j, *k;
	register struct table *i;
	int abbrev;

	getid;
	if (strlen(idvar) == 3) abbrev = 1;
	else if (strlen(idvar) == 4 && idvar[3] == '.') {
		abbrev = 1;
		idvar[3] = '\0';
	}
	else abbrev = 0;

	if (islower(*idvar)) *idvar = toupper(*idvar);

	for (i = mdtab; i->name; i++) {
		k = idvar;
		for (j = i->name; *j++ == *k++;) {
			if (abbrev && j==i->name+3) return gotit;
			if (j[-1] == 0) return gotit;
		}
	}

	getid;
	for (i = mztab; i->name; i++)
		if (strcmp(i->name, idvar) == 0) return gotit;

	for (j = idvar; *j; j++) if (isupper(*j)) *j = tolower(*j);
	for (i=mztab; i->name; i++)
		if (strcmp(i->name, idvar) == 0) return gotit;

	getid;
	for (i=unittb; i->name; i++)
		if (strcmp(i->name, idvar) == 0) return gotit;

	if (idvar[strlen(idvar)-1] == 's') idvar[strlen(idvar)-1] = '\0';
	for (i=unittb; i->name; i++)
		if (strcmp(i->name, idvar) == 0) return gotit;

	getid;
	for (i = othertb; i->name; i++)
		if (strcmp(i->name, idvar) == 0) return gotit;

	getid;
	if (strlen(idvar) == 1 && isalpha(*idvar)) {
		if (isupper(*idvar)) *idvar = tolower(*idvar);
		for (i = milzone; i->name; i++)
			if (strcmp(i->name, idvar) == 0) return gotit;
	}

	return(ID);
}

time_t getdate(p, now) char *p; struct timeb *now;
{
#define mcheck(f)	if (f>1) err++
	time_t monthadd();
	int err;
	struct tm *lt;
	struct timeb ftz;

	time_t sdate, tod;

	lptr = p;
	if (now == ((struct timeb *) NULL)) {
		now = &ftz;
		ftime(&ftz);
	}
	lt = localtime(&now->time);
	year = lt->tm_year;
	month = lt->tm_mon+1;
	day = lt->tm_mday;
	relsec = 0; relmonth = 0;
	timeflag=zoneflag=dateflag=dayflag=relflag=0;
	ourzone = now->timezone;
	daylight = MAYBE;
	hh = mm = ss = 0;
	merid = 24;

	if (err = yyparse()) return (-1);

	mcheck(timeflag);
	mcheck(zoneflag);
	mcheck(dateflag);
	mcheck(dayflag);

	if (err) return (-1);

	if (dateflag || timeflag || dayflag) {
		sdate = dateconv(month,day,year,hh,mm,ss,merid,ourzone,daylight);
		if (sdate < 0) return -1;
	}
	else {
		sdate = now->time;
		if (relflag == 0)
			sdate -= (lt->tm_sec + lt->tm_min*60 +
				lt->tm_hour*(60L*60L));
	}

	sdate += relsec;
	sdate += monthadd(sdate, relmonth);

	if (dayflag && !dateflag) {
		tod = dayconv(dayord, dayreq, sdate);
		sdate += tod;
	}

	return sdate;
}

yyerror(s) char *s;
{}
!E!O!F!

echo Part 2 of 7 extracted.



More information about the Mod.sources mailing list