[comp.os.minix] News for Minix

nick@nswitgould.cs.uts.oz (Nick Andrew) (12/07/89)

WARNING: Posting not for the faint of heart.

	This is a very messy implementation of News 2.11 for MINIX. I will
undoubtedly get flamed for posting such mess; such is life. I've been
using this software successfully with a news feed from ACSnet for over 6
months now, and it is high time Minix had some N_E_W_S software.

Some installation notes (read & follow before typing make!) ...

-	I have been primarily using News to read, not post. The links into
ACSnet were set up in an unusual way, and since I don't run UUPC on my Minix,
I don't know what needs to be changed for UUCP.

-	You will have to make some directories: /usr/spool, /usr/spool/batch,
/usr/spool/news, /usr/lib/news, /usr/local/bin. Make sure /usr/spool is on
a filesystem with plenty of space free (depending on how many newsgroups
you want to get).

-	Several programs use the "r+", "w+" type modes in fopen(). You'll
need to have one of the recently posted Stdio packages for things to work.
I just coded a quick one, and it is known in the Makefile as libcio.a (check
for the -lcio parameter).

-	Some programs won't compile, for some reason or other. caesar.c for
example. I guess you can safely ignore those; I did!  The ones to worry
about are inews, rnews, postnews, expire, and unbatch.

-	getdate.y required yacc. I used a real unix yacc to make getdate.c
but have only included getdate.s (shar'ed as getdate.s.uu).

-	The original makefile did a lot of customisation work (running
customize shell scripts before recompiling etc..). These have all been
removed as they stood in the way of the port. What you see is what you
get.

-	I haven't tried vnews at all; preferring to use vn.

-	You will have to set up various config files; active, newsgroups,
sys, distributions, etc... The install manual should tell a lot about this.

-	unbatch has been heavily modified to do its work on a compressed
news batch by decompressing to a temporary file, and then running inews on
that file. Minix's memory limitations don't allow any other method.

-	This posting comes as 12 source postings (src directory), followed
in a day or two by postings of the (essentially unmodified) manuals and
user guides etc.




My aim in making this posting is to allow the dedicated Minix hackers to
create a real News for Minix. I have not the time nor the resources to
spend further on News, and I feel that since the software is running
fairly stable on my own system, it would benefit the rest of the net,
despite the obvious work people will have to go through to get it running
locally.

Finally, good luck, and happy newsreading. Nick.

			"Zeta Microcomputer Software"
ACSnet:    nick@ultima.cs.uts.oz
UUCP:      ...!uunet!munnari!ultima.cs.uts.oz!nick
Fidonet:   Nick Andrew on 3:713/602 (Zeta)
-- 
		"Zeta Microcomputer Software"
ACSnet:    nick@ultima.cs.uts.oz
UUCP:      ...!uunet!munnari!ultima.cs.uts.oz!nick
Fidonet:   Nick Andrew on 3:713/602 (Zeta)

nick@ultima.cs.uts.oz (Nick Andrew) (12/07/89)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  README Notes visual.c
# Wrapped by nick@nswitgould on Thu Dec  7 22:39:35 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(2761 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XWARNING: Posting not for the faint of heart.
X
X	This is a very messy implementation of News 2.11 for MINIX. I will
Xundoubtedly get flamed for posting such mess; such is life. I've been
Xusing this software successfully with a news feed from ACSnet for over 6
Xmonths now, and it is high time Minix had some N_E_W_S software.
X
XSome installation notes (read & follow before typing make!) ...
X
X-	I have been primarily using News to read, not post. The links into
XACSnet were set up in an unusual way, and since I don't run UUPC on my Minix,
XI don't know what needs to be changed for UUCP.
X
X-	You will have to make some directories: /usr/spool, /usr/spool/batch,
X/usr/spool/news, /usr/lib/news, /usr/local/bin. Make sure /usr/spool is on
Xa filesystem with plenty of space free (depending on how many newsgroups
Xyou want to get).
X
X-	Several programs use the "r+", "w+" type modes in fopen(). You'll
Xneed to have one of the recently posted Stdio packages for things to work.
XI just coded a quick one, and it is known in the Makefile as libcio.a (check
Xfor the -lcio parameter).
X
X-	Some programs won't compile, for some reason or other. caesar.c for
Xexample. I guess you can safely ignore those; I did!  The ones to worry
Xabout are inews, rnews, postnews, expire, and unbatch.
X
X-	getdate.y required yacc. I used a real unix yacc to make getdate.c
Xbut have only included getdate.s (shar'ed as getdate.s.uu).
X
X-	The original makefile did a lot of customisation work (running
Xcustomize shell scripts before recompiling etc..). These have all been
Xremoved as they stood in the way of the port. What you see is what you
Xget.
X
X-	I haven't tried vnews at all; preferring to use vn.
X
X-	You will have to set up various config files; active, newsgroups,
Xsys, distributions, etc... The install manual should tell a lot about this.
X
X-	unbatch has been heavily modified to do its work on a compressed
Xnews batch by decompressing to a temporary file, and then running inews on
Xthat file. Minix's memory limitations don't allow any other method.
X
X-	This posting comes as 12 source postings (src directory), followed
Xin a day or two by postings of the (essentially unmodified) manuals and
Xuser guides etc.
X
X
X
X
XMy aim in making this posting is to allow the dedicated Minix hackers to
Xcreate a real News for Minix. I have not the time nor the resources to
Xspend further on News, and I feel that since the software is running
Xfairly stable on my own system, it would benefit the rest of the net,
Xdespite the obvious work people will have to go through to get it running
Xlocally.
X
XFinally, good luck, and happy newsreading. Nick.
X
X			"Zeta Microcomputer Software"
XACSnet:    nick@ultima.cs.uts.oz
XUUCP:      ...!uunet!munnari!ultima.cs.uts.oz!nick
XFidonet:   Nick Andrew on 3:713/602 (Zeta)
END_OF_FILE
if test 2761 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'Notes' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Notes'\"
else
echo shar: Extracting \"'Notes'\" \(519 characters\)
sed "s/^X//" >'Notes' <<'END_OF_FILE'
XNick@ultima.cs.uts.oz's notes about News ...
X
Xinews -C fails (create new newsgroup), because it forks and executes
X	itself again. The fork fails. Perhaps an execl would have been
X	a better choice! (does execl get rid of the old process before
X	creating the new one?)
X
Xcompress works ... but it is only a 12-bit compress. It seems to be
X	compatible with the 1.4a compress (the braindead one) and the
X	previous, compatible compress.
X
Xbatch & unbatch work ... fine
X
Xrnews must be in bindir, while inews must be in libdir.
END_OF_FILE
if test 519 -ne `wc -c <'Notes'`; then
    echo shar: \"'Notes'\" unpacked with wrong size!
fi
# end of 'Notes'
fi
if test -f 'visual.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'visual.c'\"
else
echo shar: Extracting \"'visual.c'\" \(55369 characters\)
sed "s/^X//" >'visual.c' <<'END_OF_FILE'
X/*
X * visual - visual news interface.
X * Kenneth Almquist
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)visual.c	1.36	3/21/87";
X#endif /* SCCSID */
X
X#include "rparams.h"
X#ifdef USG
X#include <sys/ioctl.h>
X#include <termio.h>
X#include <fcntl.h>
X#else /* !USG */
X#include <sgtty.h>
X#endif /* !USG */
X
X#include <errno.h>
X#if defined(BSD4_2) || defined(BSD4_1C)
X#include <sys/dir.h>
X#else
X#include "ndir.h"
X#endif
X#ifdef BSD4_2
X#ifndef sigmask
X#define sigmask(m) (1<<((m)-1))
X#endif /* !sigmask */
X#endif /* BSD4_2 */
X#ifdef MYDB
X#include "db.h"
X#endif /* MYDB */
X
Xextern int errno;
X
X#ifdef SIGTSTP
X#include <setjmp.h>
X#endif /* SIGTSTP */
X
X#define ARTWLEN	(ROWS-2)/* number of lines used to display article */
X#define even(cols) ((cols&1) ? cols + 1 : cols)
X#ifdef STATTOP
X#define PRLINE	0	/* prompter line */
X#define SPLINE	1	/* secondary prompt line */
X#define ARTWIN	2	/* first line of article window */
X#define SECPRLEN 81	/* length of secondary prompter */
X#else
X#define PRLINE	(ROWS-1)/* prompter line */
X#define SPLINE	(ROWS-2)/* secondary prompt line */
X#define ARTWIN	0	/* first line of article window */
X#define SECPRLEN 100	/* length of secondary prompter */
X#endif
X
X#define PIPECHAR '|'	/* indicate save command should pipe to program */
X#define	CAGAIN	('e'&0x1F)	/* Save-to-same-place indicator */
X#define META	0200	/* meta character bit (as in emacs) */
X/* print (display) flags */
X#define HDRONLY	0001	/* print header only */
X#define NOPRT	0002	/* don't print at all */
X#define NEWART	0004	/* force article display to be regenerated */
X#define HELPMSG	0010	/* display currently contains help message */
X/* prun flags */
X#define CWAIT	0001	/* type "continue?" and wait for return */
X#define BKGRND	0002	/* run process in the background */
X/* values of curflag */
X#define CURP1	1	/* cursor after prompt */
X#define CURP2	2	/* cursor after secondary prompt */
X#define CURHOME	3	/* cursor at home position */
X/* flags for vsave routine */
X#define SVHEAD	01	/* write out article header */
X#define OVWRITE	02	/* overwrite the file if it already exists */
X/* other files */
X
X#define	saveart	oobit = bit;strcpy(ofilename1, filename);strcpy(ogroupdir, groupdir);hptr = h;h = hold;hold = hptr;ongsize = pngsize
X#define NLINES(h, fp) (h->numlines[0] ? h->intnumlines : (h->intnumlines=linecnt(fp),sprintf(h->numlines, "%d", h->intnumlines), h->intnumlines))
X
X/* terminal handler stuff */
Xextern int _junked;
X#define clearok(xxx, flag) _junked = flag
Xextern int COLS;
Xextern int ROWS;
Xextern int hasscroll;
X
XFILE *tmpfile();
Xchar *getmailname();
X#ifdef MYDB
Xchar *findparent();
X#endif /* MYDB */
Xint onint();
Xint onstop();
Xint xxit();
X
Xchar *Progname = "vnews";		/* for xerror */
X
X/* variables shared between vnews routines */
Xstatic char linebuf[LBUFLEN];		/* temporary workspace */
Xstatic FILE *tfp;			/* temporary file */
Xstatic char tfname[] = "/tmp/vnXXXXXX";	/* name of temp file */
Xstatic long artbody;			/* offset of body into article */
Xstatic int quitflg;			/* if set, then quit */
Xstatic int erased;			/* current article has been erased */
Xstatic int artlines;			/* # lines in article body */
Xstatic int artread;			/* entire article has been read */
Xstatic int hdrstart;			/* beginning of header */
Xstatic int hdrend;			/* end of header */
Xstatic int lastlin;			/* number of lines in tempfile */
Xstatic int tflinno = 0;			/* next line in tempfile */
Xstatic int maxlinno;			/* number of lines in file + folded */
Xstatic char secpr[SECPRLEN];		/* secondary prompt */
Xstatic char prompt[30];			/* prompter */
Xstatic short prflags;			/* print flags (controls updscr) */
Xstatic short curflag;			/* where to locate cursor */
Xstatic int dlinno;			/* top line on screen */
Xstatic char timestr[20];		/* current time */
Xstatic int ismail;			/* true if user has mail */
Xstatic char *mailf;			/* user's mail file */
Xstatic int alflag;			/* set if unprocessed alarm signal */
Xstatic int atend;			/* set if at end of article */
Xstatic char cerase;			/* erase character */
Xstatic char ckill;			/* kill character */
Xstatic char cintr;			/* interrupt character */
X#ifdef TIOCGLTC
Xstatic char cwerase;			/* word erase character */
X#endif /* TIOCGLTC */
Xshort ospeed;				/* terminal speed NOT STATIC */
Xstatic int intflag;			/* set if interrupt received */
X
X#ifdef SIGTSTP
Xstatic int reading;			/* to keep stupid BSD from restarting reads */
Xjmp_buf intjmp, alrmjmp;
X#endif /* SIGTSTP */
X
X#ifdef MYDB
Xstatic int hasdb;			/* true if article data base exists */
X#endif /* MYDB */
X
X#ifdef DIGPAGE
Xstatic int endsuba;			/* end of sub-article in digest */
X#endif
X
X#ifdef MYDEBUG
XFILE *debugf;				/* file to write debugging info on */
X#endif
X
Xchar *tft = "/tmp/folXXXXXX";
X
X/*
X * These were made static for u370 with its buggy cc.
X * I judged it better to have one copy with no ifdefs than
X * to conditionally compile them as automatic variables
X * in readr (which they originally were).  Performance
X * considerations might warrant moving some of the simple
X * things into register variables, but I don't know what
X * breaks the u370 cc.
X */
Xstatic char goodone[BUFLEN];		/* last decent article		*/
Xstatic char ogroupdir[BUFLEN];		/* last groupdir		*/
Xstatic char edcmdbuf[128];
Xstatic int rfq = 0;			/* for last article		*/
Xstatic long ongsize;			/* Previous ngsize		*/
Xstatic long pngsize;			/* Printing ngsize		*/
Xstatic char *bptr;			/* temp pointer.		*/
Xstatic char *tfilename;			/* temporary file name 		*/
Xstatic char ofilename1[BUFLEN];		/* previous file name		*/
Xstatic struct hbuf hbuf1, hbuf2; 	/* for minusing			*/
Xstatic struct hbuf *h = &hbuf1,		/* current header		*/
X		*hold = &hbuf2,		/* previous header		*/
X		*hptr;			/* temporary			*/
Xstatic char *ptr1, *ptr2, *ptr3;	/* for reply manipulation	*/
Xstatic int  aabs = FALSE;		/* TRUE if we asked absolutely	*/
Xstatic char *ed, tf[100];
Xstatic long oobit;			/* last bit, really		*/
Xstatic int dgest = 0;
Xstatic FILE *fp;			/* current article to be printed*/
X
Xreadr()
X{
X
X#ifdef MYDEBUG
X	debugf = fopen("DEBUG", "w");
X	setbuf(debugf, (char *)NULL);
X#endif
X	if (aflag) {
X		if (*datebuf) {
X			if ((atime = cgtdate(datebuf)) == -1)
X				xerror("Cannot parse date string");
X		} else
X			atime = 0;
X	}
X
X	if (SigTrap)
X		xxit(1);
X	(void) mktemp(tfname);
X	(void) close(creat(tfname,0666));
X	if ((tfp = fopen(tfname, "w+")) == NULL)
X		xerror("Can't create temp file");
X	(void) unlink(tfname);
X	mailf = getmailname();
X#ifdef MYDB
X	if (opendb() >= 0) {
X		hasdb = 1;
X		fputs("Using article data base\n", stderr);	/*DEBUG*/
X		getng();
X	}
X#endif
X	ttysave();
X	(void) signal(SIGINT, onint);
X	(void) signal(SIGQUIT, xxit);
X	if (SigTrap)
X		xxit(1);
X	ttyraw();
X	timer();
X
X	/* loop reading articles. */
X	fp = NULL;
X	obit = -1;
X	nextng();
X	quitflg = 0;
X	while (quitflg == 0) {
X		if (getnextart(FALSE))
X			break;
X		(void) strcpy(goodone, filename);
X		if (SigTrap)
X			return;
X		vcmd();
X	}
X
X	if (!news) {
X		ttycooked();
X		ospeed = 0;	/* to convince xxit() not to clear screen */
X		fprintf(stderr, "No news.\n");
X	}
X}
X
X/*
X * Read and execute a command.
X */
Xvcmd() {
X	register c;
X	char *p;
X	long count;
X	int countset;
X
X	if (prflags & HDRONLY)
X		appfile(fp, lastlin + 1);
X	else
X		appfile(fp, dlinno + ARTWLEN + 1);
X
X#ifdef DIGPAGE
X	endsuba = findend(dlinno);
X	if (artlines > dlinno + ARTWLEN
X	 || endsuba > 0 && endsuba < artlines
X#else
X	if (artlines > dlinno + ARTWLEN
X#endif
X	 || (prflags & HDRONLY) && artlines > hdrend) {
X		atend = 0;
X		if (prflags&HDRONLY || maxlinno == 0)
X			(void) strcpy(prompt, "more? ");
X		else
X#ifdef DIGPAGE
X			(void) sprintf(prompt, "more(%d%%)? ",
X				((((endsuba > 0) ?
X				endsuba : (dlinno + ARTWLEN)) -
X				hdrend) * 100) / maxlinno);
X#else /* !DIGPAGE */
X			(void) sprintf(prompt, "more(%d%%)? ",
X				((dlinno + ARTWLEN - hdrend) * 100) / maxlinno);
X#endif /* !DIGPAGE */
X	} else {
X		atend = 1;
X		(void) strcpy(prompt, "next? ");
X		if (!erased)
X			clear(bit);		/* article read */
X	}
X	curflag = CURP1;
X	p = prompt + strlen(prompt);
X	countset = 0;
X	count = 0;
X	/*
X	 * Loop while accumulating a count, until an action character
X	 * is entered. Also handle "meta" here.
X	 *
X	 * Count is the current count. Countset=0 means no count
X	 * currently exists. Countset=1, count=0 is valid and means 
X	 * a count of 0 has been entered 
X	 */
X	for (;;) {
X		c = vgetc();
X		if (c == cerase || c == '\b' || c == '\177') {
X			if (countset == 0)
X				break;		/* Use as action char */
X			if (count < 10)
X				countset = 0;	/* Erase only char of count */
X			else
X				count /= 10L;	/* Erase 1 char of count */
X		} else {
X#ifdef TIOCGLTC
X			if (c == ckill || c == cwerase) {
X#else
X			if (c == ckill) {
X#endif
X				if (countset == 0)
X					break;
X				countset = 0;
X			} else if (c < '0' || c > '9')
X					break;
X				else {
X					countset = 1;
X					count = (count * 10) + (c - '0');
X				}
X		}
X		if (countset) {
X			(void) sprintf(p, "%ld", count);
X		} else {
X			*p = '\0';
X			count = 0;
X		}
X	}
X
X	if (c == '\033') {			/* escape */
X		(void) strcat(prompt, "M-");
X		c = vgetc();
X		if (c != cintr)
X			c |= META;
X	}
X	secpr[0] = '\0';
X	if (countset == 0)
X		count = 1;
X	docmd(c, count, countset);
X	if (c != '?' && c != 'H')		/* UGGH */
X		prflags &=~ HELPMSG;
X	if (dlinno > hdrstart)
X		prflags &=~ HDRONLY;
X}
X
X
X/*
X * Process one command, which has already been typed in.
X */
Xdocmd(c, count, countset)
Xint c;
Xlong count;
Xint countset;
X{
X	int i;
X	long nart, Hoffset;
X	char *findhist();
X
X	switch (c) {
X
X	/* display list of articles in current group */
X	case 'l':
X	case 'L':
X		botscreen();
X		ttycooked();
X		list_group(groupdir, countset ? count : 0,
X			(c == 'l') ? FALSE : TRUE, pngsize);
X		ttyraw();
X		clearok(curscr, 1);
X		updscr();
X		break;
X
X	/* Show more of current article, or advance to next article */
X	case '\n':
X	case ' ':
X#ifdef DIGPAGE
X	case 'm':
X#endif /* DIGPAGE */
X	case '\06':	/* Control-F for vi compat */
X		prflags &=~ NOPRT;
X		if (atend)
X			goto next;
X		else if (prflags & HDRONLY) {
X			prflags &=~ HDRONLY;
X			if (hasscroll)
X				dlinno = hdrstart;}
X#ifdef DIGPAGE
X		else if (endsuba > 0)
X			dlinno = endsuba;
X		else if (c == 'm') {
X			do {
X				if (lastlin >= maxlinno)
X					goto next;
X				else
X					appfile(fp, lastlin + 1);
X			} while(strncmp(linebuf, "------------------------", 24)
X				!= 0);
X			dlinno = endsuba = lastlin;
X		}
X#endif
X		else if ((appfile(fp, dlinno + 2 * ARTWLEN), artread)
X		 && hasscroll && artlines - dlinno <= ARTWLEN + 2)
X			dlinno = artlines - ARTWLEN;
X		else
X			dlinno += ARTWLEN * count;
X		break;
X
X	/* No.  Go on to next article. */
X	case '.':	/* useful if you have a keypad */
Xnext:	case 'n':
X		readmode = NEXT;
X		FCLOSE(fp);
X		clear(bit);
X		saveart;
X		nextbit();
X		break;
X
X
X	/* Back up count pages */
X	case '\b':	
X	case '\177':	
X		if (dlinno == 0)
X			goto backupone;
X		/* NO BREAK */
X	case META|'v':
X	case '\002':	/* Control-B */
X		dlinno -= ARTWLEN * count;
X		if (dlinno < 0)
X			dlinno = 0;
X		break;
X
X	/* forward half a page */
X	case '\004':	/* Control-D, as in vi */
X		if (!atend)
X			dlinno += ARTWLEN/2 * count;
X		break;
X
X	/* backward half a page */
X	case '\025':	/* Control-U */
X		dlinno -= ARTWLEN/2 * count;
X		if (dlinno < 0)
X			dlinno = 0;
X		break;
X
X	/* forward count lines */
X	case '\016':	/* Control-N */
X	case '\005':	/* Control-E */
X		dlinno += count;
X		break;
X
X	/* backwards count lines */
X	case '\020':	/* Control-P */
X	case '\031':	/* Control-Y */
X		dlinno -= count;
X		if (dlinno < 0)
X			dlinno = 0;
X		break;
X
X	/* Turn displaying of article back on */
X	case 'd':
X		prflags &=~ NOPRT;
X		break;
X
X	/* display header */
X	case 'h':
X		dlinno = hdrstart;
X		prflags |= HDRONLY;
X		prflags &=~ NOPRT;
X		break;
X
X	/*
X	 * Unsubscribe to the newsgroup and go on to next group
X	 */
X
X	case 'U':
X	case 'u':
X		strcat(prompt, "u");
X		c = vgetc();
X		if (c == 'g') {
X			obit = -1;
X			FCLOSE(fp);
X			zapng = TRUE;
X			saveart;
X			if (nextng()) {
X				if (actdirect == BACKWARD)
X					msg("Can't back up.");
X				else
X					quitflg = 1;	/* probably unnecessary */
X			}
X		} else {
X			if (c != cintr && c != ckill)
X				beep();
X				msg("Illegal command");
X		}
X		break;
X
X		/* Print the current version of news */
X	case 'v':
X		msg("News version: %s", news_version);
X		break;
X
X
X	/* Decrypt joke.  Always does rot 13 */
X	case 'D':
X		appfile(fp, 32767);
X		for (i = hdrend ; i < artlines ; i++) {
X			register char ch, *p;
X			tfget(linebuf, i);
X			for (p = linebuf ; (ch = *p) != '\0' ; p++) {
X				if (ch >= 'a' && ch <= 'z')
X					*p = (ch - 'a' + 13) % 26 + 'a';
X				else if (ch >= 'A' && ch <= 'Z')
X					*p = (ch - 'A' + 13) % 26 + 'A';
X			}
X			tfput(linebuf, i);
X		}
X		prflags |= NEWART;
X		prflags &=~ (HDRONLY|NOPRT);
X		break;
X
X		/* write out the article someplace */
X		/* w writes out without the header */
X		/* | defaults to pipeing */
X	{
X		static char savebuf[BUFLEN];
X		int wflags;
X
X	case PIPECHAR:
X	case 's':
X	case 'w':
X		/* We loop back to here each time user types ^U to prompt */
X		do {
X			/* Prompt based on command char */
X			msg( (c==PIPECHAR)? "|": "file: ");
X			curflag = CURP2;
X			while ((wflags = vgetc()) == ' ');
X			if (wflags == cintr) {
X				secpr[0] = '\0';
X				break;
X			}
X			if (wflags != CAGAIN) {
X				if ((wflags & 0x1F) == wflags) {	/* control char */
X					pushback(wflags);
X					savebuf[0] = 0;
X				} else {
X					if (c == PIPECHAR) {
X						savebuf[0] = PIPECHAR;
X						savebuf[1] = wflags;
X						savebuf[2] = 0;
X					} else {
X						savebuf[0] = wflags;
X						savebuf[1] = 0;
X					}
X				}
X			} else {
X				/* don't let them pipe to a saved filename */
X				if (c == PIPECHAR && savebuf[0] != PIPECHAR) {
X					savebuf[0] = PIPECHAR;
X					savebuf[1] = 0;
X				}
X			}
X					
X			wflags = prget( (savebuf[0] == PIPECHAR) ? "" : "file: ",
X					savebuf);
X		} while (wflags == 2);
X		if (wflags) break;	/* Interrupted out */
X		wflags = 0;
X		if (c == PIPECHAR) c = 's';
X		if (c == 's')
X			wflags |= SVHEAD;
X		if (count != 1)
X			wflags |= OVWRITE;
X		bptr = savebuf;
X		while( *bptr == ' ')
X			bptr++;	/* strip leading spaces */
X
X		if (*bptr != PIPECHAR && *bptr != '/') {
X			char	hetyped[BUFLEN];
X			char	*boxptr;
X			(void) strcpy(hetyped, bptr);
X			if (hetyped[0] == '~' && hetyped[1] == '/') {
X  				strcpy(hetyped, bptr+2);
X  				strcpy(bptr, userhome);
X			} else if (boxptr = getenv("NEWSBOX")) {
X 				if (index(boxptr, '%')) {
X					struct stat stbf;
X 					sprintf(bptr, boxptr, groupdir);
X 					if (stat(bptr,&stbf) < 0) {
X 						if (mkdir(bptr, 0777) < 0) {
X							msg("Cannot create directory %s", bptr);
X							break;
X						}
X					} else if ((stbf.st_mode&S_IFMT) !=  S_IFDIR) {
X						msg("%s not a directory", bptr);
X						break;
X					}
X				} else
X					strcpy(bptr, boxptr);
X  			 } else
X  				bptr[0] = '\0';
X
X			if (bptr[0])
X				(void) strcat(bptr, "/");
X			if (hetyped[0] != '\0')
X				(void) strcat(bptr, hetyped);
X			else
X				(void) strcat(bptr, "Articles");
X		}
X
X		/* handle ~/ for pipes */
X		if (*bptr == PIPECHAR) {
X			char	fullname[BUFLEN];
X			bptr++;		/* skip PIPECHAR */
X			while( *bptr == ' ')
X				bptr++;	/* strip leading spaces */
X			if (bptr[0] == '~' && bptr[1] == '/') {
X				strcpy(fullname,userhome);
X				strcat(fullname,bptr+2);
X			} else
X				strcpy(fullname,bptr);
X			/* we know PIPECHAR is in *savebuf */
X			strcpy(savebuf+1,fullname);
X			bptr = savebuf;
X		}
X				
X		vsave(bptr, wflags);
X		break;
X	}
X
X		/* back up  */
X	case '-':
Xcaseminus:
X		aabs = TRUE;
X		if (!*ofilename1) {
X			msg("Can't back up.");
X			break;
X		}
X		FCLOSE(fp);
X		hptr = h;
X		h = hold;
X		hold = hptr;
X		(void) strcpy(bfr, filename);
X		(void) strcpy(filename, ofilename1);
X		(void) strcpy(ofilename1, bfr);
X		obit = bit;
X		if (strcmp(groupdir, ogroupdir)) {
X			(void) strcpy(bfr, groupdir);
X			selectng(ogroupdir, FALSE, FALSE);
X			(void) strcpy(groupdir, ogroupdir);
X			(void) strcpy(ogroupdir, bfr);
X			ngrp = 1;
X			back();
X		}
X		bit = oobit;
X		oobit = obit;
X		obit = -1;
X		getnextart(TRUE);
X		break;
X
X		/* skip forwards */
X	case '+':
X	case '=':
Xcaseplus:	if (count == 0)
X			break;
X		saveart;
X		last = bit;
X		for (i = 0; i < count; i++) {
X			nextbit();
X			if ((bit > pngsize) || (rflag && bit < 1))
X				break;
X		}
X		FCLOSE(fp);
X		obit = -1;
X		break;
X
X	/* exit - time updated to that of most recently read article */
X	case 'q':
X		quitflg = 1;
X		break;
X
X	case 'x':
X		xxit(0);
X		break;
X
X	/* cancel the article. */
X	case 'c':
X		strcpy(prompt, "cancel [n]? ");
X		if (vgetc() != 'y') {
X			msg("Article not cancelled");
X			break;
X		}
X		cancel_command();
X		break;
X
X	/* escape to shell */
X	case '!': {
X		register char *p;
X		int flags;
X
X		p = linebuf;
X		*p = 0;
X		if (prget("!", p))
X			break;
X		flags = CWAIT;
X		if (*p == '\0') {
X			(void) strcpy(linebuf, SHELL);
X			flags = 0;
X		}
X		while (*p) p++;
X		while (p > linebuf && p[-1] == ' ')
X			p--;
X		if (*--p == '&') {
X			*p = '\0';
X			flags = BKGRND;
X		} else if (*p == PIPECHAR) {
X			*p = '\0';
X			(void) sprintf(bfr, "(%s)%cmail '%s'", linebuf, PIPECHAR, username);
X			(void) strcpy(linebuf, bfr);
X			flags |= BKGRND;
X		} else {
X			prflags |= NOPRT;
X		}
X		shcmd(linebuf, flags);
X		break;
X	}
X
X	/* mail reply */
X	case 'r':
X		reply(FALSE);
X		break;
X
X	case 'R':
X		reply(TRUE);
X		break;
X
X	case META|'r':
X		direct_reply();
X		break;
X
X	/* next newsgroup */
X	case 'N':
X		FCLOSE(fp);
X		if (next_ng_command())
X			quitflg = 1;
X		break;
X
X	/*  mark the rest of the articles in this group as read */
X	case 'K':
X		saveart;
X		while (bit <= ngsize && bit >= minartno) {
X			clear(bit);
X			nextbit();
X		}
X		FCLOSE(fp);
X		break;
X
X	/* Print the full header */
X	case 'H':
X		if (fp == NULL) {
X			msg("No current article");
X			break;
X		}
X		move(ARTWIN, 0);
X		Hoffset = ftell(fp);
X		(void) fseek(fp, 0L, 0);
X		for (i = 0; i < ARTWLEN; i++) {
X			if (fgets(linebuf, COLS, fp) == NULL)
X				break;
X			if (linebuf[0] == '\n')
X				break;
X			linebuf[COLS] = '\0';
X			addstr(linebuf);
X		}
X		(void) fseek(fp, Hoffset, 0);
X		for(; i < ARTWLEN; i++)
X			addstr(linebuf);
X		prflags |= HELPMSG|NEWART;
X		break;
X	case 'b':	/* backup 1 article */
Xbackupone:
X		count = bit - 1;
X		/* NO BREAK */
X
X	case 'A':	/* specific number */
X		if (count > pngsize) {
X			msg("not that many articles");
X			break;
X		}
X		readmode = SPEC;
X		aabs = TRUE;
X		bit = count;
X		obit = -1;
X		FCLOSE(fp);
X		break;
X
X	/* display parent article */
X	case 'p':
X#ifdef MYDB
X		if (hasdb && (ptr3 = findparent(h->ident, &nart)) != NULL) {
X			msg("parent: %s/%ld", ptr3, nart);	/*DEBUG*/
X			updscr();				/*DEBUG*/
X			goto selectart;
X		}
X#endif
X		if (h->followid[0] == '\0') {
X			msg("no references line");
X			break;
X		}
X		ptr1 = h->followid + strlen(h->followid);
X		do {
X			ptr2 = ptr1;
X			if (*ptr2 == '\0')
X				ptr1 = rindex(h->followid, ' ');
X			else {
X				*ptr2 = '\0';
X				ptr1 = rindex(h->followid, ' ');
X				*ptr2 = ' ';
X			}
X		} while (ptr1 != NULL && --count > 0);
X		if (ptr1 == NULL)
X			ptr1 = h->followid;
X		else	++ptr1;
X		(void) strncpy(linebuf, ptr1, ptr2 - ptr1);
X		linebuf[ptr2 - ptr1] = '\0';
X		msg("%s", linebuf);
X		curflag = CURP2;
X		updscr();		/* may take this out later */
X		goto searchid;
X	/* specific message ID. */
X	case '<':
X		/* could improve this */
X		linebuf[0] = '<'; linebuf[1] = 0;
X		if (prget("", linebuf)) {
X			secpr[0] = 0;
X			break;
X		}
Xsearchid:	secpr[0] = '\0';
X		if (index(linebuf, '@') == NULL && index(linebuf, '>') == NULL) {
X			ptr1 = linebuf;
X			if (*ptr1 == '<')
X				ptr1++;
X			ptr2 = index(ptr1, '.');
X			if (ptr2 != NULL) {
X				*ptr2++ = '\0';
X				(void) sprintf(bfr, "<%s@%s.UUCP>", ptr2, ptr1);
X				(void) strcpy(linebuf, bfr);
X			}
X		}
X		if (index(linebuf, '>') == NULL)
X			(void) strcat(linebuf, ">");
X
X		ptr1 = findhist(linebuf);
X		if (ptr1 == NULL) {
X			msg("%s not found", linebuf);
X			break;
X		}
X		ptr2 = index(ptr1, '\t');
X		ptr3 = index(++ptr2, '\t');
X		ptr2 = index(++ptr3, ' ');
X		if (ptr2)
X			*ptr2 = '\0';
X		ptr2 = index(ptr3, '/');
X		if (!ptr2) {
X			if (strcmp(ptr3, "cancelled") == 0)
X				msg("%s has been cancelled", linebuf);
X			else
X				msg("%s has expired", linebuf);
X			break;
X		}
X		*ptr2++ = '\0';
X		(void) sscanf(ptr2, "%ld", &nart);
X
X		/*
X		 * Go to a given article.  Ptr3 specifies the newsgroup
X		 * and nart specifies the article number.
X		 */
X#ifdef MYDB
Xselectart:
X#endif /* MYDB */
X		aabs = TRUE;
X		FCLOSE(fp);
X		saveart;
X		(void) strcpy(ogroupdir, ptr3);
X		if (strcmp(groupdir, ogroupdir)) {
X			(void) strcpy(bfr, groupdir);
X			selectng(ogroupdir, TRUE, PERHAPS);
X			(void) strcpy(groupdir, ogroupdir);
X			(void) strcpy(ogroupdir, bfr);
X			ngrp = 1;
X			back();
X		}
X		bit = nart;
X		oobit = obit;
X		obit = -1;
X		getnextart(TRUE);
X		if (bit != nart || strcmp(groupdir, ptr3) != 0) {
X			msg("can't read %s/%ld", ptr3, nart);
X			goto caseminus;
X		}
X		rfq = 0;
X		break;
X
X	/* follow-up article */
X	case 'f':
X		if (strcmp(h->followto, "poster") == 0) {
X			reply(FALSE);
X			break;
X		}
X		(void) sprintf(bfr, "%s/%s %s", BIN, "postnews", goodone);
X		shcmd(bfr, CWAIT);
X		break;
X
X	/* erase - pretend we haven't seen this article. */
X	case 'e':
X		erased = 1;
X		set(bit);
X		goto caseplus;	/* skip this article for now */
X
X	case '#':
X		msg("Article %ld of %ld", rfq ? oobit : bit, pngsize);
X		break;
X
X		/* error */
X	case '?':
X		{
X			FILE *helpf;
X			(void) sprintf(linebuf, "%s/vnews.help", LIB);
X			if ((helpf = fopen(linebuf, "r")) == NULL) {
X				msg("Can't open help file");
X				break;
X			}
X			move(ARTWIN, 0);
X			while (fgets(linebuf, LBUFLEN, helpf) != NULL)
X				addstr(linebuf);
X			(void) fclose(helpf);
X			prflags |= HELPMSG|NEWART;
X		}
X		break;
X
X	default:
X		if (c != ckill && c != cintr && c != cerase) 
X#ifdef TIOCGLTC
X			if (c != cwerase)
X#endif
X			{
X				beep();
X				msg("Illegal command");
X			}
X		break;
X	}
X}
X
Xcancel_command()
X{
X	register char *poster, *r;
X	int notauthor;
X	char *senderof();
X
X	poster = senderof(&h);
X	/* only compare up to '.' or ' ' */
X	r = index(poster,'.');
X	if (r == NULL)
X		r = index(poster,' ');
X	if (r != NULL)
X		*r = '\0';
X	tfilename = filename;
X	notauthor = strcmp(username, poster);
X	if (uid != ROOTID && uid && notauthor) {
X		msg("Can't cancel what you didn't write.");
X		return;
X	}
X	if (!cancel(stderr, h, notauthor)) {
X		clear(bit);
X		saveart;
X		nextbit();
X		obit = -1;
X		fp = NULL;
X	}
X	FCLOSE(fp);
X}
X/*
X * Generate replies
X */
X
Xreply(include)
X	int include;
X{
X	char *arg[4];
X	register FILE *rfp;
X	char subj[132];
X	register char *p;
X	char *replyname();
X	struct stat statb;
X	time_t creatm;
X
X	/* Put the user in the editor to create the body of the reply. */
X	ed = getenv("EDITOR");
X	if (ed == NULL || *ed == '\0')
X		ed = DFTEDITOR;
X	if (ed == NULL) {
X		msg("You don't have an editor");
X		return;
X	}
X
X	arg[0] = "/bin/sh";
X	arg[1] = "-c";
X
X	(void) strcpy(tf, tft);
X	(void) mktemp(tf);
X	(void) close(creat(tf,0600));
X	if ((rfp = fopen(tf, "w")) == NULL) {
X		msg("Can't create %s", tf) ;
X		return;
X	}
X	(void) strcpy(subj, h->title);
X	if (!prefix(subj, "Re:")){
X		(void) strcpy(bfr, subj);
X		(void) sprintf(subj, "Re: %s", bfr);
X	}
X
X	p = replyname(h);
X	fprintf(rfp, "To: %s\n", p);
X	fprintf(rfp, "Subject: %s\n", subj);
X	fprintf(rfp, "In-reply-to: your article %s\n", h->ident);
X#ifdef INTERNET
X	fprintf(rfp, "News-Path: %s\n", h->path);
X#endif /* INTERNET */
X	(void) sprintf(rcbuf, "%s -t < %s; rm -f %s", MAILPARSER, tf, tf);
X	putc('\n', rfp);
X	if (include) {
X		FILE *of;
X		char buf[BUFSIZ];
X
X		of = xart_open(goodone, "r");
X		while (fgets(buf, sizeof buf, of) != NULL)
X			if (buf[0] == '\n')
X				break;
X		while (fgets(buf, sizeof buf, of) != NULL)
X			fprintf(rfp, "> %s", buf);
X		fclose(of);
X		putc('\n', rfp);
X	}
X	fflush(rfp);
X	(void) fstat(fileno(rfp), &statb);
X	creatm = statb.st_mtime;
X	(void) fclose(rfp);
X
X	(void) sprintf(edcmdbuf, "exec %s %s", ed, tf);
X	arg[2] = edcmdbuf;
X	arg[3] = NULL;
X	if (prun(arg, 0) != 0) {
X		msg("Couldn't run editor");
X		(void) unlink(tf);
X		return;
X	}
X
X	if (access(tf, 4) || stat(tf, &statb)) {
X		msg("No input file - mail not sent");
X		(void) unlink(tf);
X		return;
X	}
X	if (statb.st_mtime == creatm || statb.st_size < 5) {
X		msg("File unchanged - no message posted");
X		(void) unlink(tf);
X		return;
X	}
X
X	arg[2] = rcbuf;
X	arg[3] = NULL;
X	prun(arg, BKGRND);
X	prflags |= NOPRT;
X}
X
Xdirect_reply()
X{
X	register char *p;
X	register char *q;
X	char *arg[4];
X	char address[PATHLEN];
X	extern char *replyname();
X	extern char *getenv();
X
X	arg[0] = "/bin/sh";
X	arg[1] = "-c";
X	p = replyname(h);
X	q = address;
X	while (*p != '\0') {
X		if (index("\"\\$", *p) != 0)
X			*q++ = '\\';
X		*q++ = *p++;
X	}
X	*q++ = '\0';
X	if ((MAILER = getenv("MAILER")) == NULL)
X		MAILER = "mail";
X	sprintf(rcbuf, MAILER, hptr->title);
X	sprintf(bfr, "%s %s", rcbuf, address);
X	arg[2] = bfr;
X	arg[3] = NULL;
X	if (prun(arg, 0) != 0) {
X		msg("Couldn't run mailer");
X		return;
X	}
X	prflags |= NOPRT;
X}
X
Xnext_ng_command()
X{
X	set(bit);
X	obit = -1;
X	linebuf[0] = 0;
X	if (prget("group? ", linebuf))
X		return FALSE;
X	bptr = linebuf;
X	if (!*bptr || *bptr == '-') {
X		if (*bptr)
X			actdirect = BACKWARD;
X		saveart;
X		if (nextng()) {
X			if (actdirect == BACKWARD)
X				msg("Can't back up.");
X			else
X				return TRUE;
X		}
X		return FALSE;
X	}
X	while (isspace(*bptr))
X		bptr++;
X	if (!validng(bptr)) {
X		msg("No such group.");
X		return FALSE;
X	}
X	saveart;
X	back();
X	selectng(bptr, TRUE, TRUE);
X	return FALSE;
X}
X
X/*
X * Find the next article we want to consider, if we're done with
X * the last one, and show the header.
X */
Xgetnextart(minus)
Xint minus;
X{
X	int noaccess;
X	register DIR *dirp;
X	register struct direct *dir;
X	long nextnum, tnum;
X	long atol();
X
X	noaccess = 0;
X	if (minus)
X		goto nextart2;	/* Kludge for "-" command. */
X
X	if (bit == obit)	/* Return if still on same article as last time */
X		return 0;
X
Xnextart:
X	if (news) {
X		curflag = CURHOME;
X		_amove(0, 0);
X		vflush();
X	}
X	dgest = 0;
X
X	/* If done with this newsgroup, find the next one. */
X	while (ngsize <= 0 || (!rflag && ((long) bit > ngsize)) || (rflag && bit < minartno)) {
X		if (nextng()) {
X			if (actdirect == BACKWARD) {
X				msg("Can't back up.");
X				actdirect = FORWARD;
X				continue;
X			}
X			else /* if (rfq++ || pflag || cflag) */
X				return 1;
X		}
X		if (rflag)
X			bit = ngsize + 1;
X		else
X			bit = -1;
X		noaccess = 2;
X	}
X
X	/* speed things up by not searching for article -1 */
X	if (bit < 0) {
X		bit = minartno - 1;
X		nextbit();
X		aabs = FALSE;
X		goto nextart;
X	}
X
Xnextart2:
X	if (rcreadok)
X		rcreadok = 2;	/* have seen >= 1 article */
X	(void) sprintf(filename, "%s/%ld", dirname(groupdir), bit);
X	if (rfq && goodone[0])	/* ??? */
X		strcpy(filename, goodone);
X	if (SigTrap == SIGHUP)
X		return 1;
X	/* Decide if we want to show this article. */
X	if ((fp = art_open(filename, "r")) == NULL) {
X		/* since there can be holes in legal article numbers, */
X		/* we wait till we hit 5 consecutive bad articles */
X		/* before we haul off and scan the directory */
X		if (++noaccess < 5)
X			goto badart;
X		noaccess = 0;
X		dirp = opendir(dirname(groupdir));
X		if (dirp == NULL) {
X			if (errno != EACCES)
X				msg("Can't open %s", dirname(groupdir));
X			goto nextart;
X		}
X		nextnum = rflag ? minartno - 1 : ngsize + 1;
X		while ((dir = readdir(dirp)) != NULL) {
X			if (!dir->d_ino)
X				continue;
X			tnum = atol(dir->d_name);
X			if (tnum <= 0)
X				continue;
X			if (rflag ? (tnum > nextnum && tnum < bit)
X				  : (tnum < nextnum && tnum > bit))
X				nextnum = tnum;
X		}
X		closedir(dirp);
X		if (rflag ? (nextnum >= bit) : (nextnum <= bit))
X			goto badart;
X		do {
X			clear(bit);
X			nextbit();
X		} while (rflag ? (nextnum < bit) : (nextnum > bit));
X		obit = -1;
X		aabs = FALSE;
X		goto nextart;
X	} else
X		noaccess = 0;
X
X	if (hread(h, fp, TRUE) == NULL || (!rfq && !aselect(h, aabs))) {
Xbadart:
X		FCLOSE(fp);
X		clear(bit);
X		obit = -1;
X		nextbit();
X		aabs = FALSE;
X		goto nextart;
X	}
X	aabs = FALSE;
X	actdirect = FORWARD;
X	news = TRUE;
X	artbody = ftell(fp);
X	fmthdr();
X	artlines = lastlin;
X	artread = 0;
X	prflags |= NEWART;
X	prflags &=~ NOPRT;
X	if (! cflag && hdrend < ARTWLEN && !cflag)
X		prflags |= HDRONLY;
X	dlinno = 0;
X	maxlinno = NLINES(h, fp);
X	erased = 0;
X
X	obit = bit;
X	return 0;
X}
X
X/*
X * Print out whatever the appropriate header is
X */
Xfmthdr() {
X	char *briefdate();
X	static FILE *ngfd = NULL;
X	static int triedopen = 0;
X	char pbuf[BUFLEN], *printbuffer = groupdir;
X
X	lastlin = 0;
X	if (ngrp) {
X		pngsize = ngsize;
X		ngrp--;
X		if (!hflag) {
X			if (!triedopen) {
X				(void) sprintf(pbuf,"%s/newsgroups", LIB);
X				ngfd = fopen(pbuf, "r");
X				triedopen++;
X			}
X			if (ngfd != NULL) {
X				register char *p;
X				char ibuf[BUFLEN];
X				rewind(ngfd);
X				while (fgets(ibuf, BUFLEN, ngfd) != NULL) {
X					p = index(ibuf, '\t');
X					if (p)
X						*p++ = '\0';
X					if (strcmp(ibuf, groupdir) == 0) {
X						register char *q;
X						q = rindex(p, '\t');
X						if (q) {
X							p = q;
X							*p++ = '\0';
X						}
X						if (p) {
X							q = index(p, '\n');
X							if (q)
X								*q = '\0';
X							if (*--q == '.')
X								*q = '\0';
X						(void) sprintf(pbuf,"%s (%s)",
X							groupdir, p);
X							printbuffer = pbuf;
X						}
X						break;
X					}
X				}
X			}
X			(void) sprintf(linebuf, "Newsgroup %s", printbuffer);
X			tfappend(linebuf);
X		}
X	}
X	hdrstart = lastlin;
X	if (!hflag) {
X		(void) sprintf(linebuf, "Article %s %s",
X			h->ident, briefdate(h->subdate));
X		tfappend(linebuf);
X	}
X	xtabs(h);
X	vhprint(h, pflag ? 1 : 0);
X	(void) sprintf(linebuf, "(%d lines)", NLINES(h, fp)); tfappend(linebuf);
X	tfappend("");
X	hdrend = lastlin;
X}
X
X/*
X * Grow tabs into spaces in header fields, 'cause the rest of this
X * lax program drops turds all over tabs (so it does with \b's, but ..)
X */
Xxtabs(p)
Xregister struct hbuf *p;
X{
X	xtabf(p->from, sizeof p->from);
X	xtabf(p->path, sizeof p->path);
X	xtabf(p->nbuf, sizeof p->nbuf);
X	xtabf(p->title, sizeof p->title);
X	xtabf(p->ident, sizeof p->ident);
X	xtabf(p->replyto, sizeof p->replyto);
X	xtabf(p->followid, sizeof p->followid);
X	xtabf(p->subdate, sizeof p->subdate);
X	xtabf(p->expdate, sizeof p->expdate);
X	xtabf(p->ctlmsg, sizeof p->ctlmsg);
X	xtabf(p->sender, sizeof p->sender);
X	xtabf(p->followto, sizeof p->followto);
X	xtabf(p->distribution, sizeof p->distribution);
X	xtabf(p->organization, sizeof p->organization);
X	xtabf(p->numlines, sizeof p->numlines);
X	xtabf(p->keywords, sizeof p->keywords);
X	xtabf(p->summary, sizeof p->summary);
X	xtabf(p->approved, sizeof p->approved);
X	xtabf(p->nf_id, sizeof p->nf_id);
X	xtabf(p->nf_from, sizeof p->nf_from);
X#ifdef DOXREFS
X	xtabf(p->xref, sizeof p->xref);
X#endif /* DOXREFS */
X}
X
Xxtabf(s, size)
Xchar *s;
Xint size;
X{
X	register char *p, *str;
X	register c, i;
X	char buf[LBUFLEN];
X
X	str = s;
X	if (index(str, '\t') == NULL)
X		return;
X	i = 0;
X	for (p = buf; c = *str++; i++) {
X		if (c == '\t') {
X			*p++ = ' ';
X			if ((i & 7) != 7)
X				str--;
X		} else if (c == '\n') {
X			i = -1;
X			*p++ = c;
X		} else
X			*p++ = c;
X	}
X	*p = '\0';
X	strncpy(s, buf, size - 1);
X}
X
X/*
X * Print the file header to the temp file.
X */
Xvhprint(hp, verbose)
Xregister struct hbuf *hp;
Xint	verbose;
X{
X	register char	*p1, *p2;
X	char	fname[BUFLEN];
X	char *tailpath();
X
X	fname[0] = '\0';		/* init name holder */
X
X	p1 = index(hp->from, '(');	/* Find the sender's full name. */
X	if (p1 == NULL && hp->path[0])
X		p1 = index(hp->path, '(');
X	if (p1 != NULL) {
X		(void) strcpy(fname, p1+1);
X		p2 = index(fname, ')');
X		if (p2 != NULL)
X			*p2 = '\0';
X	}
X
X	(void) sprintf(linebuf, "Subject: %s", hp->title);
X	tfappend(linebuf);
X	if (!hflag && hp->summary[0])
X		(void) sprintf(linebuf, "Summary: %s", hp->summary), tfappend(linebuf);
X	if (!hflag && hp->keywords[0])
X		(void) sprintf(linebuf, "Keywords: %s", hp->keywords), tfappend(linebuf);
X	if (verbose) {
X		(void) sprintf(linebuf, "From: %s", hp->from); tfappend(linebuf);
X		(void) sprintf(linebuf, "Path: %s", hp->path); tfappend(linebuf);
X		if (hp->organization[0]) {
X			(void) sprintf(linebuf, "Organization: %s", hp->organization);
X			tfappend(linebuf);
X		}
X	}
X	else {
X		if (p1 != NULL)
X			*--p1 = '\0';		/* bump over the '(' */
X#ifdef INTERNET
X		/*
X		 * Prefer Path line if it's in internet format, or if we don't
X		 * understand internet format here, or if there is no reply-to.
X		 */
X		(void) sprintf(linebuf, "From: %s", hp->from);
X#else
X		(void) sprintf(linebuf, "Path: %s", tailpath(hp));
X#endif
X		if (fname[0] || (hp->organization[0] && !hflag)) {
X			(void) strcat(linebuf, " (");
X			if (fname[0] == '\0') {
X				(void) strcpy(fname, hp->from);
X				p2 = index(fname,'@');
X				if (p2)
X					*p2 = '\0';
X			}
X			(void) strcat(linebuf, fname);
X			if (hp->organization[0] && !hflag) {
X				(void) strcat(linebuf, " @ ");
X				(void) strcat(linebuf, hp->organization);
X			}
X			(void) strcat(linebuf, ")");
X		}
X		tfappend(linebuf);
X		if (p1 != NULL)
X			*p1 = ' ';
X		if (hp->ctlmsg[0]) {
X			(void) sprintf(linebuf, "Control: %s", hp->ctlmsg);
X			tfappend(linebuf);
X		}
X	}
X
X	if (verbose) {
X		(void) sprintf(linebuf, "Newsgroups: %s", hp->nbuf); tfappend(linebuf);
X		(void) sprintf(linebuf, "Date: %s", hp->subdate); tfappend(linebuf);
X		if (hp->sender[0]) {
X			(void) sprintf(linebuf, "Sender: %s", hp->sender);
X			tfappend(linebuf);
X		}
X		if (hp->replyto[0]) {
X			(void) sprintf(linebuf, "Reply-To: %s", hp->replyto);
X			tfappend(linebuf);
X		}
X		if (hp->followto[0]) {
X			(void) sprintf(linebuf, "Followup-To: %s", hp->followto);
X			tfappend(linebuf);
X		}
X	}
X	else if (strcmp(hp->nbuf, groupdir) != 0) {
X		(void) sprintf(linebuf, "Newsgroups: %s", hp->nbuf);
X		tfappend(linebuf);
X		timer();
X	}
X}
X
X#ifdef MYDB
X
Xchar *
Xfindparent(id, num)
Xchar *id;
Xlong *num;
X{
X	struct artrec a;
X	char idbuf[BUFSIZE];
X	char *ngname();
X
X	strcpy(idbuf, id);
X	lcase(idbuf);
X
X	if (lookart(id, &a) == DNULL)
X		return NULL;
X	if (a.parent == DNULL)
X		return NULL;
X	readrec(a.parent, &a);
X	*num = a.groups[0].artno;
X	return ngname(a.groups[0].newsgroup);
X}
X
X#endif
X
X
X/*
X * Append file to temp file, handling control characters, folding lines, etc.
X * We don't grow the temp file to more than nlines so that a user won't have
X * to wait for 20 seconds to read in a monster file from net.sources.
X * What we really want is coroutines--any year now.
X */
X
X#define ULINE 0200
Xstatic char *maxcol;
X
Xappfile(iop, nlines)
Xregister FILE *iop;
X{
X	register int c;
X	register char *icol;	/* &linebuf[0] <= icol <= maxcol */
X
X	if (artread || artlines >= nlines || iop == NULL)
X		return;
X	maxcol = linebuf;
X	icol = linebuf;
X	while ((c = getc(iop)) != EOF) {
X		switch (c) {
X		case ' ':
X			if (icol == maxcol && icol < linebuf + LBUFLEN - 1) {
X				*icol++ = ' ';
X				maxcol = icol;
X			} else {
X				if (*icol == '_')
X					*icol++ = ULINE | ' ';
X				else
X					icol++;
X			}
X			break;
X		case '\t':
X			icol = (icol - linebuf &~ 07) + 8 + linebuf;
X			growline(icol);
X			break;
X		case '\b':
X			if (icol > linebuf) --icol;
X			break;
X		case '\n':
X			outline();
X			if (artlines >= nlines)
X				return;
X			icol = linebuf;
X			break;
X		case '\r':
X			icol = linebuf;
X			break;
X		case '\f':
X			outline(); outline(); outline();
X			if (artlines >= nlines)
X				return;
X			icol = linebuf;
X			break;
X		default:
X			if (c < ' ' || c > '~')
X				break;
X			else if (icol >= linebuf + LBUFLEN - 1)
X				icol++;
X			else if (icol == maxcol) {
X				*icol++ = c;
X				maxcol = icol; }
X			else if (c == '_')
X				*icol++ |= ULINE;
X			else if (*icol == '_')
X				*icol++ = (c | ULINE);
X			else	*icol++ = c;
X			break;
X		}
X	}
X	if (maxcol != linebuf)		/* file not terminated with newline */
X		outline();
X	artread++;
X}
X
Xgrowline(col)
Xchar *col;
X{
X	while (maxcol < col && maxcol < linebuf + LBUFLEN - 1)
X		*maxcol++ = ' ';
X}
X
Xoutline()
X{
X	*maxcol = '\0';
X	if (strncmp(linebuf, ">From ", 6) == 0) {
X		register char *p;
X		for (p = linebuf ; (*p = p[1]) != '\0' ; p++);
X	}
X	tfappend(linebuf);
X	if (maxcol > linebuf)
X		artlines = lastlin;
X	maxcol = linebuf;
X}
X
X
X/*
X * Prompt the user and get a line.
X * "prompter" is the prompt.  "buf" contains a string which
X * will be used as the initial user response (which may be edited
X * by the user with backspace, ^U, etc).  The resulting line is
X * returned in "buf".  The result of prget() is:
X *	 0 if the line was terminated by NL or CR
X *	 1 if it was terminated by the interrupt character.
X *	 2 if it was terminated by erasing all the characters, including
X *	   one or more that were prompted initially in "buf".  (If "buf"
X * 	   was empty, this will never occur.)
X */
Xint
Xprget(prompter, buf)
Xchar *prompter, *buf;
X{
X	register char *p, *q, *r;
X	register char c;
X	char lastc;
X	char hadprompt = buf[0];
X
X	curflag = CURP2;
X	r = buf + strlen(buf);
X	lastc = '\0';
X	for (;;) {
X		p = secpr;
X		for (q = prompter ; *q ; q++)
X			*p++ = *q;
X		for (q = buf ; *q ; q++) {
X			if (p < &secpr[SECPRLEN-1] && *q >= ' ' && *q <= '~')
X				*p++ = *q;
X		}
X		*p = '\0';
X		c = vgetc();
X		if (c == '\n' || c == '\r' || c == cintr) {
X			break;
X		}
X		if (c == cerase || c == '\b' || c == '\177') {
X			if (lastc == '\\')
X				r[-1] = c;
X			else if (r > buf)
X				r--;
X		} else if (c == ckill) {
X			if (lastc == '\\')
X				r[-1] = c;
X			else
X				r = buf;
X#ifdef TIOCGLTC
X		} else if (c == cwerase) {
X			if (lastc == '\\')
X				r[-1] = c;
X			else {
X				while (r > buf && (r[-1] == ' ' || r[-1] == '\t'))
X					r--;
X				while (r > buf && r[-1] != ' ' && r[-1] != '\t')
X					r--;
X			}
X#endif
X		} else {
X			*r++ = c;
X		}
X		lastc = c;
X		*r = '\0';
X		if ((r == buf) && hadprompt)
X			return 2;
X	}
X	curflag = CURHOME;
X	secpr[0] = '\0';
X	return (c == cintr);
X}
X
X
X
X/*
X * Execute a shell command.
X */
X
Xshcmd(cmd, flags)
Xchar *cmd;
X{
X	char *arg[4];
X
X	arg[0] = SHELL, arg[1] = "-c", arg[2] = cmd, arg[3] = NULL;
X	return prun(arg, flags);
X}
X
X
Xprun(args, flags)
Xchar **args;
X{
X	int pid;
X	int i;
X	int (*savequit)();
X	char *env[100], **envp, **oenvp;
X	char a[BUFLEN + 2];
X	extern char **environ;
X	int pstatus, retval;
X
X	if (!(flags & BKGRND)) {
X		botscreen();
X		ttycooked();
X#ifdef SIGTSTP
X		(void) signal(SIGTSTP, SIG_DFL);
X		(void) signal(SIGTTIN, SIG_DFL);
X		(void) signal(SIGTTOU, SIG_DFL);
X#endif
X	}
X#if defined(BSD4_2) && !defined(sun)
X	while ((pid = vfork()) == -1)
X#else /* !BSD4_2 */
X	/* 4.1 BSD (at least) can't handle this vfork with -ljobs */
X	while ((pid = fork()) == -1)
X#endif /* !BSD4_2 */
X		sleep(1);		/* must not clear alarm */
X	if (pid == 0) {
X		for (i = 3 ; i < 20 ; i++)
X			close(i);
X		if (flags & BKGRND) {
X			(void) signal(SIGINT, SIG_IGN);
X			(void) signal(SIGQUIT, SIG_IGN);
X#ifdef SIGTSTP
X			(void) signal(SIGTSTP, SIG_IGN);
X			(void) signal(SIGTTIN, SIG_IGN);
X			(void) signal(SIGTTOU, SIG_IGN);
X#endif
X			(void) close(0);
X			(void) close(1);
X			(void) open("/dev/null", 2);
X			(void) dup(0);
X		}
X		/* set $A */
X		(void) sprintf(a, "A=%s", filename);
X		oenvp = environ;
X		env[0] = a;
X		for (envp = env + 1 ; *oenvp != NULL && envp < env + 98 ; oenvp++)
X			if ((*oenvp)[0] != 'A' || (*oenvp)[1] != '=')
X				*envp++ = *oenvp;
X		*envp = NULL;
X
X		(void) umask(savmask);
X		execve(args[0], args, env);
X		perror(args[0]);
X		exit(20);
X	}
X	if (!(flags & BKGRND)) {
X		savequit = signal(SIGQUIT, SIG_IGN);
X		while ((i = wait(&pstatus)) != pid && (i != -1 || errno == EINTR))
X			;
X		if (i == -1)
X			retval = 1;
X		else
X			retval = pstatus;
X		if (flags & CWAIT) {
X			fprintf(stderr, "[Hit return to continue]");
X			while ((errno = 0, i = getchar()) != '\n'
X				&& (i != EOF || errno == EINTR));
X		}
X		(void) signal(SIGQUIT, savequit);
X		ttyraw();
X		clearok(curscr, 1);
X#ifdef SIGTSTP
X		(void) signal(SIGTSTP, onstop);
X		(void) signal(SIGTTIN, onstop);
X		(void) signal(SIGTTOU, onstop);
X#endif
X		return retval;
X	} else
X		return 0;
X}
X
X#ifdef DIGPAGE
X
X
X/*
X * Find end of current subarticle in digest.
X */
X
Xfindend(l)
X{
X	register int i, n;
X	register char *p;
X
X	for (i = l ; i < l + ARTWLEN && i < lastlin ; i++) {
X		tfget(linebuf, i);
X		for (p = linebuf ; *p == '-' ; p++)
X			;
X		n = (int)p - (int)linebuf;
X		if ( (n > 23 && n < 33) || (n > 65 && n < 79)) {
X			tfget(linebuf, ++i);
X			if (linebuf[0] == '\0')
X				return i + 1;
X		}
X	}
X	return 0;
X}
X
X#endif
X
X
X/*** Routines for handling temporary file ***/
X
X/*
X * Append to temp file.
X * Long lines are folded.
X */
X
Xtfappend(tline)
Xregister char *tline;
X{
X	register char *nxtlin;
X
X	do {
X		nxtlin = index(tline, '\n');
X		if (nxtlin)
X			*nxtlin++ = '\0';
X
X		while (strlen(tline) > COLS) {
X			tfput(tline, lastlin++);
X			tline += COLS;
X			maxlinno++;
X		}
X		tfput(tline, lastlin++);
X	} while ((tline = nxtlin) != NULL);
X}
X
X
Xtfput(tline, linno)
Xchar *tline;
X{
X	register char *p;
X	register FILE *rtfp;		/* try to make it a little faster */
X	register int i;
X
X	p = tline, i = even(COLS);
X	tfseek(linno, 1);
X	rtfp = tfp;
X	while (--i >= 0) {
X		if (*p)
X			putc(*p++, rtfp);
X		else
X			putc('\0', rtfp);
X	}
X	tflinno++;
X}
X
X
Xtfget(tline, linno)
Xchar *tline;
X{
X	tfseek(linno, 0);
X	fread(tline, even(COLS), 1, tfp);
X	tline[COLS] = '\0';
X	tflinno++;
X}
X
X
Xtfseek(linno, wrflag)
X{
X	static int lastwrflag = 1;
X
X	if (linno != tflinno || wrflag != lastwrflag) {
X		(void) fseek(tfp, (long)linno * even(COLS), 0);
X		tflinno = linno;
X		lastwrflag = wrflag;
X	}
X}
X
X/* VARARGS1 */
Xmsg(s, a1, a2, a3, a4)
Xchar *s;
Xlong a1, a2, a3, a4;
X{
X	(void) sprintf(secpr, s, a1, a2, a3, a4);
X}
X
X
X/*
X * Update the display.
X * The display is entirely controlled by this routine,
X * which means that this routine may get pretty snarled.
X */
X
Xstatic int savelinno = -1;		/* dlinno on last call to updscr */
Xstatic int savepr;			/* prflags on last call */
X#ifdef TIOCGWINSZ
Xstatic int UPDATING = 0, WINCH = 0;
X
X/*
X * called by winch() from virtterm.c -- resets state information back
X * to start-up state and forces a full redraw of the screen.  The
X * current article is rewound to the beginning because it's would
X * be very difficult to get the screen to return to the exact point
X * in the file that the user left off (I know, I tried).
X */
Xwinch_upd()
X{
X	if(UPDATING)	/* concurrency.  wow! */
X		WINCH++;
X	else if((WINCH == 0) && (savelinno >= 0)) {
X		int  saveflag = curflag;
X
X		/* reread the article */
X		FCLOSE(fp);
X		obit = -1;
X		getnextart(FALSE);
X		appfile(fp, dlinno + ARTWLEN + 1);
X
X		/* fix up the screen */
X		curflag = saveflag;
X		strcpy(prompt,"more? ");
X		clearok(curscr, 1);
X		updscr();
X	}
X}
X#endif /* TIOCGWINSZ */
X
X
Xupdscr()
X{
X	int count;
X	int i;
X
X#ifdef TIOCGWINSZ
X	UPDATING++;
X#endif /* TIOCGWINSZ */
X	if (checkin())
X		return;
X	if ((prflags & HELPMSG) == 0
X	 && (dlinno != savelinno || savepr != prflags)
X	 && quitflg == 0) {
X		if (dlinno != savelinno)
X			prflags &=~ NOPRT;
X		count = ARTWLEN;
X		if (prflags & NOPRT)
X			count = 0;
X		if ((prflags & HDRONLY) && count > hdrend)
X			count = hdrend - dlinno;
X#ifdef DIGPAGE
X		if (endsuba > 0 && count > endsuba - dlinno)
X			count = endsuba - dlinno;
X#endif
X		if ((prflags & NEWART) == 0)
X			ushift(ARTWIN, ARTWIN+ARTWLEN-1, dlinno - savelinno);
X		if (count > lastlin - dlinno)
X			count = lastlin - dlinno;
X		for (i = ARTWIN ; i < ARTWIN + ARTWLEN ; i++)
X			clrline(i);
X		for (i = 0 ; i < count ; i++) {
X			tfget(linebuf, dlinno + i);
X			mvaddstr(ARTWIN + i, 0, linebuf);
X		}
X		prflags &=~ NEWART;
X		savepr = prflags;
X		savelinno = dlinno;
X	}
X	clrline(SPLINE), clrline(PRLINE);
X#ifdef STATTOP
X	mvaddstr(PRLINE, 0, prompt);
X#else
X	if (strlen(secpr) <= COLS)
X		mvaddstr(PRLINE, 0, prompt);
X#endif
X	mvaddstr(PRLINE, 59, timestr);
X	mvaddstr(PRLINE, 17, groupdir);
X	addch(' '); addnum(bit); addch('/'); addnum(pngsize); addch(' ');
X	if (ismail)
X		mvaddstr(PRLINE, 75, ismail > 1? "MAIL" : "mail");
X	mvaddstr(SPLINE, 0, secpr);
X	if (curflag == CURP1)
X		move(PRLINE, strlen(prompt));
X	else if (curflag == CURHOME)
X		move(0, 0);
X	refresh();
X#ifdef TIOCGWINSZ
X	UPDATING=0;
X	if (WINCH) { /* window changed while updating screen */
X		WINCH = 0;
X		winch_upd();
X	}
X#endif /* TIOCGWINSZ */
X}
X
Xaddnum(n)
Xregister long n;
X{
X	if (n >= 10)
X		addnum(n / 10);
X	addch((char)(n % 10 + '0'));
X}
X
X/*
X * Called on alarm signal.
X * Simply sets flag, signal processed later.
X */
X
Xonalarm()
X{
X#ifdef SIGTSTP
X	int dojump = reading;
X
X	reading = FALSE;
X	alflag++;
X	if (dojump)
X		longjmp(alrmjmp, 1);
X#else /* !SIGTSTP */
X	alflag++;
X#endif
X}
X
X/*
X * Process alarm signal (or start clock)
X */
Xtimer()
X{
X	time_t tod;
X	int hour;
X	int i;
X	struct tm *t;
X	struct stat statb;
X	struct tm *localtime();
X	static char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
X	static long oldmsize = 1000000L;
X	static int rccount = 10;
X	static time_t lastismail = 0;
X
X	alflag = 0;
X	(void) signal(SIGALRM, onalarm);
X	(void) time(&tod);
X	t = localtime(&tod);
X	i = 60 - t->tm_sec;
X	(void) alarm(i > 30? 30 : i);			/* reset alarm */
X	hour = t->tm_hour % 12;
X	if (hour == 0)  hour = 12;
X	(void) sprintf(timestr, "%.3s %d %d:%02d",
X		months + 3 * t->tm_mon, t->tm_mday, hour, t->tm_min);
X	if (mailf == NULL || stat(mailf, &statb) < 0) {
X		statb.st_size = 0;
X	}
X	if (statb.st_size > oldmsize) {
X		ismail = 2;
X		beep();
X	} else {
X		if (statb.st_size == 0)
X			ismail = 0;
X					/* force MAIL for at least 30 seconds */
X		else if (ismail > 1 && (lastismail+30) < tod)
X			ismail = 1;
X	}
X	oldmsize = statb.st_size;
X	lastismail = tod;
X	if (uflag && !xflag && --rccount < 0) {
X		writeoutrc();
X		if (secpr[0] == '\0')
X			(void) strcpy(secpr, ".newsrc updated");
X		rccount = 10;
X	}
X}
X
Xchar *
Xgetmailname()
X{
X	static char mailname[32];
X	register char *p;
X
X	if( (p = getenv("MAIL")) != NULL)
X		return p;
X#ifndef MMDF
X	if (username[0] == '\0' || strlen(username) > 15)
X		return NULL;
X#ifdef USG
X	(void) sprintf(mailname, "/usr/mail/%s", username);
X#else /* !USG */
X	(void) sprintf(mailname, "/usr/spool/mail/%s", username);
X#endif /* !USG */
X#else /* MMDF */
X	(void) sprintf(mailname, "%s/mailbox", userhome);
X#endif /* MMDF */
X	return mailname;
X}
X
X
X
X/*** Terminal I/O ***/
X
X#define INBUFSIZ 8
X
Xchar inbuf[INBUFSIZ];			/* input buffer */
Xchar outbuf[BUFSIZ];			/* output buffer */
Xint innleft = 0;			/* # of chars in input buffer */
Xint outnleft = BUFSIZ;			/* room left in output buffer */
Xchar *innext;				/* next input character */
Xchar *outnext = outbuf;			/* next space in output buffer */
X#ifdef USG
Xint oflags;				/* fcntl flags (for nodelay read) */
X#endif
X
X/*
X * Input a character
X */
X
Xvgetc()
X{
X	register c;
X#if defined(BSD4_2) || defined(BSD4_1C)
X	int readfds, exceptfds;
X#endif
X
Xrecurse:
X	if (--innleft >= 0) {
X		c = *innext++;
X	} else {
X		if (alflag)
X			timer();
X		updscr();	/* update the display */
X		for (;;) {
X			if (innleft > 0 || alflag)
X				goto recurse;
X			intflag = 0;
X#ifdef USG
X			if (oflags & O_NDELAY) {
X				oflags &=~ O_NDELAY;
X				fcntl(0, F_SETFL, oflags);
X			}
X#endif
X#ifdef SIGTSTP
X			if (setjmp(alrmjmp))
X				continue;
X			if (setjmp(intjmp))
X				return cintr;
X			reading = TRUE;
X#endif /* SIGTSTP */
X#if defined(BSD4_2) || defined(BSD4_1C)
X			/* Use a select because it can be interrupted. */
X			readfds = 1; exceptfds = 1;
X			select(1, &readfds, (int *)0, &exceptfds, (int *)0);
X			if (!(readfds & 1))
X				break;
X#endif
X			innleft = read(0, inbuf, INBUFSIZ);
X#ifdef SIGTSTP
X			reading = FALSE;
X#endif /* SIGTSTP */
X			if (innleft > 0)
X				break;
X			if (innleft == 0) {
X				quitflg++;
X				return cintr;
X			}
X			if (errno != EINTR)
X				abort();	/* "Can't happen" */
X			if (intflag) {
X				intflag--;
X				return cintr;
X			}
X		}
X		innext = inbuf + 1;
X		innleft--;
X		c = inbuf[0];
X	}
X#ifndef USG
X#ifndef CBREAK
X	c &= 0177;
X	if (c == '\034')	/* FS character */
X		xxit(0);
X#endif
X#endif
X	if (c == '\f') {
X		clearok(curscr, 1);
X		prflags &=~ NOPRT;
X		goto recurse;
X	}
X	if (c == '\r')
X		c = '\n';
X	return c;
X}
X
X
X/*
X * Push a character back onto the input stream.
X */
X
Xpushback(c)
X{
X	if (innext <= inbuf)
X		abort();
X	*--innext = c;
X	innleft++;
X}
X
X/*
X * Check for terminal input
X */
X
Xcheckin()
X{
X#ifdef FIONREAD
X	int count;
X#endif
X#ifdef STATTOP
X	if (innleft > 0)
X#else
X	if (innleft > 0 || alflag)
X#endif
X		return 1;
X#if defined(USG) || defined(FIONREAD)
X	if (ospeed >= B9600)
X		return 0;
X	vflush();
X	if (ospeed <= B300)
X		ttyowait();
X#ifdef USG
X	if ((oflags & O_NDELAY) == 0) {
X		oflags |= O_NDELAY;
X		(void) fcntl(0, F_SETFL, oflags);
X	}
X	if ((innleft = read(0, inbuf, INBUFSIZ)) > 0) {
X		innext = inbuf;
X		return 1;
X	}
X#endif
X#ifdef FIONREAD
X	count = 0;			/* in case FIONREAD fails */
X	(void) ioctl(0, FIONREAD, (char *)&count);
X	if (count)
X		return 1;
X#endif
X#endif
X	return 0;
X}
X
X
X
X/*
X * flush terminal input queue.
X */
X
Xclearin()
X{
X#ifdef USG
X	(void) ioctl(0, TCFLSH, (char *)0);
X#else
X#ifdef TIOCFLUSH
X	(void) ioctl(0, TIOCFLUSH, (char *)0);
X#else
X	struct sgttyb tty;
X	(void) ioctl(0, TIOCGETP, &tty);
X	(void) ioctl(0, TIOCSETP, &tty);
X#endif
X#endif
X	innleft = 0;
X}
X
Xvputc(c)
X{
X	if (--outnleft < 0) {
X		vflush();
X		outnleft--;
X	}
X	*outnext++ = c;
X}
X
X/*
X * Flush the output buffer
X */
X
Xvflush()
X{
X	register char *p;
X	register int i;
X#ifdef BSD4_2
X	int mask;
X#else
X	unsigned oalarm;
X#endif
X
X#ifdef BSD4_2
X	mask = sigblock(1 << (SIGALRM-1));
X#else
X	oalarm = alarm(0);
X#endif
X	for (p = outbuf ; p < outnext ; p += i) {
X		if ((i = write(1, p, outnext - p)) < 0) {
X			if (errno != EINTR)
X				abort();	/* "Can't happen" */
X			i = 0;
X		}
X	}
X	outnleft = BUFSIZ;
X	outnext = outbuf;
X#ifdef BSD4_2
X	sigsetmask(mask);
X#else
X	(void) alarm(oalarm);
X#endif
X}
X
X/*** terminal modes ***/
X
X#ifdef USG
Xstatic struct termio oldtty, newtty;
X
X/*
X * Save tty modes
X */
X
Xttysave()
X{
X	if (ioctl(1, TCGETA, &oldtty) < 0)
X		xerror("Can't get tty modes");
X	newtty = oldtty;
X	newtty.c_iflag &=~ (INLCR|IGNCR|ICRNL);
X	newtty.c_oflag &=~ (OPOST);
X	newtty.c_lflag &=~ (ICANON|ECHO|ECHOE|ECHOK|ECHONL);
X	newtty.c_lflag |=  (NOFLSH);
X	newtty.c_cc[VMIN] = 1;
X	newtty.c_cc[VTIME] = 0;
X	cerase = oldtty.c_cc[VERASE];
X	ckill = oldtty.c_cc[VKILL];
X	cintr = oldtty.c_cc[VINTR];
X	ospeed = oldtty.c_cflag & CBAUD;
X	initterm();
X}
X
X
X/*
X * Set tty modes for visual processing
X */
X
Xttyraw()
X{
X	while (ioctl(1, TCSETAF, &newtty) < 0 && errno == EINTR)
X		;
X	rawterm();
X}
X
Xttyowait()
X{	/* wait for output queue to drain */
X	while (ioctl(1, TCSETAW, &newtty) < 0 && errno == EINTR)
X		;
X}
X
X/*
X * Restore tty modes
X */
X
Xttycooked()
X{
X	cookedterm();
X	vflush();
X	while (ioctl(1, TCSETAF, &oldtty) < 0 && errno == EINTR)
X		;
X	oflags &=~ O_NDELAY;
X	(void) fcntl(0, F_SETFL, oflags) ;
X}
X
X#else
X
Xstatic struct sgttyb oldtty, newtty;
X#ifdef TIOCGLTC
Xstatic struct ltchars oldltchars, newltchars;
X#endif
X
X/*
X * Save tty modes
X */
X
Xttysave()
X{
X#ifdef CBREAK
X	struct tchars tchars;	/* special characters, including interrupt */
X#endif
X#ifdef SIGTSTP
X	int getpgrp();
X#if defined(BSD4_2) || defined(BSD4_1C)
X	int tpgrp;
X#else /* BSD4_1 */
X	short tpgrp;
X#endif /* BSD4_1 */
X
Xretry:
X#ifdef BSD4_2
X	(void) sigblock(sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU));
X#else /* !BSD4_2 */
X	(void) signal(SIGTSTP, SIG_HOLD);
X	(void) signal(SIGTTIN, SIG_HOLD);
X	(void) signal(SIGTTOU, SIG_HOLD);
X#endif /* !BSD4_2 */
X	if (ioctl(2, TIOCGPGRP, (char *)&tpgrp) < 0)
X		goto nottty;
X	if (tpgrp != getpgrp(0)) { /* not in foreground */
X		(void) signal(SIGTTOU, SIG_DFL);
X#ifdef BSD4_2
X		(void) sigsetmask(sigblock(0) & ~sigmask(SIGTTOU));
X#endif /* BSD4_2 */
X		(void) kill(0, SIGTTOU);
X		/* job stops here waiting for SIGCONT */
X		goto retry;
X	}
X	(void) signal(SIGTTIN, SIG_DFL);
X	(void) signal(SIGTTOU, SIG_DFL);
X	(void) signal(SIGTSTP, SIG_DFL);
X#ifdef BSD4_2
X	(void) sigsetmask(sigblock(0) & ~(sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU)));
X#endif /* BSD4_2 */
X#endif /* SIGTSTP */
X	if (ioctl(1, TIOCGETP, (char *)&oldtty) < 0)
Xnottty:		xerror("Can't get tty modes");
X	newtty = oldtty;
X	newtty.sg_flags &=~ (CRMOD|ECHO|XTABS);
X#ifdef CBREAK
X	newtty.sg_flags |= CBREAK;
X	ioctl(1, TIOCGETC, (char *)&tchars);
X	cintr = tchars.t_intrc;
X#else /* !CBREAK */
X	newtty.sg_flags |= RAW;
X	cintr = '\0177';	/* forcibly this on V6 systems */
X#endif /* !CBREAK */
X	cerase = oldtty.sg_erase;
X	ckill = oldtty.sg_kill;
X	ospeed = oldtty.sg_ospeed;
X#ifdef	TIOCGLTC
X	if (ioctl(1, TIOCGLTC, (char *)&oldltchars) >= 0) {
X		newltchars = oldltchars;
X		newltchars.t_dsuspc = -1;
X		cwerase = oldltchars.t_werasc;
X	}
X#endif
X	initterm();
X#ifdef SIGTSTP
X	(void) signal(SIGTTIN, onstop);
X	(void) signal(SIGTTOU, onstop);
X	(void) signal(SIGTSTP, onstop);
X#endif /* SIGTSTP */
X}
X
X
X/*
X * Set tty modes for visual processing
X */
X
Xttyraw()
X{
X	while (ioctl(1, TIOCSETN, (char *)&newtty) < 0 && errno == EINTR)
X		;
X#ifdef TIOCGLTC
X	if (newltchars.t_dsuspc == '\377')
X	  while (ioctl(1, TIOCSLTC, (char *)&newltchars) < 0 && errno == EINTR)
X		;
X#endif
X	rawterm();
X}
X
Xttyowait()
X{	/* wait for output queue to drain */
X#ifdef TIOCDRAIN	/* This ioctl is a local mod on linus */
X	(void) ioctl(1, TIOCDRAIN, (char *)0);
X#endif
X}
X
X
X/*
X * Restore tty modes
X */
X
Xttycooked()
X{
X	cookedterm();
X	vflush();
X	while (ioctl(1, TIOCSETN, (char *)&oldtty) < 0 && errno == EINTR)
X		;
X#ifdef TIOCGLTC
X	if (newltchars.t_dsuspc == '\377')
X	  while (ioctl(1, TIOCSLTC, (char *)&oldltchars) < 0 && errno == EINTR)
X		;
X#endif
X}
X
X#endif
X
X
X
X/*** signal handlers ***/
X
Xonint() {
X#ifdef SIGTSTP
X	int dojump = reading;
X
X	reading = FALSE;
X#endif /* SIGTSTP */
X	if (!news) {
X		ttycooked();
X		xxit(1);
X	}
X	(void) signal(SIGINT, onint);
X	clearin();			/* flush input queue */
X#ifdef SIGTSTP
X	if (dojump)
X		longjmp(intjmp, 1);
X#endif /* SIGTSTP */
X	intflag++;
X}
X
X#ifdef SIGTSTP
Xonstop(signo)
Xint signo;
X{
X	/* restore old terminal state */
X	botscreen();
X	vflush();
X	ttycooked();
X	(void) signal(signo, SIG_DFL);
X#ifdef BSD4_2
X	(void) sigblock(sigmask(SIGALRM)|sigmask(SIGINT));
X	(void) sigsetmask(sigblock(0) & ~sigmask(signo));
X#else /* BSD4_1 */
X	(void) alarm(0);
X#endif /* BSD4_1 */
X	(void) kill(0, signo);	/* stop here until continued */
X
X	(void) signal(signo, onstop);
X	/* restore our special terminal state */
X	ttyraw();
X#ifdef TIOCGWINSZ
X	winch();	/* get current window size and redraw screen */
X#else 	/* !TIOCGWINSZ */
X	clearok(curscr, 1);
X	updscr();
X#endif 	/* !TIOCGWINSZ */
X#ifdef BSD4_2
X	(void) sigsetmask(sigblock(0) & ~(sigmask(SIGALRM)|sigmask(SIGINT)));
X#else /* BSD4_1 */
X	timer();
X#endif /* BSD4_1 */
X}
X#endif
X
X/*** stolen from rfuncs2.c and modified ***/
X
Xvsave(to, flags)
Xregister char *to;
X{
X	register FILE *ufp;
X	int	isprogram = 0;
X	int	isnew = 1;
X	long	saveoff;
X	char	temp[20];
X	char	*fname;
X	char	prog[BUFLEN + 24];
X	int	err;
X
X	saveoff = ftell(fp);
X	(void) fseek(fp, artbody, 0);
X	fname = to;
X	if (*to == PIPECHAR) {
X		if (strlen(to) > BUFLEN) {
X			msg("Command name too long");
X			goto out;
X		}
X		flags |= OVWRITE;
X		(void) strcpy(temp, "/tmp/vnXXXXXX");
X		(void) mktemp(temp);
X		fname = temp;
X		_amove(ROWS - 1, 0);
X		vflush();
X	}
X	if ((flags & OVWRITE) == 0) {
X		ufp = fopen(fname, "r");
X		if (ufp != NULL) {
X			(void) fclose(ufp);
X			isnew = 0;
X		}
X	}
X	(void) umask(savmask);
X
X	if (*to == PIPECHAR)
X		isprogram++;
X	if ((ufp = fopen(fname, (flags & OVWRITE) == 0? "a" : "w")) == NULL) {
X		msg("Cannot open %s", fname);
X		goto out;
X	}
X	/*
X	 * V7MAIL code is here to conform to V7 mail format.
X	 * If you need a different format to be able to
X	 * use your local mail command (such as four ^A's
X	 * on the end of articles) substitute it here.
X	 */
X	if (flags & SVHEAD) {
X#ifdef MMDF
X		if (!isprogram)
X			fprintf(ufp, "\001\001\001\001\n");
X#endif /* MMDF */
X#ifdef V7MAIL
X		h->subtime = cgtdate(h->subdate);
X		fprintf(ufp, "From %s %s", replyname(h), ctime(&h->subtime));
X#endif
X		hprint(h, ufp, 2);
X#ifdef V7MAIL
X		tprint(fp, ufp, TRUE);
X		putc('\n', ufp);	/* force blank line at end (ugh) */
X#else
X		tprint(fp, ufp, FALSE);
X#endif
X	} else {
X		tprint(fp, ufp, FALSE);
X	}
X
X	err = ferror(ufp);
X
X	fclose(ufp);
X	if (isprogram) {
X		if (err)
X			msg("error in writing temp file, maybe disk full?");
X		else {
X			(void) sprintf(prog, "(%s)<%s", to + 1, fname);
X			shcmd(prog, CWAIT);
X			prflags |= NOPRT;
X		}
X	} else {
X		msg("%sfile: %s %s",
X			err? "ERROR WHILE WRITING ": "",
X			to,
X			(flags&OVWRITE)? "written":
X				isnew ? "created" : "appended");
X	}
X
X	/* If we got an error, screen may be messed.  E.g. 4.2BSD
X	 * writes "disk full" messages to the user's tty.
X	 */
X	if (err) {
X		clearok(curscr, 1);
X		updscr();
X	}
X
Xout:
X	if (isprogram) {
X		(void) unlink(fname);
X	}
X	(void) umask(N_UMASK);
X	(void) fseek(fp, saveoff, 0);
X}
X
Xxxit(status)
Xint	status;
X{
X	(void) unlink(infile);
X	(void) unlink(outfile);
X#ifdef SORTACTIVE
X	if (strncmp(ACTIVE,"/tmp/", 5) == 0)
X		(void) unlink(ACTIVE);
X#endif /* SORTACTIVE */
X	if (ospeed) {	/* is == 0, we haven't been in raw mode yet */
X		botscreen();
X		vflush();
X		ttycooked();
X	}
X	exit(status);
X}
END_OF_FILE
if test 55369 -ne `wc -c <'visual.c'`; then
    echo shar: \"'visual.c'\" unpacked with wrong size!
fi
# end of 'visual.c'
fi
echo shar: End of shell archive.
exit 0
-- 
		"Zeta Microcomputer Software"
ACSnet:    nick@ultima.cs.uts.oz
UUCP:      ...!uunet!munnari!ultima.cs.uts.oz!nick
Fidonet:   Nick Andrew on 3:713/602 (Zeta)

nick@ultima.cs.uts.oz (Nick Andrew) (12/07/89)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  compress.c
# Wrapped by nick@ultima on Thu Dec  7 22:48:42 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'compress.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'compress.c'\"
else
echo shar: Extracting \"'compress.c'\" \(35496 characters\)
sed "s/^X//" >'compress.c' <<'END_OF_FILE'
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)compress.c	1.13	12/16/86";
X#endif /* SCCSID */
Xstatic char rcs_ident[] = "Based on compress.c,v 4.0 85/07/30 12:50:00 joe Release";
X
X/* 
X * Compress - data compression program 
X */
X#define	min(a,b)	((a>b) ? b : a)
X
X/*
X * machine variants which require cc -Dmachine:  pdp11, z8000, pcxt
X */
X
X/*
X * Set USERMEM to the maximum amount of physical user memory available
X * in bytes.  USERMEM is used to determine the maximum BITS that can be used
X * for compression.
X *
X * SACREDMEM is the amount of physical memory saved for others; compress
X * will hog the rest.
X */
X#ifndef SACREDMEM
X#define SACREDMEM	0
X#endif
X
X#ifndef USERMEM
X# define USERMEM 	450000	/* default user memory */
X#endif
X
X#ifdef interdata		/* (Perkin-Elmer) */
X#define SIGNED_COMPARE_SLOW	/* signed compare is slower than unsigned */
X#endif
X
X#ifdef pdp11
X# define BITS 	12	/* max bits/code for 16-bit machine */
X# define NO_UCHAR	/* also if "unsigned char" functions as signed char */
X# undef USERMEM 
X#endif /* pdp11 */	/* don't forget to compile with -i */
X
X#ifdef z8000
X# define BITS 	12
X# undef vax		/* weird preprocessor */
X# undef USERMEM 
X#endif /* z8000 */
X
X#ifdef pcxt
X# define BITS   12
X# undef USERMEM
X#endif /* pcxt */
X
X#ifdef MINIX
X/* Minixy things to at least make it compile */
X# define BITS   12
X# undef USERMEM
X#endif /* pcxt */
X
X#ifdef USERMEM
X# if USERMEM >= (433484+SACREDMEM)
X#  define PBITS	16
X# else
X#  if USERMEM >= (229600+SACREDMEM)
X#   define PBITS	15
X#  else
X#   if USERMEM >= (127536+SACREDMEM)
X#    define PBITS	14
X#   else
X#    if USERMEM >= (73464+SACREDMEM)
X#     define PBITS	13
X#    else
X#     define PBITS	12
X#    endif
X#   endif
X#  endif
X# endif
X# undef USERMEM
X#endif /* USERMEM */
X
X#ifdef PBITS		/* Preferred BITS for this memory size */
X# ifndef BITS
X#  define BITS PBITS
X# endif BITS
X#endif /* PBITS */
X
X#if BITS == 16
X# define HSIZE	69001		/* 95% occupancy */
X#endif
X#if BITS == 15
X# define HSIZE	35023		/* 94% occupancy */
X#endif
X#if BITS == 14
X# define HSIZE	18013		/* 91% occupancy */
X#endif
X#if BITS == 13
X# define HSIZE	9001		/* 91% occupancy */
X#endif
X#if BITS <= 12
X# define HSIZE	5003		/* 80% occupancy */
X#endif
X
X#ifdef M_XENIX			/* Stupid compiler can't handle arrays with */
X# if BITS == 16			/* more than 65535 bytes - so we fake it */
X#  define XENIX_16
X# else
X#  if BITS > 13			/* Code only handles BITS = 12, 13, or 16 */
X#   define BITS	13
X#  endif
X# endif
X#endif
X
X/*
X * a code_int must be able to hold 2**BITS values of type int, and also -1
X */
X#if BITS > 15
Xtypedef long int	code_int;
X#else
Xtypedef int		code_int;
X#endif
X
X#ifdef SIGNED_COMPARE_SLOW
Xtypedef unsigned long int count_int;
Xtypedef unsigned short int count_short;
X#else
Xtypedef long int	  count_int;
X#endif
X
X#ifdef NO_UCHAR
X typedef char	char_type;
X#else
X typedef	unsigned char	char_type;
X#endif /* UCHAR */
Xchar_type magic_header[] = { "\037\235" };	/* 1F 9D */
X
X/* Defines for third byte of header */
X#define BIT_MASK	0x1f
X#define BLOCK_MASK	0x80
X/* Masks 0x40 and 0x20 are free.  I think 0x20 should mean that there is
X   a fourth header byte (for expansion).
X*/
X#define INIT_BITS 9			/* initial number of bits/code */
X
X/*
X * compress.c - File compression ala IEEE Computer, June 1984.
X *
X * Authors:	Spencer W. Thomas	(decvax!harpo!utah-cs!utah-gr!thomas)
X *		Jim McKie		(decvax!mcvax!jim)
X *		Steve Davies		(decvax!vax135!petsd!peora!srd)
X *		Ken Turkowski		(decvax!decwrl!turtlevax!ken)
X *		James A. Woods		(decvax!ihnp4!ames!jaw)
X *		Joe Orost		(decvax!vax135!petsd!joe)
X *
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <signal.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X
X#define ARGVAL() (*++(*argv) || (--argc && *++argv))
X
Xint n_bits;				/* number of bits/code */
Xint maxbits = BITS;			/* user settable max # bits/code */
Xcode_int maxcode;			/* maximum code, given n_bits */
Xcode_int maxmaxcode = 1L << BITS;	/* should NEVER generate this code */
X#ifdef COMPATIBLE		/* But wrong! */
X# define MAXCODE(n_bits)	(1 << (n_bits) - 1)
X#else
X# define MAXCODE(n_bits)	((1 << (n_bits)) - 1)
X#endif /* COMPATIBLE */
X
X#ifdef XENIX_16
Xcount_int htab0[8192];
Xcount_int htab1[8192];
Xcount_int htab2[8192];
Xcount_int htab3[8192];
Xcount_int htab4[8192];
Xcount_int htab5[8192];
Xcount_int htab6[8192];
Xcount_int htab7[8192];
Xcount_int htab8[HSIZE-65536];
Xcount_int * htab[9] = {
X	htab0, htab1, htab2, htab3, htab4, htab5, htab6, htab7, htab8 };
X
X#define htabof(i)	(htab[(i) >> 13][(i) & 0x1fff])
Xunsigned short code0tab[16384];
Xunsigned short code1tab[16384];
Xunsigned short code2tab[16384];
Xunsigned short code3tab[16384];
Xunsigned short code4tab[16384];
Xunsigned short * codetab[5] = {
X	code0tab, code1tab, code2tab, code3tab, code4tab };
X
X#define codetabof(i)	(codetab[(i) >> 14][(i) & 0x3fff])
X
X#else	/* Normal machine */
X# ifdef sel
X/* support gould base register problems */
X/*NOBASE*/
Xcount_int htab [HSIZE];
Xunsigned short codetab [HSIZE];
X/*NOBASE*/
X# else /* !gould */
Xcount_int htab [HSIZE];
Xunsigned short codetab [HSIZE];
X# endif /* !gould */
X#define htabof(i)	htab[i]
X#define codetabof(i)	codetab[i]
X#endif	/* !XENIX_16 */
Xcode_int hsize = HSIZE;			/* for dynamic table sizing */
Xcount_int fsize;
X
X/*
X * To save much memory, we overlay the table used by compress() with those
X * used by decompress().  The tab_prefix table is the same size and type
X * as the codetab.  The tab_suffix table needs 2**BITS characters.  We
X * get this from the beginning of htab.  The output stack uses the rest
X * of htab, and contains characters.  There is plenty of room for any
X * possible stack (stack used to be 8000 characters).
X */
X
X#define tab_prefixof(i)	codetabof(i)
X#ifdef XENIX_16
X# define tab_suffixof(i)	((char_type *)htab[(i)>>15])[(i) & 0x7fff]
X# define de_stack		((char_type *)(htab2))
X#else	/* Normal machine */
X# define tab_suffixof(i)	((char_type *)(htab))[i]
X# define de_stack		((char_type *)&tab_suffixof(1<<BITS))
X#endif	/* XENIX_16 */
X
Xcode_int free_ent = 0;			/* first unused entry */
Xint exit_stat = 0;
X
Xcode_int getcode();
X
XUsage() {
X#ifdef DEBUG
Xfprintf(stderr,"Usage: compress [-dDVfc] [-b maxbits] [file ...]\n");
X}
Xint debug = 0;
X#else
Xfprintf(stderr,"Usage: compress [-dfvcV] [-b maxbits] [file ...]\n");
X}
X#endif /* DEBUG */
Xint nomagic = 0;	/* Use a 3-byte magic number header, unless old file */
Xint zcat_flg = 0;	/* Write output on stdout, suppress messages */
Xint quiet = 1;		/* don't tell me about compression */
X
X/*
X * block compression parameters -- after all codes are used up,
X * and compression rate changes, start over.
X */
Xint block_compress = BLOCK_MASK;
Xint clear_flg = 0;
Xlong int ratio = 0;
X#define CHECK_GAP 10000	/* ratio check interval */
Xcount_int checkpoint = CHECK_GAP;
X/*
X * the next two codes should not be changed lightly, as they must not
X * lie within the contiguous general code space.
X */ 
X#define FIRST	257	/* first free entry */
X#define	CLEAR	256	/* table clear output code */
X
Xint force = 0;
Xchar ofname [100];
X#ifdef DEBUG
Xint verbose = 0;
X#endif /* DEBUG */
Xint (*bgnd_flag)();
X
Xint do_decomp = 0;
X
X/*****************************************************************
X * TAG( main )
X *
X * Algorithm from "A Technique for High Performance Data Compression",
X * Terry A. Welch, IEEE Computer Vol 17, No 6 (June 1984), pp 8-19.
X *
X * Usage: compress [-dfvc] [-b bits] [file ...]
X * Inputs:
X *	-d:	    If given, decompression is done instead.
X *
X *      -c:         Write output on stdout, don't remove original.
X *
X *      -b:         Parameter limits the max number of bits/code.
X *
X *	-f:	    Forces output file to be generated, even if one already
X *		    exists, and even if no space is saved by compressing.
X *		    If -f is not used, the user will be prompted if stdin is
X *		    a tty, otherwise, the output file will not be overwritten.
X *
X *      -v:	    Write compression statistics
X *
X * 	file ...:   Files to be compressed.  If none specified, stdin
X *		    is used.
X * Outputs:
X *	file.Z:	    Compressed form of file with same mode, owner, and utimes
X * 	or stdout   (if stdin used as input)
X *
X * Assumptions:
X *	When filenames are given, replaces with the compressed version
X *	(.Z suffix) only if the file decreases in size.
X * Algorithm:
X * 	Modified Lempel-Ziv method (LZW).  Basically finds common
X * substrings and replaces them with a variable size code.  This is
X * deterministic, and can be done on the fly.  Thus, the decompression
X * procedure needs no input table, but tracks the way the table was built.
X */
X
Xmain( argc, argv )
Xregister int argc; char **argv;
X{
X    int overwrite = 0;	/* Do not overwrite unless given -f flag */
X    char tempname[100];
X    char **filelist, **fileptr;
X    char *cp, *rindex(), *malloc();
X    struct stat statbuf;
X    extern onintr(), oops();
X
X
X    if ( (bgnd_flag = signal ( SIGINT, SIG_IGN )) != SIG_IGN ) {
X	signal ( SIGINT, onintr );
X	signal ( SIGSEGV, oops );
X    }
X
X#ifdef COMPATIBLE
X    nomagic = 1;	/* Original didn't have a magic number */
X#endif /* COMPATIBLE */
X
X    filelist = fileptr = (char **)(malloc(argc * sizeof(*argv)));
X    *filelist = NULL;
X
X    if((cp = rindex(argv[0], '/')) != 0) {
X	cp++;
X    } else {
X	cp = argv[0];
X    }
X    if(strcmp(cp, "uncompress") == 0) {
X	do_decomp = 1;
X    } else if(strcmp(cp, "zcat") == 0) {
X	do_decomp = 1;
X	zcat_flg = 1;
X    }
X
X#ifdef BSD4_2
X    /* 4.2BSD dependent - take it out if not */
X    setlinebuf( stderr );
X#endif /* BSD4_2 */
X
X    /* Argument Processing
X     * All flags are optional.
X     * -D => debug
X     * -V => print Version; debug verbose
X     * -d => do_decomp
X     * -v => unquiet
X     * -f => force overwrite of output file
X     * -n => no header: useful to uncompress old files
X     * -b maxbits => maxbits.  If -b is specified, then maxbits MUST be
X     *	    given also.
X     * -c => cat all output to stdout
X     * -C => generate output compatible with compress 2.0.
X     * if a string is left, must be an input filename.
X     */
X    for (argc--, argv++; argc > 0; argc--, argv++) {
X	if (**argv == '-') {	/* A flag argument */
X	    while (*++(*argv)) {	/* Process all flags in this arg */
X		switch (**argv) {
X#ifdef DEBUG
X		    case 'D':
X			debug = 1;
X			break;
X		    case 'V':
X			verbose = 1;
X			version();
X			break;
X#else
X		    case 'V':
X			version();
X			break;
X#endif /* DEBUG */
X		    case 'v':
X			quiet = 0;
X			break;
X		    case 'd':
X			do_decomp = 1;
X			break;
X		    case 'f':
X		    case 'F':
X			overwrite = 1;
X			force = 1;
X			break;
X		    case 'n':
X			nomagic = 1;
X			break;
X		    case 'C':
X			block_compress = 0;
X			break;
X		    case 'b':
X			if (!ARGVAL()) {
X			    fprintf(stderr, "Missing maxbits\n");
X			    Usage();
X			    exit(1);
X			}
X			maxbits = atoi(*argv);
X			goto nextarg;
X		    case 'c':
X			zcat_flg = 1;
X			break;
X		    case 'q':
X			quiet = 1;
X			break;
X		    default:
X			fprintf(stderr, "Unknown flag: '%c'; ", **argv);
X			Usage();
X			exit(1);
X		}
X	    }
X	}
X	else {		/* Input file name */
X	    *fileptr++ = *argv;	/* Build input file list */
X	    *fileptr = NULL;
X	    /* process nextarg; */
X	}
X	nextarg: continue;
X    }
X
X    if(maxbits < INIT_BITS) maxbits = INIT_BITS;
X    if (maxbits > BITS) maxbits = BITS;
X    maxmaxcode = 1L << maxbits;
X
X    if (*filelist != NULL) {
X	for (fileptr = filelist; *fileptr; fileptr++) {
X	    exit_stat = 0;
X	    if (do_decomp != 0) {			/* DECOMPRESSION */
X		/* Check for .Z suffix */
X		if (strcmp(*fileptr + strlen(*fileptr) - 2, ".Z") != 0) {
X		    /* No .Z: tack one on */
X		    strcpy(tempname, *fileptr);
X		    strcat(tempname, ".Z");
X		    *fileptr = tempname;
X		}
X		/* Open input file */
X		if ((freopen(*fileptr, "r", stdin)) == NULL) {
X			perror(*fileptr); continue;
X		}
X		/* Check the magic number */
X		if (nomagic == 0) {
X		    if ((getchar() != (magic_header[0] & 0xFF))
X		     || (getchar() != (magic_header[1] & 0xFF))) {
X			fprintf(stderr, "%s: not in compressed format\n",
X			    *fileptr);
X		    continue;
X		    }
X		    maxbits = getchar();	/* set -b from file */
X		    block_compress = maxbits & BLOCK_MASK;
X		    maxbits &= BIT_MASK;
X		    maxmaxcode = 1L << maxbits;
X		    if(maxbits > BITS) {
X			fprintf(stderr,
X			"%s: compressed with %d bits, can only handle %d bits\n",
X			*fileptr, maxbits, BITS);
X			continue;
X		    }
X		}
X		/* Generate output filename */
X		strcpy(ofname, *fileptr);
X		ofname[strlen(*fileptr) - 2] = '\0';  /* Strip off .Z */
X	    } else {					/* COMPRESSION */
X		if (strcmp(*fileptr + strlen(*fileptr) - 2, ".Z") == 0) {
X		    	fprintf(stderr, "%s: already has .Z suffix -- no change\n",
X			    *fileptr);
X		    continue;
X		}
X		/* Open input file */
X		if ((freopen(*fileptr, "r", stdin)) == NULL) {
X		    perror(*fileptr); continue;
X		}
X		stat ( *fileptr, &statbuf );
X		fsize = (long) statbuf.st_size;
X		/*
X		 * tune hash table size for small files -- ad hoc,
X		 * but the sizes match earlier #defines, which
X		 * serve as upper bounds on the number of output codes. 
X		 */
X		hsize = HSIZE;
X		if ( fsize < (1 << 12) )
X		    hsize = min ( 5003, HSIZE );
X		else if ( fsize < (1 << 13) )
X		    hsize = min ( 9001, HSIZE );
X		else if ( fsize < (1 << 14) )
X		    hsize = min ( 18013, HSIZE );
X		else if ( fsize < (1 << 15) )
X		    hsize = min ( 35023, HSIZE );
X		else if ( fsize < 47000 )
X		    hsize = min ( 50021, HSIZE );
X
X		/* Generate output filename */
X		strcpy(ofname, *fileptr);
X#ifndef BSD4_2		/* Short filenames */
X		if ((cp=rindex(ofname,'/')) != NULL)	cp++;
X		else					cp = ofname;
X		if (strlen(cp) > 12) {
X		    fprintf(stderr,"%s: filename too long to tack on .Z\n",cp);
X		    continue;
X		}
X#endif  /* BSD4_2		Long filenames allowed */
X		strcat(ofname, ".Z");
X	    }
X	    /* Check for overwrite of existing file */
X	    if (overwrite == 0 && zcat_flg == 0) {
X		if (stat(ofname, &statbuf) == 0) {
X		    char response[2];
X		    response[0] = 'n';
X		    fprintf(stderr, "%s already exists;", ofname);
X		    if (foreground()) {
X			fprintf(stderr, " do you wish to overwrite %s (y or n)? ",
X			ofname);
X			fflush(stderr);
X			read(2, response, 2);
X			while (response[1] != '\n') {
X			    if (read(2, response+1, 1) < 0) {	/* Ack! */
X				perror("stderr"); break;
X			    }
X			}
X		    }
X		    if (response[0] != 'y') {
X			fprintf(stderr, "\tnot overwritten\n");
X			continue;
X		    }
X		}
X	    }
X	    if(zcat_flg == 0) {		/* Open output file */
X		if (freopen(ofname, "w", stdout) == NULL) {
X		    perror(ofname);
X		    continue;
X		}
X		if(!quiet)
X			fprintf(stderr, "%s: ", *fileptr);
X	    }
X
X	    /* Actually do the compression/decompression */
X	    if (do_decomp == 0)	compress();
X#ifndef DEBUG
X	    else			decompress();
X#else
X	    else if (debug == 0)	decompress();
X	    else			printcodes();
X	    if (verbose)		dump_tab();
X#endif /* DEBUG */
X	    if(zcat_flg == 0) {
X		copystat(*fileptr, ofname);	/* Copy stats */
X		if((exit_stat == 1) || (!quiet))
X			putc('\n', stderr);
X	    }
X	}
X    } else {		/* Standard input */
X	if (do_decomp == 0) {
X		compress();
X#ifdef DEBUG
X		if(verbose)		dump_tab();
X#endif /* DEBUG */
X		if(!quiet)
X			putc('\n', stderr);
X	} else {
X	    /* Check the magic number */
X	    if (nomagic == 0) {
X		if ((getchar()!=(magic_header[0] & 0xFF))
X		 || (getchar()!=(magic_header[1] & 0xFF))) {
X		    fprintf(stderr, "stdin: not in compressed format\n");
X		    exit(1);
X		}
X		maxbits = getchar();	/* set -b from file */
X		block_compress = maxbits & BLOCK_MASK;
X		maxbits &= BIT_MASK;
X		maxmaxcode = 1L << maxbits;
X		fsize = 100000;		/* assume stdin large for USERMEM */
X		if(maxbits > BITS) {
X			fprintf(stderr,
X			"stdin: compressed with %d bits, can only handle %d bits\n",
X			maxbits, BITS);
X			exit(1);
X		}
X	    }
X#ifndef DEBUG
X	    decompress();
X#else
X	    if (debug == 0)	decompress();
X	    else		printcodes();
X	    if (verbose)	dump_tab();
X#endif /* DEBUG */
X	}
X    }
X    exit(exit_stat);
X}
X
Xstatic int offset;
Xlong int in_count = 1;			/* length of input */
Xlong int bytes_out;			/* length of compressed output */
Xlong int out_count = 0;			/* # of codes output (for debugging) */
X
X/*
X * compress stdin to stdout
X *
X * Algorithm:  use open addressing double hashing (no chaining) on the 
X * prefix code / next character combination.  We do a variant of Knuth's
X * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
X * secondary probe.  Here, the modular division first probe is gives way
X * to a faster exclusive-or manipulation.  Also do block compression with
X * an adaptive reset, whereby the code table is cleared when the compression
X * ratio decreases, but after the table fills.  The variable-length output
X * codes are re-sized at this point, and a special CLEAR code is generated
X * for the decompressor.  Late addition:  construct the table according to
X * file size for noticeable speed improvement on small files.  Please direct
X * questions about this implementation to ames!jaw.
X */
X
Xcompress() {
X    register long fcode;
X    register code_int i = 0;
X    register int c;
X    register code_int ent;
X#ifdef XENIX_16
X    register code_int disp;
X#else	/* Normal machine */
X    register int disp;
X#endif
X    register code_int hsize_reg;
X    register int hshift;
X
X#ifndef COMPATIBLE
X    if (nomagic == 0) {
X	putchar(magic_header[0]); putchar(magic_header[1]);
X	putchar((char)(maxbits | block_compress));
X	if(ferror(stdout))
X		writeerr();
X    }
X#endif /* COMPATIBLE */
X
X    offset = 0;
X    bytes_out = 3;		/* includes 3-byte header mojo */
X    out_count = 0;
X    clear_flg = 0;
X    ratio = 0;
X    in_count = 1;
X    checkpoint = CHECK_GAP;
X    maxcode = MAXCODE(n_bits = INIT_BITS);
X    free_ent = ((block_compress) ? FIRST : 256 );
X
X    ent = getchar ();
X
X    hshift = 0;
X    for ( fcode = (long) hsize;  fcode < 65536L; fcode *= 2L )
X    	hshift++;
X    hshift = 8 - hshift;		/* set hash code range bound */
X
X    hsize_reg = hsize;
X    cl_hash( (count_int) hsize_reg);		/* clear hash table */
X
X#ifdef SIGNED_COMPARE_SLOW
X    while ( (c = getchar()) != (unsigned) EOF ) {
X#else
X    while ( (c = getchar()) != EOF ) {
X#endif
X	in_count++;
X	fcode = (long) (((long) c << maxbits) + ent);
X 	i = (((long)c << hshift) ^ ent);	/* xor hashing */
X
X	if ( htabof (i) == fcode ) {
X	    ent = codetabof (i);
X	    continue;
X	} else if ( (long)htabof (i) < 0 )	/* empty slot */
X	    goto nomatch;
X 	disp = hsize_reg - i;		/* secondary hash (after G. Knott) */
X	if ( i == 0 )
X	    disp = 1;
Xprobe:
X	if ( (i -= disp) < 0 )
X	    i += hsize_reg;
X
X	if ( htabof (i) == fcode ) {
X	    ent = codetabof (i);
X	    continue;
X	}
X	if ( (long)htabof (i) > 0 ) 
X	    goto probe;
Xnomatch:
X	output ( (code_int) ent );
X	out_count++;
X 	ent = c;
X#ifdef SIGNED_COMPARE_SLOW
X	if ( (unsigned) free_ent < (unsigned) maxmaxcode) {
X#else
X	if ( free_ent < maxmaxcode ) {
X#endif
X 	    codetabof (i) = free_ent++;	/* code -> hashtable */
X	    htabof (i) = fcode;
X	}
X	else if ( (count_int)in_count >= checkpoint && block_compress )
X	    cl_block ();
X    }
X    /*
X     * Put out the final code.
X     */
X    output( (code_int)ent );
X    out_count++;
X    output( (code_int)-1 );
X
X    /*
X     * Print out stats on stderr
X     */
X    if(zcat_flg == 0 && !quiet) {
X#ifdef DEBUG
X	fprintf( stderr,
X		"%ld chars in, %ld codes (%ld bytes) out, compression factor: ",
X		in_count, out_count, bytes_out );
X	prratio( stderr, in_count, bytes_out );
X	fprintf( stderr, "\n");
X	fprintf( stderr, "\tCompression as in compact: " );
X	prratio( stderr, in_count-bytes_out, in_count );
X	fprintf( stderr, "\n");
X	fprintf( stderr, "\tLargest code (of last block) was %d (%d bits)\n",
X		free_ent - 1, n_bits );
X#else /* !DEBUG */
X	fprintf( stderr, "Compression: " );
X	prratio( stderr, in_count-bytes_out, in_count );
X#endif /* DEBUG */
X    }
X    if(bytes_out > in_count)	/* exit(2) if no savings */
X	exit_stat = 2;
X    return;
X}
X
X/*****************************************************************
X * TAG( output )
X *
X * Output the given code.
X * Inputs:
X * 	code:	A n_bits-bit integer.  If == -1, then EOF.  This assumes
X *		that n_bits =< (long)wordsize - 1.
X * Outputs:
X * 	Outputs code to the file.
X * Assumptions:
X *	Chars are 8 bits long.
X * Algorithm:
X * 	Maintain a BITS character long buffer (so that 8 codes will
X * fit in it exactly).  Use the VAX insv instruction to insert each
X * code in turn.  When the buffer fills up empty it and start over.
X */
X
Xstatic char buf[BITS];
X
X#ifndef vax
Xchar_type lmask[9] = {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00};
Xchar_type rmask[9] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
X#endif /* vax */
X
Xoutput( code )
Xcode_int  code;
X{
X#ifdef DEBUG
X    static int col = 0;
X#endif /* DEBUG */
X
X    /*
X     * On the VAX, it is important to have the register declarations
X     * in exactly the order given, or the asm will break.
X     */
X    register int r_off = offset, bits= n_bits;
X    register char * bp = buf;
X
X#ifdef DEBUG
X	if ( verbose )
X	    fprintf( stderr, "%5d%c", code,
X		    (col+=6) >= 74 ? (col = 0, '\n') : ' ' );
X#endif /* DEBUG */
X    if ( code >= 0 ) {
X#ifdef vax
X	/* VAX DEPENDENT!! Implementation on other machines is below.
X	 *
X	 * Translation: Insert BITS bits from the argument starting at
X	 * offset bits from the beginning of buf.
X	 */
X	0;	/* Work around for pcc -O bug with asm and if stmt */
X	asm( "insv	4(ap),r11,r10,(r9)" );
X#else /* not a vax */
X/* 
X * byte/bit numbering on the VAX is simulated by the following code
X */
X	/*
X	 * Get to the first byte.
X	 */
X	bp += (r_off >> 3);
X	r_off &= 7;
X	/*
X	 * Since code is always >= 8 bits, only need to mask the first
X	 * hunk on the left.
X	 */
X	*bp = (*bp & rmask[r_off]) | (code << r_off) & lmask[r_off];
X	bp++;
X	bits -= (8 - r_off);
X	code >>= 8 - r_off;
X	/* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
X	if ( bits >= 8 ) {
X	    *bp++ = code;
X	    code >>= 8;
X	    bits -= 8;
X	}
X	/* Last bits. */
X	if(bits)
X	    *bp = code;
X#endif /* vax */
X	offset += n_bits;
X	if ( offset == (n_bits << 3) ) {
X	    bp = buf;
X	    bits = n_bits;
X	    bytes_out += bits;
X	    do
X		putchar(*bp++);
X	    while(--bits);
X	    offset = 0;
X	}
X
X	/*
X	 * If the next entry is going to be too big for the code size,
X	 * then increase it, if possible.
X	 */
X	if ( free_ent > maxcode || (clear_flg > 0))
X	{
X	    /*
X	     * Write the whole buffer, because the input side won't
X	     * discover the size increase until after it has read it.
X	     */
X	    if ( offset > 0 ) {
X		if( fwrite( buf, 1, n_bits, stdout ) != n_bits)
X			writeerr();
X		bytes_out += n_bits;
X	    }
X	    offset = 0;
X
X	    if ( clear_flg ) {
X    	        maxcode = MAXCODE (n_bits = INIT_BITS);
X	        clear_flg = 0;
X	    }
X	    else {
X	    	n_bits++;
X	    	if ( n_bits == maxbits )
X		    maxcode = maxmaxcode;
X	    	else
X		    maxcode = MAXCODE(n_bits);
X	    }
X#ifdef DEBUG
X	    if ( debug ) {
X		fprintf( stderr, "\nChange to %d bits\n", n_bits );
X		col = 0;
X	    }
X#endif /* DEBUG */
X	}
X    } else {
X	/*
X	 * At EOF, write the rest of the buffer.
X	 */
X	if ( offset > 0 )
X	    fwrite( buf, 1, (offset + 7) / 8, stdout );
X	bytes_out += (offset + 7) / 8;
X	offset = 0;
X	fflush( stdout );
X#ifdef DEBUG
X	if ( verbose )
X	    fprintf( stderr, "\n" );
X#endif /* DEBUG */
X	if( ferror( stdout ) )
X		writeerr();
X    }
X}
X
X/*
X * Decompress stdin to stdout.  This routine adapts to the codes in the
X * file building the "string" table on-the-fly; requiring no table to
X * be stored in the compressed file.  The tables used herein are shared
X * with those of the compress() routine.  See the definitions above.
X */
X
Xdecompress() {
X    register char_type *stackp;
X    register int finchar;
X    register code_int code, oldcode, incode;
X
X    /*
X     * As above, initialize the first 256 entries in the table.
X     */
X    maxcode = MAXCODE(n_bits = INIT_BITS);
X    for ( code = 255; code >= 0; code-- ) {
X	tab_prefixof(code) = 0;
X	tab_suffixof(code) = (char_type)code;
X    }
X    free_ent = ((block_compress) ? FIRST : 256 );
X
X    finchar = oldcode = getcode();
X    if(oldcode == -1)	/* EOF already? */
X	return;			/* Get out of here */
X    putchar( (char)finchar );		/* first code must be 8 bits = char */
X    if(ferror(stdout))		/* Crash if can't write */
X	writeerr();
X    stackp = de_stack;
X
X    while ( (code = getcode()) > -1 ) {
X
X	if ( (code == CLEAR) && block_compress ) {
X	    for ( code = 255; code >= 0; code-- )
X		tab_prefixof(code) = 0;
X	    clear_flg = 1;
X	    free_ent = FIRST - 1;
X	    if ( (code = getcode ()) == -1 )	/* O, untimely death! */
X		break;
X	}
X	incode = code;
X	/*
X	 * Special case for KwKwK string.
X	 */
X	if ( code >= free_ent ) {
X            *stackp++ = finchar;
X	    code = oldcode;
X	}
X
X	/*
X	 * Generate output characters in reverse order
X	 */
X#ifdef SIGNED_COMPARE_SLOW
X	while ( ((unsigned long)code) >= ((unsigned long)256) ) {
X#else
X	while ( code >= 256 ) {
X#endif
X	    *stackp++ = tab_suffixof(code);
X	    code = tab_prefixof(code);
X	}
X	*stackp++ = finchar = tab_suffixof(code);
X
X	/*
X	 * And put them out in forward order
X	 */
X	do
X	    putchar ( *--stackp );
X	while ( stackp > de_stack );
X
X	/*
X	 * Generate the new entry.
X	 */
X	if ( (code=free_ent) < maxmaxcode ) {
X	    tab_prefixof(code) = (unsigned short)oldcode;
X	    tab_suffixof(code) = finchar;
X	    free_ent = code+1;
X	} 
X	/*
X	 * Remember previous code.
X	 */
X	oldcode = incode;
X    }
X    fflush( stdout );
X    if(ferror(stdout))
X	writeerr();
X}
X
X/*****************************************************************
X * TAG( getcode )
X *
X * Read one code from the standard input.  If EOF, return -1.
X * Inputs:
X * 	stdin
X * Outputs:
X * 	code or -1 is returned.
X */
X
Xcode_int
Xgetcode() {
X    /*
X     * On the VAX, it is important to have the register declarations
X     * in exactly the order given, or the asm will break.
X     */
X    register code_int code;
X    static int offset = 0, size = 0;
X    static char_type buf[BITS];
X    register int r_off, bits;
X    register char_type *bp = buf;
X
X    if ( clear_flg > 0 || offset >= size || free_ent > maxcode ) {
X	/*
X	 * If the next entry will be too big for the current code
X	 * size, then we must increase the size.  This implies reading
X	 * a new buffer full, too.
X	 */
X	if ( free_ent > maxcode ) {
X	    n_bits++;
X	    if ( n_bits == maxbits )
X		maxcode = maxmaxcode;	/* won't get any bigger now */
X	    else
X		maxcode = MAXCODE(n_bits);
X	}
X	if ( clear_flg > 0) {
X    	    maxcode = MAXCODE (n_bits = INIT_BITS);
X	    clear_flg = 0;
X	}
X	size = fread( buf, 1, n_bits, stdin );
X	if ( size <= 0 )
X	    return -1;			/* end of file */
X	offset = 0;
X	/* Round size down to integral number of codes */
X	size = (size << 3) - (n_bits - 1);
X    }
X    r_off = offset;
X    bits = n_bits;
X#ifdef vax
X    asm( "extzv   r10,r9,(r8),r11" );
X#else /* not a vax */
X	/*
X	 * Get to the first byte.
X	 */
X	bp += (r_off >> 3);
X	r_off &= 7;
X	/* Get first part (low order bits) */
X#ifdef NO_UCHAR
X	code = ((*bp++ >> r_off) & rmask[8 - r_off]) & 0xff;
X#else
X	code = (*bp++ >> r_off);
X#endif /* NO_UCHAR */
X	bits -= (8 - r_off);
X	r_off = 8 - r_off;		/* now, offset into code word */
X	/* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
X	if ( bits >= 8 ) {
X#ifdef NO_UCHAR
X	    code |= (*bp++ & 0xff) << r_off;
X#else
X	    code |= *bp++ << r_off;
X#endif /* NO_UCHAR */
X	    r_off += 8;
X	    bits -= 8;
X	}
X	/* high order bits. */
X	code |= (*bp & rmask[bits]) << r_off;
X#endif /* vax */
X    offset += n_bits;
X
X    return code;
X}
X
Xchar *
Xrindex(s, c)		/* For those who don't have it in libc.a */
Xregister char *s, c;
X{
X	char *p;
X	for (p = NULL; *s; s++)
X	    if (*s == c)
X		p = s;
X	return(p);
X}
X
X#ifdef DEBUG
Xprintcodes()
X{
X    /*
X     * Just print out codes from input file.  For debugging.
X     */
X    code_int code;
X    int col = 0, bits;
X
X    bits = n_bits = INIT_BITS;
X    maxcode = MAXCODE(n_bits);
X    free_ent = ((block_compress) ? FIRST : 256 );
X    while ( ( code = getcode() ) >= 0 ) {
X	if ( (code == CLEAR) && block_compress ) {
X   	    free_ent = FIRST - 1;
X   	    clear_flg = 1;
X	}
X	else if ( free_ent < maxmaxcode )
X	    free_ent++;
X	if ( bits != n_bits ) {
X	    fprintf(stderr, "\nChange to %d bits\n", n_bits );
X	    bits = n_bits;
X	    col = 0;
X	}
X	fprintf(stderr, "%5d%c", code, (col+=6) >= 74 ? (col = 0, '\n') : ' ' );
X    }
X    putc( '\n', stderr );
X    exit( 0 );
X}
X
X#ifdef XENIX_16
Xcode_int stab1[8192] ;
Xcode_int stab2[8192] ;
Xcode_int stab3[8192] ;
Xcode_int stab4[8192] ;
Xcode_int stab5[8192] ;
Xcode_int stab6[8192] ;
Xcode_int stab7[8192] ;
Xcode_int stab8[8192] ;
Xcode_int * sorttab[8] = {stab1, stab2, stab3, stab4, stab5, stab6, stab7,
X						 stab8 } ;
X#define stabof(i) (sorttab[(i) >> 13][(i) & 0x1fff]) 
X#else
Xcode_int sorttab[SSIZE];	/* sorted pointers into htab */
X#define stabof(i) (sorttab[i])
X#endif
X
Xdump_tab()	/* dump string table */
X{
X    register int i, first;
X    register ent;
X#define STACK_SIZE	15000
X    int stack_top = STACK_SIZE;
X    register c;
X	unsigned mbshift ;
X
X    if(do_decomp == 0) {	/* compressing */
X	register int flag = 1;
X
X	for(i=0; i<hsize; i++) {	/* build sort pointers */
X		if((long)htabof(i) >= 0) {
X			stabof(codetabof(i)) = i;
X		}
X	}
X	first = block_compress ? FIRST : 256;
X	for(i = first; i < free_ent; i++) {
X		fprintf(stderr, "%5d: \"", i);
X		de_stack[--stack_top] = '\n';
X		de_stack[--stack_top] = '"';
X		stack_top = in_stack((htabof(stabof(i))>>maxbits)&0xff, 
X                                     stack_top);
X/*		for(ent=htabof(stabof(i)) & ((1<<maxbits)-1); */
X		mbshift = ((1 << maxbits) - 1) ;
X		ent = htabof(stabof(i)) & mbshift ;
X		for(;
X		    ent > 256;
X		    /* ent=htabof(stabof(ent)) & ((1<<maxbits)-1)) { */
X		    ent=htabof(stabof(ent)) & mbshift) {
X			stack_top = in_stack(htabof(stabof(ent)) >> maxbits,
X						stack_top);
X		}
X		stack_top = in_stack(ent, stack_top);
X		fwrite( &de_stack[stack_top], 1, STACK_SIZE-stack_top, stderr);
X	   	stack_top = STACK_SIZE;
X	}
X   } else if(!debug) {	/* decompressing */
X
X       for ( i = 0; i < free_ent; i++ ) {
X	   ent = i;
X	   c = tab_suffixof(ent);
X	   if ( isascii(c) && isprint(c) )
X	       fprintf( stderr, "%5d: %5d/'%c'  \"",
X			   ent, tab_prefixof(ent), c );
X	   else
X	       fprintf( stderr, "%5d: %5d/\\%03o \"",
X			   ent, tab_prefixof(ent), c );
X	   de_stack[--stack_top] = '\n';
X	   de_stack[--stack_top] = '"';
X	   for ( ; ent != NULL;
X		   ent = (ent >= FIRST ? tab_prefixof(ent) : NULL) ) {
X	       stack_top = in_stack(tab_suffixof(ent), stack_top);
X	   }
X	   fwrite( &de_stack[stack_top], 1, STACK_SIZE - stack_top, stderr );
X	   stack_top = STACK_SIZE;
X       }
X    }
X}
X
Xint
Xin_stack(c, stack_top)
X	register c, stack_top;
X{
X	if ( (isascii(c) && isprint(c) && c != '\\') || c == ' ' ) {
X	    de_stack[--stack_top] = c;
X	} else {
X	    switch( c ) {
X	    case '\n': de_stack[--stack_top] = 'n'; break;
X	    case '\t': de_stack[--stack_top] = 't'; break;
X	    case '\b': de_stack[--stack_top] = 'b'; break;
X	    case '\f': de_stack[--stack_top] = 'f'; break;
X	    case '\r': de_stack[--stack_top] = 'r'; break;
X	    case '\\': de_stack[--stack_top] = '\\'; break;
X	    default:
X	 	de_stack[--stack_top] = '0' + c % 8;
X	 	de_stack[--stack_top] = '0' + (c / 8) % 8;
X	 	de_stack[--stack_top] = '0' + c / 64;
X	 	break;
X	    }
X	    de_stack[--stack_top] = '\\';
X	}
X	return stack_top;
X}
X#endif /* DEBUG */
X
Xwriteerr()
X{
X    perror ( ofname );
X    unlink ( ofname );
X    exit ( 1 );
X}
X
Xcopystat(ifname, ofname)
Xchar *ifname, *ofname;
X{
X    struct stat statbuf;
X    int mode;
X    time_t timep[2];
X
X    fclose(stdout);
X    if (stat(ifname, &statbuf)) {		/* Get stat on input file */
X	perror(ifname);
X	return;
X    }
X    if ((statbuf.st_mode & S_IFMT/*0170000*/) != S_IFREG/*0100000*/) {
X	if(quiet)
X	    	fprintf(stderr, "%s: ", ifname);
X	fprintf(stderr, " -- not a regular file: unchanged");
X	exit_stat = 1;
X    } else if (statbuf.st_nlink > 1) {
X	if(quiet)
X	    	fprintf(stderr, "%s: ", ifname);
X	fprintf(stderr, " -- has %d other links: unchanged",
X		statbuf.st_nlink - 1);
X	exit_stat = 1;
X    } else if (exit_stat == 2 && (!force)) { /* No compression: remove file.Z */
X	if(!quiet)
X		fprintf(stderr, " -- file unchanged");
X    } else {			/* ***** Successful Compression ***** */
X	exit_stat = 0;
X	mode = statbuf.st_mode & 07777;
X	if (chmod(ofname, mode))		/* Copy modes */
X	    perror(ofname);
X	chown(ofname, statbuf.st_uid, statbuf.st_gid);	/* Copy ownership */
X	timep[0] = statbuf.st_atime;
X	timep[1] = statbuf.st_mtime;
X	utime(ofname, timep);	/* Update last accessed and modified times */
X	if (unlink(ifname))	/* Remove input file */
X	    perror(ifname);
X	if(!quiet)
X		fprintf(stderr, " -- replaced with %s", ofname);
X	return;		/* Successful return */
X    }
X
X    /* Unsuccessful return -- one of the tests failed */
X    if (unlink(ofname))
X	perror(ofname);
X}
X/*
X * This routine returns 1 if we are running in the foreground and stderr
X * is a tty.
X */
Xforeground()
X{
X	if(bgnd_flag) {	/* background? */
X		return(0);
X	} else {			/* foreground */
X		if(isatty(2)) {		/* and stderr is a tty */
X			return(1);
X		} else {
X			return(0);
X		}
X	}
X}
X
Xonintr ( )
X{
X    unlink ( ofname );
X    exit ( 1 );
X}
X
Xoops ( )	/* wild pointer -- assume bad input */
X{
X    if ( do_decomp == 1 ) 
X    	fprintf ( stderr, "uncompress: corrupt input\n" );
X    unlink ( ofname );
X    exit ( 1 );
X}
X
Xcl_block ()		/* table clear for block compress */
X{
X    register long int rat;
X
X    checkpoint = in_count + CHECK_GAP;
X#ifdef DEBUG
X	if ( debug ) {
X    		fprintf ( stderr, "count: %ld, ratio: ", in_count );
X     		prratio ( stderr, in_count, bytes_out );
X		fprintf ( stderr, "\n");
X	}
X#endif /* DEBUG */
X
X    if(in_count > 0x007fffff) {	/* shift will overflow */
X	rat = bytes_out >> 8;
X	if(rat == 0) {		/* Don't divide by zero */
X	    rat = 0x7fffffff;
X	} else {
X	    rat = in_count / rat;
X	}
X    } else {
X	rat = (in_count << 8) / bytes_out;	/* 8 fractional bits */
X    }
X    if ( rat > ratio ) {
X	ratio = rat;
X    } else {
X	ratio = 0;
X#ifdef DEBUG
X	if(verbose)
X		dump_tab();	/* dump string table */
X#endif
X 	cl_hash ( (count_int) hsize );
X	free_ent = FIRST;
X	clear_flg = 1;
X	output ( (code_int) CLEAR );
X#ifdef DEBUG
X	if(debug)
X    		fprintf ( stderr, "clear\n" );
X#endif /* DEBUG */
X    }
X}
X
Xcl_hash(hsize)		/* reset code table */
X	register count_int hsize;
X{
X#ifndef XENIX_16	/* Normal machine */
X	register count_int *htab_p = htab+hsize;
X#else
X	register j;
X	register long k = hsize;
X	register count_int *htab_p;
X#endif
X	register long i;
X	register long m1 = -1;
X
X#ifdef XENIX_16
X    for(j=0; j<=8 && k>=0; j++,k-=8192) {
X	i = 8192;
X	if(k < 8192) {
X		i = k;
X	}
X	htab_p = &(htab[j][i]);
X	i -= 16;
X	if(i > 0) {
X#else
X	i = hsize - 16;
X#endif
X 	do {				/* might use Sys V memset(3) here */
X		*(htab_p-16) = m1;
X		*(htab_p-15) = m1;
X		*(htab_p-14) = m1;
X		*(htab_p-13) = m1;
X		*(htab_p-12) = m1;
X		*(htab_p-11) = m1;
X		*(htab_p-10) = m1;
X		*(htab_p-9) = m1;
X		*(htab_p-8) = m1;
X		*(htab_p-7) = m1;
X		*(htab_p-6) = m1;
X		*(htab_p-5) = m1;
X		*(htab_p-4) = m1;
X		*(htab_p-3) = m1;
X		*(htab_p-2) = m1;
X		*(htab_p-1) = m1;
X		htab_p -= 16;
X	} while ((i -= 16) >= 0);
X#ifdef XENIX_16
X	}
X    }
X#endif
X    	for ( i += 16; i > 0; i-- )
X		*--htab_p = m1;
X}
X
Xprratio(stream, num, den)
XFILE *stream;
Xlong int num, den;
X{
X	register int q;			/* Doesn't need to be long */
X
X	if(num > 214748L) {		/* 2147483647/10000 */
X		q = num / (den / 10000L);
X	} else {
X		q = 10000L * num / den;		/* Long calculations, though */
X	}
X	if (q < 0) {
X		putc('-', stream);
X		q = -q;
X	}
X	fprintf(stream, "%d.%02d%%", q / 100, q % 100);
X}
X
Xversion()
X{
X	fprintf(stderr, "%s\n", rcs_ident);
X	fprintf(stderr, "Options: ");
X#ifdef vax
X	fprintf(stderr, "vax, ");
X#endif
X#ifdef NO_UCHAR
X	fprintf(stderr, "NO_UCHAR, ");
X#endif
X#ifdef SIGNED_COMPARE_SLOW
X	fprintf(stderr, "SIGNED_COMPARE_SLOW, ");
X#endif
X#ifdef XENIX_16
X	fprintf(stderr, "XENIX_16, ");
X#endif
X#ifdef COMPATIBLE
X	fprintf(stderr, "COMPATIBLE, ");
X#endif
X#ifdef DEBUG
X	fprintf(stderr, "DEBUG, ");
X#endif
X#ifdef BSD4_2
X	fprintf(stderr, "BSD4_2, ");
X#endif
X	fprintf(stderr, "BITS = %d\n", BITS);
X}
END_OF_FILE
if test 35496 -ne `wc -c <'compress.c'`; then
    echo shar: \"'compress.c'\" unpacked with wrong size!
fi
# end of 'compress.c'
fi
echo shar: End of shell archive.
exit 0
-- 
		"Zeta Microcomputer Software"
ACSnet:    nick@ultima.cs.uts.oz
UUCP:      ...!uunet!munnari!ultima.cs.uts.oz!nick
Fidonet:   Nick Andrew on 3:713/602 (Zeta)

nick@ultima.cs.uts.oz (Nick Andrew) (12/07/89)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  inews.c
# Wrapped by nick@ultima on Thu Dec  7 22:48:48 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'inews.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'inews.c'\"
else
echo shar: Extracting \"'inews.c'\" \(34390 characters\)
sed "s/^X//" >'inews.c' <<'END_OF_FILE'
X/*
X * This software is Copyright (c) 1986 by Rick Adams.
X *
X * Permission is hereby granted to copy, reproduce, redistribute or
X * otherwise use this software as long as: there is no monetary
X * profit gained specifically from the use or reproduction or this
X * software, it is not sold, rented, traded or otherwise marketed, and
X * this copyright notice is included prominently in any copy
X * made.
X *
X * The author make no claims as to the fitness or correctness of
X * this software for any use whatsoever, and it is provided as is. 
X * Any use of this software is at the user's own risk.
X *
X * inews - insert, receive, and transmit news articles.
X *
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)inews.c	2.80	4/10/87";
X#endif /* SCCSID */
X
X#include "iparams.h"
X
X# ifdef LOCKF
X# include <unistd.h>
X# include <fcntl.h>
X
X#  ifdef F_RDLCK
Xstruct flock news_lock;
X#  endif /* F_RDLCK */
X# endif /* LOCKF */
X
X#ifdef BSD4_2
X# include <sys/dir.h>
X# include <sys/file.h>
X#else	/* !BSD4_2 */
X# include "ndir.h"
X# if defined(USG) && !defined(LOCKF)
X# include <fcntl.h>
X# endif /* USG */
X#endif /* !BSD4_2 */
X/* local defines for inews */
X
X#define OPTION	0	/* pick up an option string */
X#define STRING	1	/* pick up a string of arguments */
X
X#define UNKNOWN 0001	/* possible modes for news program */
X#define UNPROC	0002	/* Unprocessed input */
X#define PROC	0004	/* Processed input */
X#define	CONTROL	0010	/* Control Message */
X#define	CREATENG 0020	/* Create a new newsgroup */
X
X#define DONT_SPOOL	0
X#define	DO_SPOOL	1
X#define	EXPIRE_RUNNING	2
Xint spool_news = DONT_SPOOL;
X
Xextern char histline[];
Xchar	forgedname[NAMELEN];	/* A user specified -f option. */
X/* Fake sys line in case they forget their own system */
Xstruct srec dummy_srec = { "MEMEME", "", "all", "", "" };
X
Xchar *Progname = "inews";	/* used by xerror to identify failing program */
X
Xstruct {			/* options table. */
X	char	optlet;		/* option character. */
X	char	filchar;	/* if to pickup string, fill character. */
X	int	flag;		/* TRUE if have seen this opt. */
X	int	oldmode;	/* OR of legal input modes. */
X	int	newmode;	/* output mode. */
X	char	*buf;		/* string buffer */
X} *optpt, options[] = { /*
Xoptlet	filchar		flag	oldmode	newmode		buf	*/
X't',	' ',		FALSE,	UNPROC,	UNKNOWN,	header.title,
X'n',	NGDELIM,	FALSE,	UNPROC,	UNKNOWN,	header.nbuf,
X'd',	'\0',		FALSE,	UNPROC,	UNKNOWN,	header.distribution,
X'e',	' ',		FALSE,	UNPROC,	UNKNOWN,	header.expdate,
X'p',	'\0',		FALSE,	UNKNOWN|PROC,	PROC,	filename,
X'f',	'\0',		FALSE,	UNPROC,	UNKNOWN,	forgedname,
X'F',	' ',		FALSE,	UNPROC,	UNKNOWN,	header.followid,
X'c',	' ',		FALSE,	UNKNOWN,UNKNOWN,	header.ctlmsg,
X'C',	' ',		FALSE,	UNKNOWN,CREATENG,	header.ctlmsg,
X#define hflag	options[9].flag
X'h',	'\0',		FALSE,	UNPROC,	UNKNOWN,	filename,
X#define oflag	options[10].flag
X'o',	'\0',		FALSE,	UNPROC, UNKNOWN,	header.organization,
X#define Mflag	options[11].flag
X'M',	'\0',		FALSE,	UNPROC, UNKNOWN,	filename,
X'a',	'\0',		FALSE,	UNPROC, UNKNOWN,	header.approved,
X'U',	'\0',		FALSE,	PROC, PROC,		filename,
X#define Sflag	options[14].flag
X'S',	'\0',		FALSE,	UNKNOWN|PROC, 	UNPROC,	filename,
X'x',	'\0',		FALSE,	UNPROC, UNKNOWN,	not_here,
X'r',	'\0',		FALSE,	UNPROC, UNKNOWN,	header.replyto,
X'\0',	'\0',		0,	0,	0,		(char *)NULL
X};
X
XFILE *mailhdr();
Xextern int errno;
X
Xstruct timeb Now;
X
X/*
X *	Authors:
X *		Matt Glickman	glickman@ucbarpa.Berkeley.ARPA
X *		Mark Horton	mark@cbosgd.UUCP
X *		Stephen Daniels	swd@mcnc.UUCP
X *		Tom Truscott	trt@duke.UUCP
X *		Rick Adams	rick@seismo.CSS.GOV
X *	IHCC version adapted by:
X *		Larry Marek	larry@ihuxf.UUCP
X */
Xmain(argc, argv)
Xint	argc;
Xregister char **argv;
X{
X	int	state;		/* which type of argument to pick up	*/
X	int	tlen, len;	/* temps for string processing routine	*/
X	register char *ptr;	/* pointer to rest of buffer		*/
X	int	filchar;	/* fill character (state = STRING)	*/
X	char	*user = NULL, *home = NULL;	/* environment temps	*/
X	struct passwd	*pw;	/* struct for pw lookup			*/
X	struct group	*gp;	/* struct for group lookup		*/
X	register int	i;
X	FILE	*mfd;		/* mail file file-descriptor		*/
X
X	/* uuxqt doesn't close all it's files */
X	for (i = 3; !close(i); i++)
X		;
X	/* set up defaults and initialize. */
X	mode = UNKNOWN;
X	infp = stdin;
X	pathinit();
X	savmask = umask(N_UMASK);	/* set up mask */
X	ptr = rindex(*argv, '/');
X	if (!ptr)
X		ptr = *argv - 1;
X	actfp = xfopen(ACTIVE, "r+");
X#ifdef	LOCKF
X# ifdef	F_RDLCK
X	news_lock.l_type = F_RDLCK;
X	if (fcntl(fileno(actfp), F_SETLK, &news_lock) < 0) {
X# else /* !F_RDLCK */
X	if (lockf(fileno(actfp), F_TLOCK, 0) < 0) {
X# endif /* !F_RDLCK */
X		if (errno != EAGAIN && errno != EACCES)
X#else	/* !LOCKF */
X#ifdef BSD4_2
X	if (flock(fileno(actfp), LOCK_SH|LOCK_NB) < 0) {
X		if (errno != EWOULDBLOCK)
X#else	/* !BSD4_2 */
X	sprintf(bfr, "%s.lock", ACTIVE);
X	if (LINK(ACTIVE, bfr) < 0) {
X		if (errno != EEXIST)
X#endif /* V7 */
X#endif	/* !BSD4_2 */
X			xerror("Can't lock %s: %s", ACTIVE, errmsg(errno));
X		spool_news = EXPIRE_RUNNING;
X	} else {
X#ifdef SPOOLNEWS
X		if (argc > 1 && !strcmp(*(argv+1), "-S")) {
X			argc--;
X			argv++;
X			Sflag = 1;
X		} else
X			spool_news = DO_SPOOL;
X
X#endif /* SPOOLNEWS */
X	}
X	if (spool_news != EXPIRE_RUNNING) {
X		/* only unlock if we locked */
X#ifdef	LOCKF
X		(void) lockf(fileno(actfp), F_ULOCK, 0);
X#else	/* !LOCKF */
X#ifdef 	BSD4_2
X		(void) flock(fileno(actfp), LOCK_UN);
X#else	/* !BSD4_2 */
X		(void) UNLINK(bfr);
X#endif 	/* V7 */
X#endif	/* !BSD4_2 */
X	} else {	/* expire is running */
X		if (argc > 1 && !strcmp(*(argv+1), "-S"))
X			exit(42);	/* inform rnews -U by exit status */
X	}
X	if (argc > 1 && !strcmp(*(argv+1), "-U")) {
X		/* can't unspool while things are locked */
X		if (spool_news == EXPIRE_RUNNING)
X			xxit(0);
X		dounspool();
X		/* NOT REACHED */
X	}
X
X	if (!strncmp(ptr+1, "rnews", 5)) {
X		mode = PROC;
X		if (spool_news != DONT_SPOOL) {
X			dospool((char *)NULL, FALSE);
X			/* NOT REACHED */
X		}
X#ifdef NICENESS
X		if (nice(0) < NICENESS)
X			(void) nice(NICENESS);
X#endif /* NICENESS */
X	} else {
X	/* it's not rnews, so it must be inews */
X		if (argc < 2)
X			goto usage;
X#ifndef SPOOLINEWS
X		if (spool_news == DO_SPOOL)
X			spool_news = DONT_SPOOL;
X#endif /* SPOOLINEWS */
X	}
X
X	state = OPTION;
X	header.title[0] = header.nbuf[0] = filename[0] = '\0';
X
X	/* check for existence of special files */
X#ifdef DBM
X	chkfile(ARTFILE);
X#else
X	chkdir(ARTFILE);
X#endif /* DBM */
X	chkfile(ACTIVE);
X	SigTrap = FALSE;	/* true if a signal has been caught */
X	if (mode != PROC) {
X		(void) signal(SIGHUP, onsig);
X		(void) signal(SIGINT, onsig);
X	}
X	uid = getuid();
X	gid = getgid();
X	duid = geteuid();
X	dgid = getegid();
X	(void) ftime(&Now);
X	if (uid == 0 && duid == 0) {
X		/*
X		 * Must go through with this kludge since
X		 * some systems do not honor the setuid bit
X		 * when root invokes a setuid program.
X		 */
X		if ((pw = getpwnam(NEWSUSR)) == NULL)
X			xerror("Cannot get NEWSU pw entry");
X
X		duid = pw->pw_uid;
X		if ((gp = getgrnam(NEWSGRP)) == NULL)
X			xerror("Cannot get NEWSG gr entry");
X		dgid = gp->gr_gid;
X		(void) setgid(dgid);
X		(void) setuid(duid);
X	}
X
X	/*
X	 * IHCC forces the use of 'getuser()' to prevent forgery of articles
X	 * by just changing $LOGNAME
X	 */
X#ifndef IHCC 
X	if (isatty(fileno(stderr))) {
X		if ((user = getenv("USER")) == NULL)
X			user = getenv("LOGNAME");
X		if ((home = getenv("HOME")) == NULL)
X			home = getenv("LOGDIR");
X	}
X#endif /* !IHCC */
X	if (user == NULL || home == NULL)
X		getuser();
X	else {
X		if (username == NULL || username[0] == 0) {
X			username = AllocCpy(user);
X		}
X		userhome = AllocCpy(home);
X	}
X	getuser();
X
X	/* loop once per arg. */
X
X	++argv;		/* skip first arg, which is prog name. */
X
X	while (--argc) {
X	    if (state == OPTION) {
X		if (**argv != '-') {
X			xerror("Bad option string \"%s\"", *argv);
X		}
X		while (*++*argv != '\0') {
X			for (optpt = options; optpt->optlet != '\0'; ++optpt) {
X				if (optpt->optlet == **argv)
X					goto found;
X			}
X			/* unknown option letter */
Xusage:
X			fprintf(stderr, "usage: inews -t title");
X			fprintf(stderr, " [ -n newsgroups ]");
X			fprintf(stderr, " [ -e expiration date ]\n");
X			fprintf(stderr, "\t[ -f sender]\n\n");
X			xxit(1);
X
X		    found:;
X			if (optpt->flag == TRUE || (mode != UNKNOWN &&
X			    (mode&optpt->oldmode) == 0)) {
X				xerror("Bad %c option", **argv);
X			}
X			if (mode == UNKNOWN)
X				mode = optpt->newmode;
X			filchar = optpt->filchar;
X			optpt->flag = TRUE;
X			state = STRING;
X			ptr = optpt->buf;
X			len = BUFLEN;
X		}
X
X		argv++;		/* done with this option arg. */
X
X	    } else {
X
X		/*
X		 * Pick up a piece of a string and put it into
X		 * the appropriate buffer.
X		 */
X		if (**argv == '-') {
X			state = OPTION;
X			argc++;	/* uncount this arg. */
X			continue;
X		}
X
X		if ((tlen = strlen(*argv)) >= len)
X			xerror("Argument string too long");
X		(void) strcpy(ptr, *argv++);
X		ptr += tlen;
X		if (*(ptr-1) != filchar)
X			*ptr++ = filchar;
X		len -= tlen + 1;
X		*ptr = '\0';
X	    }
X	}
X
X	/*
X	 * ALL of the command line has now been processed. (!)
X	 */
X
X	if (*filename) {
X		infp = freopen(filename, "r", stdin);
X		if (infp == NULL)
X			xerror("freopen(%s): %s", filename, errmsg(errno));
X	} else
X		infp = stdin;
X
X	tty = isatty(fileno(infp));
X
X	if (mode == CREATENG)
X		createng();
X
X	if (header.ctlmsg[0] != '\0' && header.title[0] == '\0')
X		(void) strcpy(header.title, header.ctlmsg);
X
X	if (*header.nbuf) {
X		lcase(header.nbuf);
X		ptr = index(header.nbuf, '\0');
X		if (ptr[-1] == NGDELIM)
X			*--ptr = '\0';
X	}
X	(void) nstrip(header.title);
X	(void) nstrip(header.expdate);
X	(void) nstrip(header.followid);
X	if (mode != PROC) {
X		if (hflag) {
X			header.path[0] = '\0';
X			(void) hread(&header, infp, FALSE);
X			/* there are certain fields we won't let him specify. */
X			if (header.from[0]) {
X				(void) fixfrom(header.from);
X				if (Sflag && !Mflag && !header.approved[0] &
X					!header.sender[0]) {
X					register char *p;
X					strcpy(bfr, header.from);
X					p  = strpbrk(bfr, "@ !");
X					if (p)
X						*p = '\0';
X					if ((pw = getpwnam(bfr)) != NULL) {
X						uid = pw->pw_uid;
X						gid = pw->pw_gid;
X						username = AllocCpy(bfr);
X					}
X				} else {
X					(void) strcpy(forgedname, header.from);
X					header.from[0] = '\0';
X				}
X			}
X			if (!header.approved[0])
X				Mflag = FALSE;
X			header.sender[0] = '\0';
X			if (header.subdate[0] && cgtdate(header.subdate) < 0)
X				header.subdate[0] = '\0';
X		}
X
X		if (header.ident[0] == '\0')
X			getident(&header);
X
X		if (forgedname[0]) {
X			register char *p1;
X			if (Mflag)
X				sprintf(header.path, "%s!%s",
X					PATHSYSNAME, username);
X			else if (!header.path[0]) {
X				(void) strcpy(header.path, forgedname);
X
X				if ((p1 = strpbrk(header.path, "@ (<")) != NULL)
X					*p1 = '\0';
X			}
X			if (!Mflag && !strpbrk(forgedname, "@ (<"))
X				(void) sprintf(header.from,"%s@%s",
X					forgedname, FROMSYSNAME);
X			else
X				(void) strncpy(header.from, forgedname, BUFLEN);
X
X			(void) sprintf(header.sender, "%s@%s",
X				username, FROMSYSNAME);
X		} else {
X			gensender(&header, username);
X		}
X#ifdef MYORG
X		if (header.organization[0] == '\0' && !Mflag &&
X			header.sender[0] == '\0') {
X			strncpy(header.organization, MYORG, BUFLEN);
X			if (strncmp(header.organization, "Frobozz", 7) == 0)
X				header.organization[0] = '\0';
X			if (ptr = getenv("ORGANIZATION"))
X				strncpy(header.organization, ptr, BUFLEN);
X			/*
X			 * Note that the organization can also be turned off by
X			 * setting it to the null string, either in MYORG or
X			 * $ORGANIZATION in the environment.
X			 */
X			if (header.organization[0] == '/') {
X				mfd = fopen(header.organization, "r");
X				if (mfd) {
X					(void) fgets(header.organization, sizeof header.organization, mfd);
X					(void) fclose(mfd);
X				} else {
X					header.organization[0] = '\0';
X					logerr("Couldn't open %s",
X						header.organization);
X				}
X				ptr = index(header.organization, '\n');
X				if (ptr)
X					*ptr = '\0';
X			}
X		}
X#endif /* MYORG */
X	}
X
X	/* Authorize newsgroups. */
X	if (mode == PROC) {
X		checkbatch();
X		(void) signal(SIGHUP, SIG_IGN);
X		(void) signal(SIGINT, SIG_IGN);
X		(void) signal(SIGQUIT, SIG_IGN);
X		header.ident[0] = '\0';
X		if (hread(&header, infp, TRUE) == NULL)
X			xerror("%s: Inbound news is garbled", filename);
X		input();
X	}
X	/* always check history */
X
X	if (history(&header)) {
X		log("Duplicate article %s rejected. Path: %s",
X			header.ident, header.path);
X		xxit(0);
X	}
X
X	/* Easy way to make control messages, since all.all.ctl is unblessed */
X	if (mode != PROC && prefix(header.title, "cmsg ") && header.ctlmsg[0] == 0)
X		(void) strcpy(header.ctlmsg, &header.title[5]);
X	is_ctl = mode != CREATENG &&
X		(ngmatch(header.nbuf, "all.all.ctl,") || header.ctlmsg[0]);
X#ifdef DEBUG
X	fprintf(stderr,"is_ctl set to %d\n", is_ctl);
X#endif
X
X	if (mode != CREATENG) {
X		if (!*header.title)
X			error("No title, ng %s from %s", header.nbuf,
X				header.from);
X		if (!*header.nbuf)
X			(void) strcpy(header.nbuf, DFLTNG);
X	}
X
X	if (mode <= UNPROC) {
X#ifdef FASCIST
X		if (uid && uid != ROOTID && fascist(user, header.nbuf))
X			xerror("User %s is not authorized to post to newsgroup %s",
X				user, header.nbuf);
X#endif /* FASCIST */
X		ctlcheck();
X	}
X
X	if (mode == CREATENG)
X		createng();
X
X	/* Determine input. */
X	if (mode != PROC)
X		input();
X	if (header.intnumlines == 0 && !is_ctl)
X		error("%s rejected: no text lines", header.ident);
X
X	dates(&header);
X
X	/* Do the actual insertion. */
X	insert();
X	/* NOTREACHED */
X}
X
X/* check for existence of file */
Xstatic chkfile(f)
Xchar *f;
X{
X	FILE	*mfd;		/* mail file file-descriptor		*/
X	char	cbuf[BUFLEN];	/* command buffer			*/
X
X	if (rwaccess(f))
X		return;	/* everything is ok */
X	mfd = mailhdr((struct hbuf *)NULL,
X		exists(f) ? "Unwritable files!" : "Missing files!");
X	if (mfd == NULL)
X		return;
X	putc('\n', mfd);
X	fprintf(mfd, "System: %s\n\nThere was a problem with %s!!\n",
X		LOCALSYSNAME, f);
X	(void) sprintf(cbuf, "touch %s;chmod 666 %s", f, f);
X	(void) system(cbuf);
X	if (rwaccess(f))
X		fprintf(mfd, "The problem has been taken care of.\n");
X	else
X		fprintf(mfd, "Corrective action failed - check suid bits.\n");
X	(void) mclose(mfd);
X}
X
X#ifndef DBM
X/* check for existence of directory */
Xstatic chkdir(d)
Xchar *d;
X{
X	FILE	*mfd;		/* mail file file-descriptor		*/
X	char	dir[BUFLEN];	/* holds directory name			*/
X
X	sprintf(dir, "%s.d", d);
X	if (eaccess(dir, 07) == 0)
X		return; /* everything is ok */
X	mfd = mailhdr((struct hbuf *)NULL,
X		exists(dir) ? "Unwritable directories" : "Missing directories");
X	if (mfd == NULL)
X		return;
X	putc('\n', mfd);
X	fprintf(mfd, "System: %s\n\nThere was a problem with %s!\n",
X		LOCALSYSNAME, dir);
X	(void) mkdir(dir, 0775);
X	if (eaccess(dir, 07) == 0)
X		fprintf(mfd, "The problem has been taken care of.\n");
X	else
X		fprintf(mfd, "Corrective action failed - check suid bits.\n");
X	(void) mclose(mfd);
X}
X
X/*
X * This version of access checks against effective uid and effective gid
X */
Xeaccess(name, mode)
Xregister char *name;
Xregister int mode;
X{	
X	struct stat statb;
X	int euserid = geteuid();
X	int egroupid = getegid();
X
X	if (stat(name, &statb) == 0) {
X		if (euserid == 0) {
X			if ((statb.st_mode&S_IFMT) != S_IFREG || mode != 1)
X				return 0;
X		    	/* root needs execute permission for someone */
X			mode = (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6));
X		}
X		else if (euserid == statb.st_uid)
X			mode <<= 6;
X		else if (egroupid == statb.st_gid)
X			mode <<= 3;
X#ifdef BSD4_2
X		/* in BSD4_2 you can be in several groups */
X		else {
X			int groups[NGROUPS];
X			register int n;
X			n = getgroups(NGROUPS,groups);
X			while(--n >= 0) {
X				if(groups[n] == statb.st_gid) {
X					mode <<= 3;
X					break;
X				}
X			}
X		}
X#endif /* BSD4_2 */
X
X		if (statb.st_mode & mode)
X			return 0;
X	}
X	return -1;
X}
X#endif /* DBM */
X
Xdospool(batchcmd, dolhwrite)
Xchar *batchcmd;
Xint dolhwrite;
X{
X	register int c;
X	register FILE *sp;
X	register struct tm *tp;
X	time_t t;
X	char buf[BUFLEN], sfile[BUFLEN];
X	extern struct tm *gmtime();
X
X	(void) sprintf(sfile, "%s/.spXXXXXX", SPOOL);
X	sp = xfopen(mktemp(sfile), "w");
X	if (batchcmd != NULL) {
X		if (not_here[0] != '\0')
X			fprintf(sp, "%s -x %s\n", batchcmd, not_here);
X		else
X			fprintf(sp, "%s\n", batchcmd);
X	} else
X		if (not_here[0] != '\0')
X			fprintf(sp, "#! inews -x %s -p\n", not_here);
X	if (dolhwrite)
X		lhwrite(&header, sp);
X	while ((c = getc(infp)) != EOF)
X		putc(c, sp);
X	fclose(sp);
X
X	(void) time(&t);
X	tp = gmtime(&t);
X	/* This file name "has to" be unique  (right?) */
X#ifdef USG
X	(void) sprintf(buf, "%s/.rnews/%2.2d%2.2d%2.2d%2.2d%2.2d%x",
X#else
X#ifdef VMS
X	/* Eunice doesn't like dots in directory names */
X	(void) sprintf(buf, "%s/+rnews/%02d%02d%02d%02d%02d%x",
X#else /* V7 */
X	(void) sprintf(buf, "%s/.rnews/%02d%02d%02d%02d%02d%x",
X#endif /* V7 */
X#endif /* VMS */
X		SPOOL,
X		tp->tm_year, tp->tm_mon+1, tp->tm_mday,
X		tp->tm_hour, tp->tm_min, getpid());
X	if (LINK(sfile, buf) < 0) {
X		char dbuf[BUFLEN];
X#ifdef VMS
X		sprintf(dbuf, "%s/+rnews", SPOOL);
X#else /* !VMS */
X		sprintf(dbuf, "%s/.rnews", SPOOL);
X#endif /* !VMS */
X		if (mkdir(dbuf, 0777&~N_UMASK) < 0)
X			xerror("Cannot mkdir %s: %s", dbuf, errmsg(errno));
X		if (LINK(sfile, buf) < 0) 
X			xerror("Cannot link(%s,%s): %s", sfile, buf,
X				errmsg(errno));
X	}
X	(void) UNLINK(sfile);
X	xxit(0);
X	/* NOTREACHED */
X}
X
X/*
X *	Create a newsgroup
X */
Xcreateng()
X{
X	register char *cp;
X
X	/*
X	 * Only certain users are allowed to create newsgroups
X	 */
X	if (uid != ROOTID && uid != duid && uid) {
X		logerr("Please contact one of the local netnews people");
X		xerror("to create group \"%s\" for you", header.ctlmsg);
X	}
X	if (header.distribution[0] == '\0')
X#ifdef ORGDISTRIB
X		strcpy(header.distribution, ORGDISTRIB);
X#else /* !ORGDISTRIB */
X		strcpy(header.distribution, "local");
X#endif /* !ORGDISTRIB */
X
X	(void) strcpy(header.nbuf, header.ctlmsg);
X	if ((cp=index(header.nbuf, ' ')) != NULL)
X		*cp = '\0';
X
X	if (header.approved[0] == '\0')
X		(void) sprintf(header.approved, "%s@%s",
X				username, FROMSYSNAME);
X	(void) sprintf(bfr, "%s/inews -n %s.ctl -c newgroup %s -d %s -a \"%s\"",
X		LIB, header.nbuf, header.ctlmsg, header.distribution,
X		header.approved);
X	if (tty) {
X		printf("Please type in a paragraph describing the new newsgroup.\n");
X		printf("End with control D as usual.\n");
X	}
X	printf("%s\n", bfr);
X	(void) fflush(stdout);
X	(void) system(bfr);
X	exit(0);
X	/*NOTREACHED*/
X}
X
Xchar firstbufname[BUFLEN];
X/*
X *	Link ARTICLE into dir for ngname and update active file.
X */
Xlong
Xlocalize(ngname)
Xchar	*ngname;
X{
X	char afline[BUFLEN];
X	long ngsize;
X	long fpos;
X	int e;
X	char *cp;
X
X	lock();
X	(void) rewind(actfp); clearerr(actfp);
X
X	for(;;) {
X		fpos = ftell(actfp);
X		if (fgets(afline, sizeof afline, actfp) == NULL) {
X			unlock();
X			logerr("Can't find \"%s\" in active file", ngname);
X			return FALSE;		/* No such newsgroup locally */
X		}
X		if (prefix(afline, ngname)) {
X			(void) sscanf(afline, "%s %ld", bfr, &ngsize);
X			if (strcmp(bfr, ngname) == 0) {
X				if (ngsize < 0 || ngsize > 99998) {
X					logerr("found bad ngsize %ld ng %s, setting to 1", ngsize, bfr);
X					ngsize = 1;
X				}
X				break;
X			}
X		}
X	}
X	for (;;) {
X		cp = dirname(ngname);
X
X		(void) sprintf(bfr, "%s/%ld", cp, ngsize+1);
X#ifdef VMS
X		/*
X		 * The effect of this code is to store the article in the first
X		 * newsgroup's directory and to put symbolic links elsewhere.
X		 * If this is the first group, firstbufname is not yet filled
X		 * in. It should be portable to other link-less systems.
X		 * epimass!jbuck
X		 */
X		if (firstbufname[0]) {
X			if (vmslink(firstbufname, bfr) == 0)
X				break;
X		} else if (rename(ARTICLE, bfr) == 0)
X			break;
X#else /* !VMS */
X		if (link(ARTICLE, bfr) == 0)
X			break;
X#endif /* !VMS */
X		if (!exists(cp))
X			mknewsg(cp, ngname);
X#ifdef VMS
X		if (firstbufname[0]) {
X			if (vmslink(firstbufname, bfr) == 0)
X				break;
X		} else if (rename(ARTICLE, bfr) == 0) 
X			break;
X#else /* !VMS */
X		if (link(ARTICLE, bfr) == 0)
X			break;
X#endif /* !VMS */
X		e = errno;	/* keep log from clobbering it */
X		log("Cannot install article as %s: %s", bfr, errmsg(errno));
X		if (e != EEXIST) {
X			logerr("Link into %s failed (%s); check dir permissions.",
X			    bfr, errmsg(e));
X			unlock();
X			return FALSE;
X		}
X		ngsize++;
X	}
X
X	/*
X	 * This works around a bug in the 4.1bsd stdio
X	 * on fseeks to non even offsets in r+w files
X	 */
X	if (fpos&1)
X		(void) rewind(actfp);
X
X	(void) fseek(actfp, fpos, 0);
X	/*
X	 * Has to be same size as old because of %05d.
X	 * This will overflow with 99999 articles.
X	 */
X	fprintf(actfp, "%s %05ld", ngname, ngsize+1);
X#if defined(USG) || defined(MG1)
X	/*
X	 * U G L Y   K L U D G E
X	 * This utter piece of tripe is the only way I know of to get
X	 * around the fact that ATT BROKE standard IO in System 5.2.
X	 * Basically, you can't open a file for "r+" and then try and
X	 * write to it. This works on all "real" USGUnix systems, It will
X	 * probably break on some obscure look alike that doesnt use the
X	 * real ATT stdio.h
X	 * Don't blame me, blame ATT. stdio should have already done the
X	 * following line for us, but it doesn't
X	 * also broken in WCW MG-1 42nix 2.0
X	 */
X	 actfp->_flag |= _IOWRT;
X#endif /* USG */
X	(void) fflush(actfp);
X	if (ferror(actfp))
X		xerror("Active file write failed");
X	unlock();
X	if (firstbufname[0] == '\0')
X		(void) strcpy(firstbufname, bfr);
X	(void) sprintf(bfr, "%s/%ld ", ngname, ngsize+1);
X	addhist(bfr);
X	return ngsize+1;
X}
X
X/*
X *	Localize for each newsgroup and broadcast.
X */
Xinsert()
X{
X	register char *ptr;
X	register FILE *tfp;
X	register int c;
X	struct srec srec;	/* struct for sys file lookup	*/
X	struct tm *tm, *gmtime();
X	int is_invalid = FALSE;
X	int exitcode = 0;
X	long now;
X#ifdef DOXREFS
X	register char *nextref = header.xref;
X#endif /* DOXREFS */
X
X	/* Clean up Newsgroups: line */
X	if (!is_ctl && mode != CREATENG)
X		is_invalid = ngfcheck(mode == PROC);
X
X	(void) time(&now);
X	tm = gmtime(&now);
X	if (header.expdate[0])
X		addhist(" ");
X#ifdef USG
X	sprintf(bfr,"%2.2d/%2.2d/%d %2.2d:%2.2d\t",
X#else /* !USG */
X	sprintf(bfr,"%02d/%02d/%d %02d:%02d\t",
X#endif /* !USG */
X		tm->tm_mon+1, tm->tm_mday, tm->tm_year,tm->tm_hour, tm->tm_min);
X	addhist(bfr);
X	log("%s %s ng %s subj '%s' from %s", spool_news != DONT_SPOOL
X		? "queued" : (mode==PROC ? "received" : "posted"),
X		header.ident, header.nbuf, header.title, header.from);
X
X	/* Write article to temp file. */
X	tfp = xfopen(mktemp(ARTICLE), "w");
X
X	if (is_invalid) {
X		logerr("No valid newsgroups found, moved to junk");
X		if (localize("junk"))
X			savehist(histline);
X		exitcode = 1;
X		goto writeout;
X	}
X
X#ifdef ZAPNOTES
X	if (strncmp(header.title, "Re: Orphaned Response", 21) == 0) {
X		logerr("Orphaned Response, moved to junk");
X		if (localize("junk"))
X			savehist(histline);
X		exitcode = 1;
X		goto writeout;
X	}
X#endif	/* ZAPNOTES */
X
X	if (time((time_t *)0) > (cgtdate(header.subdate) + HISTEXP) ){
X		logerr("Article too old, moved to junk");
X		if (localize("junk"))
X			savehist(histline);
X		exitcode = 1;
X		goto writeout;
X	}
X
X	if (is_mod[0] != '\0' 	/* one of the groups is moderated */
X		&& header.approved[0] == '\0') { /* and unapproved */
X		struct hbuf mhdr;
X		FILE *mfd, *mhopen();
X		register char *p;
X		char modadd[BUFLEN], *replyname();
X#ifdef DONTFOWARD
X		if(mode == PROC) {
X			logerr("Unapproved article in moderated group %s",
X				is_mod);
X			if (localize("junk"))
X				savehist(histline);
X			goto writeout;
X		}
X#endif /* DONTFORWARD */
X		fprintf(stderr,"%s is moderated and may not be posted to",
X			is_mod);
X		fprintf(stderr," directly.\nYour article is being mailed to");
X		fprintf(stderr," the moderator who will post it for you.\n");
X		/* Let's find a path to the backbone */
X		sprintf(bfr, "%s/mailpaths", LIB);
X		mfd = xfopen(bfr, "r");
X		do {
X			if (fscanf(mfd, "%s %s", bfr, modadd) != 2)
X				xerror("Can't find backbone in %s/mailpaths",
X					LIB);
X		} while (strcmp(bfr, "backbone") != 0 && !ngmatch(is_mod, bfr));
X		(void) fclose(mfd);
X		/* fake a header for mailhdr */
X		mhdr.from[0] = '\0';
X		mhdr.replyto[0] = '\0';
X		p = is_mod;
X		while (*++p)
X			if (*p == '.')
X				*p = '-';
X		sprintf(mhdr.path, modadd, is_mod);
X		mfd = mhopen(&mhdr);
X		if (mfd == NULL)
X			xerror("Can't send mail to %s", mhdr.path);
X		fprintf(mfd, "To: %s\n", replyname(mhdr.path));
X		lhwrite(&header, mfd);
X		putc('\n', mfd);
X		while ((c = getc(infp)) != EOF)
X			putc(c, mfd);
X		mclose(mfd);
X		log("Article mailed to %s", mhdr.path);
X		xxit(0);
X	}
X
X	if (mode != PROC && spool_news != DONT_SPOOL)  {
X		if (spool_news != EXPIRE_RUNNING
X			&& ngmatch(header.nbuf,"to.all.ctl"))
X				spool_news = DONT_SPOOL;
X		if (spool_news != DONT_SPOOL) {
X			fprintf(stderr,
X			"Your article has been spooled for later processing.\n");
X			dospool("#! inews -S -h", TRUE);
X			/* NOT REACHED */
X		}
X	}
X
X	if (is_ctl) {
X		exitcode = control(&header);
X		if (localize("control") && exitcode != 0)
X			savehist(histline);
X	} else {
X		if (s_find(&srec, LOCALPATHSYSNAME) == FALSE) {
X			logerr("Cannot find my name '%s' in %s",
X				LOCALPATHSYSNAME, SUBFILE);
X			srec = dummy_srec;
X		}
X#ifdef DOXREFS
X		(void) strncpy(nextref, PATHSYSNAME, BUFLEN);
X#endif /* DOXREFS */
X		for (ptr = nbuf; *ptr;) {
X			if (ngmatch(ptr,srec.s_nbuf) || index(ptr,'.') == NULL){
X#ifdef DOXREFS
X				while (*nextref++)
X					;
X				(void) sprintf(--nextref, " %s:%ld", ptr, localize(ptr));
X#else /* !DOXREFS */
X				(void) localize(ptr);
X#endif /* !DOXREFS */
X			}
X			while (*ptr++)
X				;
X		}
X		if (firstbufname[0] == '\0') {
X			logerr("Newsgroups in active, but not sys");
X			(void) localize("junk");
X		}
X	}
X#ifdef DOXREFS
X	if (index(header.nbuf, NGDELIM) == NULL)
X		header.xref[0] = '\0';
X#endif /* DOXREFS */
X
Xwriteout:
X	/* Part 1 of kludge to get around article truncation problem */
X	if ( (c=getc(infp)) != EOF) {
X		ungetc(c, infp);
X		if (c == ' ' || c == '\t') {
X			header.intnumlines++;
X			(void) sprintf(header.numlines, "%d",
X				header.intnumlines);
X		}
X	}
X	/* End of part 1 */
X	if (header.expdate[0] != '\0' && mode != PROC) {
X		/* Make sure it's fully qualified */
X		long t = cgtdate(header.expdate);
X		strcpy(header.expdate, arpadate(&t));
X	}
X
X	lhwrite(&header, tfp);
X	if ((c = getc(infp)) != EOF) {
X		/* Part 2 of kludge to get around article truncation problem */
X		if (c == ' ' || c == '\t' )
X			putc('\n', tfp);
X		/* End of part 2 */
X		ungetc(c, infp);
X		while (fgets(bfr, BUFLEN, infp) != NULL)
X			fputs(bfr, tfp);
X		if (bfr[strlen(bfr)-1] != '\n')
X			putc('\n',tfp);
X	}
X	if (ferror(tfp))
X		xerror("Write failed for temp file");
X	(void) fclose(tfp);
X	(void) fclose(infp);
X	if(exitcode == 0) {
X		/* article has passed all the checks, so work in background */
X		if (mode != PROC) {
X			int pid;
X			if ((pid=fork()) < 0)
X				xerror("Can't fork");
X			else if (pid > 0)
X				_exit(0);
X		}
X#ifdef SIGTTOU
X		(void) signal(SIGTTOU, SIG_IGN);
X#endif /* SIGTTOU */
X		savehist(histline);
X		broadcast(mode==PROC);
X	}
X	xxit((mode == PROC && filename[0] == '\0') ? 0 :
X		(exitcode < 0 ? 0 : exitcode));
X}
X
Xinput()
X{
X	register char *cp;
X	register int c;
X	register int empty = TRUE;
X	FILE *tmpfp;
X	int consec_newlines = 0;
X	int linecount = 0;
X	int linserted = 0;
X
X	tmpfp = xfopen(mktemp(INFILE), "w");
X	while (!SigTrap && fgets(bfr, BUFLEN, infp) != NULL) {
X 		if (mode == PROC) {	/* zap trailing empty lines */
X#ifdef ZAPNOTES
X			if (empty && bfr[0] == '#' && bfr[2] == ':'
X				&& header.nf_id[0] == '\0'
X				&& header.nf_from[0] == '\0' ) {
X				(void) strcpy(header.nf_id, bfr);
X				(void) nstrip(header.nf_id);
X				(void) fgets(bfr, BUFLEN, infp);
X				(void) strcpy(header.nf_from, bfr);
X				(void) nstrip(header.nf_from);
X				(void) fgets(bfr, BUFLEN, infp);
X
X				if (header.numlines[0]) {
X					header.intnumlines -= 2;
X					(void) sprintf(header.numlines, "%d", header.intnumlines);
X				}
X
X				/* Strip trailing " - (nf)" */
X				if ((cp = rindex(header.title, '-')) != NULL
X				    && !strcmp(--cp, " - (nf)"))
X					*cp = '\0';
X				log("Stripped notes header on %s", header.ident);
X				continue;
X			}
X#endif /* ZAPNOTES */
X 			if (bfr[0] == '\n' ||
X				/* Bandage for older versions of inews */
X				bfr[1] == '\n' && !isascii(bfr[0])) {
X 				consec_newlines++;	/* count it, in case */
X 				continue;		/* but don't write it*/
X 			}
X 			/* foo! a non-empty line. write out all saved lines. */
X 			while (consec_newlines > 0) {
X				putc('\n', tmpfp);
X				consec_newlines--;
X				linecount++;
X			}
X 		}
X		if (mode != PROC && tty && strcmp(bfr, ".\n") == 0)
X			break;
X		for (cp = bfr; c = toascii(*cp); cp++) {
X			if (isprint(c) || isspace(c) || c == '\b')
X				putc(c, tmpfp);
X			if (c == '\n')
X				linecount++;
X		}
X		if (bfr[0] == '>')
X			linserted++;
X		if (bfr[0] == '<') /* kludge to allow diff's to be posted */
X			linserted--;
X		empty = FALSE;
X	}
X	if (*filename)
X		(void) fclose(infp);
X	if (mode != PROC &&
X		linecount > LNCNT && linserted > (linecount-linserted))
X		error("Article rejected: %s included more text than new text",
X			username);
X
X	if (mode != PROC && !is_ctl && header.sender[0] == '\0' && !Sflag) {
X		int siglines = 0;
X		char sbuf[BUFLEN];
X		(void) sprintf(bfr, "%s/%s", userhome, ".signature");
X		if (access(bfr, 4) == 0) {
X			if ((infp = fopen(bfr, "r")) == NULL) {
X				(void) fprintf(stderr,
X    "inews: \"%s\" left off (must be readable by \"inews\" owner)\n", bfr);
X				goto finish;
X			}
X
X			while (fgets(sbuf, sizeof sbuf, infp) != NULL)
X				if (++siglines > 4)
X					break;
X			if (siglines > 4)
X				fprintf(stderr,".signature not included (> 4 lines)\n");
X			else {
X				rewind(infp);
X				fprintf(tmpfp, "-- \n");	/* To separate */
X				linecount++;
X				while ((c = getc(infp)) != EOF) {
X					putc(c, tmpfp);
X					if (c == '\n')
X						linecount++;
X				}
X			}
X			(void) fclose(infp);
X		}
X	}
X
Xfinish:
X	if (ferror(tmpfp))
X		xerror("write failed to temp file");
X	(void) fclose(tmpfp);
X	if (SigTrap) {
X		if (tty)
X			fprintf(stderr, "Interrupt\n");
X		if (tty && !empty)
X			fwait(fsubr(newssave, (char *) NULL, (char *) NULL));
X		if (!tty)
X			log("Blown away by an interrupt %d", SigTrap);
X		xxit(1);
X	}
X	if (tty)
X		fprintf(stderr, "EOT\n");
X	fflush(stdout);
X	infp = fopen(INFILE, "r");
X	if (header.numlines[0]) {
X		/*
X		 * Check line count if there's already one attached to
X		 * the article.  Could make this a fatal error -
X		 * throwing it away if it got chopped, in hopes that
X		 * another copy will come in later with a correct
X		 * line count.  But that seems a bit much for now.
X		 */
X		if (linecount != header.intnumlines) {
X			if (linecount == 0)
X				error("%s rejected. linecount expected %d, got 0", header.ident, header.intnumlines);
X			if (linecount > header.intnumlines ||
X			    linecount+consec_newlines < header.intnumlines)
X				log("linecount expected %d, got %d", header.intnumlines, linecount+consec_newlines);
X		}
X		/* adjust count for blank lines we stripped off */
X		if (consec_newlines) {
X			header.intnumlines -= consec_newlines;
X			if (header.intnumlines < 0 )
X				header.intnumlines = 0; /* paranoia */
X			(void) sprintf(header.numlines, "%d", header.intnumlines);
X		}
X
X	} else {
X		/* Attach a line count to the article. */
X		header.intnumlines = linecount;
X		(void) sprintf(header.numlines, "%d", linecount);
X	}
X}
X
X/*
X * Make the directory for a new newsgroup.  ngname should be the
X * full pathname of the directory.  Do the other stuff too.
X * The various games with setuid and chown are to try to make sure
X * the directory is owned by NEWSUSR and NEWSGRP, which is tough to
X * do if you aren't root.  This will work on a UCB system (which allows
X * setuid(geteuid()) or a USG system (which allows you to give away files
X * you own with chown), otherwise you have to change your kernel to allow
X * one of these things or run with your dirs 777 so that it doesn't matter
X * who owns them.
X */
Xmknewsg(fulldir, ngname)
Xchar	*fulldir;
Xchar	*ngname;
X{
X	if (ngname == NULL || !isalpha(ngname[0]))
X		xerror("Tried to make illegal newsgroup %s", ngname);
X
X	/* Create the directory */
X	mkparents(fulldir);
X
X	if (mkdir(fulldir, 0777) < 0)
X		xerror("Cannot mkdir %s: %s", fulldir, errmsg(errno));
X
X	log("make newsgroup %s in dir %s", ngname, fulldir);
X}
X
X/*
X * If any parent directories of this dir don't exist, create them.
X */
Xmkparents(dname)
Xchar *dname;
X{
X	char buf[200];
X	register char *p;
X
X	(void) strcpy(buf, dname);
X	p = rindex(buf, '/');
X	if (p)
X		*p = '\0';
X	if (exists(buf))
X		return;
X	mkparents(buf);
X	if (mkdir(buf, 0777) < 0)
X		xerror("Can not mkdir %s: %s", buf, errmsg(errno));
X}
X
Xcancel()
X{
X	register FILE *fp;
X
X	log("cancel article %s", filename);
X	fp = fopen(filename, "r");
X	if (fp == NULL) {
X		log("article %s not found", filename);
X		return;
X	}
X	if (hread(&header, fp, TRUE) == NULL)
X		error("Article is garbled.");
X	(void) fclose(fp);
X	(void) unlink(filename);
X}
X
Xdounspool()
X{
X	register DIR	*dirp;
X	register struct direct *dir;
X	register int foundsome;
X	int pid, status, ret;
X	char spbuf[BUFLEN];
X#ifdef LOCKF
X	FILE* LockFd;
X#endif /* LOCKF */
X#ifdef VMS
X	sprintf(spbuf, "%s/+rnews", SPOOL);
X#else /* !VMS */
X	sprintf(spbuf, "%s/.rnews", SPOOL);
X#endif /* !VMS */
X
X	if (chdir(spbuf) < 0)
X		xerror("chdir(%s):%s", spbuf, errmsg(errno));
X
X	dirp = opendir(".");
X	if (dirp == NULL)	/* Boy are things screwed up */
X		xerror("opendir can't open .:%s", errmsg(errno));
X#ifdef	LOCKF
X	LockFd = xfopen(SEQFILE, "r+w");
X	if (lockf(fileno(LockFd), F_TLOCK, 0) < 0) {
X		if (errno != EAGAIN && errno != EACCES)
X#else	/* !LOCKF */
X#ifdef BSD4_2
X	if (flock(dirp->dd_fd, LOCK_EX|LOCK_NB) < 0) {
X		if (errno != EWOULDBLOCK)
X#else	/* V7 */
X	strcat(spbuf, ".lock");
X	sprintf(bfr, "%s.tmp", spbuf);
X	(void) close(creat(bfr, 0666));
X	ret = LINK(bfr, spbuf);
X	status = errno;
X	(void) UNLINK(bfr);
X	errno = status;
X	if (ret < 0) {
X		if (errno != EEXIST)
X#endif /* V7 */
X#endif	/* !LOCKF */
X			xerror("Can't lock %s: %s", spbuf, errmsg(errno));
X		xxit(3); /* another rnews -U is running */
X	}
X
X	do {
X		foundsome = 0;
X
X		while ((dir=readdir(dirp)) != NULL) {
X			if (dir->d_name[0] == '.')
X				continue;
X			if ((pid=vfork()) == -1)
X				xerror("Can't fork: %s", errmsg(errno));
X			if (pid == 0) {
X#ifdef IHCC
X				char bufr[BUFSIZ];
X				sprintf(bufr, "%s/%s", logdir(HOME), RNEWS);
X				execl(bufr, "rnews", "-S", "-p", dir->d_name,
X					(char *) NULL);
X#else /* !IHCC */
X				execl(RNEWS, "rnews", "-S", "-p", dir->d_name,
X					(char *) NULL);
X#endif /* !IHCC */
X				_exit(1);
X			}
X			
X			while ((ret=wait(&status)) != pid && ret != -1)
X				/* continue */;
X
X			if (((status>>8)&0177) == 42) {
X				/* expire has started up, shutdown rnews -U */
X				break;
X			}
X
X			if (status != 0) {
X				sprintf(bfr, "../%s", dir->d_name);
X				(void) LINK(dir->d_name, bfr);
X				logerr("rnews failed, status %d. Batch saved in %s/%s",
X					status, SPOOL, dir->d_name);
X			}
X			(void) unlink(dir->d_name);
X			foundsome++;
X		}
X		rewinddir(dirp);
X	} while (foundsome); /* keep rereading the directory until it's empty */
X	(void) UNLINK(spbuf);
X
X	xxit(0);
X}
END_OF_FILE
if test 34390 -ne `wc -c <'inews.c'`; then
    echo shar: \"'inews.c'\" unpacked with wrong size!
fi
# end of 'inews.c'
fi
echo shar: End of shell archive.
exit 0
-- 
		"Zeta Microcomputer Software"
ACSnet:    nick@ultima.cs.uts.oz
UUCP:      ...!uunet!munnari!ultima.cs.uts.oz!nick
Fidonet:   Nick Andrew on 3:713/602 (Zeta)

nick@ultima.cs.uts.oz (Nick Andrew) (12/07/89)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  ifuncs.c expire.c
# Wrapped by nick@nswitgould on Thu Dec  7 22:39:55 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'ifuncs.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ifuncs.c'\"
else
echo shar: Extracting \"'ifuncs.c'\" \(32337 characters\)
sed "s/^X//" >'ifuncs.c' <<'END_OF_FILE'
X/*
X * This software is Copyright (c) 1986 by Rick Adams.
X *
X * Permission is hereby granted to copy, reproduce, redistribute or
X * otherwise use this software as long as: there is no monetary
X * profit gained specifically from the use or reproduction or this
X * software, it is not sold, rented, traded or otherwise marketed, and
X * this copyright notice is included prominently in any copy
X * made.
X *
X * The author make no claims as to the fitness or correctness of
X * this software for any use whatsoever, and it is provided as is. 
X * Any use of this software is at the user's own risk.
X *
X * ifuncs - functions used by inews.
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)ifuncs.c	2.65	4/10/87";
X#endif /* SCCSID */
X
X#include "iparams.h"
X
X/*LINTLIBRARY*/
X
X/*
X * Transmit this article to all interested systems.
X */
X
X#ifdef u370
Xstatic struct srec srec;
X#endif /* u370 */
X
Xstatic struct hbuf h, hh;
X
X#ifdef MULTICAST
X#define	MAXMCAST	20
X#define	MAXMCS		10
X
Xstruct multicast {
X	char mc_name[SBUFLEN];		/* "multi-cast" name */
X	short mc_syscnt;
X	char mc_tosys[MAXMCAST][SBUFLEN];
X} mcast[MAXMCS];
X
Xstatic int mccount;
X#endif /* MULTICAST */
X
Xlong lseek();
X
X#ifndef DBM
Xchar *histfile();
X#endif /* !DBM */
X
X#ifdef VMS
X/*
X * For VMS/Eunice there are no links: article was moved to firstbufname
X * before broadcast is reached.  So we read it from there.
X */
Xextern char firstbufname[];
X#endif
X
Xbroadcast(is_rnews)
Xint is_rnews;
X{
X	register char *hptr;
X	register char *sptr;
X	register FILE *fp;
X#ifndef u370
X	struct srec srec;
X#endif
X	char sentbuf[LBUFLEN];
X	int nsent = 0;
X	char *sentsys;
X#ifdef GENERICPATH
X	int len;
X#endif /* GENERICPATH */
X
X	/* h is a local copy of the header we can scribble on */
X#ifdef VMS
X	fp = xfopen (firstbufname, "r");
X#else
X	fp = xfopen(ARTICLE, "r");
X#endif
X	if (hread(&h, fp, TRUE) == NULL)
X		xerror("Cannot reread article");
X	(void) fclose(fp);
X
X	(void) strcpy(sentbuf, h.ident);
X	(void) strcat(sentbuf, " sent to ");
X	sentsys = index(sentbuf, '\0');
X	nsent = 0;
X	/* break path into list of systems. */
X	hptr = h.path;
X#ifdef GENERICPATH
X	if (!is_rnews && 
X		strncmp(PATHSYSNAME, h.path, (len = strlen(PATHSYSNAME))) == 0
X		&& index(NETCHRS, h.path[len]))
X		(void) strcpy(h.path, &(h.path[len+1]));
X#endif /* GENERICPATH */
X	sptr = hptr = h.path;
X	while ((hptr=strpbrk(hptr, NETCHRS)) != NULL) {
X		*hptr++ = '\0';
X		sptr = hptr;
X	}
X	*sptr = '\0';
X
X#ifdef MULTICAST
X	mccount = 0;
X#endif /* MULTICAST */
X
X	/* loop once per system. */
X	s_openr();
X	while (s_read(&srec)) {
X		char *dist = h.distribution;
X		if (strncmp(srec.s_name, LOCALPATHSYSNAME, SNLN) == 0)
X			continue;
X		if (sptr = srec.s_nosend) {
X			while (*sptr) {
X				while (*sptr && *sptr != ',')
X					sptr++;
X				if (*sptr == ',')
X					*sptr++ = '\0';
X			}
X			*++sptr = '\0';
X		}
X		hptr = h.path;
X		while (*hptr != '\0') {
X			if (strncmp(srec.s_name, hptr, SNLN) == 0)
X				goto contin;
X			if (sptr = srec.s_nosend) {
X				while (*sptr != '\0') {
X					if (strncmp(sptr, hptr, SNLN) == 0)
X						goto contin;
X					while (*sptr++)
X						;
X				}
X			}
X			while (*hptr++ != '\0')
X				;
X		}
X		if (!ngmatch(h.nbuf, srec.s_nbuf))
X			continue;
X		if (*dist == '\0')
X			dist = "world";
X		if (!ngmatch(dist, srec.s_nbuf) && !ngmatch(srec.s_nbuf, dist))
X			    continue;
X
X		if (nsent) {
X			hptr = sentsys;
X			while ((sptr = index(hptr, ',')) != NULL) {
X				*sptr = '\0';
X				if (strcmp(hptr, srec.s_name) == 0) {
X					*sptr = ',';
X					goto contin;
X				}
X				*sptr++ = ',';
X				for (hptr = sptr; isspace(*hptr); hptr++)
X					;
X			}
X			if (strcmp(hptr, srec.s_name) == 0)
X				continue;
X		}
X		/* now we've found a system to send this article to */
X#ifdef MULTICAST
X		if (index(srec.s_flags, 'M')) {
X			/* do a "multi-cast" transmit */
X			register struct multicast *m;
X
X			if (strlen(srec.s_name) >= SBUFLEN ||
X			    strlen(srec.s_xmit) >= SBUFLEN)
X				xerror("system name too long for multicast");
X			for (m = mcast; m < &mcast[mccount]; m++)
X				if (strcmp(srec.s_xmit, m->mc_name) == 0)
X					break;
X			if (m >= &mcast[MAXMCS])
X				xerror("Too many multicasts");
X			if (m == &mcast[mccount]) {
X				mccount++;
X				m->mc_syscnt = 0;
X				strcpy(m->mc_name, srec.s_xmit);
X			}
X			if (m->mc_syscnt >= MAXMCAST)
X				xerror("Too many systems for multicast");
X			strcpy(m->mc_tosys[m->mc_syscnt++], srec.s_name);
X		} else {
X			register struct multicast *m;
X			register char **yptr;
X			char *sysptrs[MAXMCAST];
X			int mc;
X
X			mc = 0;
X			for (m = mcast; m < &mcast[mccount]; m++)
X				if (strcmp(m->mc_name, srec.s_name) == 0) {
X					yptr = sysptrs;
X					while (mc < m->mc_syscnt)
X						*yptr++ = m->mc_tosys[mc++];
X					break;
X				}
X#ifdef VMS
X			if (!transmit(&srec, xfopen(firstbufname,"r"),
X#else /* !VMS */
X			if (!transmit(&srec, xfopen(ARTICLE,"r"),
X#endif /* !VMS */
X				(strncmp(h.nbuf, "to.", 3) != 0),
X				sysptrs, mc))
X				continue;
X		}
X#else /* !MULTICAST */
X#ifdef VMS
X		if (!transmit(&srec, xfopen(firstbufname, "r"),
X#else /* !VMS */
X		if (!transmit(&srec, xfopen(ARTICLE, "r"),
X#endif /* !VMS */
X			(strncmp(h.nbuf, "to.", 3) != 0),
X			(char **) NULL, FALSE))
X				continue;
X#endif /* !MULTICAST */
X		if (nsent)
X			(void) strcat(sentbuf, ", ");
X		(void) strcat(sentbuf, srec.s_name);
X		nsent++;
X	contin:;
X	}
X	if (nsent)
X		log(sentbuf);
X	s_close();
X}
X
X/*
X * Transmit file to system.
X */
X#define PROC 0004
X#ifndef MULTICAST
X/* ARGSUSED */
X#endif /* !MULTICAST */
Xtransmit(sp, ifp, maynotify, sysnames, mc)
Xregister struct srec *sp;
Xregister FILE *ifp;
Xint maynotify;
Xchar **sysnames;
Xint mc;
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 = ((ptr = index(sp->s_flags, 'L')) != NULL);
X/* H:	interpolate history line into command, use existing file */
X	int history = (index(sp->s_flags, 'H') != NULL);
X/* m:	moderated: only send if group is moderated */
X	int sendifmoderated = (index(sp->s_flags, 'm') != NULL);
X/* u:	unmoderated: only send if group is unmoderated */
X	int sendifunmoderated = (index(sp->s_flags, 'u') != NULL);
X/* M:	multi-cast: this is taken care of above, but don't reuse flag */
X#ifdef MULTICAST
X/* O:	multi-cast only, don't send article if not multicast hosts */
X	int multisend = (index(sp->s_flags, 'O') != NULL);
X#endif /* MULTICAST */
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/* I:	append messageid to file. implies F flag */
X	int appmsgid = maynotify && (index(sp->s_flags, 'I') != NULL);
X
X	if (notify)
X		appfile = appmsgid = FALSE;
X
X	if (local && mode == PROC) {
X		local = 0;
X		while (isdigit(*++ptr))
X			local = local * 10 + *ptr - '0';
X		for (ptr = h.path; *ptr != '\0' && local >= 0; local--)
X			while (*ptr++ != '\0')
X				;
X		if (local < 0) {
X			(void) fclose(ifp);
X			return FALSE;
X		}
X	}
X
X	/*
X	** Do not transmit to system specified in -x flag.
X	*/
X	if (not_here[0] && strcmp(not_here, sp->s_name) == 0) {
X		(void) fclose(ifp);
X		return FALSE;
X	}
X
X#ifdef DEBUG
X	printf("Transmitting to '%s'\n", sp->s_name);
X#endif /* DEBUG */
X
X#ifdef MULTICAST
X	if (multisend && mc == 0) {
X		(void) fclose(ifp);
X		return FALSE;
X	}
X#endif /* MULTICAST */
X
X	if ((sendifmoderated && is_mod[0] == '\0') ||
X	    (sendifunmoderated && is_mod[0] != '\0')) {
X		fclose(ifp);
X		return FALSE;
X	}
X
X	if (appmsgid || (!appfile && !useexist && !history)) {
X		if (!hread(&hh, ifp, TRUE)) {
X			logerr("Bad header, not transmitting %s re %s to %s",
X				hh.ident, hh.title, sp->s_name);
X			(void) 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			(void) fclose(ifp);
X			return FALSE;
X		}
X		(void) sprintf(TRANS, "%s/trXXXXXX", SPOOL);
X	}
X
X	if (notify) {
X		char oldid[50];
X		(void) sprintf(hh.title, "ihave %s %s", hh.ident, PATHSYSNAME);
X		(void) strcpy(hh.ctlmsg, hh.title);
X		(void) strcpy(hh.numlines, "0");
X		(void) sprintf(hh.nbuf, "to.%s.ctl", sp->s_name);
X		(void) 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 || appmsgid) {
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		if (sp->s_xmit[0] == '\0')
X			sprintf(sp->s_xmit, "%s/%s%s", BATCHDIR, sp->s_name,
X				appmsgid ? ".ihave" : "");
X#ifdef IHCC
X		(void) sprintf(TRANS, "%s/%s/%s", logdir(HOME), BATCHDIR, sp->s_xmit);
X		ofp = fopen(TRANS, "a");
X#else /* !IHCC */
X		ofp = fopen(sp->s_xmit, "a");
X#endif /* !IHCC */
X		if (ofp == NULL)
X			xerror("Cannot append to %s", sp->s_xmit);
X		fprintf(ofp, "%s", appmsgid ? hh.ident : firstbufname);
X#ifdef MULTICAST
X		while (--mc >= 0)
X			fprintf(ofp, " %s", *sysnames++);
X#endif /* !MULTICAST */
X		putc('\n', ofp);
X		(void) fclose(ofp);
X		(void) fclose(ifp);
X		return TRUE;
X	}
X	else if (useexist) {
X		if (firstbufname[0] == '\0')
X			xerror("No file name to xmit from");
X		if (*sp->s_xmit == '\0')
X#ifdef UXMIT
X			(void) sprintf(bfr, UXMIT, sp->s_name, firstbufname);
X#else
X			xerror("UXMIT not defined for U flag");
X#endif
X		else
X#ifdef MULTICAST
X			makeargs(bfr, sp->s_xmit, firstbufname, sysnames, mc);
X#else
X			(void) sprintf(bfr, sp->s_xmit, firstbufname);
X#endif
X		(void) fclose(ifp);
X	} else if (history) {
X		extern char histline[];
X
X		if (*sp->s_xmit == '\0')
X			xerror("no xmit command with H flag");
X#ifdef MULTICAST
X		makeargs(bfr, sp->s_xmit, histline, sysnames, mc);
X#else
X		(void) sprintf(bfr, sp->s_xmit, histline);
X#endif
X	} else {
X		ofp = xfopen(mktemp(TRANS), "w");
X		if (afmt) {
X#ifdef OLD
X			fprintf(ofp, "A%s\n%s\n%s!%s\n%s\n%s\n", oident(hh.ident), hh.nbuf, PATHSYSNAME,
X				hh.path, hh.subdate, hh.title);
X#else /* !OLD */
X			logerr("Must have OLD defined to use A flag for xmit");
X			return FALSE;
X#endif /* !OLD */
X		} else
X			hwrite(&hh, ofp);
X		if (!notify)
X			while ((c = getc(ifp)) != EOF)
X				putc(c, ofp);
X		if (ferror(ofp))
X			xerror("write failed on transmit");
X		(void) fclose(ifp);
X		(void) fclose(ofp);
X		if (*sp->s_xmit == '\0')
X			(void) sprintf(bfr, DFTXMIT, sp->s_name, TRANS);
X		else
X#ifdef MULTICAST
X			makeargs(bfr, sp->s_xmit, TRANS, sysnames, mc);
X#else /* !MULTICAST */
X			(void) sprintf(bfr, sp->s_xmit, TRANS);
X#endif /* !MULTICAST */
X	}
X
X	/* At this point, the command to be executed is in bfr. */
X	if (noshell) {
X		if (pid = vfork())
X			fwait(pid);
X		else {
X			(void) close(0);
X			(void) open(TRANS, 0);
X			ptr = bfr;
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			(void) setgid(gid);
X			(void) setuid(uid);
X			execvp(argv[0], argv);
X			xerror("Can't execv %s", argv[0]);
X		}
X	} else {
X		if (!history && sp->s_xmit[0] && !index(bfr, '<')) {
X			char newcmd[LBUFLEN];
X
X			(void) sprintf(newcmd, "(%s) <%s", bfr,
X			    useexist ? firstbufname : TRANS);
X			system(newcmd);
X		} else
X			system(bfr);
X	}
X	if (!appfile && !useexist && !history)
X		(void) unlink(TRANS);
X	(void) fclose(ifp);
X	return TRUE;
X}
X
X#ifdef MULTICAST
Xmakeargs(buf, cmd, arg2, sysargs, sac)
Xchar *buf;
Xchar *cmd;
Xchar *arg2;
Xregister char **sysargs;
Xint sac;
X{
X	register char *p = cmd;
X	register char *q;
X	register ac = 0;
X	register char *b = buf;
X
X	q = p;
X	do {
X		if (q = index(q, ' '))
X			*q = '\0';
X		if (index(p, '%')) {
X			switch (++ac) {
X			case 1:
X				while (--sac >= 0) {
X					sprintf(b, p, *sysargs++);
X					b = index(b, '\0');
X				}
X				break;
X			case 2:
X				sprintf(b, p, arg2);
X				b = index(b, '\0');
X				break;
X			default:
X				if (q)
X					*q = ' ';
X				xerror("badly formed command: %s", cmd);
X			}
X		} else {
X			strcpy(b, p);
X			b = index(b, '\0');
X		}
X		if (q) {
X			*q = ' ';
X			p = q;
X			while (isspace(*q))
X				q++;
X		}
X	} while (q != NULL);
X}
X#endif /* MULTICAST */
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	register char *p;
X#endif /* !DBM */
X	char lcident[BUFLEN];
X	extern char histline[];
X
X#ifdef DEBUG
X	fprintf(stderr,"history(%s)\n", hp->ident);
X#endif /* DEBUG */
X	/*
X	 * Make the article ID case insensitive.
X	 */
X	(void) strcpy(lcident, hp->ident);
X	lcase(lcident);
X
X	idlock(lcident);
X#ifdef DBM
X	initdbm(ARTFILE);
X	lhs.dptr = lcident;
X	lhs.dsize = strlen(lhs.dptr) + 1;
X	rhs = fetch(lhs);
X	if (rhs.dptr) {
X		idunlock();
X		return(TRUE);
X	}
X#else /* !DBM */
X	hfp = xfopen(histfile(lcident), "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		lcase(bfr);
X
X		if (strcmp(bfr, lcident) == 0) {
X			(void) fclose(hfp);
X			idunlock();
X#ifdef DEBUG
X			fprintf(stderr,"history returns true\n");
X#endif /* DEBUG */
X			return TRUE;
X		}
X	}
X	(void) fclose(hfp);
X#endif /* !DBM */
X	histline[0] = '\0';
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[PATHLEN];
X
Xaddhist(msg)
Xchar *msg;
X{
X	(void) strcat(histline, msg);
X}
X
Xsavehist(hline)
Xchar *hline;
X{
X	register FILE *hfp;
X	register char *p;
X#ifdef DBM
X	long fpos;
X#endif /* !DBM */
X
X#ifndef DBM
X	if (strcmp((p = histfile(hline)), ARTFILE) != 0) {
X	/* If the history subfile is accessible */
X		if ((hfp = xfopen(p, "a")) != NULL ) { /* If we can append */
X			fprintf(hfp, "%s\n", hline);   /* Append */
X			(void) fclose(hfp);
X		} else
X			logerr("Unable to append to %s: %s", p, errmsg(errno));
X	} else
X#endif /* !DBM */
X	{
X	hfp = xfopen(ARTFILE, "a");
X	(void) fseek(hfp, 0L, 2); /* Unisoft 5.1 doesn't seek to EOF on 'a' */
X#ifdef DBM
X	fpos = ftell(hfp);
X#endif /* !DBM */
X	fprintf(hfp, "%s\n", hline);
X	(void) fclose(hfp);
X	}
X#ifdef DBM
X	{
X	datum lhs, rhs;
X	/* We assume that history has already been called, calling dbminit. */
X	p = index(hline, '\t');
X	if (p)
X		*p = 0;
X	lcase(hline);
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	}
X#endif /* DBM */
X	idunlock();
X}
X
X/*
X * Save partial news.
X */
X/* ARGSUSED */
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	if (fd == NULL)
X		fromfd = xfopen(INFILE, "r");
X	else
X		fromfd = fd;
X	(void) umask(savmask);
X	(void) setgid(gid);
X	(void) setuid(uid);
X
X	(void) sprintf(sfname, "%s/%s", userhome, PARTIAL);
X	if ((tofd = fopen(sfname, "a")) == NULL)
X		xerror("Cannot save partial news in %s", sfname);
X	(void) time(&tim);
X	fprintf(tofd, "----- News saved at %s\n", arpadate(&tim));
X	while ((c = getc(fromfd)) != EOF)
X		putc(c, tofd);
X	(void) fclose(fromfd);
X	(void) fclose(tofd);
X	printf("News saved in %s\n", sfname);
X	xxit(1);
X}
X
X/*
X * Handle dates in header.
X */
X
Xdates(hp)
Xstruct hbuf *hp;
X{
X	time_t edt;
X
X	if (*hp->subdate) {
X		if (cgtdate(hp->subdate) < 0) {
X			error("Cannot parse submittal date '%s'", hp->subdate);
X		}
X	} else {
X		(void) time(&edt);
X		(void) strcpy(hp->subdate, arpadate(&edt));
X	}
X}
X
X#define LOCKSIZE	128
Xchar lockname[LOCKSIZE];
X
Xidlock(str)
Xchar *str;
X{
X	register int i;
X	register char *cp, *scp;
X	char tempname[LOCKSIZE];
X	time_t now;
X	struct stat sbuf;
X	extern int errno;
X#ifdef	VMS
X	int fd;
X/* The name here is because of the peculiar properties of version numbers
X * in Eunice.  We eliminate any troublesome characters also.
X */
X	(void) sprintf(lockname, "/tmp/%.10s.l.1", str);
X	for (cp = lockname; *cp; cp++)
X		if (*cp == '/' || *cp == '[' || *cp == ']') *cp = '.';
X	while ((fd = creat(lockname, 0444)) < 0) {
X#else /* !VMS */
X	(void) strcpy(tempname, "/tmp/LTMP.XXXXXX");
X	(void) mktemp(tempname);
X	(void) strcpy(lockname, "/tmp/L");
X	i = strlen(lockname);
X	cp = &lockname[i];
X	scp = str - 1;
X	while (i++ < LOCKSIZE && *++scp != '\0')
X		if (*scp == '/')	/* slash screws up the open */
X			*cp++ = '.';
X		else
X			*cp++ = *scp;
X	*cp = '\0';
X#ifdef FOURTEENMAX
X	lockname[5 /* /tmp/ */ + 14] = '\0';
X#endif
X	i = creat(tempname, 0666);
X	if (i < 0)
X		xerror("Cannot creat %s: errno %d", tempname, errno);
X	(void) close(i);
X	while (link(tempname, lockname)) {
X#endif /* !VMS */
X		(void) time(&now);
X		if (stat(lockname, &sbuf) < 0)
X			xerror("Directory permission problem in /tmp");
X
X		if (sbuf.st_mtime + 10*60 < now) {
X			(void) unlink(lockname);
X			logerr("Article %s locked up", str);
X			break;
X		}
X		log("waiting on lock for %s", lockname);
X		sleep((unsigned)60);
X	}
X#ifdef VMS
X	(void) close(fd);
X#endif
X	(void) unlink(tempname);
X}
X
Xidunlock()
X{
X	(void) 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	extern char *mydomain();
X
X	lock();
X	fp = xfopen(SEQFILE, "r");
X	(void) fgets(bfr, BUFLEN, fp);
X	(void) fclose(fp);
X	seqn = atol(bfr) + 1;
X/*
X * For Eunice, this breaks if SEQFILE is not in Unix format.
X */
X	fp = xfopen(SEQFILE, "r+w");
X	fprintf(fp, "%ld\n", seqn);
X	(void) fclose(fp);
X	unlock();
X	(void) sprintf(hp->ident, "<%ld@%s>", seqn, LOCALSYSNAME);
X}
X
X/*
X * Check that header.nbuf contains only valid newsgroup names;
X * exit with error if not valid.
X */
Xngfcheck(isproc)
X{
X	register FILE *	f;
X	register char *	cp;
X	register int	i, j;
X	register int	ngcount, okcount, havealiased;
X	register int	pass;
X	char *		ngs[sizeof header.nbuf / 2];
X	char		uses[sizeof header.nbuf / 2];
X	char		tbuf[sizeof header.nbuf];
X	char		abuf[BUFLEN];
X
X	havealiased = ngcount = 0;
X	is_mod[0] = '\0';
X	/*
X	** Split header.nbuf into constituent newsgroups.
X	** Zap "local" newsgroups of articles from remote sites.
X	*/
X	cp = tbuf;
X	(void) strcpy(cp, header.nbuf);
X	for ( ; ; ) {
X		while (*cp == NGDELIM || *cp == ' ')
X			++cp;
X		if (*cp == '\0')
X			break;
X		ngs[ngcount] = cp;
X		do {
X			++cp;
X		} while (*cp != '\0' && *cp != NGDELIM && *cp != ' ');
X		if (*cp != '\0')
X			*cp++ = '\0';
X		/*
X		** Check for local only distribution on incoming
X		** newsgroups.  This might occur if someone posted to
X		** general,net.unix
X		*/
X		if (isproc && index(ngs[ngcount], '.') == NULL &&
X			index(header.nbuf, '.') != NULL) {
X				logerr("Local group %s removed",
X					ngs[ngcount]);
X				continue;
X		}
X		uses[ngcount] = 1;	/* it should go in "Newsgroups" line */
X		++ngcount;
X	}
X	/*
X	** Check groups against active file.
X	*/
Xrecheck:
X	okcount = 0;
X	rewind(actfp); clearerr(actfp);
X	while (okcount < ngcount && fgets(bfr, BUFLEN, actfp) == bfr) {
X		if ((cp = index(bfr, ' ')) == NULL)
X			continue;	/* strange line in input! */
X		/* newsgroup 12345 12345 X */
X		/*  cp +    01234567890123 */
X		if (!isproc && cp[13]  == 'n')
X			continue;	/* can't post to this group! */
X		*cp = '\0';
X		for (i = 0; i < ngcount; ++i)
X			if (uses[i] >= 1 && strcmp(bfr, ngs[i]) == 0) {
X				uses[i] = 2;	/* it should be localized too */
X				if (cp[13] == 'm')
X					strcpy(is_mod, bfr);
X				++okcount;
X			}
X	}
X#ifdef ALWAYSALIAS
X	okcount = 0;
X#endif /* ALWAYSALIAS */
X	/*
X	** Handle groups absent from active file.
X	*/
X	if (havealiased == 0 && okcount < ngcount) {
X		/*
X		** See if remaining groups are in our alias list.
X		*/
X		f = xfopen(ALIASES, "r");
X		while (okcount < ngcount && fscanf(f, "%s %s", abuf, bfr) == 2)
X			for (i = 0; i < ngcount; ++i) {
X#ifndef ALWAYSALIAS
X				if (uses[i] == 2)
X					continue;
X#endif /* ALWAYSALIAS */
X				if (strcmp(ngs[i], abuf) != 0)
X					continue;
X				if (isproc)
X					cp = "Aliased newsgroup %s to %s";
X				else
X					cp = "Please change %s to %s";
X				logerr(cp, abuf, bfr);
X				ngs[i] = AllocCpy(bfr);
X				uses[i] = 2;
X				++havealiased;
X				++okcount;
X			}
X		(void) fclose(f);
X		for (i = 0; i < ngcount; ++i) {
X			if (uses[i] == 2)
X				continue;
X			if (isproc)
X				log("Unknown newsgroup %s not localized",
X					ngs[i]);
X			else
X				logerr("Unknown newsgroup %s", ngs[i]);
X#ifdef ALWAYSALIAS
X			++okcount;	/* so we know to exit below */
X		}
X		if (!isproc && okcount > 0)
X#else /* !ALWAYSALIAS */
X		}
X		if (!isproc)
X#endif /* !ALWAYSALIAS */
X			newssave(infp, (char *) NULL);
X		/*
X		 * Unfortunately, if you alias an unmoderated group to a
X		 * moderated group, you must recheck the active file to see
X		 * if the new group is moderated. Rude but necessary.
X		 */
X		if (havealiased)
X			goto recheck;	
X	}
X	/*
X	** Zap duplicates.
X	*/
X	for (i = 0; i < ngcount - 1; ++i) {
X		if (uses[i] == 0)
X			continue;
X		for (j = i + 1; j < ngcount; ++j) {
X			if (uses[j] == 0)
X				continue;
X			if (strcmp(ngs[i], ngs[j]) != 0)
X				continue;
X			logerr("Duplicate %s removed", ngs[j]);
X			if (uses[i] < uses[j])
X				uses[i] = uses[j];
X			uses[j] = 0;
X		}
X	}
X	for (pass = 1; pass <= 2; ++pass) {
X		register int	avail;
X
X		if (pass == 1) {
X			/*
X			** Rewrite header.nbuf.
X			*/
X			cp = header.nbuf;
X			avail = sizeof header.nbuf;
X		} else {
X			/*
X			** Fill in nbuf.
X			*/
X			cp = nbuf;
X			avail = sizeof nbuf;
X		}
X		for (i = 0; i < ngcount; ++i) {
X			if (uses[i] < pass)
X				continue;
X			j = strlen(ngs[i]);
X			if (j + 2 > avail) {
X				logerr("Redone Newsgroups too long");
X				break;
X			}
X			(void) strcpy(cp, ngs[i]);
X			cp += j;
X			*cp++ = (pass == 1) ? NGDELIM : '\0';
X			avail -= (j + 1);
X		}
X		if (pass == 1) {
X			if (cp == header.nbuf)
X				*cp = '\0';
X			else	*(cp - 1) = '\0';
X		} else	*cp = '\0';
X	}
X	/*
X	** Free aliases.
X	*/
X	for (i = 0; i < ngcount; ++i)
X		if (ngs[i] < tbuf || ngs[i] > &tbuf[sizeof tbuf - 1])
X			free(ngs[i]);
X	return nbuf[0] == '\0';
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	register char *fn, *p;
X	char buf[BUFLEN];
X	char *fullname(), *getenv();
X	int fd, n;
X	extern char *mydomain();
X
X	if ((fn = getenv("NAME")) == NULL) {
X		(void) sprintf(buf, "%s/%s", userhome, ".name");
X		if ((fd = open(buf, 0)) >= 0) {
X			n = read(fd, buf, sizeof buf);
X			(void) close(fd);
X			if (n > 0 && buf[0] >= 'A') {
X				for (p = fn = buf; *p; p++)
X					if (*p < ' ')
X						*p = '\0';
X			}
X		}
X	}
X
X	if (fn == NULL)
X		fn = fullname(logname);
X
X	(void) sprintf(hp->path, "%s", logname);
X	(void) sprintf(hp->from, "%s@%s (%s)", logname, FROMSYSNAME, 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		xerror("inews ran away looping on signal %d", n);
X	}
X	(void) signal(n, onsig);
X	SigTrap = n;
X}
X
X/*
X * If the stdin begins with "#" the input is some kind of batch.  if
X * the first line is:
X *	#!cunbatch
X * or
X *	#!c7unbatch
X * then fork off a pipe to do the either a
X *	"compress -d"
X * or a
X *	"decode | compress -d"
X * and check their output for more batch headers.  They probably
X * contain a batch format that looks like this:
X *	#! rnews 1234
X *	article with 1234 chars
X *	#! rnews 4321
X *	article with 4321 chars
X * If so, then for each article, copy the indicated number of chars into
X * a temp file, fork a copy of ourselves, make its input the temp file,
X * and allow the copy to process the article.  This avoids an exec of
X * rnews for each article.
X */
X
Xcheckbatch()
X{
X	int c;
X	char *cp;
X
X	setbuf(infp, (char *)NULL);
X	while ((c = getc(infp)) == '#') {
X		/* some kind of batch, investigate further */
X		int i;
X		char cmd[BUFLEN];
X		cmd[0] = c;
X		fgets(cmd + 1, BUFLEN, infp);
X		if (strncmp(cmd, "#! cunbatch", 11) == 0) {
X			(void) sprintf(cmd, "%s/compress", LIB);
X			input_pipe(cmd, "compress", "-d", (char *) 0);
X			continue;	/* look for the #! rnews */
X		} else if (strncmp(cmd, "#! c7unbatch", 12) == 0) {
X			(void) sprintf(cmd, "%s/decode | %s/compress -d",
X				LIB, LIB);
X			input_pipe("/bin/sh", "news-unpack", "-c", cmd);
X			continue;	/* look for the #! rnews */
X		} else if (strncmp(cmd, "#! rnews ", 9) == 0 ||
X			strncmp(cmd, "! rnews ", 8) == 0) {
X			/* instead of execing unbatch do it ourselves */
X			register int fd, rc, wc;
X			int piped[2];
X			register long size, asize;
X			char *filename;
X			int pid, wpid, exstat;
X#define CPBFSZ 8192
X			char buf[CPBFSZ];
X
X			filename = 0;
X			do {
X				while (strncmp(cmd, "#! rnews ", 9)) {
X					fprintf(stderr, "out of sync, skipping %s\n", cmd);
X					if (fgets(cmd, BUFLEN, infp) == NULL)
X						exit(0);
X				}
X				asize = atol(cmd + 9);
X				if (asize <= 0)
X					xerror("checkbatch: bad batch count %ld", asize);
X				fd = -1;
X				size = asize;
X				do {
X					if (size > CPBFSZ)
X						rc = CPBFSZ;
X					else
X						rc = size;
X					rc = fread(buf, 1, rc, infp);
X					if (rc <= 0)
X						break;
X					if (fd < 0) {
X						if (rc == asize)
X							break;	/* fits in buffer */
X						if (!filename)
X							filename = mktemp("/tmp/unbnewsXXXXXX");
X						if ((fd = creat(filename, 0666)) < 0) {
X							fprintf(stderr, "rnews: creat of \"%s\" failed",
X								filename);
X							perror(" ");
X							exit(1);
X						}
X					}
X					wc = write(fd, buf, rc);	/* write to temp file */
X					if (wc != rc) {
X						fprintf(stderr, "write of %d to \"%s\" returned %d",
X							rc, filename, wc);
X						perror(" ");
X						exit(1);
X					}
X					size -= rc;
X				} while (size > 0);
X				if (fd >= 0)
X					(void) close(fd);
X
X				/*
X				 * If we got a truncated batch, don't process
X				 * the last article; it will probably be
X				 * received again. 
X				 */
X				if ((rc < asize) && (size > 0))
X					break;
X
X				/*
X				 * This differs from the old unbatcher in
X				 * that we don't exec rnews, mainly because
X				 * we ARE rnews.  Instead we fork off a copy
X				 * of ourselves for each article and allow it
X				 * to process. 
X				 */
X				if (rc == asize) {
X					/*
X					 * article fits in buffer, use a pipe
X					 * instead of a temporary file. 
X					 */
X					if (pipe(piped) != 0)
X						xerror("checkbatch: pipe() failed");
X				}
X				while ((pid = fork()) == -1) {
X					fprintf(stderr, "fork failed, waiting...\r\n");
X					sleep(60);
X				}
X				if (pid == 0) {
X					if (rc == asize) {
X						/* article fits in buffer
X						 * make the output of the
X						 * pipe for STDIN 
X						 */
X						(void) fclose(infp);
X						/* redundant but why not */
X						(void) close(0);
X						if ((i = dup(piped[0])) != 0)
X							xerror("dup() returned %d, should be 0", i);
X						(void) close(piped[0]);
X						(void) close(piped[1]);
X						infp = fdopen(0, "r");
X					} else	/* supstitute temp file as
X						 * input */
X						freopen(filename, "r", infp);
X					return;	/* from checkbatch as if
X						 * normal article */
X				}
X				/* parent of fork */
X				if (rc == asize) {
X					/* article fits in buffer */
X					wc = write(piped[1], buf, rc);
X					if (wc != rc) {
X						fprintf(stderr, "write of %d to pipe returned %d",
X							rc, wc);
X						perror("rnews: write");
X						exit(1);
X					}
X					(void) close(piped[0]);
X					(void) close(piped[1]);
X				}
X				while ((wpid = wait(&exstat)) >= 0 && wpid != pid);
X			} while (fgets(cmd, BUFLEN, infp) != NULL);
X			(void) unlink(filename);
X			exit(0);/* all done */
X
X		} else {
X			docmd(cmd);
X			xxit(0);
X		}
X	}			/* while a batch */
X	cp = malloc((unsigned)BUFSIZ);
X	if (cp != NULL)
X		setbuf(infp, cp);
X	if (c != EOF)
X		(void) ungetc(c, infp);
X	clearerr(infp);
X}
X
X/*
X * The input requires some processing so fork and exec the indicated command
X * with its output piped to our input. 
X */
Xstatic 
Xinput_pipe(cmd, arg0, arg1, arg2)
Xchar *cmd, *arg0, *arg1, *arg2;
X{
X	int i, pid;
X	int piped[2];
X
X	if (pipe(piped) != 0) {
X		perror("checkbatch: pipe() failed");
X		exit(1);
X	}
X	fflush(stdout);
X	while ((pid = vfork()) == -1) {
X		perror("checkbatch: fork failed, waiting");
X		sleep(60);
X	}
X	if (pid == 0) {		/* child process */
X		/*
X		 * setup a pipe such that the exec'ed process will read our
X		 * input file and write to the pipe 
X		 */
X		(void) close(1);
X		if ((i = dup(piped[1])) != 1)
X			xerror("dup() returned %d, should be 1", i);
X		(void) close(piped[0]);
X		(void) close(piped[1]);
X		execl(cmd, arg0, arg1, arg2, (char *) 0);
X		perror("checkbatch");
X		xerror("Unable to exec %s to unpack news.", cmd);
X	} else {		/* parent process */
X		/* make the output of the pipe for STDIN */
X		(void) fclose(infp);
X		(void) close(0);
X		if ((i = dup(piped[0])) != 0)
X			xerror("dup() returned %d, should be 0", i);
X		(void) close(piped[0]);
X		(void) close(piped[1]);
X		/*
X		 * there should be a way to clear any buffered input and just
X		 * replace file descriptor 0 but I can't find a portable way. 
X		 */
X		infp = fdopen(0, "r");
X	}
X}
X
X#define MAXARGS 32
X
Xdocmd(p)
Xregister char *p;
X{
X	char *args[MAXARGS];
X	register char **ap = args;
X	char path[BUFSIZ];
X	char *rindex(), *cp;
X
X	while (*p && !isspace(*p))		/* skip leading #! crud */
X		p++;
X
X	while (isspace(*p))
X		p++;
X
X	while (*p != '\0') {
X		*ap++ = p;
X		if (ap >= &args[MAXARGS]) {
X			logerr("inews: unbatch: Too many args to %s", args[0]);
X			exit(2);
X		}
X		while (*p && !isspace(*p))
X			p++;
X		if (*p)
X			*p++ = '\0';
X		while (isspace(*p))
X			p++;
X	}
X	*ap = (char *)0;
X
X	if (ap == args) {
X		logerr("inews: unbatch: no command to execute");
X		exit(2);
X	}
X
X	/* strip off any leading pathname in case someone gets tricky */
X	cp = rindex(args[0], '/');
X	if (cp++ == NULL)
X		cp = args[0];
X
X# ifdef HOME
X	sprintf(path, "%s/%s/%s", logdir(HOME), LIBDIR, cp);
X# else /* !HOME */
X	sprintf(path, "%s/%s", LIBDIR, cp);
X# endif /* HOME */
X
X	/*
X	 * "path" is absolute, no searching is needed,  we use
X	 * 'execvp' solely so that sh scripts will be handled
X	 */
X	(void) execvp(path, args);
X	perror(path);
X	xxit(2);
X}
X
X/*
X *	Exit and cleanup.
X */
Xxxit(status)
Xint status;
X{
X	(void) unlink(INFILE);
X	(void) 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	(void) 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	(void) 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				(void) unlink(SUBLOCK);
X				logerr("News system locked up");
X			}
X			if (i < -3)
X				xerror("Unable to unlock news system");
X			sleep((unsigned)1);
X		}
X		(void) close(fd);
X	}
X}
X
Xunlock()
X{
X	if (--lockcount == 0)
X		(void) unlink(SUBLOCK); 
X}
X
X#else /* !VMS */
X
X/*
X * Newsystem locking.
X */
X
X#if defined(BSD4_2) || defined(LOCKF)
X#ifdef LOCKF
X#include <unistd.h>
X#else /* !LOCKF */
X#include <sys/file.h>
X#endif /* !LOCKF */
Xstatic int LockFd = -1;
Xlock()
X{
X	LockFd = open(SUBFILE, 2);
X	if (LockFd < 0)
X		logerr("Can't open(\"%s\",2) to lock", SUBFILE);
X	/* This will sleep until the other program releases the lock */
X	/* We may need to alarm out of this, but I don't think so */
X#ifdef LOCKF
X	if (lockf(LockFd, F_LOCK, 0) < 0)
X#else
X	 if (flock(LockFd, LOCK_EX) < 0)
X#endif
X		xerror("Can't get lock on %s: %s", SUBFILE, errmsg(errno));
X}
X
Xunlock()
X{
X	(void) close(LockFd);
X}
X#else /* !BSD4_2 */
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		(void) unlink(LOCKFILE);
X}
X#endif /* !BSD4_2 */
X#endif /* !VMS */
X
X/* VARARGS1 */
Xerror(message, arg1, arg2, arg3)
Xchar *message;
Xlong arg1, arg2, arg3;
X{
X	char buffer[LBUFLEN];
X
X	fflush(stdout);
X	(void) sprintf(buffer, message, arg1, arg2, arg3);
X	logerr(buffer);
X	xxit(mode == PROC ? 0 : 1);
X}
END_OF_FILE
if test 32337 -ne `wc -c <'ifuncs.c'`; then
    echo shar: \"'ifuncs.c'\" unpacked with wrong size!
fi
# end of 'ifuncs.c'
fi
if test -f 'expire.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'expire.c'\"
else
echo shar: Extracting \"'expire.c'\" \(28180 characters\)
sed "s/^X//" >'expire.c' <<'END_OF_FILE'
X/*
X * This software is Copyright (c) 1986 by Rick Adams.
X *
X * Permission is hereby granted to copy, reproduce, redistribute or
X * otherwise use this software as long as: there is no monetary
X * profit gained specifically from the use or reproduction or this
X * software, it is not sold, rented, traded or otherwise marketed, and
X * this copyright notice is included prominently in any copy
X * made.
X *
X * The author make no claims as to the fitness or correctness of
X * this software for any use whatsoever, and it is provided as is. 
X * Any use of this software is at the user's own risk.
X *
X * expire - expire daemon runs around and nails all articles that
X *		 have expired.
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)expire.c	2.53	4/6/87";
X#endif /* SCCSID */
X
X#include "params.h"
X#include <errno.h>
X#if defined(BSD4_2) || defined(BSD4_1C)
X# include <sys/dir.h>
X# include <sys/file.h>
X#else
X# include "ndir.h"
X#endif
X
X#ifdef LOCKF
X#include <unistd.h>
X#endif /* LOCKF */
X
Xchar *Progname = "expire";	/* used by xerror to identify failing program */
X
X/*	Number of array entries to allocate at a time.	*/
X#define SPACE_INCREMENT	1000
X
Xstruct expdata {
X	char *e_name;
X	long e_min, e_max;
X	time_t	e_droptime, e_expiretime;
X	char e_ignorexp;
X	char e_doarchive;
X	char e_doexpire;
X};
X
Xextern int	errno;
Xchar	NARTFILE[BUFLEN], OARTFILE[BUFLEN];
Xchar	PAGFILE[BUFLEN], DIRFILE[BUFLEN];
Xchar	NACTIVE[BUFLEN], OACTIVE[BUFLEN];
Xchar	recdate[BUFLEN];
Xlong	rectime, exptime;
Xextern char *OLDNEWS;
Xint	verbose = 0;		/* output trace information */
Xint	ignorexp = 0;		/* ignore Expire: lines */
Xint	doarchive = 0;		/* archive articles in SPOOL/oldnews */
Xint	nohistory = 0;		/* ignore history file */
Xint	dorebuild = 0;		/* rebuild history file */
Xint	dorbldhistory = 0;	/* rebuild history.d directory */
Xint	usepost = 0;		/* use posting date to expire */
Xint	frflag = 0;		/* expire specific user */
Xint	doupdateactive = 0;	/* update ACTIVE file */
Xchar	baduser[BUFLEN];
Xextern 	char filename[], nbuf[];
X
Xstruct timeb Now;
X
X/*
X * This code uses realloc to get more of the multhist array.
X */
Xstruct multhist {
X	char	*mh_ident;
X	char	*mh_file;
X} *multhist;
Xunsigned int mh_size;
Xchar *calloc();
Xchar *realloc();
Xstruct tm *gmtime();
X
X#ifdef DBM
Xtypedef struct {
X	char *dptr;
X	int dsize;
X} datum;
X#else
XFILE *nexthistfile();
X#endif /* !DBM */
X
Xlong	expincr;
Xlong	dropincr;
Xlong	atol();
Xtime_t	cgtdate(), time();
XFILE *popen();
Xstruct passwd *pw;
Xstruct group *gp;
Xchar	arpat[LBUFLEN];
Xint	arpatlen = 0;
Xchar	ngpat[LBUFLEN];
Xint	ngpatlen = 0;
Xchar	afline[BUFLEN];
Xchar	grpsleft[BUFLEN];
Xstruct hbuf h;
Xint	ExpireLock;
Xint	rmlock();
Xtime_t	today;
X
Xmain(argc, argv)
Xint	argc;
Xchar	**argv;
X{
X	pathinit();
X	(void) umask(N_UMASK);
X	username = NEWSUSR;
X
X	/*
X	 * Try to run as NEWSUSR/NEWSGRP
X	 */
X	if ((pw = getpwnam(NEWSUSR)) == NULL)
X		xerror("Cannot get NEWSUSR pw entry");
X
X	uid = pw->pw_uid;
X	if ((gp = getgrnam(NEWSGRP)) == NULL)
X		xerror("Cannot get NEWSGRP gr entry");
X	gid = gp->gr_gid;
X	(void) setgid(gid);
X	(void) setuid(uid);
X
X	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
X		signal(SIGHUP, rmlock);
X	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
X		signal(SIGINT, rmlock);
X	expincr = DFLTEXP;
X	dropincr = HISTEXP;
X	ngpat[0] = ',';
X	arpat[0] = ',';
X	while (argc > 1) {
X		switch (argv[1][1]) {
X		case 'v':
X			if (isdigit(argv[1][2]))
X				verbose = argv[1][2] - '0';
X			else if (argc > 2 && argv[2][0] != '-') {
X
X				argv++;
X				argc--;
X				verbose = atoi(argv[1]);
X			} else
X				verbose = 1;
X			if (verbose < 3)
X				setbuf(stdout, (char *)NULL);
X			break;
X		case 'e':	/* Use this as default expiration time */
X			if (argc > 2 && argv[2][0] != '-') {
X				argv++;
X				argc--;
X				expincr = atol(argv[1]) * DAYS;
X			} else if (isdigit(argv[1][2]))
X				expincr = atol(&argv[1][2]) * DAYS;
X			break;
X		case 'E':	/* Use this as default forget time */
X			if (argc > 2 && argv[2][0] != '-') {
X				argv++;
X				argc--;
X				dropincr = atol(argv[1]) * DAYS;
X			} else if (isdigit(argv[1][2]))
X				dropincr = atol(&argv[1][2]) * DAYS;
X			break;
X		case 'I':	/* Ignore any existing expiration date */
X			ignorexp = 2;
X			break;
X		case 'i':	/* Ignore any existing expiration date */
X			ignorexp = 1;
X			break;
X		case 'n':
X			if (argc > 2) {
X				argv++;
X				argc--;
X				while (argc > 1 && argv[1][0] != '-') {
X					int argvlen;
X					argvlen = strlen(argv[1]);
X					if (ngpatlen + argvlen + 2 > sizeof (ngpat)) {
X						xerror("Too many groups specified for -n\n");
X					}
X					if (ngpat[ngpatlen] == '\0') {
X						ngpat[ngpatlen++] = ',';
X						ngpat[ngpatlen] = '\0';
X					}
X					strcpy(&ngpat[ngpatlen], argv[1]);
X					ngpatlen += argvlen;
X					argv++;
X					argc--;
X				}
X				argv--;
X				argc++;
X			}
X			break;
X		case 'a':	/* archive expired articles */
X			if (access(OLDNEWS,0) < 0){
X				perror(OLDNEWS);
X				xerror("No archiving possible\n");
X			}
X			doarchive++;
X			if (argc > 2) {
X				argv++;
X				argc--;
X				while (argc > 1 && argv[1][0] != '-') {
X					int argvlen;
X					argvlen = strlen(argv[1]);
X					if (arpatlen + argvlen + 2 > sizeof (arpat)) {
X						xerror("Too many groups specified for -a\n");
X					}
X					if (arpat[arpatlen] == '\0') {
X						arpat[arpatlen++] = ',';
X						arpat[arpatlen] = '\0';
X					}
X					strcpy(&arpat[arpatlen], argv[1]);
X					arpatlen += argvlen;
X					argv++;
X					argc--;
X				}
X				argv--;
X				argc++;
X			}
X			break;
X		case 'h':	/* ignore history */
X			nohistory++;
X			break;
X		case 'r':	/* rebuild history file */
X			dorebuild++;
X			nohistory++;
X			break;
X		case 'R':	/* just rebuild the dbm files */
X#ifdef DBM
X			rebuilddbm();
X			xxit(0);
X#else /* !DBM */
X			fprintf(stderr, "You have not compiled expire with DBM, so -R is meaningless\n");
X			xxit(1);
X#endif /* !DBM */
X
X		case 'p':	/* use posting date to expire */
X			usepost++;
X			break;
X		case 'f':	/* expire messages from baduser */
X			frflag++;
X			if (argc > 2) {
X				strcpy(baduser, argv[2]);
X				argv++;
X				argc--;
X			}
X			break;
X		case 'u':	/* update the active file from 2.10.1 fmt */
X			doupdateactive++;
X			break;
X		case 'H':	/* convert to history.d format */
X			dorbldhistory++;
X			break;
X		default:
X			printf("Usage: expire [ -v [level] ] [-e days ] [-i] [-a] [-r] [-h] [-p] [-u] [-f username] [-n newsgroups] [-H]\n");
X			xxit(1);
X		}
X		argc--;
X		argv++;
X	}
X	if (dorbldhistory) {
X#ifndef DBM
X		rebuildhistorydir();
X#endif /* !DBM */
X		exit(0);
X	}
X	if (dropincr < expincr) {
X		dropincr = HISTEXP;
X		fprintf(stderr, "History expiration time < article expiration time. Default used.\n");
X	}
X	if (ngpat[0] == ',')
X		(void) strcpy(ngpat, "all,");
X	if (arpat[0] == ',')
X		(void) strcpy(arpat, "all,");
X	(void) ftime(&Now);
X	today = Now.time;
X	if (chdir(SPOOL))
X		xerror("Cannot chdir %s", SPOOL);
X
X	if (verbose) {
X		printf("expire: nohistory %d, rebuild %d, doarchive %d\n",
X			nohistory, dorebuild, doarchive);
X		printf("newsgroups: %s\n",ngpat);
X		if (doarchive)
X			printf("archiving: %s\n",arpat);
X	}
X
X#ifdef DBM
X	(void) sprintf(OARTFILE, "%s/%s", LIB, "ohistory");
X#endif /* DBM */
X	(void) sprintf(NARTFILE, "%s/%s", LIB, "nhistory");
X
X	(void) sprintf(OACTIVE, "%s/%s", LIB, "oactive");
X	(void) sprintf(NACTIVE, "%s/%s", LIB, "nactive");
X
X	if (!doupdateactive) {
X		expire();
X#ifndef DBM
X		rebuildhistorydir();
X#endif
X	}
X
X	updateactive();
X	rmlock();
X
X	/*
X	 * Now read in any saved news.
X	 */
X#ifdef PROFILING
X	monitor((int(*)())0,(int(*)())0,0,0,0);
X#endif /* PROFILING */
X#ifdef IHCC
X	/*afline happens to be available - (we're getting out anyway)*/
X	sprintf(afline, "%s/%s", logdir(HOME), RNEWS);
X	execl(afline, "rnews", "-U", (char *)NULL);
X#else /* ! IHCC */
X	execl(RNEWS, "rnews", "-U", (char *)NULL);
X#endif /* ! IHCC */
X	perror(RNEWS);
X	xxit(1);
X	/* NOTREACHED */
X}
X
Xexpire()
X{
X	register char	*p1, *p2, *p3;
X	register time_t newtime;
X	register FILE *fp = NULL;
X	FILE	*ohfd, *nhfd;
X	int i;
X	char	fn[BUFLEN];
X	DIR	*ngdirp = NULL;
X	static struct direct *ngdir;
X
X#ifdef DBM
X	if (!dorebuild) {
X		(void) sprintf(PAGFILE, "%s/%s", LIB, "nhistory.pag");
X		(void) sprintf(DIRFILE, "%s/%s", LIB, "nhistory.dir");
X		(void) close(creat(PAGFILE, 0666));
X		(void) close(creat(DIRFILE, 0666));
X		initdbm(NARTFILE);
X	}
X#endif
X
X	if (nohistory) {
X		ohfd = xfopen(ACTIVE, "r");
X		if (dorebuild) {
X			/* Allocate initial space for multiple newsgroup (for
X			   an article) array */
X			multhist = (struct multhist *)calloc (SPACE_INCREMENT,
X					sizeof (struct multhist));
X			mh_size = SPACE_INCREMENT;
X
X			(void) sprintf(afline, "exec sort -t\t +1.6 -2 +1 >%s", NARTFILE);
X			printf("%s\n",afline);
X			if ((nhfd = popen(afline, "w")) == NULL)
X				xerror("Cannot exec %s", afline);
X		} else
X			nhfd = xfopen("/dev/null", "w");
X	} else {
X#ifdef DBM
X		ohfd = xfopen(ARTFILE, "r");
X#else
X		ohfd = nexthistfile((FILE *)NULL);
X#endif /* DBM */
X		nhfd = xfopen(NARTFILE, "w");
X	}
X
X	dolock();
X
X	for(i=0;i<NUNREC;i++)
X		h.unrec[i] = NULL;
X
X	while (TRUE) {
X		fp = NULL;
X		if (nohistory) {
X			recdate[0] = '\0';
X			do {
X				if (ngdir == NULL) {
X					if ( ngdirp != NULL )
X						closedir(ngdirp);
X					if (fgets(afline, BUFLEN, ohfd) == NULL)
X						goto out;
X					(void) strcpy(nbuf, afline);
X					p1 = index(nbuf, ' ');
X					if (p1 == NULL)
X						p1 = index(nbuf, '\n');
X					if (p1 != NULL)
X						*p1 = NULL;
X					if (!ngmatch(nbuf, ngpat))
X						continue;
X
X					/* Change a group name from
X					   a.b.c to a/b/c */
X					for (p1=nbuf; *p1; p1++)
X						if (*p1 == '.')
X							*p1 = '/';
X
X					if ((ngdirp = opendir(nbuf)) == NULL)
X						continue;
X
X				}
X				ngdir = readdir(ngdirp);
X			/*	Continue looking if not an article.	*/
X			} while (ngdir == NULL || !islegal(fn,nbuf,ngdir->d_name));
X
X			p2 = fn;
X			if (verbose > 2)
X				printf("article: %s\n", fn);
X			strcpy(filename, dirname(fn));
X			fp = access(filename, 04) ? NULL : art_open(filename, "r");
X		} else {
X			char dc;
X#ifdef DBM
X			if (fgets(afline, BUFLEN, ohfd) == NULL)
X				break;
X#else
X			if (fgets(afline, BUFLEN, ohfd) == NULL)
X				if (!(ohfd = nexthistfile(ohfd)))
X					break;
X				else
X					continue;
X#endif /* DBM */
X			if (verbose > 2)
X				printf("article: %s", afline);
X			p1 = index(afline, '\t');
X			if (!p1)
X				continue;
X			*p1 = '\0';
X			(void) strcpy(h.ident, afline);
X			*p1 = '\t';
X			p2 = index(p1 + 1, '\t');
X			if (!p2)
X				continue;
X			*p2 = '\0';
X			(void) strcpy(recdate, p1+1);
X			rectime = cgtdate(recdate);
X			*p2++ = '\t';
X			(void) strcpy(nbuf, p2);
X			p3 = index(nbuf, '/');
X			if (p3) {
X				register char *p4;
X
X				p4 = index(p3, '\n');
X				if (p4) {
X					while (p4[-1] == ' ')
X						p4--;
X					*p4 = '\0';
X				}
X
X				/*
X				 * convert list of newsgroups from
X				 *	ng1/num ng2/num ...
X				 * to
X				 *	ng1,ng2,...
X				 */
X				p4 = p3;
X				do {
X					*p3++ = NGDELIM;
X					while (*p4 != '\0' && *p4 != ' ')
X						p4++;
X					if (*p4++ == '\0') {
X						*--p3 = '\0';
X						break;
X					}
X					while (*p3 = *p4++) {
X						if (*p3 == '/')
X							break;
X						else
X							p3++;
X					}
X				} while (*p3);
X			} else {
X				/*
X				 * Nothing after the 2nd tab.  This happens
X				 * when there is no message left in the spool
X				 * directory, only the memory of it in the
X				 * history file. (That is, it got cancelled
X				 * or expired.) Use date in the history file
X				 * to decide if we should keep the memory.
X				 */
X				grpsleft[0] = '\0';
X				goto checkdate;
X			}
X			if (!ngmatch(nbuf, ngpat) ||
X			     ((rectime+expincr > today) && !dorebuild &&
X				 !frflag && !usepost && recdate[0] != ' '))
X				goto keephist;
X			if (!dorebuild && !frflag && !usepost &&
X				recdate[0] != ' ') {
X				grpsleft[0] = '\0';
X				goto nailit; /* just expire it */
X			}
X
X			/*
X			 * Look for the file--possibly several times,
X			 * if it was posted to several news groups.
X			 */
X			dc = ' ';
X			p3 = p2;
X			while (dc != '\n') {
X				p1 = index(p3, ' ');
X				if (p1) {
X					dc = ' ';
X					*p1 = '\0';
X				} else {
X					p1 = index(p3, '\n');
X					if (p1 && p1 > p3) {
X						dc = '\n';
X						*p1 = '\0';
X					} else {
X						fp = NULL;
X						break;
X					}
X				}
X				strcpy(filename, dirname(p3));
X				if (access(filename, 4) == 0 &&
X					((fp=art_open(filename, "r")) != NULL))
X						break;
X				p3 = p1 + 1;
X			}
X			if (p1)
X				*p1 = dc;
X		}
X
X		if (fp == NULL) {
X			/*
X			 * this probably means that the article has been
X			 * cancelled.  Lets assume that, and make an
X			 * entry in the history file to that effect.
X			 */
X			if (verbose)
X				perror(filename);
X			strcpy(p2, "cancelled\n");
X			grpsleft[0] = '\0';
X			goto checkdate;
X		}
X		for(i=0; i<NUNREC; i++)
X			if (h.unrec[i] != NULL) {
X				free(h.unrec[i]);
X				h.unrec[i] = NULL;
X			} else
X				break;
X		if (!hread(&h, fp, TRUE)) {
X			printf("Garbled article %s.\n", filename);
X			(void) fclose(fp);
X			/*
X			 * Usually means disk ran out of space.
X			 * Drop this article from our history file
X			 * completely, so we have a chance of picking
X			 * it up again from another feed ..
X			 */
X			goto nailit;
X		}
X		if (nohistory) {
X			if (recdate[0] == '\0') {
X				struct stat statb;
X				if (fstat(fileno(fp), &statb) < 0)
X					rectime = cgtdate(h.subdate);
X				else
X					rectime = statb.st_mtime;
X			} else
X				rectime = cgtdate(recdate);
X		}
X		if (dorebuild) {
X			register char	*cp, *lastslash;
X			register struct multhist *mhp;
X
X			/*
X			 * Format of filename until now was /SPOOL/a/b/c/4
X			 * and this code changes it to a.b.c/4 (the correct
X			 * kind of entry in the history file.)
X			 *
X			 * This cannot be a strcpy because the addresses
X			 * overlap and some machines cannot handle that.
X			 */
X			p1 = filename;
X			cp = p1 + strlen(SPOOL);
X			while (*++cp) {
X				if (*cp == '/') {
X					lastslash = p1;
X					*p1++ = '.';
X				} else
X					*p1++ = *cp;
X			}
X			*p1 = '\0';
X			*lastslash = '/';
X
X			if ((cp = index(h.nbuf, NGDELIM)) == NULL) {
X				struct tm *tm;
Xsaveit:
X				tm = gmtime(&rectime);
X				if (fprintf(nhfd,
X#ifdef USG
X				     "%s\t%s%2.2d/%2.2d/%d %2.2d:%2.2d\t%s\n",
X#else /* !USG */
X				     "%s\t%s%02d/%02d/%d %02d:%02d\t%s\n",
X#endif /* !USG */
X					h.ident, h.expdate[0] ? " " : "",
X					tm->tm_mon+1, tm->tm_mday, tm->tm_year,
X					tm->tm_hour, tm->tm_min, filename)
X					== EOF)
X						xerror("History write failed");
X				(void) fclose(fp);
X				continue;
X			}
X			for (mhp = multhist; mhp < multhist+mh_size && mhp->mh_ident != NULL; mhp++) {
X				if (mhp->mh_file == NULL)
X					continue;
X				if (strcmp(mhp->mh_ident, h.ident))
X					continue;
X				(void) strcat(filename, " ");
X				(void) strcat(filename, mhp->mh_file);
X				free(mhp->mh_file);
X				mhp->mh_file = NULL;
X				/*
X				 * if we have all the links, write to hist now
X				 */
X				if (chrcnt(filename, ' ') == chrcnt(cp,NGDELIM))
X					goto saveit;
X				break;
X			}
X
X			/*
X			 * Here is where we realloc the multhist space rather
X			 * than the old way of static allocation.  It is
X			 * really trivial.  We just clear out the space
X			 * in case it was reused.  The old static array was
X			 * guaranteed to be cleared since it was cleared when
X			 * the process started.
X			 */
X			if (mhp >= multhist + mh_size) {
X				multhist = (struct multhist *)
X					realloc ((char *)multhist,
X					  sizeof (struct multhist) *
X					  (SPACE_INCREMENT + mh_size));
X				if (multhist == NULL)
X					xerror("Too many articles with multiple newsgroups");
X				for (mhp = multhist + mh_size;
X				  mhp < multhist+mh_size+SPACE_INCREMENT;
X					mhp++) {
X					mhp->mh_ident = NULL;
X					mhp->mh_file = NULL;
X				}
X				mhp = multhist + mh_size;
X				mh_size += SPACE_INCREMENT;
X			}
X
X			if (mhp->mh_ident == NULL) {
X				mhp->mh_ident = malloc(strlen(h.ident)+1);
X				(void) strcpy(mhp->mh_ident, h.ident);
X			}
X			cp = malloc(strlen(filename) + 1);
X			if (cp == NULL)
X				xerror("Out of memory");
X			(void) strcpy(cp, filename);
X			mhp->mh_file = cp;
X			(void) fclose(fp);
X			continue;
X		}
X
X		(void) fclose(fp);
X
X		if (h.expdate[0]) {
X			Now.time = rectime;
X			exptime = cgtdate(h.expdate);
X		}
X		newtime = (usepost ? cgtdate(h.subdate) : rectime) + expincr;
X		if (!h.expdate[0] || ignorexp == 2 ||
X		    (ignorexp == 1 && newtime < exptime))
X			exptime = newtime;
X		if (frflag ? strcmp(baduser,h.from)==0 : today >= exptime) {
Xnailit:
X#ifdef DEBUG
X			printf("cancel %s\n", filename);
X#else /* !DEBUG */
X			printf("cancel %s\n", filename);
X			if (verbose)
X				printf("cancel %s\n", h.ident);
X			ulall(p2, &h);
X			(void) sprintf(p2, "%s\n", grpsleft);
X			if (verbose > 2 && grpsleft[0])
X				printf("Some good in %s\n", h.ident);
X#endif /* !DEBUG */
X		} else {
X			if (verbose > 2)
X				printf("Good article %s\n", h.ident);
X			grpsleft[0] = '!';
X		}
X
Xcheckdate:
X		if (grpsleft[0] == '\0' && today >= rectime + dropincr) {
X			if (verbose > 3)
X				printf("Drop history of %s - %s\n",
X				    h.ident, recdate);
X		} else {
X#ifdef DBM
X			long hpos;
X#endif /* DBM */
Xkeephist:
X#ifdef DBM
X			hpos = ftell(nhfd);
X#endif /* DBM */
X
X			if (verbose > 3)
X				printf("Retain history of %s - %s\n",
X				    h.ident, recdate);
X			if (fputs(afline, nhfd) == EOF)
X				xerror("history write failed");
X#ifdef DBM
X			if (!dorebuild)
X				remember(h.ident, hpos);
X#endif /* DBM */
X		}
X	}
Xout:
X	if (dorebuild) {
X		register struct multhist *mhp;
X		struct tm *tm;
X		for (mhp = multhist; mhp < multhist+mh_size && mhp->mh_ident != NULL; mhp++)
X			if (mhp->mh_file != NULL) {
X				if (verbose)
X					printf("Article: %s [%s] Cannot find all links\n", mhp->mh_ident, mhp->mh_file);
X				(void) sprintf(filename,"%s/%s",SPOOL,mhp->mh_file);
X				for (p1 = filename; *p1 != ' ' && *p1 != '\0'; p1++)
X					if (*p1 == '.')
X						*p1 = '/';
X				*p1 = '\0';
X				if ((fp = art_open(filename, "r")) == NULL) {
X					if (verbose)
X						printf("Can't open %s.\n", filename);
X					continue;
X				}
X				if (!hread(&h, fp, TRUE)) {
X					printf("Garbled article %s.\n", filename);
X					(void) fclose(fp);
X					continue;
X				} else {
X					struct stat statb;
X					if (fstat(fileno(fp), &statb) < 0)
X						rectime = cgtdate(h.subdate);
X					else
X						rectime = statb.st_mtime;
X				}
X				tm = gmtime(&rectime);
X				if ( fprintf(nhfd,
X#ifdef USG
X					"%s\t%s%2.2d/%2.2d/%d %2.2d:%2.2d\t%s\n",
X#else /* !USG */
X					"%s\t%s%02d/%02d/%d %02d:%02d\t%s\n",
X#endif /* !USG */
X					h.ident, h.expdate[0] ? " " : "",
X					tm->tm_mon+1, tm->tm_mday, tm->tm_year,
X					tm->tm_hour, tm->tm_min, mhp->mh_file)
X					== EOF )
X						xerror("History write failed");
X				(void) fclose(fp);
X				continue;
X			}
X		(void) pclose(nhfd);
X		free ((char *)multhist);
X	} else
X		if (fclose(nhfd))
X			xerror("History write failed, %s", errmsg(errno));
X
X	if (dorebuild || !nohistory) {
X#ifndef DBM
X		(void) rename(ARTFILE, OARTFILE);
X#endif /* !DBM */
X		(void) rename(NARTFILE, ARTFILE);
X#ifdef DBM
X		if (dorebuild)
X			rebuilddbm( );
X		else {
X			char tempname[BUFLEN];
X			(void) sprintf(tempname,"%s.pag", ARTFILE);
X			(void) strcat(NARTFILE, ".pag");
X			(void) rename(NARTFILE, tempname);
X			(void) sprintf(tempname,"%s.dir", ARTFILE);
X			(void) strcpy(rindex(NARTFILE, '.'), ".dir");
X			(void) rename(NARTFILE, tempname);
X		}
X#endif
X	}
X}
X
X#if defined(BSD4_2) || defined(LOCKF)
Xstatic int LockFd = -1;
X#endif
X
Xdolock()
X{
X	/* set up exclusive locking so inews does not run while expire does */
X#if defined(BSD4_2) || defined(LOCKF)
X	LockFd = open(ACTIVE, 2);
X# ifdef	LOCKF
X	if (lockf(LockFd, F_LOCK, 0) < 0)
X# else	/* BSD4_2 */
X	if (flock(LockFd, LOCK_EX) < 0)
X# endif	/* BSD4_2 */
X		xerror("Can't get lock for expire: %s", errmsg(errno));
X#else	/* !BSD4_2 && !LOCKF */
X	int i = 0;
X	sprintf(afline,"%s.lock", ACTIVE);
X	while (LINK(ACTIVE, afline) < 0 && errno == EEXIST) {
X		if (i++ > 5)
X			xerror("Can't get lock for expire");
X		sleep(i*2);
X	}
X#endif	/* !BSD4_2  && !LOCKF */
X}
X
Xrmlock()
X{
X#if defined(BSD4_2) || defined(LOCKF)
X	close(LockFd);
X#else
X	sprintf(bfr, "%s.lock", ACTIVE);
X	(void) UNLINK(bfr);
X#endif	/* !BSD4_2 */
X}
X
Xupdateactive()
X{
X	register char	*p1;
X	FILE	*ohfd, *nhfd;
X	DIR	*ngdirp = NULL;
X	static struct direct *ngdir;
X
X	if (verbose)
X		printf("updating active file %s\n", ACTIVE);
X	ohfd = xfopen(ACTIVE, "r");
X	nhfd = xfopen(NACTIVE, "w");
X	do {
X		long n;
X		long maxart, minart;
X		char cansub;
X		int gdsize, hassubs;
X		struct stat stbuf;
X
X		if (fgets(afline, BUFLEN, ohfd) == NULL)
X			continue;
X		if (sscanf(afline,"%s %ld %ld %*c%c",nbuf,&maxart, &minart,
X		    &cansub) < 4)
X			xerror("Active file corrupt");
X		if (verbose > 3)
X			printf("looking at group %s\n", nbuf);
X		if (!ngmatch(nbuf, ngpat)) {
X			if (fputs(afline, nhfd) == EOF)
X				xerror("active file write failed");
X			continue;
X		}
X		minart = 99999L;
X		/* Change a group name from a.b.c to a/b/c */
X		for (p1=nbuf; *p1; p1++)
X			if (*p1 == '.')
X				*p1 = '/';
X
X		hassubs = stat(nbuf, &stbuf) != 0 || stbuf.st_nlink != 2;
X		gdsize = strlen(nbuf);
X		if ((ngdirp = opendir(nbuf)) != NULL) {
X			while (ngdir = readdir(ngdirp)) {
X				nbuf[gdsize] = '/';
X				(void) strcpy(&nbuf[gdsize+1], ngdir->d_name);
X				/* We have to do a stat because of micro.6809 */
X				if (hassubs && (stat(nbuf, &stbuf) < 0 ||
X					!(stbuf.st_mode&S_IFREG)) )
X					continue;
X				n = atol(ngdir->d_name);
X				if (n > 0 && n < minart)
X					minart = n;
X				if (n > 0 && n > maxart)
X					maxart = n;
X			}
X			closedir(ngdirp);
X		}
X		afline[gdsize] = '\0';
X		if (minart > maxart)
X			minart = maxart;
X#ifdef USG
X		if (verbose > 4)
X			printf("\tmaxart = %5.5ld, minart = %5.5ld\n",
X				maxart, minart);
X		if (fprintf(nhfd,"%s %5.5ld %5.5ld %c\n", afline, maxart,
X			minart, cansub) == EOF)
X			xerror("Active file write failed");
X#else
X		if (verbose > 4)
X			printf("\tmaxart = %05ld, minart = %05ld\n",
X				maxart, minart);
X		if (fprintf(nhfd,"%s %05ld %05ld %c\n", afline, maxart,
X			minart, cansub) == EOF)
X			xerror("Active file write failed");
X#endif /* !USG */
X	} while (!feof(ohfd));
X	if (fclose(nhfd))
X		xerror("Active file write failed, %s", errmsg(errno));
X	(void) fclose(ohfd); /* this might unlock inews as a side effect */
X
X	(void) rename(ACTIVE, OACTIVE);
X	(void) rename(NACTIVE, ACTIVE);
X}
X
X/* Unlink (using unwound tail recursion) all the articles in 'artlist'. */
Xulall(artlist, hp)
Xchar	*artlist;
Xstruct hbuf *hp;
X{
X	register char	*p, *q;
X	int	last = 0;
X	char	newname[BUFLEN];
X	time_t	timep[2];
X	char *fn;
X
X	grpsleft[0] = '\0';
X	do {
X		if (verbose > 2)
X			printf("ulall '%s', '%s'\n", artlist, hp->subdate);
X		if (nohistory) {
X			last = 1;
X		} else {
X			while (*artlist == ' ' || *artlist == '\n' || *artlist == ',')
X				artlist++;
X			if (*artlist == '\0')
X				return;
X			p = index(artlist, ' ');
X			if (p == NULL) {
X				last = 1;
X				p = index(artlist, '\n');
X			}
X			if (p == NULL) {
X				last = 1;
X				fn = dirname(artlist);
X				if (UNLINK(fn) < 0 && errno != ENOENT)
X					perror(fn);
X				return;
X			}
X			if (p)
X				*p = 0;
X		}
X		strcpy(newname, artlist);
X		q = index(newname,'/');
X		if (q) {
X			*q++ = NGDELIM;
X			*q = '\0';
X		} else {
X			q = index(newname, '\0');
X			if (q == artlist)		/* null -> the end */
X				return;
X			/* should be impossible to get here */
X		}
X		fn = dirname(artlist);
X		if (ngmatch(newname, ngpat)) {
X			if (doarchive){
X				if (ngmatch(newname, arpat)) {
X					q = fn + strlen(SPOOL) + 1;
X					(void) sprintf(newname, "%s/%s", OLDNEWS, q);
X					if (verbose)
X						printf("link %s to %s\n", fn, newname);
X					if (LINK(fn, newname) == -1) {
X						if (mkparents(newname) == 0)
X							if (LINK(fn, newname) == -1)
X								fcopy(fn, newname);
X					}
X					timep[0] = timep[1] = cgtdate(hp->subdate);
X					(void) utime(newname, timep);
X				}
X			}
X			if (verbose)
X				printf("unlink %s\n", fn);
X			if (UNLINK(fn) < 0 && errno != ENOENT)
X				perror(fn);
X		} else {
X			if (verbose > 3)
X				printf("retain %s (%s)\n", hp->ident, fn);
X			strcat(grpsleft, artlist);
X			strcat(grpsleft, " ");
X		}
X		artlist = p + 1;
X	} while (!last);
X}
X
Xfcopy(fn, newname)
Xchar *fn, *newname;
X{
X	int f1, f2;
X	int r;
X	char buf[BUFSIZ];
X	f1 = open(fn, 0);
X	if (f1 < 0)
X		return -1;
X	f2 = open(newname, 1);
X	if (f2 < 0) {
X		if (errno == ENOENT) {
X			f2 = creat(newname,0644);
X			if (f2 < 0) {
X				close(f1);
X				return -1;
X			}
X		} else {
X			close(f1);
X			return -1;
X		}
X	}
X	while((r=read(f1, buf, BUFSIZ)) > 0)
X		write(f2, buf, r);
X	(void) close(f1);
X	(void) close(f2);
X	return 0;
X}
X
X/*
X * Count instances of c in s
X */
Xchrcnt(s, c)
Xregister char *s;
Xregister c;
X{
X	register n = 0;
X	register cc;
X
X	while (cc = *s++)
X		if (cc == c)
X			n++;
X	return n;
X}
X
X/*
X * If any parent directories of this dir don't exist, create them.
X */
Xmkparents(fullname)
Xchar *fullname;
X{
X	char buf[200];
X	register char *p;
X	int rc;
X
X	(void) strcpy(buf, fullname);
X	p = rindex(buf, '/');
X	if (p)
X		*p = '\0';
X	if (access(buf, 0) == 0)
X		return 0;
X	mkparents(buf);
X	if ((rc = mkdir(buf, 0755)) < 0)
X		perror("mkdir failed");
X	if (verbose)
X		printf("mkdir %s, rc %d\n", buf, rc);
X
X	return rc;
X}
X
X/*	Make sure this file is a legal article. */
Xislegal(fullname, path, name)
Xregister char *fullname;
Xregister char *path;
Xregister char *name;
X{
X	struct stat buffer;
X
X	(void) sprintf(fullname, "%s/%s", path, name);
X
X	/* make sure the article is numeric. */
X	while (*name != '\0')
X		if (!isascii(*name) || !isdigit(*name))
X			return 0;
X		else
X			name++;
X
X	/*  Now make sure we don't have a group like net.micro.432,
X	 *  which is numeric but not a regular file -- i.e., check
X	 *  for being a regular file.
X	 */
X	if ((stat(fullname, &buffer) == 0) &&
X		((buffer.st_mode & S_IFMT) == S_IFREG)) {
X		/* Now that we found a legal group in a/b/c/4
X		   notation, switch it to a.b.c/4 notation.  */
X		for (name = fullname; name != NULL && *name != '\0'; name++)
X			if (*name == '/' && name != rindex (name, '/'))
X				*name = '.';
X
X			return 1;
X	}
X	return 0;
X}
X
X#ifdef DBM
X/*
X * This is taken mostly intact from ../cvt/cvt.hist.c and is used at the
X * end by the options that make a new history file.
X * Routine to convert history file to dbm file.  The old 3 field
X * history file is still kept there, because we need it for expire
X * and for a human readable copy.  But we keep a dbm hashed copy
X * around by message ID so we can answer the yes/no question "have
X * we already seen this message".  The content is the ftell offset
X * into the real history file when we get the article - you can't
X * really do much with this because the file gets compacted.
X */
X
XFILE *fd;
X
Xchar namebuf[BUFSIZ];
Xchar lb[BUFSIZ];
X
Xrebuilddbm()
X{
X	register char *p;
X	long fpos;
X
X	(void) sprintf(namebuf, "%s.dir", ARTFILE);
X	(void) close(creat(namebuf, 0666));
X	(void) sprintf(namebuf, "%s.pag", ARTFILE);
X	(void) close(creat(namebuf, 0666));
X	(void) sprintf(namebuf, "%s", ARTFILE);
X
X	fd = fopen(namebuf, "r");
X	if (fd == NULL) {
X		perror(namebuf);
X		xxit(2);
X	}
X
X	initdbm(namebuf);
X	while (fpos=ftell(fd), fgets(lb, BUFSIZ, fd) != NULL) {
X		p = index(lb, '\t');
X		if (p)
X			*p = 0;
X		remember(lb, fpos);
X	}
X}
X
Xremember(article, fileoff)
Xregister char *article;
Xlong fileoff;
X{
X	datum	lhs, rhs;
X
X	lcase(article);
X	lhs.dptr = article;
X	lhs.dsize = strlen(article) + 1;
X	rhs.dptr = (char *) &fileoff;
X	rhs.dsize = sizeof fileoff;
X
X	if (verbose > 5)
X		printf("remember: %s @ %ld\n", article, fileoff);
X	if (store(lhs, rhs) < 0)
X		xerror("dbm store failed");
X}
X#else
X/*
X * Open the next history subdirectory file
X */
X
XFILE *nexthistfile(ofp)
XFILE *ofp;
X{
X	static int histfilecounter = -1;
X
X	if (ofp)
X		fclose(ofp);
X	do {
X		if (++histfilecounter > 9)
X			return NULL;
X		sprintf(bfr, "%s.d/%d", ARTFILE, histfilecounter);
X		if (verbose > 3)
X			printf("reading history file %s\n", bfr);
X		ofp = xfopen(bfr, "r");
X	} while (ofp == NULL);
X	return ofp;
X}
X
X/*
X * Rebuild the history subdirectory from LIBDIR/history
X */
Xrebuildhistorydir()
X{
X	char fn[BUFLEN], ofn[BUFLEN];
X	register int i;
X	FILE *subfd[10], *ohfd;
X
X	/* rebuild history subfiles */
X	(void) sprintf(fn, "%s.od", ARTFILE);
X	if (access(fn,0) != 0)
X		(void) mkdir(fn, 0755);
X	(void) sprintf(fn, "%s.d", ARTFILE);
X	if (verbose)
X		printf("Rebuilding history subfile directory %s.\n", fn);
X	if (access(fn,0) != 0)
X		(void) mkdir(fn, 0755);
X	for (i = 0; i < 10; i++) {
X		(void) sprintf(fn, "%s.d/%c", ARTFILE, i + '0');
X		(void) sprintf(ofn, "%s.od/%c", ARTFILE, i + '0');
X		(void) rename(fn, ofn);
X		close(creat(fn, 0644));
X		subfd[i] = xfopen(fn, "w+");
X	}
X	ohfd = xfopen(ARTFILE, "r");
X	while (fgets(fn, BUFLEN, ohfd) != NULL) {
X		i = findhfdigit(fn) - '0';
X		fputs(fn, subfd[i]);
X	}
X	(void) fclose(ohfd);
X	for (i = 0; i < 10; i++)
X		if (ferror(subfd[i]) || fclose(subfd[i]))
X			xerror("History subfile write");
X	(void) UNLINK(ARTFILE);
X}
X#endif /* !DBM */
X
Xxxit(i)
X{
X	rmlock();
X	exit(i);
X}
END_OF_FILE
if test 28180 -ne `wc -c <'expire.c'`; then
    echo shar: \"'expire.c'\" unpacked with wrong size!
fi
# end of 'expire.c'
fi
echo shar: End of shell archive.
exit 0
-- 
		"Zeta Microcomputer Software"
ACSnet:    nick@ultima.cs.uts.oz
UUCP:      ...!uunet!munnari!ultima.cs.uts.oz!nick
Fidonet:   Nick Andrew on 3:713/602 (Zeta)

nick@ultima.cs.uts.oz (Nick Andrew) (12/07/89)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  postnews.c getdate.s.uu
# Wrapped by nick@nswitgould on Thu Dec  7 22:40:11 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'postnews.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'postnews.c'\"
else
echo shar: Extracting \"'postnews.c'\" \(26497 characters\)
sed "s/^X//" >'postnews.c' <<'END_OF_FILE'
X/*
X * This software is Copyright (c) 1986 by Rick Adams.
X *
X * Permission is hereby granted to copy, reproduce, redistribute or
X * otherwise use this software as long as: there is no monetary
X * profit gained specifically from the use or reproduction or this
X * software, it is not sold, rented, traded or otherwise marketed, and
X * this copyright notice is included prominently in any copy
X * made.
X *
X * The author make no claims as to the fitness or correctness of
X * this software for any use whatsoever, and it is provided as is. 
X * Any use of this software is at the user's own risk.
X *
X * Postnews: post a news message to Usenet.  This C version replaces a shell
X * script, and does more intelligent prompting and filtering than possible
X * in a shell script.
X */
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)postnews.c	1.31	3/21/87";
X#endif /* SCCSID */
X
X#include "params.h"
X
X#define APPEND 1
X#define REPLACE 2
X
Xextern char *MAILPARSER;
X
Xchar tempfname[50];		/* file name used for making article */
Xchar original[BUFLEN];		/* file name of original, used in followup */
Xchar homedir[BUFLEN];		/* HOME environment setting */
Xchar user[SBUFLEN];		/* user name */
Xchar ccname[BUFLEN];		/* file name for article copy */
X
X/* article header information */
Xchar subject[BUFLEN];
Xchar distribution[BUFLEN];
Xchar references[BUFLEN];
Xchar newsgroups[BUFLEN];
Xchar isfrom[BUFLEN];
Xchar msgid[BUFLEN];
Xchar keywords[BUFLEN];
Xchar summary[BUFLEN];
X
Xchar ngsep[] = { NGDELIM, '\0' };	/* == "," */
X
Xchar *Progname = "postnews";		/* for xerror */
X
Xtime_t fmodtime;
Xchar pobuf[BUFLEN];
X
X#define MAXDISTR	16
Xstruct distr {
X	char abbr[24];
X	char descr[128];
X} distr[MAXDISTR];
X
Xchar def_distr[24] = "";	/* default distribution */
XFILE *xfopen();
X
Xmain(argc, argv)
Xchar *argv[];
X{
X	register int c;
X
X	init();
X
X	if (argc == 2) {
X		if (!prefix(argv[1], SPOOL))
X			xerror("Can only followup to articles in %s", SPOOL);
X		followup(argv[1]);
X		(void) strcpy(original, argv[1]);
X	} else
X	if (askyes("Is this message in response to some other message? ","no")) {
X		char ng[BUFLEN], num[BUFLEN];
X		long i, j, lastnum;
X		register char *p;
X		int fd, dir;
X		char canpost;
X
X		getpr("In what newsgroup was the article posted? ",ng);
X		if (!valid_ng(ng, &i, &j, &canpost))
X			if (canpost == 'i' )
X				byebye("There is no such newsgroup.");
X			else if (canpost == 'n')
X				byebye("You are not allowed to post to that group.");
X
X		printf("Valid article numbers are from %ld to %ld\n", j, i);
X		lastnum = i + 1;
X		dir = -1;
X
X		for(;;) {
X			getpr("\nWhat was the article number? ", num);
X			switch(num[0]) {
X			case '+':
X				dir = 1;
X				sprintf(num, "%ld", lastnum + 1);
X				break;
X			case '-':
X				dir = -1;
X				/* no break */
X			case '\0':
X				sprintf(num, "%ld", lastnum + dir);
X				break;
X			}
X			(void) sprintf(original, "%s/%s", SPOOL, ng);
X			for (p=original+strlen(SPOOL)+1; *p ;++p)
X				if (*p == '.')
X					*p = '/';
X			(void) strcat(original, "/");
X			(void) strcat(original, num);
X
X			if ((fd=open(original,0)) >= 0) {
X				(void) close(fd);
X				printf("\narticle %s\n", original);
X				if (article_line(original, "From: ", pobuf))
X					printf("%s\n", pobuf);
X				if (article_line(original, "Subject: ", pobuf))
X					printf("%s\n", pobuf);
X				if (askyes("Is this the one you want? ", "n"))
X					break;
X			} else
X				printf("I can't find that article.\n");
X			lastnum = atol(num);
X		}
X
X		followup(original);
X	} else {
X		do {
X			getpr("Subject: ", subject);
X			if (*subject == '?') {
Xprintf("People read the subject line to learn what your article is about.\n");
Xprintf("You want it to do the same job as a newspaper headline.\n");
Xprintf("So type in something both brief and descriptive.\n");
X				*subject = '\0';
X			}
X		} while (*subject == '\0');
X		getpr("Keywords: ", keywords);
X
X		while (!get_newsgroup())
X			;
X		get_distribution((char *)0);
X	}
X
X	if (pre_checks())
X		exit(1);
X
X	prep_article();
X	c = 'e';
X	for (;;) {
X		if (c == 'e') {
X			edit_article();
X			post_checks();
X		}
X		do {
X			do {
X				getpr("\nWhat now?  [send, edit, list, quit, write, append] ", pobuf);
X				c = pobuf[0];
X			} while (c == '\0');
X			if (isupper(c))
X				c = tolower(c);
X			if (c == 'q') {
X				(void) UNLINK(tempfname);
X				exit(1);
X			}
X			if (c == 'l') {
X				char *pager = getenv("PAGER");
X				char lbuf[BUFLEN];
X				if (pager == NULL || *pager == '\0') {
X#ifdef PAGE
X# ifdef IHCC
X					(void) sprintf(lbuf,"%s/bin/%s", logdir(HOME), PAGE);
X# else /* !IHCC */
X					(void) strcpy(lbuf, PAGE);
X# endif /* !IHCC */
X					pager = lbuf;
X#else /* !PAGE */
X					pager = "cat";
X#endif /* !PAGE */
X				}
X				sprintf(pobuf, "exec %s %s", pager, tempfname);
X				(void)  system(pobuf);
X			}
X			if (c == 'w' || c == 'a') {
X				register int ifd, ofd, nbytes;
X				char iobuf[BUFSIZ];
X				char fname[BUFLEN];
X				getpr("Filename? ", fname);
X				if (fname[0] == '\0')
X					continue;
X				ofd = (c == 'w') ? creat(fname, 0666)
X						 : open(fname, 2);
X				if (ofd < 0)
X					perror(fname);
X				else {
X					if (c == 'a')
X						(void) lseek(ofd, 0L, 2);
X					ifd = open(tempfname, 0);
X					if (ifd < 0)
X						xerror("Can't reopen %s\n", tempfname);
X					while ((nbytes = read(ifd, iobuf, BUFSIZ)) > 0 )
X						write(ofd, iobuf, nbytes);
X					close(ofd);
X					close(ifd);
X				}
X			}
X		} while (!index("eps", c));
X		if (c != 'e')
X			post_article(); /* will exit if posted successfully */
X	};
X}
X
X/*
X * Find out the topic of interest.
X */
Xget_newsgroup()
X{
X	int n;
X	long i;
X	char canpost;
X	static int first = 1;
X
X	printf("Newsgroups (enter one at a time, end with a blank line):\n");
X	if (first) {
X		printf("\nThe most relevant newsgroup should be the first, you should\n");
X		printf("add others only if your article really MUST be read by people\n");
X		printf("who choose not to read the appropriate group for your article.\n");
X		printf("But DO use multiple newsgroups rather than posting many times.\n\n");
X		first = 0;
X	}
X	printf("For a list of newsgroups, type ?\n");
X	n = 0;
X	newsgroups[0] = '\0';
X
X	for(;;) {
X		getpr("> ", pobuf);
X		if (pobuf[0] == '\0')
X			if (n == 0)
X				return FALSE;
X			else
X				return TRUE;
X		if (pobuf[0] == '?'){
X			char *pager = getenv("PAGER");
X			char lbuf[BUFLEN];
X			if (pager == NULL) {
X#ifdef PAGE
X# ifdef IHCC
X				(void) sprintf(lbuf,"%s/bin/%s", logdir(HOME), PAGE);
X# else /* !IHCC */
X				(void) strcpy(lbuf, PAGE);
X# endif /* !IHCC */
X				pager = lbuf;
X#else /* !PAGE */
X				pager = "cat";
X#endif /* !PAGE */
X			}
X			printf("These are the currently active groups:\n");
X			(void) fflush(stdout);
X			sprintf(pobuf, "exec %s %s/newsgroups", pager, LIB);
X			(void) system(pobuf);
X			continue;
X		}
X		if (valid_ng(pobuf, &i, &i, &canpost)) {
X			if (n++ != 0)
X				(void) strcat(newsgroups, ngsep);
X			(void) strcat(newsgroups, pobuf);
X		} else {
X			if (canpost == 'n')
X				printf("You are not allowed to post to %s\n",
X					pobuf);
X			else if (canpost == 'i')
X				printf("%s is not a valid newsgroup.\n", pobuf);
X		}
X	}
X}
X
X/*
X * Find out how widely the author wants the message distributed.
X */
Xget_distribution(deflt)
X	char *deflt;
X{
X	register int i;
X	register char *r;
X	char def[BUFLEN];
X	char *lastgroup;
X
X	lastgroup = newsgroups;
X	(void) strcpy(def, newsgroups);
X	r = index(def, NGDELIM);
X	if (r)
X		*r = '\0';
X	r = index(def, '.');
X	if (r) {
X		*r = '\0';
X		if (strcmp(def, "net") == 0)
X			(void) strcpy(def, "world");
X	} else {
X		distribution[0] = '\0';
X		return;
X	}
X
X	if (strcmp(def, "to") == 0) {
X		/*
X		 * This only works if "to.xx" is the first (or only)
X		 * newsgroup, but it usually is ..
X		 * Perhaps we should make the distribution be "to.xxx" ??
X		 */
X		distribution[0] = '\0';
X		return;		/* He's probably testing something */
X	}
X	if (deflt != (char *)0)
X		(void) strcpy(def, deflt);
X	if (ngmatch("misc.test", newsgroups))
X		(void) strcpy(def, "local");
X	for (i=0; distr[i].abbr[0]; i++) {
X		if (strcmp(distr[i].abbr, def) == 0)
X			break;
X	}
X	if (distr[i].abbr[0] == '\0')
X		strcpy(def, def_distr);
X	for(;;) {
X		do {
X			(void) sprintf(pobuf, "Distribution (default='%s', '?' for help) : ", def);
X			getpr(pobuf, distribution);
X			if (distribution[0] == '\0') {
X				if (strcmp(def, "*None*") == 0)
X					printf("You must enter a distribution, '?' for help.\n");
X				(void) strcpy(distribution, def);
X			}
X		} while (strcmp(distribution, "*None*") == 0);
X
X		/* Did the user ask for help? */
X		if (distribution[0] == '?') {
X			printf("How widely should your article be distributed?\n\n");
X			for (i=0; distr[i].abbr[0]; i++)
X				printf("%s\t%s\n", distr[i].abbr, distr[i].descr);
X			printf("\nEnter the word that specifies the distribution that you require.\n");
X			continue;
X		}
X
X		/* Check that it's a proper distribution */
X		for (i=0; distr[i].abbr[0]; i++) {
X			if (strncmp(distr[i].abbr, distribution, sizeof(distr[0].abbr)) == 0) {
X				return;
X			}
X		}
X		if (strcmp(distribution, def) != 0)
X			printf("Type ? for help.\n");
X		else {
X			int once = TRUE;
X
X			do {
X				r = lastgroup;
X				while (r = index(r, NGDELIM))
X					if (!prefix(++r, def))
X						break;
X				if (r == NULL) {
X					/*
X					 * no newsgroups are distribution
X					 * names, and user simply will
X					 * not type a valid distribution,
X					 * assume that the default is OK.
X					 */
X					distribution[0] = '\0';
X					return;
X				}
X				lastgroup = r;
X				if (once)
X					printf("Sorry, '%s' is an unknown distribution.  Type ? for help.\n", def);
X				once = FALSE;
X				strcpy(def, r);
X				r = index(def, NGDELIM);
X				if (r)
X					*r = '\0';
X				r = index(def, '.');
X			} while (r == NULL);
X			*r = '\0';
X			if (strcmp(def, "net") == 0)
X				strcpy(def, "world");
X		}
X	}
X}
X
X/*
X * Do sanity checks before the author types in the message.
X */
Xpre_checks()
X{
X	if (recording(newsgroups))
X		return 1;
X	return 0;
X}
X
X/*
X * Set up the temp file with headers.
X */
Xprep_article()
X{
X	FILE *tf, *of;
X	struct stat stbuf;
X
X	(void) strcpy(tempfname, "/tmp/postXXXXXX");
X	(void) mktemp(tempfname);
X
X	/* insert a header */
X	tf = xfopen(tempfname, "w");
X	fprintf(tf, "Subject: %s\n", subject);
X	fprintf(tf, "Newsgroups: %s\n", newsgroups);
X	if (distribution[0] != '\0' && strcmp(distribution, "world"))
X		fprintf(tf, "Distribution: %s\n", distribution);
X
X	if (keywords[0] != '\0')
X		fprintf(tf, "Keywords: %s\n", keywords);
X	if (summary[0] != '\0')
X		fprintf(tf, "Summary: %s\n", summary);
X
X	if (references[0] != '\0') {
X		fprintf(tf, "References: %s\n\n", references);
X
X		if (askyes("Do you want to include a copy of the article? ", "no")){
X			of = xfopen(original, "r");
X			while (fgets(pobuf, BUFSIZ, of) != NULL)
X				if (pobuf[0] == '\n')	/* skip headers */
X					break;
X			fprintf(tf, "In article %s, %s writes:\n", msgid, isfrom);
X			while (fgets(pobuf, BUFSIZ, of) != NULL)
X				fprintf(tf, "> %s", pobuf);
X			(void) fclose(of);
X			printf("OK, but please edit it to suppress unnecessary verbiage, signatures, etc.\n");
X			(void) fflush(stdout);
X		}
X	}
X
X	fprintf(tf, "\n\n");
X	(void) fflush(tf);
X	(void) fstat(fileno(tf), &stbuf);
X	fmodtime = stbuf.st_mtime;
X	(void) fclose(tf);
X}
X
Xedit_article()
X{
X	register char *p;
X	char *editor;
X	char *endflag = "";
X	char *getenv();
X
X	/* edit the file */
X	editor = getenv("EDITOR");
X	if (editor == NULL)
X		editor = DFTEDITOR;
X
X	p = editor + strlen(editor) - 2;
X	if (strcmp(p, "vi") == 0)
X		endflag = "+";
X
X	(void) sprintf(pobuf, "A=%s;export A;exec %s %s %s",
X		original, editor, endflag, tempfname);
X
X	(void) system(pobuf);
X}
X
X/*
X * Do sanity checks after the author has typed in the message.
X */
Xpost_checks()
X{
X	char group[BUFLEN];
X	register char *c, *p;
X	struct stat stbuf;
X
X	if (stat(tempfname, &stbuf) < 0) {
X		printf("File deleted - no message posted.\n");
X		(void) UNLINK(tempfname);
X		exit(1);
X	}
X	if (stbuf.st_size < 5) {
X		printf("File too small (<5 characters) - no message posted.\n");
X		(void) UNLINK(tempfname);
X		exit(1);
X	}
X
X	if (stbuf.st_mtime == fmodtime) {
X		printf("File not modified - no message posted.\n");
X		(void) UNLINK(tempfname);
X		exit(1);
X	}
X
X	/*
X	 * Is this the right number?  Most of the headers are yet to be added
X	 */
X	if (stbuf.st_size > 64000) {
X		printf("\nYour message will probably be truncated when it\n");
X		printf("passes through a notesfile site, since it is\n");
X		printf("greater than 64000 characters.\n\n");
X		if (!askyes("Do you still want to post it? ","")) {
X			sprintf(ccname, "%s/dead.article", homedir);
X			save_article();
X			(void) UNLINK(tempfname);
X			exit(1);
X		}
X	}
X
X	/*
X	 * get the newsgroups from the edited article, in
X	 * case they were altered in the editor
X	 */
X	if (!article_line(tempfname, "Newsgroups: ", group)) {
X  nogroups:
X		printf("Not sending to any newsgroups - no message posted\n");
X		sprintf(ccname, "%s/dead.article", homedir);
X		save_article();
X		(void) UNLINK(tempfname);
X		exit(1);
X	}
X	c = &group[11];
X	while (*c == ' ' || *c == '\t')
X		c++;
X	if (*c == '\0')
X		goto nogroups;
X	for (p = newsgroups; *c; c++)	/* copy to newsgroups, w/o blanks */
X		if (*c != ' ' && *c != '\t')
X			*p++ = *c;
X	*p = '\0';
X
X	/* Sanity checks for certain newsgroups */
X	if (ngmatch(newsgroups, "all.wanted") && ngmatch(distribution,"world,na,usa,att,btl,eunet,aus")) {
X		printf("Is your message something that might go in your local\n");
X		printf("newspaper, for example a used car ad, or an apartment\n");
X		printf("for rent? ");
X		if (askyes("","")) {
X			printf("It's pointless to distribute your article widely, since\n");
X			printf("people more than a hundred miles away won't be interested.\n");
X			printf("Please use a more restricted distribution.\n");
X			get_distribution("*None*");
X			modify_article(tempfname, "Distribution: ", distribution,REPLACE);
X		}
X	}
X
X	if (ngmatch(newsgroups, "rec.humor,!rec.humor.d")) {
X		if (askyes("Could this be offensive to anyone? ","")) {
X			getpr("Whom might it offend? ", group);
X			(void) sprintf(pobuf," - offensive to %s (rot 13)",group);
X			modify_article(tempfname, "Subject: ", pobuf, APPEND);
X			encode(tempfname);
X		}
X	}
X
X	if (ngmatch(newsgroups, "comp.sources.all,!comp.sources.wanted")) {
X		if (!article_line(tempfname, "Subject: ", group)) {
X  nosubj:
X			printf("There seems to be no subject for this article.\n");
X			getpr("Subject: ", subject);
X			modify_article(tempfname, "Subject: ", subject, REPLACE);
X		} else {
X			c = &group[8];
X			while (*c == ' ' || *c == '\t')
X				c++;
X			if (*c == '\0')
X				goto nosubj;
X			strcpy(subject, c);
X		}
X		if (ngmatch(newsgroups, "all.wanted") || iswanted(subject)) {
X			printf("Requests for sources should not be posted to any of\n");
X			printf("the comp.sources newsgroups, please post such requests\n");
X			printf("to comp.sources.wanted only.     Please reenter the newsgroups.\n\n");
X			while (!get_newsgroup())
X				;
X			modify_article(tempfname, "Newsgroups: ", newsgroups, REPLACE);
X		}
X		if (ngmatch(newsgroups, "comp.sources.all")) {
X			if (!ngmatch(newsgroups, "comp.sources.wanted") &&
X			    stbuf.st_size < (4*1024)) {
X				printf("Your article seems rather small to be a source distribution.\n");
X				if (!askyes("Are you certain that this is really source? ", "")) {
X
X					while (!get_newsgroup())
X						;
X					modify_article(tempfname, "Newsgroups: ", newsgroups, REPLACE);
X				}
X			}
X			if (index(newsgroups, NGDELIM)) {
X				printf("Sources should be posted to one newsgroup only.\n");
X				printf("Please pick the most appropriate group for your article.\n\n");
X				while (!get_newsgroup())
X					;
X				modify_article(tempfname, "Newsgroups: ", newsgroups, REPLACE);
X			}
X		}
X	}
X}
X
Xiswanted(str)
Xregister char *str;
X{
X	while (*str == ' ')
X		str++;
X
X	if (prefix(str, "Re:"))
X		return (FALSE);
X
X	if (isin(str, " wanted ") || isin(str, " can any") ||
X	    isin(str, " need ") || isin(str, " please ") || isin(str, " help ")
X	    || isin(str, " looking ") || index(str, '?'))
X		return (TRUE);
X
X	return (FALSE);
X}
X
Xisin(str, words)
Xregister char *str, *words;
X{
X	register char *p;
X	register sc, wc;
X
X	p = words;
X	while (sc = *str++) {
X		if ((wc = *p++) == '\0')
X			return (TRUE);
X		if (wc == ' ') {
X			if (index(".,!?-; \t\n", sc))
X				continue;
X		} else {
X			if (isupper(wc))
X				wc = tolower(wc);
X			if (isupper(sc))
X				sc = tolower(sc);
X			if (wc == sc)
X				continue;
X		}
X		str -= p - words - 1;
X		p = words;
X	}
X	if (*p == '\0')
X		return (TRUE);
X	return (FALSE);
X}
X
X/*
X * Save a copy of the article in the users NEWSARCHIVE directory.
X */
Xsave_article()
X{
X	FILE *in, *out;
X	int c;
X	time_t timenow, time();
X	char *today, *ctime();
X	struct stat stbuf;
X	char filename[BUFLEN];
X
X	if (stat(ccname, &stbuf) == 0 && (stbuf.st_mode&S_IFMT) == S_IFDIR) {
X		/*
X		 * It would be much nicer here to write articles
X		 * in MH format (numbered files, in rfc822 format)
X		 *
X		 * one day ..
X		 */
X		(void) sprintf(filename, "%s/author_copy", ccname);
X		(void) strcpy(ccname, filename);
X	}
X	in = xfopen(tempfname, "r");
X	out = xfopen(ccname, "a");
X	timenow = time((time_t)0);
X	today = ctime(&timenow);
X	fprintf(out,"From postnews %s",today);
X	while ((c=getc(in)) != EOF)
X		putc(c, out);
X	putc('\n', out);
X	(void) fclose(in);
X	(void) fclose(out);
X}
X
X/*
X * Post the article to the net.
X */
Xpost_article()
X{
X	int status;
X
X	printf("Posting article...\n");
X	fflush(stdout);
X	(void) sprintf(pobuf, "exec %s/%s -h < %s", LIB, "inews", tempfname);
X	status = system(pobuf);
X
X	if (status) {
X		printf("Article not posted - exit status %d\n", status);
X		return;
X	} else
X		printf("Article posted successfully.\n");
X
X	if (ccname[0]) {
X		printf("A copy has been saved in %s\n", ccname);
X		save_article();
X	}
X
X	(void) UNLINK(tempfname);
X	exit(0);
X}
X
X/*
X * Initialization.
X */
Xinit()
X{
X	FILE *fd;
X	register char *p;
X	int i;
X	char *getenv();
X	struct passwd *pw;
X
X	references[0] = '\0';
X	distribution[0] = '\0';
X
X	uid = getuid();
X	pw = getpwuid(uid);
X	if (pw == NULL) {
X		fprintf(stderr,"You're not in /etc/passwd\n");
X		exit(1);
X	}
X	p = getenv("HOME");
X	if (p == NULL) {
X		p = getenv("LOGDIR");
X		if (p == NULL) 
X			p = pw->pw_dir;
X	}
X	(void) strncpy(user, pw->pw_name, SBUFLEN);
X	(void) strcpy(homedir, p);
X
X	p = getenv("NEWSARCHIVE");
X	if (p != NULL) {
X		if (*p == '\0')
X			sprintf(ccname, "%s/author_copy", homedir);
X		else
X			strcpy(ccname, p);
X	}
X
X	pathinit();
X	(void) sprintf(pobuf, "%s/%s", LIB, "distributions");
X	fd = xfopen(pobuf, "r");
X	for (i=0; i < MAXDISTR; i++) {
X		if (fscanf(fd, "%s %[^\n]", distr[i].abbr, distr[i].descr)
X			!= 2)
X			break;
X		if (strcmp(distr[i].abbr, "default") == 0)
X			strcpy(def_distr, distr[i--].descr);
X	}
X	(void) fclose(fd);
X	distr[i].abbr[0] = '\0';
X	if (def_distr[0] == '\0')
X		strcpy(def_distr, "world");	/* maybe "local" is better? */
X}
X
X/*
X * Get a yes or no answer to a question.	A default may be used.
X */
Xaskyes(msg, def)
Xchar *msg, *def;
X{
X	for(;;) {
X		printf("%s", msg);
X		pobuf[0] = 0;
X		(void) gets(pobuf);
X		switch(pobuf[0]) {
X		case 'y':
X		case 'Y':
X			return TRUE;
X		case 'n':
X		case 'N':
X			return FALSE;
X		case '\0':
X			switch(*def) {
X			case 'y':
X			case 'Y':
X				return TRUE;
X			case 'n':
X			case 'N':
X				return FALSE;
X			}
X		default:
X			printf("Please answer yes or no.\n");
X		}
X	}
X}
X
X/*
X * Get a character string into pobuf, using prompt msg.
X */
Xgetpr(msg, bptr)
Xchar *msg, *bptr;
X{
X	static int numeof = 0;
X	printf("%s", msg);
X	(void) gets(bptr);
X	(void) nstrip(bptr);
X	if (feof(stdin)) {
X		if (numeof++ > 3) {
X			fprintf(stderr,"Too many EOFs\n");
X			exit(1);
X		}
X		clearerr(stdin);
X	}
X}
X
Xbyebye(mesg)
Xchar *mesg;
X{
X	printf("%s\n", mesg);
X	exit(1);
X}
X
X/*
X * make a modification to the header of an article
X *
X *	 fname -- name of article
X *	 field -- header field to modify
X *	 line	-- modification line
X *	 how	 -- APPEND or REPLACE
X *
X * example:
X *	 modify_article("/tmp/article" , "Subject:" , "new subject" , REPLACE);
X *
X *
X */
Xmodify_article(fname, field, line, how)
Xchar *fname, *field, *line;
X{
X	FILE *fpart, *fptmp;
X	char *temp2fname = "/tmp/postXXXXXX";
X	char lbfr[BUFLEN];
X	register found = FALSE;
X
X	mktemp(temp2fname);
X
X	fptmp = xfopen(temp2fname, "w");
X	fpart = xfopen(fname, "r");
X
X	while (fgets(lbfr, BUFLEN, fpart) != NULL) {
X		if (prefix(lbfr, field)) {
X			found = TRUE;
X			(void) nstrip(lbfr);
X			if (how == APPEND) {
X				/* append to current field */
X				(void) strcat(lbfr, line);
X				(void) strcat(lbfr, "\n");
X			} else
X				/* replace current field */
X				(void) sprintf(lbfr, "%s%s\n", field, line);
X		}
X		(void) fputs(lbfr, fptmp);
X	}
X
X	fclose(fpart);
X	fclose(fptmp);
X
X	fptmp = xfopen(temp2fname, "r");
X	fpart = xfopen(fname, "w");
X
X	if (!found)
X		fprintf(fpart, "%s%s\n", field, line);
X	while (fgets(pobuf,BUFLEN,fptmp) != NULL)
X		(void) fputs(pobuf, fpart);
X
X	(void) fclose(fpart);
X	(void) fclose(fptmp);
X	(void) UNLINK(temp2fname);
X}
X
X
X/* verify that newsgroup exists, and get number of entries */
Xvalid_ng(ng, maxart, minart, canpost)
Xchar *ng;
Xlong *maxart, *minart;
Xchar *canpost;
X{
X	char ng_check[BUFLEN], ng_read[BUFLEN];
X	FILE *fp;
X
X	fp = xfopen(ACTIVE, "r");
X	while (fgets(ng_read, BUFLEN, fp) != NULL) {
X		switch (sscanf(ng_read, "%s %ld %ld %c", ng_check, maxart, minart, canpost)) {
X		case 2:
X			*minart = 1;
X			/* fall through */
X		case 3:
X			*canpost = 'y';
X			/* fall through */
X		case 4:
X			break;
X
X		default:
X			printf("Active file (%s) corrupted. ", ACTIVE);
X			byebye("Seek help!");
X		}
X			
X		if (strcmp(ng_check, ng) == 0) {
X			(void) fclose(fp);
X			if (*canpost != 'n') {
X#ifdef FASCIST
X				if (uid && uid != ROOTID && fascist(user, ng)) {
X					*canpost = 'n';
X					return FALSE;
X				}
X#endif /* FASCIST */
X				return TRUE;
X			} else
X				return FALSE;
X		}
X	}
X	*canpost = 'i';
X	*maxart = 0;
X	*minart = 0;
X	(void) fclose(fp);
X	return FALSE;
X}
X
X/* get the line specified by field from an article */
Xarticle_line(article, field, line)
Xchar *article, *field, *line;
X{
X	FILE *fp;
X	char *c;
X
X	fp = xfopen(article,"r");
X	while ((c=fgets(line,BUFLEN,fp)) != NULL && !prefix(line, field))
X		if (line[0] == '\n') {
X			c = NULL;
X			break;
X		}
X	(void) fclose(fp);
X	if (c != NULL) {
X		(void) nstrip(line);
X		return TRUE;
X	} else {
X		line[0] = '\0';
X		return FALSE;
X	}
X}
X
X/* get the header information for a followup article */
Xfollowup(baseart)
Xregister char *baseart;
X{
X	register char *p;
X
X	/* subject */
X	if (article_line(baseart, "Subject: ", pobuf)) {
X		p = pobuf+9;
X		for ( ; ; ) {
X			while (*p == ' ' || *p == '\t')
X				++p;
X			if ((*p != 'r' && *p != 'R') ||
X				(p[1] != 'e' && p[1] != 'E') ||
X				(p[2] != ':' && p[2] != ' '))
X					break;
X			p += 3;
X		}
X		(void) sprintf(subject, "Re: %s", p);
X	} else {
X		if (article_line(baseart, "From: ", pobuf))
X			(void) sprintf(subject, "Re: orphan response from %s", pobuf);
X		else
X			(void) strcpy(subject, "Re: orphan response");
X	}
X
X	/* newsgroup */
X	if (article_line(baseart, "Newsgroups: ", pobuf))
X		(void) strcpy(newsgroups, pobuf+12);
X	if (ngmatch(newsgroups, "misc.jobs")) {
X		printf("misc.jobs is for the direct posting of job announcements and requests.\n");
X		printf("it is not for discussion. You followup has been directed to misc.misc\n");
X		(void) strcpy(newsgroups,"misc.misc");
X	}
X
X	/* distribution */
X	if (article_line(baseart, "Distribution: ", pobuf))
X		(void) strcpy(distribution, pobuf+14);
X
X	/* references */
X	if (article_line(baseart, "References: ", pobuf)) {
X		register char *rcp;
X		(void) strcpy(references, pobuf+12);
X		(void) strcat(references, " ");
X		/* keep the number of references to a reasonable number */
X		rcp = rindex(references, ' '); /* Can not fail */
X		while ((int)(rcp - references) > 70) {
X			while (*--rcp != ' ')
X				;
X			rcp[1] = '\0';
X		}
X	}
X	if (article_line(baseart, "Message-ID: ", pobuf)) {
X		(void) strcat(references, pobuf+12);
X		(void) strcpy(msgid, pobuf+12);
X	}
X
X	if (article_line(baseart, "From: ", pobuf))
X		(void) strcpy(isfrom, pobuf+6);
X
X	if (article_line(baseart, "Keywords: ", pobuf))
X		(void) strcpy(keywords, pobuf+10);
X
X	if (article_line(baseart, "Followup-To: ", pobuf)) {
X		(void) strcpy(newsgroups, pobuf+13);
X		if (strcmp(newsgroups, "poster") == 0)
X			byebye("Mail followups directly to poster.");
X	}
X
X	get_summary();
X}
X
Xget_summary()
X{
X	register char *p;
X	register i;
X
X	printf("Please enter a short summary of your contribution to the discussion\n");
X	printf("Just one or two lines ...   (end with a blank line)\n");
X	p = summary;
X	for (i = 0; i < 3; i++) {	/* 3 * 80 < 256, should be safe .. */
X		getpr(">\t", p);
X		if (*p == '\0')
X			break;
X		p = index(p, '\0');
X		(void) strcpy(p, "\n\t ");
X		p += 3;
X	}
X	if (p > summary)
X		p[-3] = '\0';
X}
X
Xencode(article)
Xchar *article;
X{
X	FILE *fpart, *fphead, *fpcoded;
X	char *headerfile = "/tmp/pheadXXXXXX";
X	char *codedfile = "/tmp/pcodeXXXXXX";
X
X	(void) mktemp(headerfile);
X	(void) mktemp(codedfile);
X
X	fpart = xfopen(article, "r");
X
X	/* place article header in "headerfile" file */
X	fphead = xfopen(headerfile, "w");
X	while (fgets(pobuf, BUFLEN, fpart) != NULL) {
X		(void) fputs(pobuf, fphead);
X		if (pobuf[0] == '\n')
X			break;
X	}
X	(void) fclose(fphead);
X
X	/* place article body in "codedfile" file */
X	fpcoded = xfopen(codedfile, "w");
X	while (fgets(pobuf, BUFLEN, fpart) != NULL)
X		(void) fputs(pobuf, fpcoded);
X	(void) fclose(fpcoded);
X	(void) fclose(fpart);
X
X	/* encode body and put back together with header */
X	(void) rename(headerfile, article);
X
X	(void) sprintf(pobuf,"exec %s/%s 13 < %s >> %s\n", LIB, "caesar", codedfile, article);
X	printf("Encoding article -- please stand by\n");
X	(void) fflush(stdout);
X	if (system(pobuf)) {
X		printf("encoding failed");
X		exit(2);
X	}
X	(void) UNLINK(codedfile);
X}
X
X
X/*
X * Print a recorded message warning the poor luser what he is doing
X * and demand that he understands it before proceeding.  Only do
X * this for newsgroups listed in LIBDIR/recording.
X */
Xrecording(ngrps)
Xchar *ngrps;
X{
X	char recbuf[BUFLEN];
X	FILE *fd;
X	char nglist[BUFLEN], fname[BUFLEN];
X	int  c, n, yes, retval = 0;
X
X	(void) sprintf(recbuf, "%s/%s", LIB, "recording");
X	fd = fopen(recbuf, "r");
X	if (fd == NULL)
X		return 0;
X	while ((fgets(recbuf, sizeof recbuf, fd)) != NULL) {
X		(void) sscanf(recbuf, "%s %s", nglist, fname);
X		if (ngmatch(ngrps, nglist)) {
X			(void) fclose(fd);
X			if (fname[0] == '/')
X				(void) strcpy(recbuf, fname);
X			else
X				(void) sprintf(recbuf, "%s/%s", LIB, fname);
X			fd = fopen(recbuf, "r");
X			if (fd == NULL)
X				return 0;
X			while ((c = getc(fd)) != EOF)
X				putc(c, stderr);
X			fprintf(stderr, "Do you understand this?  Hit <return> to proceed, <BREAK> to abort: ");
X			n = read(2, recbuf, 100);
X			c = recbuf[0];
X			yes = (c=='y' || c=='Y' || c=='\n' || c=='\n' || c==0);
X			if (n <= 0 || !yes)
X				retval = -1;
X		}
X	}
X	return retval;
X}
X
Xxxit(i)
X{
X	exit(i);
X}
X
X#if !defined(BSD4_2) && !defined(BSD4_1C)
Xrename(from,to)
Xregister char *from, *to;
X{
X	(void) unlink(to);
X	if (link(from, to) < 0)
X		return -1;
X
X	(void) unlink(from);
X	return 0;
X}
X#endif /* !BSD4_2 && ! BSD4_1C */
END_OF_FILE
if test 26497 -ne `wc -c <'postnews.c'`; then
    echo shar: \"'postnews.c'\" unpacked with wrong size!
fi
# end of 'postnews.c'
fi
if test -f 'getdate.s.uu' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'getdate.s.uu'\"
else
echo shar: Extracting \"'getdate.s.uu'\" \(26072 characters\)
sed "s/^X//" >'getdate.s.uu' <<'END_OF_FILE'
Xbegin 644 getdate.s
XMK@I?;61A>7,Z"L(S,0JX"L(S,0K",S *PC,Q"L(S, K",S$*PC,Q"L(S, K"
XM,S$*PC,P"LE?9&%T96-O;G8*PC,Q"H8*7V1A=&5C;VYV.@J#"H(*U_(L(S$P
XM"L'O"L'P"L#IU IO<B#I\ IJ9V4@23 P,3,*]/ *E0K-Z2,Q,# *:F=E($DP
XM,#$V"L3I(S$Y,# *O0K XS0*P.3P"F-W9 II\'8@[0IO<B#G[@K923 P,3D*
XMP.,Q,# *P.3P"F-W9 II\'8@[0IO<B#G[@K923 P,4$*P.,T,# *P.3P"F-W
XM9 II\'8@[0IO<B#G[@K923 P,3D*23 P,4$Z"H\*@ K,23 P,4,*23 P,3DZ
XM"H<*@ I),# Q0SH*P^T*Q.,R. K 7VUD87ES*S(L[0K-Z2,Q.3<P"FIL($DP
XM,#%$"LWI(S$Y.3D*:F<@23 P,40*S<7V"FIL92!),# Q1 K-Q2PC,3(*:F<@
XM23 P,40*S<[V"FIL92!),# Q1 KSQ0J2"G-A;"#B,0K Y%]M9&%Y<\<*S<XL
XMZPIJ;&4@23 P,44*23 P,40Z"L#A+3$*@ K X38U-3,U"H *F@I),# Q13H*
XMP.3."O/K"F-W9 K V"SK"L#0+.X*R^CO"DDP,#$Q.#H*S<4L[PIJ;&4@23 P
XM,3$U"L#E[PIS86P@XC$*P.1?;61A>7/'"F-W9 K$Y-@*861C(.?0"L#8+.L*
XMP- L[@K<[PJ:. I),# Q,34Z"FUO=B!S:2PC,3DW, I),# Q,4,Z"LWI[PIJ
XM;&4@23 P,3$Y"L#C- K Y.\*8W=D"FGP=B#M"H<*;W(@Y^X*V3%F"MSK"C$Z
XM"L3A,S8U"F-W9 K$Y-@*861C(.?0"L#8+.L*P- L[@K<[PJ:0PI),# Q,3DZ
XM"K0P.#8T"L#G(S$*P= *P=@*TRYM;&DT"L#8+.L*P- L[@K Y#'4"F-W9 K!
XMT K!V J<"H *P.$V, K+Y^X*TRYM;&DT"L/M"HD*Q.;K"F%D8R#E[@K V"SM
XM"L#0+.P*P3'."L$QQ0K!,3(H\2D*P3$P*/$I"M-?=&EM96-O;G8*Q/(L(S@*
XMK K QBSN"LW&]@IJ9V4@23 P,3%%"L#A+3$*@ K X38U-3,U"H *F@I),# Q
XM,44Z"J,*P.;&"L38+.L*861C(- L[0K-,C H\2DL(S$*L3 *S3(P*/$I+",S
XM"ME),# Q,C$*V^38"H *TU]L;V-A;'1I;64*B0J["LTQ-L?V"K$Q"DDP,#$R
XM,#H*Q-@L(S8Q.3,V"F%D8R#0+",M,0I),# Q,C$Z"L'0"L'8"I\*C0K#[@K,
XM+F1S@0K)7V1A>6-O;G8*7V1A>6-O;G8Z"H,*@@K7\BPC-@K![PK!\ K Z<4*
XMP.0X*&)P*0IM;W8@8W@L,3 H\2D*P- LZPK SRSM"MODT J "M-?;&]C86QT
XM:6UE"HD*P.CK"L#DS@K7Y#$R*.\I"L3A-PK XS<*8W=D"FGP=B#M"L#D[@IC
XM=V0*N0K!T J<"H *M# X-C0*P.<C,0K3+FUL:30*P^T*B0K$YNL*861C(.7N
XM"L#0+.T*P,\L[ JY"L'0"F]R(.GP"FIG($DP,#(S"L'P"LQ),# R- I),# R
XM,SH*P.3P"O/K"H *23 P,C0Z"HT*8W=D"IP*@ J/-#DW-@K YR,Y"M,N;6QI
XM- K#[0J)"L3FZPIA9&,@Y>X*P- L[0K SRSL"L$Q,"CQ*0K!U JY"L'0"M-?
XM9&%Y;&-O<G(*Q/(L(S@*S"YD<X$*R5]T:6UE8V]N=@I?=&EM96-O;G8Z"H,*
XM@@K![PK Z,4*P? *P.G4"JX*7S$Z"L)),# S,3D*PC,*PC$*PDDP,#-!"L(R
XM"L)),# S1@K",C0*PDDP,#,Q- J&"LW.]@IJ;"!),# S,@K-SBPC-3D*:F<@
XM23 P,S(*;W(@Z? *:FP@23 P,S(*S>DC-3D*:FQE($DP,#,S"DDP,#,R.@K 
XMX2TQ"H *P.$V-34S-0J "LQ),# S,0I),# S,SH*P3$P*/$I"LQ),# S. I)
XM,# S03H*;W(@Z.\*:FQE($DP,#-""LWH(S$R"FIL92!),# S0PI),# S0CH*
XM;6]V(&%X+",M,0J "L#A-C4U,S4*@ K,23 P,S$*23 P,T,Z"L#C,3(*P.3O
XM"F-W9 II\'8@[0K Y.X*8W=D"IP*@ K X38P"LOG[@K3+FUL:30*G J "L#D
XMS@IC=V0*P^T*B0K$YNL*861C(.7N"L#A-C *R^?N"J8*P>T*TRYM;&DT"IP*
XM@ K Y/ *8W=D"L/M"HD*Q.;K"F%D8R#E[@JF"L'M"LQ),# S,0I),# S1CH*
XM;W(@Z.\*:FQE($DP,#,Q, K-Z",Q,@IJ;&4@23 P,S$Q"DDP,#,Q,#H*P.$M
XM,0J "L#A-C4U,S4*@ K,23 P,S$*23 P,S$Q.@K XS$R"L#D[PIC=V0*:?!V
XM(.T*Q.<C,3(*P.3N"F-W9 J<"H *P.$V, K+Y^X*TRYM;&DT"IP*@ K Y,X*
XM8W=D"L/M"HD*Q.;K"F%D8R#E[@K X38P"LOG[@JF"L'M"M,N;6QI- J<"H *
XMP.3P"F-W9 K#[0J)"L3FZPIA9&,@Y>X*I@K![0K,23 P,S$*23 P,S$T.@IO
XM<B#H[PIJ;"!),# S,34*S>@C,C,*:FQE($DP,#,Q-@I),# S,34Z"L#A+3$*
XM@ K X38U-3,U"H *S$DP,#,Q"DDP,#,Q-CH*P.3O"F-W9 J<"H *P.$V, K+
XMY^X*TRYM;&DT"IP*@ K Y,X*8W=D"L/M"HD*Q.9A> IA9&,@8G@L9'@*P.$V
XM, K+Y^X*I@K![0K3+FUL:30*G J "L#D\ IC=V0*P^T*B0K$YNL*861C(.7N
XM"J8*P>T*S$DP,#,Q"DDP,#,Q.3H*P.$M,0J "L#A-C4U,S4*@ K,23 P,S$*
XM23 P,S@Z"L#B7S$*C0K,+F-S8C(*23 P,S$Z"HT*P^X*S"YD<X$*R5]M;VYT
XM:&%D9 I?;6]N=&AA9&0Z"H,*@@K7\BPC-@K![PK!\ K Y#$P*/$I"F]R(.34
XM"ME),# T,PJ'"H *@ K,23 P-#$*23 P-#,Z"MODQ0J "M-?;&]C86QT:6UE
XM"HD*P.CK"H\R"FUU;" Q,"CO*0K$Y#@H[RD*8W=D"L3DU IA9&,@YS$P*/$I
XM"L#IZPK XS$R"L#D\ IC=V0*:?!V(.T*P- LZPK Y/ *8W=D"FGP=B#M"MSN
XM"L#I[@J3"H@*P.$S"H *P5]O=7)Z;VYE"K0T"H *P2CO*0K!,BCO*0K!-"CO
XM*0K!T K!-BCO*0K!\ K37V1A=&5C;VYV"L3R+",Q. J<"H *TU]D87EL8V]R
XM<@K$\BPC. J<"H *23 P-#$Z"HT*P^X*S"YD<X$*R5]D87EL8V]R<@I?9&%Y
XM;&-O<G(Z"H,*@@J "H *P>\*P? *V^34"H *TU]L;V-A;'1I;64*B0IM;W8@
XM8G@L87@*;6]V(.0TQPK<ZPJ=- IC=V0*:?!V(.T*P.GN"MODQ0J "M-?;&]C
XM86QT:6UE"HD*NPK Y#3'"MSK"ITT"F-W9 II\'8@[0K Z.X*P.3P"M?D[PIC
XM=V0*G J "L#A,S8P, K+Y^X*TRYM;&DT"L#FQ0JO"M?FU IS8F(@Y3$P*/$I
XM"L3FZPIA9&,@Y>X*P.3M"L#G[ K,+F1S@0K)7WEY;&5X"E]Y>6QE>#H*@PJ"
XM"M?R+",R. K![PK!\ I),# V-SH*P.5?;'!T<@J,"HL*NPK*86PL7U]C='EP
XM95\K,<<*BPIT97-T8B!A;"PC. IJ92!),# V-@K<7VQP='(*S$DP,#8W"DDP
XM,#8V.@K Y5]L<'1R"HP*RBTS*/$I+&%L"HL*NPK*86PL7U]C='EP95\K,<<*
XMBPIT97-T8B!A;"PC- K923 P-CD*8VUP8B M,RCQ*2PC-#4*:F4@23 P-CD*
XM8VUP8B M,RCQ*2PC-#,*V4DP,#9!"DDP,#8Y.@IC;7!B("TS*/$I+",T-0IJ
XM92!),# V10IC;7!B("TS*/$I+",T,PK923 P-D8*23 P-D4Z"F-M<&(@+3,H
XM\2DL(S0U"ME),# V,3,*P,8L(RTQ"LQ),# V,30*23 P-C$S.@K QBPC,0I)
XM,# V,30Z"MQ?;'!T<@K Y5]L<'1R"HP*BPJ["LIA;"Q?7V-T>7!E7RLQQPJ+
XM"G1E<W1B(&%L+",T"ME),# V,3 *TU]Y>6QE> J "LQ),# V,0I),# V1CH*
XMP,8L(S$*23 P-C$P.@K 7WEY;'9A;/8*23 P-C$Y.@IM;W8@8G@L7VQP='(*
XMW%]L<'1R"HP*RBTS*/$I+&%L"HL*NPK*86PL7U]C='EP95\K,<<*BPIT97-T
XM8B!A;"PC- IJ92!),# V,3@*RF%L+"TS*/$I"HL*@ J/, IM=6P@7WEY;'9A
XM; K#[0K$YNL*U^,T. K 7WEY;'9A;"SM"LQ),# V,3D*23 P-C$X.@K Y%]Y
XM>6QV86P*;75L(,8*P%]Y>6QV86PLZPKS7VQP='(*M#8Q"H *S$DP,#8Q"DDP
XM,#9!.@K*86PL+3,H\2D*BPJ["LIA;"Q?7V-T>7!E7RLQQPJ+"G1E<W1B(&%L
XM+",S"FIE($DP,#8Q0PK;Y"TRS@K Z.L*23 P-C%&.@K Y5]L<'1R"MQ?;'!T
XM<@J,"LHM,RCQ*2QA; J+"KL*RF%L+%]?8W1Y<&5?*S''"HL*=&5S=&(@86PL
XM(S,*V4DP,#8R, IC;7!B("TS*/$I+",T-@K923 P-C%%"DDP,#8R,#H*RF%L
XM+"TS*/$I"LHH[RDL86P*W.\*S$DP,#8Q1@I),# V,44Z"LHH[RGV"O-?;'!T
XM<@K;Y"TRS@J "M-?;&]O:W5P"HD*@ K,23 P-C$*23 P-C%#.@IC;7!B("TS
XM*/$I+",T, K923 P-C(S"LOI\ I),# V,C<Z"L#E7VQP='(*W%]L<'1R"HP*
XMRBTS*/$I+&%L"F-M<&(@+3,H\2GV"ME),# V,CD*RF%L+"TS*/$I"HL*@ K,
XM23 P-C$*23 P-C(Y.@IC;7!B("TS*/$I+",T, K923 P-C)#"MSP"LQ),# V
XM,C8*23 P-C)#.@IC;7!B("TS*/$I+",T,0K923 P-C(V"O/P"DDP,#8R-CH*
XM;W(@Z? *:F<@23 P-C(W"LQ),# V-PI),# V,C,Z"L!B>"Q?;'!T<@II;F,@
XM7VQP='(*C J+"H *23 P-C$Z"HT*S"YD<X$*R5]M9'1A8@JN"E]M9'1A8CH*
XMPE\R"L(R-3@*PC$*PE\S"L(R-3@*PC(*PE\T"L(R-3@*PC,*PE\U"L(R-3@*
XMPC0*PE\V"L(R-3@*PC4*PE\W"L(R-3@*PC8*PE\X"L(R-3@*PC<*PE\Y"L(R
XM-3@*PC@*PE\Q, K",C4X"L(Y"L)?,3$*PC(U. K".0K"7S$R"L(R-3@*PC$P
XM"L)?,3,*PC(U. K",3$*PE\Q- K",C4X"L(Q,@K"7S$U"L(R-3D*N K"7S$V
XM"L(R-3D*PC$*PE\Q-PK",C4Y"L(R"L)?,3@*PC(U.0K",@K"7S$Y"L(R-3D*
XMPC,*PE\R, K",C4Y"L(S"L)?,C$*PC(U.0K"- K"7S(R"L(R-3D*PC0*PE\R
XM,PK",C4Y"L(T"L)?,C0*PC(U.0K"-0K"7S(U"L(R-3D*PC8*N JX"LE?;7IT
XM86(*N I?;7IT86(Z"L)?,C8*PC(V, K",0K"7S(W"L(R-C *PC$*PE\R. K"
XM,C8P"L(R"L)?,CD*PC(V, K",@K"7S,P"L(R-C4*PC(Q, K"7S,Q"L(R-C4*
XMPC(Q, K"7S,R"L(R-C4*PC(T, K"7S,S"BYW;W)D(#(V-0HN=V]R9" R-# *
XMPE\S- K",C8V"L(R-# *PE\S-0K",C8V"L(R-# *PE\S-@K",C8U"L(S,# *
XMPE\S-PK",C8U"L(S,# *PE\S. K",C8V"L(S,# *PE\S.0K",C8V"L(S,# *
XMPE\T, K",C8U"L(S-C *PE\T,0K",C8U"L(S-C *PE\T,@K",C8V"L(S-C *
XMPE\T,PK",C8V"L(S-C *PE\T- K",C8U"L(T,C *PE\T-0K",C8U"L(T,C *
XMPE\T-@K",C8V"L(T,C *PE\T-PK",C8V"L(T,C *PE\T. K",C8U"L(T.# *
XMPE\T.0K",C8U"L(T.# *PE\U, K",C8V"L(T.# *PE\U,0K",C8V"L(T.# *
XMPE\U,@K",C8U"L(U-# *PE\U,PK",C8U"L(U-# *PE\U- K",C8V"L(U-# *
XMPE\U-0K",C8V"L(U-# *PE\U-@K",C8U"L(V,# *PE\U-PK",C8U"L(V,# *
XMPE\U. K",C8V"L(V,# *PE\U.0K",C8V"L(V,# *PE\V, K",C8U"K@*PE\V
XM,0K",C8U"K@*PE\V,@K",C8V"K@*PE\V,PK",C8V"K@*PE\V- K",C8U"K@*
XMPE\V-0K",C8U"K@*PE\V-@K",C8V"K@*PE\V-PK",C8V"K@*+G=O<F0@7S8X
XM"BYW;W)D(#(V-0K"+38P"L)?-CD*PC(V-0K"+38P"L)?-S *PC(V-@K"+38P
XM"L)?-S$*PC(V-@K"+38P"L)?-S(*PC(V-0K"+3$R, K"7S<S"L(R-C4*PBTQ
XM,C *PE\W- K",C8V"L(M,3(P"L)?-S4*PC(V-@K"+3$R, K"7S<V"L(R-C4*
XMPBTU-# *PE\W-PK",C8U"L(M-30P"L)?-S@*PC(V-0K"+38P, K"7S<Y"L(R
XM-C4*PBTV,# *PE\X, K",C8V"L(M-C P"L)?.#$*PC(V-@K"+38P, K"7S@R
XM"L(R-C4*PBTU-S *PE\X,PK",C8U"L(M-3<P"L)?.#0*PC(V-@K"+34W, K"
XM7S@U"L(R-C8*PBTU-S *PE\X-@K",C8U"L(M-#@P"L)?.#<*PC(V-0K"+30X
XM, JX"K@*R5]U;FET=&(*N I?=6YI='1B.@K"7S@X"L(R-C,*PC$R"L)?.#D*
XMPC(V,PK",0K"7SDP"L(R-C(*PC(P,38P"L)?.3$*PC(V,@K",3 P.# *PE\Y
XM,@K",C8R"L(Q-#0P"L)?.3,*PC(V,@K"-C *PE\Y- K",C8R"L(Q"L)?.34*
XMPC(V,@K",0K"7SDV"L(R-C0*PC$*PE\Y-PK",C8T"L(Q"K@*N K)7V]T:&5R
XM=&(*N I?;W1H97)T8CH*PE\Y. K",C8R"BYW;W)D(#$T-# *PE\Y.0K",C8R
XM"L(M,30T, K"7S$P, K",C8R"K@*PE\Q,#$*PC(V,@JX"L)?,3 R"L(R-C$*
XMPBTQ"L)?,3 S"L(R-C(*N K"7S$P- K",C8Q"L(R"L)?,3 U"L(R-C$*PC$*
XMPE\Q,#8*PC(V,0K",PK"7S$P-PK",C8Q"L(T"L)?,3 X"L(R-C$*PC4*PE\Q
XM,#D*PC(V,0K"-@K"7S$Q, K",C8Q"L(W"L)?,3$Q"L(R-C$*PC@*PE\Q,3(*
XMPC(V,0K".0K"7S$Q,PK",C8Q"L(Q, K"7S$Q- K",C8Q"L(Q,0K"7S$Q-0K"
XM,C8Q"L(Q,@K"7S$Q-@K",C8W"L(Q"K@*N K)7VUI;'IO;F4*N I?;6EL>F]N
XM93H*PE\Q,3<*PC(V-0K"-C *PE\Q,3@*PC(V-0K",3(P"L)?,3$Y"L(R-C4*
XMPC$X, K"7S$R, K",C8U"L(R-# *PE\Q,C$*PC(V-0K",S P"L)?,3(R"L(R
XM-C4*PC,V, K"7S$R,PK",C8U"L(T,C *PE\Q,C0*PC(V-0K"-#@P"L)?,3(U
XM"L(R-C4*PC4T, K"7S$R-@K",C8U"L(V,# *PE\Q,C<*PC(V-0K"-C8P"L)?
XM,3(X"L(R-C4*PC<R, K"7S$R.0K",C8U"L(M-C *PE\Q,S *PC(V-0K"+3$R
XM, HN=V]R9"!?,3,Q"L(R-C4*PBTQ.# *PE\Q,S(*PC(V-0K"+3(T, K"7S$S
XM,PK",C8U"L(M,S P"L)?,3,T"L(R-C4*PBTS-C *PE\Q,S4*PC(V-0K"+30R
XM, K"7S$S-@K",C8U"L(M-#@P"L)?,3,W"L(R-C4*PBTU-# *PE\Q,S@*PC(V
XM-0K"+38P, K"7S$S.0K",C8U"L(M-C8P"L)?,30P"L(R-C4*PBTW,C *PE\Q
XM-#$*PC(V-0JX"K@*N K)7VQO;VMU< JX"H8*7VQO;VMU<#H*@PJ""M?R+",Q
XM,S8*P>\*P? *B K;Y"TQ,M0*@ K37W-T<F-P>0J)"HD*V^0M,3+4"L M,3,P
XM*/$I+.L*H@KSZPK Z>L*23 P-S,Z"MSP"F-M<&(@*/ I]@IJ92!),# W,@K*
XM86PL*/ I"HL*NPK*86PL7U]C='EP95\K,<<*BPIT97-T8B!A;"PC,0IJ92!)
XM,# W-@K*86PL*/ I"HL*@ K37W1O;&]W97(*B0J "LQ),# W-PI),# W-CH*
XMRF%L+"CP*0J+"H *23 P-S<Z"L#E+3$S,"CQ*0J-"KH*W"TQ,S H\2D*S$DP
XM,#<S"DDP,#<R.@K Y2TQ,S H\2D*RL?V"MOD+3$RU J "M-?<W1R;&5N"HD*
XMS>$S"ME),# W.0K +3$SSBPC,0K,23 P-T$*23 P-SDZ"MOD+3$RU J "M-?
XM<W1R;&5N"HD*8VUP(&%X+",T"ME),# W0PIC;7!B("TQ,C4H\2DL(S0V"ME)
XM,# W0PK +3$SSBPC,0K*+3$R-2CQ*?8*S$DP,#=!"DDP,#=#.@K +3$SSO8*
XM23 P-T$Z"L#H(U]M9'1A8@I),# W,3(Z"LTH[RGV"FIE($DP,#=&"MOD+3$R
XMU K Z>L*P.0H[RD*P"TQ,S H\2DLZPI),# W,38Z"L#E+3$S,"CQ*0K<+3$S
XM,"CQ*0J,"HL*P.7P"MSP"H *C J+"L/M"LWFZPK923 P-S$P"LTM,3/.]@IJ
XM92!),# W,3@*P.4H[RD*Q.(S"LTM,3,P*/$I+.P*V4DP,#<Q. K YC0H[RD*
XMP%]Y>6QV86PL[0K!,BCO*0K,23 P-S$*23 P-S$X.@K Y2TQ,S H\2D*8VUP
XM8B M,<?V"ME),# W,38*P.8T*.\I"L!?>7EL=F%L+.T*P3(H[RD*S$DP,#<Q
XM"DDP,#<Q,#H*Q.@C-@K,23 P-S$R"DDP,#=&.@K Z"-?;7IT86(*23 P-S(Q
XM.@K-*.\I]@IJ92!),# W,44*V^0M,3+4"H *P2CO*0K37W-T<F-M< J)"HD*
XMF0K923 P-S%&"L#F-"CO*0K 7WEY;'9A;"SM"L$R*.\I"LQ),# W,0I),# W
XM,48Z"L3H(S8*S$DP,#<R,0I),# W,44Z"L#H(U]M>G1A8@I),# W,C@Z"LTH
XM[RGV"FIE($DP,#<R-0K;Y"TQ,M0*@ K!*.\I"M-?<W1R8VUP"HD*B0J9"ME)
XM,# W,C8*P.8T*.\I"L!?>7EL=F%L+.T*P3(H[RD*S$DP,#<Q"DDP,#<R-CH*
XMQ.@C-@K,23 P-S(X"DDP,#<R-3H*;6]V('-I+"-?=6YI='1B"DDP,#<R1CH*
XMS2CO*?8*:F4@23 P-S)#"MOD+3$RU J "L$H[RD*TU]S=')C;7 *B0J)"ID*
XMV4DP,#<R1 K YC0H[RD*P%]Y>6QV86PL[0K!,BCO*0K,23 P-S$*23 P-S)$
XM.@K$Z",V"LQ),# W,D8*23 P-S)#.@K;Y"TQ,M0*@ J "M-?<W1R;&5N"HD*
XM\^L*B0K$Y>L*8VUP8B#'+",Q,34*V4DP,#<S- J'"H *V^0M,3+4"H *@ K3
XM7W-T<FQE;@J)"O/K"HD*Q.7K"HT*N@I),# W,S0Z"L#H(U]U;FET=&(*23 P
XM-S,Y.@K-*.\I]@IJ92!),# W,S8*V^0M,3+4"H *P2CO*0K37W-T<F-M< J)
XM"HD*F0K923 P-S,W"L#F-"CO*0K 7WEY;'9A;"SM"L$R*.\I"LQ),# W,0I)
XM,# W,S<Z"L3H(S8*S$DP,#<S.0I),# W,S8Z"L#H(U]O=&AE<G1B"DDP,#<T
XM,#H*S2CO*?8*:F4@23 P-S-$"MOD+3$RU J "L$H[RD*TU]S=')C;7 *B0J)
XM"ID*V4DP,#<S10K YC0H[RD*P%]Y>6QV86PL[0K!,BCO*0K,23 P-S$*23 P
XM-S-%.@K$Z",V"LQ),# W-# *23 P-S-$.@K;Y"TQ,M0*@ K37W-T<FQE;@J)
XM"LWA,0K923 P-S0U"LIA;"PM,3+4"HL*NPK*86PL7U]C='EP95\K,<<*BPIT
XM97-T8B!A;"PC,PIJ92!),# W-#4*P.@C7VUI;'IO;F4*23 P-S1".@K-*.\I
XM]@IJ92!),# W-#4*V^0M,3+4"H *P2CO*0K37W-T<F-M< J)"HD*F0K923 P
XM-S0Y"L#F-"CO*0K 7WEY;'9A;"SM"L$R*.\I"LQ),# W,0I),# W-#DZ"L3H
XM(S8*S$DP,#<T0@I),# W-#4Z"K0U-PJ "DDP,#<Q.@J-"LPN9'.!"LE?9V5T
XM9&%T90I?9V5T9&%T93H*@PJ""M?R+",R,@K![PK!\ K YL4*P%]L<'1R+.T*
XMS<[V"ME),# X,PK;Y"TQQ0K SBSK"MOD+3'%"H *TU]F=&EM90J)"DDP,#@S
XM.@J3"M-?;&]C86QT:6UE"HD*P.CK"L#F,3 H[RD*P%]Y96%R+.T*P.0X*.\I
XM"MSK"L!?;6]N=&@LZPK YC8H[RD*P%]D87DL[0K 7W)E;'-E8_8*P%]R96QS
XM96,K,O8*P%]R96QM;VYT:/8*P%]R96QM;VYT:"LR]@K 7W)E;&9L86?V"L#F
XM7W)E;&9L86<*P%]D87EF;&%G+.T*P%]D871E9FQA9RSM"L!?>F]N969L86<L
XM[0K 7W1I;65F;&%G+.T*KPK YC;'"L!?;W5R>F]N92SM"L!?9&%Y;&EG:'0L
XM(S,*P%]S<_8*P.9?<W,*P%]M;2SM"L!?:&@L[0K 7VUE<FED+",R- K37WEY
XM<&%R<V4*P.GK"F]R(.GP"FIE($DP,#@V"L#A+3$*@ K X38U-3,U"H *S$DP
XM,#@Q"DDP,#@V.@K-7W1I;65F;&%G+",Q"FIL92!),# X.0K<\ I),# X.3H*
XMS5]Z;VYE9FQA9RPC,0IJ;&4@23 P.$,*:6YC(/ *23 P.$,Z"LU?9&%T969L
XM86<L(S$*:FQE($DP,#A&"MSP"DDP,#A&.@K-7V1A>69L86<L(S$*:FQE($DP
XM,#@Q,@K<\ I),# X,3(Z"F]R(.GP"FIE($DP,#@Q-0K X2TQ"H *P.$V-34S
XM-0J "LQ),# X,0I),# X,34Z"LU?9&%T969L86?V"ME),# X,3<*S5]T:6UE
XM9FQA9_8*V4DP,#@Q-PK-7V1A>69L86?V"FIE($DP,#@Q. I),# X,3<Z"L%?
XM9&%Y;&EG:'0*P5]O=7)Z;VYE"L%?;65R:60*P5]S<PK!7VUM"L%?:&@*P5]Y
XM96%R"L%?9&%Y"L%?;6]N=&@*TU]D871E8V]N=@K$\BPC,3@*P"TQU"SK"L M
XM,<XL[@K-+3'.]@IJ9V4@23 P.#$Y"L#A+3$*@ K X38U-3,U"H *S$DP,#@Q
XM"DDP,#@Q.#H*KPJK"L#F,L<*P"TQU"SK"L M,<XL[0K-7W)E;&9L86?V"ME)
XM,# X,3D*P.$V, IM=6P@,BCO*0K$Y"CO*0IC=V0*P2TQS@K!+3'4"IP*@ K 
XMY#0H[RD*8W=D"IP*@ K X3,V,# *R^?N"M,N;6QI- K#[0J)"L3FZPIA9&,@
XMY>X*C0K#[@K7Y.T*<V)B(.?L"L M,=0LZPK +3'.+.X*23 P.#$Y.@K YE]R
XM96QS96,*P.=?<F5L<V5C*S(*Q"TQU"SM"F%D8R M,<XL[@K!+3'."L$M,=0*
XMP5]R96QM;VYT:"LR"L%?<F5L;6]N=&@*P2TQS@K!+3'4"M-?;6]N=&AA9&0*
XM861D('-P+",X"G!O<"#M"HD*Q.;K"F%D8R#E[@K +3'4+.T*P"TQSBSL"LU?
XM9&%Y9FQA9_8*:F4@23 P.#(S"LU?9&%T969L86?V"ME),# X,C,*P2TQS@K!
XM+3'4"L%?9&%Y<F5Q"L%?9&%Y;W)D"M-?9&%Y8V]N=@K$\BPC. K +3(R*/$I
XM+.L*P"TR,"CQ*2SN"L#D+3(R*/$I"L#F+3(P*/$I"L0M,=0LZPIA9&,@+3'.
XM+.T*23 P.#(S.@K!+3'."L$M,=0*23 P.#$Z"HT*P^X*S"YD<X$*R5]Y>65R
XM<F]R"E]Y>65R<F]R.@J#"H(*S"YC@0K)7WEY97AC80JN"E]Y>65X8V$Z"L(M
XM,0K",0JX"L(M,0K"+3(*R5]Y>6%C= JX"E]Y>6%C=#H*PC$R"L(Q,PK",C$*
XMPCD*PC$T"L(Q-0K",38*PC$P"L(Q,0K",S0*PC$W"L(S.0K"-# *PC$Y"L(S
XM. K",S<*PC,V"L(S, K",CD*PC(X"L(R-@K",S4*PC,Q"L(R-PK". K"-PK"
XM-@K"-0K"- K",PK",@K",0JX"K@*N JX"K@*N JX"K@*N JX"K@*N JX"K@*
XMN JX"K@*N JX"K@*N JX"K@*N JX"K@*N JX"BYW;W)D(# *+G=O<F0@, JX
XM"K@*N JX"K@*N JX"K@*N JX"K@*N JX"K@*N JX"K@*N JX"K@*N JX"K@*
XMN JX"K@*N JX"K@*N JX"K@*N JX"K@*N JX"K@*N JX"K@*N JX"K@*N JX
XM"K@*N JX"K@*N JX"K@*N JX"K@*N JX"K@*N JX"K@*N JX"K@*N JX"K@*
XMN JX"K@*N JX"K@*N JX"K@*N JX"K@*N JX"K@*N JX"K@*N JX"K@*N JX
XM"K@*N JX"K@*N JX"K@*N JX"K@*N JX"K@*N JX"K@*N JX"K@*N JX"K@*
XMN JX"K@*N JX"K@*N JX"K@*N JX"K@*N HN=V]R9" P"BYW;W)D(# *N JX
XM"K@*N JX"K@*N JX"K@*N JX"K@*N JX"K@*N JX"K@*N JX"K@*PC,R"L(S
XM,PK",C(*PC(P"L(Q. JX"L(R,PK",C0*R5]Y>7!A8W0*PC(U"E]Y>7!A8W0Z
XM"L(M,3 P, K"+3(U. K"+3$P,# *PBTQ,# P"L(M,3 P, K"+3$P,# *PBTQ
XM,# P"L(M,C4W"L(M,3 P, K"+30U"L(M,3 P, K"+3$P,# *PBTR-#$*PBTR
XM,0K"+3$P,# *PBTQ,# P"L(M,3 P, K"+3$P,# *PBTQ,# P"L(M,C0R"L(M
XM,3 P, K"+3(T,PK"+3(T- K"+3$P,# *PBTQ,# P"L(M,3 P, K"+3(R"L(M
XM,3 P, K"+30Y"L(M,C8*PBTQ,# P"L(M,C0U"L(M,3 P, K"+3$P,# *PBTR
XM-#8*PBTR-#<*PBTQ,# P"L(M,C0Y"L(M,3 P, K"+3$P,# *R5]Y>7!G;PK"
XM+3$P,# *7WEY<&=O.@JX"L(S,0K",S *PC(Y"L(R. K",C<*PC(V"L(R-0K)
XM7WEY<C$*PC(T"E]Y>7(Q.@JX"L(Q"L(Q"L(R"L(R"L(R"L(R"L(R"L(R"L(X
XM"L(S"L(S"L(S"L(S"L(S"L(S"L(S"L(T"BYW;W)D(#0*+G=O<F0@-@K"-@K"
XM-@K"-0K"-0K"-0K"-0K"-0K"-0K"-PK"-PK"-PK"-PK"-PK"-PK)7WEY<C(*
XMPC<*7WEY<C(Z"K@*N K",@K",0K",0K",0K",0K",0K",0K",0K",@K",PK"
XM- K"- K"-0K"-@K"-@K",0K",0K",0K",@K",@K",PK"-0K",@K"- K",@K"
XM,PK",@K",@K",@K",0K",0K",0K)7WEY8VAK"L(R"E]Y>6-H:SH*PBTQ,# P
XM"L(M,0K"+3(*PBTS"L(M- K"+34*PBTV"L(M-PK"+3@*PC(V,0K",C8U"L(R
XM-C8*PC(U. K",C4Y"L(R-C(*PC(V,PK",C8T"L(R-C<*PC(V, K"-3@*PC(U
XM.0K"-#<*PC(U. K",C8R"L(R-C,*PC(V- K",C8Q"L(T- K",C8Q"L(R-C$*
XMPC(V,0K"-#0*PC(V, K",C8Q"L(U. K"-#<*PC(V,0K",C8Q"L(R-C$*PC(V
XM, K)7WEY9&5F"L(R-C$*7WEY9&5F.@K",0K"+3(*PC(*PC,*PC0*PC4*PC8*
XMPC<*PC@*PCD*PC$W"L(Q. JX"L(Q.0K",S$*PC,R"L(S,PK",S0*+G=O<F0@
XM,3 *N K",C$*N K",C8*PC(X"L(R.0K",S *PC(T"L(R, K",3$*PC(R"L(R
XM-PJX"L(Q,@K",3,*N JX"L(R-0K",30*PC(S"L(Q-0K)7WEY8VAA<@K",38*
XM7WEY8VAA<CH*R5]Y>6YE<G)S"L(M,0I?>7EN97)R<SH*R5]Y>65R<F9L86<*
XMN I?>7EE<G)F;&%G.@K)7WEY<&%R<V4*N J&"E]Y>7!A<G-E.@J#"H(*U_(L
XM(S,Q-@K![PK!\ JN"E\Q-#0Z"L)),#!!,S,*N K",PK"23 P03,W"L)),#!!
XM,T$*PDDP,$$S00K"23 P030T"E\Q-#4Z"L)),#!!,@K",PK",S$*PDDP,$$T
XM10K"23 P031&"L)),#!!-3 *PDDP,$$U,0K"23 P034R"L)),#!!,@K"23 P
XM034S"L)),#!!-3D*PDDP,$$U00K"23 P035""L)),#!!-4,*PDDP,$$U1 K"
XM23 P035%"L)),#!!-48*PDDP,$$V, K"23 P038Q"L)),#!!-C(*PDDP,$$V
XM,PK"23 P038T"L)),#!!-C4*PDDP,$$V-@K"23 P038W"L)),#!!-C@*PDDP
XM,$$V.0K"23 P039!"L)),#!!-D(*PDDP,$$V0PK"23 P039$"L)),#!!-D4*
XMPDDP,$$V1@K"23 P03<P"L)),#!!-S$*A@K +3,PU/8*P%]Y>6-H87(L(RTQ
XM"L!?>7EN97)R<_8*P%]Y>65R<F9L86?V"MOD+3,P,BCQ*0IM;W8@+3,Q,"AB
XM<"DLZPK +3,QQ2PC7WEY=BTR"DDP,$$R.@K$+3,Q,"CQ*2PC,@K;Y# H\2D*
XMS2TS,3 H\2DLZPIJ8F4@23 P030*P.%?,30R"H *TU]Y>65R<F]R"HD*CPJ 
XM"LQ),#!!,0I),#!!-#H*P.0M,S#4"L#E+3,Q,"CQ*0JP"L0M,S'%+",R"L#F
XM7WEY=F%L"L#E+3,QQ0K QRSM"DDP,$$V.@K Y2TS,-0*<V%L(.(Q"L#I7WEY
XM<&%C=,<*S>DC+3$P,# *:F<@23 P03@*S$DP,$%!"DDP,$$X.@K-7WEY8VAA
XM<O8*:F=E($DP,$%#"M-?>7EL97@*P%]Y>6-H87(LZPK-7WEY8VAA<O8*:F=E
XM($DP,$%#"L!?>7EC:&%R]@I),#!!0SH*Q.E?>7EC:&%R"F]R(.GP"FIL($DP
XM,$%!"LWI(S(R, IJ;"!),#!!,3(*S$DP,$%!"DDP,$$Q,CH*P.7P"G-A;"#B
XM,0K Z5]Y>6%C=,<*P.7P"G-A;"#B,0K YE]Y>6-H87(*S5]Y>6-H:\<L[0K9
XM23 P04$*P%]Y>6-H87(L(RTQ"L#F7WEY;'9A; K 7WEY=F%L+.T*P"TS,-0L
XM\ K-7WEY97)R9FQA9_8*:FQE($DP,$$R"O-?>7EE<G)F;&%G"LQ),#!!,@I)
XM,#!!03H*P.4M,S#4"G-A;"#B,0K Z5]Y>61E9L<*S>DC+3(*V4DP,$$Q0PK-
XM7WEY8VAA<O8*:F=E($DP,$$Q1@K37WEY;&5X"L!?>7EC:&%R+.L*S5]Y>6-H
XM87+V"FIG92!),#!!,48*P%]Y>6-H87+V"DDP,$$Q1CH*P"TS,<XL(U]Y>65X
XM8V$*23 P03(W.@K Y2TS,<X*S<<L(RTQ"ME),#!!,C4*P.4M,S'."L!A>"PM
XM,S X*&)P*0IC;7 @,L<LZPIJ92!),#!!,D$*23 P03(U.@K$+3,QSBPC- K,
XM23 P03(W"DDP,$$R03H*Q"TS,<XL(S0*P.4M,S'."LW']@IJ;"!),#!!,CD*
XMP.4M,S'."L#F7WEY8VAA<@K-QRSM"ME),#!!,D$*23 P03(Y.@K Y2TS,<X*
XMP.DRQPIO<B#I\ IJ9V4@23 P03%#"H<*@ K,23 P03$*23 P03%#.@IO<B#I
XM\ K923 P03,S"L%?>7EE<G)F;&%G"LQ),#!!,S4*23 P03,W.@K X5\Q-#,*
XM@ K37WEY97)R;W(*B0K<7WEY;F5R<G,*23 P03-!.@K 7WEY97)R9FQA9RPC
XM,PI),#!!,T,Z"MOD+3,P,"CQ*0K-+3,Q,"CQ*2SK"FIB($DP,$$T,PK Y2TS
XM,3 H\2D*P.7'"G-A;"#B,0K Y%]Y>7!A8W3'"L3A,C4V"L#IZPIO<B#I\ IJ
XM;"!),#!!,T8*S>DC,C(P"FIG92!),#!!,T8*P.7P"G-A;"#B,0K Y5]Y>6%C
XM=,<*<V%L(.(Q"LU?>7EC:&O'+",R-38*V4DP,$$S1@K Y? *<V%L(.(Q"L#D
XM7WEY86-TQPK +3,PU"SK"LQ),#!!,@I),#!!,T8Z"L#E+3,Q,"CQ*0K Y<<*
XM<V%L(.(Q"L#I7WEY<&%C=,<*Q"TS,3 H\2DL(RTR"L0M,S'%+",M,@K,23 P
XM03-#"DDP,$$T,SH*CPJ "LQ),#!!,0I),#!!-#0Z"LU?>7EC:&%R]@K923 P
XM030V"LQ),#!!-#,*23 P030V.@K 7WEY8VAA<BPC+3$*S$DP,$$V"DDP,$$S
XM-3H*P.)?,30T"HT*S"YC<V$R"DDP,$$S,SH*P.7P"G-A;"#B,0K 8G@L7WEY
XM<C(H8G@I"G-A;"#B,0KT[ K$Y2TS,3 H\2D*P"TS,3 H\2DL[ K Z"TS,<4*
XMP.7P"G-A;"#B,0K Y5]Y>7(RQPIS86P@XC$*].P*Q.4M,S'%"L M,S'%+.P*
XMP.4M,S'%"L#F,L<*P%]Y>79A;"SM"L M,S#%+/ *P.7P"G-A;"#B,0K Z5]Y
XM>7(QQPK Y? *<V%L(.(Q"L%?>7EP9V_'"L#E+3,Q,"CQ*0J-"L3DQPK<ZPK 
XM+3,P,BCQ*2SK"LTM,S R*/$I+",R,C *:F=E($DP,$$T. K Y2TS,#(H\2D*
XM<V%L(.(Q"L#D7WEY86-TQPK +3,PU"SK"L#E+3,PU IS86P@XC$*P.3P"O3K
XM"LU?>7EC:&O'+.L*:F4@23 P030Y"DDP,$$T.#H*P.7P"G-A;"#B,0K Y5]Y
XM>7!G;\<*<V%L(.(Q"L#D7WEY86-TQPK +3,PU"SK"DDP,$$T.3H*P2TS,,4*
XMS$DP,$$T0PI),#!!-$4Z"MQ?=&EM969L86<*S$DP,$$R"DDP,$$T1CH*W%]Z
XM;VYE9FQA9PK,23 P03(*23 P034P.@K<7V1A=&5F;&%G"LQ),#!!,@I),#!!
XM-3$Z"MQ?9&%Y9FQA9PK,23 P03(*23 P034R.@K<7W)E;&9L86<*S$DP,$$R
XM"DDP,$$U,SH*S5]T:6UE9FQA9_8*:F4@23 P034U"LU?9&%T969L86?V"FIE
XM($DP,$$U-0K-7W)E;&9L86?V"ME),#!!-34*P.8H[RD*P%]Y96%R+.T*S$DP
XM,$$R"DDP,$$U-3H*W%]T:6UE9FQA9PK XS$P, K Y"CO*0IC=V0*:?!V(.T*
XMP%]H:"SK"L#D*.\I"F-W9 II\'8@[0K 7VUM+.X*;6]V(%]S<RPC, IM;W8@
XM7VUE<FED+",R- K,23 P03(*23 P034Y.@K YBTR*.\I"L!?:&@L[0K 7VUM
XM]@K 7W-S]@K YBCO*0K 7VUE<FED+.T*S$DP,$$R"DDP,$$U03H*P.8M-"CO
XM*0K 7VAH+.T*P.8H[RD*P%]M;2SM"L!?;65R:60L(S(T"LQ),#!!,@I),#!!
XM-4(Z"L#F+38H[RD*P%]H:"SM"L#F+3(H[RD*P%]M;2SM"L#F*.\I"L!?;65R
XM:60L[0K,23 P03(*23 P035#.@K YBTV*.\I"L!?:&@L[0K YBTR*.\I"L!?
XM;6TL[0K 7VUE<FED+",R- K 7V1A>6QI9VAT+",R"L#C,3 P"L#D*.\I"F-W
XM9 II\'8@[0K XC8P"FUU;"#L"H *P.0H[RD*8W=D"FGP=B#M"L/M"L3F[@KT
XM[0K 7V]U<GIO;F4L[0K,23 P03(*23 P035$.@K YBTX*.\I"L!?:&@L[0K 
XMYBTT*.\I"L!?;6TL[0K YBCO*0K 7W-S+.T*P%]M97)I9"PC,C0*S$DP,$$R
XM"DDP,$$U13H*P.8M,3 H[RD*P%]H:"SM"L#F+38H[RD*P%]M;2SM"L#F+3(H
XM[RD*P%]S<RSM"L#F*.\I"L!?;65R:60L[0K,23 P03(*23 P035&.@K YBTQ
XM,"CO*0K 7VAH+.T*P.8M-BCO*0K 7VUM+.T*P.8M,BCO*0K 7W-S+.T*P%]M
XM97)I9"PC,C0*P%]D87EL:6=H="PC,@K XS$P, K Y"CO*0IC=V0*:?!V(.T*
XMP.(V, IM=6P@[ J "L#D*.\I"F-W9 II\'8@[0K#[0K$YNX*].T*P%]O=7)Z
XM;VYE+.T*S$DP,$$R"DDP,$$V,#H*P.8H[RD*;6]V(%]O=7)Z;VYE+.T*P%]D
XM87EL:6=H="PC,@K,23 P03(*23 P038Q.@K YBCO*0K 7V]U<GIO;F4L[0K 
XM7V1A>6QI9VAT+",Q"LQ),#!!,@I),#!!-C(Z"L!?9&%Y;W)D+",Q"L#F*.\I
XM"L!?9&%Y<F5Q+.T*S$DP,$$R"DDP,$$V,SH*P%]D87EO<F0L(S$*P.8M,BCO
XM*0K 7V1A>7)E<2SM"LQ),#!!,@I),#!!-C0Z"L#F+3(H[RD*P%]D87EO<F0L
XM[0K YBCO*0K 7V1A>7)E<2SM"LQ),#!!,@I),#!!-C4Z"L#F+30H[RD*P%]M
XM;VYT:"SM"L#F*.\I"L!?9&%Y+.T*S$DP,$$R"DDP,$$V-CH*P.8M."CO*0K 
XM7VUO;G1H+.T*P.8M-"CO*0K 7V1A>2SM"L#F*.\I"L!?>65A<BSM"LQ),#!!
XM,@I),#!!-C<Z"L#F+3(H[RD*P%]M;VYT:"SM"L#F*.\I"L!?9&%Y+.T*S$DP
XM,$$R"DDP,$$V.#H*P.8M-BCO*0K 7VUO;G1H+.T*P.8M-"CO*0K 7V1A>2SM
XM"L#F*.\I"L!?>65A<BSM"LQ),#!!,@I),#!!-CDZ"L#F*.\I"L!?;6]N=&@L
XM[0K YBTR*.\I"L!?9&%Y+.T*S$DP,$$R"DDP,$$V03H*P.8M,BCO*0K 7VUO
XM;G1H+.T*P.8M-"CO*0K 7V1A>2SM"L#F*.\I"L!?>65A<BSM"LQ),#!!,@I)
XM,#!!-D(Z"L#D+3(H[RD*8W=D"L%?<F5L<V5C*S(*P5]R96QS96,*G J "L#A
XM-C *R^?N"M,N;6QI- J<"H *P.0H[RD*8W=D"M,N;6QI- K#[0J)"L3FZPIA
XM9&,@Y>X*P%]R96QS96,L[0K 7W)E;'-E8RLR+.P*S$DP,$$R"DDP,$$V0SH*
XM;6]V(&%X+"CO*0IM=6P@+3(H[RD*8W=D"L3D7W)E;&UO;G1H"F%D8R#G7W)E
XM;&UO;G1H*S(*P%]R96QM;VYT:"SK"L!?<F5L;6]N=&@K,BSN"LQ),#!!,@I)
XM,#!!-D0Z"L#D+3(H[RD*8W=D"L3D7W)E;'-E8PIA9&,@YU]R96QS96,K,@K 
XM7W)E;'-E8RSK"L!?<F5L<V5C*S(L[@K,23 P03(*23 P039%.@K Y"CO*0IC
XM=V0*P5]R96QS96,K,@K!7W)E;'-E8PJ<"H *P.$V, K+Y^X*TRYM;&DT"L/M
XM"HD*Q.;K"F%D8R#E[@K 7W)E;'-E8RSM"L!?<F5L<V5C*S(L[ K,23 P03(*
XM23 P039&.@K Y"CO*0IC=V0*Q.1?<F5L;6]N=&@*861C(.=?<F5L;6]N=&@K
XM,@K 7W)E;&UO;G1H+.L*P%]R96QM;VYT:"LR+.X*S$DP,$$R"DDP,$$W,#H*
XMQ%]R96QS96,L(S$*861C(%]R96QS96,K,O8*S$DP,$$R"DDP,$$W,3H*]%]R
XM96QS96,*]%]R96QS96,K,@IS8F(@7W)E;'-E8RLR]@KT7W)E;&UO;G1H"O1?
XM<F5L;6]N=&@K,@IS8F(@7W)E;&UO;G1H*S+V"LQ),#!!,@I),#!!-$,Z"L#B
XM7S$T-0J-"LPN8W-A,@I),#!!,3H*C0K,+F1S@0K)7WEY=@JS"E]Y>78Z("YZ
XM97)O=R S,# O,@I?;'!T<CH@+GIE<F]W(#(O,@K)7WEY=F%L"E]Y>79A;#H@
XM+GIE<F]W(#(O,@K)7WEY;'9A; I?>7EL=F%L.B N>F5R;W<@,B\R"E]O=7)Z
XM;VYE.B N>F5R;W<@,B\R"E]Y96%R.B N>F5R;W<@,B\R"E]D87DZ("YZ97)O
XM=R R+S(*7VUO;G1H.B N>F5R;W<@,B\R"E]D87ER97$Z("YZ97)O=R R+S(*
XM7V1A>6]R9#H@+GIE<F]W(#(O,@I?9&%Y;&EG:'0Z("YZ97)O=R R+S(*7VUE
XM<FED.B N>F5R;W<@,B\R"E]S<SH@+GIE<F]W(#(O,@I?;6TZ("YZ97)O=R R
XM+S(*7VAH.B N>F5R;W<@,B\R"E]R96QM;VYT:#H@+GIE<F]W(#0O,@I?<F5L
XM<V5C.B N>F5R;W<@-"\R"E]R96QF;&%G.B N>F5R;W<@,B\R"E]D87EF;&%G
XM.B N>F5R;W<@,B\R"E]D871E9FQA9SH@+GIE<F]W(#(O,@I?>F]N969L86<Z
XM("YZ97)O=R R+S(*7W1I;65F;&%G.B N>F5R;W<@,B\R"JX*7S(Z"L(R-#DS
XM. K",S P-C(*PC(Y,C@Q"L(Q,C$*7S,Z"L(R-3DU. K",CDR.#(*PC(T.30Y
XM"L(S,3 Y, JX"E\T.@K",C0Y-#$*PC(U-#4X"L(Q,#0*7S4Z"L(R.#<V.0K"
XM,C8Y.30*PC$P. I?-CH*PC(T.30Q"L(Q,C$*7S<Z"L(S,# U. K",C4Y-C8*
XMN I?.#H*PC,P,#4X"L(S,3 X- JX"E\Y.@K",S P-#D*PC,P,#4U"L(R.3@Q
XM,0JX"E\Q,#H*PC(U.3<Q"L(R.3@P. K",C@P,#4*PC(U.34T"L(Q,30*7S$Q
XM.@K",C4Y-S$*PC(Y.# X"K@*7S$R.@K",C4T-34*PC(X-3,R"L(R-3DU- K"
XM,3$T"E\Q,SH*PC(X-3(V"L(R-3DW- K",C4Q.3<*PC(Y,C@U"K@*7S$T.@K"
XM,C4Y-38*PC(U.34U"L(R-3$Y-PK",CDR.#4*N I?,34Z"L(S,# V-PJ_,3 *
XMPC,Q,#<S"K@*7S$V.@K",C@U,C4*OS$P"L(S,3 W,PJX"E\Q-SH*PC,P,#8X
XM"L(R.34T,0K",C0Y,S(*PC$R,0I?,3@Z"L(S,# V. K",CDU-#$*N I?,3DZ
XM"L(R-3DW-0K",C@R-C *PC(Y-30Q"L(R-#DS,@HN=V]R9" Q,C$*7S(P.@K"
XM,C4Y-S4*PC(X,C8P"L(R.34T,0JX"E\R,3H*PC(V-S0P"L(R.3,P,0J_,34*
XMPC,Q,#<S"K@*7S(R.@K",C8W-# *PC(Y,S Q"K@*7S(S.@K",C8W-# *PC(Y
XM,S Q"L(Q,34*7S(T.@K",CDR.#8*OS U"L(S,3 W,PJX"E\R-3H*PC(T.30W
XM"L(S,# V. J_,30*PC,Q,#<S"K@*7S(V.@K",3$X-S,*PC$Q.#@U"K@*7S(W
XM.@K",C@P,#$*N I?,C@Z"L(Q,3@X. K",3$X.#4*N I?,CDZ"L(R.# Q-@JX
XM"E\S,#H*PC(Y-34P"L(Q,38*7S,Q.@K",3$X.#8*PC$Q.#DQ"L(Q,3@Y,@JX
XM"E\S,CH*PC(Y-3,W"L(Q,38*7S,S.@K",3$X-S,*PC$Q.#DQ"L(Q,3@Y,@JX
XM"E\S-#H*PC(U-CDW"L(Q,38*7S,U.@K",3$X-S,*PC$Q.#<V"L(Q,3@Y,@JX
XM"E\S-CH*PC(Y-30Q"L(Q,38*7S,W.@K",3$X-S<*PC$Q.#DQ"L(Q,3@Y,@JX
XM"E\S.#H*OS Q"L(Q,38*7S,Y.@K",3$X-S<*PC$Q.#<V"L(Q,3@Y,@JX"E\T
XM,#H*PC(Y-3,Y"L(Q,38*7S0Q.@K",3$X-S4*PC$Q.#DQ"L(Q,3@Y,@JX"E\T
XM,CH*PC(U-CDY"L(Q,38*7S0S.@K",3$X-S4*PC$Q.#<V"L(Q,3@Y,@JX"E\T
XM-#H*PC(Y-30Y"L(Q,38*7S0U.@K",3$X.#4*PC$Q.#DQ"L(Q,3@Y,@HN=V]R
XM9" P"E\T-CH*OS Y"L(Q,38*7S0W.@K",3$X.#4*PC$Q.#<V"L(Q,3@Y,@JX
XM"E\T.#H*PC(Y-34R"L(Q,38*7S0Y.@K",3$X.#@*PC$Q.#DQ"L(Q,3@Y,@JX
XM"E\U,#H*OS$R"L(Q,38*7S4Q.@K",3$X.#@*PC$Q.#<V"L(Q,3@Y,@JX"E\U
XM,CH*PC(Y-38Q"L(Q,38*7S4S.@K",3$X.3<*PC$Q.#DQ"L(Q,3@Y,@JX"E\U
XM-#H*OS(Q"L(Q,38*7S4U.@K",3$X.3<*PC$Q.#<V"L(Q,3@Y,@JX"E\U-CH*
XMPC(Y-30T"L(Q,38*7S4W.@K",3$X.# *PC$Q.#DQ"L(Q,3@Y,@JX"E\U.#H*
XMOS T"L(Q,38*7S4Y.@K",3$X.# *PC$Q.#<V"L(Q,3@Y,@JX"E\V,#H*PC(X
XM,# W"L(Q,38*7S8Q.@K",3$X-SD*PC$Q.#@U"L(Q,3@Y,@JX"E\V,CH*PC(Y
XM-3,X"L(Q,38*7S8S.@K",3$X-S0*PC$Q.#DQ"L(Q,3@Y,@JX"E\V-#H*PC(U
XM.34W"L(Q,38*7S8U.@K",3$X-S<*PC$Q.#<W"L(Q,3@Y,@JX"E\V-CH*PC(U
XM.34W"L(R.3@Q,0JX"E\V-SH*PC$Q.#<W"L(Q,3@W-PK",3$X.3$*PC$Q.#DR
XM"K@*7S8X.@K",C4Y-C4*PC$Q-@I?-CDZ"L(Q,3@X-0K",3$X-S<*PC$Q.#DR
XM"K@*7S<P.@K",C4Y-C4*PC(Y.#$Q"K@*7S<Q.@K",3$X.#4*PC$Q.#<W"L(Q
XM,3@Y,0HN=V]R9" Q,3@Y,@JX"E\W,CH*PC(U.3<U"L(Q,38*7S<S.@K",3$X
XM.34*PC$Q.#<W"L(Q,3@Y,@JX"E\W-#H*PC(U.3<U"L(R.3@Q,0JX"E\W-3H*
XMPC$Q.#DU"L(Q,3@W-PK",3$X.3$*PC$Q.#DR"K@*7S<V.@K",CDU-#8*PC$Q
XM-@I?-S<Z"L(Q,3@X,@K",3$X.3$*PC$Q.#DR"K@*7S<X.@K",C4Y-3,*PC(Y
XM.#$Q"K@*7S<Y.@K",3$X-S,*PC$Q.#<W"L(Q,3@Y,0K",3$X.3(*N I?.# Z
XM"L(R-3DU,PK",CDU-34*PC$Q-@I?.#$Z"L(Q,3@W,PK",3$X-S<*PC$Q.#DQ
XM"L(Q,3@Y,0K",3$X.3(*N I?.#(Z"L(R-30T,0K",CDX,3$*N I?.#,Z"L(Q
XM,3@W,PK",3$X-S4*PC$Q.#DQ"L(Q,3@Y,@JX"E\X-#H*PC(U-#0Q"L(R.34U
XM-0K",3$V"E\X-3H*PC$Q.#<S"L(Q,3@W-0K",3$X.3$*PC$Q.#DQ"L(Q,3@Y
XM,@JX"E\X-CH*PC,P-38Q"L(R.3@Q,0JX"E\X-SH*PC$Q.#<S"L(Q,3@Y-0K"
XM,3$X.3$*PC$Q.#DR"K@*7S@X.@K",C4Y-S<*PC(Y,C@Q"K@*7S@Y.@K",C@U
XM,C4*PC(Y.# V"L(Q,#0*7SDP.@K",C@U,3@*PC(Y.#$P"L(R-CDY, K",C8W
XM,C<*PC$Q-@I?.3$Z"L(R-3DW-0K",C<T.3,*N I?.3(Z"L(R-#DS,@K",3(Q
XM"E\Y,SH*PC(X-3(P"L(R.3,P,0HN=V]R9" P"E\Y-#H*+G=O<F0@,C8Y.#D*
XMPC,P,#8R"L(R-3DW,@JX"E\Y-3H*PC(V.3@Y"L(Q,3 *7SDV.@K",C4Y-S$*
XMPC(X-3$U"K\Q, JX"E\Y-SH*PC(U.3<Q"L(Y.0I?.3@Z"L(R.#4S,@K",C@U
XM,C4*PC(Y,CDX"L(S,#4W-0JX"E\Y.3H*PC(U.3<W"L(R.3@Q,0K",CDR.#4*
XMPC(T.3,R"L(Q,C$*7S$P,#H*PC(X-3,R"L(R-#DS,@K",3(Q"E\Q,#$Z"L(R
XM.#4R-@K",3$Y"E\Q,#(Z"L(R-#DT, K",CDX,3$*N I?,3 S.@K",C8W-# *
XMPC(Y-30U"K@*7S$P-#H*PC(U.38V"L(R.3@Q-@JX"E\Q,#4Z"L(R-CDX,@K"
XM,CDU-30*PC$Q-@I?,3 V.@K",C8W-# *PC(Y,C@Y"L(Q,# *7S$P-SH*PC(X
XM-3$X"L(R.3,P,0K",C8W-# *N I?,3 X.@K",C8Y.#(*PC(Y-SDX"L(Q,#0*
XM7S$P.3H*PC(V.3DU"L(R.3@Q-@K",3 T"E\Q,3 Z"L(R-3DW,0K",C4Y-S0*
XMPC(Y.# V"L(Q,#0*7S$Q,3H*PC(V.3@Q"L(R.3<Y.0K",3 T"E\Q,3(Z"L(R
XM-CDY, K",CDX,#8*PC$P- I?,3$S.@K",C4Y-S(*PC(Y.# V"L(Q,#0*7S$Q
XM-#H*PC(W-S0Y"L(S,#,P.0K",C@R-C$*PC(V-S0P"K@*7S$Q-3H*PC,P-3@P
XM"L(R-S<T.0K",CDW.3@*PC$P- I?,3$V.@K",C8T-C4*PC$Q,0I?,3$W.@K"
XM.3<*7S$Q.#H*PCDX"E\Q,3DZ"BYW;W)D(#DY"E\Q,C Z"L(Q,# *7S$R,3H*
XMPC$P,0I?,3(R.@K",3 R"E\Q,C,Z"L(Q,#,*7S$R-#H*PC$P- I?,3(U.@K"
XM,3 U"E\Q,C8Z"L(Q,#<*7S$R-SH*PC$P. I?,3(X.@K",3 Y"E\Q,CDZ"L(Q
XM,3 *7S$S,#H*PC$Q,0I?,3,Q.@K",3$R"E\Q,S(Z"L(Q,3,*7S$S,SH*PC$Q
XM- I?,3,T.@K",3$U"E\Q,S4Z"L(Q,38*7S$S-CH*PC$Q-PI?,3,W.@K",3$X
XM"E\Q,S@Z"L(Q,3D*7S$S.3H*PC$R, I?,30P.@K",3(Q"E\Q-#$Z"L(Q,C(*
XM7S$T,CH*PC(T.34S"L(R-30T,PK",CDT-S(*PC(T.30X"L(R-S0Y,0K",C@T
XM-#@*PC(U.3<T"L(R-C(R-@K",C@U,C0*PC$Q.0I?,30S.@K",S$P.3$*PC(Y
XM.# V"L(S,#@Q-PK",C4X.#@*PC(Y,CDX"L(R.3(Y-0HN=V]R9" P"BYT97AT
X!"C V
X 
Xend
END_OF_FILE
if test 26072 -ne `wc -c <'getdate.s.uu'`; then
    echo shar: \"'getdate.s.uu'\" unpacked with wrong size!
fi
# end of 'getdate.s.uu'
fi
echo shar: End of shell archive.
exit 0
-- 
		"Zeta Microcomputer Software"
ACSnet:    nick@ultima.cs.uts.oz
UUCP:      ...!uunet!munnari!ultima.cs.uts.oz!nick
Fidonet:   Nick Andrew on 3:713/602 (Zeta)

nick@ultima.cs.uts.oz (Nick Andrew) (12/07/89)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  control.c readr.c
# Wrapped by nick@nswitgould on Thu Dec  7 22:40:20 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'control.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'control.c'\"
else
echo shar: Extracting \"'control.c'\" \(26215 characters\)
sed "s/^X//" >'control.c' <<'END_OF_FILE'
X/*
X * This software is Copyright (c) 1986 by Rick Adams.
X *
X * Permission is hereby granted to copy, reproduce, redistribute or
X * otherwise use this software as long as: there is no monetary
X * profit gained specifically from the use or reproduction or this
X * software, it is not sold, rented, traded or otherwise marketed, and
X * this copyright notice is included prominently in any copy
X * made.
X *
X * The author make no claims as to the fitness or correctness of
X * this software for any use whatsoever, and it is provided as is. 
X * Any use of this software is at the user's own risk.
X *
X * Control message handling code.  Deal with messages which are to be
X * acted on by netnews itself rather than by people.
X *
X * See defs.h "news_version" for the real version of netnews.
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)control.c	2.54	4/10/87";
X#endif /* SCCSID */
X
X#include "iparams.h"
X
X#define eq(msg) (msg[0] == cargv[0][0] && strcmp(msg, cargv[0]) == 0)
X
Xint cargc;
Xchar **cargv;
X
XFILE *hfopen();
XFILE *popen(), *mhopen(), *mailhdr();
X
X#define NCARGS	30
Xchar *senderof();
X#ifdef u370
Xstatic struct hbuf htmp;
X#endif /* u370 */
X
X/*
X * The global structure is initialized to NOTIFY as the default (if defined)
X * uid to send mail to for every state.  The following conditions are
X * dealt with (assumes NOTIFY defined):
X *
X * 1) LIB/notify exists and is empty (or contains no recognizable control
X *    message types).
X *    	Action: force TELLME = "";
X * 2) LIB/notify contains the control message name "all" and no associated
X *    address.
X *	Action: force TELLME = "";
X * 3) LIB/notify contains the control message name "all" and has an address.
X *	Action: set TELLME = AlloCpy(address);
X * 4) LIB/notify contains only some of the known control message types.
X *	Action: initialize all addresses to "" and set declared addresses
X *		to listed address.
X */
X
X
Xcontrol(h)
Xstruct hbuf *h;
X{
X	register char *ctlmsgtext;
X	register struct msgtype *mp;
X
X	if (strncmp(h->title, "cmsg ", 5) == 0) {
X		register char *cp1, *cp2;
X		cp1 = h->title;
X		cp2 = h->title + 5;
X		while (*cp1++ = *cp2++)
X			;
X	}
X
X	if (*h->ctlmsg)
X		ctlmsgtext = h->ctlmsg;
X	else
X		ctlmsgtext = h->title;
X	log("Ctl Msg %s from %s: %s", h->nbuf, h->path, ctlmsgtext);
X	/*
X	 * Control messages have the standard format
X	 *	command [args]
X	 * much like shell commands.  Each site has the option
X	 * of customizing this code to deal with control messages
X	 * as they see fit, but we would like to buy back the
X	 * code, ifdeffed or otherwise parameterized, to simplify
X	 * the maintenence issues.
X	 */
X	argparse(ctlmsgtext);
X	
X	/*
X	 * We look for a match of the control message name and then
X	 * set TELLME to the value parsed from the LIB/notify file
X	 * (if any).
X	 */
X	for(mp=msgtype; mp->m_name; mp++) {
X		if(eq(mp->m_name) ) {		/* hit */
X#ifdef NOTIFY
X			TELLME = mp->m_who_to;	/* reset whom to tell */
X#endif /* NOTIFY */
X			return (*mp->m_func)(cargc, cargv); /* do it */
X		}
X	}
X	if( !mp->m_name ) {
X#ifdef NOTIFY
X		TELLME = NOTIFY;
X#endif /* NOTIFY */
X		c_unknown(h, ctlmsgtext);
X	}
X	return 0;
X}
X
X/*
X * Parse the string str into separate words in cargc and cargv
X * as per the usual UNIX convention.  Nothing fancy here, just
X * blanks and tabs separating words.
X */
Xargparse(str)
Xchar *str;
X{
X	static char *cavpbuf[NCARGS];
X	static char cavbuf[256];
X	char *nextfree = cavbuf;
X
X	if (str == '\0')
X		error("Control message %s has no title", header.ident);
X	cargc = (*str != '\0');
X	cargv = cavpbuf;
X	cargv[0] = cavbuf;
X
X	while (*str) {
X		if (*str <= ' ') {
X			/* skip over white space */
X			while (*str != '\0' && *str <= ' ')
X				str++;
X			if (*str == '\0')	/* line ends in white space */
X				return;
X			*nextfree++ = 0;
X			cargv[cargc] = nextfree;
X			if (cargc++ >= NCARGS)
X				xerror("Too many arguments to control message %s",
X						header.ident);
X		} else
X			*nextfree++ = *str++;
X	}
X}
X
X/*
X * ihave <artid> ... <remotesys>
X *	or
X * ihave <remotesys>
X *	with <artid>s in message body.
X *
X * The other system is telling you it has article <artid>, in case
X * you decide you want it to transmit it to you.
X * The assumption is that the other system only tells you about articles
X * in newsgroups you subscribe to.
X *
X * We turn the incoming ihave into an outgoing sendme on the fly.
X * It then gets saved in the SPOOL directory and transmitted to the
X * remote system.  (This way the sendme messages can be batched.)
X */
Xc_ihave(argc, argv)
Xregister char **	argv;
X{
X	register int	i;
X	char		list[sizeof header.title];
X	extern char *	findhist();
X	extern char *	mydomain();
X
X	if (argc < 2)
X		error("ihave: Too few arguments.");
X	if (strncmp(PATHSYSNAME, argv[argc - 1], SNLN) == 0)
X		return 0;
X	list[0] = '\0';
X	if (argc > 2) {
X		for (i = 1; i < (argc - 1); ++i)
X			if (findhist(argv[i]) == NULL) {
X				(void) strcat(list, " ");
X				(void) strcat(list, argv[i]);
X			}
X		if (list[0] == '\0')
X			return 0;
X	} else {
X		register FILE *	outfp;
X		register long	outpos, inpos;
X		char		myid[256];
X
X		outfp = xfopen(INFILE, "a");
X		outpos = ftell(outfp);
X		inpos = ftell(infp);
X		while (ftell(infp) < outpos) {
X			if (fgets(myid, sizeof myid, infp) != myid)
X				error("iline: Can't reread article");
X			myid[strlen(myid) - 1] = '\0';
X			if (findhist(myid) == NULL)
X				(void) fprintf(outfp, "%s\n", myid);
X		}
X		if (outpos == ftell(outfp)) {	/* if nothing is wanted */
X			(void) fclose(outfp);
X			(void) fseek(infp, inpos, 0);
X			return 0;
X		}
X		(void) fclose(outfp);
X		/*
X		** The close and open may just be paranoia.
X		*/
X		(void) fclose(infp);
X		infp = xfopen(INFILE, "r");
X		(void) fseek(infp, outpos, 0);
X	}
X	/*
X	** Turn the ihave into a sendme.
X	*/
X	(void) sprintf(header.nbuf, "to.%s.ctl", argv[argc - 1]);
X	(void) sprintf(header.title, "sendme%s %s", list, PATHSYSNAME);
X	(void) strcpy(header.ctlmsg, header.title);
X	getident(&header);
X	(void) sprintf(header.from, "%s@%s", "usenet", FROMSYSNAME);
X	(void) strcpy(header.path, NEWSUSR);
X	header.subdate[0] = header.expdate[0] = '\0';
X	dates(&header);
X	/*
X	** What else of this kind should be done?
X	*/
X	header.organization[0] = header.distribution[0] = '\0';
X	header.numlines[0] = '\0';
X	for (i = 0; i < NUNREC && header.unrec[i] != NULL; ++i) {
X		free(header.unrec[i]);
X		header.unrec[i] = NULL;
X	}
X	/*
X	** Note that we do *not* change the history line
X	** so that if the "ihave" message comes in again it gets rejected.
X	*/
X	return 0;
X}
X
X/*
X * sendme <artid> ... <remotesys>
X *	or
X * sendme <remotesys>
X *	with <artid>s in message body.
X * The other system wants me to send out article <artid>.
X * Give it to them with no fuss.
X */
X#ifdef MULTICAST
Xstatic int	c_mc;
Xstatic char **	c_sysnames;
X#endif /* MULTICAST */
Xc_sendme(argc, argv)
Xregister char **argv;
X{
X	struct srec	srec;
X
X	if (argc < 2)
X		error("sendme: Too few arguments.");
X	if (strncmp(PATHSYSNAME, argv[argc - 1], SNLN) == 0)
X		return 0;
X	if (s_find(&srec, argv[argc - 1]) != TRUE)
X		error("sendme: Can't find sys record for %s", argv[argc - 1]);
X#ifdef MULTICAST
X	c_mc = index(srec.s_flags, 'M') != 0;
X	if (c_mc) {
X		struct srec	trec;
X
X		c_sysnames = &argv[argc - 1];
X		if (s_find(&trec, srec.s_xmit) != TRUE)
X			error("sendme: Can't find sys record for %s for %s",
X				srec.s_xmit, argv[argc - 1]);
X		srec = trec;
X	} else	c_sysnames = NULL;
X#endif /* MULTICAST */
X	/* Send the articles. */
X	if (argc == 2) {
X		register FILE *	fp;
X		char		buf[256];
X
X		fp = xfopen(INFILE, "r");
X		while (fgets(buf, sizeof buf, fp) == buf) {
X			buf[strlen(buf) - 1] = '\0';	/* zap trailing '\n' */
X			sendmefunc(buf, &srec);
X		}
X		(void) fclose(fp);
X	} else { 	/* argc > 2 */
X		register int	i;
X
X		for (i = 1; i < (argc - 1); ++i)
X			sendmefunc(argv[i], &srec);
X	}
X	return 0;
X}
X
Xstatic
Xsendmefunc(id, sp)
Xregister char *		id;
Xregister struct srec *	sp;
X{
X	register FILE *	fp;
X	register char *	cp;
X	char		savedbufname[256];
X	extern char	firstbufname[];
X	extern char *	dirname();
X	extern char *	findfname();
X
X	cp = findfname(id);
X	if (cp == NULL) {
X		logerr("System %s wants unavailable article %s.",
X#ifdef MULTICAST
X			(c_mc ? c_sysnames[0] : sp->s_name), id);
X#else /* !MULTICAST */
X			sp->s_name, id);
X#endif /* !MULTICAST */
X		return;
X	}
X	cp = dirname(cp);
X	fp = fopen(cp, "r");
X	if (fp == NULL) {
X		logerr("Article %s unopenable as %s.", id, cp);
X		return;
X	}
X	(void) strcpy(savedbufname, firstbufname);
X	(void) strcpy(firstbufname, cp);
X#ifdef MULTICAST
X	transmit(sp, fp, FALSE, c_sysnames, c_mc);
X#else /* !MULTICAST */
X	transmit(sp, fp, FALSE, (char **) NULL, 0);
X#endif /* !MULTICAST */
X	/* transmit closes fp */
X	(void) strcpy(firstbufname, savedbufname);
X}
X
X/*
X * newgroup <groupname>
X * A new newsgroup has been created.
X * The body of the article, if present, is a description of the
X * purpose of the newsgroup.
X *
X */
Xc_newgroup(argc, argv)
Xchar **argv;
X{
X	FILE *fd;
X	char abuf[BUFLEN], subjline[BUFLEN];
X	int didcreate = 0;
X	register char *p, *q;
X# ifdef NONEWGROUPS
X#  ifdef ORGDISTRIB
X	/* local or ORGDISTRIB */
X	int can_change = (strcmp(header.distribution, "local") == 0) ||
X				(strcmp(header.distribution, ORGDISTRIB) == 0);
X#  else /* ! ORGDISTRIB */
X	/* local only */
X	int can_change = strcmp(header.distribution, "local") == 0;
X#  endif /* ORGDISTRIB */
X# else /* ! NONEWGROUPS */
X	int can_change = 1;	/* allow changes for all distributions */
X# endif /* NONEWGROUPS */
X
X	if (argc < 2)
X		error("newgroup: Too few arguments.");
X
X	if (header.approved[0] == '\0') {
X		logerr("newgroup: %s not approved", argv[1]);
X		return 1;
X	}
X
X	lock();
X	/* see if it already exists */
X	(void) rewind(actfp); clearerr(actfp);
X	while(fgets(abuf, BUFLEN, actfp) != NULL) {
X		p = abuf;
X		q = argv[1];
X		while (*p++ == *q++)
X			;
X		if (*--q == '\0' && *--p == ' ') {
X			int modified = 0;
X			/* Now check if it's correctly moderated/unmoderated */
X			while (*p++)
X				;
X			p -= 3;
X			if (argc > 2 && strcmp(argv[2], "moderated") == 0) {
X				if (*p == 'm') {
X					unlock();
X					return 0;
X				}
X# ifdef NONEWGROUPS
X				if(can_change) {
X					*p = 'm';
X					modified = 1;
X				}
X# else /* ! NONEWGROUPS */
X				*p = 'm';
X				modified = 1;
X#endif /* NONEWGROUPS */
X			} else {
X				if (*p != 'm') {
X					unlock();
X					return 0;
X				}
X# ifdef NONEWGROUPS
X				if(can_change)  {
X					*p = 'y';
X					modified = 1;
X				}
X# else /* ! NONEWGROUPS */
X				*p = 'y';
X				modified = 1;
X# endif /* NONEWGROUPS */
X			}
X# ifdef NOTIFY
X			(void) sprintf(subjline,
X			"Newsgroup %s change from %smoderated to %smoderated",
X				argv[1], *p=='y' ? "" : "un",
X				*p=='y' ? "un" : "");
X			fd = mailhdr((struct hbuf *)NULL, subjline);
X			if (fd != NULL) {
X				if(modified)
X					fprintf(fd,
X"%s has been changed from %smoderated to %smoderated as requested by\n%s\n",
X						argv[1], *p=='y' ? "" : "un", 
X						*p=='y' ? "un":"", header.path);
X				else {
X					fprintf(fd,
X"%s\nhas requested that %s be changed from %smoderated to %smoderated\n",
X						header.path, argv[1], 
X						*p=='y' ? "" : "un",
X						*p=='y' ? "un" : "");
X#ifdef ORGDISTRIB
X					fprintf(fd,
X"You can accomplish this by re-creating the newsgroup with a distribution\n");
X					fprintf(fd,
X"of '%s' by executing the command:\n", ORGDISTRIB);
X					fprintf(fd,
X				"%s/inews -d %s -C %s moderated\n",
X						LIB, ORGDISTRIB, argv[1]);
X#else /* !ORGDISTRIB */
X					fprintf(fd,
X"You can accomplish this by re-creating the newsgroup by executing the command:\n");
X					fprintf(fd, "%s/inews -C %s moderated\n",
X						LIB, argv[1]);
X#endif /* !ORGDISTRIB */
X				}
X				(void) mclose(fd);
X			}
X# endif /* NOTIFY */
X# ifdef NONEWGROUPS
X			/*
X			 * No permission to change
X			 */
X			if(!can_change) {
X				unlock();
X				return 0;
X			}
X# endif /* NONEWGROUPS */
X			/* The active file was wrong about the state of the
X			 * group. Rewrite the active file
X			 */
X			(void) fseek(actfp, -2L, 1); /* back up 2 characters */
X			putc(*p, actfp);
X			fflush(actfp);
X			if (*p != 'm')
X				logerr("Newsgroup %s changed from moderated to unmoderated",
X				argv[1]);
X			else
X				logerr("Newsgroup %s changed from unmoderated to moderated",
X				argv[1]);
X			unlock();
X			return 0;
X		}
X	}
X
X	/* It doesn't already exist, we must create it */
X
X	if(can_change) {
X		didcreate++;
X		(void) fseek(actfp, 0L, 2); clearerr(actfp);
X		fprintf(actfp, "%s 00000 00001 %c\n", argv[1],
X			(argc > 2 && strcmp(argv[2], "moderated") == 0) 
X				? 'm' : 'y');
X#if defined(USG) || defined(MG1)
X		/*
X		 * U G L Y   K L U D G E
X		 * This utter piece of tripe is the only way I know of
X		 * to get around the fact that ATT BROKE standard IO
X		 * in System 5.2. Basically, you can't open a file for
X		 * "r+" and then try and write to it. This hack works
X		 * on all "real" USG Unix systems, It will probably
X		 * break on some obscure look alike that doesnt use the
X		 * real ATT stdio.h
X		 * also broken in WCW MG-1 42nix 2.0
X		 * Don't blame me, blame ATT. stdio should have
X		 * already done the following line for us, but it didn't
X		 */
X		actfp->_flag |= _IOWRT;
X#endif /* USG */
X		fflush(actfp);
X	}
X
X# ifdef NOTIFY
X	(void) sprintf(subjline, "Newsgroup %s created", argv[1]);
X	fd = mailhdr((struct hbuf *)NULL, subjline);
X	if (fd != NULL) {
X		if (didcreate) 
X			fprintf(fd, 
X		"A new newsgroup called '%s' has been created by %s.\n",
X							argv[1], header.path);
X		else {
X			fprintf(fd, 
X		"%s requested that a new newsgroup called '%s' be created.\n",
X			header.path, argv[1]);
X			fprintf(fd,"It was approved by %s\n\n",header.approved);
X			fprintf(fd, 
X		"You can accomplish this by creating the newgroup yourself\n");
X#  ifdef ORGDISTRIB
X			fprintf(fd,"with a distribution of '%s'.\n",
X				ORGDISTRIB);
X			fprintf(fd,
X				"In other words, by executing the command:\n");
X			fprintf(fd, "%s/inews -d %s -C %s %s\n", LIB, 
X				ORGDISTRIB, argv[1], argc > 2 ? argv[2] : "");
X#  else /* !ORGDISTRIB */
X			fprintf(fd, "In other words, by executing the command:\n");
X			fprintf(fd, "%s/inews -C %s %s\n", LIB, argv[1],
X				argc > 2 ? argv[2] : "");
X#  endif /* !ORGDISTRIB */
X		}
X		(void) mclose(fd);
X	}
X# endif /* NOTIFY */
X	unlock();
X	return 0;
X}
X
X/*
X * rmgroup <groupname>
X * An old newsgroup is being cancelled on a network wide basis.
X */
Xc_rmgroup(argc, argv)
Xchar **argv;
X{
X	FILE *fd;
X	int shouldremove = 0;
X#ifdef NOTIFY
X	char subjline[BUFLEN];
X#endif	/* NOTIFY */
X
X	if (argc < 2)
X		error("rmgroup: Too few arguments.");
X	if (!validng(argv[1]))
X		return 0;
X	if (header.approved[0] == '\0') {
X		logerr("rmgroup: %s not approved", argv[1]);
X		return 1;
X	}
X
X#ifdef MANUALLY
X#ifdef ORGDISTRIB
X	/*
X	 * Allow local as well as organizational removals
X	 */
X	if (!strcmp(ORGDISTRIB, header.distribution)
X	   || !strcmp("local", header.distribution))
X#else	/* !ORGDISTRIB */		
X	if (!strcmp("local", header.distribution))
X#endif	/* !ORGDISTRIB */		
X#endif /* MANUALLY */
X		shouldremove++;
X#ifdef NOTIFY
X	sprintf(subjline, "Received rmgroup for %s", argv[1]);
X	fd = mailhdr((struct hbuf *)NULL, subjline);
X	if (fd != NULL) {
X		if (shouldremove) {
X		    fprintf(fd, "Newsgroup '%s' has been removed by %s.\n\n",
X				argv[1], header.path);
X#  ifdef USG
X		    fprintf(fd, "You may need to remove the directory %s by hand\n",
X				dirname(argv[1]));
X#  endif
X		} else {
X		    fprintf(fd, "%s requested that newsgroup %s be removed.\n",
X				header.path, argv[1]);
X		    fprintf(fd, "You should remove it by hand\n");
X		    fprintf(fd, "To do this, execute the command\n");
X		    fprintf(fd, "\t%s/rmgroup %s\n", LIB, argv[1]);
X		}
X		(void) mclose(fd);
X	}
X#endif /* NOTIFY */
X
X	if (shouldremove) {
X		int pid, status;
X		/* We let the shell do all the work.
X		 * See the rmgrp shell script.
X		 */
X		lock();
X		(void) sprintf(bfr, "%s/rmgroup", LIB);
X
X		if (pid = vfork()) {
X			status = fwait(pid);
X		} else {
X			(void) setuid(duid);
X			execvp(bfr, argv);
X		}
X		unlock();
X		if (status)
X			log("rmgroup status %d", status);
X	}
X	return 0;
X}
X
X/*
X * cancel <artid>
X * Cancel the named article
X */
Xc_cancel(argc, argv)
Xchar **argv;
X{
X	char *line, *p, *q, *r, *poster;
X	char *findhist();
X	register FILE *fp;
X	char whatsisname[BUFLEN], nfilename[BUFLEN];
X	time_t t;
X	int su = 0;
X#ifndef u370
X	struct hbuf htmp;
X#endif /* !u370 */
X
X	if (argc < 2)
X		error("cancel: Too few arguments.");
X	(void) strcpy(whatsisname, senderof(&header));
X	line = findhist(argv[1]);
X	if (line == NULL) {
X		struct tm *tm;
X		log("Can't cancel %s:  non-existent", argv[1]);
X		(void) time(&t);
X		tm = localtime(&t);
X#ifdef USG
X		sprintf(bfr,"%s\t%2.2d/%2.2d/%d %2.2d:%2.2d\tcancelled",
X#else /* !USG */
X		sprintf(bfr,"%s\t%02d/%02d/%d %02d:%02d\tcancelled",
X#endif /* !USG */
X		   argv[1], tm->tm_mon+1, tm->tm_mday, tm->tm_year, tm->tm_hour,
X		   tm->tm_min);
X		savehist(bfr);
X		return -1;
X	}
X
X	q = index(line, '\t');
X	p = index(q+1, '\t');
X	if (p == NULL || *++p == '\0' || *p == '\n') {
X		*q = '\0';
X		log("Expired article %s", line);
X		return -1;
X	}
X	if (strcmp(p, "cancelled") == 0) {
X		*q = '\0';
X		log("Already Cancelled %s", line);
X		return -1;
X	} else
X		log("Cancelling %s", line);
X	if ((uid == ROOTID||uid == 0) && (
X#ifdef ORGDISTRIB
X		strcmp(header.distribution, ORGDISTRIB) == 0 ||
X#endif /* ORGDISTRIB */
X		strcmp(header.distribution, "local") == 0))
X		su = 1;
X	while (*p) {
X		q = index(p, ' ');
X		if (q)
X			*q = '\0';
X		(void) strcpy(nfilename, dirname(p));
X		fp = fopen(nfilename, "r");
X		if (fp == NULL) {
X			log("Can't cancel %s: %s", line, errmsg(errno));
X			return 1;
X		}
X		htmp.unrec[0] = NULL;
X		if (hread(&htmp, fp, TRUE) == NULL) {
X			if (bfr[0] == '/') {
X				fp = fopen(bfr, "r");
X				if (fp == NULL
X					|| hread(&htmp, fp, TRUE) == NULL)
X					error("Article is garbled.");
X			} else 
X				error("Article is garbled.");
X		}
X		(void) fclose(fp);
X		poster = senderof(&htmp);
X		/* only compare up to '.' or ' ' */
X		r = index(poster,'.');
X		if (r == NULL)
X			r = index(poster,' ');
X		if (r != NULL)
X			*r = '\0';
X		if (!su && strncmp(whatsisname, poster,strlen(poster))) {
X			error("Not contributor: posted by %s, and you are %s", poster, whatsisname);
X		}
X
X		(void) unlink(nfilename);
X		p = q+1;
X	}
X	return 0;
X}
X
X/*
X * sendsys	(no arguments)
X *
X * Mail the sys file to the person submitting the article.
X * POLICY: the contents of your sys file are public information
X * and as such, you should not change this code.  You may feel
X * free to arrange for it to manually notify you, in the event
X * that you want to do something to clean it up before it goes out.
X * Secret sites on the net are expressly frowned on.
X * 
X * The purpose of this command is for making a network map.  The
X * details of your link and which newsgroups are forwarded are not
X * important, in case you want to sanitize them.  Since the definition
X * of USENET is those sites getting net.announce, you can disable this
X * on sites not getting net articles, but if you take out the list of
X * forwarded newsgroups, and you have sites that only get local newsgroups,
X * you should make this clear, or remove those sites from what you send out.
X */
X/* ARGSUSED */
Xc_sendsys(argc, argv)
Xchar **argv;
X{
X	register FILE *f, *u;
X	int c;
X
X#ifdef NOTIFY
X	f = mailhdr((struct hbuf *)NULL, "sendsys control message");
X	if (f != NULL) {
X		fprintf(f, "%s requested your %s/sys file.\n", header.path, LIB);
X		fprintf(f, "It has been sent.\n");
X		(void) mclose(f);
X	}
X#endif /* NOTIFY */
X	f = mailhdr(&header, "response to your sendsys request");
X	u = fopen(SUBFILE, "r");
X	if (f != NULL && u != NULL) {
X		while ((c=getc(u)) != EOF)
X			putc(c, f);
X		(void) fclose(u);
X		(void) mclose(f);
X	}
X	return 0;
X}
X
X/*
X * Send the version number to the right person.
X */
X/* ARGSUSED */
Xc_version(argc, argv)
Xchar **argv;
X{
X	register FILE *f;
X
X	f = mailhdr(&header, "Our news version");
X	if (f == NULL)
X		error("Cannot send back error message");
X	fprintf(f, "Currently running news version %s.\n\n", news_version);
X	fprintf(f, "The header of your message follows:\n\n");
X	(void) hwrite(&header, f);
X	(void) mclose(f);
X	return 0;
X}
X
X/*
X * Check the active file for old or missing newsgroups
X * Body of article is list of valid groups
X */
X/* ARGSUSED */
Xc_checkgroups(argc, argv)
Xchar **argv;
X{
X	int rc;
X
X	(void) setuid(geteuid());
X	/* dont change the cat %s| to < %s, it breaks some "unix" systems */
X	(void) sprintf(bfr, "cat %s | %s/checkgroups %s", INFILE, LIB,
X#ifdef NOTIFY
X		(TELLME && *TELLME) ? TELLME : NEWSUSR );
X#else /* !NOTIFY */
X		NEWSUSR);
X#endif /* !NOTIFY */
X	rc = system(bfr);
X	log("system(%s) status %d", bfr, rc);
X	return 0;
X}
X
X/*
X * An unknown control message has been received.
X */
Xc_unknown(h, ctlmsgtext)
Xstruct hbuf *h;
Xchar *ctlmsgtext;
X{
X	register FILE *f;
X
X	log("UNKNOWN Ctl Msg %s from %s", ctlmsgtext, h->path);
X#ifdef NOTIFY
X	f = mailhdr((struct hbuf *)NULL, "Unrecognized Control Message");
X	if (f != NULL) {
X		fprintf(f, "Currently running news version %s.\n\n", news_version);
X		fprintf(f, "The header of the message follows:\n\n");
X		(void) hwrite(h, f);
X		(void) mclose(f);
X	}
X#endif /* NOTIFY */
X	return 0;
X}
X
X/* ARGSUSED */
Xc_unimp(argc, argv)
Xchar **argv;
X{
X	register FILE *f;
X
X#ifdef NOTIFY
X	f = mailhdr((struct hbuf*)NULL, "Unimplemented Control Message");
X	if (f != NULL) {
X		fprintf(f, "Currently running news version B %s.\n\n", news_version);
X		fprintf(f, "The header of the message follows:\n\n");
X		(void) hwrite(&header, f);
X		(void) mclose(f);
X	}
X#endif /* NOTIFY */
X	return 0;
X}
X
X/*
X * This is a modified version of popen, made more secure.  Rather than
X * forking off a shell, you get a bare process.  You must have exactly
X * one argument, and the command must be mail (or sendmail if you have it).
X */
X#define	RDR	0
X#define	WTR	1
Xstatic	int	mopen_pid[20];
Xchar *replyname();
X
XFILE *
Xmhopen(hptr)
Xstruct hbuf *hptr;
X{
X	int p[2];
X	register myside, hisside, pid;
X	char *sendto = "usenet";
X
X	if (hptr)
X		sendto = replyname(hptr);
X	else {
X#ifdef NOTIFY
X		if (TELLME)
X			sendto = TELLME;
X#endif /* NOTIFY */
X		if (sendto == NULL || *sendto == NULL)
X			return NULL;
X	}
X	verifyname(sendto);
X	if(pipe(p) < 0)
X		return NULL;
X	myside = p[WTR];
X	hisside = p[RDR];
X	if((pid = vfork()) == 0) {
X		/* myside and hisside reverse roles in child */
X		(void) close(myside);
X		(void) close(0);
X		(void) dup(hisside);
X		(void) close(hisside);
X		(void) setgid(gid);
X		(void) setuid(uid);
X#ifdef SENDMAIL
X		execl(SENDMAIL, "sendmail", "-oi", "-oeq", sendto, (char *)NULL);
X#endif /* SENDMAIL */
X#ifdef MMDF
X		execl(MMDF, "inews-mail", "-smuxto,cc*", (char *)NULL);
X#endif /* MMDF */
X		execl("/bin/mail", "mail", sendto, (char *)NULL);
X		execl("/usr/bin/mail", "mail", sendto, (char *)NULL);
X		execl("/usr/ucb/mail", "mail", sendto, (char *)NULL);
X		_exit(1);
X	}
X	if(pid == -1)
X		return NULL;
X	mopen_pid[myside] = pid;
X	(void) close(hisside);
X	return(fdopen(myside, "w"));
X}
X
Xmclose(ptr)
XFILE *ptr;
X{
X	register f, r, (*hstat)(), (*istat)(), (*qstat)();
X	int status;
X
X	f = fileno(ptr);
X	(void) fclose(ptr);
X	istat = signal(SIGINT, SIG_IGN);
X	qstat = signal(SIGQUIT, SIG_IGN);
X	hstat = signal(SIGHUP, SIG_IGN);
X	while((r = wait(&status)) != mopen_pid[f] && r != -1)
X		;
X	if(r == -1)
X		status = -1;
X	signal(SIGINT, istat);
X	signal(SIGQUIT, qstat);
X	signal(SIGHUP, hstat);
X	return status;
X}
X
X/*
X * mhopen a pipe to mail, write out a std header, and return the file ptr.
X *
X * We don't include a From: field because this is probably uucp, i.e.
X * explicitly routed.  Leave it up to the recipient's mailer.
X * Always include the To: field because if we ge back failed mail, we
X * might be able to deliver it by hand if we know to wom it was addressed.
X * By convention, hptr==NULL means to send the message to the local contact person.
X */
XFILE *
Xmailhdr(hptr, subject)
Xstruct hbuf *hptr;
Xchar  *subject;
X{
X	FILE *fp;
X	time_t now;
X	char *to = "usenet";
X	extern char *mydomain();
X
X#ifdef NOTIFY
X	if (TELLME && *TELLME)
X		to = TELLME;
X#endif /* NOTIFY */
X	if (hptr)
X		to = replyname(hptr);
X
X	if ((fp = mhopen(hptr)) != NULL) {
X		(void) time(&now);
X		fprintf(fp, "Date: %s\n", arpadate(&now));
X#ifdef MMDF
X		fprintf(fp, "From: The News System <usenet@%s>\n",
X				FROMSYSNAME);
X#endif /* MMDF */
X		fprintf(fp, "To: %s\n", to);
X		fprintf(fp, "Subject: %s\n", subject);
X		fprintf(fp, "Responding-System: %s\n\n", LOCALSYSNAME);
X	}
X	return fp;
X}
X
X/*
X * verify that the name mail is being sent to does not contain any
X * nasty hooks to invoke funny functions from the shell or the like.
X */
Xverifyname(sendto)
Xchar *sendto;
X{
X	/* Be sure we DO allow alphabetics, !, :, ., -, @. *. */
X	char *nasty = "\"'\\`^|;& <>/~";
X	register char *p;
X
X	if (sendto[0] <= ' ') {
X		xerror("nasty mail name %s from %s", sendto, header.path);
X	}
X	for (p=sendto; *p; p++) {
X		if (*p == ' ') {
X			*p = 0;
X			break;
X		}
X	}
X	if (strpbrk(sendto, nasty) != NULL)
X		error("nasty mail name %s from %s", sendto, header.path);
X
X	for (nasty = sendto; (nasty = index(nasty, '.')) != NULL; ) {
X		if (*++nasty == '.')	/* check for .. */
X			error("nasty mail name %s from %s", sendto, header.path);
X	}
X}
X
X/*
X * Checks to make sure the control message is OK to post.
X */
Xctlcheck()
X{
X	char msg[BUFLEN];
X	char *p;
X
X	if (!is_ctl)
X		return;
X
X	if (header.ctlmsg[0])
X		(void) strcpy(msg, header.ctlmsg);
X	else
X		(void) strcpy(msg, header.title);
X
X	p = index(msg, ' ');
X	if (p)
X		*p = 0;
X	
X	if (strcmp(msg, "ihave") == 0 || strcmp(msg, "sendbad") == 0 ||
X		strcmp(msg, "sendme") == 0) {
X		return;	/* no restrictions */
X	} else if (strcmp(msg, "newgroup") == 0) {
X		suser();
X	} else if (strcmp(msg, "rmgroup") == 0) {
X		suser();
X	} else if (strcmp(msg, "sendsys") == 0) {
X		suser();
X	} else if (strcmp(msg, "checkgroups") == 0) {
X		suser();
X	} else if (strcmp(msg, "version") == 0) {
X		return;	/* no restrictions */
X	} else if (strcmp(msg, "cancel") == 0) {
X		return;	/* no restrictions at this level */
X	} else if (strcmp(msg, "delsub") == 0) {
X		if (!prefix(header.nbuf, "to.")) {
X			log("Must be in a 'to.system' newsgroup.");
X			xxit(0);
X		}
X		return;
X	} else {
X		log("Unrecognized control message - %s\n", msg);
X		xxit(0);
X	}
X}
X
X/* Make sure this guy is special. */
Xsuser()
X{
X	if (uid == 0 || uid == ROOTID)
X		return;
X	/*
X	 * We assume that since our real uid is the same as NEWSUSR
X	 * (the euid) we were run by rootid and it did a setuid.
X	 * Too bad we can't set just the effective uid like suid does.
X	 */
X	if (uid == geteuid())
X		return;
X#ifdef IHCC
X	printf("Please use the command:\n\ttoolnews providers\n");
X	printf("then call one of the news people.\n");
X#else
X	printf("Get your local netnews contact to do it for you.\n");
X#endif
X	xxit(0);
X}
END_OF_FILE
if test 26215 -ne `wc -c <'control.c'`; then
    echo shar: \"'control.c'\" unpacked with wrong size!
fi
# end of 'control.c'
fi
if test -f 'readr.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'readr.c'\"
else
echo shar: Extracting \"'readr.c'\" \(25018 characters\)
sed "s/^X//" >'readr.c' <<'END_OF_FILE'
X/*
X * This software is Copyright (c) 1986 by Rick Adams.
X *
X * Permission is hereby granted to copy, reproduce, redistribute or
X * otherwise use this software as long as: there is no monetary
X * profit gained specifically from the use or reproduction or this
X * software, it is not sold, rented, traded or otherwise marketed, and
X * this copyright notice is included prominently in any copy
X * made.
X *
X * The author make no claims as to the fitness or correctness of
X * this software for any use whatsoever, and it is provided as is. 
X * Any use of this software is at the user's own risk.
X *
X * readr - /bin/mail and msgs interface and associated functions.
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)readr.c	2.61	3/21/87";
X#endif /* SCCSID */
X
X#include "rparams.h"
X#if defined(BSD4_2) || defined(BSD4_1C)
X#include <sys/dir.h>
X#else
X#include "ndir.h"
X#endif /* !BSD4_2 && !BSD4_1C */
X#include <setjmp.h>
X#include <errno.h>
X
Xextern int errno;
X
Xchar *Progname = "readnews";	/* used by xerror to identify failing program */
X
Xstatic char	lbuf[BUFLEN*2];
Xlong atol();
X
X#define	saveart	oobit = bit;strcpy(ofilename1, filename);strcpy(ogroupdir, groupdir);hptr = h;h = hold;hold = hptr;ongsize = pngsize
X#define NLINES(h, fp) (h->numlines[0] ? h->intnumlines : (h->intnumlines=linecnt(fp),sprintf(h->numlines, "%d", h->intnumlines), h->intnumlines))
X
Xchar *tft = "/tmp/folXXXXXX";
X
X/*
X * These were made static for u370 with its buggy cc.
X * I judged it better to have one copy with no ifdefs than
X * to conditionally compile them as automatic variables
X * in readr (which they originally were).  Performance
X * considerations might warrant moving some of the simple
X * things into register variables, but I don't know what
X * breaks the u370 cc.
X */
Xstatic char goodone[BUFLEN];		/* last decent article		*/
Xstatic char ogroupdir[BUFLEN];		/* last groupdir		*/
Xstatic char address[PATHLEN];		/* for reply copy		*/
Xstatic char edcmdbuf[128];
Xstatic int rfq = 0;			/* for last article		*/
Xstatic long ongsize;			/* Previous ngsize		*/
Xstatic long pngsize;			/* Printing ngsize		*/
Xstatic char *bptr;			/* temp pointer.		*/
Xstatic struct srec srec;		/* srec for sys file entries	*/
Xstatic char *tfilename;			/* temporary file name 		*/
Xstatic char ofilename1[BUFLEN];		/* previous file name		*/
Xstatic struct hbuf hbuf1, hbuf2,	/* for minusing			*/
X		*h = &hbuf1,		/* current header		*/
X		*hold = &hbuf2,		/* previous header		*/
X		*hptr;			/* temporary 			*/
Xstatic char *ptr1, *ptr2, *ptr3;	/* for reply manipulation	*/
Xstatic int  abs = FALSE;		/* TRUE if we asked absolutely	*/
Xstatic char tf[100];
Xstatic long oobit;			/* last bit, really		*/
Xstatic int dgest = 0;
Xstatic FILE *ofp;			/* Current output file to terminal*/
Xstatic FILE *fp;			/* current article to be printed*/
Xstatic int holdup;			/* 1 iff should stop before hdr */
Xstatic int ignorenews;			/* 1 iff readnews -p > /dev/null*/
Xstatic time_t timelastsaved;		/* time newsrc last written out */
Xstatic jmp_buf sigjmpbuf;		/* for signal processing */
Xstatic int canlongjmp;			/* TRUE if setjmp on sigjmp valid */
Xshort ospeed;				/* terminal speed NOT STATIC */
X					/* used in readnews.c, declared here */
X					/* to match declaration in visual.c */
X
Xint catchcont();
Xreadr()
X{
X	register char *m = getenv("MORE");
X	register char *m2, cc;
X
X	/*
X	 * Turn of more's 'l' option, so \f kludge will work.
X	 * This is really revolting!
X	 */
X	if (m2 = m) {
X		while (cc = *m++)
X			if (cc != 'l')
X				*m2++ = cc;
X		*m2 = '\0';
X	}
X
X#ifdef DEBUG
X	fprintf(stderr, "readr()\n");
X#endif
X	if (aflag) {
X		if (*datebuf) {
X			if ((atime = cgtdate(datebuf)) == -1)
X				xerror("Cannot parse date string");
X		} else
X			atime = 0;
X	}
X
X	if (pflag && ignoring())
X		ignorenews = TRUE;
X
X	if (xflag)
X		uflag = 0;
X	if (uflag)
X		(void) time(&timelastsaved);
X
X	ofp = stdout;
X	if (cflag && coptbuf[0] != '\0') {
X		(void) umask(022);
X		(void) mktemp(outfile);	/* get "unique" file name */
X		(void) close(creat(outfile,0666));
X		ofp = xfopen(outfile, "w");
X		(void) umask(N_UMASK);
X		cflag = FALSE;
X		pflag = TRUE;
X	}
X
X	/* loop reading articles. */
X	fp = NULL;
X	obit = -1;
X	nextng();
X	for ( ;; ) {
X		if (getnextart(FALSE))
X			break;
X#ifdef DEBUG
X		fprintf(stderr,"after getnextart, fp %x, pos %ld, bit %ld, group '%s', filename '%s'\n",
X			fp, ftell(fp), bit, groupdir, filename);
X#endif
X		(void) strcpy(goodone, filename);
X		if (pflag || lflag || eflag) {
X			/* This code should be gotten rid of */
X			if (SigTrap) {
X				qfflush(ofp);
X				fprintf(ofp, "\n");
X				cdump(ofp);
X				xxit(0); /* kludge! drop when qfflush works */
X				return;
X			}
X			clear(bit);
X			nextbit();
X			FCLOSE(fp);
X			continue;
X		}
X		for ( ;; ) {
X			char *pp;
X			int nlines;
X			int (*ointr)();
X#ifdef	SIGCONT
X			int (*ocont)();
X#endif
X			(void) setjmp(sigjmpbuf);
X			canlongjmp = TRUE;
X
X			SigTrap = FALSE;
X			if (!cflag) {
X				if (rfq)
X					(void) sprintf(bfr, "Last article.  [qfr] ");
X				else {
X					nlines = NLINES(h, fp);
X					if (nlines <= 0) {
X						(void) sprintf(bfr, "(0 lines) Next? [nqfr] ");
X						FCLOSE(fp);
X					} else {
X						(void) sprintf(bfr, "(%d lines) More? [ynq] ", nlines);
X					}
X				}
X			} else
X				(void) sprintf(bfr, "? ");
X			fprintf(ofp, "%s", bfr);
X			(void) fflush(ofp);
X			bptr = lbuf;
X			ointr = signal(SIGINT, catchcont);
X#ifdef SIGCONT
X			ocont = signal(SIGCONT, catchcont);
X#endif
X			pp = fgets(bptr, BUFLEN, stdin);
X			canlongjmp = FALSE;
X			(void) signal(SIGINT, ointr);
X#ifdef SIGCONT
X			(void) signal(SIGCONT, ocont);
X#endif
X			if (pp != NULL)
X				break;
X			if (!SigTrap)
X				return;
X#ifdef SIGCONT
X			if (SigTrap != SIGCONT)
X#endif
X				fprintf(ofp, "\n");
X		}
X		(void) nstrip(bptr);
X		while (*bptr == ' ' || *bptr == '\t')
X			bptr++;
X		if (command())
X			break;
X	}
X
X	if (!pflag && !news) {
X		fprintf(stderr, "No news.\n");
X	}
X	cout(ofp);
X}
X
X#define EOL() if (*bptr != '\0') { fprintf(ofp, "? for commands.\n"); return FALSE; }
X/*
X * Process one command, which has already been typed in.
X */
Xcommand()
X{
X	char *findhist();
X	long i;
X
X	switch (*bptr++) {
X
X	/* display list of articles in current group */
X	case 'l':
X	case 'L':
X		list_group(groupdir, atoi(bptr),
X			(*(bptr - 1) == 'l') ? FALSE : TRUE, pngsize);
X		break;
X	
X	/* No.  Go on to next article. */
X	case 'n':
X		EOL();
X		readmode = NEXT;
X		if (!cflag)
X			FCLOSE(fp);
X		fprintf(ofp, "\n");
X		clear(bit);
X		saveart;
X		nextbit();
X		break;
X
X	/* Undigestify the article. */
X	case 'd':
X		dgest = 1;
X		/* fall through */
X
X	/* yes: print this article, go on. */
X	case 'y':
X		EOL();
X		/* fall through. */
X
X	/* The user hit return.  Default is 'y' unless rfq, then it's 'q'. */
X	case '\0':
X		if (!bptr[-1] && rfq)
X			return TRUE;
X		readmode = NEXT;
X		showtail(fp);
X		clear(bit);
X		saveart;
X		nextbit();
X		break;
X
X	/*
X	 * Unsubscribe to the newsgroup and go on to next group
X	 */
X	case 'u':
X		fprintf(ofp, "To unsubscribe, use 'U'\n");
X		break;
X
X	case 'U':
X		fprintf(ofp, "Unsubscribing to newsgroup: %s\n", groupdir);
X		obit = -1;
X		FCLOSE(fp);
X		if (cflag)
X			clear(bit);
X		else
X			putc('\n', ofp);
X		rfq = 0;
X		zapng = TRUE;
X		saveart;
X		if (nextng()) {
X			if (actdirect == BACKWARD)
X				fprintf(ofp, "Can't back up.\n");
X			else
X				return TRUE;
X		}
X		break;
X
X		/* Print the current version of news */
X	case 'v':
X		fprintf(ofp, "News version: %s\n", news_version);
X		break;
X
X		/* reprint the article */
X	case 'p':
X		EOL();
X		if (!cflag)
X			goto minus;
X		readmode = NEXT;
X		if (!cflag) {
X			FCLOSE(fp);
X			bit = last;
X			putc('\n', ofp);
X		}
X		obit = -1;
X		break;
X
X		/* decrypt joke */
X	case 'D':
X		caesar_command();
X		readmode = NEXT;
X		clear(bit);
X		saveart;
X		nextbit();
X		break;
X
X		/* write out the article someplace */
X	case 's':
X	case 'w':
X		{
X		char *grn = groupdir;
X		tfilename = filename;
X		if (*bptr == '-') {
X			bptr++;
X			grn = ogroupdir;
X			if (*ofilename1)
X				tfilename = ofilename1;
X		}
X		if (*bptr != '\0' && *bptr != ' ') {
X			fprintf(ofp, "Bad file name.\n");
X			break;
X		}
X		while (*bptr == ' ')
X			bptr++;
X		if (*bptr != '|' && *bptr != '/') {
X			char	hetyped[BUFLEN];
X			char	*boxptr;
X			struct	stat stbf;
X  			(void) strcpy(hetyped, bptr);
X			if (hetyped[0] == '~' && hetyped[1] == '/') {
X  				strcpy(hetyped, bptr+2);
X  				strcpy(bptr, userhome);
X			} else if (boxptr = getenv("NEWSBOX")) {
X 				if (index(boxptr, '%')) {
X 					sprintf(bptr, boxptr, grn);
X 				    	if (stat(bptr, &stbf) < 0) {
X						if (mkdir(bptr, 0777) < 0) {
X							fprintf(ofp, "Cannot create directory %s", bptr);
X							break;
X						}
X					} else if ((stbf.st_mode & S_IFMT) != S_IFDIR) {
X						fprintf(ofp, "%s is not a directory", bptr);
X						break;
X					}
X				} else
X				    strcpy(bptr, boxptr);
X			} else
X				(void) strcpy(bptr, ".");
X			(void) strcat(bptr, "/");
X			if (hetyped[0] != '\0')
X				(void) strcat(bptr, hetyped);
X			else
X				(void) strcat(bptr, "Articles");
X		}
X		fwait(fsubr(save, tfilename, bptr));
X		}
X		break;
X
X		/* back up  */
X	case '-':
Xminus:
X		rfq = 0;
X		abs = TRUE;
X		if (!*ofilename1) {
X			fprintf(ofp, "Can't back up.\n");
X			break;
X		}
X		if (cflag)
X			clear(bit);
X		else {
X			FCLOSE(fp);
X			putc('\n', ofp);
X		}
X		hptr = h;
X		h = hold;
X		hold = hptr;
X		(void) strcpy(bfr, filename);
X		(void) strcpy(filename, ofilename1);
X		(void) strcpy(ofilename1, bfr);
X		obit = bit;
X		if (strcmp(groupdir, ogroupdir)) {
X			(void) strcpy(bfr, groupdir);
X			selectng(ogroupdir, TRUE, FALSE);
X			(void) strcpy(groupdir, ogroupdir);
X			(void) strcpy(ogroupdir, bfr);
X			ngrp = 1;
X			back();
X		}
X		bit = oobit;
X		oobit = obit;
X		obit = -1;
X		(void) getnextart(TRUE);
X		return FALSE;
X
X		/* skip forwards */
X	case '+':
Xcaseplus:
X		if (*bptr == '\0')
X			(void) strcat(bptr, "1");
X		rfq = 0;
X		if (cflag)
X			clear(bit);
X		saveart;
X		last = bit;
X		for (i = 0; i < atol(bptr); i++) {
X			nextbit();
X			if ((bit > pngsize) || (rflag && bit < 1))
X				break;
X		}
X		if (!cflag) {
X			putc('\n', ofp);
X			FCLOSE(fp);
X		}
X		obit = -1;
X		break;
X
X	/* exit - time updated to that of most recently read article */
X	case 'q':
X		EOL();
X		return TRUE;
X
X	/* exit - no time update. */
X	case 'x':
X		EOL();
X		xxit(0);
X
X	/* cancel the article. */
X	case 'c':
X		(void) cancel_command();
X		break;
X
X	/* escape to shell */
X	case '!':
X		fwait(fsubr(ushell, bptr, (char *)NULL));
X		fprintf(ofp, "\n");
X		hdr();
X		break;
X
X	/* mail reply */
X	case 'r':
X		(void) reply_command();
X		break;
X
X	/* send to some system */
X	case 'X':
X		xmit_command();
X		break;
X	/* mark the rest of the articles in this group as read */
X	case 'K':
X		saveart;
X		while (bit <= pngsize && bit >= minartno) {
X			clear(bit);
X			nextbit();
X		}
X		FCLOSE(fp);
X		break;
X
X	/* next newsgroup */
X	case 'P':
X		*bptr = '-';
X	case 'N':
X		FCLOSE(fp);
X		if (next_ng_command())
X			return TRUE;
X		break;
X
X	case 'b':	/* back up 1 article */
X		i = bit - 1;
X		goto tryartnum;
X	case '0':	/* specific no. */
X	case '1':
X	case '2':
X	case '3':
X	case '4':
X	case '5':
X	case '6':
X	case '7':
X	case '8':
X	case '9':
X		(void) sscanf(--bptr, "%ld", &i);
X		if (i == 0) {
X			fprintf(ofp, "Bad article no.\n");
X			break;
X		}
X		if (i > pngsize) {
X			fprintf(ofp, "Not that many articles.\n");
X			break;
X		}
Xtryartnum:
X		readmode = SPEC;
X		abs = TRUE;
X		bit = i;
X		obit = -1;
X		if (!cflag) {
X			putc('\n', ofp);
X			FCLOSE(fp);
X		}
X		rfq = 0;
X		break;
X
X	/* specific message ID. */
X	case '<':
X		ptr1 = findhist(--bptr);
X		if (ptr1 == NULL) {
X			fprintf(ofp, "No such article: %s.\n", bptr);
X			break;
X		}
X		ptr2 = index(ptr1, '\t');
X		ptr3 = index(++ptr2, '\t');
X		ptr2 = index(++ptr3, ' ');
X		if (ptr2)
X			*ptr2 = '\0';
X		ptr2 = index(ptr3, '/');
X		if (!ptr2) {
X			if (strcmp(ptr3, "cancelled") == 0) {
X				fprintf(ofp, "Article %s has been cancelled.\n",
X					bptr);
X				break;
X			}
X			fprintf(ofp, "Article %s (dated %s) has expired.\n",
X				bptr, index(ptr1, '\t')+1);
X			break;
X		}
X		*ptr2++ = '\0';
X		abs = TRUE;
X		if (cflag)
X			clear(bit);
X		else {
X			FCLOSE(fp);
X			putc('\n', ofp);
X		}
X		saveart;
X		(void) strcpy(ogroupdir, ptr3);
X		if (strcmp(groupdir, ogroupdir)) {
X			(void) strcpy(bfr, groupdir);
X			selectng(ogroupdir, TRUE, PERHAPS);
X			(void) strcpy(groupdir, ogroupdir);
X			(void) strcpy(ogroupdir, bfr);
X			ngrp = 1;
X			back();
X		}
X		(void) sscanf(ptr2, "%ld", &bit);
X		oobit = obit;
X		obit = -1;
X		i = bit;
X		(void) getnextart(TRUE);
X		if (bit != i || strcmp(groupdir, ptr3) != 0) {
X			(void) fprintf(ofp, "Can't read %s/%ld.\n", ptr3, i);
X			goto minus;
X		}
X		rfq = 0;
X		break;
X
X	/* follow-up article */
X	case 'f':
X		if (strcmp(h->followto, "poster") == 0) {
X			(void) reply_command();
X			break;
X		}
X
X		if (*bptr == '-')
X			tfilename = ofilename1;
X		else
X			tfilename = filename;
X		(void) sprintf(bfr,"%s/%s %s", BIN, "postnews", tfilename);
X		(void) system(bfr);
X		break;
X
X	/* erase - pretend we haven't seen this article. */
X	case 'e':
X		if (rfq || *bptr == '-') {
X			if (strcmp(groupdir, ogroupdir)) {
X				i = bit;
X				(void) strcpy(bfr, groupdir);
X				selectng(ogroupdir, FALSE, PERHAPS);
X				set(oobit);
X				fprintf(ofp,"Holding article %ld newsgroup %s\n"
X					,oobit, ogroupdir);
X				(void) strcpy(groupdir, ogroupdir);
X				selectng(bfr, FALSE, FALSE);
X				bit = i;
X			} else {
X				fprintf(ofp,"Holding article %ld\n", oobit);
X				set(oobit);
X			}
X		} else {
X			fprintf(ofp,"Holding article %ld\n", bit);
X			set(bit);
X			goto caseplus;	/* skip this article for now */
X		}
X		break;
X
X	case 'H':
X	case 'h':
X		if (!hflag)
X			dash(8, ofp);
X		if (*bptr == '-') {
X			if (oobit > 0)
X				fprintf(ofp, "Article %ld:\n", oobit);
X			hprint(hold, ofp, 1 + (bptr[-1]=='H'));
X		} else {
X			fprintf(ofp, "Article %ld of %ld: %s\n",
X				rfq ? oobit : bit, pngsize, h->ident);
X			hprint(h, ofp, 1 + (bptr[-1]=='H'));
X		}
X		if (!hflag)
X			dash(8, ofp);
X		break;
X
X	case '#':
X		fprintf(ofp, "Article %ld of %ld: newsgroup %s\n",
X			rfq ? oobit : bit, pngsize, rfq ? ogroupdir : groupdir);
X		break;
X
X		/* error */
X	case '?':
X		help(ofp);
X		break;
X	default:
X		fprintf(ofp, "? for commands.\n");
X		break;
X	}
X
X	return FALSE;
X}
X
Xcancel_command()
X{
X	int notauthor;
X	tfilename = filename;
X	hptr = h;
X	if (*bptr == '-') {
X		if (*ofilename1) {
X			tfilename = ofilename1;
X			hptr = hold;
X		}
X		bptr++;
X	}
X	EOL();
X	readmode = SPEC;
X	(void) strcpy(rcbuf, hptr->path);
X	ptr1 = index(rcbuf, ' ');
X	if (ptr1)
X		*ptr1 = 0;
X	notauthor = strcmp(username, rcbuf);
X	if (uid != ROOTID && uid && notauthor) {
X		fprintf(ofp, "Can't cancel what you didn't write.\n");
X		return FALSE;
X	}
X	if (!cancel(ofp, hptr, notauthor) && hptr == h) {
X		clear(bit);
X		saveart;
X		nextbit();
X		obit = -1;
X		if (!cflag)
X			putc('\n', ofp);
X		FCLOSE(fp);
X	}
X	return TRUE;
X}
X
Xreply_command()
X{
X	register char	*pathptr;
X	int edit = 1;
X	char *ed, *fbp;
X	int idlen;
X	FILE *tfp;
X	char *replyname();
X	char subj[BUFLEN];
X	char folbuf[BUFLEN];
X	struct stat statb;
X	long creatm;
X
X	hptr = h;
X	while (*bptr && index("d-", *bptr)) {
X		switch (*bptr) {
X		/* Followup the previous article. */
X		case '-':
X			hptr = hold;
X			break;
X
X		/* Don't edit the headers */
X		case 'd':
X			edit = 0;
X			break;
X		}
X		bptr++;
X	}
X	EOL();
X	ptr1 = index(MAILPARSER, ' ');
X	if (ptr1)
X		*ptr1 = '\0';
X	if (edit && access(MAILPARSER, 1)) {
X#ifdef IHCC
X		fprintf(stderr, "Can't edit headers, 'recmail' missing.\n");
X#else
X		fprintf(stderr, "Can't edit headers without %s\n", MAILPARSER);
X#endif
X		edit = 0;
X	}
X	if (ptr1)
X		*ptr1 = ' ';
X
X	*rcbuf = '\0';
X	pathptr = replyname(hptr);;
X	for (ptr1 = address, ptr2 = pathptr; *ptr2; ptr1++, ptr2++) {
X		if (index("\"\\$", *ptr2))
X			*ptr1++ = '\\';
X		*ptr1 = *ptr2;
X	}
X	*ptr1 = '\0';
X
X	folbuf[0] = '\0';		/* References */
X	if (hptr->followid[0]) {
X		fbp = hptr->followid;
X		idlen = strlen(hptr->ident);
X
X		/*
X		 * If the references line is too long, truncate it.
X		 * The "3" is for the comma, the space, and the '\0' at
X		 * the end of the string.
X		 */
X		while (fbp && strlen(fbp) + idlen > BUFLEN - 3)
X			fbp = index(fbp + 1, '<');
X		if (fbp != NULL) {
X			(void) strcpy(folbuf, fbp);
X			(void) strcat(folbuf, " ");
X		}
X	}
X	(void) strcat(folbuf, hptr->ident);
X
X	(void) strcpy(subj, hptr->title);	/* Subject */
X	while (isspace(*bptr))
X		bptr++;
X	if (*bptr != '\0')
X		(void) strcpy(subj, bptr);
X	if (!prefix(subj, "Re:")){
X		(void) strcpy(bfr, subj);
X		(void) sprintf(subj, "Re: %s", bfr);
X	}
X	if (!edit) {
X		fprintf(ofp, "To: %s\n", pathptr);
X		ed = index(MAILER, '%');
X		if (ed && ed[1] == 's')
X			fprintf(ofp, "Subject: %s\n", subj);
X		(void) fflush(ofp);
X	}
X
X	/* Put the user in the editor to create the body of the followup. */
X	if (edit) {
X		int oumask;
X
X		(void) strcpy(tf, tft);
X		(void) mktemp(tf);
X
X		ed = getenv("EDITOR");
X		if (ed == NULL)
X			ed = DFTEDITOR;
X
X		oumask = umask(077);
X		if ((tfp = fopen(tf, "w")) == NULL) {
X			perror(tf);
X			creatm = 0L;
X		} else {
X			fprintf(tfp, "To: %s\n", pathptr);
X			fprintf(tfp, "Subject: %s\n", subj);
X#ifdef INTERNET
X			fprintf(tfp, "News-Path: %s\n", hptr->path);
X#endif /* INTERNET */
X			fprintf(tfp, "References: %s\n\n", folbuf);
X			fstat(fileno(tfp), &statb);
X			creatm = statb.st_mtime;
X			(void) fclose(tfp);
X		}
X		(void) umask(oumask);
X
X		(void) sprintf(edcmdbuf, "%s %s", ed, tf);
X		(void) system(edcmdbuf);
X		(void) strcpy(rcbuf, MAILPARSER);
X		(void) strcat(rcbuf, " -t");
X		(void) strcat(rcbuf, " < ");
X		(void) strcat(rcbuf, tf);
X		if (access(tf, 4) || stat(tf, &statb)) {
X			fprintf(stderr, "Reply not sent: no input file.\n");
X			return FALSE;
X		}
X		if (statb.st_mtime == creatm) {
X			fprintf(stderr, "Reply not sent: cancelled.\n");
X			(void) unlink(tf);
X			return FALSE;
X		}
X		fprintf(ofp,"Sending reply.\n");
X		(void) fflush(stdout);
X		if (vfork() == 0) {
X			(void) system(rcbuf);
X			(void) unlink(tf);
X			_exit(0);
X		}
X	} else {
X		(void) sprintf(rcbuf, MAILER, hptr->title);
X		(void) sprintf(bfr, "%s %s", rcbuf, address);
X		(void) system(bfr);
X	}
X	hdr();
X	return TRUE;
X}
X
Xxmit_command()
X{
X	tfilename = filename;
X	if (*bptr == '-') {
X		if (*ofilename1)
X			tfilename = ofilename1;
X		bptr++;
X	}
X	if (*bptr != '\0' && *bptr != ' ') {
X		fprintf(ofp, "Bad system name.\n");
X		return;
X	}
X	while (*bptr == ' ')
X		bptr++;
X	if (*bptr == '\0') {
X		fprintf(ofp, "Missing system name.\n");
X		return;
X	}
X	if (s_find(&srec, bptr) == NULL) {
X		fprintf(ofp, "%s not in SYSFILE\n", bptr);
X		return;
X	}
X	(void) transmit(&srec, tfilename);
X}
X
Xnext_ng_command()
X{
X	obit = -1;
X	if (!*bptr || *bptr == '-') {
X		if (cflag)
X			clear(bit);
X		else
X			putc('\n', ofp);
X		if (*bptr)
X			actdirect = BACKWARD;
X		rfq = 0;
X		saveart;
X		if (nextng()) {
X			if (actdirect == BACKWARD)
X				fprintf(ofp, "Can't back up.\n");
X			else
X				return TRUE;
X		}
X		return FALSE;
X	}
X	while (isspace(*bptr))
X		bptr++;
X	if (!validng(bptr)) {
X		fprintf(ofp, "No such group.\n");
X		return FALSE;
X	}
X	if (cflag)
X		clear(bit);
X	else
X		putc('\n', ofp);
X	readmode = SPEC;
X	rfq = 0;
X	saveart;
X	back();
X	selectng(bptr, TRUE, TRUE);
X	return FALSE;
X}
X
Xcaesar_command()
X{
X	char	temp[BUFLEN];
X	FILE	*pfp, *popen();
X
X	fprintf(stderr, "Caesar decoding:\n");
X	(void) sprintf(temp, "%s/%s", LIB, "caesar");
X	if (*bptr) {
X		(void) strcat(temp, " ");
X		(void) strcat(temp, bptr);
X	}
X	if (NLINES(h, fp) > LNCNT && *PAGER) {
X		(void) strcat(temp, " | ");
X		(void) strcat(temp, PAGER);
X	}
X	pfp = popen(temp, "w");
X	tprint(fp, pfp, FALSE);
X	FCLOSE(fp);
X	(void) pclose(pfp);
X}
X
X/*
X * Show the user the tail, if any, of the message on file
X * descriptor fd, and close fd.  The digester is considered,
X * and the pager is used if appropriate.
X */
Xshowtail(fd)
XFILE *fd;
X{
X	if (fd == NULL)
X		return;
X
X	if (dgest) {
X		digest(fd, ofp, h);
X	} else if (!lflag && !pflag && !eflag) {
X		pprint(fd);
X	}
X	(void) fclose(fd);
X}
X
X/*
X * Print out the rest of the article through the pager.
X */
Xpprint(fd)
XFILE *fd;
X{
X#ifdef PAGE
X	/* Filter the tail of long messages through PAGER. */
X	if (NLINES(h, fd) > LNCNT && *PAGER) {
X		if (!index(PAGER, FMETA)) {
X			FILE *pfp, *popen();
X
X			pfp = popen(PAGER, "w");
X			if (pfp == NULL)
X				pfp = ofp;
X			/*
X			 * What follows is an attempt to prevent the
X			 * next message from scrolling part of this
X			 * message off the top of the screen before
X			 * the poor luser can read it.
X			 */
X			tprint(fd, pfp, FALSE);
X			putc('\f', pfp);
X			putc('\n', pfp);
X			putc(' ', pfp);
X			(void) pclose(pfp);
X		}
X		else
X			pout(ofp);
X		holdup = TRUE;
X	}
X	else
X#endif
X		tprint(fd, ofp, FALSE);
X}
X
X/*
X * Find the next article we want to consider, if we're done with
X * the last one, and show the header.
X */
Xgetnextart(minus)
Xint minus;
X{
X 	int noaccess;
X 	register DIR *dirp;
X 	register struct direct *dir;
X 	long nextnum, tnum;
X
X 	noaccess = 0;
X
X	if (minus)
X		goto nextart2;	/* Kludge for "-" command. */
X
X	if (bit == obit)	/* Return if still on same article as last time */
X		return 0;
X
X	SigTrap = FALSE;
X
Xnextart:
X#ifdef DEBUG
X	fprintf(stderr,"nextart:\n");
X#endif /* DEBUG */
X	dgest = 0;
X
X	if (bit < minartno && !rflag)
X		bit = minartno;
X
X	/* If done with this newsgroup, find the next one. */
X	while (ngsize <= 0 || (!rflag && ((long) bit > ngsize)) || (rflag && bit < minartno)) {
X		if (nextng()) {
X			if (actdirect == BACKWARD) {
X				fprintf(ofp, "Can't back up.\n");
X				actdirect = FORWARD;
X				continue;
X			} else
X				if (rfq++ || pflag || cflag)
X					return 1;
X			break;
X		}
X		if (rflag)
X			bit = ngsize + 1;
X		else
X			bit = minartno - 1;
X		if (uflag && !xflag) {
X			time_t now;
X			(void) time(&now);
X			if (now - timelastsaved > 5*60 /* 5 minutes */) {
X				if (!xflag)
X					fprintf(stderr,"[Saving .newsrc]\n");
X				writeoutrc();
X				timelastsaved = now;
X			}
X		}
X		noaccess = 0;
X	}
X
Xnextart2:
X#ifdef DEBUG
X	fprintf(stderr, "article: %s/%ld\n", groupdir, bit);
X#endif
X	if (rcreadok)
X		rcreadok = 2;	/* have seen >= 1 article */
X	(void) sprintf(filename, "%s/%ld", dirname(groupdir), bit);
X	if (rfq && goodone[0])
X		strcpy(filename, goodone);
X	if (SigTrap) {
X		if (SigTrap == SIGHUP)
X			return 1;
X		if (!rcreadok)
X			xxit(0);
X		fprintf(ofp, "Abort (n)?  ");
X		(void) fflush(ofp);
X		(void) gets(bfr);
X		if (*bfr == 'y' || *bfr == 'Y')
X			xxit(0);
X		SigTrap = FALSE;
X	}
X#ifdef DEBUG
X	fprintf(stderr, "filename = '%s'\n", filename);
X#endif
X	/* Decide if we want to show this article. */
X 	if (bit <= 0 || (fp = art_open(filename, "r")) == NULL) {
X		/* don't show the header if the article was specifically
X		 * requested and it isn't there
X		 */
X		if (lbuf[0] == '<') {
X			lbuf[0] = '\0';
X			bit = -1;
X			return 1;
X		}
X 		/* since there can be holes in legal article numbers, */
X 		/* we wait till we hit 5 consecutive bad articles */
X 		/* before we haul off and scan the directory */
X 		if (++noaccess < 5)
X 			goto badart;
X		noaccess = 0;
X 		dirp = opendir(dirname(groupdir));
X 		if (dirp == NULL) {
X			if (errno != EACCES)
X				fprintf(stderr,"Can't open %s\n", dirname(groupdir));
X 			goto badart;
X 		}
X 		nextnum = rflag ? minartno - 1 : ngsize + 1;
X 		while ((dir = readdir(dirp)) != NULL) {
X 			tnum = atol(dir->d_name);
X 			if (tnum <= 0)
X 				continue;
X 			if (rflag ? (tnum > nextnum && tnum < bit)
X 				  : (tnum < nextnum && tnum > bit))
X 				nextnum = tnum;
X 		}
X 		closedir(dirp);
X 		if (rflag ? (nextnum >= bit) : (nextnum <= bit))
X 			goto badart;
X#ifdef DEBUG
X		fprintf(stderr,"nextnum = %ld\n",nextnum);
X#endif /* DEBUG */
X 		do {
X 			clear(bit);
X 			nextbit();
X 		} while (rflag ? (nextnum < bit) : (nextnum > bit));
X 		obit = -1;
X 		abs = FALSE;
X		if (ignorenews) /* ignored news is still news */
X			news = TRUE;
X 		goto nextart;
X 	} else
X 		noaccess = 0;
X
X 	if (ignorenews || hread(h, fp, TRUE) == NULL
X		|| (!rfq && !aselect(h, abs))) {
X		if (ignorenews)
X			news = TRUE;
X badart:
X#ifdef DEBUG
X		fprintf(stderr, "Bad article '%s'\n", filename);
X#endif
X		FCLOSE(fp);
X		clear(bit);
X		obit = -1;
X		nextbit();
X		abs = FALSE;
X		goto nextart;
X	}
X	abs = FALSE;
X	actdirect = FORWARD;
X	news = TRUE;
X	hdr();
X	if (pflag)
X		tprint(fp, ofp, FALSE);
X	else if (cflag && !lflag && !eflag) {
X		(void) fflush(ofp);
X		pprint(fp);
X	}
X	if (cflag || lflag || eflag || pflag) {
X		SigTrap = FALSE;
X		FCLOSE(fp);
X	}
X	obit = bit;
X	return 0;
X}
X
X/*
X * Print out whatever the appropriate header is
X */
Xhdr()
X{
X	char *briefdate();
X
X	if (rfq)
X		return;
X
X	if (lflag || eflag) {
X		hprint(h, ofp, 0);
X		return;
X	}
X
X	/* Print out a header */
X	if (ngrp) {
X		pngsize = ngsize;
X		ngrp--;
X		nghprint(groupdir);
X	}
X	if (!hflag)
X		fprintf(ofp, "Article %ld of %ld, %s.\n",
X			bit, pngsize, briefdate(h->subdate));
X	hprint(h, ofp, pflag ? 1 : 0);
X}
X
Xnghprint(title)
Xchar *title;
X{
X	char *tstr = "Newsgroup ";
X	int l = strlen(title) + strlen(tstr);
X
X	fprintf(ofp, "\n");
X	if (!hflag) {
X		dash(l, ofp);
X		fprintf(ofp, "%s%s\n", tstr, title);
X		dash(l, ofp);
X	} else {
X		fprintf(ofp, "%s%s, ", tstr, title);
X		if (bit == pngsize)
X			fprintf(ofp, "%ld\n", pngsize);
X		else
X			fprintf(ofp, "%ld-%ld\n", bit, pngsize);
X	}
X	fprintf(ofp, "\n");
X}
X
X/*
X * Routine to catch a continue signal.
X */
Xcatchcont(sig)
Xint sig;
X{
X	(void) signal(sig, catchcont);
X	SigTrap = sig;
X	(void) fflush(ofp);
X#ifdef SIGCONT
X	if (fp && sig == SIGCONT)
X		hdr();
X	if (sig != SIGCONT)
X#endif /* SIGCONT */
X		putc('\n', ofp);
X	if (canlongjmp)
X		longjmp(sigjmpbuf,1);
X}
X
Xxxit(status)
Xint	status;
X{
X	(void) unlink(infile);
X	(void) unlink(outfile);
X#ifdef SORTACTIVE
X	if (strncmp(ACTIVE,"/tmp/", 5) == 0)
X		(void) unlink(ACTIVE);
X#endif /* SORTACTIVE */
X	exit(status);
X}
END_OF_FILE
if test 25018 -ne `wc -c <'readr.c'`; then
    echo shar: \"'readr.c'\" unpacked with wrong size!
fi
# end of 'readr.c'
fi
echo shar: End of shell archive.
exit 0
-- 
		"Zeta Microcomputer Software"
ACSnet:    nick@ultima.cs.uts.oz
UUCP:      ...!uunet!munnari!ultima.cs.uts.oz!nick
Fidonet:   Nick Andrew on 3:713/602 (Zeta)

nick@ultima.cs.uts.oz (Nick Andrew) (12/07/89)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  virtterm.c rfuncs.c header.c
# Wrapped by nick@nswitgould on Thu Dec  7 22:40:34 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'virtterm.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'virtterm.c'\"
else
echo shar: Extracting \"'virtterm.c'\" \(20326 characters\)
sed "s/^X//" >'virtterm.c' <<'END_OF_FILE'
X/*
X *  Virtual terminal handler
X *  Written by Kenneth Almquist, AGS Computers  (HO 4C601, X7105).
X *  Modified by Stephen Hemminger, to use TERMCAP (without curses)
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)virtterm.c	1.13	12/16/86";
X#endif /* SCCSID */
X
X/*LINTLIBRARY*/
X
X#include <stdio.h>
X#include <ctype.h>
X#include <sys/types.h>
X#include <sys/ioctl.h>
X#include <signal.h>
X#ifdef USG
X#include <termio.h>
X#else /* !USG */
X#include <sgtty.h>
X#endif /* !USG */
X
X/*
X * These values for MAXPLEN and MAXLLEN are used to dimension arrays
X * that hold strings of relative cursor motions.  The actual arrays that
X * are used to hold screen images are malloc'd.
X */
X#define MAXPLEN 90
X#define MAXLLEN 160
X
X#define BOTLINE (ROWS - 1)
X#define DIRTY 01
X
X/* terminal escape sequences from termcap */
X#define HO _tstr[0]		/* home */
X#define CL _tstr[1]		/* clear screen */
X#define CD _tstr[2]		/* clear to end of screen */
X#define CE _tstr[3]		/* clear to end of line */
X#define xUP _tstr[4]		/* up one line */
X#define DO _tstr[5]		/* down one line */
X#define US _tstr[6]		/* underline */
X#define UE _tstr[7]		/* underline end */
X#define BT _tstr[8]		/* backtab */
X#define xBC _tstr[9]		/* backspace */
X#define AL _tstr[10]		/* insert line */
X#define DL _tstr[11]		/* delete line */
X#define CM _tstr[12]		/* cursor move */
X#define CH _tstr[13]		/* cursor horizontal move */
X#define CV _tstr[14]		/* cursor vertical move */
X#define CS _tstr[15]		/* scrolling region */
X#define SF _tstr[16]		/* scroll forwards */
X#define SR _tstr[17]		/* scroll backwards */
X#define TI _tstr[18]		/* start cursor mode */
X#define TE _tstr[19]		/* end cursor mode */
X#define TA _tstr[20]		/* tab char (if not \t) */
X#define CR _tstr[21]		/* carriage return (if not \r) */
X#define xPC _tstr[22]		/* for reading pad character */
Xchar PC;			/* pad character */
Xchar *BC, *UP;			/* external variables for tgoto */
X
Xstatic char sname[] = "hoclcdceupdousuebtbcaldlcmchcvcssfsrtitetacrpc";
Xchar *_tstr[23];
Xint     HOlen;			/* length of HO string */
X
X
X/* terminal flags */
X#define BS _tflg[0]		/* can backspace */
X#define AM _tflg[1]		/* has auto margins */
X#define XN _tflg[2]		/* no newline after wrap */
X#define RET !_tflg[3]		/* has carriage return */
X#define NS _tflg[4]		/* has SF (scroll forward) */
X#define PT _tflg[5]		/* has tabs */
X#define XT _tflg[6]		/* tabs are destructive */
Xint	GT = 1;			/* tab stops on terminal are set */
X
Xstatic char bname[] = "bsamxnncnsptxt";
Xchar _tflg[7];
X
X
Xextern char *tgoto(), *tgetstr();
Xextern char *getenv(), *strcpy();
X
X#define ULINE 0200
X
X/* Constants accessable by user */
Xint     hasscroll;		/* scrolling type, 0 == no scrolling */
Xint     ROWS;			/* number of lines on screen */
Xint     COLS;			/* width of screen */
X
Xstruct line {
X	short    len;		/* should really be u_char */
X	char    flags;
X	char    *l;		/* pointer to actual line text, NO NULL @ end */
X};
X
Xint     _row, _col;
Xint     _srow, _scol;
Xstruct line *_virt;		/* what we want the screen to look like */
Xstruct line *_actual;		/* What it actually looks like */
Xint     _uline = 0;
Xint     _junked = 1;
Xint     _curjunked;
Xint     _dir = 1;
Xint	_shifttop, _shiftbot;
Xint	_shift;
Xint	_scratched;
Xint     vputc();
X
X/*
X * Tell refresh to shift lines in region upwards count lines.  Count
X * may be negative.  The virtual image is not shifted; this may change
X * later.  The variable _scratched is set to supress all attempts to
X * shift.
X */
X
Xushift(top, bot, count)
X{
X	if (_scratched)
X		return;
X	if (_shift != 0 && (_shifttop != top || _shiftbot != bot)) {
X		_scratched++;
X		return;
X	}
X	_shifttop = top;
X	_shiftbot = bot;
X	_shift += count;
X}
X
X/*
X * generate a beep on the terminal
X */
Xbeep()
X{
X	vputc('\7');
X}
X
X/*
X * Move to one line below the bottom of the screen.
X */
Xbotscreen()
X{
X	_amove(BOTLINE, 0);
X	vputc('\n');
X	vflush();
X}
X
Xmove(row, col)
X{
X	if (row < 0 || row >= ROWS || col < 0 || col >= COLS)
X		return;
X	_row = row;
X	_col = col;
X}
X
X
X
X/*
X * Output string at specified location.
X */
Xmvaddstr(row, col, str)
Xchar *str;
X{
X	move(row, col);
X	addstr(str);
X}
X
Xaddstr(s)
Xchar   *s;
X{
X	register char  *p;
X	register struct line   *lp;
X	register int    col = _col;
X
X	lp = &_virt[_row];
X	if (lp->len < col) {
X		p = &lp->l[lp->len];
X		while (lp->len < col) {
X			*p++ = ' ';
X			lp->len++;
X		}
X	}
X	for (p = s; *p != '\0'; p++) {
X		if (*p == '\n') {
X			lp->len = col;
X			lp->flags |= DIRTY;
X			col = 0;
X			if (++_row >= ROWS)
X				_row = 0;
X			lp = &_virt[_row];
X		}
X		else {
X			lp->l[col] = *p;
X			lp->flags |= DIRTY;
X			if (++col >= COLS) {
X				lp->len = COLS;
X				col = 0;
X				if (++_row >= ROWS)
X					_row = 0;
X				lp = &_virt[_row];
X			}
X		}
X	}
X	if (lp->len <= col)
X		lp->len = col;
X	_col = col;
X}
X
Xaddch(c)
X{
X	register struct line   *lp;
X	register char  *p;
X
X	lp = &_virt[_row];
X	if (lp->len < _col) {
X		p = &lp->l[lp->len];
X		while (lp->len < _col) {
X			*p++ = ' ';
X			lp->len++;
X		}
X	}
X	lp->l[_col] = c;
X	if (lp->len == _col)
X		lp->len++;
X	if (++_col >= COLS) {
X		_col = 0;
X		if (++_row >= ROWS)
X			_row = 0;
X	}
X	lp->flags |= DIRTY;
X}
X
X/*
X * Clear an entire line.
X */
Xclrline(row)
X{
X	register struct line   *lp;
X
X	lp = &_virt[row];
X	if (lp->len > 0) {
X		lp->len = 0;
X		lp->flags |= DIRTY;
X	}
X}
X
Xerase()
X{
X	register    i;
X
X	for (i = 0; i < ROWS; i++) {
X		_virt[i].len = 0;
X		_virt[i].flags |= DIRTY;
X	}
X}
X
Xrefresh()
X{
X	register i;
X	register char *p, *q;
X	register int j, len;
X
X	if (checkin())
X		return;
X	i = 1;
X	if (_junked) {
X		_sclear();
X		_junked = 0;
X	} else if (! _scratched) {
X		if (_shift > 0) {
X			_ushift(_shifttop, _shiftbot, _shift);
X		} else if (_shift < 0) {
X			i = _dshift(_shifttop, _shiftbot, -_shift);
X		} else {
X			i = _dir;
X		}
X	}
X	_dir = i;
X	_shift = 0;
X	if (checkin())
X		return;
X	_fixlines();
X	for (i = _dir > 0 ? 0 : BOTLINE; i >= 0 && i < ROWS; i += _dir) {
X		if ((_virt[i].flags & DIRTY) == 0)
X			continue;
X		_ckclrlin(i);		/* decide whether to do a clear line */
X					/* probably should consider cd too  */
X		len = _virt[i].len;
X		if (_actual[i].len < len)
X			len = _actual[i].len;
X		p = _virt[i].l;
X		q = _actual[i].l;
X		for (j = 0; j < len; j++) {
X			if (*p != *q) {
X				/* Inline test for speed */
X				if (i != _srow || j != _scol || _curjunked)
X					_amove(i, j);
X				_aputc(*p);
X				*q = *p;
X			}
X			p++;
X			q++;
X		}
X		len = _virt[i].len;
X		if (_actual[i].len > len) {
X			_clrtoeol(i, len);
X		} else {
X			for (; j < len; j++) {
X				if (*p != ' ') {
X					/* Inline test for speed */
X					if (i != _srow || j != _scol || _curjunked)
X						_amove(i, j);
X					_aputc(*p);
X				}
X				*q++ = *p++;
X			}
X			_actual[i].len = len;
X		}
X		if (checkin())
X			return;
X	}
X	_dir = 1;
X	_amove(_row, _col);
X	vflush();			/* flush output buffer */
X	_scratched = 0;
X}
X
X_dshift(top, bot, count)
X{
X	register    i;
X
X	if (count >= bot - top || hasscroll < 4) {  /* must have CS or AL/DL */
X		_scratched++;
X		return 1;
X	}
X	for (i = bot - count; _actual[i].len == 0; i--)
X		if (i == top)
X			return 1;
X	for (i = top; i <= bot; i++)
X		_virt[i].flags |= DIRTY;
X	for (i = bot; i >= top + count; i--) {
X		/* FIXME, this should be done by recirculating the pointers */
X		register j;
X		j =     _actual[i].len   = _actual[i - count].len;
X		        _actual[i].flags = _actual[i - count].flags;
X		strncpy(_actual[i].l,      _actual[i - count].l, j);
X	}
X	for (; i >= top; i--)
X		_actual[i].len = 0;
X
X	if (hasscroll != 5) {		/* can we define scrolling region, and scroll back */
X		tputs(tgoto(CS, bot, top), 1, vputc);/* define scroll region */
X		_curjunked = 1;
X		_amove(top, 0);
X		for (i = count; --i >= 0;)
X			tputs(SR, 1, vputc);/* scroll back */
X		tputs(tgoto(CS, BOTLINE, 0), 1, vputc);
X		_curjunked = 1;
X	} else {
X		_amove(bot - count + 1, 0);
X		if (CD && bot == BOTLINE)
X			tputs(CD, 1, vputc);
X		else {
X			for (i = count; --i >= 0;)
X				tputs(DL, ROWS - _srow, vputc);
X		}
X		_amove(top, 0);
X		for (i = count; --i >= 0;)
X			tputs(AL, ROWS - _srow, vputc);
X	}
X	return -1;
X}
X
X
X_ushift(top, bot, count)
X{
X	register    i;
X
X	if (count >= bot - top || hasscroll == 0) {
X		_scratched++;
X		return;
X	}
X	for (i = top + count; _actual[i].len == 0; i++)
X		if (i == bot)
X			return;
X	if (hasscroll == 1 || hasscroll == 3) {
X		/* we cheat and shift the entire screen */
X		/* be sure we are shifting more lines into than out of position */
X		if ((bot - top + 1) - count <= ROWS - (bot - top + 1))
X			return;
X		top = 0, bot = BOTLINE;
X	}
X	for (i = top; i <= bot; i++)
X		_virt[i].flags |= DIRTY;
X	for (i = top; i <= bot - count; i++) {
X		/* FIXME, this should be done by recirculating the pointers */
X		register int j;
X		j =     _actual[i].len   = _actual[i + count].len;
X		        _actual[i].flags = _actual[i + count].flags;
X		strncpy(_actual[i].l,      _actual[i + count].l, j);
X	}
X  	for (; i <= bot; i++)
X	for (; i <= bot; i++)
X		_actual[i].len = 0;
X
X	if (hasscroll != 5) {
X		if (top != 0 || bot != BOTLINE) {
X			tputs(tgoto(CS, bot, top), 0, vputc);
X			_curjunked = 1;
X		}
X		_amove(bot, 0);	/* move to bottom */
X		for (i = 0; i < count; i++) {
X			if (SF)		/* scroll forward */
X				tputs(SF, 1, vputc);
X			else
X				vputc('\n');
X		}
X		if (top != 0 || bot != BOTLINE) {
X			tputs(tgoto(CS, BOTLINE, 0), 0, vputc);
X			_curjunked = 1;
X		}
X	} else {
X		_amove(top, 0);
X		for (i = count; --i >= 0;)
X			tputs(DL, ROWS - _srow, vputc);
X		if (bot < BOTLINE) {
X			_amove(bot - count + 1, 0);
X			for (i = count; --i >= 0;)
X				tputs(AL, ROWS - _srow, vputc);
X		}
X	}
X}
X
X_sclear()
X{
X	register struct line   *lp;
X
X	tputs(CL, 0, vputc);
X	_srow = _scol = 0;
X	for (lp = _actual; lp < &_actual[ROWS]; lp++) {
X		lp->len = 0;
X	}
X	for (lp = _virt; lp < &_virt[ROWS]; lp++) {
X		if (lp->len != 0)
X			lp->flags |= DIRTY;
X	}
X}
X
X_clrtoeol(row, col)
X{
X	register struct line *lp = &_actual[row];
X	register i;
X
X	if (CE && lp->len > col + 1) {
X		_amove(row, col);
X		tputs(CE, 1, vputc);
X	} else {
X		for (i = col ; i < lp->len ; i++) {
X			if (lp->l[i] != ' ') {
X				_amove(row, i);
X				_aputc(' ');
X			}
X		}
X	}
X	lp->len = col;
X}
X
X_fixlines()
X{
X	register struct line   *lp;
X	register char  *p;
X	register int    i;
X
X	for (i = 0; i < ROWS; i++) {
X		lp = &_virt[i];
X		if (lp->flags & DIRTY) {
X			for (p = &lp->l[lp->len]; --p >= lp->l && *p == ' ';)
X				;
X			lp->len = (int) (p - lp->l) + 1;
X			if (lp->len == _actual[i].len && strncmp(lp->l, _actual[i].l, lp->len) == 0)
X				lp->flags &= ~DIRTY;
X		}
X	}
X}
X
X
X/*
X * Consider clearing the line before overwriting it.
X * We always clear a line if it has underlined characters in it
X * because these can cause problems.  Otherwise decide whether
X * that will decrease the number of characters to change.  This
X * routine could probably be simplified with no great loss.
X */
X
X_ckclrlin(i)
X{
X	int     eval;
X	int     len;
X	int     first;
X	register struct line   *vp, *ap;
X	register int    j;
X
X	if (!CE)
X		return;
X	ap = &_actual[i];
X	vp = &_virt[i];
X	len = ap->len;
X	eval = -strlen(CE);
X	if (len > vp->len) {
X		len = vp->len;
X		eval = 0;
X	}
X	for (j = 0; j < len && vp->l[j] == ap->l[j]; j++)
X		;
X	if (j == len)
X		return;
X	first = j;
X	while (j < len) {
X		if (vp->l[j] == ' ') {
X			if (ap->l[j] != ' ') {
X				while (++j < len && vp->l[j] == ' ' && ap->l[j] != ' ') {
X					eval++;
X				}
X				if (j == len)
X					eval++;
X				continue;
X			}
X		}
X		else {
X			if (vp->l[j] == ap->l[j]) {
X				while (++j < len && vp->l[j] == ap->l[j]) {
X					eval--;
X				}
X				continue;
X			}
X		}
X		j++;
X	}
X	if (US) {
X		for (j = 0 ; j < ap->len ; j++) {
X			if (ap->l[j] & ULINE) {
X				eval = 999;
X				if (first > j)
X					first = j;
X				break;
X			}
X		}
X	}
X	for (j = first; --j >= 0;)
X		if (vp->l[j] != ' ')
X			break;
X	if (j < 0)
X		first = 0;
X	if (eval > 0) {
X		_amove(i, first);
X		tputs(CE, 0, vputc);
X		_actual[i].len = first;
X	}
X}
X
X
X
X/*
X * Move routine
X * 	first compute direct cursor address string and cost
X *	then relative motion string and cost,
X *	then home then relative and cost
X *	choose smallest and do it.
X *
X *	The plod stuff is to build the strings (with padding) then decide
X */
Xstatic char *plodstr;		/* current location in relmove string */
X
Xplodput(c)
X{
X	*plodstr++ = c;
X}
X
X/* FIXME: speedup 1-char horiz moves:  print the char that's there. */
X/* FIXME: avoid funniness if cm works. */
X/* FIXME: Avoid setul(0) if cursor motion OK in standout (XM?) */
X_amove(row, col)
X{
X	char direct[20];
X	char rel[MAXPLEN*10 + MAXLLEN*10];    /* longest move is full screen */
X	char ho[MAXPLEN*10 + MAXLLEN*10];
X	int cost, newcost;
X	register char *movstr;
X
X	if (row == _srow && col == _scol && _curjunked == 0)
X		return;
X	if (_uline)
X		_setul(0);	/* Inline test for speed */
X
X	cost = 999;
X	if (CM) {
X		plodstr = direct;
X		tputs(tgoto(CM, col, row), 0, plodput);
X		cost = plodstr - direct;
X		movstr = direct;
X	}
X	if (_curjunked == 0) {
X		plodstr = rel;
X		if (_vmove(_srow, row) >= 0
X		 && (plodstr - rel) < cost		/* after vmove */
X		 && _hmove(_scol, col, row) >= 0
X		 && (newcost = plodstr - rel) < cost) { /* after both */
X			cost = newcost;
X			movstr = rel;
X		}
X	}
X	if (cost > HOlen) {	/* is it worth calculating */
X		plodstr = ho;
X		tputs(HO, 0, plodput);
X		if (_vmove(0, row) >= 0
X		 && (plodstr - ho) < cost		/* after ho, vmove */
X		 && _hmove(0, col, row) >= 0
X		 && (newcost = plodstr - ho) < cost) {	/* after all three */
X			cost = newcost;
X			movstr = ho;
X		}
X	}
X
X	if (cost < 999)
X		while (--cost >= 0)
X			vputc(*movstr++);
X
X	_srow = row;
X	_scol = col;
X	_curjunked = 0;
X}
X
X_vmove(orow, nrow)
X{
X	char direct[128];
X	char *saveplod = plodstr;
X
X	if (CV) {
X		plodstr = direct;
X		tputs(tgoto(CV, nrow, nrow), 0, plodput);
X		*plodstr = '\0';
X		plodstr = saveplod;
X	}
X	if (orow > nrow) {		/* cursor up */
X		if (! UP)
X			return -1;
X		while (orow > nrow) {
X			tputs(UP, 1, plodput);
X			orow--;
X		}
X	}
X	while (orow < nrow) {		/* cursor down */
X		if (DO)
X			tputs(DO, 1, plodput);
X		else
X			*plodstr++ = '\n';
X		orow++;
X	}
X	if (CV && plodstr - saveplod >= strlen(direct)) {
X		register char *p;
X		plodstr = saveplod;
X		for (p = direct ; *plodstr = *p++ ; plodstr++)
X			;
X	}
X	return 0;
X}
X
X_hmove(ocol, ncol, row)
X{
X	char direct[128];
X	char ret[MAXLLEN*10];
X	char *saveplod = plodstr;
X	char *movstr;
X	int cost, newcost;
X
X	cost = 999;
X	if (CH) {
X		plodstr = direct;
X		tputs(tgoto(CH, ncol, ncol), 0, plodput);
X		cost = plodstr - direct;
X		movstr = direct;
X		plodstr = saveplod;
X	}
X	if (RET && ocol > ncol) {	/* consider doing carriage return */
X		plodstr = ret;
X		if (CR)
X			tputs(CR, 1, plodput);
X		else
X			*plodstr++ = '\r';
X		if (_relhmove(0, ncol, row) >= 0
X		 && (newcost = plodstr - ret) < cost) {
X			cost = newcost;
X			movstr = ret;
X		}
X		plodstr = saveplod;
X	}
X	if (_relhmove(ocol, ncol, row) < 0) {
X		if (cost == 999)
X			return -1;
X		goto copy;
X	}
X	if (plodstr - saveplod > cost) {
Xcopy:		plodstr = saveplod;
X		while (--cost >= 0)
X			*plodstr++ = *movstr++;
X	}
X	return 0;
X}
X
X_relhmove(ocol, ncol, row)
X{
X	int tab;
X
X	if (ocol < ncol && PT && GT) {	/* tab (nondestructive) */
X		while ((tab = (ocol + 8) & ~07) <= ncol) {
X			if (TA)
X				tputs(TA, 1, plodput);
X			else
X				*plodstr++ = '\t';
X			ocol = tab;
X		}
X		if (tab < COLS && tab - ncol < ncol - ocol) {
X			if (TA)
X				tputs(TA, 1, plodput);
X			else
X				*plodstr++ = '\t';
X			ocol = tab;
X		}
X	} else if (BT && GT && ocol > ncol) {	/* backwards tab */
X		while ((tab = (ocol - 1) &~ 07) >= ncol) {
X			if (BS && tab == ocol - 1) {
X				if (BC)
X					tputs(BC, 1, plodput);
X				else
X					*plodstr++ = '\b';
X			} else
X				tputs(BT, 1, plodput);
X			ocol = tab;
X		}
X		if (ncol - tab + 1 < ocol - ncol) {
X			tputs(BT, 1, plodput);
X			ocol = tab;
X		}
X	}
X	if (ocol > ncol) {			/* cursor left */
X		if (! BS)
X			return -1;
X		while (ocol > ncol) {
X			if (BC != NULL)
X				tputs(BC, 1, plodput);
X			else
X				*plodstr++ = '\b';
X			ocol--;
X		}
X	}
X	if (ocol < ncol) {			/* cursor right */
X		register struct line *lp = &_actual[row];
X		/*
X		 * This code doesn't move over underlined characters properly,
X		 * but in practice this doesn't seem to matter.
X		 */
X		while (ocol < ncol) {
X			if (ocol < lp->len)
X				*plodstr++ = lp->l[ocol];
X			else
X				*plodstr++ = ' ';
X			ocol++;
X		}
X	}
X	return 0;
X}
X
X_aputc(c)
X{
X	if (_uline != (c & ULINE))	/* Inline for speed */
X		_setul(c & ULINE);
X	if (++_scol >= COLS) {
X		if (_srow == ROWS - 1) {
X			/* Don't ever paint last char of last line */
X			_scol--;
X			return;
X		}
X		_curjunked++;		/* Don't assume AM is right */
X	}
X	vputc(c & ~ULINE);
X}
X
X
X_setul(on)
X{
X	if (on) {
X		if (_uline == 0 && US != NULL) {
X			tputs(US, 1, vputc);
X			_uline = ULINE;
X		}
X	}
X	else {
X		if (_uline != 0 && UE != NULL) {
X			tputs(UE, 1, vputc);
X			_uline = 0;
X		}
X	}
X}
X
X/*
X * Initialize termcap strings for later use.
X */
X
X/*
X * Hacks to help with some Tek terminals
X * rad@tek
X */
Xint tputs_len;
Xcountit(c) { tputs_len++; }
X
Xinitterm()
X{
X	static char tcbuf[1024];	/* termcap buffer */
X	register char  *cp;
X#ifdef USG
X	struct termio tio;
X#else /* !USG */
X	struct sgttyb ttyb;
X#endif /* !USG */
X
X	if ((cp = getenv("TERM")) == NULL)
X		xerror("TERM not set in environment");
X
X	switch (tgetent(tcbuf, cp)) {
X		case 0:
X			xerror("Terminal not found in TERMCAP");
X		case -1:
X			xerror("Can't open /etc/termcap");
X		case 1:
X			break;
X	}
X#ifdef TIOCGWINSZ
X	{
X		struct winsize ws;
X		int winch();
X
X		COLS = ROWS = -1;
X		if(ioctl(1, TIOCGWINSZ, &ws) == 0) {
X			ROWS = ws.ws_row;
X			COLS = ws.ws_col;
X		}
X		if(ROWS <= 0)
X			ROWS = tgetnum("li");
X		if(COLS <= 0)
X			COLS = tgetnum("co");
X		if ((ROWS <= 0) || (COLS <= 0))
X			xerror("Can't get screen size");
X
X		signal(SIGWINCH, winch); /* allow for changing window size */
X	}
X#else /* !TIOCGWINSZ */
X	if ((ROWS = tgetnum("li")) == -1
X		|| (COLS = tgetnum("co")) == -1)
X		xerror("Can't get screen size");
X#endif /* !TIOCGWINSZ */
X	_zap();
X
X	if (CL == NULL)
X		xerror ("No clear screen defined");
X
X	if (HO == NULL && CM == NULL)
X		xerror("No home or cursor addressing");
X	if (HO)
X		HOlen = strlen(HO);
X	else
X		HOlen = 999;
X
X	PC = xPC ? xPC[0] : 0;
X	BC = xBC;
X	UP = xUP;
X	/*
X	 *  _vmove() may be called with a full-screen traverse,
X	 * meaning it will put the UP (along with any padding) into
X	 * the buffer as many as MAXPLEN times.  This means that
X	 * if the UP string would be more than 10 chars long (defined
X	 * in _amove() ), the buffer might be overflowed (assuming
X	 * CH is also large).
X	 * This actually occurs with the Tek4023 termcap, where :up=1000UP:
X	 * is used to fake vi into using :cm instead, due to the fact
X	 * that a 4023 can't do upline relative motion at all.
X	 * -rdoty@tek
X	 */
X	if (UP) {
X		tputs_len = 0;
X		tputs(UP, 1, countit);
X		if (tputs_len > 10 )
X			UP = 0;
X	}
X
X	if (tgetnum("ug") > 0)
X		US = UE = NULL;
X
X	if (XT)				/* Destructive tab code not included */
X		PT = 0;			/* to keep things simple */
X
X#ifdef USG
X	if (ioctl(0, TCGETA, &tio) == 0)
X		GT = tio.c_oflag&TAB3;
X#else /* !USG */
X	if (ioctl(0, TIOCGETP, &ttyb) == 0)
X		GT = ttyb.sg_flags&XTABS;
X#endif /* !USG */
X
X	{
X		char *thelines;
X		int i;
X		char *malloc();
X
X		thelines = malloc(2 * ROWS * COLS);
X		_virt = (struct line *)malloc(2 * ROWS * sizeof (struct line));
X		_actual = _virt + ROWS;
X		for (i = 0; i < ROWS; i++) {
X			_virt[i].len = 0;
X			_virt[i].flags = 0;
X			_actual[i].len = 0;
X			_actual[i].flags = 0;
X			_virt[i].l = thelines;
X			thelines += COLS;
X			_actual[i].l = thelines;
X			thelines += COLS;
X		}
X	}
X
X	/* Select article scrolling algorithm.  We prefer scrolling region
X	   over insert/delete line because it's faster on the HP */
X	hasscroll = 0;
X	if (!NS) {
X		hasscroll = 1;
X		if (SR)
X			hasscroll = 3;
X		if (CS)
X			hasscroll++;
X	}
X	if (AL && DL && hasscroll != 4)
X		hasscroll = 5;
X}
X
Xrawterm()
X{
X	if (TI != NULL)
X		tputs(TI, 0, vputc);
X}
X
Xcookedterm()
X{
X	if (TE != NULL) {
X		tputs(TE, 0, vputc);
X		vflush();
X	}
X}
X
X/* get strings from termcap */
X_zap()
X{
X	static char tstrbuf[1024];
X	static char *tp;
X	register char  *namp, **sp, *bp;
X
X	tp = tstrbuf;
X	sp = _tstr;
X	for (namp = sname; *namp; namp += 2) {
X		*sp++ = tgetstr(namp, &tp);
X	}
X	bp = _tflg;
X	for (namp = bname; *namp; namp += 2) {
X		*bp++ = tgetflag(namp, &tp);
X	}
X}
X#ifdef TIOCGWINSZ
X/*
X * window changed size -- update ROWS and COLS
X * and then redraw screen
X */
Xwinch()
X{
X	struct winsize ws;
X	int cols, rows;
X
X	cols = rows = -1;
X	if(ioctl(1, TIOCGWINSZ, &ws) == 0) {
X		rows = ws.ws_row;
X		cols = ws.ws_col;
X	}
X	if (rows == ROWS && cols == COLS) { /* just redraw it if no change */
X		_junked = 1;	/* redraw */
X		updscr();
X		return;
X	}
X
X	if(rows > 0)
X		ROWS = rows;
X	if(cols > 0)
X		COLS = cols;
X
X	if (ROWS > MAXPLEN)
X		ROWS = MAXPLEN;
X	if (COLS > MAXLLEN) {
X		COLS = MAXLLEN;
X		AM = XN = 1;
X	}
X
X	winch_upd();
X}
X#endif /* TIOCGWINSZ */
END_OF_FILE
if test 20326 -ne `wc -c <'virtterm.c'`; then
    echo shar: \"'virtterm.c'\" unpacked with wrong size!
fi
# end of 'virtterm.c'
fi
if test -f 'rfuncs.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rfuncs.c'\"
else
echo shar: Extracting \"'rfuncs.c'\" \(18733 characters\)
sed "s/^X//" >'rfuncs.c' <<'END_OF_FILE'
X/*
X * This software is Copyright (c) 1986 by Rick Adams.
X *
X * Permission is hereby granted to copy, reproduce, redistribute or
X * otherwise use this software as long as: there is no monetary
X * profit gained specifically from the use or reproduction or this
X * software, it is not sold, rented, traded or otherwise marketed, and
X * this copyright notice is included prominently in any copy
X * made.
X *
X * The author make no claims as to the fitness or correctness of
X * this software for any use whatsoever, and it is provided as is. 
X * Any use of this software is at the user's own risk.
X *
X * rfuncs - functions for readnews.
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)rfuncs.c	2.40	2/22/87";
X#endif /* SCCSID */
X
X/*LINTLIBRARY*/
X
X#include "rparams.h"
X
Xchar lentab[LINES];	/* length of newsgroupname for each rcline */
Xlong nngsize;		/* The next upcoming value of ngsize. */
Xlong nminartno;		/* Smallest article number in this group */
Xint BITMAPSIZE = 0;
X
Xnextng()
X{
X	long	curpos;
X#ifdef DEBUG
X	fprintf(stderr, "nextng()\n");
X#endif
X	curpos = ftell(actfp);
X
Xnext:
X#ifdef DEBUG
X	fprintf(stderr, "next:\n");
X#endif
X	if (actdirect == BACKWARD) {
X		if (back()) {
X			(void) fseek(actfp, curpos, 0);
X			return 1;
X		}
X		if (back()) {
X			(void) fseek(actfp, curpos, 0);
X			return 1;
X		}
X	}
X	if (fgets(afline, BUFLEN, actfp) == NULL)
X		return 1;
X	if (sscanf(afline, "%s %ld %ld", bfr, &nngsize, &nminartno) < 3) {
X		bfr[0] = '\0';
X		nngsize = 0;
X		nminartno = 0;
X	}
X#ifdef DEBUG
X	fprintf(stderr, "bfr = '%s'\n", bfr);
X#endif
X
X	if (!ngmatch(bfr, header.nbuf))
X		goto next;
X	if (xflag)
X		readmode = SPEC;
X	else
X		readmode = NEXT;
X	if (selectng(bfr, TRUE, FALSE))
X		goto next;
X	return 0;
X}
X
X
Xselectng(name, fastcheck, resubscribe)
Xchar	*name;
X{
X	register char	*ptr, punct = ',';
X	register int	i;
X	register char	*p;
X	register long	cur;
X	long	next = 0;
X	FILE *af;
X	long s, sm;
X	char buf[100], n[100];
X
X#ifdef DEBUG
X	fprintf(stderr,"selectng: groupdir = %s\n", groupdir);
X#endif /* DEBUG */
X	if (*groupdir)
X		updaterc();
X	last = 1;
X	if (strcmp(name, bfr)) {
X		af = xfopen(ACTIVE, "r");
X		while (fgets(buf, sizeof buf, af) != NULL) {
X			if (sscanf(buf, "%s %ld %ld", n, &s, &sm) == 3 &&
X			     strcmp(n, name) == 0) {
X				ngsize = s;
X				minartno = sm;
X				break;
X			}
X		}
X		(void) fclose(af);
X	} else {
X		ngsize = nngsize;
X		minartno = nminartno;
X	}
X#ifdef DEBUG
X	fprintf(stderr, "selectng(%s) sets ngsize to %ld, minartno to %ld\n",
X		name, ngsize, minartno);
X#endif
X	(void) strcpy(groupdir, name);
X	if (!xflag) {
X		i = findrcline(name);
X		if (i >= 0) {
X			if (p = index(rcline[i], '!')) {
X				switch (resubscribe) {
X				case FALSE:
X					groupdir[0] = 0;
X					return 1;
X				case TRUE:
X					*p = ':';
X					break;
X				case PERHAPS:
X					zapng = TRUE;
X					break;
X				}
X			} else
X				p = index(rcline[i], ':');
X			if (!p) /* shouldn't happen */
X				p = rcline[i];
X			while (*++p == ' ')
X				;
X			(void) sprintf(rcbuf, "%s%s%ld", rcline[i],
X				*p == '\0' ? " " : ",", ngsize+1);
X		}
X		else
X			(void) sprintf(rcbuf, "ng: %ld", ngsize+1);
X	} else
X		(void) sprintf(rcbuf, "ng: %ld", ngsize+1);
X#ifdef DEBUG
X	fprintf(stderr, "rcbuf set to %s\n", rcbuf);
X#endif /* DEBUG */
X
X	/*
X	 * Fast check for common case: 1-###
X	 */
X	if (fastcheck) {
X		p = rcbuf;
X		while (*p != ' ')
X			p++;
X		while (*p == ' ')
X			p++;
X		if (*p++ == '1' && *p++ == '-') {
X			cur = 0;
X			while (isdigit(*p))
X				cur = 10 * cur + *p++ - '0';
X			if (*p == ',' && cur == ngsize) {
X#ifdef DEBUG
X				fprintf(stderr, "Group: %s, all read\n", groupdir);
X#endif
X				groupdir[0] = 0;
X				return 1;
X			}
X			if (cur > ngsize) {
X				/*
X				 * Claim to have read articles
X				 * which "active" believes have
X				 * never existed - we believe "active"
X				 */
X				fprintf(stderr,
X					"%s %s...\r\n\t%s %ld to %ld\r\n",
X					"Warning: newsgroup", groupdir,
X					"last article claimed read reset from",
X					cur, ngsize);
X			}
X		}
X	}
X
X/*
X * The key to understanding this piece of code is that a bit is set iff
X * that article has NOT been read.  Thus, we fill in the holes when
X * commas are found (e.g. 1-20,30-35 will result in filling in the 21-29
X * holes), and so we assume the newsrc file is properly ordered, the way
X * we write it out.
X */
X	if ((ngsize-minartno) > BITMAPSIZE) {
X		/* resize the bitmap array */
X		(void) free (bitmap);
X		BITMAPSIZE = 8 * (((ngsize - minartno) + 7) / 8);
X		bitmap = malloc((unsigned)BITMAPSIZE/8);
X		if (bitmap == NULL)
X			xerror("Can't malloc bitmap");
X	}
X
X	cur = 0;
X	bzero(bitmap, (int) (ngsize-minartno)/8+1); /* 8 bits per character */
X
X	/* Decode the .newsrc line indicating what we have read. */
X	for (ptr = rcbuf; *ptr && *ptr != ':'; ptr++)
X		;
X	while (*ptr) {
X		while (!isdigit(*ptr) && *ptr)
X			ptr++;
X		if (!*ptr)
X			break;
X		(void) sscanf(ptr, "%ld", &next);
X		if (punct == ',') {
X			while (++cur < next) {
X				set(cur);
X			}
X		}
X		cur = next;
X		while (!ispunct(*ptr) && *ptr)
X			ptr++;
X		punct = *ptr;
X	}
X	if (rflag)
X		bit = ngsize+1;
X	else
X		bit = minartno -1;
X	nextbit();
X	ngrp = 1;
X	return 0;
X}
X
X#ifdef TMAIL
Xcatchterm()
X{
X	(void) unlink(infile);
X	(void) unlink(outfile);
X	xxit(0);
X}
X
X
X/*
X * The -M (Mail) interface.  This code is a reasonably simple model for
X * writing other interfaces.  We write out all relevant articles to
X * a temp file, then invoke Mail with an option to have it tell us which
X * articles it read.  Finally we count those articles as really read.
X */
XMail()
X{
X	register FILE *fp = NULL, *ofp;
X	struct hbuf h;
X	register char	*ptr, *fname;
X	int	news = 0;
X	register int i;
X
X	for(i=0;i<NUNREC;i++)
X		h.unrec[i] = NULL;
X
X	ofp = xfopen(mktemp(outfile), "w");
X	if (aflag && *datebuf)
X		if ((atime = cgtdate(datebuf)) == -1)
X			xerror("Cannot parse date string");
X	while (!nextng())
X		while (bit <= ngsize) {
X			(void) sprintf(filename, "%s/%ld", dirname(groupdir), bit);
X			if (access(filename, 4)
X			|| ((fp = art_open (filename, "r")) == NULL)
X			|| (hread(&h, fp, TRUE) == NULL)
X			|| !aselect(&h, FALSE)) {
X#ifdef DEBUG
X				fprintf(stderr, "Bad article '%s'\n", filename);
X#endif
X				if (fp != NULL) {
X					(void) fclose(fp);
X					fp = NULL;
X				}
X				clear(bit);
X				nextbit();
X				continue;
X			}
X			fname = ptr = index(h.from, '(');
X			if (fname) {
X				while (ptr && ptr[-1] == ' ')
X					ptr--;
X				if (ptr)
X					*ptr = 0;
X				fname++;
X				ptr = fname + strlen(fname) - 1;
X				if (*ptr == ')')
X					*ptr = 0;
X			}
X			h.subtime = cgtdate(h.subdate);
X			fprintf(ofp, "From %s %s",
X#ifdef INTERNET
X			    h.from[0] ? h.from :
X#endif
X			    h.path, ctime(&h.subtime));
X			if (fname)
X				fprintf(ofp, "Full-Name: %s\n", fname);
X			fprintf(ofp, "Newsgroups: %s\n", h.nbuf);
X			fprintf(ofp, "Subject: %s\n", h.title);
X			fprintf(ofp, "Article-ID: %s/%ld\n\n", groupdir, bit);
X			tprint(fp, ofp, TRUE);
X			putc('\n', ofp);
X			news = TRUE;
X			(void) fclose(fp);
X			fp = NULL;
X			nextbit();
X		}
X	updaterc();
X	(void) fclose(ofp);
X	if (!news) {
X		fprintf(stderr, "No news.\n");
X		(void) unlink(outfile);
X		return;
X	}
X	(void) signal(SIGHUP, catchterm);
X	(void) signal(SIGTERM, catchterm);
X	(void) sprintf(bfr, "%s -f %s -T %s", TMAIL, outfile, mktemp(infile));
X	fwait(fsubr(ushell, bfr, (char *)NULL));
X	ofp = xfopen(infile, "r");
X	(void) fseek(actfp, 0L, 0);
X	while (fgets(afline, BUFLEN, actfp) != NULL) {
X		last = 0;
X		if (sscanf(afline, "%s %ld", bfr, &nngsize) < 2) {
X			bfr[0] = '\0';
X			nngsize = 0;
X		}
X		if (!ngmatch(bfr, header.nbuf))
X			continue;
X		*groupdir = 0;
X		if (selectng(bfr, TRUE, FALSE))
X			continue;
X		(void) fseek(ofp, 0L, 0);
X		while (fgets(groupdir, BUFLEN, ofp) != NULL) {
X			(void) nstrip(groupdir);
X			ptr = index(groupdir, '/');
X			*ptr = 0;
X			if (strcmp(bfr, groupdir))
X				continue;
X			(void) sscanf(++ptr, "%ld", &last);
X			clear(last);
X		}
X		if (last) {
X			(void) strcpy(groupdir, bfr);
X			updaterc();
X		}
X	}
X	(void) unlink(infile);
X	(void) unlink(outfile);
X}
X#endif
X
Xupdaterc()
X{
X	register long	cur = 1, next = 1;
X	register int i;
X	register char	*ptr;
X	char	oldptr;
X
X	sprintf(rcbuf, "%s%c ", groupdir, zapng ? '!' : ':');
X
X	zapng = FALSE;
Xagain:
X	ptr = &rcbuf[strlen(rcbuf)];
X	while (get(next) && next <= ngsize)
X		next++;
X	cur = next;
X	while (!(get(next)) && next <= ngsize)
X		next++;
X	if (cur == next) {
X		next = ngsize + 1;
X		goto skip;
X	}
X	if (ptr[-1] != ' ')
X		*ptr++ = ',';
X	if (cur + 1 == next)
X		(void) sprintf(ptr, "%ld", cur);
X	else
X		(void) sprintf(ptr, "%ld-%ld", cur, next - 1);
Xskip:
X	if ((long) next > ngsize) {
X		if (strpbrk(rcbuf, ":!") == NULL)	/* bad line, huh?? */
X			return;
X		ptr = index(rcbuf, ' ');
X		if (ptr == NULL)			/* impossible */
X			return;
X		ptr--;
X		oldptr = *ptr;
X		ptr[0] = ':';
X		ptr[1] = '\0';
X		i = findrcline(groupdir);
X		if (i >= 0) {
X			ptr[0] = oldptr;
X			ptr[1] = ' ';
X			rcline[i] = realloc(rcline[i], (unsigned)(strlen(rcbuf) + 1));
X			if (rcline[i] == NULL)
X				xerror("Cannot realloc");
X			(void) strcpy(rcline[i], rcbuf);
X#ifdef DEBUG
X			fprintf(stderr," new rcline = %s\n", rcline[i]);
X#endif /* DEBUG */
X			return;
X		}
X		if (++line > LINES)
X			xerror("Too many newsgroups");
X		ptr[0] = oldptr;
X		ptr[1] = ' ';
X		if ((rcline[line] = malloc((unsigned)(strlen(rcbuf) + 1))) == NULL)
X			xerror("Not enough memory");
X		(void) strcpy(rcline[line], rcbuf);
X#ifdef DEBUG
X		fprintf(stderr," new rcline2 = %s\n", rcline[line]);
X#endif /* DEBUG */
X		return;
X	}
X	cur = next;
X	goto again;
X}
X
Xnewrc(rcname)
Xchar *rcname;
X{
X	register FILE *fp;
X
X	if (close(creat(rcname, 0666))) {
X		(void) sprintf(bfr, "Cannot create %s", newsrc);
X		xerror(bfr);
X	}
X
X	sprintf(bfr, "%s/users", LIB);
X	if ((fp = fopen(bfr, "a")) != NULL) {
X		fprintf(fp, "%s\n", username);
X		(void) fclose(fp);
X		(void) chmod(bfr, 0666);
X	}
X}
X
Xnextbit()
X{
X#ifdef DEBUG
X	fprintf(stderr,"nextbit() bit = %ld\n", bit);
X#endif /* DEBUG */
X	last = bit;
X	if (readmode == SPEC || xflag) {
X		if (rflag)
X			bit--;
X		else
X			bit++;
X		return;
X	}
X	if (rflag)
X		while (--bit, !get(bit) && bit > minartno)
X			;
X	else
X		while (++bit, !get(bit) && bit <= ngsize)
X			;
X#ifdef DEBUG
X	fprintf(stderr,"nextng leaves bit as %ld\n", bit);
X#endif /* DEBUG */
X}
X
X/*
X * Return TRUE if the user has not ruled out this article.
X */
Xaselect(hp, insist)
Xregister struct hbuf *hp;
Xint	insist;
X{
X	if (insist)
X		return TRUE;
X	if (tflag && !titmat(hp, header.title))
X		return FALSE;
X	if (aflag && cgtdate(hp->subdate) < atime)
X		return FALSE;
X	if (index(hp->nbuf, ',') && !rightgroup(hp))
X		return FALSE;
X	if (fflag && (hp->followid[0] || prefix(hp->title, "Re:")))
X		return FALSE;
X	return TRUE;
X}
X
X/*
X * Code to avoid showing multiple articles for news.
X * Works even if you exit news.
X * Returns nonzero if we should show this article.
X */
Xrightgroup(hp)
Xstruct hbuf *hp;
X{
X	char ng[BUFLEN];
X	register char *p, *g;
X	int i, flag;
X
X	strcpy(ng, hp->nbuf);
X	g = ng;
X	flag = 1;
X	while (g != NULL) {
X		p = index(g, ',');
X		if (p != NULL) {
X			*p++ = '\0';
X			while (*p == ' ')
X				p++;
X		}
X		if (strcmp(g, groupdir) == 0)
X			return flag;
X		if (ngmatch(g, header.nbuf)
X		    && ((i = findrcline(g)) >= 0
X		    && index(rcline[i], '!') == NULL))
X			flag = 0;
X		g = p;
X	}
X	/* we must be in "junk" or "control" */
X	return TRUE;
X}
X
Xback()
X{
X	while (fseek(actfp, -2L, 1) != -1 && ftell(actfp) > 0L) {
X		if (getc(actfp) == '\n')
X			return 0;
X	}
X	if (ftell(actfp) == 0L)
X		return 0;
X	return 1;
X}
X
X/*
X * Trap interrupts.
X */
Xonsig(n)
Xint	n;
X{
X	(void) signal(n, onsig);
X	SigTrap = n;
X	if (rcreadok < 2) {
X		fprintf(stderr, "Aborted early\n");
X		xxit(0);
X	}
X}
X
X/*
X * finds the line in your .newsrc file (actually the in-core "rcline"
X * copy of it) and returns the index into the array where it was found.
X * -1 means it didn't find it.
X *
X * We play clever games here to make this faster.  It's inherently
X * quadratic - we spend lots of CPU time here because we search through
X * the whole .newsrc for each line.  The "prev" variable remembers where
X * the last match was found; we start the search there and loop around
X * to the beginning, in the hopes that the calls will be roughly in order.
X */
Xint
Xfindrcline(name)
Xregister char *name;
X{
X	register char *	p;
X	register int	i;
X	register int	top;
X	register int	len;
X	static int	prev;
X	static int	didthru;
X
X	for ( ; didthru <= line; ++didthru)
X		if ((p = index(rcline[didthru], '!')) != 0 ||
X			(p = index(rcline[didthru], ':')) != 0) {
X				lentab[didthru] = (int)(p - rcline[didthru]);
X		}
X	len = strlen(name);
X	top = line;
X	i = prev;
Xloop:
X	for ( ; i <= top; ++i)
X		if (lentab[i] == len && rcline[i] != NULL &&
X			strncmp(name, rcline[i], len) == 0)
X			return prev = i;
X	if (i > line && line > prev - 1) {
X		i = 0;
X		top = prev - 1;
X		goto loop;
X	}
X	return -1;
X}
X
X/*
X * sortactive - make a local copy of the active file, sorted according
X *   to the user's preferences, according to his .newsrc file.
X */
X
Xstruct table_elt {
X	int	rcindex;
X	long	maxart, minart;
X	char	yn;
X};
X
X#ifdef SORTACTIVE
Xstatic int
Xrcsort(a, b)
Xchar *a, *b;
X{
X	return(((struct table_elt *)a)->rcindex -
X	       ((struct table_elt *)b)->rcindex);
X}
X
Xstatic char *newactivename = "/tmp/newsaXXXXXX";
X#endif /* SORTACTIVE */
X
Xsortactive()
X{
X	register struct table_elt *tp;
X	register char *p;
X	register FILE *nfp, *afp;
X	char aline[BUFLEN], ngname[BUFLEN];
X	struct table_elt table[LINES];
X	int nlines = 0, i, delta, lastline;
X
X#ifdef SORTACTIVE
X	/* make a new sorted copy of ACTIVE */
X	nfp = fopen(mktemp(newactivename), "w");
X	(void) chmod(newactivename, 0600);
X	if (nfp == NULL) {
X		perror(newactivename);
X		return;
X	}
X
X	/* look up all the lines in ACTIVE, finding their positions in .newsrc */
X	p = ACTIVE;
X	ACTIVE = newactivename;
X	afp = xfopen(p, "r");
X#else /* !SORTACTIVE */
X	afp = xfopen(ACTIVE, "r");
X#endif /* !SORTACTIVE */
X	tp = table;
X	while (fgets(aline, sizeof aline, afp) != NULL) {
X		if (sscanf(aline,"%s %ld %ld %c", ngname, &tp->maxart,
X		    &tp->minart, &tp->yn) != 4) 
X			xerror("Active file corrupt");
X		delta = tp->maxart - tp->minart;
X		if (delta >= BITMAPSIZE)
X			BITMAPSIZE = delta + 1;
X		if (Kflag && tp->maxart > 0 && ngmatch(ngname, header.nbuf)) {
X			int j;
X
X			j = findrcline(ngname);
X			if (j >= 0 && index(rcline[j], '!') == NULL) {
X				char rbuf[BUFLEN];
X				if (tp->maxart == 1)
X					sprintf(rbuf, "%s: 1", ngname);
X				else
X					sprintf(rbuf, "%s: 1-%ld", ngname, tp->maxart);
X				rcline[j] = realloc(rcline[j],
X					(unsigned)(strlen(rbuf)+1));
X				if (rcline[j] == NULL)
X					xerror("Not enough memory");
X				strcpy(rcline[j], rbuf);
X			}
X		}
X#ifdef SORTACTIVE
X		tp->rcindex = findrcline(ngname);
X		if (tp->rcindex < 0) {
X			if (++line > LINES)
X				xerror("Too many newsgroups");
X			strcat(ngname, ":");
X			rcline[line] = malloc((unsigned)(strlen(ngname) + 1));
X			if (rcline[line] == NULL)
X				xerror("Not enough memory");
X			strcpy(rcline[line], ngname);
X			tp->rcindex = line;
X		}
X		tp++;
X#endif /* SORTACTIVE */
X	}
X	(void) fclose(afp);
X	BITMAPSIZE =  8 * ((BITMAPSIZE+7) / 8);
X	bitmap = malloc((unsigned)BITMAPSIZE/8);
X	if (bitmap == NULL)
X		xerror("Can't malloc bitmap");
X
X#ifdef SORTACTIVE
X	/* sort by position in user's .newsrc file (new groups come up last) */
X	nlines = tp - table;
X	qsort((char *)table, nlines, sizeof table[0], rcsort);
X
X	tp = table;
X	lastline = tp->rcindex - 1;
X	/* copy active to newactive, in the new order */
X	for (i = 0; i < nlines; i++) {
X		while (++lastline < tp->rcindex) {
X			if (strncmp(rcline[lastline], "options ", 8) == 0) {
X				fprintf(nfp, "%s\n", rcline[lastline]);
X			} else {
X				fprintf(stderr, "Duplicate .newsrc line or bad group %s\n",
X					rcline[lastline]);
X				lentab[lastline] = 0;
X				free(rcline[lastline]);
X				rcline[lastline] = NULL;
X			}
X		}
X		if (rcline[tp->rcindex] == NULL)
X			continue;
X		p = rcline[tp->rcindex];
X		while (*p != ':' && *p != '!')
X			fputc(*p++, nfp);
X		(void) fprintf(nfp, " %ld %ld %c\n", tp->maxart, tp->minart,
X			tp->yn);
X		tp++;
X	}
X	(void) fclose(nfp);
X#endif /* SORTACTIVE */
X}
X
X#if defined(BSD4_2) || defined(BSD4_1C)
X#include <sys/dir.h>
X# else
X#include "ndir.h"
X#endif
X#include <errno.h>
X
X/*
X * Routine to display header lines for all articles in newsgroup. If the flag
X * argument is FALSE then only articles which are not marked as read in the
X * bitmap will be displayed. This routine makes no attempt to determine if
X * the article is in multiple groups and therefore should not be displayed at
X * this time. 
X */
X
Xstatic int *lg_array = NULL;
Xstatic int *lg_entry;
Xstatic int lg_max = 0;
Xstatic int int_sig;
Xextern int errno;
X
Xlg_cmp(p1, p2)
Xint *p1, *p2;
X{
X	return *p1 - *p2;
X}
X
Xlist_group(lgroup, displines, flag, pngsize)
Xchar *lgroup;
Xint displines, flag;
Xlong pngsize;
X{
X	char *briefdate();
X	struct hbuf hh;
X	register DIR *dirp;
X	register struct direct *dir;
X	register FILE *fp_art;
X	int i;
X	int entries;
X	unsigned int alloc_size;
X	int (*old_sig) ();
X	extern lg_trap();
X	char *gets();
X
X	/* This should get the numbers from the active file XXX */
X	if ((dirp = opendir(dirname(lgroup))) == NULL) {
X		printf("Can't open %s\r\n", dirname(lgroup));
X		return;
X	}
X	entries = 0;
X	if (lg_array == NULL) {
X		lg_max = 50;
X		alloc_size = lg_max * sizeof(int);
X		lg_array = (int *) malloc(alloc_size);
X	}
X	while ((dir = readdir(dirp)) != NULL) {
X		if (dir->d_ino == 0)
X			continue;
X		i = atoi(dir->d_name);
X		if ((i < 1) || (i > pngsize))
X			continue;
X		if (flag == FALSE) {
X			if (get(i) == 0)
X				continue;
X		}
X		if (++entries > lg_max) {
X			lg_max += 50;
X			alloc_size = lg_max * sizeof(int);
X			lg_array = (int *) realloc((char *) lg_array, alloc_size);
X		}
X		lg_array[entries - 1] = i;
X	}
X	if (entries == lg_max) {
X		lg_max++;
X		alloc_size = lg_max * sizeof(int);
X		lg_array = (int *) realloc((char *) lg_array, alloc_size);
X	}
X	qsort(lg_array, entries, sizeof *lg_array, lg_cmp);
X	lg_array[entries] = 0;
X	int_sig = 0;
X	old_sig = signal(SIGINT, lg_trap);
X	hh.unrec[0] = NULL;
X	for (lg_entry = lg_array; *lg_entry != 0 && int_sig == 0; lg_entry++) {
X		(void) sprintf(filename, "%s/%d", dirname(lgroup), *lg_entry);
X		fp_art = fopen(filename, "r");
X		if (fp_art == NULL)
X			continue;
X		if (hread(&hh, fp_art, TRUE) == NULL) {
X			(void) fclose(fp_art);
X			continue;
X		}
X		printf("%5d %-20.20s %-13s  %s\r\n",
X		       *lg_entry, hh.from,
X		       briefdate(hh.subdate), hh.title);
X		for (i = 0; i < displines;) {
X			if (fgets(bfr, LBUFLEN, fp_art) == NULL) {
X				break;
X			}
X			if ((bfr[0] == '\n') || (bfr[0] == '>')) {
X				continue;
X			}
X			printf("%s", bfr);
X			i++;
X		}
X		(void) fclose(fp_art);
X	}
X	(void) fflush(stdout);
X
X	closedir(dirp);
X	(void) signal(SIGINT, old_sig);	/* restore to old value */
X
X	printf("[Press RETURN to continue]");
X	(void) fflush(stdout);
X
X	while (TRUE) {
X		errno = 0;
X		i = getchar();
X		if (errno == EINTR)
X			continue;
X		if (i == '\n' || i == '\r')
X			break;
X		if (i == EOF)
X			break;
X		if (i == '\4')
X			break;
X	}
X	(void) free(lg_array);
X	lg_array = NULL;
X
X}
X
Xlg_trap(code)
Xint code;
X{
X
X	int_sig = 1;
X	(void) signal(code, lg_trap);	/* reset signal */
X
X}
END_OF_FILE
if test 18733 -ne `wc -c <'rfuncs.c'`; then
    echo shar: \"'rfuncs.c'\" unpacked with wrong size!
fi
# end of 'rfuncs.c'
fi
if test -f 'header.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'header.c'\"
else
echo shar: Extracting \"'header.c'\" \(16672 characters\)
sed "s/^X//" >'header.c' <<'END_OF_FILE'
X/*
X * This software is Copyright (c) 1986 by Rick Adams.
X *
X * Permission is hereby granted to copy, reproduce, redistribute or
X * otherwise use this software as long as: there is no monetary
X * profit gained specifically from the use or reproduction or this
X * software, it is not sold, rented, traded or otherwise marketed, and
X * this copyright notice is included prominently in any copy
X * made.
X *
X * The author make no claims as to the fitness or correctness of
X * this software for any use whatsoever, and it is provided as is. 
X * Any use of this software is at the user's own risk.
X *
X * header.c - header functions plus some other goodies
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)header.c	2.48	3/20/87";
X#endif /* SCCSID */
X
X#include <stdio.h>
X#include "params.h"
X#include "patchlevel.h"
X
Xchar *hfgets();
X
Xchar *news_version = NEWS_VERSION;
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#ifndef GENERICPATH
X	register int	len;
X#endif /* GENERICPATH */
X	register int	i;
X#ifdef OLD
X	char *p;
X#endif /* OLD */
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		bzero((char *)hp, sizeof (*hp));
X		for (i=0;i<NUNREC;i++)
X			hp->unrec[i] = NULL;
X	}
X
X	/* Check that it's a B news style header. */
X	if (hfgets(bfr, PATHLEN, fp) != NULL && isalpha(bfr[0])
X	    && index(bfr, ':'))
X		if (frmread(fp, hp))
X			goto strip;
X
X	if (!nstrip(bfr+1))
X		return NULL;
X
X	/* It's not.  Try A news (begins with PROTO). */
X	if (bfr[0] != PROTO)
X		return NULL;
X#ifndef OLD
X	logerr("Can not process A news format article without OLD defined");
X#else /* OLD */
X	/* Read in an A news format article. */
X	p = index(bfr+1, '.');
X	if (p == NULL) {
X		(void) strcpy(hp->ident, bfr+1);
X		return NULL;
X	}
X	*p++ = '\0';
X	(void) sprintf(hp->ident, "<%s@%s%s>", p, bfr+1, ".UUCP");
X
X	/* Newsgroup List */
X	if (hfgets(hp->nbuf, BUFLEN, fp) == NULL || !nstrip(hp->nbuf))
X		return NULL;
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#endif /* OLD */
X
Xstrip:	/* strip off sys! from front of path. */
X#ifndef GENERICPATH
X	if (strncmp(PATHSYSNAME, hp->path, (len = strlen(PATHSYSNAME))) == 0
X		&& index(NETCHRS, hp->path[len]))
X		(void) strcpy(hp->path, &(hp->path[len+1]));
X#endif /* GENERICPATH */
X	lcase(hp->nbuf);
X
X	/* Intuit the From: line if only a path was given. */
X	if (wholething) {
X#ifdef OLD
X		if (hp->from[0] == '\0')
X			intuitfrom(hp);
X		else
X#endif /* OLD */
X			fixfrom(hp);
X	}
X
X	return fp;
X}
X
X
X/*
X * Get header info from mail-format file.
X * Return non-zero on success.
X */
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 XREF		24
X#define SUMMARY		25
X#define XPATH		26
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
X	i = type(bfr);
X	do {
X		curpos = ftell(fp);
X		switch (i) {
X		case PATH:
X			getfield(hp->path, sizeof(hp->path));
X			break;
X		case FROM:
X			getfield(hp->from, sizeof(hp->from));
X			break;
X		case NEWSGROUP:
X			getfield(hp->nbuf, sizeof(hp->nbuf));
X			break;
X		case TITLE:
X			getfield(hp->title, sizeof(hp->title));
X			break;
X		case SUBMIT:
X			getfield(hp->subdate, sizeof(hp->subdate));
X			break;
X		case EXPIRE:
X			getfield(hp->expdate, sizeof(hp->expdate));
X			break;
X#ifdef OLD
X		case ARTICLEID:
X			/* Believe Message-ID in preference to Article-ID */
X			if (hp->ident[0] == '\0') {
X				register char *p;
X				char msgb[NAMELEN];
X				getfield(msgb, sizeof msgb);
X				p = index(msgb, '.');
X				if (p == NULL) {
X					(void) strcpy(hp->ident, msgb);
X				} else {
X					*p++ = '\0';
X					(void) sprintf(hp->ident, "<%s@%s%s>", p, msgb, ".UUCP");
X				}
X			}
X			break;
X#endif /* OLD */
X		case MESSAGEID:
X			getfield(hp->ident, sizeof(hp->ident));
X			break;
X		case REPLYTO:
X			getfield(hp->replyto, sizeof(hp->replyto));
X			break;
X		case FOLLOWID:
X			getfield(hp->followid, sizeof(hp->followid));
X			break;
X		case SENDER:
X			getfield(hp->sender, sizeof(hp->sender));
X			break;
X		case FOLLOWTO:
X			getfield(hp->followto, sizeof(hp->followto));
X			break;
X		case CONTROL:
X			getfield(hp->ctlmsg, sizeof(hp->ctlmsg));
X			break;
X		case DISTRIBUTION:
X			getfield(hp->distribution, sizeof(hp->distribution));
X			if (strcmp(hp->distribution, "net") == 0)
X				hp->distribution[0] = '\0';
X			break;
X		case ORGANIZATION:
X			getfield(hp->organization, sizeof(hp->organization));
X			break;
X		case NUMLINES:
X			getfield(hp->numlines, sizeof(hp->numlines));
X			hp->intnumlines = atoi(hp->numlines);
X			break;
X		case KEYWORDS:
X			getfield(hp->keywords, sizeof(hp->keywords));
X			break;
X		case APPROVED:
X			getfield(hp->approved, sizeof(hp->approved));
X			break;
X		case NFID:
X			getfield(hp->nf_id, sizeof(hp->nf_id));
X			break;
X		case NFFROM:
X			getfield(hp->nf_from, sizeof(hp->nf_from));
X			break;
X		/* discard these lines */
X		case XREF:
X		case XPATH:
X		case RELAYVERSION:
X		case POSTVERSION:
X		case RECEIVE:
X			break;
X		case SUMMARY:
X			getfield(hp->summary, sizeof(hp->summary));
X			break;
X		case OTHER:
X			if (unreccnt < NUNREC) {
X				if ((hp->unrec[unreccnt] = malloc((unsigned)(strlen(bfr) + 1))) != NULL ) {
X					(void) strcpy(hp->unrec[unreccnt], bfr);
X					(void) nstrip(hp->unrec[unreccnt]);
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])
X		return TRUE;
X	return FALSE;
X}
X
X#ifdef OLD
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;
X	char *tailpath(), *rindex();
X	char *at, *dot;
X	char pathbuf[PATHLEN];
X	char fullname[BUFLEN];
X	extern char *mydomain();
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			(void) 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 = FROMSYSNAME;
X	else
X		host = tp;
X
X	tp = index(host, '@');
X	if (tp != NULL)
X		*tp = 0;
X	if (index(host, '.') != NULL)
X		(void) sprintf(hp->from, "%s@%s%s", user, host, mydomain());
X	else
X		(void) sprintf(hp->from, "%s@%s", user, host);
X
X	skin(pathbuf, fullname, hp->path);	/* remove RFC822-style comments */
X	if (fullname[0] != '\0') {
X		strcat(hp->from, " (");
X		(void) strcat(hp->from, fullname);
X		strcat(hp->from, ")");
X	}
X	strcpy(hp->path, pathbuf);	/* and stick it back in */
X}
X#endif /* OLD */
X
X/*
X * Canonicalize the "From:" line into the form
X *
X * From: <mail-address> (full-name)
X *
X * RFC822 doesn't require the comment to be at the end of the string
X * like that.
X */
Xfixfrom(hp)
Xregister struct hbuf *hp;
X{
X	char frombuf[PATHLEN];
X	char fullname[BUFLEN];
X
X	skin(frombuf, fullname, hp->from);	/* remove RFC822-style comments */
X	if (fullname[0] != '\0') {
X		strcat(frombuf, " (");
X		strcat(frombuf, fullname);
X		strcat(frombuf, ")");
X	}
X	strcpy(hp->from, frombuf);	/* stick the canonicalized "from" back in */
X}
X
Xskin(name, fullname, hfield)
Xchar *name;
Xchar *fullname;
Xchar *hfield;
X{
X	register int c;
X	register char *cp, *cp2;
X	char *bufend;
X	int gotlt, parenlev, lastsp;
X	int seenfullname = FALSE;
X
X	*fullname = '\0';	/* no full name yet */
X	if (strpbrk(hfield, "(< ") == NULL) {		/* include ',' ?? */
X		strcpy(name, hfield);
X		return;
X	}
X	gotlt = 0;
X	parenlev = 0;
X	lastsp = 0;
X	bufend = name;
X	for (cp = hfield, cp2 = bufend; c = *cp++; ) {
X		switch (c) {
X		case '(':
X			/*
X			 * Start of a "comment".
X			 * Ignore it, or save it in "fullname" if we haven't
X			 * seen a comment yet.
X			 */
X			parenlev++;
X			while ((c = *cp) != 0) {
X				cp++;
X				switch (c) {
X				case '\\':
X					if ((c = *cp) == 0)
X						goto outcm;
X					cp++;
X					break;
X				case '(':
X					parenlev++;
X					break;
X				case ')':
X					parenlev--;
X					if (parenlev == 0)
X						goto outcm;
X					break;
X				}
X				if (!seenfullname)
X					*fullname++ = c;
X			}
X		outcm:
X			parenlev = 0;
X			lastsp = 0;
X			if (!seenfullname) {
X				*fullname = '\0';
X				seenfullname = TRUE;	/* only extract first comment */
X			}
X			break;
X
X		case '"':
X			/*
X			 * Start of a "quoted-string".
X			 * Copy it in its entirety.
X			 */
X			while ((c = *cp) != 0) {
X				cp++;
X				switch (c) {
X				case '\\':
X					if ((c = *cp) == 0)
X						goto outqs;
X					cp++;
X					break;
X				case '"':
X					goto outqs;
X				}
X				*cp2++ = c;
X			}
X		outqs:
X			lastsp = 0;
X			break;
X
X		case ' ':
X			if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ')
X				cp += 3, *cp2++ = '@';
X			else
X			if (cp[0] == '@' && cp[1] == ' ')
X				cp += 2, *cp2++ = '@';
X			else
X				lastsp = 1;
X			break;
X
X		case '<':
X			if (!seenfullname) {
X				*cp2 = '\0';
X				strcpy(fullname, name);
X				seenfullname = TRUE;
X			}
X			cp2 = bufend;
X			gotlt++;
X			lastsp = 0;
X			break;
X
X		case '>':
X			if (gotlt) {
X				gotlt = 0;
X				/*
X				 * this doesn't seem reasonable, what about (,)
X				 * or "," ??
X				 */
X				while (*cp != ',' && *cp != 0)
X					cp++;
X				if (*cp == 0 )
X					goto done;
X				*cp2++ = ',';
X				*cp2++ = ' ';
X				bufend = cp2;
X				break;
X			}
X
X			/* Fall into . . . */
X
X		default:
X			if (lastsp) {
X				lastsp = 0;
X				*cp2++ = ' ';
X			}
X			*cp2++ = c;
X			break;
X		}
X	}
Xdone:
X	*cp2 = 0;
X}
X
X
X#ifdef OLD
Xchar *
Xoident(ident)
Xchar *ident;
X{
X	char lbuf[BUFLEN];
X	static char oidbuf[BUFLEN];
X	register char *p, *q;
X
X	(void) strcpy(lbuf, ident);
X	p = index(lbuf, '@');
X	if (p == NULL)
X		return ident;
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	(void) sprintf(oidbuf, "%s.%s", p, lbuf+1);
X	return oidbuf;
X}
X#endif /* OLD */
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, size)
Xchar	*hpfield;
Xint	size;
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		(void) strncpy(hpfield, ptr, size - 1);
X		(void) nstrip(hpfield);
X	}
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 (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: "))
X		return TITLE;
X	if (its("Date: "))
X		return SUBMIT;
X	if (its("Date-Received: "))
X		return RECEIVE;
X#ifdef OLD
X	if (its("Title: "))
X		return TITLE;
X	if (its("Posted: "))
X		return SUBMIT;
X	if (its("Received: "))
X		return RECEIVE;
X#endif /* OLD */
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("Summary: "))
X		return SUMMARY;
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	if (its("Xref: "))
X		return XREF;
X	if (its("Xpath: "))
X		return XPATH;
X	if (!isalpha(*ptr))
X		return FALSE;
X	colon = index(ptr, ':');
X	space = index(ptr, ' ');
X	if (!colon || space && space < colon)
X		return FALSE;
X	return OTHER;
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	/*
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, PATHSYSNAME) &&
X		index(NETCHRS, hp->path[strlen(PATHSYSNAME)]))
X		fprintf(fp, "Path: %s\n", hp->path);
X	else
X		fprintf(fp, "Path: %s!%s\n", PATHSYSNAME, hp->path);
X	if (hp->from[0])
X		fprintf(fp, "From: %s\n", hp->from);
X
X	fprintf(fp, "Newsgroups: %s\n", hp->nbuf);
X	fprintf(fp, "Subject: %s\n", hp->title);
X	if (*hp->summary)
X		fprintf(fp, "Summary: %s\n", hp->summary);
X	if (*hp->keywords)
X		fprintf(fp, "Keywords: %s\n", hp->keywords);
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", oident(hp->ident));
X	fprintf(fp, "Posted: %s", ctime(&t));
X#endif /* OLD */
X	if (*hp->expdate)
X		fprintf(fp, "Expires: %s\n", hp->expdate);
X	if (*hp->followid) {
X		register char *dp, *cp;
X
X		dp = cp = hp->followid;
X		while (*cp != '\0')
X			if (*cp == '<' && *(cp + 1) == '>')
X				cp += 2;
X			else
X				*dp++ = *cp++;
X		*dp = '\0';
X		if (*hp->followid)
X			fprintf(fp, "References: %s\n", hp->followid);
X	}
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->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#ifdef DOXREFS
X	if ( wr ==1 && *hp->xref)
X		fprintf(fp, "Xref: %s\n", hp->xref);
X#endif /* DOXREFS */
X	for (iu = 0; iu < NUNREC; iu++) {
X		if (hp->unrec[iu])
X			fprintf(fp, "%s\n", &hp->unrec[iu][0]);
X	}
X	putc('\n', fp);
X}
X
X
X#ifndef BSD4_2
X/*
X * Set nc bytes, starting at cp, to zero.
X */
Xbzero(cp, nc)
Xregister char	*cp;
Xregister int	nc;
X{
X	if (nc > 0)
X		do {
X			*cp++ = 0;
X		} while (--nc);
X}
X#endif /* !BSD4_2 */
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 int n = 0;
X	register char *cp;
X
X	cp = buf;
X	while (n < len && (c = getc(fp)) != EOF) {
X		if (c == '\n')
X			break;
X		if (isprint(c) || c == '\b' || c == ' ' || c == '\t') {
X			*cp++ = c;
X			n++;
X		}
X	}
X	if (c == EOF && cp == buf)
X		return NULL;
X	*cp = '\0';
X
X	if (c != '\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 (cp == buf) {
X		/* Don't look for continuation of blank lines */
X		*cp++ = '\n';
X		*cp = '\0';
X		return buf;
X	}
X
X	while ((c = getc(fp)) == ' ' || c == '\t') {	/* for each cont line */
X		/* Continuation line. */
X		if ((n += 2) < len) {
X			*cp++ = '\n';
X			*cp++ = c;
X		}
X		while ((c = getc(fp)) != '\n' && c != EOF)
X			if ((isprint(c) || c == '\b' || c == ' ' || c == '\t')
X				&& n++ < len)
X				*cp++ = c;
X	}
X	if (n >= len - 1)
X		cp = buf + len - 2;
X	*cp++ = '\n';
X	*cp = '\0';
X	if (c != EOF)
X		(void) ungetc(c, fp); /* push back first char of next header */
X	return buf;
X}
END_OF_FILE
if test 16672 -ne `wc -c <'header.c'`; then
    echo shar: \"'header.c'\" unpacked with wrong size!
fi
# end of 'header.c'
fi
echo shar: End of shell archive.
exit 0
-- 
		"Zeta Microcomputer Software"
ACSnet:    nick@ultima.cs.uts.oz
UUCP:      ...!uunet!munnari!ultima.cs.uts.oz!nick
Fidonet:   Nick Andrew on 3:713/602 (Zeta)

nick@ultima.cs.uts.oz (Nick Andrew) (12/07/89)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  makeactive.sh funcs2.c getdate.y funcs.c
# Wrapped by nick@nswitgould on Thu Dec  7 22:41:08 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'makeactive.sh' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'makeactive.sh'\"
else
echo shar: Extracting \"'makeactive.sh'\" \(15147 characters\)
sed "s/^X//" >'makeactive.sh' <<'END_OF_FILE'
X: "Create active file and newsgroup hierarchy for new machine"
X: "Usage: sh makeactive.sh LIBDIR SPOOLDIR NEWSUSR NEWSGRP"
X: '@(#)makeactive	1.23	12/16/86'
XLIBDIR=$1
XSPOOLDIR=$2
XNEWSUSR=$3
XNEWSGRP=$4
Xcat <<"E_O_F" > /tmp/$$groups
Xgeneral	Articles that should be read by everyone on your local system
Xnet.sources		For the posting of software packages & documentation.
Xnet.sources.bugs	For bug fixes and features discussion
Xnet.sources.games	Postings of recreational software
Xnet.sources.mac		Software for the Apple Macintosh
Xmod.announce		General announcements of interest to all. (Moderated)
Xmod.announce.newusers	Explanatory postings for new users. (Moderated)
Xmod.ai			Discussions about Artificial Intelligence (Moderated)
Xmod.amiga		Commodore Amiga micros -- info, uses, but no programs. (Moderated)
Xmod.amiga.binaries	Encoded public domain programs in binary form. (Moderated)
Xmod.amiga.sources	Public domain software in source code format. (Moderated)
Xmod.compilers		Discussion about compiler construction, theory, etc. (Moderated)
Xmod.computers		Discussion about various computers and related. (Moderated)
Xmod.computers.68k		68000-based systems. (Moderated)
Xmod.computers.apollo		Apollo computer systems. (Moderated)
Xmod.computers.masscomp		The Masscomp line of computers. (Moderated)
Xmod.computers.ibm-pc		The IBM PC, PC-XT, and PC-AT. (Moderated)
Xmod.computers.laser-printers	Laser printers, hardware and software. (Moderated)
Xmod.computers.pyramid		Pyramid 90x computers. (Moderated)
Xmod.computers.ridge		Ridge 32 computers and ROS. (Moderated)
Xmod.computers.sequent		Sequent systems, (esp. Balance 8000). (Moderated)
Xmod.computers.sun		Sun "workstation" computers (Moderated)
Xmod.computers.vax		DEC's VAX* line of computers & VMS. (Moderated)
Xmod.computers.workstations	Various workstation-type computers. (Moderated)
Xmod.conferences		Calls for papers and conference announcements. (Moderated)
Xmod.comp-soc		Discussion on the impact of technology on society. (Moderated)
Xmod.graphics		Graphics software, hardware, theory, etc. (Moderated)
Xmod.human-nets		Computer aided communications digest. (Moderated)
Xmod.mac			Apple Macintosh micros -- info, uses, but no programs. (Moderated)
Xmod.mac.binaries	Encoded public domain programs in binary form. (Moderated)
Xmod.mac.sources		Public domain software in source code format. (Moderated)
Xmod.mag.otherrealms	Edited science fiction and fantasy "magazine". (Moderated)
Xmod.map			Various maps, including UUCP maps (Moderated)
Xmod.music		Reviews and discussion of things musical (Moderated)
Xmod.music.gaffa		Progressive music discussions (e.g., Kate Bush). (Moderated)
Xmod.newprod		Announcements of new products of interest to readers (Moderated)
Xmod.newslists		Postings of news-related statistics and lists (Moderated)
Xmod.os			Disussions about operating systems and related areas. (Moderated)
Xmod.os.os9		Discussions about the os9 operating system. (Moderated)
Xmod.os.unix		Discussion of UNIX* features and bugs. (Moderated)
Xmod.philosophy		Discussion of philosphical issues and concepts. (Moderated)
Xmod.philosophy.tech	Technical philosophy: math, science, logic, etc (Moderated)
Xmod.politics		Discussions on political problems, systems, solutions. (Moderated)
Xmod.politics.arms-d		Arms discussion digest. (Moderated)
Xmod.protocols		Various forms and types of FTP protocol discussions. (Moderated)
Xmod.protocols.appletalk		Applebus hardware & software discussion. (Moderated)
Xmod.protocols.kermit		Information about the Kermit package. (Moderated)
Xmod.protocols.tcp-ip		TCP and IP network protocols. (Moderated)
Xmod.psi			Discussion of paranormal abilities and experiences. (Moderated)
Xmod.rec			Discussions on pastimes (not currently active) (Moderated)
Xmod.rec.guns		Discussions about firearms (Moderated)
Xmod.recipes		A "distributed cookbook" of screened recipes. (Moderated)
Xmod.religion		Top-level group with no moderator (as of yet). (Moderated)
Xmod.religion.christian	Discussions on Christianity and related topics. (Moderated)
Xmod.risks		Risks to the public from computers & users. (Moderated)
Xmod.sources		postings of public-domain sources. (Moderated)
Xmod.sources.doc		Archived public-domain documentation. (Moderated)
Xmod.sources.games	Postings of public-domain game sources (Moderated)
Xmod.std			Discussion about various standards (Moderated)
Xmod.std.c		Discussion about C language standards (Moderated)
Xmod.std.mumps		Discussion for the X11.1 committee on Mumps (Moderated)
Xmod.std.unix		Discussion for the P1003 committee on UNIX (Moderated)
Xmod.techreports		Announcements and lists of technical reports. (Moderated)
Xmod.telecom		Telecommunications digest. (Moderated)
Xcomp.ai			Artificial intelligence discussions.
Xcomp.arch		Computer architecture.
Xcomp.bugs.2bsd		Reports of UNIX* version 2BSD related bugs.
Xcomp.bugs.4bsd		Reports of UNIX version 4BSD related bugs.
Xcomp.bugs.misc		General bug reports and fixes (includes V7 & uucp).
Xcomp.bugs.sys5		Reports of USG (System III, V, etc.) bugs.
Xcomp.cog-eng		Cognitive engineering.
Xcomp.databases		Database and data management issues and theory.
Xcomp.dcom.lans		Local area network hardware and software.
Xcomp.dcom.modems	Data communications hardware and software.
Xcomp.edu		Computer science education.
Xcomp.emacs		EMACS editors of different flavors.
Xcomp.graphics		Computer graphics, art, animation, image processing,
Xcomp.lang.ada		Discussion about Ada*.
Xcomp.lang.apl		Discussion about APL.
Xcomp.lang.c		Discussion about C.
Xcomp.lang.c++		The object-oriented C++ language.
Xcomp.lang.forth		Discussion about Forth.
Xcomp.lang.fortran	Discussion about FORTRAN.
Xcomp.lang.lisp		Discussion about LISP.
Xcomp.lang.misc		Different computer languages not specifically listed.
Xcomp.lang.modula2	Discussion about Modula-2.
Xcomp.lang.pascal	Discussion about Pascal.
Xcomp.lang.prolog	Discussion about PROLOG.
Xcomp.lang.smalltalk	Discussion about Smalltalk 80.
Xcomp.lsi		Large scale integrated circuits.
Xcomp.mail.headers	Gatewayed from the ARPA header-people list.
Xcomp.mail.misc		General discussions about computer mail.
Xcomp.mail.uucp		Mail in the uucp network environment.
Xcomp.misc		General topics about computers not covered elsewhere.
Xcomp.org.decus		DEC* Users' Society newsgroup.
Xcomp.org.usenix		USENIX Association events and announcements.
Xcomp.os.cpm		Discussion about the CP/M operating system.
Xcomp.os.eunice		The SRI Eunice system.
Xcomp.os.misc		General OS-oriented discussion not carried elsewhere.
Xcomp.periphs		Peripheral devices.
Xcomp.sources.d		For any discussion of source postings.
Xcomp.sources.wanted	Requests for software and fixes.
Xcomp.std.internat	Discussion about international standards
Xcomp.sys.amiga		Discussion about the Amiga micro.
Xcomp.sys.apple		Discussion about Apple micros.
Xcomp.sys.atari.8bit	Discussion about 8 bit Atari micros.
Xcomp.sys.atari.st	Discussion about 16 bit Atari micros.
Xcomp.sys.att		Discussions about AT&T microcomputers 
Xcomp.sys.cbm		Discussion about Commodore micros.
Xcomp.sys.dec		Discussions about DEC computer systems.
Xcomp.sys.hp		Discussion about Hewlett/Packard's.
Xcomp.sys.ibm.pc		Discussion about IBM personal computers.
Xcomp.sys.intel		Disucussions about Intel systems and parts.
Xcomp.sys.m6809		Discussion about 6809's.
Xcomp.sys.m68k		Discussion about 68k's.
Xcomp.sys.mac		Discussions about the Apple Macintosh & Lisa.
Xcomp.sys.misc		Micro computers of all kinds.
Xcomp.sys.nsc.32k	National Semiconductor 32000 series chips
Xcomp.sys.tandy		Discussion about TRS-80's.
Xcomp.sys.ti		Discussion about Texas Instruments.
Xcomp.terminals		All sorts of terminals.
Xcomp.text		Text processing.
Xcomp.unix.questions	UNIX neophytes group.
Xcomp.unix.wizards	Discussions, bug reports, and fixes on and for UNIX.
Xcomp.unix.xenix		Discussion about the Xenix OS.
Xmisc.consumers		Consumer interests, product reviews, etc.
Xmisc.consumers.house	Discussion about owning and maintaining a house.
Xmisc.forsale		Short, tasteful postings about items for sale.
Xmisc.headlines		Current interest: drug testing, terrorism, etc.
Xmisc.invest		Investments and the handling of money.
Xmisc.jobs		Job announcements, requests, etc.
Xmisc.kids		Children, their behavior and activities.
Xmisc.legal		Legalities and the ethics of law.
Xmisc.misc		Various discussions not fitting in any other group.
Xmisc.taxes		Tax laws and advice.
Xmisc.test		For testing of network software.  Very boring.
Xmisc.wanted		Requests for things that are needed (NOT software).
Xnews.admin		Comments directed to news administrators.
Xnews.config		Postings of system down times and interruptions.
Xnews.groups		Discussions and lists of newsgroups
Xnews.lists		News-related statistics and lists (Moderated)
Xnews.misc		Discussions of USENET itself.
Xnews.newsites		Postings of new site announcements.
Xnews.software.b		Discussion about B news software.
Xnews.software.notes	Notesfile software from the Univ. of Illinois.
Xnews.stargate		Discussion about satellite transmission of news.
Xnews.sysadmin		Comments directed to system administrators.
Xrec.arts.books		Books of all genres, shapes, and sizes.
Xrec.arts.comics		The funnies, old and new.
Xrec.arts.drwho		Discussion about Dr. Who.
Xrec.arts.movies		Reviews and discussions of movies.
Xrec.arts.poems		For the posting of poems.
Xrec.arts.sf-lovers	Science fiction lovers' newsgroup.
Xrec.arts.startrek	Star Trek, the TV show and the movies.
Xrec.arts.tv		The boob tube, its history, and past and current shows.
Xrec.arts.tv.soaps	Postings about soap operas.
Xrec.arts.wobegon	"A Prairie Home Companion" radio show discussion.
Xrec.audio		High fidelity audio.
Xrec.autos		Automobiles, automotive products and laws.
Xrec.autos.tech		Technical aspects of automobiles, et. al.
Xrec.aviation		Aviation rules, means, and methods.
Xrec.bicycles		Bicycles, related products and laws.
Xrec.birds		Hobbyists interested in bird watching.
Xrec.boats		Hobbyists interested in boating.
Xrec.food.cooking	Food, cooking, cookbooks, and recipes.
Xrec.food.drink		Wines and spirits.
Xrec.food.veg		Vegetarians.
Xrec.games.board		Discussion and hints on board games.
Xrec.games.bridge	Hobbyists interested in bridge.
Xrec.games.chess		Chess & computer chess.
Xrec.games.empire	Discussion and hints about Empire.
Xrec.games.frp		Discussion about Fantasy Role Playing games.
Xrec.games.go		Discussion about Go.
Xrec.games.hack		Discussion, hints, etc. about the Hack game.
Xrec.games.misc		Games and computer games.
Xrec.games.pbm		Discussion about Play by Mail games.
Xrec.games.rogue		Discussion and hints about Rogue.
Xrec.games.trivia	Discussion about trivia.
Xrec.games.video		Discussion about video games.
Xrec.gardens		Gardening, methods and results.
Xrec.ham-radio		Amateur Radio practices, contests, events, rules, etc.
Xrec.ham-radio.packet	Discussion about packet radio setups.
Xrec.humor		Jokes and the like.  May be somewhat offensive.
Xrec.humor.d		Discussions on the content of rec.humor articles
Xrec.mag			Magazine summaries, tables of contents, etc.
Xrec.misc		General topics about recreational/participant sports.
Xrec.motorcycles		Motorcycles and related products and laws.
Xrec.music.classical	Discussion about classical music.
Xrec.music.folk		Folks discussing folk music of various sorts
Xrec.music.gdead		A group for (Grateful) Dead-heads
Xrec.music.makers	For performers and their discussions.
Xrec.music.misc		Music lovers' group.
Xrec.music.synth		Synthesizers and computer music
Xrec.nude		Hobbyists interested in naturist/nudist activities.
Xrec.pets		Pets, pet care, and household animals in general.
Xrec.photo		Hobbyists interested in photography.
Xrec.puzzles		Puzzles, problems, and quizzes.
Xrec.railroad		Real and model train fans' newsgroup.
Xrec.scuba		Hobbyists interested in SCUBA diving.
Xrec.skiing		Hobbyists interested in skiing.
Xrec.skydiving		Hobbyists interested in skydiving.
Xrec.sport.baseball	Discussion about baseball.
Xrec.sport.basketball	Discussion about basketball.
Xrec.sport.football	Discussion about football.
Xrec.sport.hockey	Discussion about hockey.
Xrec.sport.misc		Spectator sports.
Xrec.travel		Traveling all over the world.
Xrec.video		Video and video components.
Xrec.woodworking		Hobbyists interested in woodworking.
Xsci.astro		Astronomy discussions and information.
Xsci.bio			Biology and related sciences.
Xsci.crypt		Different methods of data en/decryption.
Xsci.electronics		Circuits, theory, electrons and discussions.
Xsci.lang		Natural languages, communication, etc.
Xsci.math		Mathematical discussions and pursuits
Xsci.math.stat		Statistics discussion.
Xsci.math.symbolic	Symbolic algebra discussion.
Xsci.med			Medicine and its related products and regulations.
Xsci.misc		Short-lived discussions on subjects in the sciences.
Xsci.physics		Physical laws, properties, etc.
Xsci.research		Research methods, funding, ethics, and whatever.
Xsci.space		Space, space programs, space related research, etc.
Xsci.space.shuttle	The space shuttle and the STS program.
Xsoc.college		College, college activities, campus life, etc.
Xsoc.culture.african	Discussions about Africa & things African
Xsoc.culture.celtic	Group about Celtics (*not* basketball!)
Xsoc.culture.greek	Group about Greeks.
Xsoc.culture.indian	Group for discussion about India & things Indian
Xsoc.culture.jewish	Group for discussion about Jewish culture & religion
Xsoc.culture.misc	Group for discussion about other cultures
Xsoc.misc		Socially-oriented topics not in other groups.
Xsoc.motss		Issues pertaining to homosexuality.
Xsoc.roots		Genealogical matters.
Xsoc.singles		Newsgroup for single people, their activities, etc.
Xsoc.net-people		Announcements, requests, etc. about people on the net.
Xsoc.women		Women's rights, discrimination, etc.
Xtalk.abortion		All sorts of discussions and arguments on abortion.
Xtalk.bizarre		The unusual, bizarre, curious, and often stupid.
Xtalk.origins		Evolution versus creationism (sometimes hot!).
Xtalk.philosophy.misc	Philosophical musings on all topics.
Xtalk.politics.misc	Political discussions and ravings of all kinds.
Xtalk.politics.theory	Theory of politics and political systems.
Xtalk.religion.misc	Religious, ethical, & moral implications.
Xtalk.rumors		For the posting of rumors.
XE_O_F
X: if active file is empty, create it
Xif test ! -s $LIBDIR/active
Xthen
X	sed 's/[ 	].*/ 00000 00001/' /tmp/$$groups > $LIBDIR/active
X	cat <<'E_O_F' >>$LIBDIR/active
Xcontrol 00000 00001
Xjunk 00000 00001
XE_O_F
X	set - group 0 1
Xelse
X: make sure it is in the new format
X	set - `sed 1q $LIBDIR/active`
X	case $# in
X	4)	ed - $LIBDIR/active << 'EOF'
Xg/^mod\./s/y$/m/
Xw
Xq
XEOF
X		;;
X	3)	;;
X	2)	ed - $LIBDIR/active << 'EOF'
X1,$s/$/ 00001/
Xw
Xq
XEOF
X		echo
X		echo Active file updated to new format.
X		echo You must run expire immediately after this install
X		echo is done to properly update the tables.;;
X	*) echo Active file is in unrecognized format. Not upgraded.;;
X	esac
Xfi
Xif test $# -eq 3 -o $# -eq 2
Xthen
X	(sed '/^!net/!d
Xs/^!//
Xs!^!/!
Xs!$! /s/$/ n/!
X' $LIBDIR/ngfile
X	echo '/ n$/!s/$/ y/') >/tmp/$$sed
X	mv $LIBDIR/active $LIBDIR/oactive
X	sed -f /tmp/$$sed $LIBDIR/oactive >$LIBDIR/active
X	chown $NEWSUSR $LIBDIR/active
X	chgrp $NEWSGRP $LIBDIR/active
X	chmod 644 $LIBDIR/active
Xfi
Xsort /tmp/$$groups | $LIBDIR/checkgroups | tee /tmp/checkgroups.out
Xecho the output of checkgroups has been copied into /tmp/checkgroups.out
Xrm -f /tmp/$$*
END_OF_FILE
if test 15147 -ne `wc -c <'makeactive.sh'`; then
    echo shar: \"'makeactive.sh'\" unpacked with wrong size!
fi
# end of 'makeactive.sh'
fi
if test -f 'funcs2.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'funcs2.c'\"
else
echo shar: Extracting \"'funcs2.c'\" \(14115 characters\)
sed "s/^X//" >'funcs2.c' <<'END_OF_FILE'
X/*
X * This software is Copyright (c) 1985 by Rick Adams.
X *
X * Permission is hereby granted to copy, reproduce, redistribute or
X * otherwise use this software as long as: there is no monetary
X * profit gained specifically from the use or reproduction or this
X * software, it is not sold, rented, traded or otherwise marketed, and
X * this copyright notice is included prominently in any copy
X * made.
X *
X * The author make no claims as to the fitness or correctness of
X * this software for any use whatsoever, and it is provided as is. 
X * Any use of this software is at the user's own risk.
X *
X *
X * funcs2 - functions used by both inews and readnews.
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)funcs2.c	1.20	3/20/87";
X#endif /* SCCSID */
X
X#include "params.h"
X
X#ifdef SunIII
X#ifndef INTERNET
X#define	INTERNET
X#endif /* !INTERNET */
X#endif /* SunIII */
X
X/*LINTLIBRARY*/
X
X/*
X * Get user name and home directory.
X */
Xgetuser()
X{
X	static int flag = TRUE;
X	register struct passwd *p;
X
X	if (flag) {
X		if ((p = getpwuid(uid)) == NULL)
X			xerror("Cannot get user's name");
X		if ( username == NULL || username[0] == 0)
X			username = AllocCpy(p->pw_name);
X		userhome = AllocCpy(p->pw_dir);
X		flag = FALSE;
X	}
X	(void) strcpy(header.path, username);
X}
X
Xstatic	FILE	*sysfile;
X
Xchar *fldget();
X
Xstatic int sfline;
X
X/*
X * Open SUBFILE.
X */
Xs_openr()
X{
X	fprintf(stderr,"Opening subfile, %s\n",SUBFILE);
X	sysfile = xfopen(SUBFILE, "r");
X	sfline = 0;
X}
X
X/*
X * Read SUBFILE.
X */
Xs_read(sp)
Xregister struct srec *sp;
X{
X	register char *p;
X	register int  c;
X	char *e;
X	int chop_spaces = 0;
Xagain:
X	p = bfr;
X        /*
X         * Read  the  SUBFILE  (/usr/lib/news/sys)  from   the   current
X	 * position  to  the  first  unescaped newline.  If a newline is
X	 * escaped with a backslash (\) continue reading but throw  away
X	 * the backslash and newline; read the next line skipping spaces
X	 * and tabs until the first non-space/tab character, then  start
X	 * looking   for   a   newline   again.   Skipping  the  leading
X	 * spaces/tabs after a escaped newline  keeps  the  news  groups
X	 * together.  If  a  line  begins  with a newline, just skip it.
X	 */
X	for (e=p+LBUFLEN; p < e && (c=getc(sysfile)) != EOF; p++) {
X		*p = c;
X		if (c == '\n') {
X			sfline++;
X			if (p == bfr || p[-1] != '\\') {
X				p[1] = '\0';
X				break;
X			} else {
X				chop_spaces++;
X				p -= 2;
X			}
X		} else if (chop_spaces) {
X			if (c == '\t' || c == ' ')
X				p--;
X			else
X				chop_spaces = 0;
X		}
X	}
X	if (c == EOF) {
X		return FALSE;
X	}
X	p = bfr;
X	while (*p == ' ' || *p == '\t') /* skip leading white space */
X		p++;
X	if (*p == '\n')
X		goto again;	     /* skip newlines */
X	if (!nstrip(p))
X		xerror("SUBFILE (%s) line %d too long.", SUBFILE, sfline);
X	if (*p == '#')
X		goto again;
X	sp->s_xmit[0] = '\0';
X	sp->s_flags[0] = '\0';
X	sp->s_nosend = (char *)0;
X
X	p = fldget(sp->s_name, p);
X	if (*p++ == '\0')
X		xerror("Bad SUBFILE (%s) line %d.", SUBFILE, sfline);
X	/*
X	 * A sys file line reading "ME" means the name of the local system.
X	 */
X	if (strcmp(sp->s_name, "ME") == 0)
X		(void) strcpy(sp->s_name, LOCALPATHSYSNAME);
X	e = index(sp->s_name, '/');
X	if (e) {
X		*e++ = '\0';
X		sp->s_nosend = e;
X	}
X	p = fldget(sp->s_nbuf, p);
X	lcase(sp->s_nbuf);
X	if (*p++ == '\0')
X		return TRUE;
X
X	p = fldget(sp->s_flags, p);
X	if (*p++ == '\0')
X		return TRUE;
X
X	(void) fldget(sp->s_xmit, p);
X	return TRUE;
X}
X
Xchar *
Xfldget(q, p)
Xregister char *q, *p;
X{
X	while (*p && *p != ':') {
X		if (*p == '\\' && p[1]==':')
X			p++;
X		*q++ = *p++;
X	}
X	*q = '\0';
X	return p;
X}
X
X/*
X * Find the SUBFILE record for a system.
X */
Xs_find(sp, system)
Xregister struct srec *sp;
Xchar *system;
X{
X	s_openr();
X	while (s_read(sp))
X		if (strncmp(system, sp->s_name, SNLN) == 0) {
X			s_close();
X			return TRUE;
X		}
X	s_close();
X	return FALSE;
X}
X
X/*
X * Close sysfile.
X */
Xs_close()
X{
X	(void) fclose(sysfile);
X}
X
Xextern struct timeb Now;
X
Xtime_t
Xcgtdate(datestr)
Xchar *datestr;
X{
X	char	junk[40],month[40],day[30],tod[60],year[50];
X	static time_t lasttime;
X	static char lastdatestr[BUFLEN] = "";
X
X	if ( lastdatestr[0] && strcmp(datestr, lastdatestr) == 0)
X		return lasttime;
X	lasttime = getdate(datestr, &Now);
X	if (lasttime < 0 &&
X	  sscanf(datestr, "%s %s %s %s %s", junk, month, day, tod, year) == 5) {
X		(void) sprintf(bfr, "%s %s, %s %s", month, day, year, tod);
X		lasttime = getdate(bfr, &Now);
X	}
X	strncpy(lastdatestr, datestr, BUFLEN);
X	return lasttime;
X}
X
Xlcase(s)
Xregister char *s;
X{
X	register char *ptr;
X
X	for (ptr = s; *ptr; ptr++)
X		if (isupper(*ptr))
X			*ptr = tolower(*ptr);
X}
X
X/*
X * Return a compact representation of the person who posted the given
X * message.  A sender or internet name will be used, otherwise
X * the last part of the path is used preceded by an optional ".."
X */
Xchar *
Xtailpath(hp)
Xstruct hbuf *hp;
X{
X	char *p, *r;
X	static char resultbuf[BUFLEN];
X	char pathbuf[PATHLEN];
X	char *malloc();
X
X	/*
X	 * This only happens for articles posted by old news software
X	 * in non-internet format.
X	 */
X	resultbuf[0] = '\0';
X	(void) strncpy(pathbuf, hp->path, PATHLEN);
X	p = index(pathbuf, ' ');
X	if (p)
X		*p = '\0';	/* Chop off trailing " (name)" */
X	r = rindex(pathbuf, '!');
X	if (r == 0) {
X		r = pathbuf;
X	} else {
X		while (r > pathbuf && *--r != '!')
X			;
X		if (r > pathbuf) {
X			r++;
X			(void) strcpy(resultbuf, "..!");
X		}
X	}
X	(void) strcat(resultbuf, r);
X	return resultbuf;
X}
X
X/*
X * arpadate is like ctime(3) except that the time is returned in
X * an acceptable ARPANET time format instead of ctime format.
X */
Xchar *
Xarpadate(longtime)
Xtime_t *longtime;
X{
X	register char *p, *q, *ud;
X	register int i;
X	static char b[40];
X	extern struct tm *gmtime();
X
X#ifndef	MINIX
X	extern char *asctime();
X
X	/*  Get current time. This will be used resolve the timezone. */
X	ud = asctime(gmtime(longtime));
X#else
X	/*  Get current time. This will be used resolve the timezone. */
X	ud = ctime(longtime);
X#endif
X	
X	/*  Crack the UNIX date line in a singularly unoriginal way. */
X	q = b;
X
X#ifdef notdef
X/* until every site installs the fix to getdate.y, the day
X   of the week can cause time warps */
X	p = &ud[0];		/* Mon */
X	*q++ = *p++;
X	*q++ = *p++;
X	*q++ = *p++;
X	*q++ = ','; *q++ = ' ';
X#endif
X
X	p = &ud[8];		/* 16 */
X	if (*p == ' ')
X		p++;
X	else
X		*q++ = *p++;
X	*q++ = *p++; *q++ = ' ';
X
X	p = &ud[4];		/* Sep */
X	*q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = ' ';
X
X	p = &ud[22];		/* 1979 */
X	*q++ = *p++; *q++ = *p++; *q++ = ' ';
X
X	p = &ud[11];		/* 01:03:52 */
X	for (i = 8; i > 0; i--)
X		*q++ = *p++;
X
X	*q++ = ' ';
X	*q++ = 'G';		/* GMT */
X	*q++ = 'M';
X	*q++ = 'T';
X	*q = '\0';
X
X	fprintf(stderr,"arpadate 2 asctime: %s\n",b);
X	return b;
X}
X
Xchar *
Xreplyname(hptr)
Xstruct hbuf *hptr;
X{
X	register char *ptr;
X	static char tbuf[PATHLEN];
X
X	ptr = hptr->path;
X	if (prefix(ptr, PATHSYSNAME) &&
X		index(NETCHRS, ptr[strlen(PATHSYSNAME)]))
X		ptr = index(ptr, '!') + 1;
X#ifdef INTERNET
X	if (hptr->from[0])
X		ptr = hptr->from;
X	if (hptr->replyto[0])
X		ptr = hptr->replyto;
X#endif
X	(void) strcpy(tbuf, ptr);
X	ptr = index(tbuf, '(');
X	if (ptr) {
X		while (ptr[-1] == ' ')
X			ptr--;
X		*ptr = 0;
X	}
X#ifdef	SunIII
X	if (ptr = rindex(tbuf, '.')) {
X		if (prefix(++ptr, "OZ")) {
X			/* some people only allow it in lower case ... */
X			strcpy(ptr, "oz");
X			return tbuf;
X		}
X		if (prefix(ptr, "UUCP") || prefix(ptr, "ARPA") ||
X		    prefix(ptr, "DEC") || prefix(ptr, "CSNET")) {
X			strcat(tbuf, "@munnari.oz");	/* via sun to munnari */
X			return tbuf;
X		}
X	}
X	/*
X	 * must(?) have come from a uucp site, lets look see if path passes
X	 * through munnari, and if so delete the fake uucp path after that.
X	 */
X	for (ptr = tbuf ;; ptr++) {
X		if (prefix(ptr, "munnari!")) {
X			strcpy(tbuf, ptr+8);
X			break;
X		}
X		ptr = index(ptr, '!');
X		if (ptr == (char *)0)
X			break;
X	}
X	/*
X	 * now, just send the address we have left to munnari, and
X	 * hope that something sensible will be done with it there.
X	 * (This works in more cases than you'd think ...)
X	 */
X	strcat(tbuf, "@munnari.oz");
X#else /* !SunIII */
X#ifndef INTERNET
X	/*
X	 * Play games stripping off multiple berknet
X	 * addresses (a!b!c:d:e => a!b!d:e) here.
X	 */
X	for (ptr=tbuf; *ptr; ptr++) {
X		register char *ptr2;
X
X		if (index(NETCHRS, *ptr) && *ptr == ':' &&
X		    (ptr2=index(ptr+1, ':')))
X			(void) strcpy(ptr, ptr2);
X	}
X#else	/* INTERNET */
X	{
X	char mbuf[BUFLEN], modadd[BUFLEN];
X	FILE *mfd;
X	/* Let's find a path to the backbone */
X	sprintf(mbuf, "%s/mailpaths", LIB);
X	mfd = xfopen(mbuf, "r");
X	do {
X		if (fgets(mbuf, sizeof mbuf, mfd) == NULL)
X			xerror("Can't find internet in %s/mailpaths",
X				LIB);
X	} while (!prefix(mbuf, "internet"));
X	if (sscanf(mbuf, "%*s %s", modadd) != 1)
X		xerror("backbone address corrupted");
X	(void) fclose(mfd);
X	(void)strcpy(mbuf, tbuf);
X	/* If we are lucky, there is no ! or @ in the forward address */
X	if (strpbrk(modadd, "!@") == NULL) {
X		sprintf(tbuf, modadd, mbuf);
X	} else {
X		char *cp = index(mbuf, '@');
X		if (index(modadd, '@') == NULL && cp) {
X			/* we have to rearrange the address so no @ are in it */
X			char atbuf[BUFLEN];
X			*cp++ = '\0';
X			sprintf(atbuf, "%s!%s", cp, mbuf);
X			sprintf(tbuf, modadd, atbuf);
X		} else if (cp) {
X			/* some days you don't get lucky. presume the % hack */
X			*cp = '%';
X			sprintf(tbuf, modadd, mbuf);
X		}
X	}
X	}
X#endif /* INTERNET */
X#endif /* !SunIII */
X	return tbuf;
X}
X
X#ifdef DBM
Xtypedef struct {
X	char *dptr;
X	int dsize;
X} datum;
X#endif /* DBM */
X
X/*
X * Given an article ID, find the line in the history file that mentions it.
X * Return the text of the line, or NULL if not found.  A pointer to a
X * static area is returned.
X */
Xchar *
Xfindhist(artid)
Xchar *artid;
X{
X	static char lbuf[256];
X	char oidbuf[BUFSIZ];
X	FILE *hfp;
X	register char *p;
X#ifdef DBM
X	datum lhs, rhs;
X	datum fetch();
X	long fpos; /* We have to use an explicit variable to insure alignment */
X#else /* !DBM */
X	char *histfile();
X#endif /* !DBM */
X
X	/* Try to understand old artid's as well.  Assume .UUCP domain. */
X	if (artid[0] != '<') {
X		p = index(artid, '.');
X		if (p)
X			*p++ = '\0';
X		(void) sprintf(oidbuf, "<%s@%s.UUCP>", p, artid);
X		if (p)
X			*--p = '.';
X	} else
X		(void) strcpy(oidbuf, artid);
X	lcase(oidbuf);
X#ifdef DBM
X	initdbm(ARTFILE);
X	lhs.dptr = oidbuf;
X	lhs.dsize = strlen(lhs.dptr) + 1;
X	rhs = fetch(lhs);
X	if (rhs.dptr == NULL)
X		return NULL;
X	hfp = xfopen(ARTFILE, "r");
X	/* The bcopy is NECESSARY to insure alignment on some machines */
X	xbcopy(rhs.dptr, (char *)&fpos, sizeof (long));
X	fseek(hfp, fpos, 0);
X#else /* !DBM */
X	hfp = xfopen(histfile(oidbuf), "r");
X#endif /* !DBM */
X	while (fgets(lbuf, BUFLEN, hfp) != NULL) {
X		p = index(lbuf, '\t');
X		if (p == NULL)
X			p = index(lbuf, '\n');
X		*p = 0;
X		if (strcmp(lbuf, artid) == 0 || strcmp(lbuf, oidbuf) == 0) {
X			(void) fclose(hfp);
X			*p = '\t';
X			*(lbuf + strlen(lbuf) - 1) = 0;	/* zap the \n */
X			return lbuf;
X		}
X#ifdef DBM
X		break;
X#endif /* DBM */
X	}
X	(void) fclose(hfp);
X	return NULL;
X}
X
X/*
X * Hunt up the article "artid", and return the newsgroup/artnum
X * where it can be found.
X */
Xchar *
Xfindfname(artid)
Xchar *artid;
X{
X	char *line, *p, *q;
X	char *findhist();
X	static char fname[BUFLEN];
X
X	line = findhist(artid);
X	if (line) {
X		/* Look for it stored as an article, where it should be */
X		p = index(line, '\t');
X		p = index(p+1, '\t');
X		p++;
X		if (*p) {
X			q = index(p, ' ');
X			if (q)
X				*q = 0;
X			(void) strcpy(fname, p);
X			return fname;
X		}
X	}
X	return NULL;
X}
X
X/*
X * Hunt up the article "artid", fopen it for read, and return a
X * file descriptor to it.  We look everywhere we can think of.
X */
XFILE *
Xhfopen(artid)
Xchar *artid;
X{
X	char *p;
X	char *findhist();
X	FILE *rv = NULL;
X	char fname[BUFLEN];
X
X	p = findfname(artid);
X	if (p) {
X		(void) strcpy(fname, dirname(p));
X		rv = fopen(fname, "r");	/* NOT xfopen! */
X		if (rv == NULL)
X			xerror("Cannot hfopen article %s", artid);
X	}
X	return rv;
X}
X
X#ifdef DBM
X/*
X** Avoid problems of multiple dbminit calls.
X*/
Xinitdbm(name)
Xchar *name;
X{
X	static int called = 0;
X
X	if (called != 0)
X		return;
X	called = 1;
X	(void) dbminit(name);
X}
X#endif
X
X/*
X * move n bytes from a to b
X */
Xxbcopy(a, b, n)
Xregister char *a, *b;
Xregister n;
X{
X	while (--n >= 0)
X		*b++ = *a++;
X}
X
X#if !defined(BSD4_2) && !defined(BSD4_1C)
Xrename(from,to)
Xregister char *from, *to;
X{
X	(void) unlink(to);
X	if (link(from, to) < 0)
X		return -1;
X
X	(void) unlink(from);
X	return 0;
X}
X#endif /* !BSD4_2 && ! BSD4_1C */
X
X#ifndef DBM
X/*
X** Generate the appropriate history subfile name
X*/
Xchar *
Xhistfile(hline)
Xchar *hline;
X{
X	char chr;	/* least significant digit of article number */
X	static char subfile[BUFLEN];
X
X	chr = findhfdigit(hline);
X	sprintf(subfile, "%s.d/%c", ARTFILE, chr);
X	return subfile;
X}
X
Xfindhfdigit(fn)
Xchar *fn;
X{
X	register char *p;
X	register int chr;
X
X	p = index(fn, '@');
X	if (p != NULL && p > fn)
X		chr = *(p - 1);
X	else
X		chr = '0';
X	if (!isdigit(chr))
X		chr = '0';
X	return chr;
X}
X#endif /* !DBM */
X
X#ifdef VMS
X/*
X * These functions open an article with one level of indirection,
X * to support symbolic links. xart_open exits if the open fails.
X */
XFILE *
Xxart_open (filename,mode)
Xchar *filename,*mode;
X{
X	FILE *fp = art_open (filename, mode);
X	extern int errno;
X	if (fp == NULL)
X		xerror("Cannot open article %s (%s): %s\n",
X			 filename, mode, errmsg(errno));
X	return fp;
X}
X
XFILE *
Xart_open (filename,mode)
Xchar *filename,*mode;
X{
X	char linkfile[BUFSIZ];
X	FILE *fp;
X
X	if ((fp = fopen (filename, mode)) == NULL)
X		return NULL;
X	if (fgets (linkfile, BUFSIZ, fp) == NULL || linkfile[0] != '/') {
X		rewind (fp);
X		return fp;
X	}
X/* Chase the symbolic link. */
X	(void) fclose (fp);
X	if ((fp = fopen (linkfile, mode)) == NULL)
X/* Clean up dangling link, if we have the power. Ignore error if we don't. */
X		(void) unlink (filename);
X	return fp;
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	register char *r, *q, *tp;
X	char *tailpath();
X	static char senderbuf[BUFLEN];
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	(void) strncpy(senderbuf, tp, BUFLEN);
X	/* Remove full name */
X	q = index(senderbuf, ' ');
X	if (q)
X		*q = '\0';
X
X	return senderbuf;
X}
END_OF_FILE
if test 14115 -ne `wc -c <'funcs2.c'`; then
    echo shar: \"'funcs2.c'\" unpacked with wrong size!
fi
# end of 'funcs2.c'
fi
if test -f 'getdate.y' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'getdate.y'\"
else
echo shar: Extracting \"'getdate.y'\" \(12591 characters\)
sed "s/^X//" >'getdate.y' <<'END_OF_FILE'
X%token ID MONTH DAY MERIDIAN NUMBER UNIT MUNIT SUNIT ZONE DAYZONE AGO
X%{
X	/* 	Originally from: Steven M. Bellovin (unc!smb)	*/ 
X	/*	Dept. of Computer Science			*/
X	/*	University of North Carolina at Chapel Hill	*/
X	/*	@(#)getdate.y	2.15	12/16/86	*/
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
X#include "defs.h"
X#if defined(BSD4_2) || defined (BSD4_1C)
X#include <sys/time.h>
X#else /* sane */
X#include <time.h>
X#endif /* sane */
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; 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();
X
Xtime_t
Xdateconv(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
Xdayconv(ord, day, now)
Xint 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
Xtimeconv(hh, mm, ss, mer)
Xregister 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
Xmonthadd(sdate, relmonth)
Xtime_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
Xdaylcorr(future, now)
Xtime_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))
X			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++))
X				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
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)
Xchar *id;
X{
X#define gotit (yylval=i->value,  i->type)
X
X	char idvar[128];
X	register char *j, *k;
X	register struct table *i;
X	int abbrev;
X
X	(void) strcpy(idvar, id);
X	j = idvar;
X	k = id - 1;
X	while (*++k)
X		*j++ = isupper(*k) ? tolower(*k) : *k;
X	*j = '\0';
X
X	if (strlen(idvar) == 3)
X		abbrev = 1;
X	else
X		if (strlen(idvar) == 4 && idvar[3] == '.') {
X			abbrev = 1;
X			idvar[3] = '\0';
X		}
X	else
X		abbrev = 0;
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)
X				return gotit;
X			if (j[-1] == 0)
X				return gotit;
X		}
X	}
X
X	for (i = mztab; i->name; i++)
X		if (strcmp(i->name, idvar) == 0)
X			return gotit;
X
X	for (i=mztab; i->name; i++)
X		if (strcmp(i->name, idvar) == 0)
X			return gotit;
X
X	for (i=unittb; i->name; i++)
X		if (strcmp(i->name, idvar) == 0)
X			return gotit;
X
X	if (idvar[strlen(idvar)-1] == 's')
X		idvar[strlen(idvar)-1] = '\0';
X
X	for (i=unittb; i->name; i++)
X		if (strcmp(i->name, idvar) == 0)
X			return gotit;
X
X	for (i = othertb; i->name; i++)
X		if (strcmp(i->name, idvar) == 0)
X			return gotit;
X
X	if (strlen(idvar) == 1 && isalpha(*idvar)) {
X		for (i = milzone; i->name; i++)
X			if (strcmp(i->name, idvar) == 0)
X				return gotit;
X	}
X
X	return ID;
X}
X
Xtime_t
Xgetdate(p, now)
Xchar *p;
Xstruct 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{}
END_OF_FILE
if test 12591 -ne `wc -c <'getdate.y'`; then
    echo shar: \"'getdate.y'\" unpacked with wrong size!
fi
# end of 'getdate.y'
fi
if test -f 'funcs.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'funcs.c'\"
else
echo shar: Extracting \"'funcs.c'\" \(12952 characters\)
sed "s/^X//" >'funcs.c' <<'END_OF_FILE'
X/*
X * This software is Copyright (c) 1986 by Rick Adams.
X *
X * Permission is hereby granted to copy, reproduce, redistribute or
X * otherwise use this software as long as: there is no monetary
X * profit gained specifically from the use or reproduction or this
X * software, it is not sold, rented, traded or otherwise marketed, and
X * this copyright notice is included prominently in any copy
X * made.
X *
X * The author make no claims as to the fitness or correctness of
X * this software for any use whatsoever, and it is provided as is. 
X * Any use of this software is at the user's own risk.
X *
X * funcs - functions used by many programs
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)funcs.c	2.35	12/16/86";
X#endif /* SCCSID */
X
X/*LINTLIBRARY*/
X
X#include "params.h"
X#include <errno.h>
X#if defined(USG) || defined(BSD4_2) || defined(BSD4_1C)
X#include <fcntl.h>
X#endif /* !v7 */
X
Xextern char *Progname;
X
X/*
X * News group matching.
X *
X * nglist is a list of newsgroups.
X * sublist is a list of subscriptions.
X * sublist may have "meta newsgroups" in it.
X * All fields are NGDELIM separated,
X * and there is an NGDELIM at the end of each argument.
X *
X * Currently implemented glitches:
X * sublist uses 'all' like shell uses '*', and '.' like shell '/'.
X * If subscription X matches Y, it also matches Y.anything.
X */
Xngmatch(nglist, sublist)
Xregister char *nglist, *sublist;
X{
X	register char *n, *s;
X	register int rc;
X
X	rc = FALSE;
X	for (n = nglist; *n != '\0' && rc == FALSE;) {
X		for (s = sublist; *s != '\0';) {
X			if (*s != NEGCHAR)
X				rc = rc || ptrncmp(s, n);
X			else
X				rc = rc && !ptrncmp(s+1, n);
X			while (*s++ != NGDELIM && *s != '\0')
X				;
X		}
X		while (*n++ != NGDELIM && *n != '\0')
X			;
X	}
X	return rc;
X}
X
X/*
X * Compare two newsgroups for equality.
X * The first one may be a "meta" newsgroup.
X */
Xptrncmp(ng1, ng2)
Xregister char *ng1, *ng2;
X{
X	while (*ng1 != NGDELIM && *ng1 != '\0') {
X		if (ng1[0]=='a' && ng1[1]=='l' && ng1[2]=='l') {
X			ng1 += 3;
X			while (*ng2 != NGDELIM && *ng2 != '.' && *ng2 != '\0')
X				if (ptrncmp(ng1, ng2++))
X					return(TRUE);
X			return ptrncmp(ng1, ng2);
X		} else if (*ng1++ != *ng2++)
X			return FALSE;
X	}
X	return *ng2 == '.' || *ng2 == NGDELIM || *ng2 == '\0';
X}
X
X/*
X * Exec the shell.
X * This version resets uid, gid, and umask.
X * Called with fsubr(ushell, s, NULL)
X */
X/* ARGSUSED */
Xushell(s, dummy)
Xchar *s, *dummy;
X{
X	(void) umask(savmask);
X	(void) setgid(gid);
X	(void) setuid(uid);
X	xshell(s);
X}
X
X/*
X * Exec the shell.
X */
X
X#ifdef lint
Xchar	**environ;
X#else /* !lint */
Xextern char	**environ;
X#endif /* !lint */
X
Xxshell(s)
Xchar *s;
X{
X	char *env[100], **envp;
X	char a[BUFLEN + 2];
X	extern char filename[];
X	/* set $A */
X	(void) sprintf(a, "A=%s", filename);
X	env[0] = a;
X	for (envp = env + 1 ; *environ != NULL && envp < env + 98 ; environ++)
X		if ((*environ)[0] != 'A' || (*environ)[1] != '=')
X			*envp++ = *environ;
X	*envp = NULL;
X
X	execle(SHELL, SHELL, "-c", s, (char *)0, env);
X	xerror("No shell!");
X}
X
X/*
X * Fork and call a subroutine with two args.
X * Return pid without waiting.
X */
Xfsubr(f, s1, s2)
Xint (*f)();
Xchar *s1, *s2;
X{
X	register int pid;
X
X	/* this may NOT be a vfork */
X	while ((pid = fork()) == -1)
X		sleep((unsigned)1);
X	if (pid == 0) {
X		(*f)(s1, s2);
X		exit(0);
X	}
X	return pid;
X}
X
X/*
X * Wait on a child process.
X */
Xfwait(pid)
Xregister int pid;
X{
X	register int w;
X	int status;
X	int (*onhup)(), (*onint)();
X
X	onint = signal(SIGINT, SIG_IGN);
X	onhup = signal(SIGHUP, SIG_IGN);
X	while ((w = wait(&status)) != pid && w != -1)
X		;
X	if (w == -1)
X		status = -1;
X	(void) signal(SIGINT, onint);
X	(void) signal(SIGHUP, onhup);
X	return status;
X}
X
X/*
X * Strip trailing newlines, blanks, and tabs from 's'.
X * Return TRUE if newline was found, else FALSE.
X */
Xnstrip(s)
Xregister char *s;
X{
X	register char *p;
X	register int rc;
X
X	rc = FALSE;
X	p = s;
X	while (*p)
X		if (*p++ == '\n')
X			rc = TRUE;
X	while (--p >= s && (*p == '\n' || *p == ' ' || *p == '\t'));
X	*++p = '\0';
X	return rc;
X}
X
X/*
X * Local open routine.
X */
XFILE *
Xxfopen(name, fmode)
Xregister char *name, *fmode;
X{
X	register FILE *fp;
X	char	*fname;
X	extern int errno;
X
X	if (!strcmp(fmode,"")) {
X		fprintf(stderr,"Opening %s, mode %s\n",name,fmode);
X		fmode="r";
X	}
X
X	if ((fp = fopen(name, fmode)) == NULL) {
X#ifdef IHCC
X		/*
X		 * IHCC users only see the "filename" that was in trouble,
X		 * not the whole path.  (for security!)
X		 */
X		fname = rindex(name, '/') + 1;
X#else
X		fname = name;
X#endif
X		xerror("Cannot open %s (%s): %s", fname, fmode, errmsg(errno));
X	}
X	/* kludge for setuid not being honored for root */
X	if ((uid == 0) && (duid != 0) && ((*fmode == 'a') || (*fmode == 'w')))
X		(void) chown(name, duid, dgid);
X	return fp;
X}
X
Xchar *
Xerrmsg(code)
Xint code;
X{
X	extern int sys_nerr;
X	extern char *sys_errlist[];
X	static char ebuf[6+5+1];
X
X	if (code > sys_nerr) {
X		(void) sprintf(ebuf, "Error %d", code);
X		return ebuf;
X	} else
X		return sys_errlist[code];
X}
X
Xprefix(full, pref)
Xregister char *full, *pref;
X{
X	register char fc, pc;
X
X	while ((pc = *pref++) != '\0') {
X		fc = *full++;
X		if (isupper(fc))
X			fc = tolower(fc);
X		if (isupper(pc))
X			pc = tolower(pc);
X		if (fc != pc)
X			return FALSE;
X	}
X	return TRUE;
X}
X
Xchar *
Xdirname(ngname)
Xchar *ngname;
X{
X	static char rbuf[BUFLEN];
X	register char *p;
X
X	(void) sprintf(rbuf, "%s/%s", SPOOL, ngname);
X
X	for (p=rbuf+strlen(SPOOL); *p; p++)
X		if (*p == '.')
X			*p = '/';
X	return rbuf;
X}
X
X/*
X * Return TRUE iff ngname is a valid newsgroup name
X */
Xvalidng(ngname)
Xchar *ngname;
X{
X	register FILE *fp;
X	register char *p, *q;
X	char abuf[BUFLEN];
X
X	fp = xfopen(ACTIVE, "r");
X	while(fgets(abuf, BUFLEN, fp) != NULL) {
X		p = abuf;
X		q = ngname;
X		while (*p++ == *q++)
X			;
X		if (*--q == '\0' && *--p == ' ') {
X			(void) fclose(fp);
X			return TRUE;
X		}
X	}
X	(void) fclose(fp);
X	return FALSE;
X}
X
X/* VARARGS1 */
Xxerror(message, arg1, arg2, arg3)
Xchar *message;
Xlong arg1, arg2, arg3;
X{
X	char buffer[LBUFLEN];
X
X	fflush(stdout);
X	(void) sprintf(buffer, message, arg1, arg2, arg3);
X	logerr(buffer);
X	xxit(1);
X}
X
X/* VARARGS1 */
Xlog(fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9)
Xchar *fmt;
Xlong a1, a2, a3, a4, a5, a6, a7, a8, a9;
X{
X	_dolog(0, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9);
X}
X
X/* VARARGS1 */
Xlogerr(fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9)
Xchar *fmt;
Xlong a1, a2, a3, a4, a5, a6, a7, a8, a9;
X{
X	_dolog(1, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9);
X}
X
Xchar *lfsuffix[] = {
X	"log",
X	"errlog",
X	NULL,
X};
X
X/*
X * Log the given message, with printf strings and parameters allowed,
X * on the log file, if it can be written.  The date and an attempt at
X * figuring out the remote system name are also logged.
X */
X/* VARARGS1 */
X_dolog(which, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9)
Xchar *fmt;
Xlong a1, a2, a3, a4, a5, a6, a7, a8, a9;
X{
X	FILE *logfile;
X	register char *p, *logtime;
X	int i;
X	char logfname[BUFLEN];		/* the log file */
X	char rmtsys[BUFLEN];
X	char msg[LBUFLEN];
X	time_t t;
X
X	(void) strcpy(rmtsys, header.path);
X	p = index(rmtsys, '!');
X	if (p == NULL)
X		p = index(rmtsys, ':');
X	if (p)
X		*p = 0;
X	else {
X		p = rindex(rmtsys, '@');
X		if (p)
X			(void) strcpy(rmtsys, p+1);
X		else
X			(void) strcpy(rmtsys, "local");
X	}
X
X	(void) time(&t);
X	logtime = ctime(&t);
X	logtime[16] = 0;
X	logtime += 4;
X
X
X	(void) sprintf(msg, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9);
X
X	if (which)
X		fprintf(stderr,"%s: %s\n", Progname, msg);
X
X	for (i=0; i<=which;i++) {
X		(void) sprintf(logfname, "%s/%s", LIB, lfsuffix[i]);
X
X		if (access(logfname, 0) == 0 && (logfile = fopen(logfname, "a")) != NULL) {
X#if defined(USG) || defined(BSD4_2) || defined(BSD4_1C)
X			int flags;
X			flags = fcntl(fileno(logfile), F_GETFL, 0);
X			(void) fcntl(fileno(logfile), F_SETFL, flags|O_APPEND);
X#else /* v7 */
X			(void) lseek(fileno(logfile), 0L, 2);
X#endif /* v7 */
X			if (i)
X				fprintf(logfile, "%s\t%s\t%s: %s\n", logtime,
X					header.ident[0] ? header.ident : username, Progname, msg);
X			else
X				fprintf(logfile, "%s\t%s\t%s\n", logtime,
X					rmtsys, msg);
X			(void) fclose(logfile);
X		}
X	}
X}
X#ifdef VMS
X
X/*
X * vmslink allows simulation of file linking under VMS.
X */
Xvmslink(infile,outfile)
Xchar *infile, *outfile;
X{
X	FILE *fp;
X
X	if (access(outfile,0) == 0) {
X		errno = EEXIST;
X		return -1;
X	}
X
X	fp = fopen(outfile, "w");
X	if (fp == NULL) {
X		errno = EACCES;
X		return -1;
X	}
X
X	(void) fprintf(fp, "%s", infile);
X	(void) fclose(fp);
X
X	return 0;
X}
X
X/*
X * vmsdelete deletes all revisions of a file.  It attempts to
X * appear as unlink(2) under conventional Unix in other respects.
X */
Xvmsdelete(file)
Xchar *file;
X{
X	int i;
X
X	i = unlink(file);
X	if (i != 0)
X		return i;
X
X	i = errno;
X	while (unlink(file) == 0)
X		;
X	errno = i;
X
X	return 0;
X}
X
X/*
X * Convert a Unix file to a VMS fixed record format file by
X * executing the 'unixtovms' command.
X */
Xunixtovms(file)
Xchar *file;
X{
X	char buf[BUFLEN];
X	sprintf(buf, "exec /etc/unixtovms %s", file);
X	return system(buf);
X}
X
X/*
X * Convert a VMS fixed record format file to a Unix file by
X * executing the 'vmstounix' command.
X */
Xvmstounix(file)
Xchar *file;
X{
X	char buf[BUFLEN];
X	sprintf(buf,"exec /etc/vmstounix %s", file);
X	return system(buf);
X}
X#endif /* VMS */
X
X#if !defined(BSD4_2) && !defined(BSD4_1C)
X/*
X * make a directory. Also make sure that the directory is owned
X * by the right userid
X */
Xmkdir(path, perm)
Xchar *path;
Xint perm;
X{
X	int pid, status;
X#ifdef USG
X	char parent[200];
X	char *p;
X	struct stat sbuf;
X
X	/*
X	 * Make parent directory writable, because we will
X	 * be creating a directory owned by the real user,
X	 * rather than by news.
X	 */
X	(void) strcpy(parent, path);
X	if (p = rindex(parent, '/')) {
X		*p = '\0';
X		if (stat(parent, &sbuf) == 0)
X			(void) chmod(parent, 0777);
X		else
X			return -1;
X	} else
X		return -1;
X#endif
X
X	if (pid=vfork()) {
X		status = fwait(pid);
X#if defined(USG) && !defined(CHEAP)
X		if (pid=vfork())
X			(void) fwait(pid);
X		else {
X			setgid(gid);
X			setuid(uid);
X			if (chown(path, duid, dgid) == 0)
X				(void) chmod(path, perm&(~N_UMASK));
X			_exit(0);
X		}
X#endif /* USG && !CHEAP */
X	} else {
X		(void) setgid(dgid);
X		if (setuid(duid) < 0)
X			(void) umask(0);
X		else
X			(void) umask(perm&N_UMASK);
X		(void) execlp("mkdir", "mkdir", path, (char *)NULL);
X		perror(path);
X		_exit(1);
X	}
X#ifdef USG
X	(void) chmod(parent, sbuf.st_mode); /* Restore mode of parent */
X#endif
X	return status;
X}
X#endif /* !BSD4_2 && ! BSD4_1C */
X#ifndef USG
Xchar *
Xstrpbrk(str, chars)
Xregister char *str, *chars;
X{
X	register char *cp;
X
X	do {
X		cp = chars - 1;
X		while (*++cp) {
X			if (*str == *cp)
X				return str;
X		}
X	} while (*str++);
X	return NULL;
X}
X#endif /* !USG */
X
X#ifdef FASCIST
X/*
X *  This routine checks to see if the posting user is allowed to
X *  post to the given newsgroup.  If the username is not in the file
X *  $LIBDIR/authorized then the default in the symbol FASCIST is used.
X *
X *  Format of the call:
X *     fascist(user, newgroups)
X *
X *  Returns:
X *     FALSE, if authorized
X *     TRUE, if not
X *
X *  Format of the file "authorized" is:
X *    user:allowed groups  
X *
X *  Example:
X *    root:net.all,mod.all
X *    naughty_person:junk,net.politics
X *    operator:!net.all,general,test,mod.unix
X *
X *  An open environment could have FASCIST set to "all"
X *  and then individual entries could be made in the authorized file
X *  to prevent certain individuals from posting to such a wide
X *  area.
X *
X *  Note that a distribution of "all" does NOT mean to allow postings
X *  only to local groups -- "all" includes "all.all".  
X *  Use "all,!all.all" to get this behavior
X *
X *	Eugene Spafford		spaf@gatech	May 22, 1985
X */
X
Xfascist(user, newsgroups)
Xregister char *user, *newsgroups;
X{
X	FILE *facfd;
X	char facuser[BUFLEN], facgroups[BUFLEN], factemp[BUFLEN];
X	register char  *facptr;
X
X	/* First, open the necessary file...$LIBDIR/authorized and see if there
X	 * is an entry for this user 
X	 */
X
X	(void) strncpy(facgroups, FASCIST, BUFLEN);
X	sprintf(factemp, "%s/%s", LIB, "authorized");
X	facfd = fopen(factemp, "r");
X
X	if (facfd != NULL) { /* If no such file, we go with the global default */
X		while (fscanf(facfd, "%[^:]:%s\n", facuser, factemp) != EOF)
X			if (strncmp(facuser, user, BUFLEN) == 0) {
X				(void) strcat(facgroups, ",");
X				(void) strcat(facgroups, factemp);
X				break;
X			}
X		fclose (facfd);
X	}
X#ifdef DEBUG
X	fprintf(stderr, "facgroups = %s\n", facgroups);
X	fprintf(stderr, "newsgroups = %s\n", newsgroups);
X#endif /* DEBUG */
X
X	/* We step through the newsgroups being posted to and check each against
X	 * the restriction list.  *ALL* posted groups must match the restriction
X	 * list or we don't allow the posting.
X	 */
X
X	while (*newsgroups != '\0') {
X		facptr = factemp;
X		while (*newsgroups != '\0' && *newsgroups != NGDELIM)
X			*facptr++ = *newsgroups++;
X		*facptr = '\0';
X		if (*newsgroups == NGDELIM)
X			newsgroups++;
X
X#ifdef DEBUG
X		fprintf(stderr, "Checking newsgroup '%s'\n", factemp);
X#endif
X
X		if (ngmatch(factemp, facgroups) == FALSE)
X			return TRUE;
X	}
X
X	/* must be okay -- return */
X#ifdef DEBUG
X	fprintf (stderr, "Newsgroups approved for this poster.\n");
X#endif /* DEBUG */
X	return FALSE;
X}
X#endif /* FASCIST */
END_OF_FILE
if test 12952 -ne `wc -c <'funcs.c'`; then
    echo shar: \"'funcs.c'\" unpacked with wrong size!
fi
# end of 'funcs.c'
fi
echo shar: End of shell archive.
exit 0
-- 
		"Zeta Microcomputer Software"
ACSnet:    nick@ultima.cs.uts.oz
UUCP:      ...!uunet!munnari!ultima.cs.uts.oz!nick
Fidonet:   Nick Andrew on 3:713/602 (Zeta)

nick@ultima.cs.uts.oz (Nick Andrew) (12/07/89)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  rfuncs2.c pathinit.c checknews.c install.sh recnews.c
# Wrapped by nick@nswitgould on Thu Dec  7 22:41:34 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'rfuncs2.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rfuncs2.c'\"
else
echo shar: Extracting \"'rfuncs2.c'\" \(11135 characters\)
sed "s/^X//" >'rfuncs2.c' <<'END_OF_FILE'
X/*
X * This software is Copyright 1986 by Rick Adams.
X *
X * Permission is hereby granted to copy, reproduce, redistribute or
X * otherwise use this software as long as: there is no monetary
X * profit gained specifically from the use or reproduction or this
X * software, it is not sold, rented, traded or otherwise marketed, and
X * this copyright notice is included prominently in any copy
X * made.
X *
X * The author make no claims as to the fitness or correctness of
X * this software for any use whatsoever, and it is provided as is. 
X * Any use of this software is at the user's own risk.
X *
X * rfuncs2 - more routines needed by readr.
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)rfuncs2.c	1.35	3/21/87";
X#endif /* SCCSID */
X
X/*LINTLIBRARY*/
X
X#include "rparams.h"
X
Xstatic char	lbuf[LBUFLEN];
Xextern char *replyname();
X
XFILE *popen();
X
X/*
X * Match title.
X */
Xtitmat(h, titlist)
Xregister struct hbuf *h;
Xregister char	*titlist;
X{
X	register char	*p;
X
X	while (*titlist != '\0') {
X
X		if (strcmp(titlist, h->ident) == 0)
X			return TRUE;
X		for (p = h->title; *p != '\0'; p++)
X			if (prefix(p, titlist)) {
X				return TRUE;
X			}
X		while (*titlist++ != '\0')
X			;
X	}
X	return FALSE;
X}
X
X
X/*
X * Save the news item in the user's file.
X *	Allow files with first character as '|' to write article
X *	to program across a pipe.
X */
X
X#define PIPECHAR '|'
X
Xsave(file, to)
Xregister char	*file, *to;
X{
X	register FILE *ufp, *hfp;
X#ifdef u370
X	static struct hbuf hh;
X#else /* !u370 */
X	struct hbuf hh;
X#endif /* !u370 */
X	int	isprogram = 0;
X	int	isnew = 1;
X	register int i;
X
X	for(i=0;i<NUNREC;i++)
X		hh.unrec[i] = NULL;
X
X	if ((hfp = art_open(file, "r")) == NULL) {
X		fprintf(stderr, "Can't get article.\n");
X		return;
X	}
X	if (hread(&hh, hfp, TRUE) == NULL) {
X		fprintf(stderr, "Article is garbled.\n");
X		return;
X	}
X	ufp = fopen(to, "r");
X	if (ufp != NULL) {
X		(void) fclose(ufp);
X		isnew = 0;
X	}
X	(void) setgid(gid);
X	(void) setuid(uid);
X	(void) umask(savmask);
X
X	if (*to == PIPECHAR) {
X		if ((ufp = popen (&to[1], "w")) == NULL) {
X			fprintf(stderr,"Cannot execute %s\n", &to[1]);
X			return;
X		}
X		isprogram++;
X	} else if ((ufp = fopen(to, "a")) == NULL) {
X		fprintf(stderr,"Cannot append to %s.\n", to);
X		return;
X	}
X	/*
X	 * V7MAIL code is here to conform to V7 mail format.
X	 * If you need a different format to be able to
X	 * use your local mail command (such as four ^A's
X	 * on the end of articles) substitute it here.
X	 */
X#ifdef MMDF
X	if (!isprogram)
X		fprintf(ufp, "\001\001\001\001\n");  /* MMDF message header */
X#endif /* MMDF */
X#ifdef V7MAIL
X	hh.subtime = cgtdate(hh.subdate);
X	fprintf(ufp, "From %s %s", replyname(&hh), ctime(&hh.subtime));
X#endif
X	hprint(&hh, ufp, 2);
X#ifdef V7MAIL
X	tprint(hfp, ufp, TRUE);
X	putc('\n', ufp);	/* force blank line at end (ugh) */
X#else
X	tprint(hfp, ufp, FALSE);
X#endif
X	(void) fclose(hfp);
X#ifdef MMDF
X	if (!isprogram)
X		fprintf(ufp, "\001\001\001\001\n");  /* MMDF message header */
X#endif /* MMDF */
X	if (isprogram)
X		(void) pclose (ufp);
X	else
X		(void) fclose(ufp);
X	if (!isprogram)
X		printf("%s: %s\n", to, isnew ? "New file" : "Appended");
X}
X
X
X/*
X * Print out the rest of the article.
X */
Xtprint(ifp, ofp, checkfrom)
Xregister FILE *ifp, *ofp;
Xint checkfrom;
X{
X	while ((fgets(bfr, sizeof bfr, ifp)) != NULL && !SigTrap) {
X		if (checkfrom && strncmp(bfr, "From ", 5) == 0)
X			putc('>', ofp);
X		(void) fputs(bfr, ofp);
X		if (ferror(ofp)) break;		/* E.g. disk full */
X	}
X	if (SigTrap)
X		qfflush(ofp);
X	(void) fflush(ofp);
X	fprintf(ofp, (SigTrap ? "\n\n" : "\n"));
X	SigTrap = FALSE;
X}
X
X
X/*
X * Print the file header.
X */
Xhprint(hp, ofp, verbose)
Xregister struct hbuf *hp;
Xint	verbose;
Xregister FILE *ofp;
X{
X	register char	*p1, *p2;
X	char	fname[BUFLEN];
X	char *tailpath();
X
X	fname[0] = '\0';		/* init name holder */
X
X	if (verbose == 2) {
X		lhwrite(hp, ofp);
X		return;
X	}
X
X	if (lflag || eflag) {
X		char buf1[80], buf2[200];
X		char *cp;
X
X		(void) strcpy(bfr, groupdir);
X		for (cp=bfr; *cp; cp++)
X			if (*cp == '/')
X				*cp = '.';
X		(void) sprintf(buf1, "%s/%ld", bfr, bit);
X		(void) sprintf(buf2, "%-20s %s", buf1, hp->title);
X		fprintf(ofp, "%.76s\n", buf2);
X		return;
X	}
X
X	p1 = index(hp->from, '(');	/* Find the sender's full name. */
X	if (p1 == NULL && hp->path[0])
X		p1 = index(hp->path, '(');
X	if (p1 != NULL) {
X		strcpy(fname, p1+1);
X		p2 = index(fname, ')');
X		if (p2 != NULL)
X			*p2 = '\0';
X	}
X
X	fprintf(ofp, "Subject: %s\n", hp->title);
X	if (!hflag && hp->summary[0])
X		fprintf(ofp, "Summary: %s\n", hp->summary);
X	if (!hflag && hp->keywords[0])
X		fprintf(ofp, "Keywords: %s\n", hp->keywords);
X	if (verbose) {
X		fprintf(ofp, "From: %s\n", hp->from);
X		fprintf(ofp, "Path: %s\n", hp->path);
X		if (hp->organization[0])
X			fprintf(ofp, "Organization: %s\n", hp->organization);
X	}
X	else {
X		if (p1 != NULL)
X			*--p1 = '\0';		/* bump over the '(' */
X#ifdef INTERNET
X		/*
X		 * Prefer Path line if it's in internet format, or if we don't
X		 * understand internet format here, or if there is no reply-to.
X		 */
X		fprintf(ofp, "From: %s", hp->from);
X#else
X		fprintf(ofp, "Path: %s", tailpath(hp));
X#endif
X		if (fname[0] || hp->organization[0]) {
X			if (fname[0] == '\0') {
X				(void) strcpy(fname,hp->from);
X				p2 = index(fname,'@');
X				if (p2)
X					*p2 = '\0';
X			}
X			fprintf(ofp, " (%s", fname);
X			if (hp->organization[0] && !hflag)
X				fprintf(ofp, " @ %s", hp->organization);
X			fprintf(ofp, ")");
X		}
X		fprintf(ofp, "\n");
X		if (p1 != NULL)
X			*p1 = ' ';
X	}
X
X	if (verbose) {
X		fprintf(ofp, "Newsgroups: %s\n", hp->nbuf);
X		fprintf(ofp, "Date: %s\n", hp->subdate);
X		if (hp->sender[0])
X			fprintf(ofp, "Sender: %s\n", hp->sender);
X		if (hp->replyto[0])
X			fprintf(ofp, "Reply-To: %s\n", hp->replyto);
X		if (hp->followto[0])
X			fprintf(ofp, "Followup-To: %s\n", hp->followto);
X	}
X	else if (index(hp->nbuf, ',') || strcmp(groupdir, "junk") == 0)
X		fprintf(ofp, "Newsgroups: %s\n", hp->nbuf);
X
X	if (pflag || ofp != stdout)
X		putc('\n', ofp);
X}
X
X
X/*
X * If ofp != stdout, close it and run the script in coptbuf.
X */
Xcout(ofp)
XFILE *ofp;
X{
X	register char	*p, *q, *r;
X
X	if (ofp == stdout || ofp == NULL)
X		return;
X	(void) fclose(ofp);
X	p = coptbuf;
X	q = lbuf;
X	while ((*q = *p++) != '\0')
X		if (*q++ == FMETA) {
X			q--;
X			r = outfile;
X			while ((*q++ = *r++) != '\0')
X				;
X			q--;
X		}
X	fwait(fsubr(ushell, lbuf, (char *)NULL));
X	(void) unlink(outfile);
X}
X
X
Xcdump(ofp)
Xregister FILE *ofp;
X{
X	if (ofp == stdout)
X		return;
X	fclose(ofp);
X	unlink(outfile);
X}
X
X
X/*
X * Quiet 'flush'.
X * Empty (without fflush()) the buffer for stream fp.
X */
X/* ARGSUSED */
Xqfflush(fp)
XFILE *fp;
X{
X#ifdef fileno
X	int	fno, err;
X
X	fno = fileno(fp);
X	err = ferror(fp);
X	fileno(fp) = -1;
X	(void) fflush(fp);
X	fileno(fp) = fno;
X	if (!err)
X		(void) clearerr(fp);
X#endif /* fileno */
X}
X
X/*
X * Count the number of remaining lines in file fp.
X * Do not move the file pointer.
X */
Xlinecnt(fp)
XFILE *fp;
X{
X	long	curpos;
X	register int	nlines = 0;
X	register int	c;
X
X	if (fp == NULL)
X		return 0;
X	curpos = ftell(fp);
X	while ((c = getc(fp)) != EOF)
X		if (c == '\n')
X			nlines++;
X	(void) fseek(fp, curpos, 0);
X	return nlines;
X}
X
X
X/*
X * Transmit file to system.
X */
Xtransmit(sp, file)
Xregister struct srec *sp;
Xchar	*file;
X{
X	register FILE *ifp, *ofp;
X	register int	c, i;
X#ifdef u370
X	static struct hbuf hh;
X#else /* !u370 */
X	struct hbuf hh;
X#endif /* !u370 */
X	char	TRANS[BUFLEN];
X
X#ifdef DEBUG
X	fprintf(stderr, "xmit %s to %s using %s\n", file, sp->s_name, sp->s_xmit);
X#endif
X	bzero((char *)&hh, sizeof hh);
X	ifp = xart_open(file, "r");
X	if (hread(&hh, ifp, TRUE) == NULL)
X		return;
X	strcpy(TRANS, "/tmp/trXXXXXX");
X	ofp = xfopen(mktemp(TRANS), "w");
X	if (index(sp->s_flags, 'A') == NULL)
X		hwrite(&hh, ofp);
X	else {
X#ifdef OLD
X			fprintf(ofp, "A%s\n%s\n%s!%s\n%s\n%s\n", oident(hh.ident), hh.nbuf, PATHSYSNAME,
X				hh.path, hh.subdate, hh.title);
X#else /* !OLD */
X			logerr("Must have OLD defined to use A flag for xmit");
X			unlink(TRANS);
X			return;
X#endif /* !OLD */
X	}
X	while ((c = getc(ifp)) != EOF)
X		putc(c, ofp);
X	(void) fclose(ifp);
X	(void) fclose(ofp);
X	for (i=0;i<NUNREC;i++)
X		if (hh.unrec[i] != NULL)
X			free(hh.unrec[i]);
X	if (*sp->s_xmit == '\0' || strpbrk(sp->s_flags, "FUMH"))
X		(void) sprintf(bfr, DFTXMIT, sp->s_name, TRANS);
X	else
X		(void) sprintf(bfr, "(%s) < %s", sp->s_xmit, TRANS);
X#ifdef DEBUG
X	fprintf(stderr, "%s\n", bfr);
X#endif
X	(void) system(bfr);
X	(void) unlink(TRANS);
X}
X
X
X/*
X * Cancel the article whose header is in hp, by posting a control message
X * to cancel it.  The scope of the control message depends on who would
X * really be willing to cancel it.  It is sent as far as it will do any good.
X * notauthor is true iff the person posting this article is not the
X * real author of the article being cancelled.
X */
Xcancel(ofp, hp, notauthor)
XFILE *ofp;
Xstruct hbuf *hp;
Xint	notauthor;
X{
X	int	pid;
X
X	fflush(ofp);
X	pid = vfork();
X	if (pid < 0) {
X		perror("readnews: cancel");
X		return 0;
X	}
X	if (pid > 0)
X		return 0;
X	if (notauthor)
X		(void) sprintf(bfr, "%s/%s -c 'cancel %s' -n %s -d %s < /dev/null",
X		    LIB, "inews", hp->ident, hp->nbuf,
X#ifdef ORGDISTRIB
X			ORGDISTRIB);
X#else /* !ORGDISTRIB */
X			"local");
X#endif /* !ORGDISTRIB */
X	else {
X		if (hp->distribution[0] == '\0')
X			(void) sprintf(bfr, "%s/%s -c 'cancel %s' -n %s < /dev/null",
X			    LIB, "inews", hp->ident, hp->nbuf);
X		else
X			(void) sprintf(bfr, "%s/%s -c 'cancel %s' -n %s -d %s < /dev/null",
X			    LIB, "inews", hp->ident, hp->nbuf, hp->distribution);
X	}
X	execl("/bin/sh", "sh", "-c", bfr, (char *) 0);
X	perror(bfr);
X	for ( ; ; )
X		exit(1);
X}
X
X
Xdash(num, ofp)
Xregister int	num;
Xregister FILE *ofp;
X{
X	register int	i;
X
X	for (i = 0; i < num; i++)
X		putc('-', ofp);
X	putc('\n', ofp);
X}
X
X
Xhelp(ofp)
Xregister FILE *ofp;
X{
X	register FILE *fp;
X	register int	c;
X	char temp[BUFLEN];
X
X	if (cflag) {
Xoneline:
X		fprintf(ofp, "(n)ext re(p)rint (w)rite (q)uit (r)eply\
X (c)ancel -[n] +[n] (f)ollowup (N)ext (U)nsubscribe (v)ersion\n");
X		return;
X	}
X	(void) sprintf(temp, "%s/%s", LIB, "help");
X	if ((fp = fopen(temp, "r")) == NULL) {
X		fprintf(ofp, "No help file.\n");
X		goto oneline;
X	}
X	while ((c = getc(fp)) != EOF && !SigTrap)
X		putc(c, ofp);
X	(void) fclose(fp);
X}
X
X
Xpout(ofp)
XFILE *ofp;
X{
X	register char	*p, *q, *r;
X
X	p = PAGER;
X	q = lbuf;
X	while ((*q = *p++) != '\0')
X		if (*q++ == FMETA) {
X			q--;
X			r = filename;
X			while ((*q++ = *r++) != '\0')
X				;
X			q--;
X		}
X	fwait(fsubr(ushell, lbuf, (char *)NULL));
X	fprintf(ofp, "\n");
X}
X
X/*
X * Print a very brief version of the date in question.
X */
Xchar *
Xbriefdate(datestr)
Xchar *datestr;
X{
X	time_t dt, now;
X	char *tmstr;
X	char *wkday, *monthdate, *timeofday;
X	static char rbuf[20];
X
X	dt = cgtdate(datestr);
X	tmstr = ctime(&dt);
X
X	wkday = tmstr; tmstr[3] = '\0';
X	monthdate = tmstr+4; tmstr[10] = '\0';
X	timeofday = tmstr+11; tmstr[16] = '\0';
X
X	(void) time(&now);
X	if (now - dt < WEEKS)
X		(void) strcpy(rbuf, wkday);
X	else
X		(void) strcpy(rbuf, monthdate);
X	(void) strcat(rbuf, " ");
X	(void) strcat(rbuf, timeofday);
X	return rbuf;
X}
X
X/*
X * Return TRUE iff stdout is /dev/null.
X */
Xignoring()
X{
X	struct stat ss, ns;
X
X	if (fstat(1, &ss) < 0)
X		return FALSE;
X	if (stat("/dev/null", &ns) < 0)
X		return FALSE;
X	if (ss.st_dev == ns.st_dev && ss.st_rdev == ns.st_rdev)
X		return TRUE;
X	return FALSE;
X}
END_OF_FILE
if test 11135 -ne `wc -c <'rfuncs2.c'`; then
    echo shar: \"'rfuncs2.c'\" unpacked with wrong size!
fi
# end of 'rfuncs2.c'
fi
if test -f 'pathinit.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'pathinit.c'\"
else
echo shar: Extracting \"'pathinit.c'\" \(10623 characters\)
sed "s/^X//" >'pathinit.c' <<'END_OF_FILE'
X/*
X * This software is Copyright (c) 1986 by Rick Adams.
X *
X * Permission is hereby granted to copy, reproduce, redistribute or
X * otherwise use this software as long as: there is no monetary
X * profit gained specifically from the use or reproduction or this
X * software, it is not sold, rented, traded or otherwise marketed, and
X * this copyright notice is included prominently in any copy
X * made.
X *
X * The author make no claims as to the fitness or correctness of
X * this software for any use whatsoever, and it is provided as is. 
X * Any use of this software is at the user's own risk.
X *
X * This function initializes all the strings used for the various
X * filenames.  They cannot be compiled into the program, since that
X * would be non-portable.  With this convention, the netnews sub-system
X * can be owned by any non-privileged user.  It is also possible
X * to work when the administration randomly moves users from one
X * filesystem to another.  The convention is that a particular user
X * (HOME, see Makefile) is searched for in /etc/passwd and all files
X * are presumed relative to there.  This method also allows one copy
X * of the object code to be used on ANY machine.  (this code runs
X * un-modified on 50+ machines at IH!!)
X *
X * The disadvantage to using this method is that all netnews programs
X * (inews, readnews, rnews, checknews) must first search /etc/passwd
X * before they can start up.  This can cause significant overhead if
X * you have a big password file.
X *
X * Some games are played with ifdefs to get four .o files out of this
X * one source file.  INEW is defined for inews, READ for readnews,
X * CHKN for checknews, and EXP for expire.
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)pathinit.c	1.23	4/6/87";
X#endif /* SCCSID */
X
X#if defined(INEW) || defined(EXP)
X#include	"iparams.h"
X#endif /* INEW || EXP */
X
X#ifdef READ
X#include	"rparams.h"
X#endif /* READ */
X
X#if defined(CHKN)
X#include "params.h"
X#endif /* CHKN */
X
X
Xchar *FROMSYSNAME, *PATHSYSNAME, *LOCALSYSNAME, *LOCALPATHSYSNAME;
Xchar *SPOOL, *LIB, *BIN, *ACTIVE, *SUBFILE, *ARTFILE,
X	*username, *userhome;
X
X#ifdef INEW
Xchar *LOCKFILE, *SEQFILE, *ARTICLE, *INFILE, *TELLME;
X
Xint c_cancel(), c_newgroup(), c_ihave(), c_sendme(), c_rmgroup(),
X    c_sendsys(), c_version(), c_checkgroups(), c_unimp();
X
Xstruct msgtype msgtype[] = {
X	"cancel", NULL, c_cancel,
X	"newgroup", NULL, c_newgroup,
X	"ihave", NULL, c_ihave,
X	"sendme", NULL, c_sendme,
X	"sendbad", NULL, c_sendme,
X	"rmgroup", NULL, c_rmgroup,
X	"sendsys", NULL, c_sendsys,
X	"version", NULL, c_version,
X	"checkgroups", NULL, c_checkgroups,
X	"delsub", NULL, c_unimp,
X	NULL, NULL, NULL
X};
X#endif /* INEW */
X
X#if defined(INEW) || defined(READ)
Xchar *ALIASES;
X#endif /* INEW || READ */
X
X#ifdef EXP
Xchar *OLDNEWS;
X#endif /* EXP */
X
X#ifdef READ
Xchar *MAILPARSER;
X#endif /* READ */
X
X
Xstruct passwd *getpwnam();
Xchar *rindex();
X
X#define Sprintf(where,fmt,arg)	(void) sprintf(bfr,fmt,arg); where = AllocCpy(bfr)
X
Xchar *
XAllocCpy(cp)
Xregister char *cp;
X{
X	register char *mp;
X	char *malloc();
X
X	mp = malloc((unsigned)strlen(cp) + 1);
X
X	if (mp == NULL)
X		xerror("malloc failed on %s", cp);
X
X	(void) strcpy(mp, cp);
X	return mp;
X}
X
Xpathinit()
X{
X#ifndef ROOTID
X	struct passwd	*pw;	/* struct for pw lookup	*/
X#endif /* !ROOTID */
X#ifdef EXP
X	char *p;
X#endif /* EXP */
X#ifndef CHKN
X	struct utsname ubuf;
X	char buf[BUFLEN];
X	extern char *mydomain();
X
X	uname(&ubuf);
X
X#ifdef HIDDENNET_IN_LOCALSYSNAME
X	/* old compatibility code, remove when HIDDENNET is used no more */
X	if (strcmp(ubuf.nodename, HIDDENNET) != 0)
X		(void) sprintf(buf, "%s.%s%s", ubuf.nodename, HIDDENNET,
X			mydomain());
X	else
X#endif
X		(void) sprintf(buf, "%s%s", ubuf.nodename, mydomain());
X	LOCALSYSNAME = AllocCpy(buf);
X
X#ifdef GENERICFROM
X	(void) sprintf(buf, GENERICFROM, ubuf.nodename, mydomain());
X	FROMSYSNAME = AllocCpy(buf);
X#else /* !GENERICFROM */
X	FROMSYSNAME = LOCALSYSNAME;
X#endif /* !GENERICFROM */
X
X	LOCALPATHSYSNAME = AllocCpy(ubuf.nodename);
X
X#ifdef GENERICPATH
X	(void) sprintf(buf, GENERICPATH, ubuf.nodename, mydomain());
X	PATHSYSNAME = AllocCpy(buf);
X#else	/* !GENERICPATH */
X	PATHSYSNAME = LOCALPATHSYSNAME;
X#endif	/* !GENERICPATH */
X
X#endif /* !CHKN */
X
X#ifdef HOME
X	/* Relative to the home directory of user HOME */
X	(void) sprintf(bfr, "%s/%s", logdir(HOME), SPOOLDIR);
X	SPOOL = AllocCpy(bfr);
X	(void) sprintf(bfr, "%s/%s", logdir(HOME), LIBDIR);
X	LIB = AllocCpy(bfr);
X#else /* !HOME */
X	/* Fixed paths defined in Makefile */
X	SPOOL = AllocCpy(SPOOLDIR);
X	LIB = AllocCpy(LIBDIR);
X#endif /* !HOME */
X
X#ifdef IHCC
X	(void) sprintf(bfr, "%s/%s", logdir(HOME), BINDIR);
X	BIN = AllocCpy(bfr);
X#else /* !IHCC */
X	Sprintf(BIN, "%s", BINDIR);
X#endif /* !IHCC */
X
X	Sprintf(ACTIVE, "%s/active", LIB);
X
X#ifdef EXP
X	(void) strcpy(bfr, SPOOL);
X	p = rindex(bfr, '/');
X	if (p) {
X		strcpy(++p, "oldnews");
X		OLDNEWS = AllocCpy(bfr);
X	} else
X		OLDNEWS = AllocCpy("oldnews");
X#endif /* EXP */
X
X#ifndef CHKN
X	Sprintf(SUBFILE, "%s/sys", LIB);
X	Sprintf(ARTFILE, "%s/history", LIB);
X# endif /* !CHKN */
X
X# ifdef READ
X#ifdef SENDMAIL
X	Sprintf(MAILPARSER, "%s -oi -oem", SENDMAIL);
X#else /* !SENDMAIL */
X	Sprintf(MAILPARSER, "%s/recmail", LIB);
X#endif /* !SENDMAIL */
X# endif /* READ */
X
X# if defined(READ) || defined(INEW)
X	Sprintf(ALIASES, "%s/aliases", LIB);
X# endif /* READ || INEW */
X# ifdef INEW
X	Sprintf(LOCKFILE, "%s/LOCK", LIB);
X	Sprintf(SEQFILE, "%s/seq", LIB);
X	Sprintf(ARTICLE, "%s/.arXXXXXX", SPOOL);
X	Sprintf(INFILE, "%s/.inXXXXXX", SPOOL);
X/*
X * The person notified by the netnews sub-system.  Again, no name is
X * compiled in, but instead the information is taken from a file.
X * If the file does not exist, a "default" person will get the mail.
X * If the file exists, but is empty, nobody will get the mail.  This
X * may seem backwards, but is a better fail-safe.
X */
X# ifdef NOTIFY
X	parse_notify();
X# endif /* NOTIFY */
X
X/*
X * Since the netnews owner's id number is different on different
X * systems, we'll extract it from the /etc/passwd file.  If no entry,
X * default to root.  This id number seems to only be used to control who
X * can input certain control messages or cancel any message.  Note that
X * entry is the name from the "notify" file as found above if possible.
X * Defining ROOTID in defs.h hardwires in a number and avoids
X * another search of /etc/passwd.
X */
X# ifndef ROOTID
X	if ((pw = getpwnam(TELLME)) != NULL)
X		ROOTID =  pw->pw_uid;
X	else if ((pw = getpwnam(HOME)) != NULL)
X		ROOTID =  pw->pw_uid;
X	else
X		ROOTID = 0;		/* nobody left, let only root */
X# endif /* !ROOTID */
X#endif /* INEW */
X}
X
X#ifdef INEW
X#ifdef NOTIFY
X/*
X * Attempt to parse the LIB/notify file into the global structure msgtype[].
X */
Xparse_notify()
X{
X	FILE *nfd;
X	int valid = 0, done = 0;
X	register struct msgtype *mp;
X	char mtype[BUFLEN], addr[BUFLEN];
X
X	(void) sprintf(bfr, "%s/notify", LIB);
X#ifndef ROOTID
X	TELLME = AllocCpy(NOTIFY);	
X#endif /* !ROOTID */
X	if ( (nfd = fopen(bfr, "r")) == NULL) {
X		/* 
X		 * Set defaults to NOTIFY
X		 */
X#ifdef debug
X		log("parse_notify: %s/notify not found", LIB);
X#endif /* debug */
X		(void)setmsg("all", NOTIFY);
X		return;
X	}
X	do  {
X		mtype[0] = addr[0] = 0;
X		switch( get_notify(nfd, mtype, addr) ) {
X		case 0:
X			continue;
X		case 1:
X			valid += setmsg(mtype, "");
X			break;
X		case 2:
X			valid += setmsg(mtype, addr);
X			break;
X		case -1:
X			if( !valid ) {
X#ifdef debug
X				log("parse_notify: no valid entries found.");
X#endif /* debug */
X				setmsg("all", ""); /* send mail to no one */
X			}
X			done = 1;
X		}
X	} while( !done );
X
X	/*
X	 * point to zero length string for all entries we haven't touched
X	 */
X	for(mp=msgtype; mp->m_name; mp++)
X		if(mp->m_who_to == 0)
X			mp->m_who_to = "";
X}
X
Xsetmsg(what, to)
Xchar *what, *to;
X{
X	register struct msgtype *mp;
X#ifdef debug
X	log("setmsg: what='%s', to='%s'", what, to);
X#endif /* debug */
X	/*
X	 * Special case for "all"
X	 */
X	if(strcmp(what, "all") == 0) {
X		for(mp=msgtype; mp->m_name; mp++) {
X			mp->m_who_to = AllocCpy(to);
X#ifdef debug
X			log("setmsg: '%s'='%s'", mp->m_name, mp->m_who_to);
X#endif /* debug */
X		}
X		return 1;
X	}
X
X	for(mp=msgtype; mp->m_name; mp++)
X		if(strcmp(mp->m_name, what) == 0) {
X			mp->m_who_to = AllocCpy(to);
X#ifdef debug
X			log("setmsg: '%s'='%s'", mp->m_name, mp->m_who_to);
X#endif /* debug */
X			return 1;
X		}
X	return 0;
X}
X
Xstatic
Xget_notify(fp, s, t)
XFILE *fp;
Xregister char *s, *t;
X{
X	register char *cp;
X	char pbuf[BUFSIZ];
X
X	if( cp=fgets(pbuf, sizeof(pbuf), fp ) ) {
X		if( *cp == '\n' ) 
X			return 0;
X		while(*cp && *cp != ' ' && *cp != '\t' && *cp != '\n')
X			*s++ = *cp++;
X		*s = '\0';	/* terminate first string */
X
X		while(*cp && (*cp == ' ' || *cp == '\t' || *cp == '\n') )
X			cp++;	/* look for start of second */
X		if( !*cp || *cp == '\n' )
X			return 1; 	/* no second string */
X		
X		while( *cp && *cp != '\n' )
X			*t++ = *cp++;
X		*t = '\0';
X		return 2;
X	} else
X		return -1;
X}
X#endif /* NOTIFY */
X#endif /* INEW */
X
X#ifndef CHKN
X/*
X * At sites where the are many mail domains within the support area of a single
X * news administrator, it is much nicer to be able to read the local domain of
X * a machine from a file.  What we do here is:
X * 1)	Check for the presence of a LIBDIR/localdomain file.  If it doesn't 
X * 	exist,assume that MYDOMAIN should be used instead.
X * 2)	If it does exist, we make the following assumptions:
X *	a)  If it is empty, has only comments, or only blank lines; we assume
X *	    the domain is desired to be a zero length string ( ie "").  (this
X *	    implies that the domain name is contained in the hostname.)
X *	b)  If it is not empty, we assume the first line not beginning with a
X *	    '#', blank/tab, or newline is the desired domain name.
X *	    A like '.UUCP' or '.TEK.COM' should be used.  We could insure that
X *	    the line begin with a '.' to be a valid domain name, but I don't 
X *	    think it is necessary to put that restriction on it.
X */
Xchar *
Xmydomain()
X{
X	static char *md = NULL;
X	register char *cp;
X	FILE *fp = NULL;
X	char fbuf[BUFLEN];
X	extern char *malloc(), *strcpy(), *index();
X
X	if(md)	/* we've been here before, so just return what we found */
X		return(md);
X
X	(void) sprintf(fbuf,"%s/localdomain", LIBDIR);
X	if ( (fp = fopen(fbuf,"r")) == NULL) {
X		md = MYDOMAIN;	/* No localdomain file, use MYDOMAIN instead */
X	} else {
X		while(fgets(fbuf, sizeof(fbuf), fp) ) {
X			if( *fbuf == '\n' || *fbuf == '#' 
X			    || *fbuf == ' ' || *fbuf == '\t')
X				continue;
X	
X			if( cp = index(fbuf, '\n') )
X				*cp = '\0';
X	
X			if ( (md = malloc(strlen(fbuf) + 1)) == NULL)
X				md = MYDOMAIN;	/* punt here */
X			else
X				(void)strcpy(md, fbuf);
X			break;
X		}
X	}
X
X	if(fp)
X		(void)fclose(fp);
X
X	if( md == NULL)
X		md = "";
X	
X	return md;
X}
X#endif /* !CHKN */
END_OF_FILE
if test 10623 -ne `wc -c <'pathinit.c'`; then
    echo shar: \"'pathinit.c'\" unpacked with wrong size!
fi
# end of 'pathinit.c'
fi
if test -f 'checknews.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'checknews.c'\"
else
echo shar: Extracting \"'checknews.c'\" \(11026 characters\)
sed "s/^X//" >'checknews.c' <<'END_OF_FILE'
X/*
X * This software is Copyright (c) 1986 by Rick Adams.
X *
X * Permission is hereby granted to copy, reproduce, redistribute or
X * otherwise use this software as long as: there is no monetary
X * profit gained specifically from the use or reproduction or this
X * software, it is not sold, rented, traded or otherwise marketed, and
X * this copyright notice is included prominently in any copy
X * made.
X *
X * The author make no claims as to the fitness or correctness of
X * this software for any use whatsoever, and it is provided as is. 
X * Any use of this software is at the user's own risk.
X *
X * checknews - news checking program
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)checknews.c	2.29	4/6/87";
X#endif /* SCCSID */
X
Xchar *Progname = "checknews";		/* used by xerror */
X
X#include "params.h"
X
Xchar	optbuf[BUFLEN];			/* NEWSOPTS buffer		*/
Xint	y, e, n, q;
Xint	verbose;			/* For debugging.		*/
Xint	nflag;				/* for spec. newsgroup		*/
Xchar	narggrp[BUFLEN];		/* spec newsgroup		*/
X
X#ifdef	MINIX
X/* make 'em external */
Xextern	char	bfr[LBUFLEN];		/* general-use scratch area	*/
Xextern	char	newsrc[],*rcline[],rcbuf[],*argvrc[];
Xextern	FILE	*rcfp, *actfp;
X#else
Xchar	bfr[LBUFLEN];			/* general-use scratch area	*/
Xchar	newsrc[BUFLEN],*rcline[LINES],rcbuf[LBUFLEN],*argvrc[LINES];
XFILE	*rcfp, *actfp;
X#endif
X
Xint	mode = 1;
Xextern int line;
X
X
X#ifndef SHELL
X#ifdef	MINIX
X/* Make it extern */
Xextern
X#endif
X
Xchar	*SHELL;
X#endif
X
Xmain(argc, argv)
Xint argc;
Xregister char **argv;
X{
X	register char *ptr;	/* pointer to rest of buffer		*/
X	char *user, *home;
X	struct passwd *pw;
X	struct group *gp;
X	int sflag = 0, optflag = FALSE, space = FALSE;
X	int i;
X
X	y = 0;
X	n = 0;
X	e = 0;
X	q = 0;
X	nflag = 0;
X	pathinit();
X	if (--argc > 0) {
X		for (argv++; **argv; ++*argv) {
X			switch(**argv) {
X			case 'y':
X				y++;
X				break;
X			case 'q':
X				q++;
X				break;
X			case 'v':
X				verbose++;
X				break;
X			case 'n':
X				n++;
X				break;
X			case 'N':
X				nflag++;
X				if (argc <= 1)
X					xerror("No newsgroup specified with -N");
X				strcpy(narggrp,argv[1]);
X				strcat(narggrp,",");
X				break;
X			case 'e':
X			case 'f':
X				e++;
X				break;
X			}
X		}
X	}
X	if (!n && !e && !y && !q)
X		y++;
X	if (nflag)
X		argv++;
X
X#ifndef V6
X	if ((user = getenv("USER")) == NULL)
X		user = getenv("LOGNAME");
X	if ((home = getenv("HOME")) == NULL)
X		home = getenv("LOGDIR");
X	if (user == NULL || home == NULL)
X		getuser();
X	else {
X		username = AllocCpy(user);
X		userhome = AllocCpy(home);
X	}
X	if (ptr = getenv("NEWSOPTS"))
X		strcpy(rcbuf, ptr);
X	else
X		*rcbuf = '\0';
X	if (*rcbuf) {
X		strcat(rcbuf, " \1");
X		ptr = rcbuf;
X		while (*++ptr)
X			if (isspace(*ptr))
X				*ptr = '\0';
X		for (ptr = rcbuf;; ptr++) {
X			if (!*ptr)
X				continue;
X			if (*ptr == '\1')
X				break;
X			if (++line > LINES)
X				xerror("Too many options.");
X			if ((rcline[line] = malloc(strlen(ptr) + 1)) == NULL)
X				xerror("Not enough memory.");
X			argvrc[line] = rcline[line];
X			strcpy(rcline[line], ptr);
X			while (*ptr)
X				ptr++;
X		}
X	}
X#else
X	getuser();
X#endif
X	ptr = getenv("NEWSRC");
X	if (ptr == NULL)
X		sprintf(newsrc, "%s/%s", userhome, NEWSRC);
X	else
X		strcpy(newsrc, ptr);
X	if ((rcfp = fopen(newsrc, "r")) != NULL) {
X		while (fgets(rcbuf, LBUFLEN, rcfp) != NULL) {
X			if (!(space = isspace(*rcbuf)))
X				optflag = FALSE;
X			if (!strncmp(rcbuf, "options ", 8))
X				optflag = TRUE;
X			if (optflag) {
X				strcat(rcbuf, "\1");
X				if (space)
X					ptr = rcbuf - 1;
X				else
X					ptr = &rcbuf[7];
X				while (*++ptr)
X					if (isspace(*ptr))
X						*ptr = '\0';
X				if (space)
X					ptr = rcbuf;
X				else
X					ptr = &rcbuf[8];
X				for (;; ptr++) {
X					if (!*ptr)
X						continue;
X					if (*ptr == '\1')
X						break;
X					if (++line > LINES)
X						xerror("Too many options.");
X					if ((rcline[line] = malloc(strlen(ptr) + 1)) == NULL)
X						xerror("Not enough memory.");
X					argvrc[line] = rcline[line];
X					strcpy(rcline[line], ptr);
X					while (*ptr)
X						ptr++;
X				}
X			}
X		}
X		fclose(rcfp);
X	}
X	header.nbuf[0] = 0;
X	if (line != -1) {
X#ifdef DEBUG
X		for (i = 0; i <= line; i++)
X			fprintf(stderr, "options:  %s\n", rcline[i]);
X#endif
X		process(line+2, argvrc);
X		do {
X#ifdef DEBUG
X			fprintf(stderr, "Freeing %d\n", line);
X#endif
X			free(rcline[line]);
X		} while (line--);
X	}
X
X	if (!*header.nbuf) {
X		strcpy(header.nbuf, DFLTSUB);
X		ngcat(header.nbuf);
X	}
X	strcat(header.nbuf, ADMSUB);
X	ngcat(header.nbuf);
X	if (*header.nbuf)
X		lcase(header.nbuf);
X	makehimask(header.nbuf, "junk");
X	makehimask(header.nbuf, "control");
X	makehimask(header.nbuf, "test");
X	if (access(newsrc, 0)) {
X		if (verbose > 1)
X			printf("No newsrc\n");
X		yep(argv);
X	}
X	if ((rcfp = fopen(newsrc, "r")) == NULL)
X		xerror("Cannot open .newsrc file");
X	while (fgets(rcbuf, LBUFLEN, rcfp) != NULL) {
X		if (!nstrip(rcbuf))
X			xerror(".newsrc line too long");
X		if (++line >= LINES)
X			xerror("Too many .newsrc lines");
X		if ((rcline[line] = malloc(strlen(rcbuf)+1)) == NULL)
X			xerror("Not enough memory");
X		strcpy(rcline[line], rcbuf);
X	}
X	if ((actfp = fopen(ACTIVE, "r")) == NULL)
X		xerror("Cannot open active newsgroups file");
X
X#ifdef DEBUG
X	fprintf(stderr, "header.nbuf = %s\n", header.nbuf);
X#endif
X	nchk(argv);
X	exit(0);
X}
X
Xnchk(argv)
Xchar **argv;
X{
X	register int i;
X	register char *ptr;
X	long l;
X	long narts;
X	char saveptr;
X	int isnews = 0;
X	char aline[BUFLEN];
X
X#ifdef DEBUG
X	fprintf(stderr, "nchk()\n");
X#endif
X	while (fgets(aline, sizeof aline, actfp) != NULL) {
X		sscanf(aline, "%s %ld", bfr, &narts);
X#ifdef DEBUG
X		fprintf(stderr, "bfr = '%s'\n", bfr);
X#endif
X		if (narts == 0)
X			continue;
X		ngcat(bfr);
X		if (!ngmatch(bfr, nflag ? narggrp : header.nbuf))
X			continue;
X		ngdel(bfr);
X		i = findrcline(bfr);
X		if (i < 0) {
X			if (verbose>1)
X				printf("No newsrc line for newsgroup %s\n", bfr);
X			strcpy(rcbuf, " 0");
X		} else
X			strcpy(rcbuf, rcline[i]);
X		ptr = rcbuf;
X
X		if (index(rcbuf, '!') != NULL)
X			continue;
X		if (index(rcbuf, ',') != NULL) {
X			if (verbose > 1)
X				printf("Comma in %s newsrc line\n", bfr);
X			else {
X				isnews++;
X				continue;
X			}
X		}
X		while (*ptr)
X			ptr++;
X		while (!isdigit(*--ptr) && *ptr != ':' && ptr >= rcbuf)
X			;
X		if (*ptr == ':')
X			continue;
X		if (ptr < rcbuf) {
X			if (verbose > 1)
X				printf("Ran off beginning of %s newsrc line.\n", bfr);
X			yep(argv);
X		}
X		while (isdigit(*--ptr))
X			;
X		sscanf(++ptr, "%ld", &l);
X		if (narts > l) {
X			if (verbose) {
X				printf("News: %s ...\n", bfr);
X				if (verbose < 2)
X					y = 0;
X			}
X			yep(argv);
X		}
Xcontin:;
X	}
X	if (isnews)
X		yep(argv);
X	if (n)
X		printf("No news is good news.\n");
X}
X
Xyep(argv)
Xchar **argv;
X{
X	if (y) {
X		if (verbose)
X			printf("There is probably news");
X		else
X			printf("There is news");
X		if (nflag) {
X			narggrp[strlen(narggrp)-1] = '.';
X			printf(" in %s\n",narggrp);
X		}
X		else
X			printf(".\n");
X	}
X	if (e) {
X#ifdef V6
X		execv("/usr/bin/readnews", argv);
X#else
X		execvp("readnews", argv);
X#endif
X		perror("Cannot exec readnews.");
X	}
X	if (q)
X		exit(1);
X	else
X		exit(0);
X}
X
Xxerror(message, arg1, arg2)
Xchar *message;
Xint arg1, arg2;
X{
X	char buffer[128];
X
X	sprintf(buffer, message, arg1, arg2);
X	fprintf(stderr, "checknews: %s.\n", buffer);
X	exit(1);
X}
X
X/*
X * Append NGDELIM to string.
X */
Xngcat(s)
Xregister char *s;
X{
X	if (*s) {
X		while (*s++);
X		s -= 2;
X		if (*s++ == NGDELIM)
X			return;
X	}
X	*s++ = NGDELIM;
X	*s = '\0';
X}
X
X/*
X * News group matching.
X *
X * nglist is a list of newsgroups.
X * sublist is a list of subscriptions.
X * sublist may have "meta newsgroups" in it.
X * All fields are NGDELIM separated,
X * and there is an NGDELIM at the end of each argument.
X *
X * Currently implemented glitches:
X * sublist uses 'all' like shell uses '*', and '.' like shell '/'.
X * If subscription X matches Y, it also matches Y.anything.
X */
Xngmatch(nglist, sublist)
Xregister char *nglist, *sublist;
X{
X	register char *n, *s;
X	register int rc;
X
X	rc = FALSE;
X	for (n = nglist; *n != '\0' && rc == FALSE;) {
X		for (s = sublist; *s != '\0';) {
X			if (*s != NEGCHAR)
X				rc |= ptrncmp(s, n);
X			else
X				rc &= ~ptrncmp(s+1, n);
X			while (*s++ != NGDELIM);
X		}
X		while (*n++ != NGDELIM);
X	}
X	return(rc);
X}
X
X/*
X * Compare two newsgroups for equality.
X * The first one may be a "meta" newsgroup.
X */
Xptrncmp(ng1, ng2)
Xregister char *ng1, *ng2;
X{
X	while (*ng1 != NGDELIM) {
X		if (ng1[0]=='a' && ng1[1]=='l' && ng1[2]=='l') {
X			ng1 += 3;
X			while (*ng2 != NGDELIM && *ng2 != '.')
X				if (ptrncmp(ng1, ng2++))
X					return(TRUE);
X			return (ptrncmp(ng1, ng2));
X		} else if (*ng1++ != *ng2++)
X			return(FALSE);
X	}
X	return (*ng2 == '.' || *ng2 == NGDELIM);
X}
X
X/*
X * Get user name and home directory.
X */
Xgetuser()
X{
X	static int flag = TRUE;
X	register struct passwd *p;
X
X	if (flag) {
X		if ((p = getpwuid(getuid())) == NULL)
X			xerror("Cannot get user's name");
X		if (username == NULL || *username == '\0')
X			username = AllocCpy(p->pw_name);
X		userhome = AllocCpy(p->pw_dir);
X		flag = FALSE;
X	}
X}
X
X/*
X * Strip trailing newlines, blanks, and tabs from 's'.
X * Return TRUE if newline was found, else FALSE.
X */
Xnstrip(s)
Xregister char *s;
X{
X	register char *p;
X	register int rc;
X
X	rc = FALSE;
X	p = s;
X	while (*p)
X		if (*p++ == '\n')
X			rc = TRUE;
X	while (--p >= s && (*p == '\n' || *p == ' ' || *p == '\t'));
X	*++p = '\0';
X	return(rc);
X}
X
X/*
X * Delete trailing NGDELIM.
X */
Xngdel(s)
Xregister char *s;
X{
X	if (*s++) {
X		while (*s++);
X		s -= 2;
X		if (*s == NGDELIM)
X			*s = '\0';
X	}
X}
X
Xlcase(s)
Xregister char *s;
X{
X	register char *ptr;
X
X	for (ptr = s; *ptr; ptr++)
X		if (isupper(*ptr))
X			*ptr = tolower(*ptr);
X}
X
X/*
X * finds the line in your .newsrc file (actually the in-core "rcline"
X * copy of it) and returns the index into the array where it was found.
X * -1 means it didn't find it.
X *
X * We play clever games here to make this faster.  It's inherently
X * quadratic - we spend lots of CPU time here because we search through
X * the whole .newsrc for each line.  The "prev" variable remembers where
X * the last match was found; we start the search there and loop around
X * to the beginning, in the hopes that the calls will be roughly in order.
X */
Xint
Xfindrcline(name)
Xchar *name;
X{
X	register char *p, *ptr;
X	register int cur;
X	register int i;
X	register int top;
X	static int prev = 0;
X
X	top = line; i = prev;
Xloop:
X	for (; i <= top; i++) {
X		for (p = name, ptr = rcline[i]; (cur = *p++); ) {
X			if (cur != *ptr++)
X				goto contin2;
X		}
X		if (*ptr != ':' && *ptr != '!')
X			continue;
X		prev = i;
X		return i;
Xcontin2:
X		;
X	}
X	if (i > line && line > prev-1) {
X		i = 0;
X		top = prev-1;
X		goto loop;
X	}
X	return -1;
X}
X
X/*
X * Forbid newsgroup ng, unless he asked for it in nbuf.
X */
Xmakehimask(nbuf, ng)
Xchar *nbuf, *ng;
X{
X	if (!findex(nbuf, ng)) {
X		ngcat(nbuf);
X		strcat(nbuf, "!");
X		strcat(nbuf, ng);
X		ngcat(nbuf);
X	}
X}
X
X/*
X * Return true if the string searchfor is in string, but not if preceded by !.
X */
Xfindex(string, searchfor)
Xchar *string, *searchfor;
X{
X	register char first;
X	register char *p;
X
X	first = *searchfor;
X	for (p=index(string, first); p; p = index(p+1, first)) {
X		if (p>string && p[-1] != '!' && strncmp(p, searchfor, strlen(searchfor)) == 0)
X			return TRUE;
X	}
X	return FALSE;
X}
X
Xxxit(i)
X{
X	exit(i);
X}
END_OF_FILE
if test 11026 -ne `wc -c <'checknews.c'`; then
    echo shar: \"'checknews.c'\" unpacked with wrong size!
fi
# end of 'checknews.c'
fi
if test -f 'install.sh' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'install.sh'\"
else
echo shar: Extracting \"'install.sh'\" \(9680 characters\)
sed "s/^X//" >'install.sh' <<'END_OF_FILE'
X: '@(#)install.sh	1.17	12/16/86'
X
Xif test "$#" != 6
Xthen
X	echo "usage: $0 spooldir libdir bindir nuser ngroup ostype"
X	exit 1
Xfi
XSPOOLDIR=$1
XLIBDIR=$2
XBINDIR=$3
XNEWSUSR=$4
XNEWSGRP=$5
XOSTYPE=$6
X
X: Get name of local system
Xcase $OSTYPE in
X	usg)	SYSNAME=`uname -n`
X		if test ! -d $LIBDIR/history.d
X		then
X			mkdir $LIBDIR/history.d
X			chown $NEWSUSR $LIBDIR/history.d
X			chgrp $NEWSGRP $LIBDIR/history.d
X		fi;;
X	v7)	SYSNAME=`uuname -l`
X		touch $LIBDIR/history.pag $LIBDIR/history.dir;;
X	*)	echo "$0: Unknown Ostype"
X		exit 1;;
Xesac
X
Xif test "$SYSNAME" = ""
Xthen
X	echo "$0: Cannot get system name"
X	exit 1
Xfi
X
X: Ensure SPOOLDIR exists
Xfor i in $SPOOLDIR $SPOOLDIR/.rnews
Xdo
X	if test ! -d $i
X	then
X		mkdir $i
X	fi
X	chmod 777 $i
X	chown $NEWSUSR $i
X	chgrp $NEWSGRP $i
Xdone
X
Xchown $NEWSUSR $LIBDIR
Xchgrp $NEWSGRP $LIBDIR
X
X: Ensure certain files in LIBDIR exist
Xtouch $LIBDIR/history $LIBDIR/active $LIBDIR/log $LIBDIR/errlog $LIBDIR/users
Xchmod 666 $LIBDIR/users
X
X: If no sys file, make one.
Xif test ! -f $LIBDIR/sys
Xthen
Xecho
Xecho Making a $LIBDIR/sys file to link you to oopsvax.
Xecho You must change oopsvax to your news feed.
Xecho If you are not in the USA, remove '"usa"' from your line in the sys file.
Xecho If you are not in North America, remove '"na"' from your line in the sys file.
X	cat > $LIBDIR/sys << EOF
X$SYSNAME:world,comp,sci,news,rec,soc,talk,misc,net,mod,na,usa,to::
Xoopsvax:world,comp,sci,news,rec,soc,talk,misc,net,mod,na,usa,to.oopsvax::
XEOF
Xfi
X
X: If no seq file, make one.
Xif test ! -s $LIBDIR/seq
Xthen
X	echo '100' >$LIBDIR/seq
Xfi
X
X: If no mailpaths, make one.
Xif test ! -s $LIBDIR/mailpaths
Xthen
X	cat <<E_O_F >$LIBDIR/mailpaths
Xbackbone	%s
Xinternet	%s
XE_O_F
Xecho "I have created $LIBDIR/mailpaths for you. The paths are certainly wrong."
Xecho "You must correct them manually to be able to post to moderated groups."
Xfi
X
Xsh makeactive.sh $LIBDIR $SPOOLDIR $NEWSUSR $NEWSGRP
X
Xfor i in $LIBDIR/ngfile $BINDIR/inews $LIBDIR/localgroups $LIBDIR/moderators \
X	$LIBDIR/cunbatch $LIBDIR/c7unbatch
Xdo
X	if test -f $i
X	then
X		echo "$i is no longer used. You should remove it."
X	fi
Xdone
X
Xfor i in $LIBDIR/csendbatch $LIBDIR/c7sendbatch
Xdo
X	if test -f $i
X	then
X		echo "$i is no longer used. You should remove it after"
X		echo "changing your crontab entry to use sendbatch [flags]"
X	fi
Xdone
X
Xif test -f $BINDIR/cunbatch
Xthen
X	echo "$BINDIR/cunbatch is not used by the new batching scheme."
X	echo "You should remove it when all of your neighbors have upgraded."
Xfi
X
Xcat >$LIBDIR/aliases.new <<EOF
Xnet.audio	rec.audio
Xnet.auto	rec.autos
Xnet.auto.tech	rec.autos.tech
Xnet.aviation	rec.aviation
Xnet.bicycle	rec.bicycles
Xnet.rec.birds	rec.birds
Xnet.rec.boat	rec.boats
Xnet.cooks	rec.food.cooking
Xnet.wines	rec.food.drink
Xnet.veg		rec.food.veg
Xnet.games	rec.games.misc
Xnet.games.board	rec.games.board
Xnet.rec.bridge	rec.games.bridge
Xnet.games.chess	rec.games.chess
Xnet.games.emp	rec.games.empire
Xnet.games.frp	rec.games.frp
Xnet.games.go	rec.games.go
Xnet.games.hack	rec.games.hack
Xnet.games.pbm	rec.games.pbm
Xnet.games.rogue	rec.games.rogue
Xnet.games.trivia	rec.games.trivia
Xnet.games.video	rec.games.video
Xnet.garden	rec.gardens
Xnet.ham-radio	 rec.ham-radio
Xnet.ham-radio.packet rec.ham-radio.packet
Xnet.jokes	rec.humor
Xnet.jokes.d	rec.humor.d
Xmod.mag		rec.mag
Xnet.mag		rec.mag
Xnet.books	rec.arts.books
Xnet.comics	rec.arts.comics
Xnet.tv.drwho	rec.arts.drwho
Xmod.movies	rec.arts.movies
Xnet.movies	rec.arts.movies
Xnet.sf-lovers	rec.arts.sf-lovers
Xnet.startrek	rec.arts.startrek
Xnet.tv		rec.arts.tv
Xnet.tv.soaps	rec.arts.tv.soaps
Xnet.wobegon	rec.arts.wobegon
Xnet.rec		rec.misc
Xnet.cycle	rec.motorcycles
Xnet.music.classical	rec.music.classical
Xnet.music.folk	rec.music.folk
Xnet.music.gdead	rec.music.gdead
Xnet.music.makers	rec.music.makers
Xnet.music	rec.music.misc
Xnet.music.synth	rec.music.synth
Xnet.rec.nude	rec.nude
Xnet.pets	rec.pets
Xnet.rec.photo	rec.photo
Xnet.poems	rec.arts.poems
Xnet.puzzle	rec.puzzles
Xnet.railroad	rec.railroad
Xnet.rec.scuba	rec.scuba
Xnet.rec.ski	rec.skiing
Xnet.rec.skydive	rec.skydiving
Xnet.sport	rec.sport.misc
Xnet.sport.baseball	rec.sport.baseball
Xnet.sport.hoops	rec.sport.basketball
Xnet.sport.football	rec.sport.football
Xnet.sport.hockey	rec.sport.hockey
Xnet.travel	rec.travel
Xnet.video	rec.video
Xnet.rec.wood	rec.woodworking
Xnet.ai	comp.ai
Xnet.arch	comp.arch
Xnet.bugs.2bsd	comp.bugs.2bsd
Xnet.bugs.4bsd	comp.bugs.4bsd
Xnet.bugs.usg	comp.bugs.sys5
Xnet.bugs.uucp	comp.bugs.misc
Xnet.bugs.v7	comp.bugs.misc
Xnet.bugs	comp.bugs.misc
Xnet.cog-eng	comp.cog-eng
Xnet.cse		comp.edu
Xnet.database	comp.databases
Xnet.dcom	comp.dcom.modems
Xnet.decus	comp.org.decus
Xnet.emacs	comp.emacs
Xnet.eunice	comp.os.eunice
Xnet.graphics	comp.graphics
Xnet.info-terms	comp.terminals
Xnet.internat	comp.std.internat
Xnet.lan		comp.dcom.lans
Xnet.lang	comp.lang.misc
Xnet.lang.ada	comp.lang.ada
Xnet.lang.apl	comp.lang.apl
Xnet.lang.c	comp.lang.c
Xnet.lang.c++	comp.lang.c++
Xnet.lang.f77	comp.lang.fortran
Xnet.lang.forth	comp.lang.forth
Xnet.lang.lisp	comp.lang.lisp
Xnet.lang.mod2	comp.lang.modula2
Xnet.lang.pascal	comp.lang.pascal
Xnet.lang.prolog	comp.lang.prolog
Xnet.lang.st80	comp.lang.smalltalk
Xnet.lsi		comp.lsi
Xnet.mail	comp.mail.uucp
Xnet.mail.headers	comp.mail.headers
Xnet.micro	comp.sys.misc
Xnet.micro.6809	comp.sys.m6809
Xnet.micro.68k	comp.sys.m68k
Xnet.micro.apple	comp.sys.apple
Xnet.micro.amiga	comp.sys.amiga
Xnet.micro.atari16	comp.sys.atari.st
Xnet.micro.atari8	comp.sys.atari.8bit
Xnet.micro.att	comp.sys.att
Xnet.micro.cbm	comp.sys.cbm
Xnet.micro.cpm	comp.os.cpm
Xnet.micro.hp	comp.sys.hp
Xnet.micro.mac	comp.sys.mac
Xnet.micro.ns32k	comp.sys.nsc.32k
Xnet.micro.pc	comp.sys.ibm.pc
Xnet.micro.ti	comp.sys.ti
Xnet.micro.trs-80	comp.sys.tandy
Xnet.news	news.misc
Xnet.news.adm	news.admin
Xnet.news.b	news.software.b
Xnet.news.config	news.config
Xnet.news.group	news.groups
Xnet.news.newsite	news.newsites
Xnet.news.notes	news.software.notes
Xnet.news.sa	news.sysadmin
Xnet.news.stargate	news.stargate
Xnet.periphs	comp.periphs
Xnet.sources.d	comp.sources.d
Xnet.text	comp.text
Xnet.unix	comp.unix.questions
Xnet.unix-wizards	comp.unix.wizards
Xnet.usenix	comp.org.usenix
Xnet.wanted.sources	comp.sources.wanted
Xnet.chess		rec.games.chess
Xnet.trivia		rec.games.trivia
Xnet.rec.radio		rec.ham-radio
Xnet.term		comp.terminals
Xnet.joke		rec.humor
Xnet.vlsi		comp.lsi
Xnet.micro.16k		comp.sys.nsc.32k
Xnet.music.gdea		rec.music.gdead
Xnet.notes		news.software.notes
Xnet.periph		comp.periphs
Xnet.puzzles		rec.puzzles
Xnet.unix.wizards	comp.unix.wizards
Xnet.sources.wanted	comp.sources.wanted
Xnet.consumers		misc.consumers
Xnet.consumers.house	misc.consumers.house
Xnet.house		misc.consumers.house
Xna.forsale		misc.forsale
Xnet.forsale		misc.forsale
Xnet.politics.terror	misc.headlines
Xnet.invest		misc.invest
Xnet.jobs		misc.jobs
Xnet.kids		misc.kids
Xmod.legal		misc.legal
Xnet.legal		misc.legal
Xnet.followup		misc.misc
Xnet.general		misc.misc
Xnet.misc		misc.misc
Xnet.suicide		misc.misc
Xnet.taxes		misc.taxes
Xmod.test		misc.test
Xnet.test		misc.test
Xnet.wanted		misc.wanted
Xnet.announce		mod.announce
Xnet.announce.newusers	mod.announce.newusers
Xmod.map.uucp		mod.map
Xnet.religion.christian	mod.religion.christian
Xnet.religion.xian	mod.religion.christian
Xnet.astro		sci.astro
Xnet.astro.expert	sci.astro
Xnet.bio			sci.bio
Xnet.crypt		sci.crypt
Xnet.analog		sci.electronics
Xnet.nlang		sci.lang
Xnet.math		sci.math
Xnet.stat		sci.math.stat
Xnet.math.stat		sci.math.stat
Xnet.math.symbolic	sci.math.symbolic
Xnet.med			sci.med
Xnet.sci			sci.misc
Xnet.physics		sci.physics
Xnet.research		sci.research
Xnet.space		sci.space
Xnet.columbia		sci.space.shuttle
Xnet.challenger		sci.space.shuttle
Xnet.college		soc.college
Xnet.nlang.africa	soc.culture.african
Xnet.nlang.celts		soc.culture.celtic
Xnet.nlang.greek		soc.culture.greek
Xnet.nlang.india		soc.culture.indian
Xnet.religion.jewish	soc.culture.jewish
Xnet.social		soc.misc
Xmod.motss		soc.motss
Xnet.motss		soc.motss
Xnet.net-people		soc.net-people
Xnet.roots		soc.roots
Xnet.singles		soc.singles
Xnet.women		soc.women
Xnet.abortion		talk.abortion
Xnet.bizarre		talk.bizarre
Xnet.origins		talk.origins
Xnet.philosophy		talk.philosophy.misc
Xnet.politics		talk.politics.misc
Xnet.politics.theory	talk.politics.theory
Xnet.religion		talk.religion.misc
Xtalk.religion		talk.religion.misc
Xnet.rumor		talk.rumors
Xtalk.rumor		talk.rumors
Xrec.skydive		rec.skydiving
Xcomp.sources.games	net.sources.games
Xcomp.sources.bugs	net.sources.bugs
Xcomp.sources.unix	net.sources
Xcomp.sources.mac	net.sources.mac
XEOF
X: if no aliases file, make one
Xif test ! -f $LIBDIR/aliases
Xthen
X	mv $LIBDIR/aliases.new $LIBDIR/aliases
Xelse
X	: see whats missing
X	sort $LIBDIR/aliases | sed -e 's/  */	/g'  -e 's/		*/	/g' >/tmp/$$aliases
X	sort $LIBDIR/aliases.new | sed -e 's/  */	/g'  -e 's/		*/	/g' >/tmp/$$aliases.new
X	comm -23 /tmp/$$aliases.new /tmp/$$aliases >/tmp/$$comm
X	if test -s /tmp/$$comm
X	then
X		echo "The following suggested aliases are missing or incorrect in your"
X		echo "$LIBDIR/aliases file. It is suggested you add them."
X		echo ""
X		cat /tmp/$$comm
X		echo ""
X		echo "A suggested aliases file has been left in $LIBDIR/aliases.new"
X		echo "for your convenience."
X		rm /tmp/$$comm /tmp/$$aliases
X	else
X		rm /tmp/$$comm /tmp/$$aliases $LIBDIR/aliases.new
X	fi
Xfi
X
X: if no distributions file, make one
Xif test ! -f $LIBDIR/distributions
Xthen
X	cat >$LIBDIR/distributions <<EOF
Xlocal		Local to this site
Xregional	Everywhere in this general area
Xusa		Everywhere in the USA
Xna		Everywhere in North America
Xworld		Everywhere on Usenet in the world
XEOF
Xecho
Xecho You may want to add distributions to $LIBDIR/distributions if your
Xecho site particpates in a regional distribution such as '"ba"' or '"dc"'.
Xfi
X
Xchown $NEWSUSR $LIBDIR/[a-z]*
Xchgrp $NEWSGRP $LIBDIR/[a-z]*
X
Xecho
Xecho Reminder: uux must permit rnews if running over uucp.
Xrm -f /tmp/$$*
END_OF_FILE
if test 9680 -ne `wc -c <'install.sh'`; then
    echo shar: \"'install.sh'\" unpacked with wrong size!
fi
# end of 'install.sh'
fi
if test -f 'recnews.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'recnews.c'\"
else
echo shar: Extracting \"'recnews.c'\" \(8212 characters\)
sed "s/^X//" >'recnews.c' <<'END_OF_FILE'
X/*
X * recnews [to newsgroup] [from user]
X *
X * Process a news article which has been mailed to some group like msgs.
X * Such articles are in normal mail format and have never seen the insides
X * of netnews.  If the "to newsgroup" is included, the article is posted
X * to this newsgroup instead of trying to intuit it from the headers.
X * If the "from user" is included, the return address is forged to look
X * like that user instead of what getuid or a from line says.
X *
X * It is recommended that you always include the to newsgroup, since the
X * intuition code is flakey and out of date.  The from user is probably
X * appropriate for arpanet mailing lists being funnelled at ucbvax but
X * not otherwise.  Sample lines in /usr/lib/aliases (if you run delivermail):
X *	worldnews: "|/usr/lib/news/recnews net.general"
X *		Allows you to mail to worldnews rather than using inews.
X *		Intended for humans to mail to.
X *	post-unix-wizards: "|/usr/lib/news/recnews fa.unix-wizards unix-wizards"
X *		Causes mail to post-unix-wizards to be fed into fa.unix-wizards
X *		and the return address forged as unix-wizards on the local
X *		machine.  post-unix-wizards (on the local machine) should
X *		be part of the master mailing list somewhere (on a different
X *		machine.)
X *
X * Recnews is primarily useful in remote places on the usenet which collect
X * mail from mailing lists and funnel them into the network.  It is also
X * useful if you like to send mail to some user instead of invoking
X * inews -t .. -n .. when you want to submit an article.  (Many mailers give
X * you nice facilities like editing the message.)  It is not, however,
X * essential to use recnews to be able to join usenet.
X *
X * WARNING: recnews disables the "recording" check - it has to because
X * by the time inews is run, it's in the background and too late to
X * ask permission.  If you depend heavily on recordings you probably
X * should not allow recnews (and thus the mail interface) to be used.
X*
X * 1) We leave the from line alone.  Just escape the double quotes, but let the
X *    mailer do the rest.
X * 2) We give precedence to "From:" over "From " or ">From " in determining
X *    who the article is really from.
X *    Modifications by rad@tek
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)recnews.c	2.13	10/23/86";
X#endif /* SCCSID */
X
X#include "defs.h"
X
X#include <stdio.h>
X#include <ctype.h>
X
X/*
X * Note: we assume there are 2 kinds of hosts using recnews:
X * Those that have delivermail (and hence this program will never
X * have to deal with more than one message at a time) and those on the arpanet
X * that do not (and hence all messages end with a sentinel).  It is
X * supposed that regular v7 type systems without delivermail or some
X * other automatic forwarding device will just use rnews.  We do
X * not attempt to tell where a message ends on all systems due to the
X * different conventions in effect.  (This COULD be fixed, I suppose.)
X */
X
X/*
X * Kinds of lines in a message.
X */
X#define FROM	001		/* From line */
X#define SUBJ	002		/* Subject */
X#define TO	003		/* To (newgroup based on this) */
X#define BLANK	004		/* blank line */
X#define EOM	005		/* End of message (4 ctrl A's) */
X#define HEADER	006		/* any unrecognized header */
X#define TEXT	007		/* anything unrecognized */
X#define INCLUSIVE 010		/* newsgroup is already in header */
X
X/*
X * Possible states program can be in.
X */
X#define SKIPPING	0100	/* In header of message */
X#define READING		0200	/* In body of message */
X
X#define BFSZ 250
X
X#define EOT	'\004'
X
Xchar	from[BFSZ];		/* mailing address for replies */
Xchar	sender[BFSZ];		/* mailing address of author, if different */
Xchar	to[BFSZ];		/* Destination of mail (msgs, etc) */
Xchar	subject[BFSZ];		/* subject of message */
Xchar	newsgroup[BFSZ];	/* newsgroups of message */
Xint	fromset;		/* from passed on command line */
Xchar	cmdbuf[BFSZ];		/* command to popen */
X
Xextern	char	*strcat(), *strcpy();
Xextern	FILE	*popen();
Xchar	*any();
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	char buf[BFSZ], inews[BFSZ];
X	register char *p, *q;
X	register FILE *pipe = NULL;
X	register int state;
X
X	/* build inews command */
X#ifdef IHCC
X	sprintf(inews, "%s/%s/%s", logdir(HOME), LIBDIR, "inews");
X#else
X	sprintf(inews, "%s/%s", LIBDIR, "inews");
X#endif
X
X	if (argc > 1)
X		strcpy(to, argv[1]);
X	if (argc > 2)
X		strcpy(from, argv[2]);
X
X	/*
X	 * Flag that we know who message is from to avoid trying to 
X	 * decipher the From line.
X	 */
X	if (argc > 2 && (argv[2][0] != '\0'))
X		fromset++;
X
X#ifdef debug
X	printf("argv[0] is <%s>, argv[1] is <%s>, argv[2] is <%s>\n",
X		argv[0], argv[1], argv[2]);
X#endif
X	state = SKIPPING;
X	while (fgets(buf, BFSZ, stdin) != NULL) {
X		if (state == READING) {
X			fputs(buf,pipe);
X			continue;
X		}
X		switch (type(buf)) {
X
X		case FROM:
X			frombreak(buf, from);
X			break;
X
X		case SUBJ:
X			p = any(buf, " \t");
X			if (p == NULL)
X				p = buf + 8;
X			q = subject;
X			while (*++p) {
X				if (*p == '"')
X					*q++ = '\\';
X				*q++ = *p;
X			}
X			q[-1] = '\0';
X			break;
X
X		case TO:
X			if (to[0])
X				break;		/* already have one */
X			p = any(buf, " \t");
X			if (p == NULL)
X				p = buf + 3;
X			q = to;
X			while (*++p) {
X				if (*p == '"')
X					*q++ = '\\';
X				*q++ = *p;
X			}
X			q[-1] = '\0';
X			break;
X
X		case INCLUSIVE:
X			sprintf(cmdbuf,"exec %s -p", inews);
X			pipe = popen(cmdbuf,"w");
X			if (pipe == NULL){
X				perror("recnews: open failed");
X				exit(1);
X			}
X			state = READING;
X			fputs(buf,pipe);
X			break;
X			
X		/*
X		 * Kludge to compensate for messages without real headers
X		 */
X		case HEADER:
X			break;
X
X		case BLANK:
X			state = READING;
X			strcpy(newsgroup, to);
X			sprintf(cmdbuf, "exec %s -t \"%s\" -n \"%s\" -f \"%s\"",
X				inews, *subject ? subject : "(none)",
X				newsgroup, from);
X#ifdef debug
X			pipe = stdout;
X			printf("BLANK: %s\n", cmdbuf);
X#else
X			pipe = popen(cmdbuf, "w");
X			if (pipe == NULL) {
X				perror("recnews: popen failed");
X				exit(1);
X			}
X#endif
X			if (sender[0]) {
X				fputs(sender, pipe);
X				putc('\n', pipe);
X			}
X			break;
X
X		case TEXT:
X			strcpy(newsgroup, to);
X			state = READING;
X			if (subject[0] == 0) {
X				strcpy(subject, buf);
X				if (subject[strlen(subject)-1] == '\n')
X					subject[strlen(subject)-1] = '\0';
X			}
X			sprintf(cmdbuf, "exec \"%s\" -t \"%s\" -n \"%s\" -f \"%s\"",
X				inews, subject, newsgroup, from);
X#ifdef debug
X			pipe = stdout;
X			printf("TEXT: %s\n", cmdbuf);
X#else
X			pipe = popen(cmdbuf, "w");
X			if (pipe == NULL) {
X				perror("pipe failed");
X				exit(1);
X			}
X#endif
X			if (sender[0]){
X				fputs(sender, pipe);
X				putc('\n',pipe);
X			}
X			break;
X		}
X	}
X	exit(0);
X}
X
Xtype(p)
Xregister char *p;
X{
X	char *firstbl;
X	static char lasthdr = 1;		/* prev line was a header */
X
X	if ((*p == ' ' || *p == '\t') && lasthdr)
X		return HEADER;		/* continuation line */
X	firstbl = any(p, " \t");
X	while (*p == ' ' || *p == '?' || *p == '\t')
X		++p;
X
X	if (*p == '\n' || *p == 0)
X		return BLANK;
X	if (strncmp(p, ">From", 5) == 0 || strncmp(p, "From", 4) == 0)
X		return FROM;
X	if (strncmp(p, "Subj", 4)==0 || strncmp(p, "Re:", 3)==0 ||
X		strncmp(p, "re:", 3)==0)
X		return SUBJ;
X	if (strncmp(p, "To", 2)==0)
X		return TO;
X	if (strncmp(p, "\1\1\1\1", 4)==0)
X		return EOM;
X	if (firstbl && firstbl[-1] == ':' && isalpha(*p))
X		return HEADER;
X	lasthdr = 0;
X	return TEXT;
X}
X
X/*
X * Figure out who a message is from.
X */
Xfrombreak(buf, fbuf)
Xregister char *buf, *fbuf;
X{
X	register char *p, *q;
X
X	if (fbuf[0] && fromset) {	/* we already know who it's from */
X		if (sender[0] == 0 || buf[4] == ':') {
X#ifdef debug
X			printf("sender set to: %s", buf);
X#endif
X			strcpy(sender, buf);
X		}
X		return;
X	}
X	/*
X	 * Leave fancy Froms alone - this parsing is done by mail
X	 * Just quote the double quotes to prevent interpetation 
X	 * by the shell.
X	 * rad@tek
X	 */
X	p = any(buf, " \t");
X	if (p==NULL)
X		p = buf + 4;
X	q = fbuf;
X	while (*++p) {
X		if (*p == '"')
X			*q++ = '\\';
X		*q++ = *p;
X	}
X	q[-1] = '\0';
X	if ((p=(char *)index(fbuf,'\n')) != NULL)
X		*p = '\0';
X	if (buf[4] == ':')
X		fromset++;
X}
X
X/*
X * Return the ptr in sp at which a character in sq appears;
X * NULL if not found
X *
X */
Xchar *
Xany(sp, sq)
Xchar *sp, *sq;
X{
X	register c1, c2;
X	register char *q;
X
X	while (c1 = *sp++) {
X		q = sq;
X		while (c2 = *q++)
X			if (c1 == c2)
X				return(--sp);
X	}
X	return(NULL);
X}
END_OF_FILE
if test 8212 -ne `wc -c <'recnews.c'`; then
    echo shar: \"'recnews.c'\" unpacked with wrong size!
fi
# end of 'recnews.c'
fi
echo shar: End of shell archive.
exit 0
-- 
		"Zeta Microcomputer Software"
ACSnet:    nick@ultima.cs.uts.oz
UUCP:      ...!uunet!munnari!ultima.cs.uts.oz!nick
Fidonet:   Nick Andrew on 3:713/602 (Zeta)

nick@ultima.cs.uts.oz (Nick Andrew) (12/07/89)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  Makefile.minix Makefile readnews.c digest.c unbatch.c
#   recmail.c defs.minix
# Wrapped by nick@nswitgould on Thu Dec  7 22:42:01 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Makefile.minix' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile.minix'\"
else
echo shar: Extracting \"'Makefile.minix'\" \(8385 characters\)
sed "s/^X//" >'Makefile.minix' <<'END_OF_FILE'
X# '@(#)Makefile.minix	1.23	3/23/87'
X# Makefile for Minix.
X# This is converted to USG/v7/etc by localize.sh
X# which should at least be a copy of localize.v7 or localize.usg
X
X# definitions
X
XOSTYPE = v7
X
X# HOME is the user name whose home dir has all the news stuff in it.
XHOME=	news
X# Use the -DHOME line if you want dynamic lookup in /etc/passwd
X#HOMENAME=	-DHOME=\"$(HOME)\"
XHOMENAME=
X
XNEWSUSR = news
XNEWSGRP = news
XSPOOLDIR = /usr/spool/news
XBATCHDIR = /usr/spool/batch
XLIBDIR = /usr/lib/news
XBINDIR = /usr/local/bin
XDESTDIR =
XUUXFLAGS = -r -z -n -gd
X
XLNRNEWS = ln
XDEBUG =
XSCCSID = -DSCCSID
XDEFS =	-DRNEWS=\"$(BINDIR)/rnews\" -DSPOOLDIR=\"$(SPOOLDIR)\" \
X	-DBATCHDIR=\"$(BATCHDIR)\" -DLIBDIR=\"$(LIBDIR)\" \
X	-DBINDIR=\"$(BINDIR)\" -DNEWSUSR=\"$(NEWSUSR)\" \
X	-DNEWSGRP=\"$(NEWSGRP)\"
X
X# Order is important here
XINSCOMPRESS=
XCOMPRESS= compress
X
XVFORK=-Dvfork=fork
X
X
X# Originally CFLAGS = ${DEBUG} -O -DDBM ${DEFS} ${SCCSID} ${VFORK}
XCFLAGS = ${DEBUG} -O -DMINIX ${DEFS} ${SCCSID} ${VFORK}
XLFLAGS = ${DEBUG} -i
XSRCHLIBS = -lcio
X# originally: LIBS = -ldbm
XLIBS =
XLINTFLAGS = -chba -DDBM ${DEFS}
X
XTERMLIB = -ltermlib
XFIXACTIVE =
X
X
XMISC = uname.s ftime.s
XOBJECTS = funcs.s funcs2.s getdate.s header.s ndir.s $(MISC)
XIOBJECTS = inews.s ifuncs.s iextern.s control.s fullname.s \
X	ipathinit.s $(OBJECTS)
X
XPOBJECTS = postnews.s rextern.s rpathinit.s funcs.s $(MISC)
XROBJECTS = readnews.s rfuncs.s rfuncs2.s rextern.s readr.s \
X	process.s rpathinit.s digest.s $(OBJECTS)
XVOBJECTS = readnews.s rfuncs.s rfuncs2.s rextern.s process.s rpathinit.s \
X	$(OBJECTS) visual.s virtterm.s
XEXPOBJS = expire.s header.s funcs.s getdate.s iextern.s epathinit.s \
X	funcs2.s ndir.s $(MISC)
X
XSRCS = funcs.c funcs2.c header.c
XISRCS = inews.c ifuncs.c iextern.c control.c fullname.c $(SRCS)
XPSRCS = postnews.c rextern.c funcs.c
XRSRCS = readnews.c rfuncs.c rfuncs2.c rextern.c readr.c process.c  \
X	digest.c $(SRCS)
XVSRCS = readnews.c rfuncs.c rfuncs2.c rextern.c process.c $(SRCS) \
X	visual.c virtterm.c
XESRCS = expire.c header.c funcs.c iextern.c \
X	funcs2.c
XOSRCS = uurec.c recnews.c sendnews.c batch.c unbatch.c \
X	recmail.c compress.c
X
XUTILS = inews uurec recnews sendnews expire batch unbatch recmail \
X	encode decode $(COMPRESS)
XSCRIPTS = sendbatch rmgroup checkgroups
XOTHERS = $(UTILS) $(SCRIPTS)
X
X# Cannot get readnews to work due to asld bug
X# Cannot get vnews to work due to being too lazy
X#COMMANDS = readnews checknews postnews vnews
XCOMMANDS = checknews postnews
X
X# dependencies
Xall: $(OTHERS) $(COMMANDS)
X
Xinstall: all help vnews.help
X	-mkdir $(DESTDIR)$(BINDIR)
X	-mkdir $(DESTDIR)$(LIBDIR)
X	cp $(COMMANDS) $(DESTDIR)$(BINDIR)
X	-cd $(DESTDIR)$(BINDIR); \
X		chown $(NEWSUSR) $(COMMANDS); \
X		chgrp $(NEWSGRP) $(COMMANDS); \
X		chmod 755 $(COMMANDS)
X	cp help vnews.help $(OTHERS) $(DESTDIR)$(LIBDIR)
X	cd $(DESTDIR)$(LIBDIR); \
X		chown $(NEWSUSR) $(OTHERS); \
X		chgrp $(NEWSGRP) $(OTHERS); \
X		chmod 755 $(OTHERS)
X	-rm -f $(DESTDIR)$(BINDIR)/rnews $(DESTDIR)$(BINDIR)/inews
X	${LNRNEWS} $(DESTDIR)$(LIBDIR)/inews $(DESTDIR)$(BINDIR)/rnews
X	chown $(NEWSUSR) $(DESTDIR)$(LIBDIR)/inews
X	chgrp $(NEWSGRP) $(DESTDIR)$(LIBDIR)/inews
X	chmod 6755 $(DESTDIR)$(LIBDIR)/inews $(DESTDIR)$(BINDIR)/rnews
X	$(INSCOMPRESS)
X
Xbindir:	$(COMMANDS)
X	cp $(COMMANDS) $(DESTDIR)$(BINDIR)
X	-cd $(DESTDIR)$(BINDIR); \
X		chown $(NEWSUSR) $(COMMANDS); \
X		chgrp $(NEWSGRP) $(COMMANDS); \
X		chmod 755 $(COMMANDS)
X
X
X# defs.h:	defs.dist localize.sh
X# 	sh localize.sh
X#	@echo Localize has been run. Restart the make.
X#	@exit 1
X
X# Makefile: localize.sh Makefile.dst
X# 	sh localize.sh
X#	@echo Localize has been run. Restart the make.
X#	@exit 1
X
Xupdate: install.sh makeactive.sh
X	sh install.sh $(SPOOLDIR) $(LIBDIR) $(BINDIR) $(NEWSUSR) $(NEWSGRP) $(OSTYPE)
X	chmod 6755 $(LIBDIR)/inews
X
Xclean:
X	rm -f $(COMMANDS) $(OTHERS) *.s a.out
X	rm -f core index errs
X
Xinews:  $(IOBJECTS)
X	$(CC) -T. $(LFLAGS) $(IOBJECTS) $(SRCHLIBS) -o inews $(LIBS)  
X	cp inews $(LIBDIR)
X
Xreadnews:  $(ROBJECTS)
X	$(CC) $(LFLAGS) $(ROBJECTS) $(SRCHLIBS) -o readnews $(LIBS)
X
Xfuncs.s:  funcs.c params.h defs.h header.h
X	$(CC) $(CFLAGS) -c funcs.c
X
Xfuncs2.s:  funcs2.c params.h defs.h header.h
X	$(CC) $(CFLAGS) -c funcs2.c
X
X# getdate.s:  getdate.c defs.h
X# 	@echo "expect 8 shift/reduce conflicts"
X# 	@echo yacc getdate.y
X# 	@echo mv y.tab.c getdate.c
X# 	$(CC) $(CFLAGS) -c getdate.c
X# 	@echo -rm -f getdate.c
X
Xinews.s:  inews.c iparams.h defs.h params.h header.h
X	$(CC) $(CFLAGS) -c inews.c
X
Xifuncs.s:  ifuncs.c iparams.h defs.h params.h header.h
X	$(CC) $(CFLAGS) $(HOMENAME) -c ifuncs.c
X
Xiextern.s:  iextern.c iparams.h defs.h params.h header.h
X	$(CC) $(CFLAGS) $(HOMENAME) -c iextern.c
X
Xpostnews: $(POBJECTS)
X	$(CC) $(CFLAGS) $(LFLAGS) $(POBJECTS) $(SRCHLIBS) -o postnews
X
Xpostnews.s: postnews.c defs.h params.h header.h
X	$(CC) $(CFLAGS) -c postnews.c
X
Xreadnews.s:  readnews.c rparams.h defs.h params.h header.h
X	$(CC) $(CFLAGS) $(HOMENAME) -c readnews.c
X
Xrfuncs.s:  rfuncs.c rparams.h defs.h params.h header.h
X	$(CC) $(CFLAGS) -c rfuncs.c
X
Xrfuncs2.s:  rfuncs2.c rparams.h defs.h params.h header.h
X	$(CC) $(CFLAGS) -c rfuncs2.c
X
Xrextern.s:  rextern.c rparams.h defs.h params.h header.h
X	$(CC) $(CFLAGS) -c rextern.c
X
Xreadr.s:  readr.c rparams.h defs.h params.h ndir.h header.h
X	$(CC) $(CFLAGS) -c readr.c
X
Xchecknews.s:  checknews.c defs.h header.h params.h
X	$(CC) $(CFLAGS) -c checknews.c
X
Xvnews:	$(VOBJECTS)
X	$(CC) $(LFLAGS) $(VOBJECTS) $(TERMLIB) $(LIBS) $(SRCHLIBS) -o $@
X
Xvisual.s:  visual.c rparams.h defs.h params.h ndir.h header.h
X	$(CC) $(CFLAGS) -c visual.c
X
Xcontrol.s:  control.c defs.h iparams.h params.h header.h
X	$(CC) $(CFLAGS) -c control.c
X
Xlogdir.s: logdir.c
X	$(CC) $(CFLAGS) -c logdir.c
X
Xftime.s:  ftime.c
X	$(CC) $(CFLAGS) -c ftime.c
X
Xuname.s:  uname.c defs.h params.h header.h
X	$(CC) $(CFLAGS) -c uname.c
X
Xndir.s: ndir.c ndir.h defs.h
X	$(CC) $(CFLAGS) -c ndir.c
X
Xuurec:  uurec.c defs.h
X	$(CC) $(CFLAGS) $(LFLAGS) uurec.c $(SRCHLIBS) -o uurec
X
Xrecnews:  recnews.c defs.h header.h
X	$(CC) $(CFLAGS) $(LFLAGS) recnews.c $(SRCHLIBS) -o recnews
X
Xsendnews:  sendnews.s
X	$(CC) $(LFLAGS) sendnews.s $(SRCHLIBS) -o sendnews
X
Xbatch:  batch.c defs.h
X	$(CC) $(CFLAGS) $(LFLAGS) batch.c $(SRCHLIBS) -o batch
X	chmem =6144 batch
X
Xunbatch:  unbatch.c
X	$(CC) $(CFLAGS) $(LFLAGS) unbatch.c $(SRCHLIBS) -o unbatch
X	chmem =6144 unbatch
X
Xencode:	encode.c
X	$(CC) $(CFLAGS) $(LFLAGS) encode.c $(SRCHLIBS) -o encode
X
Xdecode:	decode.c
X	$(CC) $(CFLAGS) $(LFLAGS) decode.c $(SRCHLIBS) -o decode
X
X# caesar:  caesar.c
X# 	$(CC) $(CFLAGS) $(LFLAGS) caesar.c $(SRCHLIBS) -o caesar -lm
X
Xcompress: compress.c
X	$(CC) $(CFLAGS) $(LFLAGS) $(SRCHLIBS) -o compress compress.c
X
Xrecmail:  recmail.c defs.h params.h header.h
X	$(CC) $(CFLAGS) $(LFLAGS) recmail.c $(SRCHLIBS) -o recmail
X
Xprocess.s:  process.c rparams.h defs.h params.h header.h
X	$(CC) $(CFLAGS) -c process.c
X
Xchecknews:  checknews.s process.s cpathinit.s rextern.s
X	$(CC) $(LFLAGS) checknews.s process.s cpathinit.s rextern.s $(SRCHLIBS) -o checknews
X
Xsendbatch: sendbatch.sh
X	sed -e "s%LIBDIR%$(LIBDIR)%g" \
X	 -e "s%UUXFLAGS%$(UUXFLAGS)%g" \
X	 -e "s%BATCHDIR%$(BATCHDIR)%g" sendbatch.sh > sendbatch
X
Xrmgroup: rmgroup.sh
X	sed -e "s%LIBDIR%$(LIBDIR)%g" \
X	 -e "s%FIXACTIVE%$(FIXACTIVE)%g" \
X	 -e "s%SPOOLDIR%$(SPOOLDIR)%g" rmgroup.sh > rmgroup
X
Xcheckgroups: checkgroups.sh
X	sed -e "s%LIBDIR%$(LIBDIR)%g" checkgroups.sh > checkgroups
X
Xsendnews.s: sendnews.c defs.h
X	$(CC) $(CFLAGS) -c sendnews.c
X
Xfullname.s: fullname.c defs.h params.h header.h
X	$(CC) $(CFLAGS) -c fullname.c
X
Xexpire: $(EXPOBJS)
X	$(CC) $(LFLAGS) $(EXPOBJS) $(SRCHLIBS) -o expire $(LIBS)
X
Xheader.s:  header.c header.h defs.h patchlevel.h params.h
X	$(CC) $(CFLAGS) -c header.c
X
Xexpire.s:  expire.c defs.h params.h ndir.h header.h
X	$(CC) $(CFLAGS) -c expire.c
X
Xdigest.s:  digest.c rparams.h
X	$(CC) -DMINIX -c digest.c
X
X# Some silliness here to get pathinit for both readnews & inews
Xrpathinit.s:  pathinit.c rparams.h header.h params.h defs.h
X	$(CC) $(CFLAGS) $(HOMENAME) -DREAD -c pathinit.c
X	mv pathinit.s rpathinit.s
X
Xipathinit.s:  pathinit.c iparams.h header.h params.h defs.h
X	$(CC) $(CFLAGS) $(HOMENAME) -DINEW -c pathinit.c
X	mv pathinit.s ipathinit.s
X
Xcpathinit.s:  pathinit.c iparams.h header.h params.h defs.h
X	$(CC) $(CFLAGS) $(HOMENAME) -DCHKN -c pathinit.c
X	mv pathinit.s cpathinit.s
X
Xepathinit.s:  pathinit.c iparams.h header.h params.h defs.h
X	$(CC) $(CFLAGS) $(HOMENAME) -DEXP -c pathinit.c
X	mv pathinit.s epathinit.s
X
Xtags:	/tmp
X	ctags -w *.h *.c
X
END_OF_FILE
if test 8385 -ne `wc -c <'Makefile.minix'`; then
    echo shar: \"'Makefile.minix'\" unpacked with wrong size!
fi
# end of 'Makefile.minix'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(8385 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X# '@(#)Makefile.minix	1.23	3/23/87'
X# Makefile for Minix.
X# This is converted to USG/v7/etc by localize.sh
X# which should at least be a copy of localize.v7 or localize.usg
X
X# definitions
X
XOSTYPE = v7
X
X# HOME is the user name whose home dir has all the news stuff in it.
XHOME=	news
X# Use the -DHOME line if you want dynamic lookup in /etc/passwd
X#HOMENAME=	-DHOME=\"$(HOME)\"
XHOMENAME=
X
XNEWSUSR = news
XNEWSGRP = news
XSPOOLDIR = /usr/spool/news
XBATCHDIR = /usr/spool/batch
XLIBDIR = /usr/lib/news
XBINDIR = /usr/local/bin
XDESTDIR =
XUUXFLAGS = -r -z -n -gd
X
XLNRNEWS = ln
XDEBUG =
XSCCSID = -DSCCSID
XDEFS =	-DRNEWS=\"$(BINDIR)/rnews\" -DSPOOLDIR=\"$(SPOOLDIR)\" \
X	-DBATCHDIR=\"$(BATCHDIR)\" -DLIBDIR=\"$(LIBDIR)\" \
X	-DBINDIR=\"$(BINDIR)\" -DNEWSUSR=\"$(NEWSUSR)\" \
X	-DNEWSGRP=\"$(NEWSGRP)\"
X
X# Order is important here
XINSCOMPRESS=
XCOMPRESS= compress
X
XVFORK=-Dvfork=fork
X
X
X# Originally CFLAGS = ${DEBUG} -O -DDBM ${DEFS} ${SCCSID} ${VFORK}
XCFLAGS = ${DEBUG} -O -DMINIX ${DEFS} ${SCCSID} ${VFORK}
XLFLAGS = ${DEBUG} -i
XSRCHLIBS = -lcio
X# originally: LIBS = -ldbm
XLIBS =
XLINTFLAGS = -chba -DDBM ${DEFS}
X
XTERMLIB = -ltermlib
XFIXACTIVE =
X
X
XMISC = uname.s ftime.s
XOBJECTS = funcs.s funcs2.s getdate.s header.s ndir.s $(MISC)
XIOBJECTS = inews.s ifuncs.s iextern.s control.s fullname.s \
X	ipathinit.s $(OBJECTS)
X
XPOBJECTS = postnews.s rextern.s rpathinit.s funcs.s $(MISC)
XROBJECTS = readnews.s rfuncs.s rfuncs2.s rextern.s readr.s \
X	process.s rpathinit.s digest.s $(OBJECTS)
XVOBJECTS = readnews.s rfuncs.s rfuncs2.s rextern.s process.s rpathinit.s \
X	$(OBJECTS) visual.s virtterm.s
XEXPOBJS = expire.s header.s funcs.s getdate.s iextern.s epathinit.s \
X	funcs2.s ndir.s $(MISC)
X
XSRCS = funcs.c funcs2.c header.c
XISRCS = inews.c ifuncs.c iextern.c control.c fullname.c $(SRCS)
XPSRCS = postnews.c rextern.c funcs.c
XRSRCS = readnews.c rfuncs.c rfuncs2.c rextern.c readr.c process.c  \
X	digest.c $(SRCS)
XVSRCS = readnews.c rfuncs.c rfuncs2.c rextern.c process.c $(SRCS) \
X	visual.c virtterm.c
XESRCS = expire.c header.c funcs.c iextern.c \
X	funcs2.c
XOSRCS = uurec.c recnews.c sendnews.c batch.c unbatch.c \
X	recmail.c compress.c
X
XUTILS = inews uurec recnews sendnews expire batch unbatch recmail \
X	encode decode $(COMPRESS)
XSCRIPTS = sendbatch rmgroup checkgroups
XOTHERS = $(UTILS) $(SCRIPTS)
X
X# Cannot get readnews to work due to asld bug
X# Cannot get vnews to work due to being too lazy
X#COMMANDS = readnews checknews postnews vnews
XCOMMANDS = checknews postnews
X
X# dependencies
Xall: $(OTHERS) $(COMMANDS)
X
Xinstall: all help vnews.help
X	-mkdir $(DESTDIR)$(BINDIR)
X	-mkdir $(DESTDIR)$(LIBDIR)
X	cp $(COMMANDS) $(DESTDIR)$(BINDIR)
X	-cd $(DESTDIR)$(BINDIR); \
X		chown $(NEWSUSR) $(COMMANDS); \
X		chgrp $(NEWSGRP) $(COMMANDS); \
X		chmod 755 $(COMMANDS)
X	cp help vnews.help $(OTHERS) $(DESTDIR)$(LIBDIR)
X	cd $(DESTDIR)$(LIBDIR); \
X		chown $(NEWSUSR) $(OTHERS); \
X		chgrp $(NEWSGRP) $(OTHERS); \
X		chmod 755 $(OTHERS)
X	-rm -f $(DESTDIR)$(BINDIR)/rnews $(DESTDIR)$(BINDIR)/inews
X	${LNRNEWS} $(DESTDIR)$(LIBDIR)/inews $(DESTDIR)$(BINDIR)/rnews
X	chown $(NEWSUSR) $(DESTDIR)$(LIBDIR)/inews
X	chgrp $(NEWSGRP) $(DESTDIR)$(LIBDIR)/inews
X	chmod 6755 $(DESTDIR)$(LIBDIR)/inews $(DESTDIR)$(BINDIR)/rnews
X	$(INSCOMPRESS)
X
Xbindir:	$(COMMANDS)
X	cp $(COMMANDS) $(DESTDIR)$(BINDIR)
X	-cd $(DESTDIR)$(BINDIR); \
X		chown $(NEWSUSR) $(COMMANDS); \
X		chgrp $(NEWSGRP) $(COMMANDS); \
X		chmod 755 $(COMMANDS)
X
X
X# defs.h:	defs.dist localize.sh
X# 	sh localize.sh
X#	@echo Localize has been run. Restart the make.
X#	@exit 1
X
X# Makefile: localize.sh Makefile.dst
X# 	sh localize.sh
X#	@echo Localize has been run. Restart the make.
X#	@exit 1
X
Xupdate: install.sh makeactive.sh
X	sh install.sh $(SPOOLDIR) $(LIBDIR) $(BINDIR) $(NEWSUSR) $(NEWSGRP) $(OSTYPE)
X	chmod 6755 $(LIBDIR)/inews
X
Xclean:
X	rm -f $(COMMANDS) $(OTHERS) *.s a.out
X	rm -f core index errs
X
Xinews:  $(IOBJECTS)
X	$(CC) -T. $(LFLAGS) $(IOBJECTS) $(SRCHLIBS) -o inews $(LIBS)  
X	cp inews $(LIBDIR)
X
Xreadnews:  $(ROBJECTS)
X	$(CC) $(LFLAGS) $(ROBJECTS) $(SRCHLIBS) -o readnews $(LIBS)
X
Xfuncs.s:  funcs.c params.h defs.h header.h
X	$(CC) $(CFLAGS) -c funcs.c
X
Xfuncs2.s:  funcs2.c params.h defs.h header.h
X	$(CC) $(CFLAGS) -c funcs2.c
X
X# getdate.s:  getdate.c defs.h
X# 	@echo "expect 8 shift/reduce conflicts"
X# 	@echo yacc getdate.y
X# 	@echo mv y.tab.c getdate.c
X# 	$(CC) $(CFLAGS) -c getdate.c
X# 	@echo -rm -f getdate.c
X
Xinews.s:  inews.c iparams.h defs.h params.h header.h
X	$(CC) $(CFLAGS) -c inews.c
X
Xifuncs.s:  ifuncs.c iparams.h defs.h params.h header.h
X	$(CC) $(CFLAGS) $(HOMENAME) -c ifuncs.c
X
Xiextern.s:  iextern.c iparams.h defs.h params.h header.h
X	$(CC) $(CFLAGS) $(HOMENAME) -c iextern.c
X
Xpostnews: $(POBJECTS)
X	$(CC) $(CFLAGS) $(LFLAGS) $(POBJECTS) $(SRCHLIBS) -o postnews
X
Xpostnews.s: postnews.c defs.h params.h header.h
X	$(CC) $(CFLAGS) -c postnews.c
X
Xreadnews.s:  readnews.c rparams.h defs.h params.h header.h
X	$(CC) $(CFLAGS) $(HOMENAME) -c readnews.c
X
Xrfuncs.s:  rfuncs.c rparams.h defs.h params.h header.h
X	$(CC) $(CFLAGS) -c rfuncs.c
X
Xrfuncs2.s:  rfuncs2.c rparams.h defs.h params.h header.h
X	$(CC) $(CFLAGS) -c rfuncs2.c
X
Xrextern.s:  rextern.c rparams.h defs.h params.h header.h
X	$(CC) $(CFLAGS) -c rextern.c
X
Xreadr.s:  readr.c rparams.h defs.h params.h ndir.h header.h
X	$(CC) $(CFLAGS) -c readr.c
X
Xchecknews.s:  checknews.c defs.h header.h params.h
X	$(CC) $(CFLAGS) -c checknews.c
X
Xvnews:	$(VOBJECTS)
X	$(CC) $(LFLAGS) $(VOBJECTS) $(TERMLIB) $(LIBS) $(SRCHLIBS) -o $@
X
Xvisual.s:  visual.c rparams.h defs.h params.h ndir.h header.h
X	$(CC) $(CFLAGS) -c visual.c
X
Xcontrol.s:  control.c defs.h iparams.h params.h header.h
X	$(CC) $(CFLAGS) -c control.c
X
Xlogdir.s: logdir.c
X	$(CC) $(CFLAGS) -c logdir.c
X
Xftime.s:  ftime.c
X	$(CC) $(CFLAGS) -c ftime.c
X
Xuname.s:  uname.c defs.h params.h header.h
X	$(CC) $(CFLAGS) -c uname.c
X
Xndir.s: ndir.c ndir.h defs.h
X	$(CC) $(CFLAGS) -c ndir.c
X
Xuurec:  uurec.c defs.h
X	$(CC) $(CFLAGS) $(LFLAGS) uurec.c $(SRCHLIBS) -o uurec
X
Xrecnews:  recnews.c defs.h header.h
X	$(CC) $(CFLAGS) $(LFLAGS) recnews.c $(SRCHLIBS) -o recnews
X
Xsendnews:  sendnews.s
X	$(CC) $(LFLAGS) sendnews.s $(SRCHLIBS) -o sendnews
X
Xbatch:  batch.c defs.h
X	$(CC) $(CFLAGS) $(LFLAGS) batch.c $(SRCHLIBS) -o batch
X	chmem =6144 batch
X
Xunbatch:  unbatch.c
X	$(CC) $(CFLAGS) $(LFLAGS) unbatch.c $(SRCHLIBS) -o unbatch
X	chmem =6144 unbatch
X
Xencode:	encode.c
X	$(CC) $(CFLAGS) $(LFLAGS) encode.c $(SRCHLIBS) -o encode
X
Xdecode:	decode.c
X	$(CC) $(CFLAGS) $(LFLAGS) decode.c $(SRCHLIBS) -o decode
X
X# caesar:  caesar.c
X# 	$(CC) $(CFLAGS) $(LFLAGS) caesar.c $(SRCHLIBS) -o caesar -lm
X
Xcompress: compress.c
X	$(CC) $(CFLAGS) $(LFLAGS) $(SRCHLIBS) -o compress compress.c
X
Xrecmail:  recmail.c defs.h params.h header.h
X	$(CC) $(CFLAGS) $(LFLAGS) recmail.c $(SRCHLIBS) -o recmail
X
Xprocess.s:  process.c rparams.h defs.h params.h header.h
X	$(CC) $(CFLAGS) -c process.c
X
Xchecknews:  checknews.s process.s cpathinit.s rextern.s
X	$(CC) $(LFLAGS) checknews.s process.s cpathinit.s rextern.s $(SRCHLIBS) -o checknews
X
Xsendbatch: sendbatch.sh
X	sed -e "s%LIBDIR%$(LIBDIR)%g" \
X	 -e "s%UUXFLAGS%$(UUXFLAGS)%g" \
X	 -e "s%BATCHDIR%$(BATCHDIR)%g" sendbatch.sh > sendbatch
X
Xrmgroup: rmgroup.sh
X	sed -e "s%LIBDIR%$(LIBDIR)%g" \
X	 -e "s%FIXACTIVE%$(FIXACTIVE)%g" \
X	 -e "s%SPOOLDIR%$(SPOOLDIR)%g" rmgroup.sh > rmgroup
X
Xcheckgroups: checkgroups.sh
X	sed -e "s%LIBDIR%$(LIBDIR)%g" checkgroups.sh > checkgroups
X
Xsendnews.s: sendnews.c defs.h
X	$(CC) $(CFLAGS) -c sendnews.c
X
Xfullname.s: fullname.c defs.h params.h header.h
X	$(CC) $(CFLAGS) -c fullname.c
X
Xexpire: $(EXPOBJS)
X	$(CC) $(LFLAGS) $(EXPOBJS) $(SRCHLIBS) -o expire $(LIBS)
X
Xheader.s:  header.c header.h defs.h patchlevel.h params.h
X	$(CC) $(CFLAGS) -c header.c
X
Xexpire.s:  expire.c defs.h params.h ndir.h header.h
X	$(CC) $(CFLAGS) -c expire.c
X
Xdigest.s:  digest.c rparams.h
X	$(CC) -DMINIX -c digest.c
X
X# Some silliness here to get pathinit for both readnews & inews
Xrpathinit.s:  pathinit.c rparams.h header.h params.h defs.h
X	$(CC) $(CFLAGS) $(HOMENAME) -DREAD -c pathinit.c
X	mv pathinit.s rpathinit.s
X
Xipathinit.s:  pathinit.c iparams.h header.h params.h defs.h
X	$(CC) $(CFLAGS) $(HOMENAME) -DINEW -c pathinit.c
X	mv pathinit.s ipathinit.s
X
Xcpathinit.s:  pathinit.c iparams.h header.h params.h defs.h
X	$(CC) $(CFLAGS) $(HOMENAME) -DCHKN -c pathinit.c
X	mv pathinit.s cpathinit.s
X
Xepathinit.s:  pathinit.c iparams.h header.h params.h defs.h
X	$(CC) $(CFLAGS) $(HOMENAME) -DEXP -c pathinit.c
X	mv pathinit.s epathinit.s
X
Xtags:	/tmp
X	ctags -w *.h *.c
X
END_OF_FILE
if test 8385 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'readnews.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'readnews.c'\"
else
echo shar: Extracting \"'readnews.c'\" \(7970 characters\)
sed "s/^X//" >'readnews.c' <<'END_OF_FILE'
X/*
X * readnews - read news articles.
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)readnews.c	2.32	3/21/87";
X#endif /* SCCSID */
X
X#include "rparams.h"
X
X/*
X * readnews - article reading program
X */
X
X#ifndef SYSBUF
Xchar	SYSBUF[BUFSIZ];	/* to buffer std out */
X#endif
X
X#define OPTION	0	/* pick up an option string */
X#define STRING	1	/* pick up a string of arguments */
X
Xstruct timeb Now;
X
Xint onsig(), cleanup();
X
X/*
X *	Authors:
X *		Matt Glickman	ucbvax!glickman
X *		Mark Horton	cbosg!mark
X *		Stephen Daniels	duke!swd
X *		Tom Truscott	duke!trt
X */
X
Xmain(argc, argv)
Xint	argc;
Xregister char	**argv;
X{
X	register char	*ptr;	/* pointer to rest of buffer		*/
X	char	*user = NULL, *home = NULL;
X	int	optflag = FALSE, space = FALSE;
X	struct utsname ubuf;
X	char	*myrc;
X
X	/* set up defaults and initialize. */
X	pathinit();
X	mode = UNKNOWN;
X	header.title[0] = header.nbuf[0] = '\0';
X	coptbuf[0] = datebuf[0] = '\0';
X	uname(&ubuf);
X	strcpy(FROMSYSNAME, ubuf.nodename);
X
X	savmask = umask(N_UMASK);	/* set up mask */
X	uid = getuid();
X	gid = getgid();
X	duid = 0;
X	dgid = 0;
X	(void) ftime(&Now);
X
X	/* give reasonable error message if SPOOL directory
X	 * is unaccessable... usually means system administrator
X	 * has "turned off" news reading...
X	 */
X	if (access(SPOOL, 05))
X	{
X		fputs("News articles are not available at this time\n",stderr);
X		xxit(1);
X	}
X
X#ifndef SHELL
X	if ((SHELL = getenv("SHELL")) == NULL)
X		SHELL = "/bin/sh";
X#endif
X	/*
X	 * IHCC forces the use of 'getuser()' to prevent forgery of articles
X	 * by just changing $LOGNAME
X	 * Note that this shouldn't matter in readnews, since inews
X	 * does all the actual posting of news.
X	 */
X#ifndef IHCC
X	if ((user = getenv("USER")) == NULL)
X		user = getenv("LOGNAME");
X	if ((home = getenv("HOME")) == NULL)
X		home = getenv("LOGDIR");
X#endif /* ! IHCC */
X	if (user == NULL || home == NULL)
X		getuser();
X	else {
X		username = AllocCpy(user);
X		(void) strcpy(header.path, username);
X		userhome = AllocCpy(home);
X	}
X
X	if (!(MAILER = getenv("MAILER")))
X		MAILER = "mail";	/* was /bin/mail */
X
X#ifdef PAGE
X	if (myrc = getenv("PAGER"))
X		PAGER = AllocCpy(myrc);
X	else {
X# ifdef IHCC
X		(void) sprintf(bfr, "%s/bin/%s", logdir(HOME), PAGE);
X		PAGER = AllocCpy(bfr);
X# else /* !IHCC */
X		PAGER = PAGE;
X# endif /* !IHCC */
X	}
X#endif /* PAGE */
X
X	if (ptr = getenv("NEWSOPTS"))
X		(void) strcpy(rcbuf, ptr);
X	else
X		*rcbuf = '\0';
X	if (*rcbuf) {
X		(void) strcat(rcbuf, " \1");
X		ptr = rcbuf;
X		while (*++ptr)
X			if (isspace(*ptr))
X				*ptr = '\0';
X		for (ptr = rcbuf; ; ptr++) {
X			if (!*ptr)
X				continue;
X			if (*ptr == '\1')
X				break;
X			if (++line > LINES)
X				xerror("Too many options.");
X			if ((rcline[line] = malloc((unsigned)(strlen(ptr) + 1))) == NULL)
X				xerror("Not enough memory.");
X			argvrc[line] = rcline[line];
X			(void) strcpy(rcline[line], ptr);
X			while (*ptr)
X				ptr++;
X		}
X	}
X	myrc = getenv("NEWSRC");
X	if (myrc == NULL) {
X		myrc = NEWSRC;
X		(void) sprintf(newsrc, "%s/%s", userhome, myrc);
X	} else {
X		(void) strcpy(newsrc, myrc);
X	}
X	if (access(newsrc, 0))
X		newrc(newsrc);
X	if ((rcfp = fopen(newsrc, "r")) != NULL) {
X		rcreadok = FALSE;
X		while (fgets(rcbuf, LBUFLEN, rcfp) != NULL) {
X			if (!(space = isspace(*rcbuf)))
X				optflag = FALSE;
X			if (!strncmp(rcbuf, "options ", 8))
X				optflag = TRUE;
X			if (optflag) {
X				(void) strcat(rcbuf, "\1");
X				if (space)
X					ptr = rcbuf - 1;
X				else
X					ptr = &rcbuf[7];
X				while (*++ptr)
X					if (isspace(*ptr))
X						*ptr = '\0';
X				if (space)
X					ptr = rcbuf;
X				else
X					ptr = &rcbuf[8];
X				for (; ; ptr++) {
X					if (!*ptr)
X						continue;
X					if (*ptr == '\1')
X						break;
X					if (++line > LINES)
X						xerror("Too many options.");
X					if ((rcline[line] = malloc((unsigned)(strlen(ptr) + 1))) == NULL)
X						xerror("Not enough memory.");
X					argvrc[line] = rcline[line];
X					(void) strcpy(rcline[line], ptr);
X					while (*ptr)
X						ptr++;
X				}
X			}
X		}
X		(void) fclose(rcfp);
X		rcreadok = TRUE;
X	}
X	if (line != -1) {
X#ifdef DEBUG
X		register int i;
X		for (i = 0; i <= line; i++)
X			fprintf(stderr, "options:  %s\n", rcline[i]);
X#endif
X		process(line + 2, argvrc);
X		do {
X#ifdef DEBUG
X			fprintf(stderr, "Freeing %d\n", line);
X#endif
X			free(rcline[line]);
X		} while (line--);
X	}
X
X	argv++;
X	(void) strcat(header.nbuf, ADMSUB);
X	process(argc, argv);
X	if (!nflag)
X		(void) sprintf(header.nbuf, "%s,%s", ADMSUB, DFLTSUB);
X	else {
X		char *p = rindex(header.nbuf, ',');
X		if (p && p[1] == '\0')	/* strip of trailing NGDELIM */
X			*p ='\0';
X	}
X	lcase(header.nbuf);
X	makehimask(header.nbuf, "junk");
X	makehimask(header.nbuf, "control");
X	makehimask(header.nbuf, "test");
X
X	setbuf(stdout, SYSBUF);
X	SigTrap = FALSE;	/* true if a signal has been caught */
X	if (!pflag && !lflag && !eflag) {
X		(void) signal(SIGQUIT, SIG_IGN);
X		(void) signal(SIGHUP, cleanup);
X		(void) signal(SIGINT, onsig);
X		(void) signal(SIGPIPE, onsig);
X	} else {
X		int (* old)();
X		if ((old = signal(SIGQUIT, SIG_IGN)) != SIG_IGN)
X			(void) signal(SIGQUIT, cleanup);
X		if ((old = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
X			(void) signal(SIGHUP, cleanup);
X		if ((old = signal(SIGINT, SIG_IGN)) != SIG_IGN)
X			(void) signal(SIGINT, cleanup);
X	}
X
X	/*
X	 * ALL of the command line has now been processed. (!)
X	 */
X
X	if (!*header.nbuf)
X		strcpy(header.nbuf, DFLTSUB);
X	if (sflag) {
X		printf("Subscription list:  %s\n", header.nbuf);
X		xxit(0);
X	}
X	if (xflag)
X		line = -1;
X	rcfp = xfopen(newsrc, "r");
X	while (fgets(rcbuf, LBUFLEN, rcfp) != NULL) {
X		if (!nstrip(rcbuf))
X			xerror(".newsrc line too long");
X		if (++line >= LINES)
X			xerror("Too many .newsrc lines");
X		if ((rcline[line] = malloc((unsigned)(strlen(rcbuf) + 1))) == NULL)
X			xerror("Not enough memory");
X		(void) strcpy(rcline[line], rcbuf);
X	}
X	fclose(rcfp);
X
X	if (SigTrap) {
X		if (SigTrap == SIGHUP || !rcreadok)
X			xxit(0);
X		fprintf(stdout, "Abort (n)?  ");
X		(void) fflush(stdout);
X		if (gets(bfr) == NULL || *bfr == 'y' || *bfr == 'Y')
X			xxit(0);
X		SigTrap = FALSE;
X	}
X	sortactive();
X	actfp = xfopen(ACTIVE, "r");
X
X#ifdef DEBUG
X	fprintf(stderr, "header.nbuf = %s\n", header.nbuf);
X#endif /* DEBUG */
X	if (Kflag)
X		news++;
X	else {
X		switch (mode) {
X		case UNKNOWN:
X			readr();
X			break;
X#ifdef TMAIL
X		case MAIL:
X			Mail();
X			break;
X#endif /* TMAIL */
X		}
X	}
X
X	cleanup(0);
X	/*NOTREACHED*/
X}
X
Xcleanup(signo)
X{
X	extern short ospeed;
X
X	(void) signal(SIGHUP, SIG_IGN);
X	(void) fflush(stdout);
X	if (news && !xflag && !lflag && !tflag) {
X		if (*groupdir && mode != MAIL)
X			updaterc();
X		writeoutrc();
X	}
X	/*
X	 * stop vnews from clearing the screen if we're
X	 * killed by a hangup
X	 */
X	if (signo == SIGHUP)
X		ospeed = 0;
X	xxit(0);
X}
X
X/*
X * Write out the .newsrc file. It's already been cleaned up by sortactive()
X * Take care that data is all written, and flushed, before we destroy the
X * old copy.
X */
Xwriteoutrc()
X{
X	FILE *wrcfp;
X	char aline[BUFLEN];
X	register int i;
X
X	/* NEVER write it out if xflag */
X	if (xflag || !rcreadok)
X		return;
X
X	(void) strcpy(aline, newsrc);
X	(void) strcat(aline, ".new");
X
X#ifdef VMS
X	(void) vmsdelete(aline);
X#endif
X	wrcfp = xfopen(aline, "w");
X
X	for (i = 0; i <= line; i++) {
X		if (rcline[i] != NULL)
X			if (fprintf(wrcfp, "%s\n", rcline[i]) < 0)
X				goto fouled;
X	}
X	if (fclose(wrcfp) < 0)
X		goto fouled;
X
X#ifdef VMS
X	(void) vmsdelete(newsrc);
X#endif
X	if (rename(aline, newsrc) < 0)
X		xerror("Cannot rename %s to %s", aline, newsrc);
X	return;
X
X  fouled:
X	xerror("Error writing new .newsrc file - no changes made\n");
X	return;
X}
X
X/*
X * Forbid newsgroup ng, unless he asked for it in nbuf.
X */
Xmakehimask(nbuf, ng)
Xchar *nbuf, *ng;
X{
X	if (!findex(nbuf, ng))
X		(void) sprintf(rindex(nbuf, '\0'), ",!%s", ng);
X}
X
X/*
X * Return true if the string searchfor is in string, but not if preceded by !.
X */
Xfindex(string, searchfor)
Xchar *string, *searchfor;
X{
X	register char first;
X	register char *p;
X
X	first = *searchfor;
X	for (p=index(string, first); p; p = index(p+1, first)) {
X		if (((p==string) || (p[-1]!='!')) && strncmp(p, searchfor, strlen(searchfor)) == 0)
X			return TRUE;
X	}
X	return FALSE;
X}
END_OF_FILE
if test 7970 -ne `wc -c <'readnews.c'`; then
    echo shar: \"'readnews.c'\" unpacked with wrong size!
fi
# end of 'readnews.c'
fi
if test -f 'digest.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'digest.c'\"
else
echo shar: Extracting \"'digest.c'\" \(8135 characters\)
sed "s/^X//" >'digest.c' <<'END_OF_FILE'
X/*
X * digest - process ARPANET digests
X *
X * digest(ifile, ofile, header)
X * FILE *ifile, *ofile;
X * struct header *header;
X *
X * returns:	TRUE	EOF reached, exit from readnews.
X *		FALSE	normal exit, continue reading news.
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)digest.c	1.7	9/19/86";
X#endif /* SCCSID */
X
X#include "rparams.h"
X
Xstruct art {
X	long	a_hdr;
X	long	a_bod;
X	int	a_blen;
X	int	a_hlen;
X};
X
X#define	loop		for(;;)
X#define	getnum(p, n)	for (n=0; *p>='0' && *p<='9'; p++) n = n*10 + *p-'0'
X#define	errchk(p)	if (*p) goto badopt
X
X#define	MAXART		128
X
Xstruct art	*arts;
Xint		lastart;
X
Xdigest(ifp, ofp, h)
XFILE *ifp, *ofp;
Xstruct hbuf *h;
X{
X	register int	n, curart;
X	struct art	artbuf[MAXART];
X	int		printh, eod, nomore;
X	char		cbuf[BUFLEN], *cmd;
X
X	arts = artbuf;
X	printh = TRUE;
X	nomore = eod = FALSE;
X	curart = 1;
X
X	if (dscan(ifp))
X		return FALSE;
X
X	dprint(0, ifp, ofp);
X
X	loop {
X		if (nomore) break;
X		if (curart < 1) {
X			curart = 1;
X			eod = nomore = FALSE;
X		}
X		if (curart > lastart) curart = lastart;
X		if (eod) nomore = TRUE;
X		if (printh && !nomore)
X			(void) dhprint(curart, ifp, ofp);
X	getcmd:
X		loop {
X			SigTrap = FALSE;
X			fprintf(ofp, "Digest article %d of %d ", curart, lastart);
X			if (curart==lastart && nomore)
X				fprintf(ofp, "Last digest article ");
X			fprintf(ofp, "(%d lines) More? [%s] ",
X				arts[curart].a_blen, nomore?"snq":"ynq");
X			(void) fflush(ofp);
X			cmd = cbuf;
X			if (fgets(cmd, BUFLEN, stdin))
X				break;
X			if (!SigTrap)
X				return(TRUE);
X			putc('\n', ofp);
X		}
X		(void) nstrip(cmd);
X		while (*cmd==' ' || *cmd=='\t')
X			cmd++;
X		printh = TRUE;
X
X		switch (*cmd++) {
X		case '#':
X			fprintf(ofp, "%d articles in digest\n", lastart);
X			(void) fflush(ofp);
X			printh = FALSE;
X			break;
X
X		case '$':
X			curart = lastart;
X			break;
X
X		case '!':
X			fwait(fsubr(ushell, cmd, (char *)NULL));
X			fprintf(ofp, "!\n");
X			printh = FALSE;
X			break;
X
X		case '\0':
X			if (nomore) {
X				putc('\n', ofp);
X				return(FALSE);
X			}
X			cmd--;
X		case 'y':
X		case 'p':
X			errchk(cmd);
X			dprint(curart++, ifp, ofp);
X			if (curart > lastart)
X				eod = TRUE;
X			break;
X
X		case 'n':
X			errchk(cmd);
X			if (++curart > lastart) {
X				putc('\n', ofp);
X				return(FALSE);
X			}
X			break;
X
X		case '+':
X			getnum(cmd, n);
X			errchk(cmd);
X			if (nomore) {
X				putc('\n', ofp);
X				return(FALSE);
X			}
X			if (n)	curart += n;
X			else {
X				curart += 1;
X				if (curart > lastart)
X					eod = TRUE;
X			}
X			break;
X
X		case '-':
X			getnum(cmd, n);
X			errchk(cmd);
X			eod = nomore = FALSE;
X			curart -= (n) ? n : 1;
X			break;
X
X		case '0': case '1': case '2': case '3': case '4':
X		case '5': case '6': case '7': case '8': case '9':
X			cmd--;
X			getnum(cmd, n);
X			errchk(cmd);
X			curart = n;
X			eod = nomore = FALSE;
X			break;
X
X		case 'q':
X		case 'x':
X			putc('\n', ofp);
X			return(FALSE);
X
X		case '?':
X			fprintf(ofp, "\nDigester options:\n\n");
X			fprintf(ofp, "y\tyes, print article.\n");
X			fprintf(ofp, "n\tno, go to next article.\n");
X			fprintf(ofp, "q\texit from digester.\n");
X			fprintf(ofp, "h\tprint article header.\n");
X			fprintf(ofp, "s file\tsave article in file.\n");
X			fprintf(ofp, "t\ttable of contents.\n");
X			fprintf(ofp, "+[n]\tforward n articles (1).\n");
X			fprintf(ofp, "-[n]\tback n articles (1).\n");
X			fprintf(ofp, "\nh and s may be followed by '-'\n");
X			(void) fflush(ofp);
X			break;
X
X		case 'h':
X			n = curart;
X			if (*cmd=='-') {
X				cmd++;
X				if (n > 1) n--;
X			}
X			errchk(cmd);
X			(void) dhprint(n, ifp, ofp);
X			nomore = printh = FALSE;
X			if (n!=curart)
X				putc('\n', ofp);
X			break;
X
X		case 's':
X		case 'w':
X			n = curart;
X			if (*cmd=='-') {
X				cmd++;
X				if (n > 1) n--;
X			}
X			while (*cmd==' ' || *cmd=='\t')
X				cmd++;
X			dsaveart(n, ifp, ofp, cmd);
X			nomore = printh = FALSE;
X			if (n!=curart)
X				putc('\n', ofp);
X			break;
X
X		case 'H':
X			errchk(cmd);
X			hprint(h, ofp, 1);
X			eod = nomore = FALSE;
X			break;
X
X		case 'T':
X		case 't':
X			errchk(cmd);
X			if (cmd[-1]=='T')
X				hprint(h, ofp, 0);
X			dprint(0, ifp, ofp);
X			eod = nomore = FALSE;
X			break;
X
X		default:
X	badopt:
X			if (!nomore)
X				fprintf(ofp, "y (yes), n (no), ");
X			fprintf(ofp, "q (quit), s file (save), h (header), t (table of contents)\n");
X			fprintf(ofp, "? for help\n");
X			goto getcmd;
X		}
X	}
X	putc('\n', ofp);
X	return(FALSE);
X}
X
Xdscan(ifp)
Xregister FILE *ifp;
X{
X	char		scanbuf[BUFLEN];
X	register int	n, len;
X	register char	*s;
X	register long	pos;
X	short		wasblank, ishead;
X
X	n = len = 0;
X	wasblank = FALSE;
X	s = scanbuf;
X	arts[0].a_bod = arts[1].a_hdr = ftell(ifp);
X	arts[0].a_hdr = 0L;
X	arts[1].a_bod = -1L;
X
X	loop {
X		if (SigTrap)
X			return(TRUE);
X		pos = ftell(ifp);
X		if (fgets(s, BUFLEN, ifp)==NULL)
X			*s = '\0';
X		if (wasblank && isheader(s)) {
X			long lastpos;
X			short is_blank;
X			short nhlines;
X			arts[n++].a_blen = len;
X			len = 0;
X			nhlines = 0;
X			arts[n].a_hdr = pos;
X			is_blank = FALSE;
X			ishead = TRUE;
X			do {
X				lastpos = pos;
X				wasblank = is_blank;
X				nhlines++;
X				pos = ftell(ifp);
X				if (fgets(s, BUFLEN, ifp)==NULL)
X					*s = '\0';
X				else
X					len++;
X				is_blank = (*s=='\n') ? TRUE : FALSE;
X				if (is_blank && nhlines==1)
X					/* one liner--not a header */
X					break;
X				if (!ishead || (s[0] != ' ' && s[0] != '\t'))
X					ishead = isheader(s);
X			} while ((is_blank && !wasblank) || ishead);
X			if ((!is_blank && !wasblank) || nhlines < 2) {
X				/* oops! not a header... back off */
X				arts[n].a_hdr = arts[n-1].a_bod;
X				len += arts[--n].a_blen;
X			} else {
X				if (wasblank)
X					pos = lastpos;
X				arts[n].a_hlen = len;
X				arts[n].a_bod = arts[n+1].a_hdr = pos;
X				arts[n+1].a_bod = -1L;
X				arts[n+1].a_hlen = 3;	/* average header len */
X				len = 0;
X			}
X		}
X		if (*s=='\0')
X			break;
X		wasblank = (*s=='\n') ? TRUE : FALSE;
X		len++;
X	}
X	arts[n].a_blen = len;
X	arts[n+1].a_hdr = pos;
X	lastart = n;
X	return FALSE;
X}
X
Xdhprint(art, ifp, ofp)
Xregister int art;
Xregister FILE *ifp, *ofp;
X{
X	register char	c;
X	register long	pos = arts[art].a_hdr;
X	register long	epos = arts[art].a_bod;
X	register int	nlines = 1;
X
X	putc('\n', ofp);
X	fseek(ifp, pos, 0);
X	while (pos++ < epos && !SigTrap) {
X		if ((c = getc(ifp))=='\n')
X			nlines++;
X		putc(c, ofp);
X	}
X	(void) fflush(ofp);
X	SigTrap = FALSE;
X	return nlines;
X}
X
Xdprint(art, ifp, ofp)
Xint art;
XFILE *ifp, *ofp;
X{
X#ifdef	PAGE
X	register int	cnt;
X	FILE		*pfp, *popen();
X
X	if (art && arts[art].a_blen > 23-arts[art+1].a_hlen && *PAGER) {
X		if (!index(PAGER, FMETA)) {
X			if ((pfp = popen(PAGER, "w"))==NULL)
X				(void) dprinta(art, ifp, ofp);
X			else {
X				cnt = dprinta(art, ifp, pfp) % 23;
X				if (cnt > 23-arts[art+1].a_hlen)
X					while (cnt++ < 24)
X						putc('\n', pfp);
X				(void) pclose(pfp);
X			}
X		} else
X			pout(ofp);
X	} else
X#endif /* PAGE */
X		(void) dprinta(art, ifp, ofp);
X}
X
Xdprinta(art, ifp, ofp)
Xint art;
Xregister FILE *ifp, *ofp;
X{
X	register char	c;
X	register long	pos = arts[art].a_bod;
X	register long	epos = arts[art+1].a_hdr;
X	register int	nlines = 0;
X
X	(void) fseek(ifp, pos, 0);
X	while (pos++ < epos && !SigTrap) {
X		if ((c = getc(ifp))=='\n')
X			nlines++;
X		putc(c, ofp);
X	}
X	(void) fflush(ofp);
X	SigTrap = FALSE;
X	return nlines;
X}
X
Xdsaveart(art, ifp, ofp, name)
Xint art;
Xregister FILE *ifp, *ofp;
Xregister char *name;
X{
X	register FILE	*nfp;
X	char		fname[BUFLEN];
X	char		*strcat(), *strcpy(), *getenv();
X	register char	*nb;
X
X	while (*name==' ' || *name=='\t')
X		name++;
X
X	if (*name=='|') {
X		fprintf(ofp, "don't know how to pipe yet.\n");
X		(void) fflush(ofp);
X		return;
X	} else if (*name=='/')
X		(void) strcpy(fname, name);
X	else {
X		if (nb = getenv("NEWSBOX"))
X			(void) strcpy(fname, nb);
X		else
X			(void) strcpy(fname, userhome);
X		(void) strcat(fname, "/");
X		(void) strcat(fname, name);
X	}
X
X	fprintf(ofp, "Save digest article %d in \"%s\"", art, fname);
X	(void) fflush(ofp);
X	if ((nfp = fopen(fname, "a"))!=NULL) {
X		int ln;
X		ln = dhprint(art, ifp, nfp);
X		ln += dprinta(art, ifp, nfp);
X		fprintf(ofp, " [Appended] %d lines\n", ln);
X		(void) fclose(nfp);
X	} else
X		fprintf(ofp, " cannot append to.\n");
X}
X
Xisheader(s)
Xregister char *s;
X{
X	if (isupper(*s) || islower(*s)) {
X		while (*s && *s!=':' && !isspace(*s))
X			s++;
X		if (*s==':' && *++s==' ')
X			return TRUE;
X	}
X	return FALSE;
X}
END_OF_FILE
if test 8135 -ne `wc -c <'digest.c'`; then
    echo shar: \"'digest.c'\" unpacked with wrong size!
fi
# end of 'digest.c'
fi
if test -f 'unbatch.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'unbatch.c'\"
else
echo shar: Extracting \"'unbatch.c'\" \(6310 characters\)
sed "s/^X//" >'unbatch.c' <<'END_OF_FILE'
X/*
X * unbatchnews: extract news in batched format and process it one article
X * at a time.  The format looks like
X *	#! rnews 1234
X *	article containing 1234 characters
X *	#! rnews 4321
X *	article containing 4321 characters
X *
X *	or
X *
X *	#! cunbatch
X *	calls LIBDIR/compress and writes the output to a temp file
X *	executes rnews for every article in the temp file
X *	unlinks the temp file
X *
X *	or
X *
X *	#! command [args]
X *	calls LIBDIR/command [args] to process the news
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)unbatch.c	1.26	3/21/87";
X#endif /* SCCSID */
X
X#define	MAXARGS		32
X
X#include "defs.h"
X#include <stdio.h>
X#include <ctype.h>
X
X#if defined(USG) || defined(BSD4_2) || defined(BSD4_1C) || defined(MINIX)
X#include <fcntl.h>
X#endif /* USG */
X
Xchar xbuf[BUFSIZ];
Xchar sibuf[BUFSIZ];
X
Xmain()
X{
X	register int c;
X	register FILE *pfn;
X	register long size;
X	char *filename;
X	int pid, wpid, exstat;
X	char *mktemp(), *gets();
X	long atol();
X
X	filename = mktemp("/tmp/unbnewsXXXXXX");
X	setbuf(stdin, (char *)NULL);	/* only for the first line */
X	if (gets(xbuf) == NULL) {
X		(void) unlink(filename);
X		exit(0);
X	}
X	if (strncmp(xbuf, "#! cunbatch", 11) == 0) {
X		docunbatch(xbuf);
X		/* should not return */
X		logerr("unbatch: docunbatch returned!");
X		exit(1);
X	}
X
X	if (strncmp(xbuf, "#! rnews ", 9) != 0) {
X		docmd(xbuf);
X		/* should not return */
X		logerr("unbatch: docmd returned!");
X		exit(1);
X	}
X
X	setbuf(stdin, sibuf);	/* buffer the rest of the file */
X
X	do {
X		while (strncmp(xbuf, "#! rnews ", 9) 
X		    && strncmp(xbuf, "! rnews ", 8)) { /* kludge for bug */
X			register char *cp;
X			for (cp = xbuf; *cp != '\0'; ++cp)
X				if (!isascii(*cp) ||
X					(!isprint(*cp) && !isspace(*cp)))
X						*cp = '?';
X			logerr("out of sync, skipping %s", xbuf);
X			if (gets(xbuf) == NULL)
X				exit(0);
X		}
X		size = atol(xbuf + (xbuf[0] == '#' ? 9 : 8));
X		if(size <= 0) {
X			logerr("nonsense size %ld", size);
X			continue;
X		}
X#ifdef VMS
X/* The loop is to delete all versions. */
X		while (unlink(filename) == 0)
X			;
X#endif /* VMS */
X		pfn = fopen(filename, "w");
X		while(--size >= 0 && (c = getc(stdin)) != EOF)
X			putc(c, pfn);
X		if (ferror(pfn) || fclose(pfn)) {	/* disk full? */
X			logerr("error writing temporary file");
X			break;
X		}
X
X		/*
X		 * If we got a truncated batch, don't process the
X		 * last article; it will probably be received again.
X		 */
X		if (size > 0) {
X			logerr("truncated batch");
X			break;
X		}
X
X		/*
X		 * rnews < filename
X		 */
X		while ((pid = vfork()) == -1) {
X			logerr("fork failed, waiting...\n");
X			sleep(60);
X		}
X		if (pid == 0) {
X			(void) close(0);
X			(void) open(filename, 0);
X#ifdef IHCC
X			(void) sprintf(xbuf, "%s/%s", logdir(HOME), RNEWS);
X#else
X			strcpy(xbuf, RNEWS);
X#endif
X#ifdef SPOOLNEWS
X			execlp(xbuf, "rnews", "-S", (char *)0);
X#else /* !SPOOLNEWS */
X			execlp(xbuf, "rnews", (char *)0);
X#endif /* !SPOOLNEWS */
X			perror("rnews");
X			exit(1);
X		}
X		while ((wpid = wait(&exstat)) >= 0 && wpid != pid)
X			;
X	} while (gets(xbuf) != NULL);
X	(void) unlink(filename);
X	exit(0);
X}
X
Xdocmd(p)
Xregister char *p;
X{
X	char *args[MAXARGS];
X	register char **ap = args;
X	char path[BUFSIZ];
X	char *rindex(), *cp;
X
X	while (*p && !isspace(*p))		/* skip leading #! crud */
X		p++;
X
X	while (isspace(*p))
X		p++;
X
X	while (*p != '\0') {
X		*ap++ = p;
X		if (ap >= &args[MAXARGS]) {
X			logerr("unbatch: Too many args to %s", args[0]);
X			exit(2);
X		}
X		while (*p && !isspace(*p))
X			p++;
X		if (*p)
X			*p++ = '\0';
X		while (isspace(*p))
X			p++;
X	}
X	*ap = (char *)0;
X
X	if (ap == args) {
X		logerr("unbatch: no command to execute");
X		exit(2);
X	}
X
X	/* strip off any leading pathname in case someone gets tricky */
X	cp = rindex(args[0], '/');
X	if (cp++ == NULL)
X		cp = args[0];
X
X# ifdef HOME
X	sprintf(path, "%s/%s/%s", logdir(HOME), LIBDIR, cp);
X# else /* !HOME */
X	sprintf(path, "%s/%s", LIBDIR, cp);
X# endif /* HOME */
X
X	/*
X	 * "path" is absolute, no searching is needed,  we use
X	 * 'execvp' solely so that sh scripts will be handled
X	 */
X	(void) execvp(path, args);
X	perror(path);
X	exit(2);
X}
X
X/* docunbatch ... feed a compressed batch into compress & put output to
X * a temporary file. Used to conserve memory when rnews is running.
X */
X
Xdocunbatch(arg)
Xchar	*arg;
X{
X	char	cmd[200];
X	int	pid, wpid, exstat;
X	char	*filename;
X	char	*mktemp();
X
X	filename = mktemp("/usr/tmp/unbnewsXXXXXX");
X	/* feed stdin into compress */
X	sprintf(cmd,"%s/compress -d >%s", LIBDIR, filename);
X
X	if ((pid=vfork()) == 0) {
X		execl("/bin/sh", "news-decompress", "-c", cmd, (char *) 0);
X		logerr("execl /bin/sh -c compress failed!\n");
X		exit(2);
X	} else {
X		while ((wpid = wait(&exstat)) >= 0 && wpid != pid)
X			;
X	}
X
X	if (exstat) {
X		logerr("Decompress failed\n");
X		exit(3);
X	}
X
X	/* now run unbatch (hey, that's us!) on the file */
X	/* redirect stdin to point to the file */
X	fclose(stdin);
X	if (open(filename, O_RDONLY) != 0) {
X		logerr("unbatch cannot reopen stdin\n");
X		unlink(filename);
X		exit(2);
X	}
X
X	sprintf(cmd, "%s/unbatch", LIBDIR);
X
X	if ((pid=vfork()) == 0) {
X		execl(cmd, "news-unpack", (char *) 0);
X		logerr("execl LIBDIR/unbatch failed!\n");
X		unlink(filename);
X		exit(2);
X	} else {
X		while ((wpid = wait(&exstat)) >= 0 && wpid != pid)
X			;
X	}
X
X	unlink(filename);
X	if (exstat) {
X		logerr("Unbatch failed\n");
X		exit(3);
X	}
X
X	exit(0);
X}
X
X
X/*
X * Log the given message, with printf strings and parameters allowed,
X * on the log file, if it can be written.
X */
X/* VARARGS1 */
Xlogerr(fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9)
Xchar *fmt;
Xlong a1, a2, a3, a4, a5, a6, a7, a8, a9;
X{
X	FILE *logfile;
X	char lfname[BUFSIZ];		/* the log file */
X	char bfr[BUFSIZ];
X	char *logtime, *ctime(); 
X	long t;
X
X	(void) time(&t);
X	logtime = ctime(&t);
X	logtime[16] = 0;
X	logtime += 4;
X
X#ifdef IHCC
X	(void) sprintf(lfname, "%s/%s/errlog", logdir(HOME), LIBDIR);
X#else
X	(void) sprintf(lfname, "%s/errlog", LIBDIR);
X#endif
X
X	(void) sprintf(bfr, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9);
X	(void) fprintf(stderr, "%s\n", bfr);
X	if (access(lfname, 0) == 0 && (logfile = fopen(lfname, "a")) != NULL) {
X#if defined(USG) || defined(BSD4_2) || defined(BSD4_1C)
X		int flags;
X		flags = fcntl(fileno(logfile), F_GETFL, 0);
X		(void) fcntl(fileno(logfile), F_SETFL, flags|O_APPEND);
X#else /* v7 */
X		(void) lseek(fileno(logfile), 0L, 2);
X#endif /* v7 */
X		fprintf(logfile, "%s\tbatch\t%s\n", logtime, bfr);
X		(void) fclose(logfile);
X	}
X}
END_OF_FILE
if test 6310 -ne `wc -c <'unbatch.c'`; then
    echo shar: \"'unbatch.c'\" unpacked with wrong size!
fi
# end of 'unbatch.c'
fi
if test -f 'recmail.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'recmail.c'\"
else
echo shar: Extracting \"'recmail.c'\" \(6021 characters\)
sed "s/^X//" >'recmail.c' <<'END_OF_FILE'
X/*
X * This software is Copyright (c) 1986 by Rick Adams.
X *
X * Permission is hereby granted to copy, reproduce, redistribute or
X * otherwise use this software as long as: there is no monetary
X * profit gained specifically from the use or reproduction or this
X * software, it is not sold, rented, traded or otherwise marketed, and
X * this copyright notice is included prominently in any copy
X * made.
X *
X * The author make no claims as to the fitness or correctness of
X * this software for any use whatsoever, and it is provided as is. 
X * Any use of this software is at the user's own risk.
X *
X * recmail: read a mail message on stdin, grab all addresses in To and Cc
X * lines, and pass the full message to all addressees.  This is useful to
X * send the output of a recently edited mail message (with headers edited too).
X * It is similar to sendmail -t, but only assumes /bin/mail.
X * To use your own mailer, e. g. nmail, compile with -DMAILER=my_mailer.
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)recmail.c	1.15	10/23/86";
X#endif /* SCCSID */
X
X#include "params.h"
X
X#ifndef MAILER
X#define MAILER "/bin/mail"
X#endif
Xchar mailer[] = MAILER;
X
X#define MAXRECIPS 100
Xchar *recips[MAXRECIPS];
Xint nrecips = 0;
X
Xmain()
X{
X	FILE *fd;
X	char *tmpf;
X	FILE *errfd;
X	char *errf;
X	char linebuf[1024];
X	int i, pid, wpid;
X	int exstat;
X	char *mypath;
X	int goodcnt, badcnt;
X	char *mktemp(), *getenv();
X
X	tmpf = mktemp("/tmp/rmXXXXXX");
X	(void) close(creat(tmpf,0666));
X	fd = fopen(tmpf, "w");
X	errf = mktemp("/tmp/rmXXXXXX");
X	(void) close(creat(errf,0666));
X	errfd = fopen(errf, "w");
X	fprintf(errfd, "Subject: Returned mail\n");
X	fprintf(errfd, "\n  ----- Transcript of session follows -----\n");
X	(void) fflush(errfd);
X	goodcnt = badcnt = 0;
X
X	while (fgets(linebuf, sizeof linebuf, stdin) != NULL) {
X	   	 if ((strncmp(linebuf, "Bcc: ", 5) == 0 ||
X		    strncmp(linebuf, "bcc: ", 5) == 0 ||
X		    strncmp(linebuf, "BCC: ", 5) == 0)) {
X		    if (linebuf[5] != '\n')
X			addrecips(linebuf+5);
X		    }
X		else if (fputs(linebuf, fd) == EOF)
X			goto werror;
X		if (linebuf[0] == '\n')
X			break;
X		if ((strncmp(linebuf, "To: ", 4) == 0 ||
X		    strncmp(linebuf, "to: ", 4) == 0 ||
X		    strncmp(linebuf, "TO: ", 4) == 0 ||
X		    strncmp(linebuf, "Cc: ", 4) == 0 ||
X		    strncmp(linebuf, "cc: ", 4) == 0 ||
X		    strncmp(linebuf, "CC: ", 4) == 0) &&
X		     linebuf[4] != '\n')
X			addrecips(linebuf+4);
X	}
X	if (!feof(stdin)) {
X		while (fgets(linebuf, sizeof linebuf, stdin) != NULL) {
X			if (fputs(linebuf, fd) == EOF) {
Xwerror:
X				printf("write error on temp file\n");
X				exit(2);
X			}
X		}
X	}
X	/*
X	 * Append the contents of the .signature file (if it exists) to
X	 * the end of the mail message
X	 */
X	{
X		char sigbuf[BUFSIZ];
X		register c;
X		register char *p = getenv("HOME");
X		FILE *infp;
X			
X		if (p) {
X			(void) sprintf(sigbuf, "%s/%s", p, ".signature");
X			if (infp = fopen(sigbuf, "r")) {
X				fprintf(fd,"---\n");
X				while ((c = getc(infp)) != EOF)
X					putc(c,fd);
X				(void) fclose(infp);
X			}
X		}
X	}
X	(void) fclose(fd);
X
X	/*
X	 * Force the path to only consider /bin and /usr/bin, since
X	 * that's the version of mail we want (not /usr/ucb/mail)
X	 */
X	if (mailer[0] != '/') {
X		register int e;
X		extern char **environ;
X		for (e = 0; environ[e] != NULL; ++e)
X			if (strncmp(environ[e], "PATH=", 5) == 0) {
X				environ[e] = "PATH=/bin:/usr/bin";
X				break;
X			}
X	}
X	mypath = getenv("PATH");
X	if (mypath)
X		strcpy(mypath, "/bin:/usr/bin");
X
X	/*
X	 * We send the copies out separately, because of a bug in
X	 * USG's /bin/mail which will generate ANOTHER To: line,
X	 * even though we already have one, if there are at least
X	 * two recipients.
X	 */
X	for (i=0; i<nrecips; i++) {
X		/*
X		 * mail recips[i] < tmpf
X		 */
X		pid = mailto(tmpf, errfd, recips[i]);
X		exstat = -1;
X		while ((wpid = wait(&exstat)) >= 0 && wpid != pid)
X			;
X		if (exstat == 0)
X			goodcnt++;
X		else
X			badcnt++;
X	}
X	if (badcnt) {
X		mailback(errfd, tmpf, errf);
X		(void) unlink(tmpf);
X		(void) unlink(errf);
X		exit(1);
X	} else if (goodcnt == 0) {
X		fprintf(errfd, "recmail: no 'To:' line\n");
X		mailback(errfd, tmpf, errf);
X		(void) unlink(tmpf);
X		(void) unlink(errf);
X		exit (1);
X	}
X	(void) unlink(tmpf);
X	(void) unlink(errf);
X	exit (0);
X}
X
X#define isok(c) (isprint(c) && (c) != ' ' && c != ',')
Xaddrecips(line)
Xchar *line;
X{
X	char *front, *back, *tail;
X	char *malloc();
X
X	tail = line + strlen(line);
X	for (front=line; front < tail; ) {
X		while (!isok(*front) && front < tail)
X			front++;
X		if (front >= tail)
X			break;	/* skip end of line garbage */
X		for (back=front; isok(*back); back++)
X			;
X		*back=0;
X		if (nrecips >= MAXRECIPS) {
X			printf("Too many destinations\n");
X			exit(2);
X		}
X		if ((recips[nrecips] = malloc(strlen(front) + 1)) == NULL) {
X			printf("Out of space\n");
X			exit(2);
X		}
X		(void) strcpy(recips[nrecips], front);
X		nrecips++;
X		front = back+1;
X	}
X}
X
Xint
Xmailto(tmpf, errfd, recip)
Xchar *tmpf;
XFILE *errfd;
Xchar *recip;
X{
X	register int pid;
X
X	/*
X	 * mail recips < tmpf
X	 */
X	while ((pid = vfork()) == -1) {
X		fprintf(stderr, "fork failed, waiting...\r\n");
X		sleep(60);
X	}
X	if (pid == 0) {
X		(void) close(0);
X		(void) open(tmpf, 0);
X		if (errfd != NULL) {
X			(void) close(1);
X			(void) dup(fileno(errfd));
X			(void) fclose(errfd);
X			(void) close(2);
X			(void) dup(1);
X		}
X		execlp(mailer, mailer, recip, (char *)0);
X		perror(mailer);
X		exit(1);
X	}
X	return pid;
X}
X
Xmailback(errfd, tmpf, errf)
Xregister FILE *errfd;
Xchar *tmpf;
Xchar *errf;
X{
X	register FILE *fd;
X	register int c;
X	int exstat;
X	register int pid, wpid;
X	char *logn;
X	char *getlogin(), *getenv();
X	register struct passwd *pwd;
X
X	if ((fd = fopen(tmpf, "r")) != NULL) {
X		fprintf(errfd, "\n   ----- Unsent message follows -----\n");
X		while ((c = getc(fd)) != EOF)
X			putc(c, errfd);
X		(void) fclose(fd);
X	}
X	(void) fclose(errfd);
X	if ((logn = getlogin()) == NULL && (logn = getenv("USER")) == NULL) {
X		if ((pwd = getpwent(getuid())) == NULL)
X			return;
X		logn = pwd->pw_name;
X	}
X	pid = mailto(errf, (FILE *)NULL, logn);
X	while ((wpid = wait(&exstat)) >= 0 && wpid != pid)
X		;
X}
END_OF_FILE
if test 6021 -ne `wc -c <'recmail.c'`; then
    echo shar: \"'recmail.c'\" unpacked with wrong size!
fi
# end of 'recmail.c'
fi
if test -f 'defs.minix' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'defs.minix'\"
else
echo shar: Extracting \"'defs.minix'\" \(5776 characters\)
sed "s/^X//" >'defs.minix' <<'END_OF_FILE'
X/*
X *	This software is Copyright (c) 1986 by Rick Adams.
X *
X *	Permission is hereby granted to copy, reproduce, redistribute or
X *	otherwise use this software as long as: there is no monetary
X *	profit gained specifically from the use or reproduction or this
X *	software, it is not sold, rented, traded or otherwise marketed, and
X *	this copyright notice is included prominently in any copy
X *	made.
X *
X *	The author make no claims as to the fitness or correctness of
X *	this software for any use whatsoever, and it is provided as is. 
X *	Any use of this software is at the user's own risk.
X *
X */
X
X/*	@(#)defs.dist	2.58	4/10/87 	*/
X
X/*
X * defs.h - defines for news-related programs.
X *
X * If you remove any lines here or in your Makefile, make the change
X * to localize.sh so you won't have to redo it for each news release.
X *
X * If TMAIL is undefined, the -M option will be disabled.
X *
X * By convention, the version of the software you are running is taken
X * to be news_version below.
X */
X
X#define DAYS	(60L*60L*24L)
X#define WEEKS	(7*DAYS)
X/* Things that very well may require local configuration */
X#ifndef HOME
X#define ROOTID	10	/* uid of person allowed to cancel anything	*/
X#endif
X#define N_UMASK 002	/* mask for umask call, 022 for secure system	*/
X#define DFLTEXP	2*WEEKS	/* default no. of seconds to expire in		*/
X#define HISTEXP	4*WEEKS	/* default no. of seconds to forget in		*/
X#define DFLTSUB "general,all.announce"	/* default subscription list	*/
X#define TMAIL	"/usr/bin/Mail"	/* Mail program that understands -T	*/
X#define ADMSUB	"general,all.announce"	/* Mandatory subscription list	*/
X#define PAGE	"/usr/bin/more"	/* Default pager			*/
X#define NOTIFY	"usenet"	/* Tell him about certain ctl messages	*/
X				/* Default xmit command - remove -z if	*/
X#define DFTXMIT	"uux - -r -z -gd %s!rnews < %s" /* your uux can't do it	*/
X#define UXMIT	"uux -r -z -gd -c %s!rnews '<' %s" /* If uux -c is ok	*/
X#define DFTEDITOR "mined"		/* Default editor, see also postnews.	*/
X/* #define UUPROG "euuname"	/* omit for uuname, put in LIBDIR	*/
X#define MANUALLY		/* Don't execute rmgroups, just notify.	*/
X/* #define NONEWGROUPS		/* Don't create new groups, just notify.*/
X/* #define SPOOLNEWS		/* Spool incoming rnews, don't process	*/
X/* #define SPOOLINEWS		/* Spool local inews, don't process	*/
X/* #define LOCALNAME 		/* There is no full name database. 	*/
X#define INTERNET		/* Internet mail works locally		*/
X#define MYDOMAIN ".OZ"	/* Local domain				*/
X/* #define CHEAP		/* don't chown files to news		*/
X/* #define OLD			/* Add extra headers for old neighbors	*/
X/* #define UNAME		/* If uname call returns your nodename  */
X/* #define GHNAME		/* If gethostname call is available.	*/
X#define UUNAME "/etc/uucpname" /* If your nodename is stored in a file */
X#define V7MAIL			/* Local mail format is V7 ("From ")	*/
X#define SORTACTIVE		/* if you want news presented in the order of the .newsrc */
X#define ZAPNOTES		/* if you want old style notes headers moved into the headers */
X#define DIGPAGE			/* allow digestifying in vnews */
X/* #define DOXREFS		/* Generate xref line for rn to use */
X#define MULTICAST		/* If you want to be able to multicast news */
X/* #define BSD4_2		/* If you are running 4.2  or 4.3 BSD	*/
X/* #define BSD4_1C		/* If you are running 4.1C BSD		*/
X/* #define LOCKF		/* If you have the lockf() sys call */
X/* #define ALWAYSALIAS		/* temporary kludge for conversion */
X/* #define SENDMAIL "/usr/bin/sendmail" /* command line to run "sendmail" if you have it	*/
X/* #define MMDF	"/usr/mmdf/submit"	/* command line to run mmdf if you have it */
X#define MYORG "Minix support BBS, NSW, Australia"	/* My organization.  Please	*/
X				/* include your city (and state, and	*/
X				/* country, if not obvious) in MYORG,	*/
X				/* and please keep it short.		*/
X/* #define HIDDENNET "frooz"	/* if you have a local network and want */
X				/* The mail address to look like it came */
X				/* from one machine */
X/* NOTE: The following two macros replace the use of HIDDENNET */
X/* #define GENERICPATH "frooz"	/* If you are using a shared USENET/UUCP node */
X/* #define GENERICFROM "Frobozz.COM"	/* If you want generic From:-addresses */
X/* #define NICENESS	4	/* does a nice(NICENESS) in rnews */
X/* #define FASCIST	"all,!all.all"	/* only permit posting to certain groups */
X				/* see installation guide for details */
X#define SMALL_ADDRESS_SPACE	/* If your machine can't address > 32767 */
X/* #define ORGDISTRIB	"froozum"	/* For organization wide control message handling */
X
X/* Things you might want to change */
X#define NEWSRC  ".newsrc"	/* name of .newsrc file (in home dir)	*/
X#define LINES	512	/* maximum no. of lines in .newsrc		*/
X#define NEGCHAR	'!'	/* newsgroup negation character			*/
X#define DEADTIME 45	/* no. of seconds to wait on deadlock		*/
X#define FMETA	'%'	/* file meta-character for c option		*/
X#if defined(pdp11) || defined(SMALL_ADDRESS_SPACE)
X# define BUFLEN	128	/* standard buffer size				*/
X#else
X# define BUFLEN	256	/* standard buffer size				*/
X#endif
X#define LBUFLEN 1024	/* big buffer size				*/
X#define SBUFLEN 32	/* small buffer size (for system names, etc)	*/
X#define LNCNT	14	/* Articles with > LNCNT lines go through pager */
X
X/* Things you probably won't want to change */
X#define PATHLEN 512	/* length of longest source string		*/
X#define DATELEN	64	/* length of longest allowed date string	*/
X#define NAMELEN	128	/* length of longest possible message ID	*/
X#define SNLN	8	/* max significant characters in sysname	*/
X#define PROTO	'A'	/* old protocol name				*/
X#define NETCHRS	"!:@^%,"/* Punct. chars used for various networks	*/
X#define TRUE	1	/* boolean true					*/
X#define FALSE	0	/* boolean false				*/
X#define PERHAPS	2	/* indeterminate boolean value			*/
X#define NGDELIM	','	/* delimit character in news group line		*/
X
END_OF_FILE
if test 5776 -ne `wc -c <'defs.minix'`; then
    echo shar: \"'defs.minix'\" unpacked with wrong size!
fi
# end of 'defs.minix'
fi
echo shar: End of shell archive.
exit 0
-- 
		"Zeta Microcomputer Software"
ACSnet:    nick@ultima.cs.uts.oz
UUCP:      ...!uunet!munnari!ultima.cs.uts.oz!nick
Fidonet:   Nick Andrew on 3:713/602 (Zeta)

nick@ultima.cs.uts.oz (Nick Andrew) (12/07/89)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  defs.h defs.dist batch.c uurec.c process.c decode.c
#   checkgroups.sh uname.c rparams.h
# Wrapped by nick@nswitgould on Thu Dec  7 22:42:33 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'defs.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'defs.h'\"
else
echo shar: Extracting \"'defs.h'\" \(5776 characters\)
sed "s/^X//" >'defs.h' <<'END_OF_FILE'
X/*
X *	This software is Copyright (c) 1986 by Rick Adams.
X *
X *	Permission is hereby granted to copy, reproduce, redistribute or
X *	otherwise use this software as long as: there is no monetary
X *	profit gained specifically from the use or reproduction or this
X *	software, it is not sold, rented, traded or otherwise marketed, and
X *	this copyright notice is included prominently in any copy
X *	made.
X *
X *	The author make no claims as to the fitness or correctness of
X *	this software for any use whatsoever, and it is provided as is. 
X *	Any use of this software is at the user's own risk.
X *
X */
X
X/*	@(#)defs.dist	2.58	4/10/87 	*/
X
X/*
X * defs.h - defines for news-related programs.
X *
X * If you remove any lines here or in your Makefile, make the change
X * to localize.sh so you won't have to redo it for each news release.
X *
X * If TMAIL is undefined, the -M option will be disabled.
X *
X * By convention, the version of the software you are running is taken
X * to be news_version below.
X */
X
X#define DAYS	(60L*60L*24L)
X#define WEEKS	(7*DAYS)
X/* Things that very well may require local configuration */
X#ifndef HOME
X#define ROOTID	10	/* uid of person allowed to cancel anything	*/
X#endif
X#define N_UMASK 002	/* mask for umask call, 022 for secure system	*/
X#define DFLTEXP	2*WEEKS	/* default no. of seconds to expire in		*/
X#define HISTEXP	4*WEEKS	/* default no. of seconds to forget in		*/
X#define DFLTSUB "general,all.announce"	/* default subscription list	*/
X#define TMAIL	"/usr/bin/Mail"	/* Mail program that understands -T	*/
X#define ADMSUB	"general,all.announce"	/* Mandatory subscription list	*/
X#define PAGE	"/usr/bin/more"	/* Default pager			*/
X#define NOTIFY	"usenet"	/* Tell him about certain ctl messages	*/
X				/* Default xmit command - remove -z if	*/
X#define DFTXMIT	"uux - -r -z -gd %s!rnews < %s" /* your uux can't do it	*/
X#define UXMIT	"uux -r -z -gd -c %s!rnews '<' %s" /* If uux -c is ok	*/
X#define DFTEDITOR "mined"		/* Default editor, see also postnews.	*/
X/* #define UUPROG "euuname"	/* omit for uuname, put in LIBDIR	*/
X#define MANUALLY		/* Don't execute rmgroups, just notify.	*/
X/* #define NONEWGROUPS		/* Don't create new groups, just notify.*/
X/* #define SPOOLNEWS		/* Spool incoming rnews, don't process	*/
X/* #define SPOOLINEWS		/* Spool local inews, don't process	*/
X/* #define LOCALNAME 		/* There is no full name database. 	*/
X#define INTERNET		/* Internet mail works locally		*/
X#define MYDOMAIN ".OZ"	/* Local domain				*/
X/* #define CHEAP		/* don't chown files to news		*/
X/* #define OLD			/* Add extra headers for old neighbors	*/
X/* #define UNAME		/* If uname call returns your nodename  */
X/* #define GHNAME		/* If gethostname call is available.	*/
X#define UUNAME "/etc/uucpname" /* If your nodename is stored in a file */
X#define V7MAIL			/* Local mail format is V7 ("From ")	*/
X#define SORTACTIVE		/* if you want news presented in the order of the .newsrc */
X#define ZAPNOTES		/* if you want old style notes headers moved into the headers */
X#define DIGPAGE			/* allow digestifying in vnews */
X/* #define DOXREFS		/* Generate xref line for rn to use */
X#define MULTICAST		/* If you want to be able to multicast news */
X/* #define BSD4_2		/* If you are running 4.2  or 4.3 BSD	*/
X/* #define BSD4_1C		/* If you are running 4.1C BSD		*/
X/* #define LOCKF		/* If you have the lockf() sys call */
X/* #define ALWAYSALIAS		/* temporary kludge for conversion */
X/* #define SENDMAIL "/usr/bin/sendmail" /* command line to run "sendmail" if you have it	*/
X/* #define MMDF	"/usr/mmdf/submit"	/* command line to run mmdf if you have it */
X#define MYORG "Minix support BBS, NSW, Australia"	/* My organization.  Please	*/
X				/* include your city (and state, and	*/
X				/* country, if not obvious) in MYORG,	*/
X				/* and please keep it short.		*/
X/* #define HIDDENNET "frooz"	/* if you have a local network and want */
X				/* The mail address to look like it came */
X				/* from one machine */
X/* NOTE: The following two macros replace the use of HIDDENNET */
X/* #define GENERICPATH "frooz"	/* If you are using a shared USENET/UUCP node */
X/* #define GENERICFROM "Frobozz.COM"	/* If you want generic From:-addresses */
X/* #define NICENESS	4	/* does a nice(NICENESS) in rnews */
X/* #define FASCIST	"all,!all.all"	/* only permit posting to certain groups */
X				/* see installation guide for details */
X#define SMALL_ADDRESS_SPACE	/* If your machine can't address > 32767 */
X/* #define ORGDISTRIB	"froozum"	/* For organization wide control message handling */
X
X/* Things you might want to change */
X#define NEWSRC  ".newsrc"	/* name of .newsrc file (in home dir)	*/
X#define LINES	512	/* maximum no. of lines in .newsrc		*/
X#define NEGCHAR	'!'	/* newsgroup negation character			*/
X#define DEADTIME 45	/* no. of seconds to wait on deadlock		*/
X#define FMETA	'%'	/* file meta-character for c option		*/
X#if defined(pdp11) || defined(SMALL_ADDRESS_SPACE)
X# define BUFLEN	128	/* standard buffer size				*/
X#else
X# define BUFLEN	256	/* standard buffer size				*/
X#endif
X#define LBUFLEN 1024	/* big buffer size				*/
X#define SBUFLEN 32	/* small buffer size (for system names, etc)	*/
X#define LNCNT	14	/* Articles with > LNCNT lines go through pager */
X
X/* Things you probably won't want to change */
X#define PATHLEN 512	/* length of longest source string		*/
X#define DATELEN	64	/* length of longest allowed date string	*/
X#define NAMELEN	128	/* length of longest possible message ID	*/
X#define SNLN	8	/* max significant characters in sysname	*/
X#define PROTO	'A'	/* old protocol name				*/
X#define NETCHRS	"!:@^%,"/* Punct. chars used for various networks	*/
X#define TRUE	1	/* boolean true					*/
X#define FALSE	0	/* boolean false				*/
X#define PERHAPS	2	/* indeterminate boolean value			*/
X#define NGDELIM	','	/* delimit character in news group line		*/
X
END_OF_FILE
if test 5776 -ne `wc -c <'defs.h'`; then
    echo shar: \"'defs.h'\" unpacked with wrong size!
fi
# end of 'defs.h'
fi
if test -f 'defs.dist' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'defs.dist'\"
else
echo shar: Extracting \"'defs.dist'\" \(5768 characters\)
sed "s/^X//" >'defs.dist' <<'END_OF_FILE'
X/*
X *	This software is Copyright (c) 1986 by Rick Adams.
X *
X *	Permission is hereby granted to copy, reproduce, redistribute or
X *	otherwise use this software as long as: there is no monetary
X *	profit gained specifically from the use or reproduction or this
X *	software, it is not sold, rented, traded or otherwise marketed, and
X *	this copyright notice is included prominently in any copy
X *	made.
X *
X *	The author make no claims as to the fitness or correctness of
X *	this software for any use whatsoever, and it is provided as is. 
X *	Any use of this software is at the user's own risk.
X *
X */
X
X/*	@(#)defs.dist	2.58	4/10/87 	*/
X
X/*
X * defs.h - defines for news-related programs.
X *
X * If you remove any lines here or in your Makefile, make the change
X * to localize.sh so you won't have to redo it for each news release.
X *
X * If TMAIL is undefined, the -M option will be disabled.
X *
X * By convention, the version of the software you are running is taken
X * to be news_version below.
X */
X
X#define DAYS	(60L*60L*24L)
X#define WEEKS	(7*DAYS)
X/* Things that very well may require local configuration */
X#ifndef HOME
X#define ROOTID	10	/* uid of person allowed to cancel anything	*/
X#endif
X#define N_UMASK 000	/* mask for umask call, 022 for secure system	*/
X#define DFLTEXP	2*WEEKS	/* default no. of seconds to expire in		*/
X#define HISTEXP	4*WEEKS	/* default no. of seconds to forget in		*/
X#define DFLTSUB "general,all.announce"	/* default subscription list	*/
X#define TMAIL	"/usr/ucb/Mail"	/* Mail program that understands -T	*/
X#define ADMSUB	"general,all.announce"	/* Mandatory subscription list	*/
X#define PAGE	"/usr/ucb/more"	/* Default pager			*/
X#define NOTIFY	"usenet"	/* Tell him about certain ctl messages	*/
X				/* Default xmit command - remove -z if	*/
X#define DFTXMIT	"uux - -r -z %s!rnews < %s" /* your uux can't do it	*/
X#define UXMIT	"uux -r -z -c %s!rnews '<' %s" /* If uux -c is ok	*/
X#define DFTEDITOR "vi"		/* Default editor, see also postnews.	*/
X/* #define UUPROG "euuname"	/* omit for uuname, put in LIBDIR	*/
X#define MANUALLY		/* Don't execute rmgroups, just notify.	*/
X/* #define NONEWGROUPS		/* Don't create new groups, just notify.*/
X/* #define SPOOLNEWS		/* Spool incoming rnews, don't process	*/
X/* #define SPOOLINEWS		/* Spool local inews, don't process	*/
X/* #define LOCALNAME 		/* There is no full name database. 	*/
X/* #define INTERNET		/* Internet mail works locally		*/
X#define MYDOMAIN ".UUCP"	/* Local domain				*/
X/* #define CHEAP		/* don't chown files to news		*/
X/* #define OLD			/* Add extra headers for old neighbors	*/
X/* #define UNAME		/* If uname call returns your nodename  */
X/* #define GHNAME		/* If gethostname call is available.	*/
X/* #define UUNAME "/etc/uucpname" /* If your nodename is stored in a file */
X#define V7MAIL			/* Local mail format is V7 ("From ")	*/
X#define SORTACTIVE		/* if you want news presented in the order of the .newsrc */
X#define ZAPNOTES		/* if you want old style notes headers moved into the headers */
X#define DIGPAGE			/* allow digestifying in vnews */
X/* #define DOXREFS		/* Generate xref line for rn to use */
X/* #define MULTICAST		/* If you want to be able to multicast news */
X/* #define BSD4_2		/* If you are running 4.2  or 4.3 BSD	*/
X/* #define BSD4_1C		/* If you are running 4.1C BSD		*/
X/* #define LOCKF		/* If you have the lockf() sys call */
X/* #define ALWAYSALIAS		/* temporary kludge for conversion */
X/* #define SENDMAIL "/usr/lib/sendmail" /* command line to run "sendmail" if you have it	*/
X/* #define MMDF	"/usr/mmdf/submit"	/* command line to run mmdf if you have it */
X#define MYORG "Frobozz Inc., St. Louis"	/* My organization.  Please	*/
X				/* include your city (and state, and	*/
X				/* country, if not obvious) in MYORG,	*/
X				/* and please keep it short.		*/
X/* #define HIDDENNET "frooz"	/* if you have a local network and want */
X				/* The mail address to look like it came */
X				/* from one machine */
X/* NOTE: The following two macros replace the use of HIDDENNET */
X/* #define GENERICPATH "frooz"	/* If you are using a shared USENET/UUCP node */
X/* #define GENERICFROM "Frobozz.COM"	/* If you want generic From:-addresses */
X/* #define NICENESS	4	/* does a nice(NICENESS) in rnews */
X/* #define FASCIST	"all,!all.all"	/* only permit posting to certain groups */
X				/* see installation guide for details */
X/* #define SMALL_ADDRESS_SPACE	/* If your machine can't address > 32767 */
X/* #define ORGDISTRIB	"froozum"	/* For organization wide control message handling */
X
X/* Things you might want to change */
X#define NEWSRC  ".newsrc"	/* name of .newsrc file (in home dir)	*/
X#define LINES	512	/* maximum no. of lines in .newsrc		*/
X#define NEGCHAR	'!'	/* newsgroup negation character			*/
X#define DEADTIME 45	/* no. of seconds to wait on deadlock		*/
X#define FMETA	'%'	/* file meta-character for c option		*/
X#if defined(pdp11) || defined(SMALL_ADDRESS_SPACE)
X# define BUFLEN	128	/* standard buffer size				*/
X#else
X# define BUFLEN	256	/* standard buffer size				*/
X#endif
X#define LBUFLEN 1024	/* big buffer size				*/
X#define SBUFLEN 32	/* small buffer size (for system names, etc)	*/
X#define LNCNT	14	/* Articles with > LNCNT lines go through pager */
X
X/* Things you probably won't want to change */
X#define PATHLEN 512	/* length of longest source string		*/
X#define DATELEN	64	/* length of longest allowed date string	*/
X#define NAMELEN	128	/* length of longest possible message ID	*/
X#define SNLN	8	/* max significant characters in sysname	*/
X#define PROTO	'A'	/* old protocol name				*/
X#define NETCHRS	"!:@^%,"/* Punct. chars used for various networks	*/
X#define TRUE	1	/* boolean true					*/
X#define FALSE	0	/* boolean false				*/
X#define PERHAPS	2	/* indeterminate boolean value			*/
X#define NGDELIM	','	/* delimit character in news group line		*/
END_OF_FILE
if test 5768 -ne `wc -c <'defs.dist'`; then
    echo shar: \"'defs.dist'\" unpacked with wrong size!
fi
# end of 'defs.dist'
fi
if test -f 'batch.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'batch.c'\"
else
echo shar: Extracting \"'batch.c'\" \(5484 characters\)
sed "s/^X//" >'batch.c' <<'END_OF_FILE'
X
X/*
X * This software is Copyright (c) 1985 by Rick Adams.
X *
X * Permission is hereby granted to copy, reproduce, redistribute or
X * otherwise use this software as long as: there is no monetary
X * profit gained specifically from the use or reproduction or this
X * software, it is not sold, rented, traded or otherwise marketed, and
X * this copyright notice is included prominently in any copy
X * made.
X *
X * The author make no claims as to the fitness or correctness of
X * this software for any use whatsoever, and it is provided as is. 
X * Any use of this software is at the user's own risk.
X *
X * Batch: program to batch a list of news articles into an unbatch script.
X * Usage: /usr/lib/news/batch listfile [bytecount]
X *  where listfile is a file containing a list, one per line, of full
X *  path names of files containing articles, e.g. as produced by the F
X *  transmission option in the sys file.
X *  bytecount is the maximum number of bytes to output before exiting
X * Output is placed on standard output.
X *
X * Intended usage:
X *
X *	With the shellfile "sendbatch", with machine names as arguments:
X * 		e.g
X *		sendbatch rlgvax seismo
X *
X * This would be invoked every hour or two from crontab.
X *
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)batch.c	1.18	12/16/86";
X#endif /* SCCSID */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <errno.h>
X#include "defs.h"
X
X#if defined(USG) || defined(BSD4_2) || defined(BSD4_1C)
X#include <fcntl.h>
X#endif
X
Xstruct stat sbuf;
X
Xextern int errno;
Xextern char *sys_errlist[];
X
Xmain(argc,argv)
Xchar **argv;
X{
X	register FILE *fd, *nfd;
X	register int c;
X	register long n;
X	register char *cp;
X	char *fdstatus;
X	long maxbytes, nbytes;
X	long atol();
X	char fname[512];
X	char workfile[512];
X	char *index(), *fgets();
X
X	if (argc < 2) {
X		fprintf(stderr, "Usage: batch listfile [bytecount]\n");
X		exit(1);
X	}
X
X	/*
X	 * Rename real file to a work name to avoid race conditions.
X	 * If workfile exists skip the rename in order
X	 * to recover from a crash w/o losing anything.
X	 */
X	(void) strcpy(workfile, argv[1]);
X	(void) strcat(workfile, ".work");
X	if (access(workfile, 0) < 0) {
X		if (access(argv[1], 0) < 0 && errno == ENOENT)
X			exit(0);	/* no news */
X		if (rename(argv[1], workfile) < 0) {
X			logerror("rename(%s,%s) %s", argv[1], workfile,
X				sys_errlist[errno]);
X			exit(1);
X		}
X	}
X	fd = fopen(workfile, "r");
X	if (fd == NULL) {
X		logerror("fopen(%s,r) %s", workfile, sys_errlist[errno]);
X		exit(1);
X	}
X
X	if (argc > 2)
X		maxbytes = atol(argv[2]);
X	else
X		maxbytes = 100000000L; /* backwards compatible */
X	nbytes = 0;
X	while ((fdstatus = fgets(fname, sizeof fname, fd)) != NULL) {
X		cp = index(fname, '\n');
X		if (cp)
X			*cp = '\0';
X		nfd = fopen(fname, "r");
X		if (nfd == NULL) {
X			perror(fname);
X			continue;
X		}
X		(void) fstat(fileno(nfd), &sbuf);
X		if (cp)
X			*cp = '\n';
X		nbytes += sbuf.st_size;
X		if (nbytes > maxbytes && nbytes != sbuf.st_size)
X			break;
X		printf("#! rnews %ld\n", (long)sbuf.st_size);
X		/* guess length of #! rnews string */
X		nbytes += 13;
X		n = 0;
X		while ((c = getc(nfd)) != EOF) {
X			putchar(c);
X			n++;
X		}
X		(void) fclose(nfd);
X		if (ferror(stdout)){
X			logerror("stdout write %s", sys_errlist[errno]);
X			exit(1);
X		}
X		if (n != sbuf.st_size) { /* paranoia */
X			logerror("%s, expected %ld bytes, got %ld", fname,
X				n, sbuf.st_size);
X			/* breaking out of this early will end up resyncing
X			   the batch files (isn't serendipity wonderful?) */
X			break;
X		}
X	}
X	if (fdstatus != NULL) {		/* exceeded maxbytes */
X		char tmpfile[512];
X
X		(void) umask(2);
X		(void) strcpy(tmpfile, argv[1]);
X		(void) strcat(tmpfile, ".tmp");
X	    	nfd = fopen(tmpfile, "w");
X		if (nfd == NULL) {
X			logerror("fopen(%s,w) %s", tmpfile, sys_errlist[errno]);
X			exit(1);
X		}
X		do {
X			fputs(fname, nfd);
X		} while (fgets(fname, sizeof fname, fd) != NULL);
X		if (ferror(nfd)) {
X			logerror("write(%s) %s", tmpfile, sys_errlist[errno]);
X			exit(1);
X		}
X		(void) fclose(nfd);
X		(void) fclose(fd);
X		/* will pick it up next time thru */
X		if (rename(tmpfile, workfile) < 0) {
X			logerror("rename(%s,%s) %s", tmpfile, workfile,
X				sys_errlist[errno]);
X			exit(1);
X		}
X	}		
X	else
X		(void) unlink(workfile);
X	exit(0);
X}
X
X/*
X * Log the given message, with printf strings and parameters allowed,
X * on the log file, if it can be written.
X */
X/* VARARGS1 */
Xlogerror(fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9)
Xchar *fmt;
Xlong a1, a2, a3, a4, a5, a6, a7, a8, a9;
X{
X	FILE *logfile;
X	char lfname[BUFLEN];		/* the log file */
X	char bfr[BUFLEN];
X	char *logtime, *ctime(); 
X	time_t t;
X
X	(void) time(&t);
X	logtime = ctime(&t);
X	logtime[16] = 0;
X	logtime += 4;
X
X#if defined(IHCC) || defined(HOME)
X	(void) sprintf(lfname, "%s/%s/errlog", logdir(HOME), LIBDIR);
X#else
X	(void) sprintf(lfname, "%s/errlog", LIBDIR);
X#endif
X
X	(void) sprintf(bfr, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9);
X	fprintf(stderr, bfr);
X	if (access(lfname, 0) == 0 && (logfile = fopen(lfname, "a")) != NULL) {
X#if defined(USG) || defined(BSD4_2) || defined(BSD4_1C)
X		int flags;
X		flags = fcntl(fileno(logfile), F_GETFL, 0);
X		(void) fcntl(fileno(logfile), F_SETFL, flags|O_APPEND);
X#else /* v7 */
X		(void) lseek(fileno(logfile), 0L, 2);
X#endif /* v7 */
X		fprintf(logfile, "%s\tbatch\t%s\n", logtime, bfr);
X		(void) fclose(logfile);
X	}
X}
X
X#if !defined(BSD4_2) && !defined(BSD4_1C)
Xrename(from, to)
Xregister char *from, *to;
X{
X	(void) unlink(to);
X	if (link(from, to) < 0)
X		return -1;
X
X	(void) unlink(from);
X	return 0;
X}
X#endif /* !BSD4_2 && !BSD4_1C */
END_OF_FILE
if test 5484 -ne `wc -c <'batch.c'`; then
    echo shar: \"'batch.c'\" unpacked with wrong size!
fi
# end of 'batch.c'
fi
if test -f 'uurec.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'uurec.c'\"
else
echo shar: Extracting \"'uurec.c'\" \(3247 characters\)
sed "s/^X//" >'uurec.c' <<'END_OF_FILE'
X/*
X * uurec - receive articles via /bin/mail.
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)uurec.c	2.11	3/21/87";
X#endif /* SCCSID */
X
X#include "defs.h"
X
X#include <stdio.h>
X#include <ctype.h>
X
X/*
X * Process a news article which has been shipped via /bin/mail.
X */
X
X#define FROM	01
X#define NLIN	02
X#define BLANK	03
X#define OTHER	04
X
X#define SKIPPING	010
X#define READING		020
X
X#define BFSZ 250
X
X#define EOT	'\004'
X
X#define A	01
X#define B	02
X
X#ifdef debug
X# define RNEWS "cat"
X#endif
Xextern	char	*strcat(), *strcpy();
Xextern	char	*frombreak();
Xextern	FILE	*popen();
X
X/* ARGSUSED */
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	char buf[BFSZ], fbuf[BFSZ];
X	char bfr[BFSZ], *pbfr = bfr;
X	register char *p = NULL;
X	register FILE *pipe = stdout;
X	register int mode, frmflg, pathcnt, format;
X	char *index();
X
X	mode = SKIPPING;
X	frmflg = FALSE;
X	while (fgets(buf, BFSZ, stdin) != NULL) {
X#ifdef debug
X		printf("%o\t%s", mode|type(buf), buf);
X#endif
X		switch (mode | type(buf)) {
X
X		case FROM | SKIPPING:
X			if (frmflg)
X				p = frombreak(p, buf);
X			else
X				p = fbuf;
X			frmflg = TRUE;
X			break;
X
X		case FROM | READING:
X			if (!frmflg) {
X				frmflg = TRUE;
X				p = fbuf;
X				pclose(pipe);
X			}
X			p = frombreak(p, buf);
X			break;
X
X		case NLIN | SKIPPING:
X			if ((isupper(buf[1]) && index(buf, ':')) || !strncmp(buf, "From ", 5))
X				format = B;
X			else
X				format = A;
X#ifdef debug
X			printf("format = %d\n", format);
X#endif
X			mode = READING;
X
X		case NLIN | READING:
X			if (frmflg) {
X				frmflg = FALSE;
X				--p;
X				while (p >= fbuf && *--p != '!')
X					;
X				*++p = '\0';
X				pathcnt = 0;
X#ifdef IHCC
X				sprintf(pbfr, "%s/%s", logdir(HOME), RNEWS);
X#else
X				pbfr = RNEWS;
X#endif
X				if ((pipe = popen(pbfr, "w")) == NULL) {
X					perror("uurec: popen failed");
X					exit(1);
X				}
X			}
X			if (format == A) {
X				if (++pathcnt == 3)
X					fputs(fbuf, pipe);
X				fputs(buf+1, pipe);
X			} else {
X				if (!pathcnt && (!strncmp(buf+1, "From: ", 6) || !strncmp(buf+1, "From ", 5))) {
X					pathcnt++;
X					fprintf(pipe, "From: %s", fbuf);
X					sscanf(buf, "%s %[^\n]", fbuf, fbuf);
X					fprintf(pipe, "%s\n", fbuf);
X				} else
X					fputs(buf+1, pipe);
X			}
X			break;
X
X		case OTHER | SKIPPING:
X			break;
X
X		case OTHER | READING:
X			pclose(pipe);
X			mode = SKIPPING;
X		}
X	}
X	if (pipe && pipe != stdout)
X		pclose(pipe);
X	exit(0);
X}
X
Xtype(p)
Xregister char *p;
X{
X	while (*p == ' ' || *p == '?')
X		++p;
X
X	if (*p == 'N')
X		return (NLIN);
X
X	if (strncmp(p, ">From ", 6) == 0)
X		return (FROM);
X
X	if (strncmp(p, "From ", 5) == 0)
X		return (FROM);
X
X	return(OTHER);
X}
X
X/*
X * Get the system name out of a from line.
X */
Xchar *
Xfrombreak(buf, fbuf)
Xregister char *buf, *fbuf;
X{
X	register char *p;
X
X	/* break the line into tokens. */
X	p = fbuf;
X	while (*++p != '\0')
X		switch (*p) {
X		case '\n':
X		case '\t':
X		case ' ':
X			*p = '\0';
X			break;
X		case EOT:
X			goto garbled;
X		default:;
X		}
X	*++p = EOT;
X	*++p = '\0';
X
X	for (p=fbuf; *p != EOT  || p[1] != '\0'; p += strlen(p)+1) {
X		if (strcmp(p, "forwarded") == 0)
X			return(buf);
X		if (strcmp(p, "remote") == 0) {
X			p += strlen(p)+1;
X			if (strcmp(p, "from") == 0) {
X				p += strlen(p)+1;
X				strcpy(buf, p);
X				strcat(buf, "!");
X				return(buf+strlen(buf));
X			}
X		}
X	}
X    garbled:
X	strcat(buf, "???!");
X	return(buf+4);
X}
END_OF_FILE
if test 3247 -ne `wc -c <'uurec.c'`; then
    echo shar: \"'uurec.c'\" unpacked with wrong size!
fi
# end of 'uurec.c'
fi
if test -f 'process.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'process.c'\"
else
echo shar: Extracting \"'process.c'\" \(3638 characters\)
sed "s/^X//" >'process.c' <<'END_OF_FILE'
X/*
X * This software is Copyright (c) 1986 by Rick Adams.
X *
X * Permission is hereby granted to copy, reproduce, redistribute or
X * otherwise use this software as long as: there is no monetary
X * profit gained specifically from the use or reproduction or this
X * software, it is not sold, rented, traded or otherwise marketed, and
X * this copyright notice is included prominently in any copy
X * made.
X *
X * The author make no claims as to the fitness or correctness of
X * this software for any use whatsoever, and it is provided as is. 
X * Any use of this software is at the user's own risk.
X *
X * process - process options for readnews/vnews
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)process.c	2.17	12/16/86";
X#endif /* SCCSID */
X
X#include "rparams.h"
X
Xchar	coptbuf[LBUFLEN], datebuf[LBUFLEN];
X
X#define OPTION	0	/* pick up an option string */
X#define STRING	1	/* pick up a string of arguments */
X
Xstruct optable *optpt, options[] = { /*
Xoptlet	filchar	flag	newstate oldmode	newmode	buf	*/
X'p',	'\0',	FALSE,	OPTION,	UNKNOWN,	UNKNOWN,(char *)NULL,	
X't',	'\0',	FALSE,	STRING,	ANY,		UNKNOWN,header.title,	
X'a',	' ',	FALSE,	STRING,	ANY,		UNKNOWN,datebuf,
X'n',   NGDELIM,	FALSE,	STRING,	ANY,		UNKNOWN,header.nbuf,
X'c',	' ',	FALSE,	STRING,	UNKNOWN,	UNKNOWN,coptbuf,	
X'l',	' ',	FALSE,	OPTION,	UNKNOWN,	UNKNOWN,(char *)NULL,
X'r',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,(char *)NULL,
X's',   NGDELIM,	FALSE,	STRING,	ANY,		UNKNOWN,header.nbuf,
X'x',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,(char *)NULL,
X'h',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,(char *)NULL,
X#ifdef TMAIL
X'M',	'\0',	FALSE,	OPTION,	UNKNOWN,	MAIL,	(char *)NULL,
X#else /* !TMAIL */
X'\377',	'\0',	FALSE,	OPTION,	UNKNOWN,	UNKNOWN,(char *)NULL,
X#endif /* !TMAIL */
X'f',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,(char *)NULL,
X'u',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,(char *)NULL,
X'e',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,(char *)NULL,
X'K',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,(char *)NULL,
X'\0',	'\0',	0,	0,	0,		0,	(char *)NULL
X};
X
Xprocess(argc,argv)
Xregister int argc;
Xregister char **argv;
X{
X	register int state = STRING;
X	register char *ptr = header.nbuf;
X	char filchar = NGDELIM;
X	int len = LBUFLEN, tlen;
X
X	/* loop once per arg. */
X
X	if (argc > 1 && **argv != '-')
X		nflag = TRUE;
X
X	while (--argc) {
X	    if (state == OPTION) {
X		if (**argv != '-') {
X			xerror("Bad option string \"%s\"", *argv);
X		}
X		while (*++*argv != '\0') {
X			for (optpt = options; optpt->optlet != '\0'; ++optpt) {
X				if (optpt->optlet == **argv)
X					goto found;
X			}
X			/* unknown option letter */
X#ifdef TMAIL
X			fprintf(stderr, "Usage: %s [ -a [ date ]] [ -n newsgroups ] [ -t titles ] [ -lprxhfuMK ]\n", Progname);
X#else /* !TMAIL */
X			fprintf(stderr, "Usage: %s [ -a [ date ]] [ -n newsgroups ] [ -t titles ] [ -lprxhfuK ]\n", Progname);
X#endif /* !TMAIL */
X			fprintf(stderr, "\t[ -c [ ``mailer'' ]]\n\n");
X			fprintf(stderr, "       %s -s\n", Progname);
X			exit(1);
X
X		    found:;
X			if (mode != UNKNOWN && (mode&optpt->oldmode) == 0) {
X				xerror("Bad %c option", **argv);
X			}
X			if (mode == UNKNOWN)
X				mode = optpt->newmode;
X			filchar = optpt->filchar;
X			optpt->flag = TRUE;
X			state = optpt->newstate;
X			ptr = optpt->buf;
X			len = LBUFLEN;
X		}
X
X		argv++;		/* done with this option arg. */
X
X	    } else {
X
X		/*
X		 * Pick up a piece of a string and put it into
X		 * the appropriate buffer.
X		 */
X		if (**argv == '-') {
X			state = OPTION;
X			argc++;	/* uncount this arg. */
X			continue;
X		}
X
X		if ((tlen = strlen(*argv)) >= len)
X			xerror("Argument string too long");
X		strcpy(ptr, *argv++);
X		ptr += tlen;
X		if (*(ptr-1) != filchar)
X			*ptr++ = filchar;
X		len -= tlen + 1;
X		*ptr = '\0';
X	    }
X	}
X	return;
X}
END_OF_FILE
if test 3638 -ne `wc -c <'process.c'`; then
    echo shar: \"'process.c'\" unpacked with wrong size!
fi
# end of 'process.c'
fi
if test -f 'decode.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'decode.c'\"
else
echo shar: Extracting \"'decode.c'\" \(3365 characters\)
sed "s/^X//" >'decode.c' <<'END_OF_FILE'
X#include <stdio.h>
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)decode.c	1.3	5/15/85";
X#endif /* SCCSID */
X
X/*
X * This program is the inverse of encode
X *
X * It collects runs of 12 characters, combines pairs of those
X * to form 6 13 bit numbers, extracts the top bit of each of
X * those to make a 13th 6 bit character, and splits each of
X * the remaining 6 12 bit numbers to form 12 6 bit ones.
X *
X * The strings of 6 bit numbers are collected into groups of
X * 4 and converted into 3 8 bit characters.
X *
X * Now all that would be trivial, if we didn't need to worry
X * about ending all this correctly.  About 1/2 of the following
X * program wouldn't be here if the ending didn't matter....
X */
X
X/*
X * the following pair of characters can never occur as a pair
X * in legal input (since (90 * 91 + 90) > 2^13) - they are
X * noticed at the beginning of a 12 char block, and serve to
X * indicate that this block is the terminator.  The character
X * immediately following is the (expanded) terminator length.
X */
X#define	ENDMARK1	((90*91 + 90) / 91)
X#define	ENDMARK2	((90*91 + 90) % 91)
X
Xmain()
X{
X	register c;
X	register char *p;
X	register i;
X	register first = 1;
X	register cnt = 0;
X	int errcnt = 0;
X	char b12[12];
X	char c12[12];
X
X	p = b12;
X	i = 12;
X
X	while ((c = getchar()) != EOF) {
X		if (c < ' ' || c >= (' ' + 91)) {
X			if (errcnt++ == 0)
X				fprintf(stderr, "decode: Bad data\n");
X			continue;
X		}
X		if (i == 10 && p[-1] == ENDMARK1 && p[-2] == ENDMARK2) {
X			cnt = c - ' ';
X			i = 12;
X			p -= 2;
X			continue;
X		}
X		*p++ = c - ' ';
X		if (--i == 0) {
X			if (p == &b12[12]) {
X				if (!first)
X					pack12(c12, 12, 0);
X				else
X					first = 0;
X				p = c12;
X			} else {
X				pack12(b12, 12, 0);
X				p = b12;
X			}
X			i = 12;
X		}
X	}
X
X	if (p >= &b12[0] && p < &b12[12]) {
X		if (!first)
X			pack12(c12, 12, i == 12 ? cnt : 0);
X	} else
X		pack12(b12, 12, i == 12 ? cnt : 0);
X
X	if (i != 12) {
X		if (p >= &b12[0] && p < &b12[12])
X			pack12(b12, 12-i, cnt);
X		else
X			pack12(c12, 12-i, cnt);
X	}
X
X	exit(0);
X}
X
Xstatic char b4[4];
Xstatic int cnt = 0;
X
Xpack12(p, n, last)
X	register char *p;
X	register n;
X	int last;
X{
X	register i;
X	register char *q;
X	char b13[13];
X
X	{
X		register c;
X		register c13;
X
X		q = b13;
X		c13 = 0;
X
X		for (i = 0; i < n; i += 2) {
X			c = *p++ * 91;
X			c += *p++;
X			c13 <<= 1;
X			if (c & (1 << 12))
X				c13 |= 1;
X			*q++ = (c >> 6) & 0x3f;
X			*q++ = c & 0x3f;
X		}
X		*q++ = c13;
X		if (last)
X			q = &b13[last];
X	}
X
X	p = b13;
X	n = q - p;
X	i = cnt;
X	q = &b4[cnt];
X
X	while (--n > 0) {
X		*q++ = *p++;
X		if (++i == 4) {
X			char b3[3];
X			register char *b = b4;
X
X			/* inline expansion of pack6bit, to save calls ... */
X
X			q = b3;
X			*q++ = (b[0] << 2) | ((b[1] >> 4) & 0x3);
X			*q++ = (b[1] << 4) | ((b[2] >> 2) & 0xf);
X			*q = (b[2] << 6) | (b[3] & 0x3f);
X
X			q = b3;
X			while (--i > 0)
X				putchar(*q++);
X
X			q = b4;
X		}
X	}
X
X	*q++ = *p++;	/* the last octet */
X	++i;
X
X	if (last || i == 4) {
X		pack6bit(b4, i, last);
X		i = 0;
X	}
X
X	cnt = i;
X}
X
Xpack6bit(p, n, last)
X	register char *p;
X	register int n;
X	int last;
X{
X	register char *q;
X	register i = 3;
X	char b3[3];
X
X	if (last) {
X		i = p[n-1];
X		if (i >= 3) {
X			fprintf(stderr, "Badly encoded file\n");
X			i = 3;		/* do the best we can */
X		}
X	}
X
X	q = b3;
X	*q++ = (p[0] << 2) | ((p[1] >> 4) & 0x3);
X	*q++ = (p[1] << 4) | ((p[2] >> 2) & 0xf);
X	*q = (p[2] << 6) | (p[3] & 0x3f);
X
X	q = b3;
X
X	while (--i >= 0)
X		putchar(*q++);
X}
END_OF_FILE
if test 3365 -ne `wc -c <'decode.c'`; then
    echo shar: \"'decode.c'\" unpacked with wrong size!
fi
# end of 'decode.c'
fi
if test -f 'checkgroups.sh' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'checkgroups.sh'\"
else
echo shar: Extracting \"'checkgroups.sh'\" \(3677 characters\)
sed "s/^X//" >'checkgroups.sh' <<'END_OF_FILE'
X: check active file for missing or extra newsgroups
X: '@(#)checkgroups	1.22	3/20/87'
X
Xif  test  ! -s LIBDIR/newsgroups
Xthen
X	cp /dev/null LIBDIR/newsgroups
Xfi
X# Read first line of stdin.  If of the form "-n group", then only check
X# for the specified group.  Otherwise, assume doing standard groups
Xsed -e '/^[a-zA-Z-]*: /d' -e '/^$/d' -e '/^[#:]/d' | (
Xread line
Xcase "${line}" in
X-n*)
X	# Doing specific group.  extract group name and preserve
X	# all of current newsgroups file except for that group.
X	# Then append entries for this group.
X	group=`echo "${line}" | sed -e 's/-n /^/' -e 's/$/\\\\./'`
X	egrep -v "${group}" LIBDIR/newsgroups > /tmp/$$a
X	cat /tmp/$$a - > LIBDIR/newsgroups
X	;;
X*)
X	# Get the distributions from the checkgroups message itself
X	# This allows sites to append their local groups to the distributed
X	# checkgroups message and prevents stray checkgroups from other sites
X	# from showing all the local groups as being bad groups.
X	#
X	echo "${line}" > /tmp/$$msg
X	cat >> /tmp/$$msg
X	cp /dev/null /tmp/$$b
X	sed -e "s;[ 	].*;;" -e "s;\..*;;" -e "s;^!;;" /tmp/$$msg | sort -u |
X		while read dist
X		do
X			group=`cat /tmp/$$b`
X			group="${group}|^$dist\\."
X			echo "${group}" > /tmp/$$b
X		done
X	group=`cat /tmp/$$b`
X	egrep -v "${group}" LIBDIR/newsgroups > /tmp/$$a
X	cat /tmp/$$a > LIBDIR/newsgroups
X	sed -e "/^!/d" /tmp/$$msg >> LIBDIR/newsgroups
X	rm -f /tmp/$$b /tmp/$$msg
X	;;
Xesac
X
Xegrep "${group}" LIBDIR/active | sed 's/ .*//' | sort >/tmp/$$active
Xegrep "${group}" LIBDIR/newsgroups | sed 's/	.*//' | sort >/tmp/$$newsgrps
X
Xcomm -13 /tmp/$$active /tmp/$$newsgrps >/tmp/$$missing
Xcomm -23 /tmp/$$active /tmp/$$newsgrps >/tmp/$$remove
X
Xegrep "${group}" LIBDIR/active | sed -n "/m\$/s/ .*//p" |
X	sort > /tmp/$$amod.all
Xegrep "${group}" LIBDIR/newsgroups |
Xsed -n "/Moderated/s/[ 	][ 	]*.*//p" | sort > /tmp/$$ng.mod
X
Xcomm -12 /tmp/$$missing /tmp/$$ng.mod >/tmp/$$add.mod
Xcomm -23 /tmp/$$missing /tmp/$$ng.mod >/tmp/$$add.unmod
Xcat /tmp/$$add.mod /tmp/$$add.unmod >>/tmp/$$add
X
Xcomm -23 /tmp/$$amod.all /tmp/$$remove >/tmp/$$amod
Xcomm -13 /tmp/$$ng.mod /tmp/$$amod >/tmp/$$ismod
Xcomm -23 /tmp/$$ng.mod /tmp/$$amod >/tmp/$$nm.all
Xcomm -23 /tmp/$$nm.all /tmp/$$add >/tmp/$$notmod
X
Xif test -s /tmp/$$remove
Xthen
X	(
X	echo "The following newsgroups are not valid and should be removed."
X	sed "s/^/	/" /tmp/$$remove
X	echo ""
X	echo "You can do this by executing the command:"
X	echo \	LIBDIR/rmgroup `cat /tmp/$$remove`
X	echo ""
X	) 2>&1 >/tmp/$$out
Xfi
X
Xif test -s /tmp/$$add
Xthen
X	(
X	echo "The following newsgroups were missing and should be added."
X	sed "s/^/	/" /tmp/$$add
X	echo ""
X	echo "You can do this by executing the command(s):"
X	for i in `cat /tmp/$$add.unmod`
X	do
X		echo 'LIBDIR/inews -C '$i' </dev/null'
X	done
X	for i in `cat /tmp/$$add.mod`
X	do
X		echo 'LIBDIR/inews -C '$i' moderated </dev/null'
X	done
X	echo ""
X	) 2>&1 >>/tmp/$$out
Xfi
X
Xif test -s /tmp/$$ismod
Xthen
X	(
X	echo "The following newsgroups are not moderated and are marked moderated."
X	sed "s/^/	/" /tmp/$$ismod
X	echo ""
X	echo "You can correct this by executing the command(s):"
X	for i in `cat /tmp/$$ismod`
X	do
X		echo 'LIBDIR/inews -C '$i' </dev/null'
X	done
X	echo ""
X	) 2>&1 >>/tmp/$$out
Xfi
X
Xif test -s /tmp/$$notmod
Xthen
X	(
X	echo "The following newsgroups are moderated and not marked so."
X	sed "s/^/	/" /tmp/$$notmod
X	echo ""
X	echo "You can correct this by executing the command(s):"
X	for i in `cat /tmp/$$notmod`
X	do
X		echo 'LIBDIR/inews -C '$i' moderated </dev/null'
X	done
X	echo ""
X	) 2>&1 >>/tmp/$$out
Xfi
X
Xif test -s /tmp/$$out
Xthen
X	(echo	"Subject: Problems with your active file"
X	echo ""
X	cat /tmp/$$out
X	) | if test $# -gt 0
X		then
X			mail $1
X		else
X			cat
X		fi	
Xfi
X)
X
Xrm -f /tmp/$$*
X
END_OF_FILE
if test 3677 -ne `wc -c <'checkgroups.sh'`; then
    echo shar: \"'checkgroups.sh'\" unpacked with wrong size!
fi
# end of 'checkgroups.sh'
fi
if test -f 'uname.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'uname.c'\"
else
echo shar: Extracting \"'uname.c'\" \(2333 characters\)
sed "s/^X//" >'uname.c' <<'END_OF_FILE'
X/*
X * This software is Copyright (c) 1986 by Rick Adams.
X *
X * Permission is hereby granted to copy, reproduce, redistribute or
X * otherwise use this software as long as: there is no monetary
X * profit gained specifically from the use or reproduction or this
X * software, it is not sold, rented, traded or otherwise marketed, and
X * this copyright notice is included prominently in any copy
X * made.
X *
X * The author make no claims as to the fitness or correctness of
X * this software for any use whatsoever, and it is provided as is. 
X * Any use of this software is at the user's own risk.
X *
X * This routine is compatible with the Unix T/S system call uname,
X * which figures out the name of the local system.
X * However, we do it by reading the file /usr/include/whoami.h.
X * This avoids having to recompile uucp for each site and hence
X * avoids having to distribute the source to uucp to people who
X * have only binary licenses.
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)uname.c	2.15	3/21/87";
X#endif /* SCCSID */
X
X#include "params.h"
X
X#ifdef UNAME
X# define DONE
X#endif /* UNAME */
X
X#ifdef GHNAME
Xuname(uptr)
Xstruct utsname *uptr;
X{
X	char *cp;
X	extern char *mydomain();
X	gethostname(uptr->nodename, sizeof (uptr->nodename));
X	cp = mydomain();
X	if (*cp == '\0') /* get domain name from hostname */
X		return;
X	cp = index(uptr->nodename, '.');
X	if (cp)
X		*cp = '\0';
X}
X# define DONE
X#endif
X
X#ifdef	UUNAME
Xuname(uptr)
Xstruct utsname *uptr;
X{
X	FILE *uucpf;
X	register char *p;
X	/* uucp name is stored UUNAME */
X
X	if (((uucpf = fopen(UUNAME, "r")) == NULL) ||
X		fgets(uptr->nodename, sizeof (uptr->nodename), uucpf) == NULL) {
X			fprintf(stderr, "no sysname in %s\n", UUNAME);
X			return;
X	}
X	p = index(uptr->nodename, '\n');
X	if (p)
X		*p = '\0';
X	if (uucpf != NULL)
X		fclose(uucpf);
X}
X#define DONE
X#endif /* UUNAME */
X
X#ifndef DONE
X#define	HDRFILE "/usr/include/whoami.h"
X
Xuname(uptr)
Xstruct utsname *uptr;
X{
X	char buf[BUFSIZ];
X	FILE *fd;
X	
X	fd = fopen(HDRFILE, "r");
X	if (fd == NULL) {
X		fprintf(stderr, "Cannot open %s\n", HDRFILE);
X		exit(1);
X	}
X	
X	for (;;) {	/* each line in the file */
X		if (fgets(buf, sizeof buf, fd) == NULL) {
X			fprintf(stderr, "no sysname in %s\n", HDRFILE);
X			fclose(fd);
X			exit(2);
X		}
X		if (sscanf(buf, "#define sysname \"%[^\"]\"", uptr->nodename) == 1) {
X			fclose(fd);
X			return;
X		}
X	}
X}
X#endif
END_OF_FILE
if test 2333 -ne `wc -c <'uname.c'`; then
    echo shar: \"'uname.c'\" unpacked with wrong size!
fi
# end of 'uname.c'
fi
if test -f 'rparams.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rparams.h'\"
else
echo shar: Extracting \"'rparams.h'\" \(2146 characters\)
sed "s/^X//" >'rparams.h' <<'END_OF_FILE'
X/*
X * rparams.h - parameters for readnews, rfuncs, and readr.
X */
X
X/*	@(#)rparams.h	2.23	10/23/86	*/
X
X#include "params.h"
X
X/* flags for readnews */
X#define pflag	options[0].flag
X#define tflag	options[1].flag
X#define aflag	options[2].flag
X#define nflag	options[3].flag
X#define cflag	options[4].flag
X#define lflag	options[5].flag
X#define rflag	options[6].flag
X#define sflag	options[7].flag
X#define xflag	options[8].flag
X#define hflag	options[9].flag
X#define Mflag	options[10].flag
X#define fflag	options[11].flag
X#define uflag	options[12].flag
X#define eflag	options[13].flag
X#define Kflag	options[14].flag
X
X#define	NEXT	0
X#define SPEC	1
X
X#define	FORWARD	0
X#define BACKWARD 1
X
X#define UNKNOWN 0001	/* possible modes for news program */
X#define MAIL	0004
X#define ANY	0007
X
Xstruct optable {			/* options table. */
X	char	optlet;		/* option character. */
X	char	filchar;	/* if to pickup string, fill character. */
X	int	flag;		/* TRUE if have seen this opt. */
X	int	newstate;	/* STRING if takes arg, else OPTION */
X	int	oldmode;	/* OR of legal input modes. */
X	int	newmode;	/* output mode. */
X	char	*buf;		/* string buffer */
X};
X
X/* external declarations specific to readnews */
Xextern	char	*infile, *outfile, *PAGER, *ALIASES;
Xextern	char	*bitmap, *MAILER, *MAILPARSER;
X
X#ifndef ROOTID
Xextern	int	ROOTID;
X#endif
X
X#ifdef NOTIFY
Xextern	char	*TELLME;
X#endif
X
Xextern char	filename[],coptbuf[],datebuf[],afline[];
Xextern char	newsrc[],groupdir[],rcbuf[],*rcline[],*argvrc[];
Xextern int	mode, ngrp, line, newrc(), readmode, news;
Xextern long	bit, obit, last, ngsize, minartno;
Xextern FILE	*rcfp,*actfp;
Xextern time_t	atime;
Xextern struct optable *optpt, options[];
Xextern int	actdirect, rcreadok, zapng;
X
X#ifndef lint
X/* lint gets very mad about i-minartno, this is one way of shutting it up */
X/* macros */
X#define get(i)	((i<minartno)? 0 : (bitmap[(i-minartno) >> 3] & (1 << (i-minartno) % 8)))
X#define set(i)	if (i>=minartno) bitmap[(i-minartno) >> 3] |= (1 << (i-minartno) % 8);else
X#define clear(i) if (i>=minartno) bitmap[(i-minartno) >> 3] &= ~(1 << (i-minartno) % 8);else
X#endif /* !lint */
X
X#define FCLOSE(fp)	{if (fp != NULL) {fclose(fp);fp = NULL;}}
END_OF_FILE
if test 2146 -ne `wc -c <'rparams.h'`; then
    echo shar: \"'rparams.h'\" unpacked with wrong size!
fi
# end of 'rparams.h'
fi
echo shar: End of shell archive.
exit 0
-- 
		"Zeta Microcomputer Software"
ACSnet:    nick@ultima.cs.uts.oz
UUCP:      ...!uunet!munnari!ultima.cs.uts.oz!nick
Fidonet:   Nick Andrew on 3:713/602 (Zeta)

nick@ultima.cs.uts.oz (Nick Andrew) (12/07/89)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  params.h ndir.c logdir.c fullname.c encode.c caesar.c
#   berknews.c vnews.help sendnews.c sendbatch.sh
# Wrapped by nick@nswitgould on Thu Dec  7 22:43:07 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'params.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'params.h'\"
else
echo shar: Extracting \"'params.h'\" \(2904 characters\)
sed "s/^X//" >'params.h' <<'END_OF_FILE'
X/*
X * params.h - parameters for everyone.
X */
X
X/*	@(#)params.h	2.23	4/6/87	*/
X
X#include <stdio.h>
X#include <signal.h>
X#include <sys/types.h>
X#include <grp.h>
X#include <pwd.h>
X#include <sys/stat.h>
X#include <ctype.h>
X
X#include "defs.h"
X
X#if defined(BSD4_2) || defined(BSD4_1C)
X#include <sys/time.h>
X#else /* sane */
X#include <time.h>
X#endif /* sane */
X
X#ifndef UNAME
X/*
X * 9 bytes is for compatibility with USG, in case you forget to define UNAME.
X * 33 bytes in nodename because many sites have names longer than 8 chars.
X */
X
Xstruct utsname {
X	char	sysname[9];
X	char	nodename[33];
X	char	release[9];
X	char	version[9];
X};
X#else
X#include <sys/utsname.h>
X#endif
X
X#ifndef USG
X#include <sys/timeb.h>
X#else
Xstruct timeb
X{
X	time_t	time;
X	unsigned short millitm;
X	short	timezone;
X	short	dstflag;
X};
X#endif
X
X#include "header.h"
X
X/* line from SUBFILE */
Xstruct	srec {
X	char	s_name[2*BUFLEN];	/* system name		*/
X	char	*s_nosend;		/* systems that inhibit sending */
X	char	s_nbuf[LBUFLEN];	/* system subscriptions */
X	char	s_flags[BUFLEN];	/* system flags		*/
X	char	s_xmit[LBUFLEN];	/* system xmit routine	*/
X};
X
Xextern	int	uid, gid, duid, dgid;
Xextern	int	savmask, SigTrap, mode, lockcount;
Xextern	struct	hbuf header;
Xextern	char	bfr[LBUFLEN], *username, *userhome;
X
Xextern	char	*SPOOL, *LIB, *BIN, *SUBFILE, *ACTIVE;
Xextern	char	*LOCKFILE, *SEQFILE, *ARTFILE;
Xextern	char	*news_version, *Progname;
X
X#ifdef NOTIFY
Xextern	char	*TELLME;
X#endif /* NOTIFY */
X
Xextern	char	*LOCALSYSNAME, *LOCALPATHSYSNAME, *FROMSYSNAME, *PATHSYSNAME;
X
X#ifndef SHELL
Xextern	char	*SHELL;
X#endif /* !SHELL */
X
X/* external function declarations */
Xextern	FILE	*xfopen(), *hread();
Xextern	char	*strcpy(), *strncpy(), *strcat(), *index(), *rindex();
Xextern	char	*ctime(), *mktemp(), *malloc(), *realloc(), *getenv();
Xextern	char	*arpadate(), *dirname(), *AllocCpy(), *strpbrk();
Xextern	char	*errmsg();
Xextern	struct	passwd *getpwnam(), *getpwuid(), *getpwent();
Xextern	struct	group *getgrnam();
Xextern	time_t	time(), getdate(), cgtdate();
Xextern	int	broadcast(), save(), newssave(), ushell(), onsig();
Xextern	long	atol();
Xextern	struct	tm *localtime();
X
X#ifdef lint
X/* This horrible gross kludge is the only way I know to
X * convince lint that signal(SIGINT,SIG_IGN) is legal. It hates SIG_IGN.
X */
X#ifdef SIG_IGN
X#undef SIG_IGN
X#endif /* SIG_IGN */
X#define SIG_IGN	main
Xextern int main();
X#endif /* lint */
X
X#ifdef VMS
X#define LINK(a,b)	vmslink(a,b)
X#define UNLINK(a)	vmsdelete(a)
XFILE *art_open(), *xart_open();
X#else	
X#define LINK(a,b)	link(a,b)
X#define UNLINK(a)	unlink(a)
X#define art_open fopen
X#define xart_open xfopen
X#endif /* !VMS */
X
X/* Check for old naming scheme using HIDDENNET */
X#ifdef HIDDENNET
X#  ifndef GENERICFROM		/* Ugly fix, only for use in pathinit.c */
X#    define GENERICFROM "%s%0.0s%s", HIDDENNET
X#    define HIDDENNET_IN_LOCALSYSNAME
X#  endif
X#  ifndef GENERICPATH
X#    define GENERICPATH HIDDENNET
X#  endif
X#endif
END_OF_FILE
if test 2904 -ne `wc -c <'params.h'`; then
    echo shar: \"'params.h'\" unpacked with wrong size!
fi
# end of 'params.h'
fi
if test -f 'ndir.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ndir.c'\"
else
echo shar: Extracting \"'ndir.c'\" \(2564 characters\)
sed "s/^X//" >'ndir.c' <<'END_OF_FILE'
X#include "defs.h"
X#if !defined(BSD4_2) && !defined(BSD4_1C) && !defined(HP9K5)
X#ifndef MINIX
X#include <sys/param.h>
X#endif
X#include "ndir.h"
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)ndir.c	1.11	3/20/87";
X#endif /* SCCSID */
X
X/*
X * support for Berkeley directory reading routine on a V7 file system
X */
X
Xextern char *malloc();
X
X/*
X * open a directory.
X */
XDIR *
Xopendir(name)
Xchar *name;
X{
X	register DIR *dirp;
X	register int fd;
X
X	if ((fd = open(name, 0)) == -1)
X		return NULL;
X	if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
X		close (fd);
X		return NULL;
X	}
X	dirp->dd_fd = fd;
X	dirp->dd_loc = 0;
X	return dirp;
X}
X
X/*
X * read an old style directory entry and present it as a new one
X */
X#ifdef pyr
X/* Pyramid in the AT&T universe */
X#define ODIRSIZ 248
Xstruct olddirect {
X	long	od_ino;
X	short	od_fill1, od_fill2;
X	char od_name[ODIRSIZ];
X};
X#else /* V7 file system */
X#define	ODIRSIZ	14
X
Xstruct	olddirect {
X	short	od_ino;
X	char	od_name[ODIRSIZ];
X};
X#endif /* !pyr */
X
X/*
X * get next entry in a directory.
X */
Xstruct direct *
Xreaddir(dirp)
Xregister DIR *dirp;
X{
X	register struct olddirect *dp;
X	static struct direct dir;
X
X	for (;;) {
X		if (dirp->dd_loc == 0) {
X			dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, 
X			    DIRBLKSIZ);
X			if (dirp->dd_size <= 0)
X				return NULL;
X		}
X		if (dirp->dd_loc >= dirp->dd_size) {
X			dirp->dd_loc = 0;
X			continue;
X		}
X		dp = (struct olddirect *)(dirp->dd_buf + dirp->dd_loc);
X		dirp->dd_loc += sizeof(struct olddirect);
X		if (dp->od_ino == 0)
X			continue;
X		dir.d_ino = dp->od_ino;
X		strncpy(dir.d_name, dp->od_name, ODIRSIZ);
X		dir.d_name[ODIRSIZ] = '\0'; /* insure null termination */
X		dir.d_namlen = strlen(dir.d_name);
X		dir.d_reclen = DIRSIZ(&dir);
X		return (&dir);
X	}
X}
X
X/*
X * close a directory.
X */
Xvoid
Xclosedir(dirp)
Xregister DIR *dirp;
X{
X	close(dirp->dd_fd);
X	dirp->dd_fd = -1;
X	dirp->dd_loc = 0;
X	free((char *)dirp);
X}
X
X/*
X * seek to an entry in a directory.
X * Only values returned by "telldir" should be passed to seekdir.
X */
Xvoid
Xseekdir(dirp, loc)
Xregister DIR *dirp;
Xlong loc;
X{
X	long curloc, base, offset;
X	struct direct *dp;
X	long lseek(), telldir();
X
X	curloc = telldir(dirp);
X	if (loc == curloc)
X		return;
X	base = loc & ~(DIRBLKSIZ - 1);
X	offset = loc & (DIRBLKSIZ - 1);
X	(void) lseek(dirp->dd_fd, base, 0);
X	dirp->dd_loc = 0;
X	while (dirp->dd_loc < offset) {
X		dp = readdir(dirp);
X		if (dp == NULL)
X			return;
X	}
X}
X
X/*
X * return a pointer into a directory
X */
Xlong
Xtelldir(dirp)
XDIR *dirp;
X{
X	return lseek(dirp->dd_fd, 0L, 1) - dirp->dd_size + dirp->dd_loc;
X}
X#endif /* !BSD4_2 && !BSD4_1C && !HP9K5 */
END_OF_FILE
if test 2564 -ne `wc -c <'ndir.c'`; then
    echo shar: \"'ndir.c'\" unpacked with wrong size!
fi
# end of 'ndir.c'
fi
if test -f 'logdir.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'logdir.c'\"
else
echo shar: Extracting \"'logdir.c'\" \(2152 characters\)
sed "s/^X//" >'logdir.c' <<'END_OF_FILE'
X/*
X *	UNIX shell - logdir routine
X *
X *	Joe Steffen
X *	Bell Telephone Laboratories
X *
X *	This routine does not use the getpwent(3) library routine
X *	because the latter uses the stdio package.  The allocation of
X *	storage in this package destroys the integrity of the shell's
X *	storage allocation.
X *
X *	Modified 2/82 by DJ Molny
X *
X *	This routine now implements name cacheing, so multiple requests
X *	for the same logdir do not result in multiple open/reads of
X *	/etc/passwd.  If the previous request was successful and the name
X *	is the same as the last request, the same login directory is returned.
X */
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)logdir.c	1.4	4/16/85";
X#endif /* SCCSID */
X
X#define	BUFSIZ	160
X
Xstatic char line[BUFSIZ+1];
X
Xchar *
Xlogdir(name)
Xchar *name;
X{
X	int	pwf;
X	static char lastname[BUFSIZ+1];
X	static char lastdir[BUFSIZ+1];
X	register char *p;
X	register int i, j;
X	char *getenv(), *field(), *strcpy();
X	
X	if (*lastdir && !strcmp(lastname,name))		/* djm */
X		return(lastdir);
X
X	strcpy(lastname, name);			/* djm */
X	strcpy(lastdir, "");			/* djm */
X	
X#ifdef IHCC
X	/* if the logname is exptools, see if $TOOLS is set */
X	if (strcmp(name, "exptools") &&
X	    (p = getenv("TOOLS")) != 0 && *p != '\0') {
X		strcpy(lastdir, p);
X		return(lastdir);
X	}
X#endif
X
X	/* attempt to open the password file */
X	if ((pwf = open("/etc/passwd", 0)) == -1)
X		return(0);
X		
X	/* find the matching password entry */
X	do {
X		/* get the next line in the password file */
X		i = read(pwf, line, BUFSIZ);
X		for (j = 0; j < i; j++)
X			if (line[j] == '\n')
X				break;
X		/* return a null pointer if the whole file has been read */
X		if (j >= i)
X			return(0);
X		line[++j] = 0;			/* terminate the line */
X		lseek(pwf, (long) (j - i), 1);	/* point at the next line */
X		p = field(line);		/* get the logname */
X	} while (strcmp(name, line) != 0);
X	close(pwf);
X	
X	/* skip the intervening fields */
X	p = field(p);
X	p = field(p);
X	p = field(p);
X	p = field(p);
X	
X	/* return the login directory */
X	field(p);
X	strcpy(lastdir,p);			/* djm */
X	return(p);
X}
X
Xstatic char *
Xfield(p)
Xregister char *p;
X{
X	while (*p && *p != ':')
X		++p;
X	if (*p) *p++ = 0;
X	return(p);
X}
END_OF_FILE
if test 2152 -ne `wc -c <'logdir.c'`; then
    echo shar: \"'logdir.c'\" unpacked with wrong size!
fi
# end of 'logdir.c'
fi
if test -f 'fullname.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'fullname.c'\"
else
echo shar: Extracting \"'fullname.c'\" \(2985 characters\)
sed "s/^X//" >'fullname.c' <<'END_OF_FILE'
X/*
X * fullname.c - this file is made separate so that different local
X * conventions can be applied.  The stock version understands two
X * conventions:
X *
X * (a) Berkeley finger: the gecos field in /etc/passwd begins with
X *     the full name, terminated with comma, semicolon, or end of
X *     field.  & expands to the login name.
X * (b) BTL RJE: the gecos field looks like
X *	: junk - full name ( junk :
X *     where the "junk -" is optional.
X *
X * If you have a different local convention, modify this file accordingly.
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)fullname.c	1.11	9/16/86";
X#endif /* SCCSID */
X
X#include "params.h"
X
X#ifndef LOCALNAME
X/*
X * Figure out who is sending the message and sign it.
X * We attempt to look up the user in the gecos field of /etc/passwd.
X */
Xchar *
Xfullname(un)
Xchar *un;
X{
X	static char inbuf[BUFLEN];
X	struct passwd *pw;
X
X	pw = getpwnam(un);
X	if (pw == NULL)
X		return un;
X	buildfname(pw->pw_gecos, un, inbuf);
X	if (inbuf[0] == 0)
X		return un;
X	return inbuf;
X}
X
X#else
X
X/*
X * Alternative version of fullname which asks the user for his full name.
X * This is mainly suitable for systems that don't have a full name
X * database somewhere.  It puts the answer in $HOME/.name
X */
Xchar *
Xfullname(un)
Xchar *un;
X{
X	static char inbuf[BUFLEN];
X	char fbuf[BUFLEN];
X	FILE *fd;
X	char *p, *index(), *getenv();
X	int pid;
X
X	if (!isatty(2))
X		return un;
X	printf("What is your full name (for news article signatures): ");
X	fflush(stdout);
X	read(2, inbuf, sizeof inbuf);
X	if (inbuf[0] == 0)
X		return un;
X	p = index(inbuf, '\n');
X	if (p)
X		*p = 0;
X	if ((p = getenv("HOME")) == NULL) {
X		fprintf(stderr,
X		"inews: no HOME environment variable - .name not written\n");
X		return inbuf;
X	}
X	sprintf(fbuf, "%s/%s", p, ".name");
X	if ((pid = vfork()) < 0) {
X		perror("inews");
X		return inbuf;
X	}
X	else if (pid != 0)
X		while (wait((int *)0) != pid)
X			;
X	else {
X		setuid(getuid());	/* become the user */
X		if ((fd = fopen(fbuf, "w")) == NULL)
X			fprintf(stderr, "inews: can't create %s\n", fbuf);
X		else {
X			fprintf(fd, "%s\n", inbuf);
X			fclose(fd);
X		}
X		exit(0);
X	}
X	return inbuf;
X}
X#endif
X
X#ifndef LOCALNAME
X/*
X**  BUILDFNAME -- build full name from gecos style entry.
X**	(routine lifted from sendmail)
X**
X**	This routine interprets the strange entry that would appear
X**	in the GECOS field of the password file.
X**
X**	Parameters:
X**		p -- name to build.
X**		login -- the login name of this user (for &).
X**		buf -- place to put the result.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		none.
X*/
X
Xbuildfname(p, login, buf)
X	register char *p;
X	char *login;
X	char *buf;
X{
X	register char *bp = buf;
X
X	if (*p == '*')
X		p++;
X	while (*p != '\0' && *p != ',' && *p != ';' && *p != ':' && *p != '(')
X	{
X		if (*p == '-') {
X			bp = buf;
X			p++;
X		}
X		else if (*p == '&')
X		{
X			strcpy(bp, login);
X			if ((bp == buf || !isalpha(bp[-1])) && islower(*bp))
X				*bp = toupper(*bp);
X			while (*bp != '\0')
X				bp++;
X			p++;
X		}
X		else
X			*bp++ = *p++;
X	}
X	*bp = '\0';
X}
X#endif
END_OF_FILE
if test 2985 -ne `wc -c <'fullname.c'`; then
    echo shar: \"'fullname.c'\" unpacked with wrong size!
fi
# end of 'fullname.c'
fi
if test -f 'encode.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'encode.c'\"
else
echo shar: Extracting \"'encode.c'\" \(3062 characters\)
sed "s/^X//" >'encode.c' <<'END_OF_FILE'
X#include <stdio.h>
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)encode.c	1.3	5/15/85";
X#endif /* SCCSID */
X
X/*
X * Produce a 7 bit printable encoding of stdin on stdout.
X *
X * Encoding uses acsii chars from ' ' .. 'z'
X * (040 .. 0172) (0x20 - 0x7a) inclusive
X *
X * Method is to expand 3 chars -> 4 6 bit ones.
X * Then collect 13 6 bit chars, and spread the 13th over
X * the preceding 12, so that each of the 12 chars is now
X * 6.5 bits.  These 2 6.5 bit chars are a little hard
X * to represent on most common machines (one of these days
X * sane hosts will have 1/2 bits just for this program)
X * so we take a pair of them, and represent that in 13 bits.
X * 13 bits (max value 8191) can be represented as
X *	A * 91 + B
X * where A < 91, B < 91  (91^2 == 8281, so it fits!)
X *
X * Each of A and B is encoded as a character by adding 32
X * to make it printable (ie: 0x20).
X *
X * The termination conditions are foul beyond belief.  Don't
X * monkey with them!
X *
X * If you think its a fluke that 040 .. 0171 just happen to
X * be the chars that Piet Beertema's uucp 'f' protocol transmits
X * as single bytes, you're insane.  0172 chars are produced
X * with lower frequency than any other (given random data)
X * so the doubling that occurs with that we will just suffer.
X * (A newer 'f' proto, sometime, will probably not use 0172)
X */
X
X/*
X * the following pair of characters cannot legally occur
X * in normal output (since 90*91 + 90 == 8280, which > 2^13)
X * so we use them to indicate that the data that follows is the
X * terminator.  The character immediately following this
X * pair is the length of the (expanded) terminator (which
X * otherwise might be indeterminable)
X */
X#define	ENDMARK1	((90*91 + 90) / 91 + ' ')
X#define	ENDMARK2	((90*91 + 90) % 91 + ' ')
X
Xmain()
X{
X	register char *p;
X	register char *e;
X	register c;
X	char b3[3];
X
X	p = b3;
X	e = b3 + 3;
X	while ((c = getchar()) != EOF) {
X		*p++ = c;
X		if (p == e) {
X			encode(b3, 3);
X			p = b3;
X		}
X	}
X	encode(b3, p - b3);
X	flushout();
X	exit(0);
X}
X
Xstatic char b13[13];
Xstatic int cnt = 0;
X
Xencode(c, n)
X	register char *c;
X	int n;
X{
X	register char *p;
X	register i = cnt;
X	register j;
X	char b4[4];
X
X	p = b4;
X
X	p[0] = (c[0] >> 2) & 0x3f;
X	p[1] = ((c[0] & 0x3) << 4) | ((c[1] >> 4) & 0xf);
X	p[2] = ((c[1] & 0xF) << 2) | ((c[2] >> 6) & 0x3);
X	if (n == 3)
X		p[3] = c[2] & 0x3f;
X	else
X		p[3] = n;
X
X	c = &b13[i];
X	for (j = 4; --j >= 0; i++) {
X		if (i == 13) {
X			dumpcode(b13, 13);
X			c = b13;
X			i = 0;
X		}
X		*c++ = *p++;
X	}
X	cnt = i;
X}
X
Xflushout()
X{
X	putchar(ENDMARK1);
X	putchar(ENDMARK2);
X	putchar(cnt + ' ');
X	dumpcode(b13, cnt);
X}
X
Xdumpcode(p, n)
X	register char *p;
X	register int n;
X{
X	register last;
X	register c;
X
X	if (n == 13)
X		n--, last = p[12];
X	else if (n & 1)
X		last = (1 << (6-1));
X	else
X		last = 0;
X
X	for ( ; n > 0; n -= 2) {
X		c = *p++ << 6;
X		c |= *p++;
X		if (last & (1 << (6-1)))
X			c |= (1 << 12);
X		last <<= 1;
X
X		/*
X		 * note: 91^2 > 2^13, 90^2 < 2^13, (91 + ' ') is printable
X		 */
X
X		/* oh for a compiler that would only do one division... */
X		putchar((c / 91) + ' ');
X		putchar((c % 91) + ' ');
X	}
X}
END_OF_FILE
if test 3062 -ne `wc -c <'encode.c'`; then
    echo shar: \"'encode.c'\" unpacked with wrong size!
fi
# end of 'encode.c'
fi
if test -f 'caesar.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'caesar.c'\"
else
echo shar: Extracting \"'caesar.c'\" \(2376 characters\)
sed "s/^X//" >'caesar.c' <<'END_OF_FILE'
X/*
X * program to to decrypt caesar(tm) cypher
X * (caesar is a trademark of the roman empire)
X *
X * to compile:
X *
X *	cc decrypt.c -lm -o decrypt.c
X *
X * usage:
X *
X *	decrypt [n] < file
X *
X * where n is an optional forced rotation.
X *
X * authors: Stan King, John Eldridge, based on algorithm suggested by
X *		Bob Morris
X * 29-Sep-82
X *
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)caesar.c	1.7	4/16/85";
X#endif /* SCCSID */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <math.h>
Xextern char *calloc();
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X	/* letter frequencies (taken from some unix(tm) documentation) */
X	/* (unix is a trademark of Bell Laboratories) */
X	static double stdf[ 26 ] =
X	{
X		7.97, 1.35, 3.61, 4.78, 12.37, 2.01, 1.46, 4.49,
X		6.39, 0.04, 0.42, 3.81, 2.69, 5.92, 6.96, 2.91,
X		0.08, 6.63, 8.77, 9.68, 2.62, 0.81, 1.88, 0.23,
X		2.07, 0.06,
X	};
X	int obs[26];
X	int bufsize;
X	int c, i, try;
X	double dot, winnerdot;  /* .. */
X	int winner, forced = 0;
X	char *inbuf;
X
X	bufsize = 0;
X	if( argc > 1 )
X		sscanf( argv[1], "%d", &forced );
X	if( forced == 0 )
X		forced = -1000;
X		
X	inbuf = calloc( BUFSIZ, 1 );
X
X	/* adjust frequency table to weight low probs REAL low */
X	for (i=0; i<26; i++)	{
X		stdf[i] = log(stdf[i]) + log(26.0/100.0);
X	}
X
X	/* Decode each line separately */
X	for (;;) {
X		for (i=0; i<=25; obs[i++]=0)
X			;
X
X		/* get a sample of the text */
X		for( i = 0; i < BUFSIZ; i++ ) {
X			if( (c = getchar()) == EOF ) {
X				exit(0);
X			}	
X			inbuf[i] = c;
X			if (c == '\n') {
X				bufsize = i+1; 
X				break;
X			}
X			if (islower(c))
X				obs[c-'a'] += 1;
X			else if (isupper(c))
X				obs[c-'A'] += 1;
X		}
X
X		/* now "dot" the freqs with the observed letter freqs */
X		/*	and keep track of best fit */
X		winner = 0;	
X		for (try = 0; try<26; try+=13) {
X			dot = 0;
X			for ( i=0; i<26; i++ ) {
X				dot += obs[i] * stdf[ (i+try) % 26 ];
X				}
X			/* initialize winning score */
X			if( try == 0 )
X				winnerdot = dot;
X			if( dot > winnerdot ) {
X				/* got a new winner! */
X				winner = try;
X				winnerdot = dot;
X			}
X		}
X
X		if (forced != -1000)
X			winner = forced;
X
X		/* print out sample buffer */
X		for( i = 0; i < bufsize; i++ )
X			putchar( rotate( inbuf[i], winner ) );
X	}
X }
X
X
Xstatic int
Xrotate( c, perm )
Xchar c;
Xint perm;
X{
X	if (isupper(c))	{
X		return 'A' + (c - 'A' + perm) % 26 ;
X	}
X	else if (islower(c)) {
X		return 'a' + (c-'a'+perm) % 26 ;
X	}
X	else return c;
X}
END_OF_FILE
if test 2376 -ne `wc -c <'caesar.c'`; then
    echo shar: \"'caesar.c'\" unpacked with wrong size!
fi
# end of 'caesar.c'
fi
if test -f 'berknews.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'berknews.c'\"
else
echo shar: Extracting \"'berknews.c'\" \(2809 characters\)
sed "s/^X//" >'berknews.c' <<'END_OF_FILE'
X/*
X * berknews - send news article via Berknet
X * 
X * Synopsis:
X *	berknews [-o] [-n newsgroup] host_net_command machine remote_rnews
X */
X
X#ifdef SCCSID
Xstatic char *SccsId = "@(#)berknews.c	2.5	4/16/85";
X#endif /* SCCSID */
X
X#include <stdio.h>
X#include <ctype.h>
X#ifndef USG
X#include <whoami.h>
Xstruct utsname {
X	char	Sysname[9];
X	char	nodename[33];
X	char	release[9];
X	char	version[9];
X};
X#else /* USG */
X#include <sys/utsname.h>
X#endif /* USG */
X
X
Xstruct network {
X	char *uucpname;
X	char *berkname;
X} berknet[] = {
X/*	UUCP Net Name	BerkNet Name
X	-------------	------------	*/	
X	"ucbvax",	"CSVAX",
X	"populi",	"G",
X	"ucbarpa",	"ARPAVAX",
X	"ucbcfo-c",	"C",
X	"ucbopt",	"ESVAX",
X	"ucbcad",	"ucbcad",
X	"ucbcory",	"Cory",
X	"ucb",		"C70",
X	"ucbmathstat",	"MathStat",
X	"ucbonyx",	"Onyx",
X	"ucbkim",	"Kim",
X	"ucbcfo-a",	"A",
X	"ucbcfo-b",	"B",
X	"ucbcfo-d",	"D",
X	"ucbcfo-e",	"E",
X	"ucbcfo-f",	"F",
X	"ucbingvax",	"IngVAX",
X	"ucbingres",	"Ingres",
X	"ucbeecs40",	"EECS40",
X	"ucbvlsi",	"VLSI",
X	"ucbsrc",	"SRC",
X	"ucbimage",	"Image",
X	'\0',		'\0'
X};
X
Xchar *index();
Xchar buffer[BUFSIZ];
Xint linecount;
X
XFILE *popen();
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	FILE *out;
X	char sender[BUFSIZ],newsgroup[100];
X	char *punct;
X	char sysn[20];
X	int sysnl;
X	struct utsname ubuf;
X
X	if (argc < 4) {
X		fprintf(stderr, "Too few arguments.\n");
X		exit(1);
X	}
X
X#ifdef debug
X	printf("%s - -m%s %s\n", argv[1], argv[2], argv[3]);
X	sprintf(buffer, "cat");
X#else
X	sprintf(buffer, "%s - -m%s %s", argv[1], argv[2], argv[3]);
X#endif
X	out = popen(buffer, "w");
X	uname(&ubuf);
X	strcpy(sysn, ubuf.nodename);
X	strcat(sysn, "!");
X	sysnl = strlen(sysn);
X
X	while (fgets(buffer, sizeof buffer, stdin)) {
X		if (fromline()) {
X			punct = index(buffer, '!');
X			if (punct == NULL)
X				printf("Bad from line: '%s'\n", buffer);
X			else {
X				*punct = ':';	/* berknet mail delimiter */
X				if (!strncmp("From: ", buffer, 6))
X					punct = &buffer[6];
X				else if (!strncmp("From ",buffer,5))
X					punct = &buffer[5];
X				else
X					punct = buffer;
X				fiddle(punct);
X			}
X		}
X		fputs(buffer, out);
X	}
X	pclose(out);
X	exit(0);
X}
X
Xfromline() {
X	if (!linecount && (!strncmp("From: ", buffer, 6) || !strncmp("From ", buffer, 5)))
X		return ++linecount;
X	return 0;
X}
X
X/*
X * make sure the host name is a correct berknet address, since the
X * internal names are not the berknet host names.
X */
Xfiddle(buf)
Xchar *buf;
X{
X	char uucpname[100];
X	register struct network *netptr;
X	char *rest;
X
X	strcpy(uucpname, buf);
X	rest = index(uucpname, ':');
X	*rest++ = 0;
X#ifdef debug
X	printf("uucpname = '%s', buf = '%s', rest = '%s'\n", uucpname, buf, rest);
X#endif
X	for (netptr = &berknet[0]; strcmp(netptr->uucpname, uucpname) && netptr->uucpname; netptr++)
X		;
X	if (netptr->uucpname)
X		sprintf(buf, "%s:%s", netptr->berkname, rest);
X	else
X		sprintf(buf, "UNKNOWN:%s", rest);
X}
END_OF_FILE
if test 2809 -ne `wc -c <'berknews.c'`; then
    echo shar: \"'berknews.c'\" unpacked with wrong size!
fi
# end of 'berknews.c'
fi
if test -f 'vnews.help' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'vnews.help'\"
else
echo shar: Extracting \"'vnews.help'\" \(1326 characters\)
sed "s/^X//" >'vnews.help' <<'END_OF_FILE'
XVnews commands:    (each may be preceded by a non-negative count)
X
XCR  Next page or article                D   Decrypt a rot 13 joke
Xn   Go to next article                  A   Go to article numbered count
Xe   Mark current article as unread      <   Go to article with given ID
X+   Go forwards count articles          p   Go to parent article
X-   Go to previous article              ug  Unsubscribe to this group
X^B  Go backwards count pages            ^L  Redraw screen
X^N  Go forward count lines              v   Print netnews version
X^P  Go backwards count lines            q   Quit
X^D  Go forward half a page              x   Quit without updating .newsrc
X^U  Go backwards half a page            c   Cancel the current article
Xh   Display article header              H   Display all article headers
X!   Escape to shell                     ?   Display this message
Xr   Reply to article using editor       K   Mark rest of newsgroup read
XR   Reply--put current article in reply b   Go back 1 article in same group
XESC-r  Reply directly using mailer      m   Move on to next item in a digest
Xf   Post a followup article             s   Save article in file
XN   Go to newsgroup (next is default)   w   Save without header
Xl   List unread articles in group       L   List all articles in group
X
X[Press ^L to see article again]
END_OF_FILE
if test 1326 -ne `wc -c <'vnews.help'`; then
    echo shar: \"'vnews.help'\" unpacked with wrong size!
fi
# end of 'vnews.help'
fi
if test -f 'sendnews.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'sendnews.c'\"
else
echo shar: Extracting \"'sendnews.c'\" \(1640 characters\)
sed "s/^X//" >'sendnews.c' <<'END_OF_FILE'
X/*
X * sendnews - send news article by mail.
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)sendnews.c	2.12	3/21/87";
X#endif /* SCCSID */
X
X#include <stdio.h>
X#include <ctype.h>
X#include "defs.h"
X
Xchar buffer[BUFSIZ];
X
Xint linecount, oflag = 0, aflag = 0, bflag = 0, toflag = 0;
X
Xextern FILE *popen();
X
X/* ARGSUSED */
Xmain(argc, argv)
Xchar **argv;
X{
X	register FILE *out;
X	char newsgroup[BUFSIZ];
X
X	while (**(++argv) == '-') {
X		if (*++*argv == 'o')
X			oflag++;
X		else if (**argv == 'a')
X			aflag++;
X		else if (**argv == 'b')
X			bflag++;
X		else if (**argv == 'n')
X			strcpy(newsgroup, *(++argv));
X	}
X	if (aflag && bflag) {
X		fprintf(stderr, "'-a' and '-b' options mutually exclusive.\n");
X		exit(1);
X	}
X
X#ifdef DEBUG
X	printf("/bin/mail %s\n", *argv);
X	sprintf(buffer, "cat");
X#else
X#ifdef SENDMAIL
X	(void) sprintf(buffer, "%s -i -odq %s", SENDMAIL, *argv);
X#else /* !SENDMAIL */
X	(void) sprintf(buffer, "/bin/mail %s", *argv);
X#endif /* !SENDMAIL */
X#endif
X	if ((out = popen(buffer, "w")) == NULL) {
X		perror(buffer);
X		exit(1);
X	}
X
X	/* Standard mail prelude to make the formatters happy */
X	fprintf(out, "Subject: network news article\n");
X	fprintf(out, "To: %s\n\n", *argv);
X
X	while (fgets(buffer, sizeof buffer, stdin)) {
X		if (*newsgroup && ngline()) {
X			if (oflag)
X				sprintf(buffer, "%s\n", newsgroup);
X			else
X				sprintf(buffer, "Newsgroups: %s\n", newsgroup);
X		}
X		putc('N', out);
X		fputs(buffer, out);
X		if (ferror(out))
X			exit(1);
X	}
X	pclose(out);
X	exit(0);
X}
X
Xngline()
X{
X	if (oflag)
X		return linecount == 2;
X	if (!toflag && (!strncmp("Newsgroups: ", buffer, 12) ||
X		!strncmp("To: ",buffer, 4)))
X		return ++toflag;
X	return 0;
X}
END_OF_FILE
if test 1640 -ne `wc -c <'sendnews.c'`; then
    echo shar: \"'sendnews.c'\" unpacked with wrong size!
fi
# end of 'sendnews.c'
fi
if test -f 'sendbatch.sh' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'sendbatch.sh'\"
else
echo shar: Extracting \"'sendbatch.sh'\" \(1231 characters\)
sed "s/^X//" >'sendbatch.sh' <<'END_OF_FILE'
X: '@(#)sendbatch.sh	1.10	9/23/86'
X
Xcflags=
XLIM=50000
XCMD='LIBDIR/batch BATCHDIR/$rmt $BLIM'
XECHO=
XCOMP=
XC7=
XDOIHAVE=
XRNEWS=rnews
X
Xfor rmt in $*
Xdo
X	case $rmt in
X	-[bBC]*)	cflags="$cflags $rmt"; continue;;
X	-s*)	LIM=`expr "$rmt" : '-s\(.*\)'`
X		continue;;
X	-c7) 	COMP='| LIBDIR/compress $cflags'
X		C7='| LIBDIR/encode'
X		ECHO='echo "#! c7unbatch"'
X		continue;;
X	-c)	COMP='| LIBDIR/compress $cflags'
X		ECHO='echo "#! cunbatch"'
X		continue;;
X	-o*)	ECHO=`expr "$rmt" : '-o\(.*\)'`
X		RNEWS='cunbatch'
X		continue;;
X	-i*)	DOIHAVE=`expr "$rmt" : '-i\(.*\)'`
X		if test -z "$DOIHAVE"
X		then
X			DOIHAVE=`uuname -l`
X		fi
X		continue;;
X	esac
X
X	if test -n "$COMP"
X	then
X		BLIM=`expr $LIM \* 2`
X	else
X		BLIM=$LIM
X	fi
X
X	: make sure $? is zero
X	while test $? -eq 0 -a \( -s BATCHDIR/$rmt -o -s BATCHDIR/$rmt.work -o  \( -n "$DOIHAVE" -a -s BATCHDIR/$rmt.ihave \) \)
X	do
X		if test -n "$DOIHAVE" -a -s BATCHDIR/$rmt.ihave
X		then
X			mv BATCHDIR/$rmt.ihave BATCHDIR/$rmt.$$
X			LIBDIR/inews -t "cmsg ihave $DOIHAVE" -n to.$rmt.ctl < \
X				BATCHDIR/$rmt.$$
X			rm BATCHDIR/$rmt.$$
X					
X		else
X			(eval $ECHO; eval $CMD $COMP $C7) |
X			if test -s BATCHDIR/$rmt.cmd
X			then
X				BATCHDIR/$rmt.cmd
X			else
X				uux - UUXFLAGS $rmt!$RNEWS
X			fi
X		fi
X	done
Xdone
END_OF_FILE
if test 1231 -ne `wc -c <'sendbatch.sh'`; then
    echo shar: \"'sendbatch.sh'\" unpacked with wrong size!
fi
# end of 'sendbatch.sh'
fi
echo shar: End of shell archive.
exit 0
-- 
		"Zeta Microcomputer Software"
ACSnet:    nick@ultima.cs.uts.oz
UUCP:      ...!uunet!munnari!ultima.cs.uts.oz!nick
Fidonet:   Nick Andrew on 3:713/602 (Zeta)

nick@ultima.cs.uts.oz (Nick Andrew) (12/07/89)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  rextern.c ndir.c iextern.c help header.h rmgroup.sh
#   patchlevel.h iparams.h ftime.c
# Wrapped by nick@nswitgould on Thu Dec  7 22:44:29 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'rextern.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rextern.c'\"
else
echo shar: Extracting \"'rextern.c'\" \(1456 characters\)
sed "s/^X//" >'rextern.c' <<'END_OF_FILE'
X/*
X * rextern - external definitions for readnews
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)rextern.c	2.17	11/21/86";
X#endif /* SCCSID */
X
X/*LINTLIBRARY*/
X
X#include "rparams.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			*/
X#ifdef	MINIX
X/* mode is external */
Xextern int	mode;				/* mode of news program		*/
X#else
Xint	mode;				/* mode of news program		*/
X#endif
X
Xstruct hbuf header;			/* general-use header structure	*/
Xchar	bfr[LBUFLEN];			/* general-use scratch area	*/
X
X#ifndef ROOTID
Xint	ROOTID;				/* special users id #		*/
X#endif
X
Xchar	*outfile = "/tmp/M1XXXXXX";	/* output file for -M and -c	*/
Xchar	*infile = "/tmp/M2XXXXXX";	/* -T output from Mail		*/
Xint	ngrp, line = -1;
X
Xchar	filename[BUFLEN];
Xchar	afline[BUFLEN];
XFILE	*rcfp, *actfp;
Xtime_t	atime;
Xchar	newsrc[BUFLEN], groupdir[BUFLEN], *rcline[LINES], rcbuf[LBUFLEN];
Xchar	*bitmap, *argvrc[LINES];
Xlong	bit, obit, last;
Xint	readmode = NEXT;
Xint	news = 0;		/* Was there any news to read */
Xint	actdirect = FORWARD;	/* read direction in ACTIVE file */
Xint	rcreadok = FALSE;	/* NEWSRC has been read OK */
Xint	zapng = FALSE;		/* ! out this newsgroup on next updaterc */
Xlong	ngsize;			/* max article # in this newsgroup */
Xlong	minartno;		/* min article # in this newsgroup */
X
X#ifndef SHELL
Xchar	*SHELL;
X#endif
X
X#ifndef MAILER
Xchar	*MAILER;
X#endif
X
Xchar	*PAGER = "";
END_OF_FILE
if test 1456 -ne `wc -c <'rextern.c'`; then
    echo shar: \"'rextern.c'\" unpacked with wrong size!
fi
# end of 'rextern.c'
fi
if test -f 'ndir.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ndir.c'\"
else
echo shar: Extracting \"'ndir.c'\" \(2564 characters\)
sed "s/^X//" >'ndir.c' <<'END_OF_FILE'
X#include "defs.h"
X#if !defined(BSD4_2) && !defined(BSD4_1C) && !defined(HP9K5)
X#ifndef MINIX
X#include <sys/param.h>
X#endif
X#include "ndir.h"
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)ndir.c	1.11	3/20/87";
X#endif /* SCCSID */
X
X/*
X * support for Berkeley directory reading routine on a V7 file system
X */
X
Xextern char *malloc();
X
X/*
X * open a directory.
X */
XDIR *
Xopendir(name)
Xchar *name;
X{
X	register DIR *dirp;
X	register int fd;
X
X	if ((fd = open(name, 0)) == -1)
X		return NULL;
X	if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
X		close (fd);
X		return NULL;
X	}
X	dirp->dd_fd = fd;
X	dirp->dd_loc = 0;
X	return dirp;
X}
X
X/*
X * read an old style directory entry and present it as a new one
X */
X#ifdef pyr
X/* Pyramid in the AT&T universe */
X#define ODIRSIZ 248
Xstruct olddirect {
X	long	od_ino;
X	short	od_fill1, od_fill2;
X	char od_name[ODIRSIZ];
X};
X#else /* V7 file system */
X#define	ODIRSIZ	14
X
Xstruct	olddirect {
X	short	od_ino;
X	char	od_name[ODIRSIZ];
X};
X#endif /* !pyr */
X
X/*
X * get next entry in a directory.
X */
Xstruct direct *
Xreaddir(dirp)
Xregister DIR *dirp;
X{
X	register struct olddirect *dp;
X	static struct direct dir;
X
X	for (;;) {
X		if (dirp->dd_loc == 0) {
X			dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, 
X			    DIRBLKSIZ);
X			if (dirp->dd_size <= 0)
X				return NULL;
X		}
X		if (dirp->dd_loc >= dirp->dd_size) {
X			dirp->dd_loc = 0;
X			continue;
X		}
X		dp = (struct olddirect *)(dirp->dd_buf + dirp->dd_loc);
X		dirp->dd_loc += sizeof(struct olddirect);
X		if (dp->od_ino == 0)
X			continue;
X		dir.d_ino = dp->od_ino;
X		strncpy(dir.d_name, dp->od_name, ODIRSIZ);
X		dir.d_name[ODIRSIZ] = '\0'; /* insure null termination */
X		dir.d_namlen = strlen(dir.d_name);
X		dir.d_reclen = DIRSIZ(&dir);
X		return (&dir);
X	}
X}
X
X/*
X * close a directory.
X */
Xvoid
Xclosedir(dirp)
Xregister DIR *dirp;
X{
X	close(dirp->dd_fd);
X	dirp->dd_fd = -1;
X	dirp->dd_loc = 0;
X	free((char *)dirp);
X}
X
X/*
X * seek to an entry in a directory.
X * Only values returned by "telldir" should be passed to seekdir.
X */
Xvoid
Xseekdir(dirp, loc)
Xregister DIR *dirp;
Xlong loc;
X{
X	long curloc, base, offset;
X	struct direct *dp;
X	long lseek(), telldir();
X
X	curloc = telldir(dirp);
X	if (loc == curloc)
X		return;
X	base = loc & ~(DIRBLKSIZ - 1);
X	offset = loc & (DIRBLKSIZ - 1);
X	(void) lseek(dirp->dd_fd, base, 0);
X	dirp->dd_loc = 0;
X	while (dirp->dd_loc < offset) {
X		dp = readdir(dirp);
X		if (dp == NULL)
X			return;
X	}
X}
X
X/*
X * return a pointer into a directory
X */
Xlong
Xtelldir(dirp)
XDIR *dirp;
X{
X	return lseek(dirp->dd_fd, 0L, 1) - dirp->dd_size + dirp->dd_loc;
X}
X#endif /* !BSD4_2 && !BSD4_1C && !HP9K5 */
END_OF_FILE
if test 2564 -ne `wc -c <'ndir.c'`; then
    echo shar: \"'ndir.c'\" unpacked with wrong size!
fi
# end of 'ndir.c'
fi
if test -f 'iextern.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'iextern.c'\"
else
echo shar: Extracting \"'iextern.c'\" \(1118 characters\)
sed "s/^X//" >'iextern.c' <<'END_OF_FILE'
X/*
X * iextern - external definitions for inews.
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)iextern.c	2.17	9/19/86";
X#endif /* SCCSID */
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[BUFLEN];		/* general-use file name */
Xchar	not_here[SBUFLEN];		/* name of system not to xmit to */
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	is_ctl;				/* true for a control message */
Xchar	is_mod[NAMELEN];		/* contains newsgroup if moderated */
END_OF_FILE
if test 1118 -ne `wc -c <'iextern.c'`; then
    echo shar: \"'iextern.c'\" unpacked with wrong size!
fi
# end of 'iextern.c'
fi
if test -f 'help' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'help'\"
else
echo shar: Extracting \"'help'\" \(1149 characters\)
sed "s/^X//" >'help' <<'END_OF_FILE'
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.
Xl		List unread articles in newsgroup.
XL		List all articles in newsgroup.
Xc, f, r, e, h, and s can be followed by -'s to refer to the previous article
END_OF_FILE
if test 1149 -ne `wc -c <'help'`; then
    echo shar: \"'help'\" unpacked with wrong size!
fi
# end of 'help'
fi
if test -f 'header.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'header.h'\"
else
echo shar: Extracting \"'header.h'\" \(1239 characters\)
sed "s/^X//" >'header.h' <<'END_OF_FILE'
X/*
X * header.h - Article header format
X */
X
X/*	@(#)header.h	2.20	2/22/87	*/
X
X#define NUNREC 50
X
X/* article header */
Xstruct	hbuf {
X	char	from[BUFLEN];		/* From:		*/
X	char	path[PATHLEN];		/* Path:		*/
X	char	nbuf[LBUFLEN];		/* Newsgroups:		*/
X	char	title[BUFLEN];		/* Subject:		*/
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	expdate[DATELEN];	/* Expires:		*/
X	char	ctlmsg[PATHLEN];	/* Control:		*/
X	char	sender[BUFLEN];		/* Sender:		*/
X	char	followto[BUFLEN];	/* Followup-to:		*/
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	summary[BUFLEN];	/* Summary:		*/
X	char	approved[BUFLEN];	/* Approved:		*/
X	char	nf_id[BUFLEN];		/* Nf-ID:		*/
X	char	nf_from[BUFLEN];	/* Nf-From:		*/
X#ifdef DOXREFS
X	char 	xref[BUFLEN];		/* Xref:		*/
X#endif /* DOXREFS */
X	char	*unrec[NUNREC];		/* unrecognized lines	*/
X};
X
X#define hwrite(hp,fp)	ihwrite(hp,fp,0)
X#define lhwrite(hp,fp)	ihwrite(hp,fp,1)
X
Xchar *oident();
END_OF_FILE
if test 1239 -ne `wc -c <'header.h'`; then
    echo shar: \"'header.h'\" unpacked with wrong size!
fi
# end of 'header.h'
fi
if test -f 'rmgroup.sh' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rmgroup.sh'\"
else
echo shar: Extracting \"'rmgroup.sh'\" \(832 characters\)
sed "s/^X//" >'rmgroup.sh' <<'END_OF_FILE'
X: '@(#)rmgroup.sh	1.8	12/16/86'
Xfor group
Xdo
X	qgrp="`echo $group | sed 's/\./\\\./g'`"
X	if
X		grep -s "^$qgrp " LIBDIR/active
X	then
X		echo "Removing newsgroup $group"
X		echo "/^$qgrp[ 	]/d" >>/tmp/,edit$$
X		dir=SPOOLDIR/"`echo $group | sed 's/\./\//g'`"
X		if test  -d $dir
X		then
X			rm $dir/* >/dev/null 2>&1
X			echo "rmdir $dir >/dev/null 2>&1" >>/tmp/,rmdir$$
X		else
X			echo "$0: $dir: no spool directory" 2>&1
X		fi
X	else
X		echo "$0: $group: no such newsgroup" 2>&1
X	fi
Xdone
Xecho w >>/tmp/,edit$$
Xecho q >>/tmp/,edit$$
Xecho "Editing LIBDIR/active..."
Xed - LIBDIR/active < /tmp/,edit$$
XFIXACTIVE
Xecho "Editing LIBDIR/newsgroups..."
Xed - LIBDIR/newsgroups < /tmp/,edit$$
Xecho "Removing directories..."
Xif test -s /tmp/,rmdir$$
Xthen
X	sort +1r -o /tmp/,rmdir$$ /tmp/,rmdir$$
X	. /tmp/,rmdir$$
Xfi
Xrm -f /tmp/,edit$$ /tmp/,rmdir$$
Xexit 0
END_OF_FILE
if test 832 -ne `wc -c <'rmgroup.sh'`; then
    echo shar: \"'rmgroup.sh'\" unpacked with wrong size!
fi
# end of 'rmgroup.sh'
fi
if test -f 'patchlevel.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'patchlevel.h'\"
else
echo shar: Extracting \"'patchlevel.h'\" \(62 characters\)
sed "s/^X//" >'patchlevel.h' <<'END_OF_FILE'
X#define	PATCHLEVEL	8
X
X#define NEWS_VERSION   "B 2.11 4/10/87"
END_OF_FILE
if test 62 -ne `wc -c <'patchlevel.h'`; then
    echo shar: \"'patchlevel.h'\" unpacked with wrong size!
fi
# end of 'patchlevel.h'
fi
if test -f 'iparams.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'iparams.h'\"
else
echo shar: Extracting \"'iparams.h'\" \(577 characters\)
sed "s/^X//" >'iparams.h' <<'END_OF_FILE'
X/*
X * iparams - parameters for inews.
X */
X
X/*	@(#)iparams.h	2.17	11/21/86	*/
X
X#include "params.h"
X#include <errno.h>
Xextern int errno;
X
X/* external declarations specific to inews */
Xextern	char	nbuf[LBUFLEN], *ARTICLE, *INFILE, *ALIASES, *PARTIAL;
X#ifndef ROOTID
Xextern	int	ROOTID;
X#endif
X
X#ifdef NOTIFY
Xextern	char	*TELLME;
X#endif /* NOTIFY */
X
Xstruct msgtype {
X	char *m_name;
X	char *m_who_to;
X	int (*m_func)();
X};
X
Xextern struct msgtype msgtype[];
X
Xextern	FILE	*infp, *actfp;
Xextern	int	tty, is_ctl;
Xextern	char	filename[BUFLEN], is_mod[NAMELEN], not_here[SBUFLEN], *DFLTNG;
END_OF_FILE
if test 577 -ne `wc -c <'iparams.h'`; then
    echo shar: \"'iparams.h'\" unpacked with wrong size!
fi
# end of 'iparams.h'
fi
if test -f 'ftime.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ftime.c'\"
else
echo shar: Extracting \"'ftime.c'\" \(506 characters\)
sed "s/^X//" >'ftime.c' <<'END_OF_FILE'
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)ftime.c	2.5	4/26/85";
X#endif /* SCSCID */
X
X#include <sys/types.h>
Xstruct timeb
X{
X	time_t	time;
X	unsigned short millitm;
X	short	timezone;
X	short	dstflag;
X};
X
X#ifndef MINIX
Xextern long timezone;
Xextern int  daylight;
X#else
X/* We are using dummies for these two variables */
Xlong timezone = 0L;
Xint  daylight = 0;
X#endif MINIX
X
Xftime(tp)
Xstruct timeb *tp;
X{
X	long t;
X
X	time(&t);
X	tp->time = t;
X	tp->millitm = 0;
X	tp->timezone = timezone/60;
X	tp->dstflag = daylight;
X}
END_OF_FILE
if test 506 -ne `wc -c <'ftime.c'`; then
    echo shar: \"'ftime.c'\" unpacked with wrong size!
fi
# end of 'ftime.c'
fi
echo shar: End of shell archive.
exit 0
-- 
		"Zeta Microcomputer Software"
ACSnet:    nick@ultima.cs.uts.oz
UUCP:      ...!uunet!munnari!ultima.cs.uts.oz!nick
Fidonet:   Nick Andrew on 3:713/602 (Zeta)

nick@ultima.cs.uts.oz (Nick Andrew) (12/21/89)

It was just pointed out to me (and most politely, if I do say so) that I
missed posting one file in my recent News 2.11 posting. This file was
ndir.h ... somehow I posted ndir.c twice.

Additionally, for those without a Yacc, I have converted getdate.y into
getdate.c using BSD Yacc and included that in this posting.

There seems to have been a resounding lack of response from the newsgroup
about these News postings. Is it just the Xmas cheer (and holidays), or
are the postings not getting out?

	M E R R Y   C H R I S T M A S   T O   A L L   . . .

Nick.

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  ndir.h getdate.c
# Wrapped by nick@ultima on Thu Dec 21 23:19:46 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'ndir.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ndir.h'\"
else
echo shar: Extracting \"'ndir.h'\" \(1281 characters\)
sed "s/^X//" >'ndir.h' <<'END_OF_FILE'
X/* @(#)ndir.h	1.6	3/9/87	*/
X#if defined(HP9K5)
X/* He should have included it instead of this, but prevent confusion */
X#include <sys/ndir.h>
X#else /* other */
X#ifndef DEV_BSIZE
X#define	DEV_BSIZE	512
X#endif
X#define DIRBLKSIZ	DEV_BSIZE
X#define	MAXNAMLEN	255
X
Xstruct	direct {
X	long	d_ino;			/* inode number of entry */
X	short	d_reclen;		/* length of this record */
X	short	d_namlen;		/* length of string in d_name */
X	char	d_name[MAXNAMLEN + 1];	/* name must be no longer than this */
X};
X
X/*
X * The DIRSIZ macro gives the minimum record length which will hold
X * the directory entry.  This requires the amount of space in struct direct
X * without the d_name field, plus enough space for the name with a terminating
X * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary.
X */
X
X#ifdef DIRSIZ
X#undef DIRSIZ
X#endif /* DIRSIZ */
X#define DIRSIZ(dp) \
X    ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
X
X/*
X * Definitions for library routines operating on directories.
X */
Xtypedef struct _dirdesc {
X	int	dd_fd;
X	long	dd_loc;
X	long	dd_size;
X	char	dd_buf[DIRBLKSIZ];
X} DIR;
X#ifndef NULL
X#define NULL 0
X#endif
Xextern	DIR *opendir();
Xextern	struct direct *readdir();
Xextern	void closedir();
X
X#define rewinddir(dirp)	seekdir((dirp), (long)0)
X#endif /* other */
END_OF_FILE
if test 1281 -ne `wc -c <'ndir.h'`; then
    echo shar: \"'ndir.h'\" unpacked with wrong size!
fi
# end of 'ndir.h'
fi
if test -f 'getdate.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'getdate.c'\"
else
echo shar: Extracting \"'getdate.c'\" \(19766 characters\)
sed "s/^X//" >'getdate.c' <<'END_OF_FILE'
X# define ID 257
X# define MONTH 258
X# define DAY 259
X# define MERIDIAN 260
X# define NUMBER 261
X# define UNIT 262
X# define MUNIT 263
X# define SUNIT 264
X# define ZONE 265
X# define DAYZONE 266
X# define AGO 267
X
X# line 3 "getdate.y"
X	/* 	Originally from: Steven M. Bellovin (unc!smb)	*/ 
X	/*	Dept. of Computer Science			*/
X	/*	University of North Carolina at Chapel Hill	*/
X	/*	@(#)getdate.y	2.15	12/16/86	*/
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
X#include "defs.h"
X#if defined(BSD4_2) || defined (BSD4_1C)
X#include <sys/time.h>
X#else /* sane */
X#include <time.h>
X#endif /* sane */
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#define yyclearin yychar = -1
X#define yyerrok yyerrflag = 0
Xextern int yychar;
Xextern short yyerrflag;
X#ifndef YYMAXDEPTH
X#define YYMAXDEPTH 150
X#endif
X#ifndef YYSTYPE
X#define YYSTYPE int
X#endif
XYYSTYPE yylval, yyval;
X# define YYERRCODE 256
X
X# line 121 "getdate.y"
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();
X
Xtime_t
Xdateconv(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
Xdayconv(ord, day, now)
Xint 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
Xtimeconv(hh, mm, ss, mer)
Xregister 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
Xmonthadd(sdate, relmonth)
Xtime_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
Xdaylcorr(future, now)
Xtime_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))
X			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++))
X				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
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)
Xchar *id;
X{
X#define gotit (yylval=i->value,  i->type)
X
X	char idvar[128];
X	register char *j, *k;
X	register struct table *i;
X	int abbrev;
X
X	(void) strcpy(idvar, id);
X	j = idvar;
X	k = id - 1;
X	while (*++k)
X		*j++ = isupper(*k) ? tolower(*k) : *k;
X	*j = '\0';
X
X	if (strlen(idvar) == 3)
X		abbrev = 1;
X	else
X		if (strlen(idvar) == 4 && idvar[3] == '.') {
X			abbrev = 1;
X			idvar[3] = '\0';
X		}
X	else
X		abbrev = 0;
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)
X				return gotit;
X			if (j[-1] == 0)
X				return gotit;
X		}
X	}
X
X	for (i = mztab; i->name; i++)
X		if (strcmp(i->name, idvar) == 0)
X			return gotit;
X
X	for (i=mztab; i->name; i++)
X		if (strcmp(i->name, idvar) == 0)
X			return gotit;
X
X	for (i=unittb; i->name; i++)
X		if (strcmp(i->name, idvar) == 0)
X			return gotit;
X
X	if (idvar[strlen(idvar)-1] == 's')
X		idvar[strlen(idvar)-1] = '\0';
X
X	for (i=unittb; i->name; i++)
X		if (strcmp(i->name, idvar) == 0)
X			return gotit;
X
X	for (i = othertb; i->name; i++)
X		if (strcmp(i->name, idvar) == 0)
X			return gotit;
X
X	if (strlen(idvar) == 1 && isalpha(*idvar)) {
X		for (i = milzone; i->name; i++)
X			if (strcmp(i->name, idvar) == 0)
X				return gotit;
X	}
X
X	return ID;
X}
X
Xtime_t
Xgetdate(p, now)
Xchar *p;
Xstruct 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{}
Xshort yyexca[] ={
X-1, 1,
X	0, -1,
X	-2, 0,
X	};
X# define YYNPROD 35
X# define YYLAST 220
Xshort yyact[]={
X
X  12,  13,  21,   9,  14,  15,  16,  10,  11,  34,
X  17,  39,  40,  19,  38,  37,  36,  30,  29,  28,
X  26,  35,  31,  27,   8,   7,   6,   5,   4,   3,
X   2,   1,   0,   0,   0,   0,   0,   0,   0,   0,
X   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
X   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
X   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
X   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
X   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
X   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
X   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
X   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
X   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
X   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
X   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
X   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
X   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
X   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
X   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
X   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
X   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
X   0,  32,  33,  22,  20,  18,   0,  23,  24,  25 };
Xshort yypact[]={
X
X-1000,-258,-1000,-1000,-1000,-1000,-1000,-257,-1000, -45,
X-1000,-1000,-241, -21,-1000,-1000,-1000,-1000,-1000,-242,
X-1000,-243,-244,-1000,-1000,-1000, -22,-1000, -49, -26,
X-1000,-245,-1000,-1000,-246,-247,-1000,-249,-1000,-1000,
X-1000 };
Xshort yypgo[]={
X
X   0,  31,  30,  29,  28,  27,  26,  25,  24 };
Xshort yyr1[]={
X
X   0,   1,   1,   2,   2,   2,   2,   2,   2,   8,
X   3,   3,   3,   3,   3,   3,   3,   4,   4,   6,
X   6,   6,   5,   5,   5,   5,   5,   5,   7,   7,
X   7,   7,   7,   7,   7 };
Xshort yyr2[]={
X
X   0,   0,   2,   1,   1,   1,   1,   1,   1,   1,
X   2,   3,   4,   4,   5,   6,   6,   1,   1,   1,
X   2,   2,   3,   5,   2,   4,   2,   3,   2,   2,
X   2,   1,   1,   1,   2 };
Xshort yychk[]={
X
X-1000,  -1,  -2,  -3,  -4,  -5,  -6,  -7,  -8, 261,
X 265, 266, 258, 259, 262, 263, 264, 267, 260,  58,
X 259,  47, 258, 262, 263, 264, 261,  44, 261, 261,
X 261,  44, 260, 261,  58,  47, 261, 261, 261, 260,
X 261 };
Xshort yydef[]={
X
X   1,  -2,   2,   3,   4,   5,   6,   7,   8,   9,
X  17,  18,   0,  19,  31,  32,  33,  34,  10,   0,
X  21,   0,  26,  28,  29,  30,  24,  20,  11,  22,
X  27,   0,  12,  13,   0,   0,  25,  14,  23,  15,
X  16 };
X#ifndef lint
Xstatic	char yaccpar_sccsid[] = "@(#)yaccpar 1.6 88/02/08 SMI"; /* from UCB 4.1 83/02/11 */
X#endif
X
X#
X# define YYFLAG -1000
X# define YYERROR goto yyerrlab
X# define YYACCEPT return(0)
X# define YYABORT return(1)
X
X/*	parser for yacc output	*/
X
X#ifdef YYDEBUG
Xint yydebug = 0; /* 1 for debugging */
X#endif
XYYSTYPE yyv[YYMAXDEPTH]; /* where the values are stored */
Xint yychar = -1; /* current input token number */
Xint yynerrs = 0;  /* number of errors */
Xshort yyerrflag = 0;  /* error recovery flag */
X
Xyyparse() {
X
X	short yys[YYMAXDEPTH];
X	short yyj, yym;
X	register YYSTYPE *yypvt;
X	register short yystate, *yyps, yyn;
X	register YYSTYPE *yypv;
X	register short *yyxi;
X
X	yystate = 0;
X	yychar = -1;
X	yynerrs = 0;
X	yyerrflag = 0;
X	yyps= &yys[-1];
X	yypv= &yyv[-1];
X
X yystack:    /* put a state and value onto the stack */
X
X#ifdef YYDEBUG
X	if( yydebug  ) printf( "state %d, char 0%o\n", yystate, yychar );
X#endif
X		if( ++yyps>= &yys[YYMAXDEPTH] ) { yyerror( "yacc stack overflow" ); return(1); }
X		*yyps = yystate;
X		++yypv;
X		*yypv = yyval;
X
X yynewstate:
X
X	yyn = yypact[yystate];
X
X	if( yyn<= YYFLAG ) goto yydefault; /* simple state */
X
X	if( yychar<0 ) if( (yychar=yylex())<0 ) yychar=0;
X	if( (yyn += yychar)<0 || yyn >= YYLAST ) goto yydefault;
X
X	if( yychk[ yyn=yyact[ yyn ] ] == yychar ){ /* valid shift */
X		yychar = -1;
X		yyval = yylval;
X		yystate = yyn;
X		if( yyerrflag > 0 ) --yyerrflag;
X		goto yystack;
X		}
X
X yydefault:
X	/* default state action */
X
X	if( (yyn=yydef[yystate]) == -2 ) {
X		if( yychar<0 ) if( (yychar=yylex())<0 ) yychar = 0;
X		/* look through exception table */
X
X		for( yyxi=yyexca; (*yyxi!= (-1)) || (yyxi[1]!=yystate) ; yyxi += 2 ) ; /* VOID */
X
X		while( *(yyxi+=2) >= 0 ){
X			if( *yyxi == yychar ) break;
X			}
X		if( (yyn = yyxi[1]) < 0 ) return(0);   /* accept */
X		}
X
X	if( yyn == 0 ){ /* error */
X		/* error ... attempt to resume parsing */
X
X		switch( yyerrflag ){
X
X		case 0:   /* brand new error */
X
X			yyerror( "syntax error" );
X		yyerrlab:
X			++yynerrs;
X
X		case 1:
X		case 2: /* incompletely recovered error ... try again */
X
X			yyerrflag = 3;
X
X			/* find a state where "error" is a legal shift action */
X
X			while ( yyps >= yys ) {
X			   yyn = yypact[*yyps] + YYERRCODE;
X			   if( yyn>= 0 && yyn < YYLAST && yychk[yyact[yyn]] == YYERRCODE ){
X			      yystate = yyact[yyn];  /* simulate a shift of "error" */
X			      goto yystack;
X			      }
X			   yyn = yypact[*yyps];
X
X			   /* the current yyps has no shift onn "error", pop stack */
X
X#ifdef YYDEBUG
X			   if( yydebug ) printf( "error recovery pops state %d, uncovers %d\n", *yyps, yyps[-1] );
X#endif
X			   --yyps;
X			   --yypv;
X			   }
X
X			/* there is no state on the stack with an error shift ... abort */
X
X	yyabort:
X			return(1);
X
X
X		case 3:  /* no shift yet; clobber input char */
X
X#ifdef YYDEBUG
X			if( yydebug ) printf( "error recovery discards char %d\n", yychar );
X#endif
X
X			if( yychar == 0 ) goto yyabort; /* don't discard EOF, quit */
X			yychar = -1;
X			goto yynewstate;   /* try again in the same state */
X
X			}
X
X		}
X
X	/* reduction by production yyn */
X
X#ifdef YYDEBUG
X		if( yydebug ) printf("reduce %d\n",yyn);
X#endif
X		yyps -= yyr2[yyn];
X		yypvt = yypv;
X		yypv -= yyr2[yyn];
X		yyval = yypv[1];
X		yym=yyn;
X			/* consult goto table to find next state */
X		yyn = yyr1[yyn];
X		yyj = yypgo[yyn] + *yyps + 1;
X		if( yyj>=YYLAST || yychk[ yystate = yyact[yyj] ] != -yyn ) yystate = yyact[yypgo[yyn]];
X		switch(yym){
X			
Xcase 3:
X# line 48 "getdate.y"
X
X		{timeflag++;} break;
Xcase 4:
X# line 50 "getdate.y"
X
X		{zoneflag++;} break;
Xcase 5:
X# line 52 "getdate.y"
X
X		{dateflag++;} break;
Xcase 6:
X# line 54 "getdate.y"
X
X		{dayflag++;} break;
Xcase 7:
X# line 56 "getdate.y"
X
X		{relflag++;} break;
Xcase 9:
X# line 60 "getdate.y"
X
X		{if (timeflag && dateflag && !relflag) year = yypvt[-0];
X		else {timeflag++;hh = yypvt[-0]/100;mm = yypvt[-0]%100;ss = 0;merid = 24;}} break;
Xcase 10:
X# line 64 "getdate.y"
X
X		{hh = yypvt[-1]; mm = 0; ss = 0; merid = yypvt[-0];} break;
Xcase 11:
X# line 66 "getdate.y"
X
X		{hh = yypvt[-2]; mm = yypvt[-0]; merid = 24;} break;
Xcase 12:
X# line 68 "getdate.y"
X
X		{hh = yypvt[-3]; mm = yypvt[-1]; merid = yypvt[-0];} break;
Xcase 13:
X# line 70 "getdate.y"
X
X		{hh = yypvt[-3]; mm = yypvt[-1]; merid = 24;
X		daylight = STANDARD; ourzone = -(yypvt[-0]%100 + 60*(yypvt[-0]/100));} break;
Xcase 14:
X# line 73 "getdate.y"
X
X		{hh = yypvt[-4]; mm = yypvt[-2]; ss = yypvt[-0]; merid = 24;} break;
Xcase 15:
X# line 75 "getdate.y"
X
X		{hh = yypvt[-5]; mm = yypvt[-3]; ss = yypvt[-1]; merid = yypvt[-0];} break;
Xcase 16:
X# line 77 "getdate.y"
X
X		{hh = yypvt[-5]; mm = yypvt[-3]; ss = yypvt[-1]; merid = 24;
X		daylight = STANDARD; ourzone = -(yypvt[-0]%100 + 60*(yypvt[-0]/100));} break;
Xcase 17:
X# line 81 "getdate.y"
X
X		{ourzone = yypvt[-0]; daylight = STANDARD;} break;
Xcase 18:
X# line 83 "getdate.y"
X
X		{ourzone = yypvt[-0]; daylight = DAYLIGHT;} break;
Xcase 19:
X# line 86 "getdate.y"
X
X		{dayord = 1; dayreq = yypvt[-0];} break;
Xcase 20:
X# line 88 "getdate.y"
X
X		{dayord = 1; dayreq = yypvt[-1];} break;
Xcase 21:
X# line 90 "getdate.y"
X
X		{dayord = yypvt[-1]; dayreq = yypvt[-0];} break;
Xcase 22:
X# line 93 "getdate.y"
X
X		{month = yypvt[-2]; day = yypvt[-0];} break;
Xcase 23:
X# line 95 "getdate.y"
X
X		{month = yypvt[-4]; day = yypvt[-2]; year = yypvt[-0];} break;
Xcase 24:
X# line 97 "getdate.y"
X
X		{month = yypvt[-1]; day = yypvt[-0];} break;
Xcase 25:
X# line 99 "getdate.y"
X
X		{month = yypvt[-3]; day = yypvt[-2]; year = yypvt[-0];} break;
Xcase 26:
X# line 101 "getdate.y"
X
X		{month = yypvt[-0]; day = yypvt[-1];} break;
Xcase 27:
X# line 103 "getdate.y"
X
X		{month = yypvt[-1]; day = yypvt[-2]; year = yypvt[-0];} break;
Xcase 28:
X# line 107 "getdate.y"
X
X		{relsec +=  60L * yypvt[-1] * yypvt[-0];} break;
Xcase 29:
X# line 109 "getdate.y"
X
X		{relmonth += yypvt[-1] * yypvt[-0];} break;
Xcase 30:
X# line 111 "getdate.y"
X
X		{relsec += yypvt[-1];} break;
Xcase 31:
X# line 113 "getdate.y"
X
X		{relsec +=  60L * yypvt[-0];} break;
Xcase 32:
X# line 115 "getdate.y"
X
X		{relmonth += yypvt[-0];} break;
Xcase 33:
X# line 117 "getdate.y"
X
X		{relsec++;} break;
Xcase 34:
X# line 119 "getdate.y"
X
X		{relsec = -relsec; relmonth = -relmonth;} break;
X		}
X		goto yystack;  /* stack new state and value */
X
X	}
END_OF_FILE
if test 19766 -ne `wc -c <'getdate.c'`; then
    echo shar: \"'getdate.c'\" unpacked with wrong size!
fi
# end of 'getdate.c'
fi
echo shar: End of shell archive.
exit 0
-- 
		"Zeta Microcomputer Software"
ACSnet:    nick@ultima.cs.uts.oz
UUCP:      ...!uunet!munnari!ultima.cs.uts.oz!nick
Fidonet:   Nick Andrew on 3:713/602 (Zeta)