[net.sources] news 2.10.2 src part 3

rick@seismo.UUCP (Rick Adams) (09/04/84)

if test ! -d src
then
	mkdir src
fi
echo x - src/getdate.y
sed 's/^X//' >src/getdate.y <<'*-*-END-of-src/getdate.y-*-*'
X%token ID MONTH DAY MERIDIAN NUMBER UNIT MUNIT SUNIT ZONE DAYZONE AGO
X%{
X	/* 	Steven M. Bellovin (unc!smb)			*/
X	/*	Dept. of Computer Science			*/
X	/*	University of North Carolina at Chapel Hill	*/
X	/*	@(#)getdate.y	2.6	4/20/84	*/
X
X#include <sys/types.h>
X#ifdef USG
Xstruct timeb
X{
X	time_t	time;
X	unsigned short millitm;
X	short	timezone;
X	short	dstflag;
X};
X#else
X#include <sys/timeb.h>
X#endif
X#include <ctype.h>
X#include <time.h>
X
X#define	NULL	0
X#define daysec (24L*60L*60L)
X	static int timeflag, zoneflag, dateflag, dayflag, relflag;
X	static time_t relsec, relmonth;
X	static int hh, mm, ss, merid, daylight;
X	static int dayord, dayreq;
X	static int month, day, year;
X	static int ourzone;
X#define AM 1
X#define PM 2
X#define DAYLIGHT 1
X#define STANDARD 2
X#define MAYBE    3
X%}
X
X%%
Xtimedate: 		/* empty */
X	| timedate item;
X
Xitem:	tspec =
X		{timeflag++;}
X	| zone =
X		{zoneflag++;}
X	| dtspec =
X		{dateflag++;}
X	| dyspec =
X		{dayflag++;}
X	| rspec =
X		{relflag++;}
X	| nspec;
X
Xnspec:	NUMBER =
X		{if (timeflag && dateflag && !relflag) year = $1;
X		else {timeflag++;hh = $1/100;mm = $1%100;ss = 0;merid = 24;}};
X
Xtspec:	NUMBER MERIDIAN =
X		{hh = $1; mm = 0; ss = 0; merid = $2;}
X	| NUMBER ':' NUMBER =
X		{hh = $1; mm = $3; merid = 24;}
X	| NUMBER ':' NUMBER MERIDIAN =
X		{hh = $1; mm = $3; merid = $4;}
X	| NUMBER ':' NUMBER ':' NUMBER =
X		{hh = $1; mm = $3; ss = $5; merid = 24;}
X	| NUMBER ':' NUMBER ':' NUMBER MERIDIAN =
X		{hh = $1; mm = $3; ss = $5; merid = $6;};
X
Xzone:	ZONE =
X		{ourzone = $1; daylight = STANDARD;}
X	| DAYZONE =
X		{ourzone = $1; daylight = DAYLIGHT;};
X
Xdyspec:	DAY =
X		{dayord = 1; dayreq = $1;}
X	| DAY ',' =
X		{dayord = 1; dayreq = $1;}
X	| NUMBER DAY =
X		{dayord = $1; dayreq = $2;};
X
Xdtspec:	NUMBER '/' NUMBER =
X		{month = $1; day = $3;}
X	| NUMBER '/' NUMBER '/' NUMBER =
X		{month = $1; day = $3; year = $5;}
X	| MONTH NUMBER =
X		{month = $1; day = $2;}
X	| MONTH NUMBER ',' NUMBER =
X		{month = $1; day = $2; year = $4;}
X	| NUMBER MONTH =
X		{month = $2; day = $1;}
X	| NUMBER MONTH NUMBER =
X		{month = $2; day = $1; year = $3;};
X
X
Xrspec:	NUMBER UNIT =
X		{relsec +=  60L * $1 * $2;}
X	| NUMBER MUNIT =
X		{relmonth += $1 * $2;}
X	| NUMBER SUNIT =
X		{relsec += $1;}
X	| UNIT =
X		{relsec +=  60L * $1;}
X	| MUNIT =
X		{relmonth += $1;}
X	| SUNIT =
X		{relsec++;}
X	| rspec AGO =
X		{relsec = -relsec; relmonth = -relmonth;};
X%%
X
Xstatic int mdays[12] =
X	{31, 0, 31,  30, 31, 30,  31, 31, 30,  31, 30, 31};
X#define epoch 1970
X
Xextern struct tm *localtime();
Xtime_t dateconv(mm, dd, yy, h, m, s, mer, zone, dayflag)
Xint mm, dd, yy, h, m, s, mer, zone, dayflag;
X{
X	time_t tod, jdate;
X	register int i;
X	time_t timeconv();
X
X	if (yy < 0) yy = -yy;
X	if (yy < 100) yy += 1900;
X	mdays[1] = 28 + (yy%4 == 0);
X	if (yy < epoch || yy > 1999 || mm < 1 || mm > 12 ||
X		dd < 1 || dd > mdays[--mm]) return (-1);
X	jdate = dd-1;
X        for (i=0; i<mm; i++) jdate += mdays[i];
X	for (i = epoch; i < yy; i++) jdate += 365 + (i%4 == 0);
X	jdate *= daysec;
X	jdate += zone * 60L;
X	if ((tod = timeconv(h, m, s, mer)) < 0) return (-1);
X	jdate += tod;
X	if (dayflag==DAYLIGHT || (dayflag==MAYBE&&localtime(&jdate)->tm_isdst))
X		jdate += -1*60*60;
X	return (jdate);
X}
X
Xtime_t dayconv(ord, day, now) int ord, day; time_t now;
X{
X	register struct tm *loctime;
X	time_t tod;
X	time_t daylcorr();
X
X	tod = now;
X	loctime = localtime(&tod);
X	tod += daysec * ((day - loctime->tm_wday + 7) % 7);
X	tod += 7*daysec*(ord<=0?ord:ord-1);
X	return daylcorr(tod, now);
X}
X
Xtime_t timeconv(hh, mm, ss, mer) register int hh, mm, ss, mer;
X{
X	if (mm < 0 || mm > 59 || ss < 0 || ss > 59) return (-1);
X	switch (mer) {
X		case AM: if (hh < 1 || hh > 12) return(-1);
X			 return (60L * ((hh%12)*60L + mm)+ss);
X		case PM: if (hh < 1 || hh > 12) return(-1);
X			 return (60L * ((hh%12 +12)*60L + mm)+ss);
X		case 24: if (hh < 0 || hh > 23) return (-1);
X			 return (60L * (hh*60L + mm)+ss);
X		default: return (-1);
X	}
X}
Xtime_t monthadd(sdate, relmonth) time_t sdate, relmonth;
X{
X	struct tm *ltime;
X	time_t dateconv();
X	time_t daylcorr();
X	int mm, yy;
X
X	if (relmonth == 0) return 0;
X	ltime = localtime(&sdate);
X	mm = 12*ltime->tm_year + ltime->tm_mon + relmonth;
X	yy = mm/12;
X	mm = mm%12 + 1;
X	return daylcorr(dateconv(mm, ltime->tm_mday, yy, ltime->tm_hour,
X		ltime->tm_min, ltime->tm_sec, 24, ourzone, MAYBE), sdate);
X}
X
Xtime_t daylcorr(future, now) time_t future, now;
X{
X	int fdayl, nowdayl;
X
X	nowdayl = (localtime(&now)->tm_hour+1) % 24;
X	fdayl = (localtime(&future)->tm_hour+1) % 24;
X	return (future-now) + 60L*60L*(nowdayl-fdayl);
X}
X
Xstatic char *lptr;
X
Xyylex()
X{
X	extern int yylval;
X	int sign;
X	register char c;
X	register char *p;
X	char idbuf[20];
X	int pcnt;
X
X	for (;;) {
X		while (isspace(*lptr)) lptr++;
X
X		if (isdigit(c = *lptr) || c == '-' || c == '+') {
X			if (c== '-' || c == '+') {
X				if (c=='-') sign = -1;
X				else sign = 1;
X				if (!isdigit(*++lptr)) {
X					/* yylval = sign; return (NUMBER); */
X					return yylex();	/* skip the '-' sign */
X				}
X			} else sign = 1;
X			yylval = 0;
X			while (isdigit(c = *lptr++)) yylval = 10*yylval + c - '0';
X			yylval *= sign;
X			lptr--;
X			return (NUMBER);
X
X		} else if (isalpha(c)) {
X			p = idbuf;
X			while (isalpha(c = *lptr++) || c=='.')
X				*p++ = c;
X			*p = '\0';
X			lptr--;
X			return (lookup(idbuf));
X		}
X
X		else if (c == '(') {
X			pcnt = 0;
X			do {
X				c = *lptr++;
X				if (c == '\0') return(c);
X				else if (c == '(') pcnt++;
X				else if (c == ')') pcnt--;
X			} while (pcnt > 0);
X		}
X
X		else return (*lptr++);
X	}
X}
X
Xstruct table {
X	char *name;
X	int type, value;
X};
X
Xstruct table mdtab[] = {
X	{"January", MONTH, 1},
X	{"February", MONTH, 2},
X	{"March", MONTH, 3},
X	{"April", MONTH, 4},
X	{"May", MONTH, 5},
X	{"June", MONTH, 6},
X	{"July", MONTH, 7},
X	{"August", MONTH, 8},
X	{"September", MONTH, 9},
X	{"Sept", MONTH, 9},
X	{"October", MONTH, 10},
X	{"November", MONTH, 11},
X	{"December", MONTH, 12},
X
X	{"Sunday", DAY, 0},
X	{"Monday", DAY, 1},
X	{"Tuesday", DAY, 2},
X	{"Tues", DAY, 2},
X	{"Wednesday", DAY, 3},
X	{"Wednes", DAY, 3},
X	{"Thursday", DAY, 4},
X	{"Thur", DAY, 4},
X	{"Thurs", DAY, 4},
X	{"Friday", DAY, 5},
X	{"Saturday", DAY, 6},
X	{0, 0, 0}};
X
X#define HRS *60
X#define HALFHR 30
Xstruct table mztab[] = {
X	{"a.m.", MERIDIAN, AM},
X	{"am", MERIDIAN, AM},
X	{"p.m.", MERIDIAN, PM},
X	{"pm", MERIDIAN, PM},
X	{"nst", ZONE, 3 HRS + HALFHR},		/* Newfoundland */
X	{"n.s.t.", ZONE, 3 HRS + HALFHR},
X	{"ast", ZONE, 4 HRS},		/* Atlantic */
X	{"a.s.t.", ZONE, 4 HRS},
X	{"adt", DAYZONE, 4 HRS},
X	{"a.d.t.", DAYZONE, 4 HRS},
X	{"est", ZONE, 5 HRS},		/* Eastern */
X	{"e.s.t.", ZONE, 5 HRS},
X	{"edt", DAYZONE, 5 HRS},
X	{"e.d.t.", DAYZONE, 5 HRS},
X	{"cst", ZONE, 6 HRS},		/* Central */
X	{"c.s.t.", ZONE, 6 HRS},
X	{"cdt", DAYZONE, 6 HRS},
X	{"c.d.t.", DAYZONE, 6 HRS},
X	{"mst", ZONE, 7 HRS},		/* Mountain */
X	{"m.s.t.", ZONE, 7 HRS},
X	{"mdt", DAYZONE, 7 HRS},
X	{"m.d.t.", DAYZONE, 7 HRS},
X	{"pst", ZONE, 8 HRS},		/* Pacific */
X	{"p.s.t.", ZONE, 8 HRS},
X	{"pdt", DAYZONE, 8 HRS},
X	{"p.d.t.", DAYZONE, 8 HRS},
X	{"yst", ZONE, 9 HRS},		/* Yukon */
X	{"y.s.t.", ZONE, 9 HRS},
X	{"ydt", DAYZONE, 9 HRS},
X	{"y.d.t.", DAYZONE, 9 HRS},
X	{"hst", ZONE, 10 HRS},		/* Hawaii */
X	{"h.s.t.", ZONE, 10 HRS},
X	{"hdt", DAYZONE, 10 HRS},
X	{"h.d.t.", DAYZONE, 10 HRS},
X	{"bst", ZONE, 11 HRS},		/* Bering */
X	{"b.s.t.", ZONE, 11 HRS},
X	{"bdt", DAYZONE, 11 HRS},
X	{"b.d.t.", DAYZONE, 11 HRS},
X
X	{"gmt", ZONE, 0 HRS},
X	{"g.m.t.", ZONE, 0 HRS},
X
X	{"aest", ZONE, -10 HRS},	/* Australian Eastern Time */
X	{"a.e.s.t.", ZONE, -10 HRS},
X	{"aesst", DAYZONE, -10 HRS},	/* Australian Eastern Summer Time */
X	{"a.e.s.s.t.", DAYZONE, -10 HRS},
X	{"acst", ZONE, -(9 HRS + HALFHR)},	/* Australian Central Time */
X	{"a.c.s.t.", ZONE, -(9 HRS + HALFHR)},
X	{"acsst", DAYZONE, -(9 HRS + HALFHR)},	/* Australian Central Summer */
X	{"a.c.s.s.t.", DAYZONE, -(9 HRS + HALFHR)},
X	{"awst", ZONE, -8 HRS},		/* Australian Western Time */
X	{"a.w.s.t.", ZONE, -8 HRS},	/* (no daylight time there, I'm told */
X	{0, 0, 0}};
X
Xstruct table unittb[] = {
X	{"year", MUNIT, 12},
X	{"month", MUNIT, 1},
X	{"fortnight", UNIT, 14*24*60},
X	{"week", UNIT, 7*24*60},
X	{"day", UNIT, 1*24*60},
X	{"hour", UNIT, 60},
X	{"minute", UNIT, 1},
X	{"min", UNIT, 1},
X	{"second", SUNIT, 1},
X	{"sec", SUNIT, 1},
X	{0, 0, 0}};
X
Xstruct table othertb[] = {
X	{"tomorrow", UNIT, 1*24*60},
X	{"yesterday", UNIT, -1*24*60},
X	{"today", UNIT, 0},
X	{"now", UNIT, 0},
X	{"last", NUMBER, -1},
X	{"this", UNIT, 0},
X	{"next", NUMBER, 2},
X	{"first", NUMBER, 1},
X	/* {"second", NUMBER, 2}, */
X	{"third", NUMBER, 3},
X	{"fourth", NUMBER, 4},
X	{"fifth", NUMBER, 5},
X	{"sixth", NUMBER, 6},
X	{"seventh", NUMBER, 7},
X	{"eigth", NUMBER, 8},
X	{"ninth", NUMBER, 9},
X	{"tenth", NUMBER, 10},
X	{"eleventh", NUMBER, 11},
X	{"twelfth", NUMBER, 12},
X	{"ago", AGO, 1},
X	{0, 0, 0}};
X
Xstruct table milzone[] = {
X	{"a", ZONE, 1 HRS},
X	{"b", ZONE, 2 HRS},
X	{"c", ZONE, 3 HRS},
X	{"d", ZONE, 4 HRS},
X	{"e", ZONE, 5 HRS},
X	{"f", ZONE, 6 HRS},
X	{"g", ZONE, 7 HRS},
X	{"h", ZONE, 8 HRS},
X	{"i", ZONE, 9 HRS},
X	{"k", ZONE, 10 HRS},
X	{"l", ZONE, 11 HRS},
X	{"m", ZONE, 12 HRS},
X	{"n", ZONE, -1 HRS},
X	{"o", ZONE, -2 HRS},
X	{"p", ZONE, -3 HRS},
X	{"q", ZONE, -4 HRS},
X	{"r", ZONE, -5 HRS},
X	{"s", ZONE, -6 HRS},
X	{"t", ZONE, -7 HRS},
X	{"u", ZONE, -8 HRS},
X	{"v", ZONE, -9 HRS},
X	{"w", ZONE, -10 HRS},
X	{"x", ZONE, -11 HRS},
X	{"y", ZONE, -12 HRS},
X	{"z", ZONE, 0 HRS},
X	{0, 0, 0}};
X
Xlookup(id) char *id;
X{
X#define gotit (yylval=i->value,  i->type)
X#define getid for(j=idvar, k=id; *j++ = *k++; )
X
X	char idvar[20];
X	register char *j, *k;
X	register struct table *i;
X	int abbrev;
X
X	getid;
X	if (strlen(idvar) == 3) abbrev = 1;
X	else if (strlen(idvar) == 4 && idvar[3] == '.') {
X		abbrev = 1;
X		idvar[3] = '\0';
X	}
X	else abbrev = 0;
X
X	if (islower(*idvar)) *idvar = toupper(*idvar);
X
X	for (i = mdtab; i->name; i++) {
X		k = idvar;
X		for (j = i->name; *j++ == *k++;) {
X			if (abbrev && j==i->name+3) return gotit;
X			if (j[-1] == 0) return gotit;
X		}
X	}
X
X	getid;
X	for (i = mztab; i->name; i++)
X		if (strcmp(i->name, idvar) == 0) return gotit;
X
X	for (j = idvar; *j; j++) if (isupper(*j)) *j = tolower(*j);
X	for (i=mztab; i->name; i++)
X		if (strcmp(i->name, idvar) == 0) return gotit;
X
X	getid;
X	for (i=unittb; i->name; i++)
X		if (strcmp(i->name, idvar) == 0) return gotit;
X
X	if (idvar[strlen(idvar)-1] == 's') idvar[strlen(idvar)-1] = '\0';
X	for (i=unittb; i->name; i++)
X		if (strcmp(i->name, idvar) == 0) return gotit;
X
X	getid;
X	for (i = othertb; i->name; i++)
X		if (strcmp(i->name, idvar) == 0) return gotit;
X
X	getid;
X	if (strlen(idvar) == 1 && isalpha(*idvar)) {
X		if (isupper(*idvar)) *idvar = tolower(*idvar);
X		for (i = milzone; i->name; i++)
X			if (strcmp(i->name, idvar) == 0) return gotit;
X	}
X
X	return(ID);
X}
X
Xtime_t getdate(p, now) char *p; struct timeb *now;
X{
X#define mcheck(f)	if (f>1) err++
X	time_t monthadd();
X	int err;
X	struct tm *lt;
X	struct timeb ftz;
X
X	time_t sdate, tod;
X
X	lptr = p;
X	if (now == ((struct timeb *) NULL)) {
X		now = &ftz;
X		ftime(&ftz);
X	}
X	lt = localtime(&now->time);
X	year = lt->tm_year;
X	month = lt->tm_mon+1;
X	day = lt->tm_mday;
X	relsec = 0; relmonth = 0;
X	timeflag=zoneflag=dateflag=dayflag=relflag=0;
X	ourzone = now->timezone;
X	daylight = MAYBE;
X	hh = mm = ss = 0;
X	merid = 24;
X
X	if (err = yyparse()) return (-1);
X
X	mcheck(timeflag);
X	mcheck(zoneflag);
X	mcheck(dateflag);
X	mcheck(dayflag);
X
X	if (err) return (-1);
X
X	if (dateflag || timeflag || dayflag) {
X		sdate = dateconv(month,day,year,hh,mm,ss,merid,ourzone,daylight);
X		if (sdate < 0) return -1;
X	}
X	else {
X		sdate = now->time;
X		if (relflag == 0)
X			sdate -= (lt->tm_sec + lt->tm_min*60 +
X				lt->tm_hour*(60L*60L));
X	}
X
X	sdate += relsec;
X	sdate += monthadd(sdate, relmonth);
X
X	if (dayflag && !dateflag) {
X		tod = dayconv(dayord, dayreq, sdate);
X		sdate += tod;
X	}
X
X	return sdate;
X}
X
Xyyerror(s) char *s;
X{}
*-*-END-of-src/getdate.y-*-*
echo x - src/header.c
sed 's/^X//' >src/header.c <<'*-*-END-of-src/header.c-*-*'
X/*
X * header.c - header functions plus some other goodies
X */
X
X#ifndef lint
Xstatic char	*SccsId = "@(#)header.c	2.28	8/28/84";
X#endif !lint
X
X#include <stdio.h>
X#include <sys/types.h>
X#include "defs.h"
X#include "header.h"
X
Xextern char	bfr[], *FULLSYSNAME, *index();
Xextern char	*strcpy(), *arpadate(), *ctime();
Xextern time_t defexp;
X
Xchar *hfgets();
X
Xchar *news_version = NEWS_VERSION;
X
X
X/*
X * Read header from file fp into *hp.  If wholething is FALSE,
X * it's an incremental read, otherwise start from scratch.
X * Return (FILE *) if header okay, else NULL.
X */
XFILE *
Xhread(hp, fp, wholething)
Xregister struct hbuf *hp;
XFILE *fp;
Xint wholething;
X{
X	register int	len;
X	register int	i;
X
X	if (wholething) {
X		for(i=0;i<NUNREC;i++)
X			if (hp->unrec[i] != NULL)
X				free(hp->unrec[i]);
X			else
X				break;
X		bclear(hp, sizeof (*hp));
X	}
X
X
X	/* Check that it's a B news style header. */
X	if (((hfgets(bfr, PATHLEN, fp) != NULL &&
X	    *bfr >= 'A' && *bfr <= 'Z') && index(bfr, ':')))
X		if (frmread(fp, hp))
X			goto strip;
X
X	/* It's not.  Try A news (begins with PROTO). */
X	if (*bfr != PROTO)
X		return(NULL);
X
X	/* Read in an A news format article. */
X	strncpy(hp->oident, &(bfr[1]), NAMELEN);	/* file name */
X	if (!nstrip(hp->oident))
X		return(NULL);
X	/* Newsgroup List */
X	if (hfgets(hp->nbuf, BUFLEN, fp) == NULL || !nstrip(hp->nbuf))
X		return(NULL);
X	ngcat(hp->nbuf);
X	/* source path */
X	if (hfgets(hp->path, PATHLEN, fp) == NULL || !nstrip(hp->path))
X		return(NULL);
X	/* date */
X	if (hfgets(hp->subdate, DATELEN, fp) == NULL || !nstrip(hp->subdate))
X		return(NULL);
X	/* title */
X	if (hfgets(hp->title, BUFLEN, fp) == NULL || !nstrip(hp->title))
X		return(NULL);
X
Xstrip:	/* strip off sys! from front of path. */
X	strcpy(bfr, FULLSYSNAME);
X	if (strncmp(bfr, hp->path, (len = strlen(bfr))) == 0
X	&& index(NETCHRS, hp->path[len]))
X		strcpy(hp->path, &(hp->path[len+1]));
X	lcase(hp->nbuf);
X
X	/* Intuit the From: line if only a path was given. */
X	if (wholething && hp->from[0] == '\0')
X		intuitfrom(hp);
X
X	/* Get old and new style message ID's. */
X	if (wholething)
X		fixid(hp);
X
X	return(fp);
X}
X
X
X/*
X * Get header info from mail-format file.
X * Return non-zero on success.
X */
X#include <ctype.h>
X#define FROM 		1
X#define NEWSGROUP 	2
X#define TITLE 		3
X#define SUBMIT		4
X#define RECEIVE		5
X#define EXPIRE		6
X#define ARTICLEID	7
X#define MESSAGEID	8
X#define REPLYTO		9
X#define FOLLOWID	10
X#define CONTROL		11
X#define SENDER		12
X#define FOLLOWTO	13
X#define PATH		14
X#define POSTVERSION	15
X#define RELAYVERSION	16
X#define DISTRIBUTION	17
X#define ORGANIZATION	18
X#define NUMLINES	19
X#define KEYWORDS	20
X#define APPROVED	21
X#define NFID		22
X#define NFFROM		23
X#define OTHER		99
X
Xchar *malloc();
X
Xfrmread(fp, hp)
Xregister FILE *fp;
Xregister struct hbuf *hp;
X{
X	int	unreccnt = 0;
X	register int	i;
X	long	curpos;
X	int hdrlineno = 0;
X
X	i = type(bfr);
X	do {
X		curpos = ftell(fp);
X		hdrlineno++;
X		switch (i) {
X		case PATH:
X			getfield(hp->path);
X			break;
X		case FROM:
X			getfield(hp->from);
X			break;
X		case NEWSGROUP:
X			getfield(hp->nbuf);
X			break;
X		case TITLE:
X			getfield(hp->title);
X			break;
X		case SUBMIT:
X			getfield(hp->subdate);
X			break;
X		case RECEIVE:
X			getfield(hp->recdate);
X			break;
X		case EXPIRE:
X			getfield(hp->expdate);
X			break;
X		case ARTICLEID:
X			getfield(hp->oident);
X			break;
X		case MESSAGEID:
X			getfield(hp->ident);
X			break;
X		case REPLYTO:
X			getfield(hp->replyto);
X			break;
X		case FOLLOWID:
X			getfield(hp->followid);
X			break;
X		case SENDER:
X			getfield(hp->sender);
X			break;
X		case FOLLOWTO:
X			getfield(hp->followto);
X			break;
X		case CONTROL:
X			getfield(hp->ctlmsg);
X			break;
X		case POSTVERSION:
X			getfield(hp->postversion);
X			break;
X		case DISTRIBUTION:
X			getfield(hp->distribution);
X			break;
X		case ORGANIZATION:
X			getfield(hp->organization);
X			break;
X		case NUMLINES:
X			getfield(hp->numlines);
X			hp->intnumlines = atoi(hp->numlines);
X			break;
X		case KEYWORDS:
X			getfield(hp->keywords);
X			break;
X		case APPROVED:
X			getfield(hp->approved);
X			break;
X		case NFID:
X			getfield(hp->nf_id);
X			break;
X		case NFFROM:
X			getfield(hp->nf_from);
X			break;
X		case RELAYVERSION:
X			/*
X			 * Only believe a relay version if it's the first
X			 * line, otherwise it probably got passed through
X			 * by some old neighbor.
X			 */
X			if (hdrlineno == 1) {
X				getfield(hp->relayversion);
X			}
X			break;
X		case OTHER:
X			if (unreccnt < NUNREC) {
X				if ((hp->unrec[unreccnt] = malloc((unsigned)(strlen(bfr) + 1))) != NULL ) {
X					strcpy(hp->unrec[unreccnt], bfr);
X					unreccnt++;
X				} else
X					xerror("frmread: out of memory");
X			}
X			break;
X		}
X	} while ((i = type(hfgets(bfr, LBUFLEN, fp))) > 0);
X
X	if (*bfr != '\n')
X		fseek(fp, curpos, 0);
X	if ((hp->from[0] || hp->path[0]) && hp->subdate[0] && (hp->ident[0] || hp->oident[0]))
X		return TRUE;
X	return FALSE;
X}
X
X/*
X * There was no From: line in the message (because it was generated by
X * an old news program).  Guess what it should have been and create it.
X */
Xintuitfrom(hp)
Xregister struct hbuf *hp;
X{
X	char *tp;
X	char *user, *host, *fullname;
X	char *tailpath(), *rindex();
X	char *at, *dot;
X
X	tp = tailpath(hp);
X	user = rindex(tp, '!');
X	if (user == NULL)
X		user = tp;
X	else
X		*user++ = '\0';
X
X	/* Check for an existing Internet address on the end. */
X	at = index(user, '@');
X	if (at) {
X		dot = index(at, '.');
X		if (dot) {
X			strcpy(hp->from, user);
X			return;
X		}
X		/* @ signs are illegal except for the biggie, so */
X		*at = '%';
X	}
X
X	if (tp[0] == '.')
X		host = index(tp, '!') + 1;
X	else if (user == tp)
X		host = FULLSYSNAME;
X	else
X		host = tp;
X
X	tp = index(host, '@');
X	if (tp != NULL)
X		*tp = 0;
X	sprintf(hp->from, "%s@%s%s", user, host, MYDOMAIN);
X
X	fullname = index(hp->path, '(');
X	if (fullname != NULL) {
X		fullname--;
X		strcat(hp->from, fullname);
X		*fullname = 0;
X	}
X}
X
X/*
X * If the message has only one of ident/oident, guess what
X * the other one should be and fill them both in.
X */
Xfixid(hp)
Xregister struct hbuf *hp;
X{
X	char lbuf[100];
X	register char *p;
X#ifdef OLD
X	register char *q;
X#endif OLD
X
X	if (hp->ident[0] == '\0' && hp->oident[0] != '\0') {
X		strcpy(lbuf, hp->oident);
X		p = index(lbuf, '.');
X		if (p == NULL) {
X			strcpy(hp->ident, hp->oident);
X			return;
X		}
X		*p++ = '\0';
X		/*
X		 * It may seem strange that we hardwire ".UUCP" in
X		 * here instead of MYDOMAIN.  However, we are trying
X		 * to guess what the domain was on the posting system,
X		 * not the local system.  Since we don't really know
X		 * what the posting system does, we just go with the
X		 * majority - almost everyone will be a .UUCP if they
X		 * didn't fill in their Message-ID.
X		 */
X		sprintf(hp->ident, "<%s@%s%s>", p, lbuf, ".UUCP");
X	}
X
X#ifdef OLD
X	if (hp->oident[0] == '\0' && hp->ident[0] != '\0') {
X		strcpy(lbuf, hp->ident);
X		p = index(lbuf, '@');
X		if (p == 0) {
X			strcpy(hp->oident, hp->ident);
X			return;
X		}
X		*p++ = '\0';
X		q = index(p, '.');
X		if (q == NULL)
X			q = index(p, '>');
X		if (q)
X			*q++ = '\0';
X		p[SNLN] = '\0';
X		sprintf(hp->oident, "%s.%s", p, lbuf+1);
X	}
X#endif
X}
X
X/*
X * Get the given field of a header (char * parm) from bfr, but only
X * if there's something actually there (after the colon).  Don't
X * bother if we already have an entry for this field.
X */
Xgetfield(hpfield)
Xchar	*hpfield;
X{
X	register char	*ptr;
X
X	if (hpfield[0])
X		return;
X	for (ptr = index(bfr, ':'); isspace(*++ptr); )
X		;
X	if (*ptr != '\0') {
X		strncpy(hpfield, ptr, BUFLEN);
X		nstrip(hpfield);
X	}
X	return;
X}
X
X
X#define its(type) (prefix(ptr, type))
Xtype(ptr)
Xregister char	*ptr;
X{
X	register char	*colon, *space;
X
X	if (ptr == NULL)
X		return FALSE;
X	if (!isalpha(*ptr) && strncmp(ptr, "From ", 5))
X		return FALSE;
X	colon = index(ptr, ':');
X	space = index(ptr, ' ');
X	if (!colon || space && space < colon)
X		return FALSE;
X	if (its("From: "))
X		if (index(ptr, '@') && !index(ptr, '!'))
X			return FROM;
X		else
X			return PATH;
X	if (its("Path: "))
X		return PATH;
X	if (its("Newsgroups: "))
X		return NEWSGROUP;
X	if (its("Subject: ") || its("Title: "))
X		return TITLE;
X	if (its("Posted: ") || its("Date: "))
X		return SUBMIT;
X	if (its("Date-Received: ") || its("Received: "))
X		return RECEIVE;
X	if (its("Expires: "))
X		return EXPIRE;
X	if (its("Article-I.D.: "))
X		return ARTICLEID;
X	if (its("Message-ID: "))
X		return MESSAGEID;
X	if (its("Reply-To: "))
X		return REPLYTO;
X	if (its("References: "))
X		return FOLLOWID;
X	if (its("Control: "))
X		return CONTROL;
X	if (its("Sender: "))
X		return SENDER;
X	if (its("Followup-To: "))
X		return FOLLOWTO;
X	if (its("Posting-Version: "))
X		return POSTVERSION;
X	if (its("Relay-Version: "))
X		return RELAYVERSION;
X	if (its("Distribution: "))
X		return DISTRIBUTION;
X	if (its("Organization: "))
X		return ORGANIZATION;
X	if (its("Lines: "))
X		return NUMLINES;
X	if (its("Keywords: "))
X		return KEYWORDS;
X	if (its("Approved: "))
X		return APPROVED;
X	if (its("Nf-ID: "))
X		return NFID;
X	if (its("Nf-From: "))
X		return NFFROM;
X	return OTHER;
X}
X
X/*
X * Generate the current version of the news software.
X */
Xchar *
Xgenversion()
X{
X	static char retbuf[32];
X	char rb[100];
X	register char *t;
X
X	strcpy(rb, news_version);
X	while (t = index(rb, '\t'))
X		*t = ' ';
X	/* This is B news, so we say "B", the version, and the date. */
X	sprintf(retbuf, "version %s; site %s%s", rb, FULLSYSNAME, MYDOMAIN);
X	return retbuf;
X}
X
X/*
X * Write header at 'hp' on stream 'fp' in B format.  This goes out to
X * some other system.
X */
Xhwrite(hp, fp)
Xregister struct hbuf *hp;
Xregister FILE *fp;
X{
X	ihwrite(hp, fp, 0);
X}
X
X
X/*
X * Same as above, except include receival date for local usage and
X * an extra \n for looks.
X */
Xlhwrite(hp, fp)
Xregister struct hbuf *hp;
Xregister FILE *fp;
X{
X	ihwrite(hp, fp, 1);
X}
X
X
X/*
X * Write header at 'hp' on stream 'fp' in B+ format.  Include received date
X * if wr is 1.  Leave off sysname if wr is 2.
X */
Xihwrite(hp, fp, wr)
Xregister struct hbuf *hp;
Xregister FILE *fp;
Xint	wr;
X{
X	int iu;
X	time_t t;
X	time_t cgtdate();
X
X	fprintf(fp, "Relay-Version: %s\n", genversion());
X	if (*hp->postversion)
X		fprintf(fp, "Posting-Version: %s\n", hp->postversion);
X	/*
X	 * We're being tricky with Path/From because of upward compatibility
X	 * issues.  The new version considers From and Path to be separate.
X	 * The old one thinks they both mean "Path" but only believes the
X	 * first one it sees, so will ignore the second.
X	 */
X	if (prefix(hp->path, FULLSYSNAME))
X		fprintf(fp, "Path: %s\n", hp->path);
X	else
X		fprintf(fp, "Path: %s!%s\n", FULLSYSNAME, hp->path);
X	if (hp->from[0])
X		fprintf(fp, "From: %s\n", hp->from);
X
X	strcpy(bfr, hp->nbuf);
X	ngdel(bfr);
X	fprintf(fp, "Newsgroups: %s\n", bfr);
X	fprintf(fp, "Subject: %s\n", hp->title);
X	fixid(hp);
X	fprintf(fp, "Message-ID: %s\n", hp->ident);
X	t = cgtdate(hp->subdate);
X	fprintf(fp, "Date: %s\n", arpadate(&t));
X#ifdef OLD
X	fprintf(fp, "Article-I.D.: %s\n", hp->oident);
X	fprintf(fp, "Posted: %s", ctime(&t));
X#endif
X	if (wr == 1)
X		fprintf(fp, "Date-Received: %s\n", hp->recdate);
X	if (*hp->expdate)
X		fprintf(fp, "Expires: %s\n", hp->expdate);
X	if (*hp->followid)
X		fprintf(fp, "References: %s\n", hp->followid);
X	if (*hp->ctlmsg)
X		fprintf(fp, "Control: %s\n", hp->ctlmsg);
X	if (*hp->sender)
X		fprintf(fp, "Sender: %s\n", hp->sender);
X	if (*hp->replyto)
X		fprintf(fp, "Reply-To: %s\n", hp->replyto);
X	if (*hp->followto)
X		fprintf(fp, "Followup-To: %s\n", hp->followto);
X	if (*hp->distribution)
X		fprintf(fp, "Distribution: %s\n", hp->distribution);
X	if (*hp->organization)
X		fprintf(fp, "Organization: %s\n", hp->organization);
X	if (*hp->numlines)
X		fprintf(fp, "Lines: %s\n", hp->numlines);
X	if (*hp->keywords)
X		fprintf(fp, "Keywords: %s\n", hp->keywords);
X	if (*hp->approved)
X		fprintf(fp, "Approved: %s\n", hp->approved);
X	if (*hp->nf_id)
X		fprintf(fp, "Nf-ID: %s\n", hp->nf_id);
X	if (*hp->nf_from)
X		fprintf(fp, "Nf-From: %s\n", hp->nf_from);
X	for (iu = 0; iu < NUNREC; iu++) {
X		if (hp->unrec[iu])
X			fprintf(fp, "%s", &hp->unrec[iu][0]);
X	}
X	putc('\n', fp);
X}
X
X
X/*
X * Set nc bytes, starting at cp, to zero.
X */
Xbclear(cp, nc)
Xregister char	*cp;
Xregister int	nc;
X{
X	while (nc--)
X		*cp++ = 0;
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	cp = fgets(buf, len, fp);
X	if (cp == NULL)
X		return NULL;
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 if (tp == (cp+1))
X		return(cp);	/* Don't look for continuation of blank lines */
X	else
X		*--tp = '\0';	/* clobber newline */
X
X	while ((c = getc(fp)) == ' ' || c == '\t') {	/* for each cont line */
X		/* Continuation line. */
X		while ((c = getc(fp)) == ' ' || c == '\t')	/* skip white space */
X			;
X 		if (c == '\n')	/* oops, it's really a blank line (I hope) */
X 			break;
X		if (tp-cp < len) {*tp++ = ' '; *tp++ = c;}
X		while ((c = getc(fp)) != '\n' && c != EOF)
X			if (tp-cp < len) *tp++ = c;
X	}
X	*tp++ = '\n';
X	*tp++ = '\0';
X	if (c != EOF)
X		(void) ungetc(c, fp); /* push back first char of next header */
X	return cp;
X}
*-*-END-of-src/header.c-*-*
echo x - src/header.h
sed 's/^X//' >src/header.h <<'*-*-END-of-src/header.h-*-*'
X/*
X * header.h - Article header format
X */
X
X/*	@(#)header.h	2.14	8/7/84	*/
X
X#define NUNREC 50
X
X/* article header */
Xstruct	hbuf {
X	char	from[BUFLEN];		/* From:		*/
X	char	path[PATHLEN];		/* Path:		*/
X	char	nbuf[BUFLEN];		/* Newsgroups:		*/
X	char	title[BUFLEN];		/* Subject:		*/
X	char	oident[BUFLEN];		/* Article-I.D.:	*/
X	char	ident[BUFLEN];		/* Message-ID:		*/
X	char	replyto[BUFLEN];	/* Reply-To:		*/
X	char	followid[BUFLEN];	/* References:		*/
X	char	subdate[DATELEN];	/* Date: (submission)	*/
X	time_t	subtime;		/* subdate in secs	*/
X	char	recdate[DATELEN];	/* Date-Received:	*/
X	time_t	rectime;		/* recdate in secs	*/
X	char	expdate[DATELEN];	/* Expires:		*/
X	time_t	exptime;		/* expdate in secs	*/
X	char	ctlmsg[PATHLEN];	/* Control:		*/
X	char	sender[BUFLEN];		/* Sender:		*/
X	char	followto[BUFLEN];	/* Followup-to:		*/
X	char	postversion[BUFLEN];	/* Post-Version:	*/
X	char	relayversion[BUFLEN];	/* Relay-Version:	*/
X	char	distribution[BUFLEN];	/* Distribution:	*/
X	char	organization[BUFLEN];	/* Organization:	*/
X	char	numlines[8];		/* Lines:		*/
X	int	intnumlines;		/* Integer version	*/
X	char	keywords[BUFLEN];	/* Keywords:		*/
X	char	approved[BUFLEN];	/* Approved:		*/
X	char	nf_id[BUFLEN];		/* Nf-ID:		*/
X	char	nf_from[BUFLEN];	/* Nf-From:		*/
X	char	*unrec[NUNREC];		/* unrecognized lines	*/
X};
*-*-END-of-src/header.h-*-*
echo x - src/help
sed 's/^X//' >src/help <<'*-*-END-of-src/help-*-*'
XCommand		Meaning
X
Xy		Yes.  (Or just hit return.)  Prints this article and goes on.
Xn		No.  Goes on to next article without printing current one.
Xd		Digest.  Breaks a digest article up into seperate articles.
Xq		Quit.  Update .newsrc if -l or -x not used.
XU		Unsubscribe.  You won't be shown this newsgroup anymore.
Xc		Cancel an article you posted.
Xr		Reply.  Reply to article's author via mail.
Xf [title]	Submit a follow up article.
XN [newsgroup]	Go to next newsgroup or named newsgroup.
Xs [file]	Save.  Article is appended to file (default is "Articles").
Xs |program	Run program with article as standard input.
Xe		Erase.  Forget that an article was read.
Xh		Print verbose header.  Use H for extremely verbose header.
X!		Shell escape.
X<number>	Go to message #<number> in this newsgroup.
X-		Go back to last article.
Xb		Back up one article in the current group.
XK		Mark the rest of the articles in current group as read.
Xx		Exit.  Don't update .newsrc.
Xv		Version.  Print current news version number.
X
Xc, f, r, e, h, and s can be followed by -'s to refer to the previous article
*-*-END-of-src/help-*-*
echo x - src/iextern.c
sed 's/^X//' >src/iextern.c <<'*-*-END-of-src/iextern.c-*-*'
X/*
X * iextern - external definitions for inews.
X */
X
X#ifndef lint
Xstatic char	*SccsId = "@(#)iextern.c	2.11	8/28/84";
X#endif !lint
X
X#include "iparams.h"
X
Xint	uid, gid;			/* real user/group I.D. */
Xint	duid, dgid;			/* effective user/group I.D. */
Xint	sigtrap;			/* set if signal trapped */
Xint	savmask;			/* old umask */
Xint	mode;				/* mode of news program */
Xstruct hbuf header;			/* general-use header structure */
Xchar	bfr[LBUFLEN];			/* general-use scratch area */
Xchar	nbuf[LBUFLEN];			/* local newsgroup buffer */
Xchar	filename[NAMELEN];		/* general-use file name */
X
X#ifndef ROOTID
Xint	ROOTID;				/* special users id # */
X#endif
X
Xchar	*DFLTNG = "general";		/* default newsgroup */
XFILE	*infp;				/* input file-pointer */
XFILE	*actfp;				/* active newsgroups file pointer */
Xint	tty;				/* set if infp is a tty */
Xchar	*PARTIAL = "dead.article";	/* place to save partial news */
Xchar	*SHELL = "/bin/sh";		/* shell for inews to use	*/
Xint	defexp = FALSE;			/* set if def. expiration date used */
Xint	is_ctl;				/* true for a control message */
*-*-END-of-src/iextern.c-*-*
echo x - src/ifuncs.c
sed 's/^X//' >src/ifuncs.c <<'*-*-END-of-src/ifuncs.c-*-*'
X/*
X * ifuncs - functions used by inews.
X */
X
X#ifndef lint
Xstatic char	*SccsId = "@(#)ifuncs.c	2.33	9/3/84";
X#endif !lint
X
X#include "iparams.h"
X#include <errno.h>
X
X/*
X * Transmit this article to all interested systems.
X */
X
X#ifdef u370
Xstatic struct srec srec;
X#endif
X
Xstatic struct hbuf h, hh;
X
Xbroadcast()
X{
X	register char *nptr, *hptr;
X	register FILE *fp;
X#ifndef u370
X	struct srec srec;
X#endif
X	char sentbuf[BUFLEN];
X	int nsent = 0;
X
X	/* h is a local copy of the header we can scribble on */
X	fp = xfopen(ARTICLE, "r");
X	if (hread(&h, fp, TRUE) == NULL)
X		xerror("Cannot reread article");
X	fclose(fp);
X
X	strcpy(sentbuf, h.ident);
X	strcat(sentbuf, " sent to ");
X	nsent = 0;
X	/* break path into list of systems. */
X	hptr = nptr = h.path;
X	while (*hptr != '\0') {
X		if (index(NETCHRS, *hptr)) {
X			*hptr++ = '\0';
X			nptr = hptr;
X		} else
X			hptr++;
X	}
X	*nptr = '\0';
X
X	/* loop once per system. */
X	lock();
X	s_openr();
X	while (s_read(&srec)) {
X		if (strncmp(srec.s_name, FULLSYSNAME, SNLN) == 0)
X			continue;
X		hptr = h.path;
X		while (*hptr != '\0') {
X			if (strncmp(srec.s_name, hptr, SNLN) == 0)
X				goto contin;
X			while (*hptr++ != '\0')
X				;
X		}
X		if (ngmatch(h.nbuf, srec.s_nbuf)) {
X			if (h.distribution[0] == '\0' ||
X				ngmatch(h.distribution, srec.s_nbuf)) {
X				if (transmit(&srec, xfopen(ARTICLE, "r"), 1)) {
X					if (nsent)
X						strcat(sentbuf, ", ");
X					strcat(sentbuf, srec.s_name);
X					nsent++;
X				}
X			}
X		}
X	contin:;
X	}
X	if (nsent)
X		log(sentbuf);
X	s_close();
X	unlock();
X}
X
X/*
X * Transmit file to system.
X */
X#define PROC 0004
Xtransmit(sp, ifp, maynotify)
Xregister struct srec *sp;
Xregister FILE *ifp;
Xint maynotify;
X{
X	register FILE *ofp;
X	register int c;
X	register char *ptr;
X	char TRANS[BUFLEN];
X	char *argv[20];
X	register int pid;
X	extern char firstbufname[];
X
X/* A:	afmt: the other machine runs an A news, so we xmit in A format */
X	int afmt = (index(sp->s_flags, 'A') != NULL);
X/* B:	use B format (this is the default - don't use this letter elsewise). */
X/* F:	append name to file */
X	int appfile = (index(sp->s_flags, 'F') != NULL);
X/* L:	local: don't send the article unless it was generated locally */
X	int local = (index(sp->s_flags, 'L') != NULL);
X/* N:	notify: don't send the article, just tell him we have it */
X	int notify = maynotify && (index(sp->s_flags, 'N') != NULL);
X/* S:	noshell: don't fork a shell to execute the xmit command */
X	int noshell = (index(sp->s_flags, 'S') != NULL);
X/* U:	useexist: use the -c option to uux to use the existing copy */
X	int useexist = (index(sp->s_flags, 'U') != NULL);
X
X	if (local && mode == PROC) {
X		fclose(ifp);
X		return FALSE;
X	}
X#ifdef DEBUG
X	printf("Transmitting to '%s'\n", sp->s_name);
X#endif
X	if (!appfile && !useexist) {
X		if (hread(&hh, ifp, TRUE) == NULL) {
X			logerr("Bad header, not transmitting %s re %s to %s",
X				hh.ident, hh.title, sp->s_name);
X			fclose(ifp);
X			return FALSE;
X		}
X		if (hh.nbuf[0] == '\0') {
X			fprintf(stderr, "Article not subscribed to by %s\n", sp->s_name);
X			fclose(ifp);
X			return FALSE;
X		}
X		sprintf(TRANS, "%s/trXXXXXX", SPOOL);
X	}
X
X	if (notify) {
X		char oldid[50];
X		sprintf(hh.title, "ihave %s %s", hh.ident, FULLSYSNAME);
X		sprintf(hh.nbuf, "to.%s.ctl", sp->s_name);
X		strcpy(oldid, hh.ident);
X		getident(&hh);
X		log("tell %s about %s, notif. id %s",
X			sp->s_name, oldid, hh.ident);
X	}
X
X	if (appfile) {
X		if (firstbufname[0] == '\0') {
X			extern char histline[];
X			localize("junk");
X			savehist(histline);
X			xerror("No file name to xmit from");
X		}
X#ifdef IHCC
X		sprintf(TRANS, "%s/%s/%s", logdir(HOME), BATCHDIR, sp->s_xmit);
X		ofp = fopen(TRANS, "a");
X#else
X		ofp = fopen(sp->s_xmit, "a");
X#endif
X		if (ofp == NULL)
X			xerror("Cannot append to %s", sp->s_xmit);
X		fprintf(ofp, "%s\n", firstbufname);
X		fclose(ofp);
X		fclose(ifp);
X		return TRUE;
X	}
X	else
X#ifdef UXMIT
X	if (useexist) {
X		if (firstbufname[0] == '\0')
X			xerror("No file name to xmit from");
X		if (*sp->s_xmit == '\0')
X			sprintf(bfr, UXMIT, sp->s_name, firstbufname);
X		else
X			sprintf(bfr, sp->s_xmit, firstbufname);
X		fclose(ifp);
X	} else
X#endif
X	{
X		ofp = xfopen(mktemp(TRANS), "w");
X		if (afmt)
X			ohwrite(&hh, ofp);
X		else
X			hwrite(&hh, ofp);
X		if (!notify)
X			while ((c = getc(ifp)) != EOF)
X				putc(c, ofp);
X		fclose(ifp);
X		fclose(ofp);
X		if (*sp->s_xmit == '\0')
X			sprintf(bfr, DFTXMIT, sp->s_name, TRANS);
X		else
X			sprintf(bfr, "(%s) < %s", sp->s_xmit, TRANS);
X	}
X
X	/* At this point, the command to be executed is in bfr. */
X	if (noshell) {
X		if (pid = fork())
X			fwait(pid);
X		else {
X			close(0);
X			open(TRANS, 0);
X			if (*sp->s_xmit == '\0')
X				ptr = bfr;
X			else
X				ptr = sp->s_xmit;
X			for (pid = 0; pid < 19; pid++) {
X				while (isspace(*ptr))
X					*ptr++ = 0;
X				argv[pid] = ptr;
X				while (!isspace(*++ptr) && *ptr)
X					;
X				if (!*ptr)
X					break;
X			}
X			argv[++pid] = 0;
X			execv(argv[0], argv);
X			xerror("Can't execv %s", argv[0]);
X		}
X	} else
X		system(bfr);
X	if (!appfile && !useexist)
X		unlink(TRANS);
X	fclose(ifp);
X	return TRUE;
X}
X
Xtypedef struct {
X	char *dptr;
X	int dsize;
X} datum;
X
X/*
X * Return TRUE if we have seen this file before, else FALSE.
X */
Xhistory(hp)
Xstruct hbuf *hp;
X{
X#ifdef DBM
X	datum lhs, rhs;
X	datum fetch();
X#else !DBM
X	register FILE *hfp;
X#endif !DBM
X	register char *p;
X	char lcident[BUFLEN];
X
X#ifdef DEBUG
X	fprintf(stderr,"history(%s)\n", hp->ident);
X#endif
X	/*
X	 * Make the article ID case insensitive.
X	 */
X	strcpy(lcident, hp->ident);
X	p = lcident;
X	while (*++p)
X		if (isupper(*p))
X			*p = tolower(*p);
X
X	idlock(lcident);
X#ifdef DBM
X	dbminit(ARTFILE);
X	lhs.dptr = lcident;
X	lhs.dsize = strlen(lhs.dptr) + 1;
X	rhs = fetch(lhs);
X	if (rhs.dptr)
X		return(TRUE);
X#else
X	hfp = xfopen(ARTFILE, "r");
X	while (fgets(bfr, BUFLEN, hfp) != NULL) {
X		p = index(bfr, '\t');
X		if (p == NULL)
X			p = index(bfr, '\n');
X		if (p != NULL)	/* can happen if nulls in file */
X			*p = 0;
X		p = bfr;
X		while (*++p)
X			if (isupper(*p))
X				*p = tolower(*p);
X
X		if (strcmp(bfr, lcident)==0 ||
X				hp->oident[0] && strcmp(bfr, hp->oident)==0) {
X			fclose(hfp);
X			idunlock();
X#ifdef DEBUG
X			fprintf(stderr,"history returns true\n");
X#endif
X			return(TRUE);
X		}
X	}
X	fclose(hfp);
X#endif
X	addhist(hp->ident);
X	addhist("\t");
X#ifdef DEBUG
X	fprintf(stderr,"history returns false\n");
X#endif
X	return(FALSE);
X}
X
Xchar histline[256];	/* Assumed initially zero */
X
Xaddhist(msg)
Xchar *msg;
X{
X	strcat(histline, msg);
X}
X
Xsavehist(hline)
Xchar *hline;
X{
X	register FILE *hfp;
X	datum lhs, rhs;
X	long fpos;
X	register char *p;
X
X	hfp = xfopen(ARTFILE, "a");
X	fpos = ftell(hfp);
X	fprintf(hfp, "%s\n", hline);
X	fclose(hfp);
X#ifdef DBM
X	/* We assume that history has already been called, calling dbminit. */
X	p = index(hline, '\t');
X	if (p)
X		*p = 0;
X	p = hline;
X	while(*++p)
X		if(isupper(*p))
X			*p = tolower(*p);
X	lhs.dptr = hline;
X	lhs.dsize = strlen(lhs.dptr) + 1;
X	rhs.dptr = (char *) &fpos;
X	rhs.dsize = sizeof fpos;
X	store(lhs, rhs);
X#endif
X	hline[0] = 0;
X	idunlock();
X}
X
X/*
X * Save partial news.
X */
Xnewssave(fd, dummy)
XFILE *fd;
Xchar *dummy;
X{
X	register FILE *tofd, *fromfd;
X	char sfname[BUFLEN];
X	register int c;
X	time_t tim;
X
X#ifdef lint
X	c = *dummy;
X#endif lint
X
X	if (fd == NULL)
X		fromfd = xfopen(INFILE, "r");
X	else
X		fromfd = fd;
X	umask(savmask);
X	setgid(gid);
X	setuid(uid);
X
X	sprintf(sfname, "%s/%s", userhome, PARTIAL);
X	if ((tofd = fopen(sfname, "a")) == NULL)
X		xerror("Cannot save partial news");
X	time(&tim);
X	fprintf(tofd, "----- News saved at %s\n", arpadate(&tim));
X	while ((c = getc(fromfd)) != EOF)
X		putc(c, tofd);
X	fclose(fromfd);
X	fclose(tofd);
X	printf("News saved in %s\n", sfname);
X	xxit(0);
X}
X
X/*
X * Handle dates in header.
X */
X
Xdates(hp)
Xstruct hbuf *hp;
X{
X	time_t edt;
X
X	time(&hp->rectime);
X	strcpy(hp->recdate, arpadate(&hp->rectime));
X	nstrip(hp->recdate);
X	if (*hp->subdate) {
X		if (cgtdate(hp->subdate) < 0) {
X			xerror("Cannot parse submittal date '%s'", hp->subdate);
X		}
X	} else
X		strcpy(hp->subdate, hp->recdate);
X	if (*hp->expdate) {
X		if ((edt = cgtdate(hp->expdate)) < 0)
X			xerror("Cannot parse expiration date '%s'",hp->expdate);
X		nstrip(strcpy(hp->expdate, arpadate(&edt)));
X	} else {
X		defexp = TRUE;
X		/*
X		 * Default is now applied in expire.c
X		hp->exptime = hp->rectime + DFLTEXP;
X		nstrip(strcpy(hp->expdate, arpadate(&hp->exptime)));
X		*/
X	}
X}
X
Xchar lockname[80];
Xidlock(str)
Xchar *str;
X{
X	register int i;
X	char tempname[80];
X	time_t now;
X	struct stat sbuf;
X#ifdef	VMS
X	int fd;
X
X	sprintf(lockname, "/tmp/%s.l.1", str);
X	if ((fd = creat(lockname, 0444)) < 0) {
X#else	!VMS
X	sprintf(tempname, "/tmp/LTMP.%d", getpid());
X	sprintf(lockname, "/tmp/L%s", str);
X#ifdef FOURTEENMAX
X	lockname[5 /* /tmp/ */ + 14] = '\0';
X#endif
X	close(creat(tempname, 0666));
X	while (link(tempname, lockname)) {
X#endif	!VMS
X		(void) time(&now);
X		i = stat(lockname, &sbuf);
X		if (i < 0) {
X			xerror("Directory permission problem in /tmp");
X		}
X		if (sbuf.st_mtime + 10*60 < now) {
X			unlink(lockname);
X			logerr("Article %s locked up", str);
X			continue;
X		}
X		log("waiting on lock for %s", lockname);
X		sleep((unsigned)60);
X	}
X#ifdef VMS
X	close(fd);
X#else
X	unlink(tempname);
X#endif
X	unlink(tempname);
X}
X
Xidunlock()
X{
X	unlink(lockname);
X}
X
X/*
X * Put a unique name into header.ident.
X */
Xgetident(hp)
Xstruct hbuf *hp;
X{
X	long seqn;
X	register FILE *fp;
X
X	lock();
X	fp = xfopen(SEQFILE, "r");
X	fgets(bfr, BUFLEN, fp);
X	fclose(fp);
X	seqn = atol(bfr) + 1;
X#ifdef	VMS
X	unlink(SEQFILE);
X#endif	VMS
X	fp = xfopen(SEQFILE, "w");
X	fprintf(fp, "%ld\n", seqn);
X	fclose(fp);
X	unlock();
X	sprintf(hp->ident, "<%ld@%s%s>", seqn, FULLSYSNAME, MYDOMAIN);
X}
X
X/*
X * Check that header.nbuf contains only valid newsgroup names;
X * exit with error if not valid.
X *
X */
X
Xngfcheck(isproc)
Xint isproc;
X{
X	register char *s1, *s2;
X	register FILE *f;
X	register char *os1;
X	char tbuf[BUFLEN], ngcheck[AFSIZ];
X
X	f = xfopen(ACTIVE, "r");
X	s1 = ngcheck;
X	while (fgets(bfr, BUFLEN, f) != NULL) {
X		os1 = s1;
X		for(s2 = bfr; *s2 != '\0' && *s2 != ' ';) {
X			if (s1 >= &ngcheck[AFSIZ-2])
X				xerror("ACTIVE file too long");
X			*s1++ = *s2++;
X		}

rick@seismo.UUCP (Rick Adams) (09/08/84)

cat << 'E_O_F'
	This was received truncated at many places.
	---rick
E_O_F
if test ! -d src
then
	mkdir src
fi
echo x - src/getdate.y
sed 's/^X//' >src/getdate.y <<'*-*-END-of-src/getdate.y-*-*'
X%token ID MONTH DAY MERIDIAN NUMBER UNIT MUNIT SUNIT ZONE DAYZONE AGO
X%{
X	/* 	Steven M. Bellovin (unc!smb)			*/
X	/*	Dept. of Computer Science			*/
X	/*	University of North Carolina at Chapel Hill	*/
X	/*	@(#)getdate.y	2.6	4/20/84	*/
X
X#include <sys/types.h>
X#ifdef USG
Xstruct timeb
X{
X	time_t	time;
X	unsigned short millitm;
X	short	timezone;
X	short	dstflag;
X};
X#else
X#include <sys/timeb.h>
X#endif
X#include <ctype.h>
X#include <time.h>
X
X#define	NULL	0
X#define daysec (24L*60L*60L)
X	static int timeflag, zoneflag, dateflag, dayflag, relflag;
X	static time_t relsec, relmonth;
X	static int hh, mm, ss, merid, daylight;
X	static int dayord, dayreq;
X	static int month, day, year;
X	static int ourzone;
X#define AM 1
X#define PM 2
X#define DAYLIGHT 1
X#define STANDARD 2
X#define MAYBE    3
X%}
X
X%%
Xtimedate: 		/* empty */
X	| timedate item;
X
Xitem:	tspec =
X		{timeflag++;}
X	| zone =
X		{zoneflag++;}
X	| dtspec =
X		{dateflag++;}
X	| dyspec =
X		{dayflag++;}
X	| rspec =
X		{relflag++;}
X	| nspec;
X
Xnspec:	NUMBER =
X		{if (timeflag && dateflag && !relflag) year = $1;
X		else {timeflag++;hh = $1/100;mm = $1%100;ss = 0;merid = 24;}};
X
Xtspec:	NUMBER MERIDIAN =
X		{hh = $1; mm = 0; ss = 0; merid = $2;}
X	| NUMBER ':' NUMBER =
X		{hh = $1; mm = $3; merid = 24;}
X	| NUMBER ':' NUMBER MERIDIAN =
X		{hh = $1; mm = $3; merid = $4;}
X	| NUMBER ':' NUMBER ':' NUMBER =
X		{hh = $1; mm = $3; ss = $5; merid = 24;}
X	| NUMBER ':' NUMBER ':' NUMBER MERIDIAN =
X		{hh = $1; mm = $3; ss = $5; merid = $6;};
X
Xzone:	ZONE =
X		{ourzone = $1; daylight = STANDARD;}
X	| DAYZONE =
X		{ourzone = $1; daylight = DAYLIGHT;};
X
Xdyspec:	DAY =
X		{dayord = 1; dayreq = $1;}
X	| DAY ',' =
X		{dayord = 1; dayreq = $1;}
X	| NUMBER DAY =
X		{dayord = $1; dayreq = $2;};
X
Xdtspec:	NUMBER '/' NUMBER =
X		{month = $1; day = $3;}
X	| NUMBER '/' NUMBER '/' NUMBER =
X		{month = $1; day = $3; year = $5;}
X	| MONTH NUMBER =
X		{month = $1; day = $2;}
X	| MONTH NUMBER ',' NUMBER =
X		{month = $1; day = $2; year = $4;}
X	| NUMBER MONTH =
X		{month = $2; day = $1;}
X	| NUMBER MONTH NUMBER =
X		{month = $2; day = $1; year = $3;};
X
X
Xrspec:	NUMBER UNIT =
X		{relsec +=  60L * $1 * $2;}
X	| NUMBER MUNIT =
X		{relmonth += $1 * $2;}
X	| NUMBER SUNIT =
X		{relsec += $1;}
X	| UNIT =
X		{relsec +=  60L * $1;}
X	| MUNIT =
X		{relmonth += $1;}
X	| SUNIT =
X		{relsec++;}
X	| rspec AGO =
X		{relsec = -relsec; relmonth = -relmonth;};
X%%
X
Xstatic int mdays[12] =
X	{31, 0, 31,  30, 31, 30,  31, 31, 30,  31, 30, 31};
X#define epoch 1970
X
Xextern struct tm *localtime();
Xtime_t dateconv(mm, dd, yy, h, m, s, mer, zone, dayflag)
Xint mm, dd, yy, h, m, s, mer, zone, dayflag;
X{
X	time_t tod, jdate;
X	register int i;
X	time_t timeconv();
X
X	if (yy < 0) yy = -yy;
X	if (yy < 100) yy += 1900;
X	mdays[1] = 28 + (yy%4 == 0);
X	if (yy < epoch || yy > 1999 || mm < 1 || mm > 12 ||
X		dd < 1 || dd > mdays[--mm]) return (-1);
X	jdate = dd-1;
X        for (i=0; i<mm; i++) jdate += mdays[i];
X	for (i = epoch; i < yy; i++) jdate += 365 + (i%4 == 0);
X	jdate *= daysec;
X	jdate += zone * 60L;
X	if ((tod = timeconv(h, m, s, mer)) < 0) return (-1);
X	jdate += tod;
X	if (dayflag==DAYLIGHT || (dayflag==MAYBE&&localtime(&jdate)->tm_isdst))
X		jdate += -1*60*60;
X	return (jdate);
X}
X
Xtime_t dayconv(ord, day, now) int ord, day; time_t now;
X{
X	register struct tm *loctime;
X	time_t tod;
X	time_t daylcorr();
X
X	tod = now;
X	loctime = localtime(&tod);
X	tod += daysec * ((day - loctime->tm_wday + 7) % 7);
X	tod += 7*daysec*(ord<=0?ord:ord-1);
X	return daylcorr(tod, now);
X}
X
Xtime_t timeconv(hh, mm, ss, mer) register int hh, mm, ss, mer;
X{
X	if (mm < 0 || mm > 59 || ss < 0 || ss > 59) return (-1);
X	switch (mer) {
X		case AM: if (hh < 1 || hh > 12) return(-1);
X			 return (60L * ((hh%12)*60L + mm)+ss);
X		case PM: if (hh < 1 || hh > 12) return(-1);
X			 return (60L * ((hh%12 +12)*60L + mm)+ss);
X		case 24: if (hh < 0 || hh > 23) return (-1);
X			 return (60L * (hh*60L + mm)+ss);
X		default: return (-1);
X	}
X}
Xtime_t monthadd(sdate, relmonth) time_t sdate, relmonth;
X{
X	struct tm *ltime;
X	time_t dateconv();
X	time_t daylcorr();
X	int mm, yy;
X
X	if (relmonth == 0) return 0;
X	ltime = localtime(&sdate);
X	mm = 12*ltime->tm_year + ltime->tm_mon + relmonth;
X	yy = mm/12;
X	mm = mm%12 + 1;
X	return daylcorr(dateconv(mm, ltime->tm_mday, yy, ltime->tm_hour,
X		ltime->tm_min, ltime->tm_sec, 24, ourzone, MAYBE), sdate);
X}
X
Xtime_t daylcorr(future, now) time_t future, now;
X{
X	int fdayl, nowdayl;
X
X	nowdayl = (localtime(&now)->tm_hour+1) % 24;
X	fdayl = (localtime(&future)->tm_hour+1) % 24;
X	return (future-now) + 60L*60L*(nowdayl-fdayl);
X}
X
Xstatic char *lptr;
X
Xyylex()
X{
X	extern int yylval;
X	int sign;
X	register char c;
X	register char *p;
X	char idbuf[20];
X	int pcnt;
X
X	for (;;) {
X		while (isspace(*lptr)) lptr++;
X
X		if (isdigit(c = *lptr) || c == '-' || c == '+') {
X			if (c== '-' || c == '+') {
X				if (c=='-') sign = -1;
X				else sign = 1;
X				if (!isdigit(*++lptr)) {
X					/* yylval = sign; return (NUMBER); */
X					return yylex();	/* skip the '-' sign */
X				}
X			} else sign = 1;
X			yylval = 0;
X			while (isdigit(c = *lptr++)) yylval = 10*yylval + c - '0';
X			yylval *= sign;
X			lptr--;
X			return (NUMBER);
X
X		} else if (isalpha(c)) {
X			p = idbuf;
X			while (isalpha(c = *lptr++) || c=='.')
X				*p++ = c;
X			*p = '\0';
X			lptr--;
X			return (lookup(idbuf));
X		}
X
X		else if (c == '(') {
X			pcnt = 0;
X			do {
X				c = *lptr++;
X				if (c == '\0') return(c);
X				else if (c == '(') pcnt++;
X				else if (c == ')') pcnt--;
X			} while (pcnt > 0);
X		}
X
X		else return (*lptr++);
X	}
X}
X
Xstruct table {
X	char *name;
X	int type, value;
X};
X
Xstruct table mdtab[] = {
X	{"January", MONTH, 1},
X	{"February", MONTH, 2},
X	{"March", MONTH, 3},
X	{"April", MONTH, 4},
X	{"May", MONTH, 5},
X	{"June", MONTH, 6},
X	{"July", MONTH, 7},
X	{"August", MONTH, 8},
X	{"September", MONTH, 9},
X	{"Sept", MONTH, 9},
X	{"October", MONTH, 10},
X	{"November", MONTH, 11},
X	{"December", MONTH, 12},
X
X	{"Sunday", DAY, 0},
X	{"Monday", DAY, 1},
X	{"Tuesday", DAY, 2},
X	{"Tues", DAY, 2},
X	{"Wednesday", DAY, 3},
X	{"Wednes", DAY, 3},
X	{"Thursday", DAY, 4},
X	{"Thur", DAY, 4},
X	{"Thurs", DAY, 4},
X	{"Friday", DAY, 5},
X	{"Saturday", DAY, 6},
X	{0, 0, 0}};
X
X#define HRS *60
X#define HALFHR 30
Xstruct table mztab[] = {
X	{"a.m.", MERIDIAN, AM},
X	{"am", MERIDIAN, AM},
X	{"p.m.", MERIDIAN, PM},
X	{"pm", MERIDIAN, PM},
X	{"nst", ZONE, 3 HRS + HALFHR},		/* Newfoundland */
X	{"n.s.t.", ZONE, 3 HRS + HALFHR},
X	{"ast", ZONE, 4 HRS},		/* Atlantic */
X	{"a.s.t.", ZONE, 4 HRS},
X	{"adt", DAYZONE, 4 HRS},
X	{"a.d.t.", DAYZONE, 4 HRS},
X	{"est", ZONE, 5 HRS},		/* Eastern */
X	{"e.s.t.", ZONE, 5 HRS},
X	{"edt", DAYZONE, 5 HRS},
X	{"e.d.t.", DAYZONE, 5 HRS},
X	{"cst", ZONE, 6 HRS},		/* Central */
X	{"c.s.t.", ZONE, 6 HRS},
X	{"cdt", DAYZONE, 6 HRS},
X	{"c.d.t.", DAYZONE, 6 HRS},
X	{"mst", ZONE, 7 HRS},		/* Mountain */
X	{"m.s.t.", ZONE, 7 HRS},
X	{"mdt", DAYZONE, 7 HRS},
X	{"m.d.t.", DAYZONE, 7 HRS},
X	{"pst", ZONE, 8 HRS},		/* Pacific */
X	{"p.s.t.", ZONE, 8 HRS},
X	{"pdt", DAYZONE, 8 HRS},
X	{"p.d.t.", DAYZONE, 8 HRS},
X	{"yst", ZONE, 9 HRS},		/* Yukon */
X	{"y.s.t.", ZONE, 9 HRS},
X	{"ydt", DAYZONE, 9 HRS},
X	{"y.d.t.", DAYZONE, 9 HRS},
X	{"hst", ZONE, 10 HRS},		/* Hawaii */
X	{"h.s.t.", ZONE, 10 HRS},
X	{"hdt", DAYZONE, 10 HRS},
X	{"h.d.t.", DAYZONE, 10 HRS},
X	{"bst", ZONE, 11 HRS},		/* Bering */
X	{"b.s.t.", ZONE, 11 HRS},
X	{"bdt", DAYZONE, 11 HRS},
X	{"b.d.t.", DAYZONE, 11 HRS},
X
X	{"gmt", ZONE, 0 HRS},
X	{"g.m.t.", ZONE, 0 HRS},
X
X	{"aest", ZONE, -10 HRS},	/* Australian Eastern Time */
X	{"a.e.s.t.", ZONE, -10 HRS},
X	{"aesst", DAYZONE, -10 HRS},	/* Australian Eastern Summer Time */
X	{"a.e.s.s.t.", DAYZONE, -10 HRS},
X	{"acst", ZONE, -(9 HRS + HALFHR)},	/* Australian Central Time */
X	{"a.c.s.t.", ZONE, -(9 HRS + HALFHR)},
X	{"acsst", DAYZONE, -(9 HRS + HALFHR)},	/* Australian Central Summer */
X	{"a.c.s.s.t.", DAYZONE, -(9 HRS + HALFHR)},
X	{"awst", ZONE, -8 HRS},		/* Australian Western Time */
X	{"a.w.s.t.", ZONE, -8 HRS},	/* (no daylight time there, I'm told */
X	{0, 0, 0}};
X
Xstruct table unittb[] = {
X	{"year", MUNIT, 12},
X	{"month", MUNIT, 1},
X	{"fortnight", UNIT, 14*24*60},
X	{"week", UNIT, 7*24*60},
X	{"day", UNIT, 1*24*60},
X	{"hour", UNIT, 60},
X	{"minute", UNIT, 1},
X	{"min", UNIT, 1},
X	{"second", SUNIT, 1},
X	{"sec", SUNIT, 1},
X	{0, 0, 0}};
X
Xstruct table othertb[] = {
X	{"tomorrow", UNIT, 1*24*60},
X	{"yesterday", UNIT, -1*24*60},
X	{"today", UNIT, 0},
X	{"now", UNIT, 0},
X	{"last", NUMBER, -1},
X	{"this", UNIT, 0},
X	{"next", NUMBER, 2},
X	{"first", NUMBER, 1},
X	/* {"second", NUMBER, 2}, */
X	{"third", NUMBER, 3},
X	{"fourth", NUMBER, 4},
X	{"fifth", NUMBER, 5},
X	{"sixth", NUMBER, 6},
X	{"seventh", NUMBER, 7},
X	{"eigth", NUMBER, 8},
X	{"ninth", NUMBER, 9},
X	{"tenth", NUMBER, 10},
X	{"eleventh", NUMBER, 11},
X	{"twelfth", NUMBER, 12},
X	{"ago", AGO, 1},
X	{0, 0, 0}};
X
Xstruct table milzone[] = {
X	{"a", ZONE, 1 HRS},
X	{"b", ZONE, 2 HRS},
X	{"c", ZONE, 3 HRS},
X	{"d", ZONE, 4 HRS},
X	{"e", ZONE, 5 HRS},
X	{"f", ZONE, 6 HRS},
X	{"g", ZONE, 7 HRS},
X	{"h", ZONE, 8 HRS},
X	{"i", ZONE, 9 HRS},
X	{"k", ZONE, 10 HRS},
X	{"l", ZONE, 11 HRS},
X	{"m", ZONE, 12 HRS},
X	{"n", ZONE, -1 HRS},
X	{"o", ZONE, -2 HRS},
X	{"p", ZONE, -3 HRS},
X	{"q", ZONE, -4 HRS},
X	{"r", ZONE, -5 HRS},
X	{"s", ZONE, -6 HRS},
X	{"t", ZONE, -7 HRS},
X	{"u", ZONE, -8 HRS},
X	{"v", ZONE, -9 HRS},
X	{"w", ZONE, -10 HRS},
X	{"x", ZONE, -11 HRS},
X	{"y", ZONE, -12 HRS},
X	{"z", ZONE, 0 HRS},
X	{0, 0, 0}};
X
Xlookup(id) char *id;
X{
X#define gotit (yylval=i->value,  i->type)
X#define getid for(j=idvar, k=id; *j++ = *k++; )
X
X	char idvar[20];
X	register char *j, *k;
X	register struct table *i;
X	int abbrev;
X
X	getid;
X	if (strlen(idvar) == 3) abbrev = 1;
X	else if (strlen(idvar) == 4 && idvar[3] == '.') {
X		abbrev = 1;
X		idvar[3] = '\0';
X	}
X	else abbrev = 0;
X
X	if (islower(*idvar)) *idvar = toupper(*idvar);
X
X	for (i = mdtab; i->name; i++) {
X		k = idvar;
X		for (j = i->name; *j++ == *k++;) {
X			if (abbrev && j==i->name+3) return gotit;
X			if (j[-1] == 0) return gotit;
X		}
X	}
X
X	getid;
X	for (i = mztab; i->name; i++)
X		if (strcmp(i->name, idvar) == 0) return gotit;
X
X	for (j = idvar; *j; j++) if (isupper(*j)) *j = tolower(*j);
X	for (i=mztab; i->name; i++)
X		if (strcmp(i->name, idvar) == 0) return gotit;
X
X	getid;
X	for (i=unittb; i->name; i++)
X		if (strcmp(i->name, idvar) == 0) return gotit;
X
X	if (idvar[strlen(idvar)-1] == 's') idvar[strlen(idvar)-1] = '\0';
X	for (i=unittb; i->name; i++)
X		if (strcmp(i->name, idvar) == 0) return gotit;
X
X	getid;
X	for (i = othertb; i->name; i++)
X		if (strcmp(i->name, idvar) == 0) return gotit;
X
X	getid;
X	if (strlen(idvar) == 1 && isalpha(*idvar)) {
X		if (isupper(*idvar)) *idvar = tolower(*idvar);
X		for (i = milzone; i->name; i++)
X			if (strcmp(i->name, idvar) == 0) return gotit;
X	}
X
X	return(ID);
X}
X
Xtime_t getdate(p, now) char *p; struct timeb *now;
X{
X#define mcheck(f)	if (f>1) err++
X	time_t monthadd();
X	int err;
X	struct tm *lt;
X	struct timeb ftz;
X
X	time_t sdate, tod;
X
X	lptr = p;
X	if (now == ((struct timeb *) NULL)) {
X		now = &ftz;
X		ftime(&ftz);
X	}
X	lt = localtime(&now->time);
X	year = lt->tm_year;
X	month = lt->tm_mon+1;
X	day = lt->tm_mday;
X	relsec = 0; relmonth = 0;
X	timeflag=zoneflag=dateflag=dayflag=relflag=0;
X	ourzone = now->timezone;
X	daylight = MAYBE;
X	hh = mm = ss = 0;
X	merid = 24;
X
X	if (err = yyparse()) return (-1);
X
X	mcheck(timeflag);
X	mcheck(zoneflag);
X	mcheck(dateflag);
X	mcheck(dayflag);
X
X	if (err) return (-1);
X
X	if (dateflag || timeflag || dayflag) {
X		sdate = dateconv(month,day,year,hh,mm,ss,merid,ourzone,daylight);
X		if (sdate < 0) return -1;
X	}
X	else {
X		sdate = now->time;
X		if (relflag == 0)
X			sdate -= (lt->tm_sec + lt->tm_min*60 +
X				lt->tm_hour*(60L*60L));
X	}
X
X	sdate += relsec;
X	sdate += monthadd(sdate, relmonth);
X
X	if (dayflag && !dateflag) {
X		tod = dayconv(dayord, dayreq, sdate);
X		sdate += tod;
X	}
X
X	return sdate;
X}
X
Xyyerror(s) char *s;
X{}
*-*-END-of-src/getdate.y-*-*
echo x - src/header.c
sed 's/^X//' >src/header.c <<'*-*-END-of-src/header.c-*-*'
X/*
X * header.c - header functions plus some other goodies
X */
X
X#ifndef lint
Xstatic char	*SccsId = "@(#)header.c	2.28	8/28/84";
X#endif !lint
X
X#include <stdio.h>
X#include <sys/types.h>
X#include "defs.h"
X#include "header.h"
X
Xextern char	bfr[], *FULLSYSNAME, *index();
Xextern char	*strcpy(), *arpadate(), *ctime();
Xextern time_t defexp;
X
Xchar *hfgets();
X
Xchar *news_version = NEWS_VERSION;
X
X
X/*
X * Read header from file fp into *hp.  If wholething is FALSE,
X * it's an incremental read, otherwise start from scratch.
X * Return (FILE *) if header okay, else NULL.
X */
XFILE *
Xhread(hp, fp, wholething)
Xregister struct hbuf *hp;
XFILE *fp;
Xint wholething;
X{
X	register int	len;
X	register int	i;
X
X	if (wholething) {
X		for(i=0;i<NUNREC;i++)
X			if (hp->unrec[i] != NULL)
X				free(hp->unrec[i]);
X			else
X				break;
X		bclear(hp, sizeof (*hp));
X	}
X
X
X	/* Check that it's a B news style header. */
X	if (((hfgets(bfr, PATHLEN, fp) != NULL &&
X	    *bfr >= 'A' && *bfr <= 'Z') && index(bfr, ':')))
X		if (frmread(fp, hp))
X			goto strip;
X
X	/* It's not.  Try A news (begins with PROTO). */
X	if (*bfr != PROTO)
X		return(NULL);
X
X	/* Read in an A news format article. */
X	strncpy(hp->oident, &(bfr[1]), NAMELEN);	/* file name */
X	if (!nstrip(hp->oident))
X		return(NULL);
X	/* Newsgroup List */
X	if (hfgets(hp->nbuf, BUFLEN, fp) == NULL || !nstrip(hp->nbuf))
X		return(NULL);
X	ngcat(hp->nbuf);
X	/* source path */
X	if (hfgets(hp->path, PATHLEN, fp) == NULL || !nstrip(hp->path))
X		return(NULL);
X	/* date */
X	if (hfgets(hp->subdate, DATELEN, fp) == NULL || !nstrip(hp->subdate))
X		return(NULL);
X	/* title */
X	if (hfgets(hp->title, BUFLEN, fp) == NULL || !nstrip(hp->title))
X		return(NULL);
X
Xstrip:	/* strip off sys! from front of path. */
X	strcpy(bfr, FULLSYSNAME);
X	if (strncmp(bfr, hp->path, (len = strlen(bfr))) == 0
X	&& index(NETCHRS, hp->path[len]))
X		strcpy(hp->path, &(hp->path[len+1]));
X	lcase(hp->nbuf);
X
X	/* Intuit the From: line if only a path was given. */
X	if (wholething && hp->from[0] == '\0')
X		intuitfrom(hp);
X
X	/* Get old and new style message ID's. */
X	if (wholething)
X		fixid(hp);
X
X	return(fp);
X}
X
X
X/*
X * Get header info from mail-format file.
X * Return non-zero on success.
X */
X#include <ctype.h>
X#define FROM 		1
X#define NEWSGROUP 	2
X#define TITLE 		3
X#define SUBMIT		4
X#define RECEIVE		5
X#define EXPIRE		6
X#define ARTICLEID	7
X#define MESSAGEID	8
X#define REPLYTO		9
X#define FOLLOWID	10
X#define CONTROL		11
X#define SENDER		12
X#define FOLLOWTO	13
X#define PATH		14
X#define POSTVERSION	15
X#define RELAYVERSION	16
X#define DISTRIBUTION	17
X#define ORGANIZATION	18
X#define NUMLINES	19
X#define KEYWORDS	20
X#define APPROVED	21
X#define NFID		22
X#define NFFROM		23
X#define OTHER		99
X
Xchar *malloc();
X
Xfrmread(fp, hp)
Xregister FILE *fp;
Xregister struct hbuf *hp;
X{
X	int	unreccnt = 0;
X	register int	i;
X	long	curpos;
X	int hdrlineno = 0;
X
X	i = type(bfr);
X	do {
X		curpos = ftell(fp);
X		hdrlineno++;
X		switch (i) {
X		case PATH:
X			getfield(hp->path);
X			break;
X		case FROM:
X			getfield(hp->from);
X			break;
X		case NEWSGROUP:
X			getfield(hp->nbuf);
X			break;
X		case TITLE:
X			getfield(hp->title);
X			break;
X		case SUBMIT:
X			getfield(hp->subdate);
X			break;
X		case RECEIVE:
X			getfield(hp->recdate);
X			break;
X		case EXPIRE:
X			getfield(hp->expdate);
X			break;
X		case ARTICLEID:
X			getfield(hp->oident);
X			break;
X		case MESSAGEID:
X			getfield(hp->ident);
X			break;
X		case REPLYTO:
X			getfield(hp->replyto);
X			break;
X		case FOLLOWID:
X			getfield(hp->followid);
X			break;
X		case SENDER:
X			getfield(hp->sender);
X			break;
X		case FOLLOWTO:
X			getfield(hp->followto);
X			break;
X		case CONTROL:
X			getfield(hp->ctlmsg);
X			break;
X		case POSTVERSION:
X			getfield(hp->postversion);
X			break;
X		case DISTRIBUTION:
X			getfield(hp->distribution);
X			break;
X		case ORGANIZATION:
X			getfield(hp->organization);
X			break;
X		case NUMLINES:
X			getfield(hp->numlines);
X			hp->intnumlines = atoi(hp->numlines);
X			break;
X		case KEYWORDS:
X			getfield(hp->keywords);
X			break;
X		case APPROVED:
X			getfield(hp->approved);
X			break;
X		case NFID:
X			getfield(hp->nf_id);
X			break;
X		case NFFROM:
X			getfield(hp->nf_from);
X			break;
X		case RELAYVERSION:
X			/*
X			 * Only believe a relay version if it's the first
X			 * line, otherwise it probably got passed through
X			 * by some old neighbor.
X			 */
X			if (hdrlineno == 1) {
X				getfield(hp->relayversion);
X			}
X			break;
X		case OTHER:
X			if (unreccnt < NUNREC) {
X				if ((hp->unrec[unreccnt] = malloc((unsigned)(strlen(bfr) + 1))) != NULL ) {
X					strcpy(hp->unrec[unreccnt], bfr);
X					unreccnt++;
X				} else
X					xerror("frmread: out of memory");
X			}
X			break;
X		}
X	} while ((i = type(hfgets(bfr, LBUFLEN, fp))) > 0);
X
X	if (*bfr != '\n')
X		fseek(fp, curpos, 0);
X	if ((hp->from[0] || hp->path[0]) && hp->subdate[0] && (hp->ident[0] || hp->oident[0]))
X		return TRUE;
X	return FALSE;
X}
X
X/*
X * There was no From: line in the message (because it was generated by
X * an old news program).  Guess what it should have been and create it.
X */
Xintuitfrom(hp)
Xregister struct hbuf *hp;
X{
X	char *tp;
X	char *user, *host, *fullname;
X	char *tailpath(), *rindex();
X	char *at, *dot;
X
X	tp = tailpath(hp);
X	user = rindex(tp, '!');
X	if (user == NULL)
X		user = tp;
X	else
X		*user++ = '\0';
X
X	/* Check for an existing Internet address on the end. */
X	at = index(user, '@');
X	if (at) {
X		dot = index(at, '.');
X		if (dot) {
X			strcpy(hp->from, user);
X			return;
X		}
X		/* @ signs are illegal except for the biggie, so */
X		*at = '%';
X	}
X
X	if (tp[0] == '.')
X		host = index(tp, '!') + 1;
X	else if (user == tp)
X		host = FULLSYSNAME;
X	else
X		host = tp;
X
X	tp = index(host, '@');
X	if (tp != NULL)
X		*tp = 0;
X	sprintf(hp->from, "%s@%s%s", user, host, MYDOMAIN);
X
X	fullname = index(hp->path, '(');
X	if (fullname != NULL) {
X		fullname--;
X		strcat(hp->from, fullname);
X		*fullname = 0;
X	}
X}
X
X/*
X * If the message has only one of ident/oident, guess what
X * the other one should be and fill them both in.
X */
Xfixid(hp)
Xregister struct hbuf *hp;
X{
X	char lbuf[100];
X	register char *p;
X#ifdef OLD
X	register char *q;
X#endif OLD
X
X	if (hp->ident[0] == '\0' && hp->oident[0] != '\0') {
X		strcpy(lbuf, hp->oident);
X		p = index(lbuf, '.');
X		if (p == NULL) {
X			strcpy(hp->ident, hp->oident);
X			return;
X		}
X		*p++ = '\0';
X		/*
X		 * It may seem strange that we hardwire ".UUCP" in
X		 * here instead of MYDOMAIN.  However, we are trying
X		 * to guess what the domain was on the posting system,
X		 * not the local system.  Since we don't really know
X		 * what the posting system does, we just go with the
X		 * majority - almost everyone will be a .UUCP if they
X		 * didn't fill in their Message-ID.
X		 */
X		sprintf(hp->ident, "<%s@%s%s>", p, lbuf, ".UUCP");
X	}
X
X#ifdef OLD
X	if (hp->oident[0] == '\0' && hp->ident[0] != '\0') {
X		strcpy(lbuf, hp->ident);
X		p = index(lbuf, '@');
X		if (p == 0) {
X			strcpy(hp->oident, hp->ident);
X			return;
X		}
X		*p++ = '\0';
X		q = index(p, '.');
X		if (q == NULL)
X			q = index(p, '>');
X		if (q)
X			*q++ = '\0';
X		p[SNLN] = '\0';
X		sprintf(hp->oident, "%s.%s", p, lbuf+1);
X	}
X#endif
X}
X
X/*
X * Get the given field of a header (char * parm) from bfr, but only
X * if there's something actually there (after the colon).  Don't
X * bother if we already have an entry for this field.
X */
Xgetfield(hpfield)
Xchar	*hpfield;
X{
X	register char	*ptr;
X
X	if (hpfield[0])
X		return;
X	for (ptr = index(bfr, ':'); isspace(*++ptr); )
X		;
X	if (*ptr != '\0') {
X		strncpy(hpfield, ptr, BUFLEN);
X		nstrip(hpfield);
X	}
X	return;
X}
X
X
X#define its(type) (prefix(ptr, type))
Xtype(ptr)
Xregister char	*ptr;
X{
X	register char	*colon, *space;
X
X	if (ptr == NULL)
X		return FALSE;
X	if (!isalpha(*ptr) && strncmp(ptr, "From ", 5))
X		return FALSE;
X	colon = index(ptr, ':');
X	space = index(ptr, ' ');
X	if (!colon || space && space < colon)
X		return FALSE;
X	if (its("From: "))
X		if (index(ptr, '@') && !index(ptr, '!'))
X			return FROM;
X		else
X			return PATH;
X	if (its("Path: "))
X		return PATH;
X	if (its("Newsgroups: "))
X		return NEWSGROUP;
X	if (its("Subject: ") || its("Title: "))
X		return TITLE;
X	if (its("Posted: ") || its("Date: "))
X		return SUBMIT;
X	if (its("Date-Received: ") || its("Received: "))
X		return RECEIVE;
X	if (its("Expires: "))
X		return EXPIRE;
X	if (its("Article-I.D.: "))
X		return ARTICLEID;
X	if (its("Message-ID: "))
X		return MESSAGEID;
X	if (its("Reply-To: "))
X		return REPLYTO;
X	if (its("References: "))
X		return FOLLOWID;
X	if (its("Control: "))
X		return CONTROL;
X	if (its("Sender: "))
X		return SENDER;
X	if (its("Followup-To: "))
X		return FOLLOWTO;
X	if (its("Posting-Version: "))
X		return POSTVERSION;
X	if (its("Relay-Version: "))
X		return RELAYVERSION;
X	if (its("Distribution: "))
X		return DISTRIBUTION;
X	if (its("Organization: "))
X		return ORGANIZATION;
X	if (its("Lines: "))
X		return NUMLINES;
X	if (its("Keywords: "))
X		return KEYWORDS;
X	if (its("Approved: "))
X		return APPROVED;
X	if (its("Nf-ID: "))
X		return NFID;
X	if (its("Nf-From: "))
X		return NFFROM;
X	return OTHER;
X}
X
X/*
X * Generate the current version of the news software.
X */
Xchar *
Xgenversion()
X{
X	static char retbuf[32];
X	char rb[100];
X	register char *t;
X
X	strcpy(rb, news_version);
X	while (t = index(rb, '\t'))
X		*t = ' ';
X	/* This is B news, so we say "B", the version, and the date. */
X	sprintf(retbuf, "version %s; site %s%s", rb, FULLSYSNAME, MYDOMAIN);
X	return retbuf;
X}
X
X/*
X * Write header at 'hp' on stream 'fp' in B format.  This goes out to
X * some other system.
X */
Xhwrite(hp, fp)
Xregister struct hbuf *hp;
Xregister FILE *fp;
X{
X	ihwrite(hp, fp, 0);
X}
X
X
X/*
X * Same as above, except include receival date for local usage and
X * an extra \n for looks.
X */
Xlhwrite(hp, fp)
Xregister struct hbuf *hp;
Xregister FILE *fp;
X{
X	ihwrite(hp, fp, 1);
X}
X
X
X/*
X * Write header at 'hp' on stream 'fp' in B+ format.  Include received date
X * if wr is 1.  Leave off sysname if wr is 2.
X */
Xihwrite(hp, fp, wr)
Xregister struct hbuf *hp;
Xregister FILE *fp;
Xint	wr;
X{
X	int iu;
X	time_t t;
X	time_t cgtdate();
X
X	fprintf(fp, "Relay-Version: %s\n", genversion());
X	if (*hp->postversion)
X		fprintf(fp, "Posting-Version: %s\n", hp->postversion);
X	/*
X	 * We're being tricky with Path/From because of upward compatibility
X	 * issues.  The new version considers From and Path to be separate.
X	 * The old one thinks they both mean "Path" but only believes the
X	 * first one it sees, so will ignore the second.
X	 */
X	if (prefix(hp->path, FULLSYSNAME))
X		fprintf(fp, "Path: %s\n", hp->path);
X	else
X		fprintf(fp, "Path: %s!%s\n", FULLSYSNAME, hp->path);
X	if (hp->from[0])
X		fprintf(fp, "From: %s\n", hp->from);
X
X	strcpy(bfr, hp->nbuf);
X	ngdel(bfr);
X	fprintf(fp, "Newsgroups: %s\n", bfr);
X	fprintf(fp, "Subject: %s\n", hp->title);
X	fixid(hp);
X	fprintf(fp, "Message-ID: %s\n", hp->ident);
X	t = cgtdate(hp->subdate);
X	fprintf(fp, "Date: %s\n", arpadate(&t));
X#ifdef OLD
X	fprintf(fp, "Article-I.D.: %s\n", hp->oident);
X	fprintf(fp, "Posted: %s", ctime(&t));
X#endif
X	if (wr == 1)
X		fprintf(fp, "Date-Received: %s\n", hp->recdate);
X	if (*hp->expdate)
X		fprintf(fp, "Expires: %s\n", hp->expdate);
X	if (*hp->followid)
X		fprintf(fp, "References: %s\n", hp->followid);
X	if (*hp->ctlmsg)
X		fprintf(fp, "Control: %s\n", hp->ctlmsg);
X	if (*hp->sender)
X		fprintf(fp, "Sender: %s\n", hp->sender);
X	if (*hp->replyto)
X		fprintf(fp, "Reply-To: %s\n", hp->replyto);
X	if (*hp->followto)
X		fprintf(fp, "Followup-To: %s\n", hp->followto);
X	if (*hp->distribution)
X		fprintf(fp, "Distribution: %s\n", hp->distribution);
X	if (*hp->organization)
X		fprintf(fp, "Organization: %s\n", hp->organization);
X	if (*hp->numlines)
X		fprintf(fp, "Lines: %s\n", hp->numlines);
X	if (*hp->keywords)
X		fprintf(fp, "Keywords: %s\n", hp->keywords);
X	if (*hp->approved)
X		fprintf(fp, "Approved: %s\n", hp->approved);
X	if (*hp->nf_id)
X		fprintf(fp, "Nf-ID: %s\n", hp->nf_id);
X	if (*hp->nf_from)
X		fprintf(fp, "Nf-From: %s\n", hp->nf_from);
X	for (iu = 0; iu < NUNREC; iu++) {
X		if (hp->unrec[iu])
X			fprintf(fp, "%s", &hp->unrec[iu][0]);
X	}
X	putc('\n', fp);
X}
X
X
X/*
X * Set nc bytes, starting at cp, to zero.
X */
Xbclear(cp, nc)
Xregister char	*cp;
Xregister int	nc;
X{
X	while (nc--)
X		*cp++ = 0;
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	cp = fgets(buf, len, fp);
X	if (cp == NULL)
X		return NULL;
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 if (tp == (cp+1))
X		return(cp);	/* Don't look for continuation of blank lines */
X	else
X		*--tp = '\0';	/* clobber newline */
X
X	while ((c = getc(fp)) == ' ' || c == '\t') {	/* for each cont line */
X		/* Continuation line. */
X		while ((c = getc(fp)) == ' ' || c == '\t')	/* skip white space */
X			;
X 		if (c == '\n')	/* oops, it's really a blank line (I hope) */
X 			break;
X		if (tp-cp < len) {*tp++ = ' '; *tp++ = c;}
X		while ((c = getc(fp)) != '\n' && c != EOF)
X			if (tp-cp < len) *tp++ = c;
X	}
X	*tp++ = '\n';
X	*tp++ = '\0';
X	if (c != EOF)
X		(void) ungetc(c, fp); /* push back first char of next header */
X	return cp;
X}
*-*-END-of-src/header.c-*-*
echo x - src/header.h
sed 's/^X//' >src/header.h <<'*-*-END-of-src/header.h-*-*'
X/*
X * header.h - Article header format
X */
X
X/*	@(#)header.h	2.14	8/7/84	*/
X
X#define NUNREC 50
X
X/* article header */
Xstruct	hbuf {
X	char	from[BUFLEN];		/* From:		*/
X	char	path[PATHLEN];		/* Path:		*/
X	char	nbuf[BUFLEN];		/* Newsgroups:		*/
X	char	title[BUFLEN];		/* Subject:		*/
X	char	oident[BUFLEN];		/* Article-I.D.:	*/
X	char	ident[BUFLEN];		/* Message-ID:		*/
X	char	replyto[BUFLEN];	/* Reply-To:		*/
X	char	followid[BUFLEN];	/* References:		*/
X	char	subdate[DATELEN];	/* Date: (submission)	*/
X	time_t	subtime;		/* subdate in secs	*/
X	char	recdate[DATELEN];	/* Date-Received:	*/
X	time_t	rectime;		/* recdate in secs	*/
X	char	expdate[DATELEN];	/* Expires:		*/
X	time_t	exptime;		/* expdate in secs	*/
X	char	ctlmsg[PATHLEN];	/* Control:		*/
X	char	sender[BUFLEN];		/* Sender:		*/
X	char	followto[BUFLEN];	/* Followup-to:		*/
X	char	postversion[BUFLEN];	/* Post-Version:	*/
X	char	relayversion[BUFLEN];	/* Relay-Version:	*/
X	char	distribution[BUFLEN];	/* Distribution:	*/
X	char	organization[BUFLEN];	/* Organization:	*/
X	char	numlines[8];		/* Lines:		*/
X	int	intnumlines;		/* Integer version	*/
X	char	keywords[BUFLEN];	/* Keywords:		*/
X	char	approved[BUFLEN];	/* Approved:		*/
X	char	nf_id[BUFLEN];		/* Nf-ID:		*/
X	char	nf_from[BUFLEN];	/* Nf-From:		*/
X	char	*unrec[NUNREC];		/* unrecognized lines	*/
X};
*-*-END-of-src/header.h-*-*
echo x - src/help
sed 's/^X//' >src/help <<'*-*-END-of-src/help-*-*'
XCommand		Meaning
X
Xy		Yes.  (Or just hit return.)  Prints this article and goes on.
Xn		No.  Goes on to next article without printing current one.
Xd		Digest.  Breaks a digest article up into seperate articles.
Xq		Quit.  Update .newsrc if -l or -x not used.
XU		Unsubscribe.  You won't be shown this newsgroup anymore.
Xc		Cancel an article you posted.
Xr		Reply.  Reply to article's author via mail.
Xf [title]	Submit a follow up article.
XN [newsgroup]	Go to next newsgroup or named newsgroup.
Xs [file]	Save.  Article is appended to file (default is "Articles").
Xs |program	Run program with article as standard input.
Xe		Erase.  Forget that an article was read.
Xh		Print verbose header.  Use H for extremely verbose header.
X!		Shell escape.
X<number>	Go to message #<number> in this newsgroup.
X-		Go back to last article.
Xb		Back up one article in the current group.
XK		Mark the rest of the articles in current group as read.
Xx		Exit.  Don't update .newsrc.
Xv		Version.  Print current news version number.
X
Xc, f, r, e, h, and s can be followed by -'s to refer to the previous article
*-*-END-of-src/help-*-*
echo x - src/iextern.c
sed 's/^X//' >src/iextern.c <<'*-*-END-of-src/iextern.c-*-*'
X/*
X * iextern - external definitions for inews.
X */
X
X#ifndef lint
Xstatic char	*SccsId = "@(#)iextern.c	2.11	8/28/84";
X#endif !lint
X
X#include "iparams.h"
X
Xint	uid, gid;			/* real user/group I.D. */
Xint	duid, dgid;			/* effective user/group I.D. */
Xint	sigtrap;			/* set if signal trapped */
Xint	savmask;			/* old umask */
Xint	mode;				/* mode of news program */
Xstruct hbuf header;			/* general-use header structure */
Xchar	bfr[LBUFLEN];			/* general-use scratch area */
Xchar	nbuf[LBUFLEN];			/* local newsgroup buffer */
Xchar	filename[NAMELEN];		/* general-use file name */
X
X#ifndef ROOTID
Xint	ROOTID;				/* special users id # */
X#endif
X
Xchar	*DFLTNG = "general";		/* default newsgroup */
XFILE	*infp;				/* input file-pointer */
XFILE	*actfp;				/* active newsgroups file pointer */
Xint	tty;				/* set if infp is a tty */
Xchar	*PARTIAL = "dead.article";	/* place to save partial news */
Xchar	*SHELL = "/bin/sh";		/* shell for inews to use	*/
Xint	defexp = FALSE;			/* set if def. expiration date used */
Xint	is_ctl;				/* true for a control message */
*-*-END-of-src/iextern.c-*-*
echo x - src/ifuncs.c
sed 's/^X//' >src/ifuncs.c <<'*-*-END-of-src/ifuncs.c-*-*'
X/*
X * ifuncs - functions used by inews.
X */
X
X#ifndef lint
Xstatic char	*SccsId = "@(#)ifuncs.c	2.33	9/3/84";
X#endif !lint
X
X#include "iparams.h"
X#include <errno.h>
X
X/*
X * Transmit this article to all interested systems.
X */
X
X#ifdef u370
Xstatic struct srec srec;
X#endif
X
Xstatic struct hbuf h, hh;
X
Xbroadcast()
X{
X	register char *nptr, *hptr;
X	register FILE *fp;
X#ifndef u370
X	struct srec srec;
X#endif
X	char sentbuf[BUFLEN];
X	int nsent = 0;
X
X	/* h is a local copy of the header we can scribble on */
X	fp = xfopen(ARTICLE, "r");
X	if (hread(&h, fp, TRUE) == NULL)
X		xerror("Cannot reread article");
X	fclose(fp);
X
X	strcpy(sentbuf, h.ident);
X	strcat(sentbuf, " sent to ");
X	nsent = 0;
X	/* break path into list of systems. */
X	hptr = nptr = h.path;
X	while (*hptr != '\0') {
X		if (index(NETCHRS, *hptr)) {
X			*hptr++ = '\0';
X			nptr = hptr;
X		} else
X			hptr++;
X	}
X	*nptr = '\0';
X
X	/* loop once per system. */
X	lock();
X	s_openr();
X	while (s_read(&srec)) {
X		if (strncmp(srec.s_name, FULLSYSNAME, SNLN) == 0)
X			continue;
X		hptr = h.path;
X		while (*hptr != '\0') {
X			if (strncmp(srec.s_name, hptr, SNLN) == 0)
X				goto contin;
X			while (*hptr++ != '\0')
X				;
X		}
X		if (ngmatch(h.nbuf, srec.s_nbuf)) {
X			if (h.distribution[0] == '\0' ||
X				ngmatch(h.distribution, srec.s_nbuf)) {
X				if (transmit(&srec, xfopen(ARTICLE, "r"), 1)) {
X					if (nsent)
X						strcat(sentbuf, ", ");
X					strcat(sentbuf, srec.s_name);
X					nsent++;
X				}
X			}
X		}
X	contin:;
X	}
X	if (nsent)
X		log(sentbuf);
X	s_close();
X	unlock();
X}
X
X/*
X * Transmit file to system.
X */
X#define PROC 0004
Xtransmit(sp, ifp, maynotify)
Xregister struct srec *sp;
Xregister FILE *ifp;
Xint maynotify;
X{
X	register FILE *ofp;
X	register int c;
X	register char *ptr;
X	char TRANS[BUFLEN];
X	char *argv[20];
X	register int pid;
X	extern char firstbufname[];
X
X/* A:	afmt: the other machine runs an A news, so we xmit in A format */
X	int afmt = (index(sp->s_flags, 'A') != NULL);
X/* B:	use B format (this is the default - don't use this letter elsewise). */
X/* F:	append name to file */
X	int appfile = (index(sp->s_flags, 'F') != NULL);
X/* L:	local: don't send the article unless it was generated locally */
X	int local = (index(sp->s_flags, 'L') != NULL);
X/* N:	notify: don't send the article, just tell him we have it */
X	int notify = maynotify && (index(sp->s_flags, 'N') != NULL);
X/* S:	noshell: don't fork a shell to execute the xmit command */
X	int noshell = (index(sp->s_flags, 'S') != NULL);
X/* U:	useexist: use the -c option to uux to use the existing copy */
X	int useexist = (index(sp->s_flags, 'U') != NULL);
X
X	if (local && mode == PROC) {
X		fclose(ifp);
X		return FALSE;
X	}
X#ifdef DEBUG
X	printf("Transmitting to '%s'\n", sp->s_name);
X#endif
X	if (!appfile && !useexist) {
X		if (hread(&hh, ifp, TRUE) == NULL) {
X			logerr("Bad header, not transmitting %s re %s to %s",
X				hh.ident, hh.title, sp->s_name);
X			fclose(ifp);
X			return FALSE;
X		}
X		if (hh.nbuf[0] == '\0') {
X			fprintf(stderr, "Article not subscribed to by %s\n", sp->s_name);
X			fclose(ifp);
X			return FALSE;
X		}
X		sprintf(TRANS, "%s/trXXXXXX", SPOOL);
X	}
X
X	if (notify) {
X		char oldid[50];
X		sprintf(hh.title, "ihave %s %s", hh.ident, FULLSYSNAME);
X		sprintf(hh.nbuf, "to.%s.ctl", sp->s_name);
X		strcpy(oldid, hh.ident);
X		getident(&hh);
X		log("tell %s about %s, notif. id %s",
X			sp->s_name, oldid, hh.ident);
X	}
X
X	if (appfile) {
X		if (firstbufname[0] == '\0') {
X			extern char histline[];
X			localize("junk");
X			savehist(histline);
X			xerror("No file name to xmit from");
X		}
X#ifdef IHCC
X		sprintf(TRANS, "%s/%s/%s", logdir(HOME), BATCHDIR, sp->s_xmit);
X		ofp = fopen(TRANS, "a");
X#else
X		ofp = fopen(sp->s_xmit, "a");
X#endif
X		if (ofp == NULL)
X			xerror("Cannot append to %s", sp->s_xmit);
X		fprintf(ofp, "%s\n", firstbufname);
X		fclose(ofp);
X		fclose(ifp);
X		return TRUE;
X	}
X	else
X#ifdef UXMIT
X	if (useexist) {
X		if (firstbufname[0] == '\0')
X			xerror("No file name to xmit from");
X		if (*sp->s_xmit == '\0')
X			sprintf(bfr, UXMIT, sp->s_name, firstbufname);
X		else
X			sprintf(bfr, sp->s_xmit, firstbufname);
X		fclose(ifp);
X	} else
X#endif
X	{
X		ofp = xfopen(mktemp(TRANS), "w");
X		if (afmt)
X			ohwrite(&hh, ofp);
X		else
X			hwrite(&hh, ofp);
X		if (!notify)
X			while ((c = getc(ifp)) != EOF)
X				putc(c, ofp);
X		fclose(ifp);
X		fclose(ofp);
X		if (*sp->s_xmit == '\0')
X			sprintf(bfr, DFTXMIT, sp->s_name, TRANS);
X		else
X			sprintf(bfr, "(%s) < %s", sp->s_xmit, TRANS);
X	}
X
X	/* At this point, the command to be executed is in bfr. */
X	if (noshell) {
X		if (pid = fork())
X			fwait(pid);
X		else {
X			close(0);
X			open(TRANS, 0);
X			if (*sp->s_xmit == '\0')
X				ptr = bfr;
X			else
X				ptr = sp->s_xmit;
X			for (pid = 0; pid < 19; pid++) {
X				while (isspace(*ptr))
X					*ptr++ = 0;
X				argv[pid] = ptr;
X				while (!isspace(*++ptr) && *ptr)
X					;
X				if (!*ptr)
X					break;
X			}
X			argv[++pid] = 0;
X			execv(argv[0], argv);
X			xerror("Can't execv %s", argv[0]);
X		}
X	} else
X		system(bfr);
X	if (!appfile && !useexist)
X		unlink(TRANS);
X	fclose(ifp);
X	return TRUE;
X}
X
Xtypedef struct {
X	char *dptr;
X	int dsize;
X} datum;
X
X/*
X * Return TRUE if we have seen this file before, else FALSE.
X */
Xhistory(hp)
Xstruct hbuf *hp;
X{
X#ifdef DBM
X	datum lhs, rhs;
X	datum fetch();
X#else !DBM
X	register FILE *hfp;
X#endif !DBM
X	register char *p;
X	char lcident[BUFLEN];
X
X#ifdef DEBUG
X	fprintf(stderr,"history(%s)\n", hp->ident);
X#endif
X	/*
X	 * Make the article ID case insensitive.
X	 */
X	strcpy(lcident, hp->ident);
X	p = lcident;
X	while (*++p)
X		if (isupper(*p))
X			*p = tolower(*p);
X
X	idlock(lcident);
X#ifdef DBM
X	dbminit(ARTFILE);
X	lhs.dptr = lcident;
X	lhs.dsize = strlen(lhs.dptr) + 1;
X	rhs = fetch(lhs);
X	if (rhs.dptr)
X		return(TRUE);
X#else
X	hfp = xfopen(ARTFILE, "r");
X	while (fgets(bfr, BUFLEN, hfp) != NULL) {
X		p = index(bfr, '\t');
X		if (p == NULL)
X			p = index(bfr, '\n');
X		if (p != NULL)	/* can happen if nulls in file */
X			*p = 0;
X		p = bfr;
X		while (*++p)
X			if (isupper(*p))
X				*p = tolower(*p);
X
X		if (strcmp(bfr, lcident)==0 ||
X				hp->oident[0] && strcmp(bfr, hp->oident)==0) {
X			fclose(hfp);
X			idunlock();
X#ifdef DEBUG
X			fprintf(stderr,"history returns true\n");
X#endif
X			return(TRUE);
X		}
X	}
X	fclose(hfp);
X#endif
X	addhist(hp->ident);
X	addhist("\t");
X#ifdef DEBUG
X	fprintf(stderr,"history returns false\n");
X#endif
X	return(FALSE);
X}
X
Xchar histline[256];	/* Assumed initially zero */
X
Xaddhist(msg)
Xchar *msg;
X{
X	strcat(histline, msg);
X}
X
Xsavehist(hline)
Xchar *hline;
X{
X	register FILE *hfp;
X	datum lhs, rhs;
X	long fpos;
X	register char *p;
X
X	hfp = xfopen(ARTFILE, "a");
X	fpos = ftell(hfp);
X	fprintf(hfp, "%s\n", hline);
X	fclose(hfp);
X#ifdef DBM
X	/* We assume that history has already been called, calling dbminit. */
X	p = index(hline, '\t');
X	if (p)
X		*p = 0;
X	p = hline;
X	while(*++p)
X		if(isupper(*p))
X			*p = tolower(*p);
X	lhs.dptr = hline;
X	lhs.dsize = strlen(lhs.dptr) + 1;
X	rhs.dptr = (char *) &fpos;
X	rhs.dsize = sizeof fpos;
X	store(lhs, rhs);
X#endif
X	hline[0] = 0;
X	idunlock();
X}
X
X/*
X * Save partial news.
X */
Xnewssave(fd, dummy)
XFILE *fd;
Xchar *dummy;
X{
X	register FILE *tofd, *fromfd;
X	char sfname[BUFLEN];
X	register int c;
X	time_t tim;
X
X#ifdef lint
X	c = *dummy;
X#endif lint
X
X	if (fd == NULL)
X		fromfd = xfopen(INFILE, "r");
X	else
X		fromfd = fd;
X	umask(savmask);
X	setgid(gid);
X	setuid(uid);
X
X	sprintf(sfname, "%s/%s", userhome, PARTIAL);
X	if ((tofd = fopen(sfname, "a")) == NULL)
X		xerror("Cannot save partial news");
X	time(&tim);
X	fprintf(tofd, "----- News saved at %s\n", arpadate(&tim));
X	while ((c = getc(fromfd)) != EOF)
X		putc(c, tofd);
X	fclose(fromfd);
X	fclose(tofd);
X	printf("News saved in %s\n", sfname);
X	xxit(0);
X}
X
X/*
X * Handle dates in header.
X */
X
Xdates(hp)
Xstruct hbuf *hp;
X{
X	time_t edt;
X
X	time(&hp->rectime);
X	strcpy(hp->recdate, arpadate(&hp->rectime));
X	nstrip(hp->recdate);
X	if (*hp->subdate) {
X		if (cgtdate(hp->subdate) < 0) {
X			xerror("Cannot parse submittal date '%s'", hp->subdate);
X		}
X	} else
X		strcpy(hp->subdate, hp->recdate);
X	if (*hp->expdate) {
X		if ((edt = cgtdate(hp->expdate)) < 0)
X			xerror("Cannot parse expiration date '%s'",hp->expdate);
X		nstrip(strcpy(hp->expdate, arpadate(&edt)));
X	} else {
X		defexp = TRUE;
X		/*
X		 * Default is now applied in expire.c
X		hp->exptime = hp->rectime + DFLTEXP;
X		nstrip(strcpy(hp->expdate, arpadate(&hp->exptime)));
X		*/
X	}
X}
X
Xchar lockname[80];
Xidlock(str)
Xchar *str;
X{
X	register int i;
X	char tempname[80];
X	time_t now;
X	struct stat sbuf;
X#ifdef	VMS
X	int fd;
X
X	sprintf(lockname, "/tmp/%s.l.1", str);
X	if ((fd = creat(lockname, 0444)) < 0) {
X#else	!VMS
X	sprintf(tempname, "/tmp/LTMP.%d", getpid());
X	sprintf(lockname, "/tmp/L%s", str);
X#ifdef FOURTEENMAX
X	lockname[5 /* /tmp/ */ + 14] = '\0';
X#endif
X	close(creat(tempname, 0666));
X	while (link(tempname, lockname)) {
X#endif	!VMS
X		(void) time(&now);
X		i = stat(lockname, &sbuf);
X		if (i < 0) {
X			xerror("Directory permission problem in /tmp");
X		}
X		if (sbuf.st_mtime + 10*60 < now) {
X			unlink(lockname);
X			logerr("Article %s locked up", str);
X			continue;
X		}
X		log("waiting on lock for %s", lockname);
X		sleep((unsigned)60);
X	}
X#ifdef VMS
X	close(fd);
X#else
X	unlink(tempname);
X#endif
X	unlink(tempname);
X}
X
Xidunlock()
X{
X	unlink(lockname);
X}
X
X/*
X * Put a unique name into header.ident.
X */
Xgetident(hp)
Xstruct hbuf *hp;
X{
X	long seqn;
X	register FILE *fp;
X
X	lock();
X	fp = xfopen(SEQFILE, "r");
X	fgets(bfr, BUFLEN, fp);
X	fclose(fp);
X	seqn = atol(bfr) + 1;
X#ifdef	VMS
X	unlink(SEQFILE);
X#endif	VMS
X	fp = xfopen(SEQFILE, "w");
X	fprintf(fp, "%ld\n", seqn);
X	fclose(fp);
X	unlock();
X	sprintf(hp->ident, "<%ld@%s%s>", seqn, FULLSYSNAME, MYDOMAIN);
X}
X
X/*
X * Check that header.nbuf contains only valid newsgroup names;
X * exit with error if not valid.
X *
X */
X
Xngfcheck(isproc)
Xint isproc;
X{
X	register char *s1, *s2;
X	register FILE *f;
X	register char *os1;
X	char tbuf[BUFLEN], ngcheck[AFSIZ];
X
X	f = xfopen(ACTIVE, "r");
X	s1 = ngcheck;
X	while (fgets(bfr, BUFLEN, f) != NULL) {
X		os1 = s1;
X		for(s2 = bfr; *s2 != '\0' && *s2 != ' ';) {
X			if (s1 >= &ngcheck[AFSIZ-2])
X				xerror("ACTIVE file too long");
X			*s1++ = *s2++;
X		}
X		*s1++ = '\0';
X		if (isproc) /* don't check to see if can post to this group */
X			continue;
X		while (*s2++ != '\0' && *s2 != ' ')
X			;	/* skip max article number */
X		while (*s2++ != '\0' && *s2 != ' ')
X			;	/* skip min article number */
X		if (*s2++ != '\0' && *s2 == 'n')
X			s1 = os1;	/* can't post to this group */
X	}
X	*s1++ = '\0';
X	*s1 = '\0';
X	fclose(f);
X
X	s1 = header.nbuf;
X	s2 = nbuf;
X	while (*s1 == NGDELIM || *s1 == ' ')
X		s1++;	/* skip leading junk */
X	do {
X		/* there shouldn't be blanks, but give the jerk a break */
X		if (*s1 == NGDELIM || *s1 == ' ') {
X			*s2++ = '\0';
X			while (*++s1 == NGDELIM || *s1 == ' ')
X				;	/* remove extra commas */
X		} else
X			*s2++ = *s1++;
X	} while (*s1 != '\0');
X	*s2 = '\0';
X
X	s1 = nbuf;
X	while (*s1 != '\0') {	/* for each newsgroup in header */
X		s2 = ngcheck;
X		while (*s2 != '\0') { /* for each newsgroup in active file */
X			if (strcmp(s1, s2) == 0)
X				break;
X			while (*s2++ != '\0')
X				;
X		}
X		if (*s2 == '\0') {	/* not found. remove it */
X			if (!isproc) {
X				logerr("Invalid news group '%s'", s1);
X				newssave(stdin, (char *)NULL);
X			}
X			/* See if it's in our alias list */
X			f = xfopen(ALIASES,"r");
X			while (fscanf(f,"%s %s", tbuf, bfr) == 2
X				&& strcmp(s1, tbuf))
X				;
X			fclose(f);
X			if (strcmp(s1, tbuf) == 0) {
X				logerr("Aliased newsgroup '%s' to '%s'", s1, bfr);
X				os1 = s1;
X				s1 = nbuf;
X				s2 = tbuf;
X				while (s1 < os1) /* copy left part */
X					*s2++ = *s1++;
X				s1 = bfr;
X				while (*s1 != '\0') /* copy alias */
X					*s2++ = *s1++;
X				*s2++ = '\0';
X				s1 = os1;
X				os1 = nbuf + (s2 - tbuf);
X				while (*s1++ != '\0') /* skip old group */
X					;
X				/* copy right part */
X				tbufcpy(s2, s1);
X				/* copy back to original buffer */
X				tbufcpy(nbuf, tbuf);
X				s1 = os1;
X			} else {
X				logerr("Unknown newsgroup '%s' removed", s1);
X				s2 = s1;
X				while (*s2++ != '\0')	/* skip the bad one */
X					;
X				tbufcpy(s1, s2);
X			}
X		} else { /* It's in our active file */
X			os1 = s1;
X			while (*s1++ != '\0')
X				;
X			/* check for local only distribution on incoming
X			   newsgroups. This might occur if someone posted to
X			   general,net.unix */
X			if(isproc && index(os1, '.') == NULL) {
X				logerr("Local group '%s' removed", os1);
X				tbufcpy(os1, s1);
X				s1 = os1;
X			}
X		}
X	}
X	/*  remove any duplicates */
X	os1 = s1 = nbuf;
X	for(;;) {
X		if (*s1++ == '\0') {
X			if (*s1 == '\0')
X				break;
X			s2 = s1;
X			while (*s2 != '\0') {
X				if (strcmp(os1, s2) == 0) {
X					logerr("Duplicate '%s' removed",os1);
X					os1 = s2;
X					while (*s2++ != '\0') /* skip it */
X						;
X					tbufcpy(os1, s2);
X				} else
X					while (*s2++ != '\0')
X						;
X			}
X			os1 = s1;
X			s1[-1] = '\0';
X		}
X	}
X	if (nbuf[0] != '\0') {
X		s1 = header.nbuf;
X		s2 = nbuf;
X		do {
X			while (*s2 != '\0')
X				*s1++ = *s2++;
X			*s1++ = NGDELIM;
X		} while (*++s2 != '\0');
X		*s1 = '\0';
X		return FALSE;
X	}
X	return TRUE;
X}
X
Xtbufcpy(s1, s2)
Xregister char *s1, *s2;
X{
X	do {
X		while (*s2 != '\0')
X			*s1++ = *s2++;
X		*s1++ = '\0';
X	} while (*++s2 != '\0');
X	*s1 = '\0';
X}
X
X
X/*
X * Figure out who posted the article (which is locally entered).
X * The results are placed in the header structure hp.
X */
Xgensender(hp, logname)
Xstruct hbuf *hp;
Xchar *logname;
X{
X	char *fn;
X	static char buf[100];
X	char buf2[100];
X	char *fullname(), *getenv();
X	char *p;
X	int fd;
X
X	fn = getenv("NAME");
X
X	if (fn == NULL) {
X		sprintf(buf, "%s/%s", getenv("HOME"), ".name");
X		fd = open(buf, 0);
X		if (fd >= 0) {
X			read(fd, buf2, sizeof buf2);
X			close(fd);
X			if (buf2[0] >= 'A')
X				fn = buf2;
X			for (p=fn; *p; p++)
X				if (*p < ' ')
X					*p = 0;
X		}
X	}
X
X	if (fn == NULL)
X		fn = fullname(logname);
X
X	sprintf(hp->path, "%s", logname);
X	sprintf(hp->from, "%s@%s%s (%s)", logname, FULLSYSNAME, MYDOMAIN, fn);
X}
X
X/*
X * Trap interrupts.
X */
Xonsig(n)
Xint n;
X{
X	static int numsigs = 0;
X	/*
X	 * Most UNIX systems reset caught signals to SIG_DFL.
X	 * This bad design requires that the trap be set again here.
X	 * Unfortunately, if the signal recurs before the trap is set,
X	 * the program will die, possibly leaving the lock in place.
X	 */
X	if (++numsigs > 100) {
X		logerr("inews ran away looping on signal %d", n);
X		xxit(1);
X	}
X	signal(n, onsig);
X	sigtrap = n;
X}
X
X#ifdef BATCH
X/*
X * If the stdin begins with "#", we assume we have been fed a batched
X * shell script which looks like this:
X *	#! rnews 1234
X *	article with 1234 chars
X *	#! rnews 4321
X *	article with 4321 chars
X *
X * In this case we just exec the unbatcher and let it unpack and call us back.
X *
X * Note that there is a potential security hole here.  If the batcher is
X * /bin/sh, someone could ship you arbitrary stuff to run as shell commands.
X * The main protection you have is that the effective uid will be news, not
X * uucp and not the super user.  (That, plus the fact that BATCH is set to
X * "unbatch" as the system is distributed.)  If you want to run a batched link
X * and you are security concious, do not use /bin/sh as the unbatcher.
X * the thing to do is to change BATCH in your localize.sh file from /bin/sh
X * to some restricted shell which can only run rnews.
X */
Xcheckbatch()
X{
X	int c;
X
X	c = getc(stdin);
X	if (c != EOF)
X		ungetc(c, stdin);
X	clearerr(stdin);
X	if (c == '#') {
X		char unbatcher[BUFLEN];
X
X		sprintf(unbatcher, "%s/%s", LIB, BATCH);
X		reset_stdin();
X		execl(unbatcher, "news-unpack", (char *)0);
X		xerror("Unable to exec shell to unpack news.");
X	}
X}
X
X/*
X * We've already done a read on stdin, and we want to seek back to the
X * beginning.  We want the real file descriptor (beyond buffers) to
X * reflect the true beginning.  Do whatever is necessary.
X */
Xreset_stdin()
X{
X	register FILE *ofd;
X	register int c;
X	char *ofdname;
X	long lseek();
X
X	/* First try to seek back - if so, it's a cheap way back. */
X	if (lseek(0, 0L, 0) == 0L)
X		return;
X
X	/* Can't seek, so have to copy input to a file and use that. */
X	ofdname = "/tmp/inewsXXXXX";
X	mktemp(ofdname);
X	ofd = fopen(ofdname, "w");
X	while ((c=getc(stdin)) != EOF)
X		putc(c, ofd);
X	fclose(stdin);
X	fclose(ofd);
X
X	/* Now for a few lower level hacks to reopen stdin and make
X	 * absolutely sure that the right fd's are done for the exec.
X	 */
X	(void) close(0);		/* make sure stdin is really closed. */
X	(void) open(ofdname, 0);	/* should return zero */
X	(void) unlink(ofdname);		/* to avoid cleaning it up later. */
X}
X#endif BATCH
X
X/*
X *	Exit and cleanup.
X */
Xxxit(status)
Xint status;
X{
X	unlink(INFILE);
X	unlink(ARTICLE);
X	while (lockcount > 0)
X		unlock();
X	idunlock();
X	exit(status);
X}
X
Xrwaccess(fname)
Xchar *fname;
X{
X	int fd;
X
X	fd = open(fname, 2);
X	if (fd < 0)
X		return 0;
X	close(fd);
X	return 1;
X}
X
Xexists(fname)
Xchar *fname;
X{
X	int fd;
X
X	fd = open(fname, 0);
X	if (fd < 0)
X		return 0;
X	close(fd);
X	return 1;
X}
X
Xint	lockcount = 0;			/* no. of times we've called lock */
X
X#ifdef	VMS
X
X#define	SUBLOCK	"/tmp/netnews.lck.1"
X
X/*
X * Newsystem locking.
X * These routines are different for VMS because we can not
X * effectively simulate links, and VMS supports multiple
X * version numbers of files
X */
Xlock()
X{
X	register int i;
X	register int fd;
X
X	if (lockcount++ == 0) {
X		i = DEADTIME;
X		while ((fd = creat(SUBLOCK, 0444)) < 0) {
X			if (--i < 0) {
X				unlink(SUBLOCK);
X				fprintf(stderr,"%s: %s\n", Progname, msg);
X				logerr("News system locked up");
X			}
X			if (i < -3)
X				xerror("Unable to unlock news system");
X			sleep((unsigned)1);
X		}
X		close(fd);
X	}
X}
X
Xunlock()
X{
X	if (--lockcount == 0)
X		unlink(SUBLOCK);
X}
X
X#else	!VMS
X
X/*
X * Newsystem locking.
X */
X
Xlock()
X{
X	register int i;
X	extern int errno;
X
X	if (lockcount++ == 0) {
X		i = DEADTIME;
X		while (link(SUBFILE, LOCKFILE)) {
X			if (errno != EEXIST)
X				break;
X			if (--i < 0)
X				xerror("News system locked up");
X			sleep((unsigned)1);
X		}
X	}
X}
X
Xunlock()
X{
X	if (--lockcount == 0)
X		unlink(LOCKFILE);
X}
X#endif	VMS
X
X/*
X * Generate the name of the person responsible for posting this article,
X * in order to check that two articles were posted by the same person.
X */
Xchar *
Xsenderof(hp)
Xstruct hbuf *hp;
X{
X	char *q, *tp;
X	char *tailpath();
X
X	if (hp->sender[0])
X		tp = hp->sender;
X	else if (hp->from[0])
X		tp = hp->from;
X	else
X		tp = tailpath(hp);
X
X	/* Remove full name */
X	q = index(tp, ' ');
X	if (q)
X		*q = '\0';
X
X	q = malloc((unsigned)(strlen(tp) + 1));
X	strcpy(q, tp);
X	return q;
X}
*-*-END-of-src/ifuncs.c-*-*
exit