[comp.sources.unix] v19i087: Cnews production release, Part10/19

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

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

: ---CUT HERE---
echo 'libc/getdate.y':
sed 's/^X//' >'libc/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.13	9/16/86 */
X
X#include <sys/types.h>
X#include <sys/timeb.h>
X#include <ctype.h>
X#include <time.h>
X
X#define	NULL	0
X
X#define daysec (24L*60L*60L)
X
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
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; merid = 24;
X		daylight = STANDARD; ourzone = $4%100 + 60*$4/100;}
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	| NUMBER ':' NUMBER ':' NUMBER NUMBER =
X		{hh = $1; mm = $3; ss = $5; merid = 24;
X		daylight = STANDARD; ourzone = $6%100 + 60*$6/100;};
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 && (yy%100 != 0 || yy%400 == 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				if (p < &idbuf[sizeof(idbuf)-1])
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
X	{"gmt", ZONE, 0 HRS},
X	{"g.m.t.", ZONE, 0 HRS},
X	{"bst", DAYZONE, 0 HRS},		/* British Summer Time */
X	{"b.s.t.", DAYZONE, 0 HRS},
X	{"eet", ZONE, 0 HRS},		/* European Eastern Time */
X	{"e.e.t.", ZONE, 0 HRS},
X	{"eest", DAYZONE, 0 HRS},	/* European Eastern Summer Time */
X	{"e.e.s.t.", DAYZONE, 0 HRS},
X	{"met", ZONE, -1 HRS},		/* Middle European Time */
X	{"m.e.t.", ZONE, -1 HRS},
X	{"mest", DAYZONE, -1 HRS},	/* Middle European Summer Time */
X	{"m.e.s.t.", DAYZONE, -1 HRS},
X	{"wet", ZONE, -2 HRS },		/* Western European Time */
X	{"w.e.t.", ZONE, -2 HRS },
X	{"west", DAYZONE, -2 HRS},	/* Western European Summer Time */
X	{"w.e.s.t.", DAYZONE, -2 HRS},
X
X	{"jst", ZONE, -9 HRS},		/* Japan Standard Time */
X	{"j.s.t.", ZONE, -9 HRS},	/* Japan Standard Time */
X					/* No daylight savings time */
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++)
X		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')
X		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	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{}
!
echo 'libc/nfclose.c':
sed 's/^X//' >'libc/nfclose.c' <<'!'
X/*
X * nfclose(stream) - flush the stream, fsync its file descriptor and
X * fclose the stream, checking for errors at all stages.  This dance
X * is needed to work around the lack of Unix file system semantics
X * in Sun's NFS.  Returns EOF on error.
X */
X
X#include <stdio.h>
X
Xint
Xnfclose(stream)
Xregister FILE *stream;
X{
X	register int ret = 0;
X
X	if (fflush(stream) == EOF)
X		ret = EOF;
X	if (fsync(fileno(stream)) < 0)		/* may get delayed error here */
X		ret = EOF;
X	if (fclose(stream) == EOF)
X		ret = EOF;
X	return ret;
X}
!
echo 'libcnews/config.c':
sed 's/^X//' >'libcnews/config.c' <<'!'
X/*
X * news configuration inquiry
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include "libc.h"
X#include "news.h"
X#include "config.h"
X
X#ifndef NULL
X#define	NULL	0
X#endif
X
X#ifndef NEWSCTL
X/* =()<#define	NEWSCTL	"@<NEWSCTL>@">()= */
X#define	NEWSCTL	"/usr/lib/news"
X#endif
X#ifndef NEWSPATH
X/* =()<#define	NEWSPATH	"@<NEWSPATH>@">()= */
X#define	NEWSPATH	"/bin:/usr/bin"
X#endif
X#ifndef NEWSARTS
X/* =()<#define	NEWSARTS	"@<NEWSARTS>@">()= */
X#define	NEWSARTS	"/usr/spool/news"
X#endif
X#ifndef NEWSBIN
X/* =()<#define	NEWSBIN	"@<NEWSBIN>@">()= */
X#define	NEWSBIN	"/usr/lib/newsbin"
X#endif
X#ifndef NEWSUMASK
X/* =()<#define	NEWSUMASK	@<NEWSUMASK>@>()= */
X#define	NEWSUMASK	002
X#endif
X#ifndef NEWSMASTER
X/* =()<#define	NEWSMASTER	"@<NEWSMASTER>@">()= */
X#define	NEWSMASTER	"usenet"
X#endif
X
Xstatic char *pwd = NULL;	/* Current directory, NULL means unknown. */
Xstatic int dirsset = NO;	/* Have the following been set up? */
Xstatic char *arts = NEWSARTS;
Xstatic char *bin = NEWSBIN;
Xstatic char *ctl = NEWSCTL;
Xstatic char *path = NEWSPATH;
Xstatic int numask = NEWSUMASK;
Xstatic char *nmaster = NEWSMASTER;
X#define	DIRS()	if (!dirsset) setdirs()
X
Xextern char *strcpy();
Xextern char *strcat();
Xextern char *getenv();
X
X/*
X - setdirs - set up stuff from environment, for use by other functions
X *
X * Invokes user-supplied function unprivileged() if non-standard values used.
X */
Xstatic void
Xsetdirs()
X{
X	register char *p;
X	register int nonstd = NO;
X	register int mask;
X	register char *scan;
X
X	if (dirsset)
X		return;
X
X	p = getenv("NEWSARTS");
X	if (p != NULL && !STREQ(p, arts)) {
X		nonstd = YES;
X		arts = p;
X	}
X
X	p = getenv("NEWSCTL");
X	if (p != NULL && !STREQ(p, ctl)) {
X		ctl = p;
X		nonstd = YES;
X	}
X
X	p = getenv("NEWSPATH");
X	if (p != NULL && !STREQ(p, path)) {
X		path = p;
X		nonstd = YES;
X	}
X
X	p = getenv("NEWSBIN");
X	if (p != NULL && !STREQ(p, bin)) {
X		bin = p;
X		nonstd = YES;
X	}
X
X	p = getenv("NEWSUMASK");
X	if (p != NULL) {
X		mask = 0;
X		for (scan = p; *scan != '\0'; scan++)
X			if ('0' <= *scan && *scan <= '7' && mask <= 077)
X				mask = (mask << 3) | (*scan - '0');
X			else {	/* Garbage, ignore it. */
X				mask = numask;
X				break;			/* NOTE BREAK OUT */
X			}
X		if (mask != numask) {
X			numask = mask;
X			nonstd = YES;
X		}
X	}
X
X	p = getenv("NEWSMASTER");
X	if (p != NULL && !STREQ(p, nmaster)) {
X		nmaster = p;
X		nonstd = YES;
X	}
X
X	dirsset = YES;
X	if (nonstd)
X		unprivileged();
X}
X
X/*
X - artfile - best pathname for a file in NEWSARTS
X */
Xchar *
Xartfile(base)
Xchar *base;
X{
X	static char *artf = NULL;
X
X	DIRS();
X
X	if (base == NULL)	/* he just wants the directory */
X		return (arts);
X
X	if (artf != NULL)
X		free(artf);	/* toss old returned value */
X	if (pwd != NULL && STREQ(pwd, arts))
X		artf = strsave(base);
X	else
X		artf = str3save(arts, SFNDELIM, base);
X
X	return (artf);
X}
X
X/*
X - fullartfile - full pathname for a file in NEWSARTS
X */
Xchar *
Xfullartfile(base)
Xchar *base;
X{
X	register char *p;
X	register char *pwdsave;
X
X	pwdsave = pwd;
X	pwd = NULL;		/* fool artfile() into giving full path */
X	p = artfile(base);
X	pwd = pwdsave;
X	return (p);
X}
X
X/*
X - ctlfile - full pathname for a file in NEWSCTL
X */
Xchar *
Xctlfile(base)
Xchar *base;
X{
X	static char *ctlf = NULL;
X
X	DIRS();
X
X	if (ctlf != NULL)
X		free(ctlf);		/* toss old returned value */
X
X	if (base == NULL) {
X		ctlf = NULL;
X		return(ctl);
X	} else {
X		ctlf = str3save(ctl, SFNDELIM, base);
X		return(ctlf);
X	}
X}
X
X/*
X - binfile - full pathname for a file in NEWSBIN
X */
Xchar *
Xbinfile(base)
Xchar *base;
X{
X	static char *binf = NULL;
X
X	DIRS();
X
X	if (binf != NULL)
X		free(binf);		/* toss old returned value */
X
X	if (base == NULL) {
X		binf = NULL;
X		return(bin);
X	} else {
X		binf = str3save(bin, SFNDELIM, base);
X		return (binf);
X	}
X}
X
X/*
X - cd - change to a directory, with checking
X */
Xvoid
Xcd(dir)
Xchar *dir;
X{
X	if (pwd != NULL)
X		free(pwd);
X	if (chdir(dir) < 0)
X		errunlock("cannot chdir(%s)", dir);
X	pwd = strsave(dir);
X}
X
X/*
X - newspath - search path for normal system commands
X */
Xchar *
Xnewspath()
X{
X	DIRS();
X	return(path);
X}
X
X/*
X - newsumask - suitable value of umask for news stuff
X */
Xint
Xnewsumask()
X{
X	DIRS();
X	return(numask);
X}
X
X/*
X - newsmaster - mail address to complain to
X */
Xchar *
Xnewsmaster()
X{
X	DIRS();
X	return(nmaster);
X}
!
echo 'libcnews/gethdr.c':
sed 's/^X//' >'libcnews/gethdr.c' <<'!'
X/*
X * gethdr - read an entire RFC 822 header "line", including continuations
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <fgetmfs.h>
X#include <sys/types.h>
X#include "news.h"
X#include "libc.h"
X
X/*
X * Read the first line; if it's a header, repeatedly read lines until a
X * non-continuation line is found.  For each continuation line, grow
X * hdr to accomodate it and append it to hdr.
X * *limitp is updated by subtracting the number of bytes read.
X * 
X */
Xchar *						/* malloced; caller must not free */
Xgethdr(in, limitp, ishdrp)
XFILE *in;
Xregister long *limitp;
Xint *ishdrp;
X{
X	register int c, hdrlen, contlen, limitset = *limitp >= 0;
X	register char *contin = NULL;
X	static char *hdr = NULL;
X
X	nnfree(&hdr);
X	*ishdrp = NO;
X	hdr = fgetmfs(in, (int)*limitp, CONT_NO);
X	if (hdr == NULL)
X		return hdr;
X	hdrlen = strlen(hdr);
X	*limitp -= hdrlen;
X	*ishdrp = ishdr(hdr);
X	if (!*ishdrp)
X		return hdr;
X	while (hdr != NULL && (!limitset || *limitp > 1) && (c = getc(in)) != EOF) {
X		(void) ungetc(c, in);
X
X		if (!iswhite(c))
X			break;
X		contin = fgetmfs(in, (int)*limitp, CONT_NO);
X		if (contin == NULL)
X			break;
X
X		contlen = strlen(contin);
X		*limitp -= contlen;
X		hdr = realloc(hdr, (unsigned)(hdrlen + contlen + 1)); /* 1 for NUL */
X		if (hdr != NULL) {
X			(void) strcpy(hdr + hdrlen, contin);
X			hdrlen += contlen;
X		}
X		free(contin);
X		contin = NULL;
X	}
X	return hdr;
X}
X
X
X/*
X * Is s an RFC 822 header line?
X * If a colon is seen before whitespace, it is.
X */
Xint
Xishdr(s)
Xregister char *s;
X{
X	register char *cp = s;
X	register int c;
X
X	while ((c = *cp) != '\0' && !(isascii(c) && isspace(c)) && c != ':')
X		++cp;
X	return c == ':' && cp > s;
X}
!
echo 'libcnews/strlower.c':
sed 's/^X//' >'libcnews/strlower.c' <<'!'
X/*
X * make a string all lower-case.
X */
X
X#include <ctype.h>
X
Xstrlower(s)
Xregister char *s;
X{
X	for (; *s != '\0'; ++s)
X		if (isascii(*s) && isupper(*s))
X			*s = tolower(*s);
X}
!
echo 'libcnews/complain.c':
sed 's/^X//' >'libcnews/complain.c' <<'!'
X#include <stdio.h>
X
X/*
X - complain - lodge a complaint
X */
Xvoid
Xcomplain(s1, s2)
Xchar *s1;
Xchar *s2;
X{
X	extern char *progname;
X
X	(void) fprintf(stderr, "%s: ", progname);
X	(void) fprintf(stderr, s1, s2);
X	(void) putc('\n', stderr);
X}
!
echo 'libcnews/ngmatch.c':
sed 's/^X//' >'libcnews/ngmatch.c' <<'!'
X/*
X * ngmatch - newsgroup name matching
X *
X * ngmatch returns true iff the newsgroup(s) in ngs match
X * the pattern(s) in ngpat, where
X *
X * 	ngpats: { ngpat { "," ngpat }* }?
X *	ngpat: "!"? word { "." word }*
X *	word: { alphanum }+ | "all"
X *
X * Only one group need match for success.  (Redundant?)
X *
X * For each group, note the depth of each match against the patterns,
X * negated or not.  Ignore mismatches.  The deepest match wins at the end;
X * if it's a tie, negated matches are rejections.
X *
X * A match of any group against the patterns is a success.
X * Failure to match any pattern with a group is a mismatch of that group.
X * Failure to match any group against any pattern is a total failure.
X *
X * "all" in a pattern is a wildcard that matches exactly one word;
X * it does not cross "." (NGDELIM) delimiters.
X */
X
X#include <stdio.h>
X#include <string.h>
X#include <sys/types.h>
X#include "news.h"
X
X#define truth(bool) ((bool)? "yes": "no")
X
X#ifndef STATIC
X#define STATIC /* static */
X#endif
X
X#define ALL "all"			/* word wildcard */
X
X/* private */
Xstatic boolean debug = NO;
X
X/* forwards */
Xextern boolean mpatsmatch();
X
Xvoid
Xmatchdebug(state)
Xboolean state;
X{
X	debug = state;
X}
X
Xboolean
Xngmatch(ngpat, ngs)
Xchar *ngpat, *ngs;
X{
X	register char *ngp;			/* point at current group */
X	register char *ngcomma;
X	register char *rngpat = ngpat;
X
X	if (debug)
X		(void) fprintf(stderr, "ngmatch(`%s', `%s')\n", rngpat, ngs);
X	for (ngp = ngs; ngp != NULL; ngp = ngcomma) {
X		register boolean match;
X
X		INDEX(ngp, NGSEP, ngcomma);
X		if (ngcomma != NULL)
X			*ngcomma = '\0';	/* will be restored below */
X		match = mpatsmatch(rngpat, ngp); /* try 1 group, n-patterns */
X		if (ngcomma != NULL)
X			*ngcomma++ = NGSEP;	/* point after the comma */
X		if (match)
X			return YES;
X	}
X	return NO;			/* no pattern matched any group */
X}
X
X/*
X * Match one group against multiple patterns, as above.
X * The key is to keep track of how deeply plain and negated patterns matched.
X */
XSTATIC boolean
Xmpatsmatch(ngpat, grp)
Xchar *ngpat, *grp;
X{
X	register char *patp;		/* point at current pattern */
X	register char *patcomma;
X	register int depth;
X	register int faildeepest = 0, hitdeepest = 0;	/* in case no match */
X	register boolean negation;
X
X	if (debug)
X		(void) fprintf(stderr, "mpatsmatch(`%s', `%s')\n", ngpat, grp);
X	for (patp = ngpat; patp != NULL; patp = patcomma) {
X		negation = NO;
X		INDEX(patp, NGSEP, patcomma);
X		if (patcomma != NULL)
X			*patcomma = '\0';	/* will be restored below */
X		if (*patp == NGNEG) {
X			++patp;
X			negation = YES;
X		}
X		depth = onepatmatch(patp, grp);	/* try 1 pattern, 1 group */
X		if (patcomma != NULL)
X			*patcomma++ = NGSEP;	/* point after the comma */
X		if (depth == 0)			/* mis-match */
X			;			/* ignore it */
X		else if (negation) {
X			/* record ordinal # of deepest negated matched word */
X			if (depth > faildeepest)
X				faildeepest = depth;
X		} else {
X			/* record ordinal # of deepest plain matched word */
X			if (depth > hitdeepest)
X				hitdeepest = depth;
X		}
X	}
X	if (debug)
X		(void) fprintf(stderr, "mpatsmatch(`%s', `%s') returns %s\n",
X			ngpat, grp, truth(hitdeepest > faildeepest));
X	return hitdeepest > faildeepest;
X}
X
X/*
X * Match a pattern against a group by looking at each word of pattern in turn.
X *
X * On a match, return the ordinal number of the rightmost word that matches.
X * If group runs out first, the match fails; else it succeeds.
X * On a failure, return zero.
X */
XSTATIC int
Xonepatmatch(patp, grp)
Xchar *patp, *grp;
X{
X	register char *rpatwd;		/* used by word match (inner loop) */
X	register char *patdot, *grdot;	/* point at dots after words */
X	register char *patwd, *grwd;	/* point at current words */
X	register int depth = 0;
X
X	for (patwd = patp, grwd = grp; patwd != NULL && grwd != NULL;
X	    patwd = patdot, grwd = grdot, depth++) {
X		register boolean match;
X
X	    	/* null-terminate words */
X	    	INDEX(patwd, NGDELIM, patdot);
X		if (patdot != NULL)
X			*patdot = '\0';		/* will be restored below */
X	    	INDEX(grwd, NGDELIM, grdot);
X		if (grdot != NULL)
X			*grdot = '\0';		/* will be restored below */
X
X		/*
X		 * Match one word of pattern with one word of group.
X		 * A pattern word of "all" matches any group word.
X		 */
X#ifdef FAST_STRCMP
X		match = STREQ(patwd, grwd) || STREQ(patwd, ALL);
X#else
X		match = NO;
X		for (rpatwd = patwd; *rpatwd == *grwd++; )
X			if (*rpatwd++ == '\0') {
X				match = YES;		/* literal match */
X				break;
X			}
X		if (!match) {
X			/* ugly special case match for "all" */
X			rpatwd = patwd;
X			match = *rpatwd++ == 'a' && *rpatwd++ == 'l' &&
X			        *rpatwd++ == 'l' && *rpatwd   == '\0';
X		}
X#endif				/* FAST_STRCMP */
X
X		if (patdot != NULL)
X			*patdot++ = NGDELIM;	/* point after the dot */
X		if (grdot != NULL)
X			*grdot++ = NGDELIM;
X		if (!match) {
X			depth = 0;		/* words differed - mismatch */
X			break;
X		}
X	}
X	/* if group name ran out before pattern, then match fails */
X	if (grwd == NULL && patwd != NULL)
X		depth = 0;
X	if (debug)
X		(void) fprintf(stderr, "onepatmatch(`%s', `%s') returns %d\n",
X			patp, grp, depth);
X	return depth;
X}
X
X#ifdef CROSS_POSTINGS_RESTRICTED
X/*
X * ngtopsame(ngs) - true iff ngs are all in the same top-level distribution
X */
Xboolean
Xngtopsame(ngs)
Xregister char *ngs;
X{
X	register char *nextng;
X
X	INDEX(ngs, NGSEP, nextng);
X	if (nextng == NULL)		/* no groups left */
X		return YES;
X	++nextng;			/* skip NGSEP */
X	return firstsame(ngs, nextng) && ngtopsame(nextng);
X}
X
X/*
X * firstsame(ng1, ng2) - true iff first characters (up to the first
X * NGDELIM or NGSEP) are the same in each string.  Neither string
X * is guaranteed to be null-terminated (a small lie; one *is*).
X */
XSTATIC boolean
Xfirstsame(ng1, ng2)
Xregister char *ng1, *ng2;
X{
X	register int ng1brk;
X	static char delimstr[] = { NGSEP, NGDELIM, '\0' };
X	extern int strcspn();
X
X	ng1brk = strcspn(ng1, delimstr);
X	return ng1brk == strcspn(ng2, delimstr) && STREQN(ng1, ng2, ng1brk);
X}
X#endif				/* CROSS_POSTINGS_RESTRICTED */
!
echo 'libcnews/time.c':
sed 's/^X//' >'libcnews/time.c' <<'!'
X/*
X * time utilities
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/timeb.h>
X#include "libc.h"
X#include "news.h"
X
X/*
X * Write a timestamp of the form "Jun 12 12:34:56.789" on fp.
X * N.B.: no trailing newline is written.
X */
Xvoid
Xtimestamp(fp, timep)
XFILE *fp;
Xtime_t *timep;	/* if non-null, return time() here for later use */
X{
X	struct timeb ftnow;
X	char ms[4];				/* room for "123" and a NUL */
X
X	ftime(&ftnow);
X	if (timep != NULL)
X		*timep = ftnow.time;
X	/* .15 excludes yyyy\n\0; + 4 omits day-of-week */
X	(void) fprintf(fp, "%.15s.", ctime(&ftnow.time) + 4);
X	(void) ltoza(ms, (long)ftnow.millitm, 3);	/* 3 digits of output */
X	(void) fputs(ms, fp);
X}
!
echo 'libcnews/nemalloc.c':
sed 's/^X//' >'libcnews/nemalloc.c' <<'!'
X#include <stdio.h>
X#include <sys/types.h>
X#include "libc.h"
X
Xchar *
Xnemalloc(size)			/* news emalloc - calls errunlock on error */
Xunsigned size;
X{
X	register char *result = malloc(size);
X
X	if (result == NULL)
X		errunlock("out of memory", "");
X	return result;
X}
!
echo 'libcnews/ltoza.c':
sed 's/^X//' >'libcnews/ltoza.c' <<'!'
X/*
X * ltoza, ltozan - long to zero-padded ascii conversions
X *
X * These functions exist only because there is no portable way
X * to do this with printf and there may be no way do it at all
X * with printf on V7, due to a bug in V7's printf.
X */
X#include <stdlib.h>
X
X#define RADIX 10
X
X/*
X * convert value to at most width characters in outstr, padding with
X * zeros on the left (after any sign); do not terminate with a NUL.
X * returns true iff the value fits in width characters.
X */
Xint					/* boolean */
Xltozan(outstr, value, width)
Xchar *outstr;
Xlong value;
Xint width;
X{
X	register char *op = outstr;
X	register long wval = value;
X	register int wwid = width;
X
X	if (wval < 0 && wwid > 0) {
X		*op++ = '-';
X		--wwid;
X		wval = -wval;		/* fails on smallest int; tough */
X	}
X	op += wwid - 1;			/* find right end */
X	while (wwid-- > 0) {		/* generate "wwid" digits */
X		register ldiv_t result;
X
X		result = ldiv(wval, (long)RADIX);	/* shades of V6! */
X		wval = result.quot;
X		*op-- = result.rem + '0';
X	}
X	return wval == 0;
X}
X
X/*
X * convert value to at most width characters in outstr, padding with
X * zeros on the left (after any sign); terminate with a NUL.
X */
Xint					/* boolean */
Xltoza(outstr, value, width)
Xregister char *outstr;			/* char outstr[width+1]; */
Xlong value;
Xregister int width;
X{
X	register int fits = ltozan(outstr, value, width);
X
X	outstr[width] = '\0';
X	return fits;
X}
!
echo 'libcnews/str3save.c':
sed 's/^X//' >'libcnews/str3save.c' <<'!'
X#include <stdio.h>
X#include <sys/types.h>
X#include "libc.h"
X#include "news.h"
X
X/*
X - str3save - malloc space for 3 strings, concatenated, and concatenate them
X * This may seem kind of ad-hoc, but it's just right for filename work.
X */
Xchar *
Xstr3save(s1, s2, s3)
Xchar *s1;
Xchar *s2;
Xchar *s3;
X{
X	register char *p;
X	static char *empty = "";
X
X	if (s1 == NULL)
X		s1 = empty;
X	if (s2 == NULL)
X		s2 = empty;
X	if (s3 == NULL)
X		s3 = empty;
X
X	p = nemalloc((unsigned)(strlen(s1) + strlen(s2) + strlen(s3) + 1));
X	(void) strcpy(p, s1);
X	(void) strcat(p, s2);
X	(void) strcat(p, s3);
X	return(p);
X}
!
echo 'libcnews/lock.c':
sed 's/^X//' >'libcnews/lock.c' <<'!'
X/*
X * C news system locking.
X * It's compatible with B 2.10.1 news, except that locks are never
X * declared stale (blow 'em away in /etc/rc).
X * Only permit relaynews to run on a file server to make this sane.
X */
X
X#include <stdio.h>
X#include <errno.h>
X#include <sys/types.h>
X#include "libc.h"
X#include "news.h"
X#include "config.h"
X
X#define LOCKNAME "LOCK"
X#define LOCKTEMP "LOCKTMXXXXXX"
X#define INTERVAL 25		/* seconds to sleep on a busy lock */
X
Xstatic boolean debug = NO;
Xstatic boolean mylock = NO;
X
Xvoid
Xlockdebug(state)
Xboolean state;
X{
X	debug = state;
X}
X
X/*
X * lock the news system.
X * create a temporary name in $NEWSCTL for linking, store my pid in it.
X * repeatedly try to link the temporary name to LOCKNAME.
X */
Xvoid
Xnewslock()
X{
X	register char *tempnm, *lockfile;
X	register FILE *tempfp;
X	int locktries = 0;
X
X	tempnm = strsave(ctlfile(LOCKTEMP));
X	(void) mktemp(tempnm);
X	tempfp = fopen(tempnm, "w");
X	if (tempfp == NULL)
X		error("can't create lock temporary `%s'", tempnm);
X	(void) fprintf(tempfp, "%d\n", getpid());
X	(void) fclose(tempfp);
X
X	lockfile = strsave(ctlfile(LOCKNAME));
X	while (link(tempnm, lockfile) < 0) {
X		if (errno != EEXIST)
X			error("can't link `%s' to LOCK", tempnm);
X		/*
X		 * Could decide here if the lock is stale.
X		 * If so, remove it and try again to lock.
X		 */
X		if (debug && ++locktries == 1)
X			(void) printf("%s: sleeping on LOCK\n", progname);
X		sleep(INTERVAL);
X	}
X	free(lockfile);
X	(void) unlink(tempnm);
X	free(tempnm);
X	mylock = YES;
X}
X
Xvoid
Xnewsunlock()
X{
X	if (mylock) {
X		(void) unlink(ctlfile(LOCKNAME));
X		mylock = NO;
X	}
X}
X
Xvoid
Xerrunlock(fmt, s)		/* like error(3), but unlock before exit */
Xchar *fmt, *s;
X{
X	warning(fmt, s);
X	newsunlock();
X	exit(1);
X	/* NOTREACHED */
X}
!
echo 'libcnews/fopenclex.c':
sed 's/^X//' >'libcnews/fopenclex.c' <<'!'
X/*
X * fopen and set close-on-exec (to avoid leaking descriptors into children)
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include "news.h"
X
XFILE *
Xfopenwclex(name, mode)	/* open name; close-on-exec if OK, else warning */
Xchar *name, *mode;
X{
X	register FILE *fp;
X
X	if ((fp = fopenclex(name, mode)) == NULL)
X		warning("can't open `%s'", name);
X	return fp;
X}
X
XFILE *
Xfopenclex(file, mode)		/* open file and if OK, close-on-exec */
Xchar *file, *mode;
X{
X	register FILE *fp;
X
X	if ((fp = fopen(file, mode)) != NULL)
X		fclsexec(fp);
X	return fp;
X}
!
echo 'libcnews/string.c':
sed 's/^X//' >'libcnews/string.c' <<'!'
X/*
X * string operations
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <sys/types.h>
X#include "libc.h"
X#include "news.h"
X
X/* forwards */
Xchar *findhost();
X
X/*
X * Return strsave() of the first word in "tokens".
X * Words are delimited by spaces.
X */
Xchar *
Xfirst(tokens)
Xchar *tokens;
X{
X	return strsvto(tokens, ' ');
X}
X
X/*
X * Turn a newsgroup name into a file name, in place.
X */
Xvoid
Xmkfilenm(ng)
Xregister char *ng;
X{
X	for (; *ng != '\0'; ng++)
X		if (*ng == NGDELIM)
X			*ng = FNDELIM;
X}
X
Xvoid
Xtrim(s)					/* trim trailing newline */
Xchar *s;
X{
X	register char *nl;
X
X	INDEX(s, '\n', nl);
X	if (nl != NULL)
X		*nl = '\0';
X}
X
Xchar *
Xskipsp(s)				/* skip any whitespace at *s */
Xregister char *s;
X{
X	while (iswhite(*s))
X		s++;
X	return s;
X}
X
Xchar *
Xstrsvto(s, c)				/* save s up to (but excluding) c */
Xchar *s;
Xint c;
X{
X	register char *endp, *copy;
X
X	endp = index(s, c);		/* find interesting part's end of s */
X	if (endp != NULL)
X		*endp = '\0';		/* restored below */
X	copy = strsave(s);		/* copy interesting substring of s */
X    	if (endp != NULL)
X	    	*endp = c;
X	return copy;
X}
X
Xint
Xcharcount(s, c)			/* how many c's in s? */
Xregister char *s;
Xregister int c;
X{
X	register int count = 0;
X
X#ifdef CLASSY
X	for (; (s = index(s, c)) != NULL; s = (s == NULL? NULL: s+1))
X		++count;
X#else
X	while (*s != '\0')
X		if (*s++ == c)
X			++count;
X#endif				/* CLASSY */
X	return count;
X}
X
Xchar *
Xnullify(s)				/* return s or "" if NULL */
Xregister char *s;
X{
X	if (s == NULL)
X		return "";
X	else
X		return s;
X}
X
X/*
X * If c is NUL, hostchar will be false, so don't test (optimisation: ==).
X */
X#define nothostchar(c, ch) (!hostchar(c, ch) /* || (c) == '\0' */ )
X/*
X * True if c can be part of a hostname. RFC 850 allows letters, digits, periods,
X * and hyphens and specifically disallows blanks. False may mean c is NUL.
X */
X#define hostchar(c, ch) ((ch) = (c), \
X	(isascii(ch) && isalnum(ch) || (ch) == '.' || (ch) == '-'))
X
X/*
X * Return true iff any host in hosts appears in s, as per hostin().
X * hosts are separated by non-hostname characters.
X */
Xboolean
Xanyhostin(hosts, s)
Xchar *hosts, *s;
X{
X	register char *host = hosts;
X
X	while (*host != '\0') {
X		register char *delimp;
X		register int ch;
X		register int delim;
X		register boolean hostisin;
X
X		while (nothostchar(*host, ch) && *host != '\0')
X			++host;			/* skip leading delims */
X		if (*host == '\0')		/* no more hosts */
X			break;
X		for (delimp = host; hostchar(*delimp, ch); delimp++)
X			;			/* skip to next delim */
X		delim = *delimp;		/* may be NUL */
X		*delimp = '\0';			/* terminate host */
X		hostisin = hostin(host, s);
X		*delimp = delim;		/* restore hosts delimiter */
X		if (hostisin)
X			return YES;
X		host = delimp;			/* advance to next host */
X	}
X	return NO;
X}
X
X/*
X * Return true iff host appears in s, with no characters from the alphabet
X * of legal hostname characters immediately adjacent.
X */
Xboolean
Xhostin(host, s)
Xregister char *host, *s;
X{
X	return findhost(host, s) != NULL;
X}
X
X/*
X * Return the number of machines appearing in path,
X * by counting transitions from delimiters.
X * See hostin() for the rules, and the macros.
X */
Xint
Xhopcount(path)
Xregister char *path;
X{
X	register int count = 0;
X	register int ch;
X
X	for (; *path != '\0'; path++)
X		if (nothostchar(path[0], ch) &&
X		    (hostchar(path[1], ch) || path[1] == '\0'))
X			++count;	/* trailing edge of delimiters */
X	return count;
X}
X
Xchar *
Xsendersite(path)
Xregister char *path;
X{
X	register char *p;
X	register int ch;
X	static char *sender = NULL;
X
X	nnfree(&sender);		/* free the last answer */
X	for (p = path; hostchar(*p, ch); p++)
X		;
X	if (*p == '\0')			/* only a user name */
X		return hostname();	/* a local posting */
X	else {
X		register int delim = *p;
X
X		*p = '\0';
X		sender = strsave(path);	/* copy the first machine name */
X		*p = delim;
X		return sender;
X	}
X}
X
X/*
X * Canonicalise rawpath: NULL -> "", chop last site (actually user name) but not
X * its leading delimiter, and if Approved:, chop everything after the site,
X * and its trailing delimiter, from Approved: (or Sender:) (user@host).
X * Result is malloced memory.
X */
Xchar *
Xcanonpath(rawpath, approved, sender)
Xchar *rawpath, *approved, *sender;
X{
X	register char *newpath = strsave(nullify(rawpath));
X	register char *p, *lastdelim = newpath, *site = NULL;
X	register int ch;
X
X	for (p = newpath; *p != '\0'; ++p)
X		if (nothostchar(*p, ch))
X			lastdelim = p + 1;	/* just past delim */
X	if (lastdelim != NULL)
X		*lastdelim = '\0';		/* omit user's name */
X
X	if (approved != NULL) {			/* moderated article */
X		site = index(approved, '@');
X		if (site == NULL)
X			site = index(nullify(sender), '@');
X	}
X	if (site != NULL) {
X		p = findhost(site+1, newpath);
X		if (p != NULL && *p++ != '\0')	/* delim after site? */
X			*p = '\0';		/* terminate newpath after site */
X	}
X	return newpath;
X}
X
X/*
X * Return pointer to the first byte after host in path, if any,
X * with no characters from the alphabet of legal hostname characters
X * immediately adjacent.
X * This function is a profiling hot spot, so it has been optimised.
X */
Xchar *
Xfindhost(host, path)
Xregister char *host, *path;
X{
X	register int hostlen = strlen(host), ch;
X
X	/* Special case: match host!path or host. */
X	if (STREQN(path, host, hostlen) && nothostchar(path[hostlen], ch))
X		return &path[hostlen];
X
X	/* Match path2!host!path or path2!host. */
X	while (*path != '\0')
X		if (hostchar(path[0], ch))	/* can't start after here */
X			++path;
X		else if ((++path, STREQN(path, host, hostlen)) &&
X	   	    nothostchar(path[hostlen], ch))
X			return &path[hostlen];
X	return NULL;
X}
!
echo 'libcnews/hostname.c':
sed 's/^X//' >'libcnews/hostname.c' <<'!'
X/*
X * hostname - return the Usenet name of this machine
X *
X * One interesting possibility would be to assume that the first
X * name in the sys file is our Usenet name, unless it is "ME",
X * which would require our current strategy anyway.
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X
X#include "libc.h"
X#include "news.h"
X#include "config.h"
X
X/* 2BSD funniness */
X#ifdef BSD2_10
X# include <short_names.h>
X#endif
X
X#ifndef NAMEFILE
X#define NAMEFILE ctlfile("whoami")
X#endif
X
Xchar *
Xhostname()			/* return this Usenet machine's name */
X{
X	static char name[MAXHOST];
X
X	if (name[0] == '\0') {	/* try to get the "news hostname" */
X		register FILE *fp;
X
X		fp = fopenclex(NAMEFILE, "r");
X		if (fp != NULL) {
X			(void) fgets(name, sizeof name, fp);
X			(void) nfclose(fp);
X			if (name[0] != '\0' && name[strlen(name) - 1] == '\n')
X				name[strlen(name) - 1] = '\0';
X		}
X	}
X	if (name[0] == '\0')	/* else use the ordinary hostname */
X		(void) gethostname(name, sizeof name);
X	return name;
X}
!
echo 'libcnews/strsave.c':
sed 's/^X//' >'libcnews/strsave.c' <<'!'
X/*
X * strsave - like strdup, but error if can't allocate
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include "libc.h"
X#include "news.h"
X
X/*
X * Copy "s" into malloced memory, if any is available.
X * If not, unlock the news system, print a message and exit,
X * else return the address of the malloced memory.
X */
Xchar *
Xstrsave(s)
Xregister char *s;
X{
X	register char *news =nemalloc((unsigned)strlen(s)+1); /* include NUL */
X
X	(void) strcpy(news, s);
X	return news;
X}
!
echo 'libcnews/Makefile':
sed 's/^X//' >'libcnews/Makefile' <<'!'
X# libcnews makefile
XINCLUDE=../include
XDEFINES=-I$(INCLUDE)
XCOPTS= -O # -pg -p
XCFLAGS= $(COPTS) $(DEFINES)
XLINTFLAGS=-hau $(DEFINES)
XLIB=libcnews.a
X# RANLIB is ranlib on non-USG systems, echo on USG systems
XRANLIB=ranlib
X#RANLIB=:
XSRCS=complain.c config.c fopenclex.c hostname.c \
X lock.c ltoza.c ngmatch.c readline.c \
X string.c strlower.c strsave.c str3save.c time.c
XOBJS = complain.o config.o fopenclex.o gethdr.o hostname.o lock.o ltoza.o \
X	nemalloc.o ngmatch.o str3save.o string.o strlower.o strsave.o time.o
X# workaround for System V make bug
XSHELL = /bin/sh
X
Xu:	$(OBJS)
X	ar ruv ../libcnews.a $(OBJS)
X
Xall:	$(OBJS)
X
X$(LIB): $(SRCS)
X	$(CC) $(CFLAGS) -c $?
X	ar rv $@ *.o
X	rm *.o
X	$(RANLIB) $@
X
Xlint:
X	lint $(LINTFLAGS) $(SRCS)
X
Xclean:
X	rm -f *.o
X
X# header dependencies for libcnews.a
Xconfig.o: $(INCLUDE)/news.h $(INCLUDE)/config.h
Xlock.o: $(INCLUDE)/news.h
Xngmatch.o: $(INCLUDE)/news.h
Xtime.o: $(INCLUDE)/news.h
X# ltoza.o: $(INCLUDE)/stdlib.h	# trouble if stdlib.h is system one, not ours
!
echo 'libfake/getopt.3':
sed 's/^X//' >'libfake/getopt.3' <<'!'
X.TH GETOPT 3 local
X.DA 25 March 1982
X.SH NAME
Xgetopt \- get option letter from argv
X.SH SYNOPSIS
X.ft B
Xint getopt(argc, argv, optstring)
X.br
Xint argc;
X.br
Xchar **argv;
X.br
Xchar *optstring;
X.sp
Xextern char *optarg;
X.br
Xextern int optind;
X.ft
X.SH DESCRIPTION
X.I Getopt
Xreturns the next option letter in
X.I argv
Xthat matches a letter in
X.IR optstring .
X.I Optstring
Xis a string of recognized option letters;
Xif a letter is followed by a colon, the option is expected to have
Xan argument that may or may not be separated from it by white space.
X.I Optarg
Xis set to point to the start of the option argument on return from
X.IR getopt .
X.PP
X.I Getopt
Xplaces in
X.I optind
Xthe
X.I argv
Xindex of the next argument to be processed.
XBecause
X.I optind
Xis external, it is normally initialized to zero automatically
Xbefore the first call to 
X.IR getopt .
X.PP
XWhen all options have been processed (i.e., up to the first
Xnon-option argument),
X.I getopt
Xreturns
X.BR EOF .
XThe special option
X.B \-\-
Xmay be used to delimit the end of the options;
X.B EOF
Xwill be returned, and
X.B \-\-
Xwill be skipped.
X.SH SEE ALSO
Xgetopt(1)
X.SH DIAGNOSTICS
X.I Getopt
Xprints an error message on
X.I stderr
Xand returns a question mark
X.RB ( ? )
Xwhen it encounters an option letter not included in
X.IR optstring .
X.SH EXAMPLE
XThe following code fragment shows how one might process the arguments
Xfor a command that can take the mutually exclusive options
X.B a
Xand
X.BR b ,
Xand the options
X.B f
Xand
X.BR o ,
Xboth of which require arguments:
X.PP
X.RS
X.nf
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	int c;
X	extern int optind;
X	extern char *optarg;
X	\&.
X	\&.
X	\&.
X	while ((c = getopt(argc, argv, "abf:o:")) != EOF)
X		switch (c) {
X		case 'a':
X			if (bflg)
X				errflg++;
X			else
X				aflg++;
X			break;
X		case 'b':
X			if (aflg)
X				errflg++;
X			else
X				bproc();
X			break;
X		case 'f':
X			ifile = optarg;
X			break;
X		case 'o':
X			ofile = optarg;
X			break;
X		case '?':
X		default:
X			errflg++;
X			break;
X		}
X	if (errflg) {
X		fprintf(stderr, "Usage: ...");
X		exit(2);
X	}
X	for (; optind < argc; optind++) {
X		\&.
X		\&.
X		\&.
X	}
X	\&.
X	\&.
X	\&.
X}
X.RE
X.PP
XA template similar to this can be found in
X.IR /usr/pub/template.c .
X.SH HISTORY
XWritten by Henry Spencer, working from a Bell Labs manual page.
XBehavior believed identical to the Bell version.
X.SH BUGS
XIt is not obvious how
X`\-'
Xstanding alone should be treated;  this version treats it as
Xa non-option argument, which is not always right.
X.PP
XOption arguments are allowed to begin with `\-';
Xthis is reasonable but reduces the amount of error checking possible.
X.PP
X.I Getopt
Xis quite flexible but the obvious price must be paid:  there is much
Xit could do that it doesn't, like
Xchecking mutually exclusive options, checking type of
Xoption arguments, etc.
!
echo 'libfake/getopt.c':
sed 's/^X//' >'libfake/getopt.c' <<'!'
X/*
X * getopt - get option letter from argv
X */
X
X#include <stdio.h>
X
Xchar	*optarg;	/* Global argument pointer. */
Xint	optind = 0;	/* Global argv index. */
X
Xstatic char	*scan = NULL;	/* Private scan pointer. */
X
Xextern char	*index();
X
Xint
Xgetopt(argc, argv, optstring)
Xint argc;
Xchar *argv[];
Xchar *optstring;
X{
X	register char c;
X	register char *place;
X
X	optarg = NULL;
X
X	if (scan == NULL || *scan == '\0') {
X		if (optind == 0)
X			optind++;
X	
X		if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0')
X			return(EOF);
X		if (strcmp(argv[optind], "--")==0) {
X			optind++;
X			return(EOF);
X		}
X	
X		scan = argv[optind]+1;
X		optind++;
X	}
X
X	c = *scan++;
X	place = index(optstring, c);
X
X	if (place == NULL || c == ':') {
X		fprintf(stderr, "%s: unknown option -%c\n", argv[0], c);
X		return('?');
X	}
X
X	place++;
X	if (*place == ':') {
X		if (*scan != '\0') {
X			optarg = scan;
X			scan = NULL;
X		} else if (optind < argc) {
X			optarg = argv[optind];
X			optind++;
X		} else {
X			fprintf(stderr, "%s: -%c argument missing\n", argv[0], c);
X			return('?');
X		}
X	}
X
X	return(c);
X}
!
echo 'libfake/ldiv.c':
sed 's/^X//' >'libfake/ldiv.c' <<'!'
X/*
X * ANSI's ldiv(num, denom) - yields (num/denom, num%denom)
X */
X
X#include <stdlib.h>
X
Xldiv_t
Xldiv(num, denom)
Xregister long num, denom;
X{
X	register ldiv_t result;
X
X	result.quot = num/denom;
X	result.rem = num%denom;
X	return result;
X}
!
echo 'libfake/README':
sed 's/^X//' >'libfake/README' <<'!'
XThis is stuff that your system ought to have but might not.  Some of these
Xare just quick fakes that do the right thing for C News but not in general;
Xsome are freely-redistributable portable implementations of the real thing.
X
XThe dbm imitation is exceedingly crude and inefficient but provides full
Xfunctionality.
X
XFsync and symlink are C-News-specific fakes.
X
XLdiv.c is inefficient but provides the full ANSI C functionality.  (Do not
Xconfuse it with the prehistoric ldiv function found in some very old Unixes.)
X
XThe mem* and str* and *index functions come from Henry Spencer's public-domain
Xstring-functions implementation.  Said implementation is somewhat in need of
Xan updating to match ANSI C, and there are minor portability glitches, but in
Xgeneral these should do the right thing for you.
X
XGetopt is Henry Spencer's public-domain implementation of System III getopt.
XIt differs from the System V one in minor ways.
X
XMkdir may give problems if called from a setuid program and either your
Xsystem does not implement setuid(geteuid()) or the program doesn't bother
Xto do it.  Otherwise it's fine.
X
XPutenv is crude but works.
!
echo 'libfake/putenv.c':
sed 's/^X//' >'libfake/putenv.c' <<'!'
X/*
X * putenv - add a variable to the environment, as in SysV
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include "libc.h"
X
X#define YES 1
X#define NO 0
X
X/* peculiar return values */
X#define WORKED 0
X#define FAILED 1
X
Xint
Xputenv(var)			/* put var in the environment */
Xchar *var;
X{
X	register char **envp, **newenv;
X	register int oldenvcnt;
X	extern char **environ;
X
X	/* count variables, look for var */
X	for (envp = environ; *envp != 0; envp++) {
X		register char *varp = var, *ep = *envp;
X		register int namesame;
X
X		namesame = NO;
X		for (; *varp == *ep && *varp != '\0'; ++ep, ++varp)
X			if (*varp == '=')
X				namesame = YES;
X		if (*varp == *ep && *ep == '\0')
X			return WORKED;	/* old & new var's are the same */
X		if (namesame) {
X			*envp = var;	/* replace var with new value */
X			return WORKED;
X		}
X	}
X	oldenvcnt = envp - environ;
X
X	/* allocate new environment with room for one more variable */
X	newenv = (char **)malloc((unsigned)((oldenvcnt+1+1)*sizeof(*envp)));
X	if (newenv == NULL)
X		return FAILED;
X
X	/* copy old environment pointers, add var, switch environments */
X	(void) memcpy((char *)newenv, (char *)environ, oldenvcnt*sizeof(*envp));
X	newenv[oldenvcnt] = var;
X	newenv[oldenvcnt+1] = NULL;
X	environ = newenv;
X	return WORKED;
X}
!
echo 'libfake/Makefile':
sed 's/^X//' >'libfake/Makefile' <<'!'
XINCLUDE = ../include
XCOPTS = -O
XCFLAGS = $(COPTS) -I$(INCLUDE)
X# workaround for System V make bug
XSHELL = /bin/sh
X
XALL = dbm.o fsync.o getopt.o index.o ldiv.o memchr.o memcmp.o memcpy.o \
Xmemset.o mkdir.o putenv.o rindex.o strchr.o strcspn.o strpbrk.o strrchr.o \
Xstrspn.o strtok.o symlink.o
X
X# beware -- build knows about NEEDED
XNEEDED =  ldiv.o
X
Xu:	$(NEEDED)
X	ar ruv ../libcnews.a $(NEEDED)
X
Xall:	$(NEEDED)
X
Xtry:	$(ALL)
X
X# ldiv.o:	$(INCLUDE)/stdlib.h	# trouble if stdlib.h isn't ours
X
Xclean:
X	rm -f *.o
!
echo 'libfake/dbm.c':
sed 's/^X//' >'libfake/dbm.c' <<'!'
X/*
X * Incredibly slow Uglix dbm simulation.
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include "libc.h"
X
X#define STRLEN(s) (sizeof (s) - 1)	/* s must be a char array */
X
Xstatic char *pagname = NULL;
Xstatic FILE *db;
Xstatic int dbrdonly;
X
Xtypedef struct {
X	char *dptr;
X	int dsize;
X} datum;
X
Xdbminit(file)
Xchar *file;
X{
X	dbrdonly = 0;
X
X	if (pagname != NULL)			/* old name? */
X		free(pagname);
X	pagname = malloc((unsigned)(strlen(file) + STRLEN(".pag") + 1));
X	if (pagname == NULL) {
X		warning("cannot allocate memory to open database `%s'\n", file);
X		return -1;
X	}
X	(void) strcpy(pagname, file);
X	(void) strcat(pagname, ".pag");
X
X	if ((db = fopen(pagname, "r+")) == NULL) {
X		db = fopen(pagname, "r");
X		dbrdonly = 1;
X	}
X	if (db == NULL) {
X		warning("cannot open database `%s'\n", file);
X		return -1;
X	}
X	return 0;
X}
X
Xdatum
Xfetch(key)
Xdatum key;
X{
X	datum item;
X
X	rewind(db);
X	while (getitem(&item, db) != EOF)		/* read key */
X		if (strncmp(item.dptr, key.dptr, key.dsize) == 0)
X			if (getitem(&item, db) == EOF)	/* read data */
X				break;
X			else
X				return item;
X	/* EOF */
X	item.dptr = NULL;
X	item.dsize = 0;
X	return item;
X}
X
Xdelete(key)
Xdatum key;
X{
X	datum item;
X	FILE *temp;
X	FILE *tmpfile();
X
X	if (dbrdonly)
X		return -1;
X	temp = tmpfile();
X	if (temp == NULL)
X		return -1;
X	/* copy from db to temp, omitting key & its data */
X	rewind(db);
X	while (getitem(&item, db) != EOF)
X		if (strncmp(item.dptr, key.dptr, key.dsize) == 0) {
X			if (getitem(&item, db) == EOF)	/* toss data too */
X				return -1;
X		} else
X			if (putitem(&item, temp) == EOF)
X				return -1;
X	/* copy back from temp to db */
X	rewind(temp);
X	db = freopen(pagname, "w+", db);
X	while (getitem(&item, temp) != EOF)
X		if (putitem(&item, db) == EOF)
X			return -1;
X	return 0;
X}
X
Xstore(key, dat)
Xdatum key, dat;
X{
X	if (dbrdonly)
X		return -1;
X#ifdef REALDBM			/* else, it's only for news */
X	if (delete(key) == -1)
X		return -1;
X#endif
X	if (putitem(&key, db) == EOF || putitem(&dat, db) == EOF)
X		return -1;
X	return 0;
X}
X
Xdatum
Xfirstkey()
X{
X	datum trash;
X	datum nextkey();
X
X	rewind(db);
X	return nextkey(trash);
X}
X
X/* ARGSUSED */
Xdatum
Xnextkey(key)			/* simplistic version, ignores key */
Xdatum key;
X{
X	static datum dat;
X
X	if (getitem(&dat, db) == EOF)
X		dat.dptr = NULL;
X	return dat;
X}
X
Xstatic int
Xgetitem(datump, fp)
Xregister datum *datump;			/* points at static storage */
XFILE *fp;
X{
X	static char *data = NULL;	/* the current item */
X
X	if (fread((char *)&datump->dsize, sizeof datump->dsize, 1, fp) != 1)
X		return EOF;
X	if (data != NULL)
X		free(data);		/* pitch old item */
X	datump->dptr = data = malloc((unsigned)datump->dsize);
X	if (data == NULL ||
X	    fread(datump->dptr, datump->dsize, 1, fp) != 1)
X		return EOF;
X	return 0;
X}
X
Xstatic int
Xputitem(datump, fp)
Xdatum *datump;
XFILE *fp;
X{
X	if (fwrite((char *)&datump->dsize, sizeof datump->dsize, 1, fp) != 1 ||
X	    fwrite(datump->dptr, datump->dsize, 1, fp) != 1)
X		return EOF;
X	return 0;
X}
!
echo 'libfake/fsync.c':
sed 's/^X//' >'libfake/fsync.c' <<'!'
X/*
X * fsync(2) emulation for systems lacking it
X */
X
X/* ARGSUSED */
Xint
Xfsync(fd)
Xint fd;
X{
X	return 0;
X}
!
echo 'libfake/mkdir.c':
sed 's/^X//' >'libfake/mkdir.c' <<'!'
X/*
X * 4.2BSD mkdir simulation
X */
X
X#include <stdio.h>
X#include <errno.h>
X#include <sys/types.h>	/* argh */
X#include "libc.h"
X
X/* system call returns */
X#define SYS_OK 0
X#define SYS_ERR (-1)
X
X#define UMASK_MASK 0777
X
X#define STRLEN(s) (sizeof (s) - 1)		/* s must be a char array */
X
Xint
Xmkdir(dir, mode)
Xchar *dir;
Xint mode;
X{
X	register char *cbuf = malloc((unsigned)STRLEN("mkdir ") + strlen(dir) + 1);
X	register int oldmask, ret;
X
X	if (cbuf == NULL) {
X		errno = ENOMEM;			/* kludge */
X		return SYS_ERR;
X	}
X	oldmask = umask(0);
X	(void) umask(~(mode & ~oldmask) & UMASK_MASK);
X
X	(void) sprintf(cbuf, "mkdir %s", dir);
X	ret = (system(cbuf) != 0? SYS_ERR: SYS_OK);
X	if (ret == SYS_ERR)
X		errno = EINVAL;			/* kludge */
X
X	(void) umask(oldmask);
X	free(cbuf);
X	return ret;
X}
!
echo 'libfake/symlink.c':
sed 's/^X//' >'libfake/symlink.c' <<'!'
X/*
X * symlink dummy
X */
X
Xint
Xsymlink(n1, n2)
Xchar *n1;
Xchar *n2;
X{
X	extern int errno;
X
X	errno = 0;		/* kludge */
X	return(-1);
X}
!
echo 'libfake/memchr.c':
sed 's/^X//' >'libfake/memchr.c' <<'!'
X/*
X * memchr - search for a byte
X *
X * CHARBITS should be defined only if the compiler lacks "unsigned char".
X * It should be a mask, e.g. 0377 for an 8-bit machine.
X */
X
X#define	NULL	0
X
X#ifndef CHARBITS
X#	define	UNSCHAR(c)	((unsigned char)(c))
X#else
X#	define	UNSCHAR(c)	((c)&CHARBITS)
X#endif
X
Xchar *
Xmemchr(s, ucharwanted, size)
X char * s;
Xint ucharwanted;
Xint size;
X{
X	register  char *scan;
X	register int n;
X	register int uc;
X
X	scan = s;
X	uc = UNSCHAR(ucharwanted);
X	for (n = size; n > 0; n--)
X		if (UNSCHAR(*scan) == uc)
X			return(scan);
X		else
X			scan++;
X
X	return(NULL);
X}
!
echo done


-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.