[net.sources] vnews bug fixes

chuqui@nsc.UUCP (06/01/84)

There have been a large number of bug fixes to vnews. Rather than to try to
figure out how to diff it and things, I'm going to post my copy which has
all of the bug fixes I've seen that I found to work. This version should
run on all associated systems if the proper ifdefs are set up including 4.2
with the wonderful new signal mechanisms. This file is visual.c, the next
message will contain virtterm.c.


chuq
----- visual.c -----
/*
 * readr - visual news interface.
 */

static char SccsId[] = "@(#)visual.c	2.26.1	7/12/83";
static char Author[] = "@(#)visual interface written by Kenneth Almquist";
/*
 * visual.c: version 1.7 of 5/25/84
 * 
 */
# ifdef SCCS
static char *sccsid = "@(#)visual.c	1.7 (NSC) 5/25/84";
# endif

/*
 *  Changes to visual.c (W. Sebok astrovax!wls)
 *
 * 1) Added 'H' command to print out full header.
 * 2) Interrupt character was hard-wired to Rubout.  Replaced this with
 *    interrupt (and quit) characters obtained from terminal ioctl's.
 * 3) fixed 4.1 BSD bug which would cause the interrupt character to be
 *    ignored anyway unless set differently than vnews is expecting. This
 *    bug is produced by the fact that the terminal read is *not* aborted
 *    by an interrupt signal in 4.1 BSD when the new signal mechanism is used.
 * 4) Added handling of delete and kill characters to the entry of a number
 *    used as a command parameter.
 * 5) Cleaned up behavior of `s' command when environment variable NEWSBOX
 *    is present:
 *   a) when %s is present in NEWSBOX string added directory creation when
 *      directory is not present.  It also recognizes and rejects the case
 *      when the "directory" is present but not a directory.
 *   b) formerly, saving at article at ~/file would cause the article to
 *      saved at newsdir/groupname/~/file  where "newsdir" is the news
 *      directory and groupname is the name of the news group.  This has
 *	been fixed to instead save the article at $HOME/file.
 * 6) Applied many of the bug fixes given on the net.  This includes the bug fix
 *    I posted to make ^Z not hang vnews when given from the editor invoked by
 *    the "r" or "f" commands.
 * 
 * 7) Fixed it for 4.2 signal package. All sigsets()'s chaned to signal.
 *    Added statement to clear sigmask in SIGSTP handler. Changed
 *    vgetc to use select since that is interrupted by a signal whereas
 *    read isn't (from Mark Calloe -- qubix!msc)
 *
 * 8) Added code from W. Sebok to access pathalias data base for return 
 *    addresses.
 */

#ifdef NETPATHS
#include <dbm.h>
#endif NETPATHS
/* ack! dbm defines null, and causes compiler noises. This shuts it up! */
#ifdef NULL
#undef NULL
#endif NULL

#define STATTOP
#ifdef USG
#include <termio.h>
#include <fcntl.h>
#else
#include <sgtty.h>
#ifdef CBREAK		/* is this BSD or V7? */
#   define BSD 1
#else
#   define V7 1
#endif
#endif USG

#define BSD4_2 1	 /* remember to turn this off for non 4.2 sites! */

#ifdef BSD4_2
#    define BIT(_a) (1<<((_a)-1))
#endif

#include <errno.h>
#include "rparams.h"
#include "dir.h"
#ifdef MYDB
#include "db.h"
#endif

#define even(cols) ((cols&1) ? cols + 1 : cols)

#define ARTWLEN	(ROWS-2)/* number of lines used to display article */
#ifdef STATTOP
#define PRLINE	0	/* prompter line */
#define SPLINE	1	/* secondary prompt line */
#define ARTWIN	2	/* first line of article window */
#define SECPRLEN 81	/* length of secondary prompter */
#else
#define PRLINE	(ROWS-1)/* prompter line */
#define SPLINE	(ROWS-2)/* secondary prompt line */
#define ARTWIN	0	/* first line of article window */
#define SECPRLEN 100	/* length of secondary prompter */
#endif
#define PIPECHAR '|'	/* indicate save command should pipe to program */
/* #define INTR	0177	/* returned by vgetc on interrupt */
#define META	0200	/* meta chatacter bit (as in emacs) */
/* print (display) flags */
#define HDRONLY	0001	/* print header only */
#define NOPRT	0002	/* don't print at all */
#define NEWART	0004	/* force article display to be regenerated */
#define HELPMSG	0010	/* display currently contains help message */
/* prun flags */
#define CWAIT	0001	/* type "continue?" and wait for return */
#define BKGRND	0002	/* run process in the background */
/* values of curflag */
#define CURP1	1	/* cursor after prompt */
#define CURP2	2	/* cursor after secondary prompt */
#define CURHOME	3	/* cursor at home position */
/* flags for vsave routine */
#define SVHEAD	01	/* write out article header */
#define OVWRITE	02	/* overwrite the file if it already exists */
/* shell procedures to reply and post followups */
#ifndef REPLYPROG
#define REPLYPROG "reply"
#endif
#ifndef FOLLOWPROG
#define FOLLOWPROG "follow"
#endif
/* other files */
#ifndef VHELP
#define VHELP "/usr/lib/news/vnews.help"
#endif



#define	saveart	oobit = bit;strcpy(ofilename1, filename);strcpy(ogroupdir, groupdir);hbufcp(&hbuf1, &h);ongsize = pngsize
#define NLINES(h, fp) (h.numlines[0] ? h.intnumlines : (h.intnumlines=linecnt(fp),sprintf(h.numlines, "%d", h.intnumlines), h.intnumlines))

/* terminal handler stuff */
extern int _junked;
#define clearok(xxx, flag) _junked = flag
extern int COLS;
extern int ROWS;
extern int hasscroll;

extern int errno;
FILE *tmpfile();
char *getmailname();
char *findparent();
int onint(), onquit();
int onstop(), catchcont();

/*
 * Kludge: space so that routines can access beyond
 * the end of arrays without messing me up.
 */
static char junk[64];

/* variables shared between vnews routines */
static char linebuf[LBUFLEN];		/* temporary workspace */
static FILE *tfp;			/* temporary file */
static char tfname[] = "/tmp/vnXXXXXX";	/* name of temp file */
static long artbody;			/* offset of body into article */
static int quitflg;			/* if set, then quit */
static int erased;			/* current article has been erased */
static int artlines;			/* # lines in article body */
static int artread;			/* entire article has been read */
static int hdrstart;			/* beginning of header */
static int hdrend;			/* end of header */
static int lastlin;			/* number of lines in tempfile */
static int tflinno = 0;			/* next line in tempfile */
static char secpr[SECPRLEN];		/* secondary prompt */
static char prompt[30];			/* prompter */
static short prflags;			/* print flags (controls updscr) */
static short curflag;			/* where to locate cursor */
static int dlinno;			/* top line on screen */
static char timestr[20];		/* current time */
static int ismail;			/* true if user has mail */
static char *mailf;			/* user's mail file */
static int alflag;			/* set if unprocessed alarm signal */
static int atend;			/* set if at end of article */
static char cerase;			/* erase character */
static char ckill;			/* kill character */
static char cintr = 0177;  /* (WLS) */	/* interrupt character */
       int ospeed;			/* terminal speed */
static int pstatus;			/* status return form process */
static int intflag;			/* set if interrupt received */
#ifdef MYDB
static int hasdb;			/* true if article data base exists */
#endif
#ifdef DIGPAGE
static int endsuba;			/* end of sub-article in digest */
#endif
#ifdef MYDEBUG
FILE *debugf;				/* file to write debugging info on */
#endif


char *tft = "/tmp/folXXXXXX";


/*
 * These were made static for u370 with its buggy cc.
 * I judged it better to have one copy with no ifdefs than
 * to conditionally compile them as automatic variables
 * in readr (which they originally were).  Performance
 * considerations might warrent moving some of the simple
 * things into register variables, but I don't know what
 * breaks the u370 cc.
 */
static char goodone[BUFLEN];		/* last decent article		*/
static char ogroupdir[BUFLEN];		/* last groupdir		*/
static int rfq = 0;			/* for last article		*/
static long ongsize;			/* Previous ngsize		*/
static long pngsize;			/* Printing ngsize		*/
static char *bptr;			/* temp pointer.		*/
static char *tfilename;			/* temporary file name 		*/
static char ofilename1[BUFLEN];		/* previous file name		*/
static struct hbuf hbuf1, hbuf2, *hptr;	/* for minusing			*/
static char *ptr1, *ptr2, *ptr3;	/* for reply manipulation	*/
static int  news = 0;
static int  aabs = FALSE;		/* TRUE if we asked absolutely	*/
static char *ed, tf[100];
static struct hbuf h;			/* ditto.			*/
static int i;
static int oobit;			/* last bit, really		*/
static char *oldsig;
static int dgest = 0;
static FILE *fp;			/* current article to be printed*/
static int holdup;			/* 1 iff should stop before hdr */


readr()
{

#ifdef MYDEBUG
	debugf = fopen("DEBUG", "w");
	setbuf(debugf, NULL);
#endif
	if (aflag) {
		if (*datebuf) {
			if ((atime = cgtdate(datebuf)) == -1)
				xerror("Cannot parse date string");
		} else 
			atime = 0L;
	}

	if (sigtrap)
		xxit(1);
	mktemp(tfname);
	if ((tfp = fopen(tfname, "w+")) == NULL)
		xerror("Can't create temp file");
	unlink(tfname);
	mailf = getmailname();
#ifdef MYDB
	if (opendb() >= 0) {
		hasdb = 1;
		fputs("Using article data base\n", stderr);	/*DEBUG*/
		getng();
	}
#endif
	ttysave();
	signal(SIGINT, onint);
	signal(SIGQUIT, onquit);
	if (sigtrap)
		xxit(1);
	ttyraw();
	timer();

	/* loop reading articles. */
	fp = NULL;
	obit = -1;
	nextng();
	quitflg = 0;
	while (quitflg == 0) {
		if (getnextart(FALSE))
			break;
		strcpy(goodone, filename);
		if (sigtrap)
			return;
		vcmd();
	}

	if (news)
		botscreen();
	ttycooked();
	if (!news)
		fprintf(stderr, "No news.\n");
}

/*
 * Read and execute a command.
 */

vcmd() {
	register c;
	char *p;
	int count;
	int countset;

#ifdef DIGPAGE
	appfile(fp, dlinno + ARTWLEN + 1);
	endsuba = findend(dlinno);
	if (artlines > dlinno + ARTWLEN
	 || endsuba > 0 && endsuba < artlines
#else
	if ((appfile(fp, dlinno + ARTWLEN + 1), artlines > dlinno + ARTWLEN)
#endif
	 || (prflags & HDRONLY) && artlines > hdrend) {
		atend = 0;
		strcpy(prompt, "more? ");
	} else {
		atend = 1;
		strcpy(prompt, "next? ");
		if (! erased)
			clear(bit);		/* article read */
	}
	curflag = CURP1;
	p = prompt + strlen(prompt);
	count = 0;
	countset = 0;

	while ( ( (c = vgetc()) >= '0' && c <= '9') || c==cerase || c==ckill) {
		if (c==cerase) {
			count /= 10;
		}

		if (c==ckill || count==0) {
			countset = 0;
			count = 0;
		}

		if (c!=cerase && c!=ckill) {
			count = (count * 10) + (c - '0');
			countset = 1;
		}
		*p = '\0';
		if (count!=0)
			sprintf(p, "%d", count);
	}

	if (c == '\033') {			/* escape */
		strcat(prompt, "M-");
		c = vgetc();
		if (c != cintr)
			c |= META;
	}
	secpr[0] = '\0';
	if (countset == 0)
		count = 1;
	docmd(c, count);
	if (c != '?' && c != 'H')		/* UGGH */
		prflags &=~ HELPMSG;
	if (dlinno > hdrstart)
		prflags &=~ HDRONLY;
}


/*
 * Process one command, which has already been typed in.
 */
docmd(c, count)
{
	int i;
	char *findhist();

	switch (c) {

	/* Show more of current article, or advance to next article */
	case '\n':
	case ' ':
		prflags &=~ NOPRT;
		if (atend)
			goto next;
		else if (prflags & HDRONLY) {
			prflags &=~ HDRONLY;
			if (hasscroll)
				dlinno = hdrstart;}
#ifdef DIGPAGE
		else if (endsuba > 0)
			dlinno = endsuba;
#endif
		else if ((appfile(fp, dlinno + 2 * ARTWLEN), artread)
		 && hasscroll && artlines - dlinno <= ARTWLEN + 2)
			dlinno = artlines - ARTWLEN;
		else
			dlinno += ARTWLEN;
		break;

	/* No.  Go on to next article. */
next:	case 'n':
		readmode = NEXT;
		if (fp != NULL) {	/* always false? */
			fclose(fp);
			fp = NULL;
		}
		clear(bit);
		saveart;
		nextbit();
		break;


	/* Back up count pages */
	case META|'v':
	case '\2':	/* Control-B */
		dlinno -= ARTWLEN * count;
		if (dlinno < 0)
			dlinno = 0;
		break;

	/* forward half a page */
	case '\4':	/* Control-D, as in vi */
		dlinno += ARTWLEN/2 * count;
		break;

	/* backward half a page */
	case '\25':	/* Control-U */
		dlinno -= ARTWLEN/2 * count;
		if (dlinno < 0)
			dlinno = 0;
		break;

	/* forward count lines */
	case '\16':	/* Control-N */
	case '\32':	/* Control-Z */
		dlinno += count;
		break;

	/* backwards count lines */
	case '\20':	/* Control-P */
	case '\31':	/* Control-Y */
		dlinno -= count;
		if (dlinno < 0)
			dlinno = 0;
		break;

	/* Turn displaying of article back on */
	case 'l':
	case 'd':
		prflags &=~ NOPRT;
		break;

	/* display header */
	case 'h':
		dlinno = hdrstart;
		prflags |= HDRONLY;
		prflags &=~ NOPRT;
		break;

	/*
	 * Unsubscribe to the newsgroup and go on to next group
	 */

	case 'U':
	case 'u':
		strcat(prompt, "u");
		c = vgetc();
		if (c == 'g') {
			obit = -1;
			if (fp != NULL) {
				fclose(fp);
				fp = NULL;
			}
			zapng = TRUE;
			saveart;
			if (nextng()) {
				if (actdirect == BACKWARD)
					msg("Can't back up.");
				else
					quitflg = 1;  /* probably unnecessary */
			}
		} else {
			if (c != cintr && c != ckill)
				msg("Illegal command");
		}
		break;

		/* Print the current version of news */
	case 'v':
		msg("News version: %s", news_version);
		break;


	/* Decrypt joke.  Always does rot 13 */
	case 'D':
		appfile(fp, 32767);
		for (i = hdrend ; i < artlines ; i++) {
			register char c, *p;
			tfget(linebuf, i);
			for (p = linebuf ; (c = *p) != '\0' ; p++) {
				if (c >= 'a' && c <= 'z')
					*p = (c - 'a' + 13) % 26 + 'a';
				else if (c >= 'A' && c <= 'Z')
					*p = (c - 'A' + 13) % 26 + 'A';
			}
			tfput(linebuf, i);
		}
		prflags |= NEWART;
		prflags &=~ (HDRONLY|NOPRT);
		break;

		/* write out the article someplace */
		/* w writes out without the header */
	case 's':
	case 'w': {
		char *grn = groupdir;
		int wflags;

		msg("file: ");
		curflag = CURP2;
		while ((wflags = vgetc()) == ' ');
		if (wflags == cintr) {
			secpr[0] = '\0';
			break;
		}
		if (wflags == '|') {
			linebuf[0] = '|';
			if (prget("| ", linebuf+1))
				break;
		} else {
			pushback(wflags);
			if (prget("file: ", linebuf))
				break;
		}
		wflags = 0;
		if (c == 's')
			wflags |= SVHEAD;
		if (count != 1)
			wflags |= OVWRITE;
		bptr = linebuf;
		if (*bptr != PIPECHAR && *bptr != '/') {
			char	hetyped[BUFLEN];
			char	*boxptr;
			struct stat stbf;
			strcpy(hetyped, bptr);
			if (hetyped[0] == '~' && hetyped[1] == '/') {
				strcpy(hetyped, bptr+2);
				strcpy(bptr, userhome);
			} else {
				if (boxptr = getenv("NEWSBOX")) {
					if (index(boxptr, '%')) {
					    sprintf(bptr, boxptr, grn);
					    if (stat(bptr,&stbf)<0) {
						int status, pid;
						if ((pid=fork())<=0) {
						    setuid(getuid());
						    setgid(getgid());
						    close(1); close(2);
						    open("/dev/null",2); dup(1);
						    execl("/bin/mkdir","mkdir",
							bptr,0);
						    exit(127);
						}
						while ( wait(&status) != pid);
						if (status != 0) {
					msg("Cannot create directory %s",bptr);
						    break;
						}
					    } else {
						if ((stbf.st_mode&S_IFMT)
						 !=  S_IFDIR ) {
						 msg("%s not a directory",bptr);
						 break;
						}
					    }
					} else {
					    strcpy(bptr, boxptr);
					}
				} else {
					bptr[0] = '\0';
				}
			}
			if (bptr[0])
				strcat(bptr, "/");
			if (hetyped[0] != '\0')
				strcat(bptr, hetyped);
			else
				strcat(bptr, "Articles");
		}
		vsave(bptr, wflags);
		break;
	}

		/* back up  */
	case '-':
	case 'b':
minus:
		aabs = TRUE;
		if (!*ofilename1) {
			msg("Can't back up.");
			break;
		}
		if (fp != NULL) {	/* always true? */
			fclose(fp);
			fp = NULL;
		}
		hbufcp(&hbuf2, &h);
		hbufcp(&h, &hbuf1);
		hbufcp(&hbuf1, &hbuf2);
		strcpy(bfr, filename);
		strcpy(filename, ofilename1);
		strcpy(ofilename1, bfr);
		obit = bit;
		if (strcmp(groupdir, ogroupdir)) {
			strcpy(bfr, groupdir);
			selectng(ogroupdir);
			strcpy(groupdir, ogroupdir);
			strcpy(ogroupdir, bfr);
			ngrp = 1;
			back();
		}
		bit = oobit;
		oobit = obit;
		obit = -1;
		getnextart(TRUE);
		return FALSE;

		/* skip forwards */
	case '+':
caseplus:	if (count == 0)
			break;
		saveart;
		last = bit;
		for (i = 0; i < count; i++) {
			nextbit();
			if ((bit > pngsize) || (rflag && bit < 1))
				break;
		}
		if (fp != NULL) {	/* always true? */
			fclose(fp);
			fp = NULL;
		}
		obit = -1;
		break;

	/* exit - time updated to that of most recently read article */
	case 'q':
		quitflg = 1;
		break;


	/* cancel the article. */
	case 'c':
		strcpy(prompt, "cancel [n]? ");
		if (vgetc() != 'y') {
			msg("article not cancelled");
			break;
		}
		cancel_command();
		break;

	/* escape to shell */
	case '!': {
		register char *p;
		int flags;

		p = linebuf;
		if (prget("!", p))
			break;
		flags = CWAIT;
		if (*p == '\0') {
			strcpy(linebuf, SHELL);
			flags = 0;
		}
		while (*p) p++;
		while (p > linebuf && p[-1] == ' ')
			p--;
		if (*--p == '&') {
			*p = '\0';
			flags = BKGRND;
		} else if (*p == '|') {
			*p = '\0';
			sprintf(bfr, "(%s)|mail '%s'", linebuf, username);
			strcpy(linebuf, bfr);
			flags |= BKGRND;
		} else {
			prflags |= NOPRT;
		}
		shcmd(linebuf, flags);
		break;
	}

	/* mail reply */
	case 'r':
		reply(0);
		break;

	/* punt rest of newsgroup */

	case 'K':
		saveart;
		while (bit <= ngsize && bit > 0) {
			clear(bit);
			nextbit();
		}
		break;


	/* next newsgroup */
	case 'N':
		if (fp != NULL) {
			fclose(fp);
			fp = NULL;
		}
		if (next_ng_command())
			quitflg = 1;
		break;

	/* sepecific number */
	case 'A':
		if (count > pngsize) {
			msg("not that many articles");
			break;
		}
		readmode = SPEC;
		aabs = TRUE;
		bit = count;
		obit = -1;
		if (fp) {
			fclose(fp);
			fp = NULL;
		}
		break;

	/* display parent article */
	case 'p':
#ifdef MYDB
		if (hasdb && (ptr3 = findparent(h.ident, &i)) != NULL) {
			msg("parent: %s/%d", ptr3, i);		/*DEBUG*/
			updscr();				/*DEBUG*/
			goto selectart;
		}
#endif
		if (h.followid[0] == '\0') {
			msg("no references line");
			break;
		}
		if ((ptr1 = rindex(h.followid, ' ')) != NULL)
			ptr1++;
		else
			ptr1 = h.followid;
		strcpy(linebuf, ptr1);
		msg("%s", linebuf);
		curflag = CURP2;
		updscr();		/* may take this out later */
		goto searchid;

	/* specific message ID. */
	case '<':
		/* could improve this */
		linebuf[0] = '<';
		if (prget("<", linebuf+1))
			break;
searchid:	secpr[0] = '\0';
		if (index(linebuf, '@') == NULL && index(linebuf, '>') == NULL) {
			ptr1 = linebuf;
			if (*ptr1 == '<')
				ptr1++;
			ptr2 = index(ptr1, '.');
			if (ptr2 != NULL) {
				*ptr2++ = '\0';
				sprintf(bfr, "<%s@%s.UUCP>", ptr2, ptr1);
				strcpy(linebuf, bfr);
			}
		}
		if (index(linebuf, '>') == NULL)
			strcat(linebuf, ">");

		ptr1 = findhist(linebuf);
		if (ptr1 == NULL) {
			msg("%s not found", linebuf);
			break;
		}
		ptr2 = index(ptr1, '\t');
		ptr3 = index(++ptr2, '\t');
		ptr2 = index(++ptr3, ' ');
		if (ptr2)
			*ptr2 = '\0';
		ptr2 = index(ptr3, '/');
		*ptr2++ = '\0';
		sscanf(ptr2, "%d", &i);

		/*
		 * Go to a given article.  Ptr3 specifies the newsgroup
		 * and i specifies the article number.
		 */
selectart:	aabs = TRUE;
		if (fp != NULL) {	/* always true */
			fclose(fp);
			fp = NULL;
		}
		hbufcp(&hbuf1, &h);
		saveart;
		strcpy(ogroupdir, ptr3);
		if (strcmp(groupdir, ogroupdir)) {
			strcpy(bfr, groupdir);
			selectng(ogroupdir);
			strcpy(groupdir, ogroupdir);
			strcpy(ogroupdir, bfr);
			back();
		}
		bit = i;
		oobit = obit;
		obit = -1;
		getnextart(TRUE);
		rfq = 0;
		break;

	/* follow-up article */
	case 'f':
		reply(1);
		break;

	/* erase - pretend we haven't seen this article. */
	case 'e':
			erased = 1;
			set(bit);
			goto caseplus;	/* skip this article for now */
		break;


	case '#':
		msg("Article %d of %ld",
			rfq ? oobit : bit, pngsize);
		break;

		/* error */
	case '?':
		{
			FILE *helpf;
			if ((helpf = fopen(VHELP, "r")) == NULL) {
				msg("Can't open help file");
				break;
			}
			move(ARTWIN, 0);
			while (fgets(linebuf, 1000, helpf) != NULL)
				addstr(linebuf);
			fclose(helpf);
			prflags |= HELPMSG|NEWART;
		}
		break;

	case 'H':
		{
			long oldpos, ftell();
			if (fp == NULL) {
				msg("Bad FP");
				break;
			}
			move(ARTWIN, 0);
			oldpos = ftell(fp);
			fseek(fp,0l,0);
			for (i=0; i<ARTWLEN ; i++)  {
				if (fgets(linebuf, COLS, fp) == NULL) break;
				if (linebuf[0] == '\n') break;
				linebuf[COLS] = '\0';
				addstr(linebuf);
			}
			for (; i<ARTWLEN ; i++) {
				addstr("\n");
			}
			fseek(fp,oldpos,0);
			prflags |= HELPMSG|NEWART;
		}
		break;

	default:
		if (c != ckill && c != cintr)
			msg("Illegal command");
		break;
	}

	return FALSE;
}

cancel_command()
{
	tfilename = filename;
	hptr = &h;
	readmode = SPEC;
	strcpy(rcbuf, hptr->path);
	ptr1 = index(rcbuf, ' ');
	if (ptr1)
		*ptr1 = 0;
	if (uid == ROOTID)
		i = 0;		/* root gets to cancel */
	else
		i = strcmp(username, rcbuf);
	if (i != 0) {
		msg("Can't cancel what you didn't write.");
		return;
	}
	if (!cancel(stderr, hptr, i) && hptr == &h) {
		clear(bit);
		saveart;
		nextbit();
		obit = -1;
		/* fix suggested by watcgl!dmmartindale */
		if (fp != NULL)
			fclose(fp);
		fp = NULL;
		/* end fix */
	}
}

/*
 * Generate replies and followups.
 * We generate a file and then call a shell procedure to do the work.
 */

#define MODGROUPS	"fa.all,mod.all,all.mod,all.announce,"

reply(followup) {
	char *arg[8];
	FILE *tfp;
	char subj[132];
	char frprog[64];
	char *p;
	char *replyname();

#ifdef NETPATHS
	/* Stuff for finding paths in data base */
	static int dbmopen = 0;
	static char newspaths[] = NETPATHS;
	datum key, result;
	register char *p1, *p2;
	char sitename[100];
	/* end of stuff for finding paths in data base */
#endif NETPATHS

	strcpy(tf, tft);
	mktemp(tf);
	if ((tfp = fopen(tf, "w")) == NULL) {
		msg("Can't create %s", tf) ;
		return;
	}
	strcpy(subj, h.title);
	if (!prefix(subj, "Re:") && !prefix(subj, "re:")) {
		strcpy(bfr, subj);
		sprintf(subj, "Re: %s", bfr);
	}
	arg[0] = "/bin/sh";

	/* test for moderated group */
	if (followup) {
		strcpy(linebuf, h.nbuf);
		ngcat(linebuf);
		if (ngmatch(linebuf, MODGROUPS))
			followup = 2;
	}

	if (followup != 1) {		/* send mail */
#ifdef INTERNET
		if (followup == 2 && h.sender[0])
			p = h.sender;
		else
#endif
			p = replyname(&h);
#ifdef NETPATHS
		/* Use data base to find a return path to a site */
		/* W. Sebok, 11/13/83 */
		if (Oflag && dbmopen == 0)
		    dbmopen = (dbminit(newspaths)==0) ? 1 : -1 ;

		if (dbmopen > 0) { 
		    for (p1=p; (p2 = index(p1,'!'))!= NULL; p1 = p2+1);
		    if (p!= p1) {
			key.dptr = sitename;
			p1--;
			do {
			    for (p2 = p1 ; (p1 != p) && (*--p1!='!'); );
			    key.dsize = p2 - p1;
			    strncpy(sitename,p1+1, key.dsize);
			    sitename[key.dsize-1] = '\0';
			    result = fetch(key);
			} while ( (p1 != p) && result.dptr == NULL);
		    }
		}
		if (dbmopen > 0 && result.dptr != NULL) {
		    fprintf(tfp,"To: ");
		    fprintf(tfp,result.dptr,p2+1);
		    fprintf(tfp,"\nSubject: %s\n",subj);
		    if (followup != 2)
			fprintf(tfp,"In-reply-to: your article %s\n",h.ident);
		} else
#endif NETPATHS
		{
		    fprintf(tfp, "To: %s\n", p);
		    fprintf(tfp, "Subject: %s\n", subj);
		    if (followup != 2)
			fprintf(tfp, "In-reply-to: your article %s\n", h.ident);
		}
		/* end of stuff for finding return paths in data base */
		sprintf(frprog,"%s/%s",LIB,REPLYPROG);
		arg[1] = frprog;;
		arg[4] = p;
	} else {
		p = h.nbuf;
		if (h.followto[0])
			p = h.followto;
		strcpy(linebuf, p);
		launder(linebuf);
		fprintf(tfp, "Newsgroups: %s\n", linebuf);
		fprintf(tfp, "Subject: %s\n", subj);
		if (h.keywords[0])
			fprintf(tfp, "Keywords: %s\n", h.keywords);
		if (h.distribution[0])
			fprintf(tfp, "Distribution: %s\n", h.distribution);
		sprintf(frprog,"%s/%s",LIB,FOLLOWPROG);
		arg[1] = frprog;
		arg[4] = linebuf;
	}
	if (followup != 0) {
		bfr[0] = 0;				/* References */
		if (h.followid[0]) {
			strcpy(bfr, h.followid);
			strcat(bfr, " ");
		}
		strcat(bfr, h.ident);
		fprintf(tfp, "References: %s\n", bfr);
	}
	putc('\n', tfp);
	fclose(tfp);

	arg[2] = tf;
	arg[3] = filename;
	/* arg[4] (name or newsgroup) set above */
	arg[5] = NULL;
	prun(arg, 0);
	if (pstatus != 0) {
		if (pstatus != 22 << 8)
			p = "Command failed";
		else if (followup == 1)
			p = "Article not posted";
		else
			p = "Mail not sent";
		msg(p);
	}
	prflags |= NOPRT;
}

next_ng_command()
{
	if (prget("group? ", linebuf))
		return FALSE;
	bptr = linebuf;
	if (!*bptr || *bptr == '-') {
		obit = -1;
		if (*bptr)
			actdirect = BACKWARD;
		saveart;
		if (nextng()) {
			if (actdirect == BACKWARD)
				msg("Can't back up.");
			else
				return TRUE;
		}
		return FALSE;
	}
	while (isspace(*bptr))
		bptr++;
	if (!validng(bptr)) {
		msg("No such group.");
		return FALSE;
	}
	obit = -1;
/*	readmode = SPEC;	tekgds!charliep thinks this is a bug */
	saveart;
	back();
	selectng(bptr);
	return FALSE;
}

/*
 * Find the next article we want to consider, if we're done with
 * the last one, and show the header.
 */
getnextart(minus)
int minus;
{
	int noaccess;
	register DIR *dirp;
	register struct direct *dir;
	long nextnum, tnum;
	long atol();

	noaccess = 0;
	if (minus)
		goto nextart2;	/* Kludge for "-" command. */

	if (bit == obit)     /* Return if still on same article as last time */
		return 0;

nextart:
	if (news) {
		curflag = CURHOME;
		_amove(0, 0);
		vflush();
	}
	dgest = 0;

	/* If done with this newsgroup, find the next one. */
	while ((!rflag && (long) bit > ngsize) || (rflag && bit < 1)) {

/*	tekgds!charliep thinks these two lines are in error 
	while (((long) bit > ngsize) || (rflag && bit < 1)) {
		int i;
 */
		if (i=nextng()) {
			if (actdirect == BACKWARD) {
				msg("Can't back up.");
				actdirect = FORWARD;
				continue;
			} 
			else /* if (rfq++ || pflag || cflag) */
				return 1;
		}
		if (rflag)
			bit = ngsize + 1L;
		else
			bit = -1;
		noaccess = 2;
	}

	/* speed things up by not searching for article -1 */
	if (bit < 0) {
		bit = 0;
		nextbit();
		aabs = FALSE;
		goto nextart;
	}

nextart2:
	if (rcreadok)
		rcreadok = 2;	/* have seen >= 1 article */
	sprintf(filename, "%s/%d", dirname(groupdir), bit);
	if (rfq && goodone[0])	/* ??? */
		strcpy(filename, goodone);
	if (sigtrap == SIGHUP)
		return 1;
	/* Decide if we want to show this article. */
	if ((fp = fopen(filename, "r")) == NULL) {
		/* since there can be holes in legal article numbers, */
		/* we wait till we hit 5 consecutive bad articles */
		/* before we haul off and scan the directory */
		if (++noaccess < 5)
			goto badart;
		dirp = opendir(dirname(groupdir));
		if (dirp == NULL) {
			msg("Can't open %s", dirname(groupdir));
			noaccess = 0;
			goto badart;
		}
		nextnum = rflag ? 0 : ngsize + 1;
		while ((dir = readdir(dirp)) != NULL) {
			if (!dir->d_ino)
				continue;
			tnum = atol(dir->d_name);
			if (tnum <= 0)
				continue;
			if (rflag ? (tnum > nextnum && tnum < bit)
				  : (tnum < nextnum && tnum > bit))
				nextnum = tnum;
		}
		closedir(dirp);
		if (rflag ? (nextnum >= bit) : (nextnum <= bit))
			goto badart;
		do {
			clear(bit);
			nextbit();
		} while (rflag ? (nextnum < bit) : (nextnum > bit));
		obit = -1;
		aabs = FALSE;
		goto nextart;
	} else
		noaccess = 0;
		
	if (hread(&h, fp, TRUE) == NULL
	|| (!rfq && !vselect(&h, aabs))) {
badart:
		if (fp != NULL) {
			fclose(fp);
			fp = NULL;
		}
		clear(bit);
		obit = -1;
		nextbit();
		aabs = FALSE;
		goto nextart;
	}
	aabs = FALSE;
	actdirect = FORWARD;
	news = TRUE;
	{	/* strip off any notesfile header */
		register c;
		register char *p = h.title + strlen(h.title) - 5;
		if (p > h.title
		 && (strcmp(p, " (nf)") == 0 || strcmp(p, "(nf)\"") == 0)) {
			if ((c = getc(fp)) != '#') {
				ungetc(c, fp);
			} else {
				while ((c = getc(fp)) != '\n' && c != EOF);
				while ((c = getc(fp)) != '\n' && c != EOF);
				while ((c = getc(fp)) != '\n' && c != EOF);
			}
		}
	}
	artbody = ftell(fp);
	fmthdr();
	artlines = lastlin;
	artread = 0;
	prflags |= NEWART;
	prflags &=~ NOPRT;
	if (! cflag && hdrend < ARTWLEN && !cflag)
		prflags |= HDRONLY;
	dlinno = 0;
	erased = 0;
	
	obit = bit;
	return 0;
}

/*
 * Print out whatever the appropriate header is
 */
hdr() { abort(); }

fmthdr() {
	char *vbriefdate();

	lastlin = 0;
	if (ngrp) {
		pngsize = ngsize;
		ngrp--;
		if (!hflag) {
			sprintf(linebuf, "Newsgroup %s", groupdir);  tfappend(linebuf);
			tfappend("");
		}
	}
	hdrstart = lastlin;
	if (!hflag) {
		sprintf(linebuf, "Article %s %s",
			h.ident, vbriefdate(h.subdate));
		tfappend(linebuf);
	}
	vhprint(&h, pflag ? 1 : 0);
	sprintf(linebuf, "(%d lines)", NLINES(h, fp)); tfappend(linebuf);
	tfappend("");
	hdrend = lastlin;
}



/* Arpa format: Sat, 14-May-83 03:45:14 EDT */
/* Bugs: doesn't work on article with non-arpa dates */
char *
vbriefdate(q)
	register char *q;
	{
	register char *p;
	register i;
	char day[2];

	p = bfr;
	for (i = 3 ; --i >= 0 ; )
		*p++ = *q++;
	*p++ = ' ';
	q += 2;
	day[0] = *q++;
	if (*q != '-') day[1] = *q++;
	else day[1] = '\0';
	q++;
	for (i = 3 ; --i >= 0 ; )
		*p++ = *q++;
	*p++ = ' ';
	*p++ = day[0];
	if (day[1]) *p++ = day[1];
	q += 5;
	*p++ = ' ';
	if (q[-1] != '0') *p++ = q[-1];
	for (i = 4 ; --i >= 0 ; )
		*p++ = *q++;
	*p++ = '\0';
	return bfr;
}		

/*
 * Print the file header to the temp file.
 */
vhprint(hp, verbose)
register struct hbuf *hp;
int	verbose;
{
	register char	*p1, *p2;
	int	i;
	char	fname[BUFLEN];
	char *tailpath();

	fname[0] = '\0';		/* init name holder */

	p1 = index(hp->from, '(');	/* Find the sender's full name. */
	if (p1 == NULL && hp->path[0])
		p1 = index(hp->path, '(');
	if (p1 != NULL) {
		strcpy(fname, p1+1);
		p2 = index(fname, ')');
		if (p2 != NULL)
			*p2 = '\0';
	}

	sprintf(linebuf, "Subject: %s", hp->title);
	if ((i = strlen(linebuf) - 7) > 9
	 && strcmp(linebuf + i, " - (nf)") == 0
	 && (strncmp(linebuf+9, "Re: ", 4) != 0 || i < 9+39))
		linebuf[i] = '\0';		/* clobber "- (nf)" */
	tfappend(linebuf);
	if (!hflag && hp->keywords[0])
		sprintf(linebuf, "Keywords: %s", hp->keywords), tfappend(linebuf);
	if (verbose) {
		sprintf(linebuf, "From: %s", hp->from); tfappend(linebuf);
		sprintf(linebuf, "Path: %s", hp->path); tfappend(linebuf);
		if (hp->organization[0])
			sprintf(linebuf, "Organization: %s", hp->organization), tfappend(linebuf);
	}
	else {
		if (p1 != NULL)
			*--p1 = '\0';		/* bump over the '(' */
#ifdef INTERNET
		/*
		 * Prefer Path line if it's in internet format, or if we don't
		 * understand internet format here, or if there is no reply-to.
		 */
		sprintf(linebuf, "From: %s", hp->from);
#else
		sprintf(linebuf, "Path: %s", tailpath(hp));
#endif
		if (fname[0] != '\0') {
			strcat(linebuf, " (");
			strcat(linebuf, fname);
			if (hp->organization[0] && !hflag) {
				strcat(linebuf, " @ ");
				strcat(linebuf, hp->organization);
			}
			strcat(linebuf, ")");
		}
		tfappend(linebuf);
		if (p1 != NULL)
			*p1 = ' ';
		if (hp->ctlmsg[0]) {
			sprintf(linebuf, "Control: %s", hp->ctlmsg);
			tfappend(linebuf);
		}
	}

	ngdel(strcpy(bfr, hp->nbuf));
	if (verbose) {
		sprintf(linebuf, "Newsgroups: %s", bfr); tfappend(linebuf);
		sprintf(linebuf, "Date: %s", hp->subdate); tfappend(linebuf);
		if (hp->sender[0])
			sprintf(linebuf, "Sender: %s", hp->sender), tfappend(linebuf);
		if (hp->replyto[0])
			sprintf(linebuf, "Reply-To: %s", hp->replyto), tfappend(linebuf);
		if (hp->followto[0])
			sprintf(linebuf, "Followup-To: %s", hp->followto), tfappend(linebuf);
	}
	else if (strcmp(bfr, groupdir) != 0)
		sprintf(linebuf, "Newsgroups: %s", bfr), tfappend(linebuf);
}




#ifdef MYDB


char *
findparent(id, num)
	char *id;
	int (*num);
	{
	struct artrec a;
	char *ngname();

	if (lookart(id, &a) == DNULL)
		return NULL;
	if (a.parent == DNULL)
		return NULL;
	readrec(a.parent, &a);
	(*num) = a.groups[0].artno;
	return ngname(a.groups[0].newsgroup);
}

#endif


/*
 * Append file to temp file, handling control characters, folding lines, etc.
 * We don't grow the temp file to more than nlines so that a user won't have
 * to wait for 20 seconds to read in a monster file from net.sources.
 * What we really want is coroutines--any year now.
 */

#define ULINE 0200
static char *maxcol;


appfile(iop, nlines)
	register FILE *iop;
	{
	register int c;
	register char *icol;	/* &linebuf[0] <= icol <= maxcol */

	if (artread || artlines >= nlines)
		return;
	maxcol = linebuf;
	icol = linebuf;
	while ((c = getc(iop)) != EOF) {
		switch (c) {
		case ' ':
			if (icol == maxcol && icol < linebuf + LBUFLEN - 1) {
				*icol++ = ' ';
				maxcol = icol;
			} else {
				icol++;
			}
			break;
		case '\t':
			icol = (icol - linebuf &~ 07) + 8 + linebuf;
			growline(icol);
			break;
		case '\b':
			if (icol > linebuf) --icol;
			break;
		case '\n':
			outline();
			if (artlines >= nlines)
				return;
			icol = linebuf;
			break;
		case '\r':
			icol = linebuf;
			break;
		case '\f':
			outline(); outline(); outline();
			if (artlines >= nlines)
				return;
			icol = linebuf;
			break;
		default:
			if (c < ' ' || c > '~')
				break;
			else if (icol >= linebuf + LBUFLEN - 1)
				icol++;
			else if (icol == maxcol) {
				*icol++ = c;
				maxcol = icol; }
			else if (*icol == ' ')
				*icol++ = c;
			else if (c == '_')
				*icol++ |= ULINE;
			else
				*icol++ = (c | ULINE);
			break;
		}
	}
	if (maxcol != linebuf)		/* file not terminated with newline */
		outline();
	artread++;
}



growline(col)
	char *col;
	{
	while (maxcol < col && maxcol < linebuf + LBUFLEN - 1)
		*maxcol++ = ' ';
}


outline() {
	*maxcol = '\0';
	if (strncmp(linebuf, ">From ", 6) == 0) {
		register char *p;
		for (p = linebuf ; (*p = p[1]) != '\0' ; p++);
	}
	tfappend(linebuf);
	if (maxcol > linebuf)
		artlines = lastlin;
	maxcol = linebuf;
}



prget(prompter, buf)
	char *prompter, *buf;
	{
	char *p, *q, *r;
	int c, lastc;

	curflag = CURP2;
	r = buf;
	lastc = '\0';
	for (;;) {
		*r = '\0';
		p = secpr;
		for (q = prompter ; *q ; q++)
			*p++ = *q;
		for (q = buf ; *q ; q++) {
			if (p < &secpr[SECPRLEN-1] && *q >= ' ' && *p <= '~')
				*p++ = *q;
		}
		*p = '\0';
		c = vgetc();
		if (c == '\n' || c == cintr) {
			break;
		}
		if (c == cerase) {
			if (lastc == '\\')
				r[-1] = c;
			else if (r > buf)
				r--;
		} else if (c == ckill) {
			if (lastc == '\\')
				r[-1] = c;
			else
				r = buf;
		} else {
			*r++ = c;
		}
		lastc = c;
	}
	curflag = CURHOME;
	secpr[0] = '\0';
	return (c == cintr);
}



/*
 * Execute a shell command.
 */

shcmd(cmd, flags)
	char *cmd;
	{
	char *arg[4];

	arg[0] = SHELL, arg[1] = "-c", arg[2] = cmd, arg[3] = NULL;
	prun(arg, flags);
}


prun(args, flags)
	char **args;
	{
	int pid;
	int i;
	int (*savequit)();
	char *env[100], **envp;
	char a[BUFLEN + 2];
	extern char **environ;

#ifdef SIGTSTP/* ^Z bug fix Sept 17,1983 W.Sebok */
		signal(SIGTSTP, SIG_DFL);
		signal(SIGTTIN, SIG_DFL);
		signal(SIGTTOU, SIG_DFL);
#endif
	if (!(flags & BKGRND)) {
		botscreen();
		ttycooked();
	}
	while ((pid = fork()) == -1)
		sleep(1);		/* must not clear alarm */
	if (pid == 0) {
		for (i = 3 ; i < 20 ; i++)
			close(i);
		if (flags & BKGRND) {
			signal(SIGINT, SIG_IGN);
			signal(SIGQUIT, SIG_IGN);
			close(0), close(1), close(2);
			open("/dev/null", 2);
			dup(0), dup(0);
		}
		/* set $A */
		sprintf(a, "A=%s", filename);
		env[0] = a;
		for (envp = env + 1 ; *environ != NULL && envp < env + 98 ; environ++)
			if ((*environ)[0] != 'A' || (*environ)[1] != '=')
				*envp++ = *environ;
		*envp = NULL;

		umask(savmask);
		execve(args[0], args, env);
		fprintf(stderr, "%s: not found\n", args[0]);
		exit(20);
	}
	if (!(flags & BKGRND)) {
		savequit = signal(SIGQUIT, SIG_IGN);
		while ((i = wait(&pstatus)) != pid && (i != -1 || errno == EINTR));
		if (flags & CWAIT) {
			fprintf(stderr, "continue? ");
			while ((errno = 0, i = getchar()) != '\n'
				&& (i != EOF || errno == EINTR));
		}
		signal(SIGQUIT, savequit);
		ttyraw();
		clearok(curscr, 1);
#ifdef SIGTSTP/* ^Z bug fix Sept 17,1983 W.Sebok */
		signal(SIGTSTP, onstop);
		signal(SIGTTIN, onstop);
		signal(SIGTTOU, onstop);
#endif
	}
}

#ifdef DIGPAGE


/*
 * Find end of current subarticle in digest.
 */

findend(l) {
	register i;
	register char *p;

	for (i = l ; i < l + ARTWLEN && i < lastlin ; i++) {
		tfget(linebuf, i);
		for (p = linebuf ; *p == '-' ; p++);
		if (p > linebuf + 24)
			return i + 1;
	}
	return 0;
}

#endif


/*** Routines for handling temporary file ***/

/*
 * Append to temp file.
 * Long lines are folded.
 */

tfappend(line)
	char *line;
	{
	while (strlen(line) > COLS) {
		tfput(line, lastlin++);
		line += COLS;
	}
	tfput(line, lastlin++);
}


tfput(line, linno)
	char *line;
	{
	register char *p;
	register FILE *rtfp;		/* try to make it a litte faster */
	register int i;

	p = line, i = even(COLS);
	tfseek(linno, 1);
	rtfp = tfp;
	while (--i >= 0) {
		if (*p)
			putc(*p++, rtfp);
		else
			putc('\0', rtfp);
	}
	tflinno++;
}


tfget(line, linno)
	char *line;
	{
	tfseek(linno, 0);
	fread(line, even(COLS), 1, tfp);
	line[COLS] = '\0';
	tflinno++;
}


tfseek(linno, wrflag) {
	static int last = 1;

	if (linno != tflinno || wrflag != last) {
		fseek(tfp, (long)linno * even(COLS), 0);
		tflinno = linno;
		last = wrflag;
	}
}



msg(s, a1, a2, a3, a4) char *s; {
	sprintf(secpr, s, a1, a2, a3, a4);
}


/*
 * Update the display.
 * The display is entirely controlled by this routine,
 * which means that this routine may get pretty snarled.
 */

static int savelinno = -1;		/* dlinno on last call to updscr */
static int savepr;			/* prflags on last call */

updscr() {
	int count;
	int i;

	if (checkin())
		return;
	if ((prflags & HELPMSG) == 0
	 && (dlinno != savelinno || savepr != prflags)
	 && quitflg == 0) {
		if (dlinno != savelinno)
			prflags &=~ NOPRT;
		count = ARTWLEN;
		if (prflags & NOPRT)
			count = 0;
		if ((prflags & HDRONLY) && count > hdrend)
			count = hdrend - dlinno;
#ifdef DIGPAGE
		if (endsuba > 0 && count > endsuba - dlinno)
			count = endsuba - dlinno;
#endif
		if ((prflags & NEWART) == 0)
			ushift(ARTWIN, ARTWIN+ARTWLEN-1, dlinno - savelinno);
		if (count > lastlin - dlinno)
			count = lastlin - dlinno;
		for (i = ARTWIN ; i < ARTWIN + ARTWLEN ; i++)
			clrline(i);
		for (i = 0 ; i < count ; i++) {
			tfget(linebuf, dlinno + i);
			mvaddstr(ARTWIN + i, 0, linebuf);
		}
		prflags &=~ NEWART;
		savepr = prflags;
		savelinno = dlinno;
	}
	clrline(SPLINE), clrline(PRLINE);
#ifdef STATTOP
	mvaddstr(PRLINE, 0, prompt);
#else
	if (strlen(secpr) <= COLS)
		mvaddstr(PRLINE, 0, prompt);
#endif
	mvaddstr(PRLINE, 48, timestr);
	mvaddstr(PRLINE, 20, groupdir);
	addch(' '); addnum(bit); addch('/'); addnum((int)pngsize); addch(' ');
	if (ismail)
		mvaddstr(PRLINE, 62, ismail > 1? "MAIL" : "mail");
	mvaddstr(SPLINE, 0, secpr);
	if (curflag == CURP1)
		move(PRLINE, strlen(prompt));
	else if (curflag == CURHOME)
		move(0, 0);
	refresh();
}


addnum(n) {
	if (n >= 10)
		addnum(n / 10);
	addch(n % 10 + '0');
}



/*** alarm handler ***/

/* #include <time.h> */


/*
 * Called on alarm signal.
 * Simply sets flag, signal processed later.
 */

onalarm() {
	alflag++;
}


/*
 * Process alarm signal (or start clock)
 */

timer() {
	long tod;
	int hour;
	int i;
	struct tm *t, *localtime();
	struct stat statb;
	long time();
	static char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
	static long oldmsize = 1000000L;
	static int rccount = 10;

	alflag = 0;
	signal(SIGALRM, onalarm);
	time(&tod);
	t = localtime(&tod);
	i = 60 - t->tm_sec;
	alarm(i > 30? 30 : i);			/* reset alarm */
	hour = t->tm_hour % 12;
	if (hour == 0)  hour = 12;
	sprintf(timestr, "%.3s %d %d:%02d",
		months + 3 * t->tm_mon, t->tm_mday, hour, t->tm_min);
#ifdef GGRMAIL
	if (mailf == NULL || stat(mailf, &statb) < 0) {
		statb.st_size = 0;
	}
	if(statb.st_size > oldmsize) {
		ismail = 1;
		beep();
	} else if (statb.st_size < oldmsize) {
		ismail = 0;
	}
	oldmsize = statb.st_size;
#else
	ismail = 0;
	if (mailf != NULL && stat(mailf, &statb) >= 0 && statb.st_size > 0L) {
		ismail = 1;
		if (oldmsize < statb.st_size) {
			ismail = 2;		/* new mail */
			beep();
		}
	} else {
		statb.st_size = 0L;
	}
	oldmsize = statb.st_size;
#endif
	if (uflag && !xflag && --rccount < 0) {
		writeoutrc();
		if (secpr[0] == '\0')
			strcpy(secpr, ".newsrc updated");
		rccount = 10;
	}
}


char *
getmailname() {
	static char mailname[32];
	register char *p;

	if( (p = getenv("MAIL")) != NULL)
		return p;
	if (username[0] == '\0' || strlen(username) > 15)
		return NULL;
#ifdef BSD
	sprintf(mailname, "/usr/spool/mail/%s", username);
#else
	sprintf(mailname, "/usr/mail/%s", username);
#endif
	return mailname;
}



/*** Terminal I/O ***/

#define INBUFSIZ 8

char inbuf[INBUFSIZ];			/* input buffer */
char outbuf[BUFSIZ];			/* output buffer */
int innleft = 0;			/* # of chars in input buffer */
int outnleft = BUFSIZ;			/* room left in output buffer */
char *innext;				/* next input character */
char *outnext = outbuf;			/* next space in output buffer */
#ifdef USG
int oflags;				/* fcntl flags (for nodelay read) */
#endif


/*
 * Input a character
 */

vgetc() {
	register c;
#ifdef BSD4_2
	int readfds, exceptfds;
#endif

recurse:
	if (--innleft >= 0) {
		c = *innext++;
	} else {
		if (alflag)
			timer();
		updscr();	/* update the display */
		for (;;) {
			if (innleft > 0 || alflag)
				goto recurse;
			intflag = 0;
#ifdef USG
			if (oflags & O_NDELAY) {
				oflags &=~ O_NDELAY;
				fcntl(0, F_SETFL, oflags);
			}
#endif
#ifdef BSD4_2
			/* use a read because it can be interrupted */
			readfds = 1; exceptfds = 1;
			select(1,&readfds,0,&exceptfds,0);
			if (readfds & 1) { /* got a key, go ahead and get it */
				innleft = read(0,inbuf,INBUFSIZ);
#else
			if ((innleft = read(0, inbuf, INBUFSIZ)) > 0) {
#endif
#ifdef BSD
				if (intflag) {
					intflag--;
					inbuf[0] = cintr;
					clearin(); /* flush input queue */
					return cintr;
				}
#endif
				break;
			}
			if (innleft == 0) {
				quitflg++;
				return cintr;
			}
			if (errno != EINTR)
				abort();	/* "Can't happen" */
			if (intflag) {
				intflag--;
				return cintr;
			}
		}
		innext = inbuf + 1;
		innleft--;
		c = inbuf[0];
	}
#ifdef V7
	c &= 0177;
	if (c == '\034')	/* FS character */
		onquit();
#endif
	if (c == '\f') {
		clearok(curscr, 1);
		goto recurse;
	}
	if (c == '\r')
		c = '\n';
	return c;
}


/*
 * Push a character back onto the input stream.
 */

pushback(c) {
	if (innext <= inbuf)
		abort();
	*--innext = c;
	innleft++;
}



/*
 * Check for terminal input
 */

checkin() {
#ifdef BSD
	long count;

#endif
#ifdef STATTOP
	if (innleft > 0)
#else
	if (innleft > 0 || alflag)
#endif
		return 1;
#if !0 && !defined(V7)
	if (ospeed == B9600)
		return 0;
	vflush();
	if (ospeed <= B300)
		ttyowait();
#ifdef USG
	if ((oflags & O_NDELAY) == 0) {
		oflags |= O_NDELAY;
		fcntl(0, F_SETFL, oflags);
	}
	if ((innleft = read(0, inbuf, INBUFSIZ)) > 0) {
		innext = inbuf;
		return 1;
	}
#else
	count = 0;			/* in case FIONREAD fails */
	ioctl(0, FIONREAD, &count);
	if (count)
		return 1;
#endif
#endif
	return 0;
}



/*
 * flush terminal input queue.
 */

clearin() {
#ifdef USG
	ioctl(0, TCFLSH, 0);
#else
#ifdef TIOCFLUSH
	ioctl(0, TIOCFLUSH, 0);
#else
	struct sgttyb tty;
	gtty(0, &tty);
	stty(0, &tty);
#endif
#endif
	innleft = 0;
}


vputc(c) {
	if (--outnleft < 0) {
		vflush();
		outnleft--;
	}
	*outnext++ = c;
}


/*
 * Flush the output buffer
 */

vflush() {
	register char *p;
	register int i;

	for (p = outbuf ; p < outnext ; p += i) {
		if ((i = write(1, p, outnext - p)) < 0) {
			if (errno != EINTR)
				abort();	/* "Can't happen" */
			i = 0;
		}
	}
	outnleft = BUFSIZ;
	outnext = outbuf;
}




/*** terminal modes ***/

#ifdef USG
static struct termio oldtty, newtty;


/*
 * Save tty modes
 */

ttysave() {
	if (ioctl(1, TCGETA, &oldtty) < 0)
		xerror("Can't get tty modes");
	newtty = oldtty;
	newtty.c_iflag &=~ (INLCR|IGNCR|ICRNL);
	newtty.c_oflag &=~ (OPOST);
	newtty.c_lflag &=~ (ICANON|ECHO|ECHOE|ECHOK|ECHONL);
	newtty.c_lflag |=  (NOFLSH);
	newtty.c_cc[VMIN] = 1;
	newtty.c_cc[VTIME] = 0;
	cerase = oldtty.c_cc[VERASE];
	cintr = oldtty.c_cc[VINTR];	/* (WLS) */
	ckill = oldtty.c_cc[VKILL];
	ospeed = oldtty.c_cflag & CBAUD;
	initterm();
}


/*
 * Set tty modes for visual processing
 */

ttyraw() {
	while (ioctl(1, TCSETAF, &newtty) < 0 && errno == EINTR);
	rawterm();
}



ttyowait() {	/* wait for output queue to drain */
	while (ioctl(1, TCSETAW, &newtty) < 0 && errno == EINTR);
}


/*
 * Restore tty modes
 */

ttycooked() {
	cookedterm();
	while (ioctl(1, TCSETAF, &oldtty) < 0 && errno == EINTR);
	oflags &=~ O_NDELAY;
	fcntl(0, F_SETFL, oflags) ;
}

#else

static struct sgttyb oldtty, newtty;
#ifdef TIOCGETC
static struct tchars tchars;
#endif


/*
 * Save tty modes
 */

ttysave() {
#ifdef SIGTSTP
	/* How to get/change terminal modes in a job control environment.
	   This code is right from the 4.1 bsd jobs(3) manual page.
	 */
	short tpgrp, getpgrp();

retry:
#ifdef BSD4_2
	sigblock(BIT(SIGTSTP)|BIT(SIGTTIN)|BIT(SIGTTOU));
#else
	signal(SIGTSTP, SIG_HOLD);
	signal(SIGTTIN, SIG_HOLD);
	signal(SIGTTOU, SIG_HOLD);
#endif
	if (ioctl(2, TIOCGPGRP, &tpgrp) != 0)
		goto nottty;
	if (tpgrp != getpgrp(0)) { /* not in foreground */
		signal(SIGTTOU, SIG_DFL);
#ifdef BSD4_2
		sigsetmask(sigblock(0) & ~BIT(SIGTTOU));
#endif
		kill(0, SIGTTOU);
		/* job stops here waiting for SIGCONT */
		goto retry;
	}
	signal(SIGTTIN, onstop);
	signal(SIGTTOU, onstop);
	signal(SIGTSTP, onstop);
#ifdef BSD4_2
	sigsetmask(sigblock(0) & ~(BIT(SIGTSTP)|BIT(SIGTTIN)|BIT(SIGTTOU)));
#endif BSD4_2
#endif SIGTSTP
	if (gtty(1, &oldtty) < 0)
nottty:		xerror("Can't get tty modes");
	newtty = oldtty;
	newtty.sg_flags &=~ (CRMOD|ECHO|XTABS);
#ifdef BSD
	newtty.sg_flags |= CBREAK;
#else
	newtty.sg_flags |= RAW;
#endif
#ifdef TIOCGETC
	if (ioctl(1,TIOCGETC,&tchars)<0)
		xerror("Can't get special tty characters");
	cintr = tchars.t_intrc;
#endif
	cerase = oldtty.sg_erase;
	ckill = oldtty.sg_kill;
	ospeed = oldtty.sg_ospeed;
	initterm();
}


/*
 * Set tty modes for visual processing
 */

ttyraw() {
	while (stty(1, &newtty) < 0 && errno == EINTR);
	rawterm();
}



ttyowait() {	/* wait for output queue to drain */
#ifdef TIOCDRAIN	/* This ioctl is a local mod on linus */
	ioctl(1, TIOCDRAIN, 0);
#endif
}


/*
 * Restore tty modes
 */

ttycooked() {
	cookedterm();
	while (stty(1, &oldtty) < 0 && errno == EINTR);
}

#endif



/*** signal handlers ***/

onint() {
	if (!news) {
		ttycooked();
		xxit(1);
	}
	signal(SIGINT, onint);
	clearin();			/* flush input queue */
	intflag++;
#ifdef BSD
	ioctl(0,TIOCSTI,"\0");
#endif
}


onquit() {
	botscreen();
	vflush();
	ttycooked();
#ifdef COREDUMP
	abort();
#else
	exit(0);
#endif
}

#ifdef SIGTSTP

onstop(signo)
	int signo;
{
	/* restore old terminal state */
	botscreen();
	vflush();
	ttycooked();
	signal(signo, SIG_DFL);
#ifdef BSD4_2
	/* clear SIGSTP mask bit */
	sigsetmask(sigblock(0) & ~BIT(signo));
#endif
	kill(getpid(), signo);	/* stop here until continued */

	fprintf(stderr,"Vnews restarted.");
	signal(signo, onstop);
	/* restore our special terminal state */
	ttyraw();
	clearok(curscr, 1);
}

#endif


/*** stolen from rfuncs2.c and modified ***/

vsave(to, flags)
register char *to;
{
	register FILE *ufp;
	int	isprogram = 0;
	int	isnew = 1;
	long	saveoff;
	char	temp[20];
	char	*fname;
	char	prog[BUFLEN + 24];
	int	(*svpipe)();

#define hh h
#define hfp fp
	saveoff = ftell(fp);
	fseek(fp, artbody, 0);
	fname = to;
	if (*to == PIPECHAR) {
		if (strlen(to) > BUFLEN) {
			msg("Command name too long");
			goto out;
		}
		flags |= OVWRITE;
		strcpy(temp, "/tmp/vnXXXXXX");
		mktemp(temp);
		fname = temp;
		_amove(ROWS - 1, 0);
		vflush();
	}
	if ((flags & OVWRITE) == 0) {
		ufp = fopen(fname, "r");
		if (ufp != NULL) {
			fclose(ufp);
			isnew = 0;
		}
	}
	umask(savmask);

	if (*to == PIPECHAR)
		isprogram++;
	if ((ufp = fopen(fname, (flags & OVWRITE) == 0? "a" : "w")) == NULL) {
		msg("Cannot open %s", fname);
		goto out;
	}
	/*
	 * V7MAIL code is here to conform to V7 mail format.
	 * If you need a different format to be able to
	 * use your local mail command (such as four ^A's
	 * on the end of articles) substitute it here.
	 */
	if (flags & SVHEAD) {
#ifdef V7MAIL
		hh.subtime = cgtdate(hh.subdate);
		fprintf(ufp, "From %s %s",
#ifdef INTERNET
				hh.from,
#else
				hh.path,
#endif
					ctime(&hh.subtime));
#endif
		hprint(&hh, ufp, 2);
#ifdef V7MAIL
		tprint(hfp, ufp, TRUE);
		putc('\n', ufp);	/* force blank line at end (ugh) */
#else
		tprint(hfp, ufp, FALSE);
#endif
	} else {
		tprint(hfp, ufp, FALSE);
	}

	fclose(ufp);
	if (isprogram) {
		sprintf(prog, "(%s)<%s", to + 1, fname);
		shcmd(prog, CWAIT);
		prflags |= NOPRT;
	} else {
		if ((flags & OVWRITE) == 0)
			msg("file: %s %s", to, isnew ? "created" : "appended");
		else
			msg("file: %s written", to);
	}

out:
	if (isprogram) {
		unlink(fname);
	}
	umask(N_UMASK);
	fseek(fp, saveoff, 0);
}


/*** stolen from rfuncs.c ***/

/*
 * Return TRUE if the user has not ruled out this article.
 */
vselect(hp, insist)
register struct hbuf *hp;
int	insist;
{
	if (insist)
		return TRUE;
	if (tflag && !titmat(hp, header.title))
		return FALSE;
	if (aflag && cgtdate(hp->recdate) < atime)
		return FALSE;
	if (index(hp->nbuf, ',') && !rightgroup(hp))
		return FALSE;
	if (fflag && isfol(hp))
		return FALSE;
	return TRUE;
}



/*
 * Code to avoid showing multiple articles for vnews.
 * Works even if you exit vnews.
 * Returns nonzero if we should show this article.
 */
rightgroup(hp)
	struct hbuf *hp;
	{
	char ng[BUFLEN];
	char temp[BUFLEN];	/* genrad!john (WLS) */
	register char *p, *g;
	int i, flag;

	strcpy(ng, hp->nbuf);
	ngcat(ng);
	g = ng;
	flag = 1;
	while ((p = index(g, ',')) != NULL) {
		*p++ = '\0';
		while (*p == ' ')
			p++;
		if (strcmp(g, groupdir) == 0) {
			return flag;
		}
		/* this hack is required because ngmatch expects trailing ','*/
		strcpy(temp, g);		/* genrad!john (WLS) */
		strcat(temp, ",");		/* genrad!john (WLS) */
		if (ngmatch(temp, header.nbuf)	/* genrad!john (WLS) */
		 && ((i = findrcline(g)) < 0 || index(rcline[i], '!') == NULL)) {
			flag = 0;
		}
		g = p;
	}
	/* we must be in "junk" or "control" */
	return 1;
}
-- 
>From the closet of anxieties of:			Chuq Von Rospach
{amd70,fortune,hplabs,ihnp4}!nsc!chuqui			(408) 733-2600 x242

I'm sure I have my death ray in here somewhere...

chuqui@nsc.UUCP (06/01/84)

This is the other half of vnews, virtterm.c.

---- virtterm.c ----

/*
 *  Virtual terminal handler for the HP-2621 terminal.
 *  Written by Kenneth Almquist, AGS Computers  (HO 4C601, X7105).
 *  Modified by Stephen Hemminger, to use TERMCAP (without curses)
 */

#include <stdio.h>
#include <ctype.h>

#define MAXPLEN 24
#define MAXLLEN 80
#define BOTLINE (ROWS - 1)
#define DIRTY 01

/* terminal escape sequences from termcap */
#define HO _tstr[0]		/* home */
#define CL _tstr[1]		/* clear screen */
#define CD _tstr[2]		/* clear to end of screen */
#define CE _tstr[3]		/* clear to end of line */
#define UP _tstr[4]		/* up one line */
#define DO _tstr[5]		/* down one line */
#define US _tstr[6]		/* underline */
#define UE _tstr[7]		/* underline end */
#define BT _tstr[8]		/* backtab */
#define BC _tstr[9]		/* backspace */
#define AL _tstr[10]		/* insert line */
#define DL _tstr[11]		/* delete line */
#define CM _tstr[12]		/* cursor move */
#define CH _tstr[13]		/* cursor horizontal move */
#define CV _tstr[14]		/* cursor vertical move */
#define CS _tstr[15]		/* scrolling region */
#define SF _tstr[16]		/* scroll forwards */
#define SR _tstr[17]		/* scroll backwards */
#define TI _tstr[18]		/* start cursor mode */
#define TE _tstr[19]		/* end cursor mode */
#define TA _tstr[20]		/* tab char (if not \t) */
#define CR _tstr[21]		/* carriage return (if not \r) */
#define xPC _tstr[22]		/* for reading pad character */
char PC;			/* pad character */

static char sname[] = "hoclcdceupdousuebtbcaldlcmchcvcssfsrtitetacrpc";
char *_tstr[23];
int     HOlen;			/* length of HO string */


/* terminal flags */
#define BS _tflg[0]		/* can backspace */
#define AM _tflg[1]		/* has auto margins */
#define XN _tflg[2]		/* no newline after wrap */
#define RET !_tflg[3]		/* has carriage return */
#define NS _tflg[4]		/* has SF (scroll forward) */
#define PT _tflg[5]		/* has tabs */
#define XT _tflg[6]		/* tabs are destructive */
int	GT = 1;			/* tab stops on terminal are set */

static char bname[] = "bsamxnncnsptxt";
char _tflg[7];


extern char *tgoto(), *tgetstr();
extern char *getenv(), *strcpy();

#define ULINE 0200
#define CURSEEN 1

/* Constants accessable by user */
int     hasscroll;		/* scrolling type, 0 == no scrolling */
int     ROWS;			/* number of lines on screen */
int     COLS;			/* width of screen */

struct line {
	char    len;
	char    flags;
	char    l[MAXLLEN];
};

int     _row, _col;
int     _srow, _scol;
struct line _virt[MAXPLEN], _actual[MAXPLEN];
int     _uline = 0;
int     _junked = 1;
int     _curjunked;
int     _dir = 1;
int	_shifttop, _shiftbot;
int	_shift;
int	_scratched;
int     vputc();



/*
 * Tell refresh to shift lines in region upwards count lines.  Count
 * may be negative.  The virtual image is not shifted; this may change
 * later.  The variable _scratched is set to supress all attempts to
 * shift.
 */

ushift(top, bot, count) {
	if (_scratched)
		return;
	if (_shift != 0 && (_shifttop != top || _shiftbot != bot)) {
		_scratched++;
		return;
	}
	_shifttop = top;
	_shiftbot = bot;
	_shift += count;
}



/*
 * generate a beep on the terminal
 */

beep() {
	vputc('\7');
}

/*
 * Move to one line below the bottom of the screen.
 */

botscreen() {
	_amove(BOTLINE, 0);
	vputc('\n');
	vflush();
}



move(row, col) {
	if (row < 0 || row >= ROWS || col < 0 || col >= COLS)
		return;
	_row = row;
	_col = col;
}



/*
 * Output string at specified location.
 */

mvaddstr(row, col, str)
	char *str;
	{
	move(row, col);
	addstr(str);
}


addstr(s)
char   *s;
{
	register char  *p;
	register struct line   *lp;
	register int    col = _col;

	lp = &_virt[_row];
	if (lp->len < col) {
		p = &lp->l[lp->len];
		while (lp->len < col) {
			*p++ = ' ';
			lp->len++;
		}
	}
	for (p = s; *p != '\0'; p++) {
		if (*p == '\n') {
			lp->len = col;
			lp->flags |= DIRTY;
			col = 0;
			if (++_row >= ROWS)
				_row = 0;
			lp = &_virt[_row];
		}
		else {
			lp->l[col] = *p;
			lp->flags |= DIRTY;
			if (++col >= COLS) {
				lp->len = COLS;
				col = 0;
				if (++_row >= ROWS)
					_row = 0;
				lp = &_virt[_row];
			}
		}
	}
	if (lp->len <= col)
		lp->len = col;
	_col = col;
}



addch (c) {
	register struct line   *lp;
	register char  *p;

	lp = &_virt[_row];
	if (lp->len < _col) {
		p = &lp->l[lp->len];
		while (lp->len < _col) {
			*p++ = ' ';
			lp->len++;
		}
	}
	lp->l[_col] = c;
	if (lp->len == _col)
		lp->len++;
	if (++_col >= COLS) {
		_col = 0;
		if (++_row >= ROWS)
			_row = 0;
	}
	lp->flags |= DIRTY;
}



clrtoeol() {
	register struct line   *lp;

	lp = &_virt[_row];
	if (lp->len > _col) {
		lp->len = _col;
		lp->flags |= DIRTY;
	}
}


/*
 * Clear an entire line.
 */

clrline(row) {
	register struct line   *lp;

	lp = &_virt[row];
	if (lp->len > 0) {
		lp->len = 0;
		lp->flags |= DIRTY;
	}
}



clear() {
	erase();
	_junked++;
}



erase() {
	register    i;

	for (i = 0; i < ROWS; i++) {
		_virt[i].len = 0;
		_virt[i].flags |= DIRTY;
	}
}



refresh() {
	register i;
	int j, len;
	register char *p, *q;

	if (checkin())
		return;
	i = 1;
	if (_junked) {
		_sclear();
		_junked = 0;
	} else if (! _scratched) {
		if (_shift > 0) {
			_ushift(_shifttop, _shiftbot, _shift);
		} else if (_shift < 0) {
			i = _dshift(_shifttop, _shiftbot, -_shift);
		} else {
			i = _dir;
		}
	} 
	_dir = i;
	_shift = 0;
	if (checkin())
		return;
	_fixlines();
	for (i = _dir > 0 ? 0 : BOTLINE; i >= 0 && i < ROWS; i += _dir) {
		if ((_virt[i].flags & DIRTY) == 0)
			continue;
		_ckclrlin(i);		/* decide whether to do a clear line */
					/* probably should consider cd as well */
		len = _virt[i].len;
		if (_actual[i].len < len)
			len = _actual[i].len;
		p = _virt[i].l;
		q = _actual[i].l;
		for (j = 0; j < len; j++) {
			if (*p != *q) {
				_amove(i, j);
				_aputc(*p);
				*q = *p;
			}
			p++, q++;
		}
		len = _virt[i].len;
		if (_actual[i].len > len) {
			_clrtoeol(i, len);
		}
		else {
			for (; j < len; j++) {
				if (*p != ' ') {
					_amove(i, j);
					_aputc(*p);
				}
				*q++ = *p++;
			}
			_actual[i].len = len;
		}
		if (checkin())
			return;
	}
	_dir = 1;
	if (CURSEEN)
		_amove(_row, _col);
	vflush();			/* flush output buffer */
	_scratched = 0;
}


_dshift(top, bot, count) {
	register    i;

	if (count >= bot - top || hasscroll < 4) {  /* must have CS or AL/DL */
		_scratched++;
		return 1;
	}
	for (i = bot - count; _actual[i].len == 0; i--)
		if (i == top)
			return 1;
	for (i = top; i <= bot; i++)
		_virt[i].flags |= DIRTY;
	for (i = bot; i >= top + count; i--)
		_actual[i] = _actual[i - count];
	for (; i >= top; i--)
		_actual[i].len = 0;

	if (hasscroll != 5) {		/* can we define scrolling region, and scroll back */
		tputs(tgoto(CS, bot, top), 1, vputc);/* define scroll region */
		_curjunked = 1;
		_amove(top, 0);
		for (i = count; --i >= 0;)
			tputs(SR, 1, vputc);/* scroll back */
		tputs(tgoto(CS, BOTLINE, 0), 1, vputc);
		_curjunked = 1;
	} else {
		_amove(bot - count + 1, 0);
		if (CD && bot == BOTLINE)
			tputs(CD, 1, vputc);
		else {
			for (i = count; --i >= 0;)
				tputs(DL, ROWS - _srow, vputc);
		}
		_amove(top, 0);
		for (i = count; --i >= 0;)
			tputs(AL, ROWS - _srow, vputc);
	}
	return -1;
}


_ushift(top, bot, count) {
	register    i;

	if (count >= bot - top || hasscroll == 0) {
		_scratched++;
		return;
	}
	for (i = top + count; _actual[i].len == 0; i++)
		if (i == bot)
			return;
	if (hasscroll == 1 || hasscroll == 3) {
		/* we cheat and shift the entire screen */
		/* be sure we are shifting more lines into than out of position */
		if ((bot - top + 1) - count <= ROWS - (bot - top + 1))
			return;
		top = 0, bot = BOTLINE;
	}
	for (i = top; i <= bot; i++)
		_virt[i].flags |= DIRTY;
	for (i = top; i <= bot - count; i++)
		_actual[i] = _actual[i + count];
	for (; i <= bot; i++)
		_actual[i].len = 0;

	if (hasscroll != 5) {
		if (top != 0 || bot != BOTLINE) {
			tputs(tgoto(CS, bot, top), 0, vputc);
			_curjunked = 1;
		}
		_amove(bot, 0);	/* move to bottom */
		for (i = 0; i < count; i++) {
			if (SF)		/* scroll forward */
				tputs(SF, 1, vputc);
			else
				vputc('\n');
		}
		if (top != 0 || bot != BOTLINE) {
			tputs(tgoto(CS, BOTLINE, 0), 0, vputc);
			_curjunked = 1;
		}
	} else {
		_amove(top, 0);
		for (i = count; --i >= 0;)
			tputs(DL, ROWS - _srow, vputc);
		if (bot < BOTLINE) {
			_amove(bot - count + 1, 0);
			for (i = count; --i >= 0;)
				tputs(AL, ROWS - _srow, vputc);
		}
	}
}


_sclear() {
	register struct line   *lp;

	tputs(CL, 0, vputc);
	_srow = _scol = 0;
	for (lp = _actual; lp < &_actual[ROWS]; lp++) {
		lp->len = 0;
	}
	for (lp = _virt; lp < &_virt[ROWS]; lp++) {
		if (lp->len != 0)
			lp->flags |= DIRTY;
	}
}


_clrtoeol(row, col) {
	register struct line *lp = &_actual[row];
	register i;

	if (CE && lp->len > col + 1) {
		_amove(row, col);
		tputs(CE, 1, vputc);
	} else {
		for (i = col ; i < lp->len ; i++) {
			if (lp->l[i] != ' ') {
				_amove(row, i);
				_aputc(' ');
			}
		}
	}
	lp->len = col;
}


_fixlines() {
	register struct line   *lp;
	register char  *p;
	register int    i;

	for (i = 0; i < ROWS; i++) {
		lp = &_virt[i];
		if (lp->flags & DIRTY) {
			lp = &_virt[i];
			for (p = &lp->l[lp->len]; --p >= lp->l && *p == ' ';);
			lp->len = p + 1 - lp->l;
			if (lp->len == _actual[i].len && strncmp(lp->l, _actual[i].l, lp->len) == 0)
				lp->flags &= ~DIRTY;
		}
	}
}


/*
 * Consider clearing the line before overwriting it.
 * We always clear a line if it has underlined characters in it
 * because these can cause problems.  Otherwise decide whether
 * that will decrease the number of characters to change.  This
 * routine could probably be simplified with no great loss.
 */

_ckclrlin(i) {
	int     eval;
	int     len;
	int     first;
	register struct line   *vp, *ap;
	register int    j;

	if (!CE)
		return;
	ap = &_actual[i];
	vp = &_virt[i];
	len = ap->len;
	eval = -strlen(CE);
	if (len > vp->len) {
		len = vp->len;
		eval = 0;
	}
	for (j = 0; j < len && vp->l[j] == ap->l[j]; j++);
	if (j == len)
		return;
	first = j;
	while (j < len) {
		if (vp->l[j] == ' ') {
			if (ap->l[j] != ' ') {
				while (++j < len && vp->l[j] == ' ' && ap->l[j] != ' ') {
					eval++;
				}
				if (j == len)
					eval++;
				continue;
			}
		}
		else {
			if (vp->l[j] == ap->l[j]) {
				while (++j < len && vp->l[j] == ap->l[j]) {
					eval--;
				}
				continue;
			}
		}
		j++;
	}
	if (US) {
		for (j = 0 ; j < ap->len ; j++) {
			if (ap->l[j] & ULINE) {
				eval = 999;
				if (first > j)
					first = j;
				break;
			}
		}
	}
	for (j = first; --j >= 0;)
		if (vp->l[j] != ' ')
			break;
	if (j < 0)
		first = 0;
	if (eval > 0) {
		_amove(i, first);
		tputs(CE, 0, vputc);
		_actual[i].len = first;
	}
}



/*
 * Move routine
 * 	first compute direct cursor address string and cost
 *	then relative motion string and cost,
 *	then home then relative and cost
 *	choose smallest and do it.
 *
 *	The plod stuff is to build the strings (with padding) then decide
 */
static char *plodstr;		/* current location in relmove string */

plodput(c) { *plodstr++ = c; }

_amove(row, col) {
	char direct[20];
	char rel[MAXPLEN + MAXLLEN + 50];    /* longest move is full screen */
	char ho[MAXPLEN + MAXLLEN + 50];
	int cost, newcost;
	register char *movstr;

	if (row == _srow && col == _scol && _curjunked == 0)
		return;
	_setul(0);

	cost = 999;
	if (CM) {
		plodstr = direct;
		tputs(tgoto(CM, col, row), 0, plodput);
		*plodstr = '\0';
		cost = plodstr - direct;
		movstr = direct;
	}
	if (_curjunked == 0) {
		plodstr = rel;
		if (_vmove(_srow, row) >= 0
		 && _hmove(_scol, col, row) >= 0
		 && (newcost = plodstr - rel) < cost) {
			*plodstr = '\0';
			cost = newcost;
			movstr = rel;
		}
	}
	if (cost > HOlen) {	/* is it worth calculating */
		plodstr = ho;
		tputs(HO, 0, plodput);
		if (_vmove(0, row) >= 0
		 && _hmove(0, col, row) >= 0
		 && (newcost = plodstr - ho) < cost) {
			*plodstr = '\0';
			cost = newcost;
			movstr = ho;
		}
	}

	while (--cost >= 0) {
		vputc(*movstr++);
	}
	_srow = row, _scol = col;
	_curjunked = 0;
}


_vmove(orow, nrow) {
	char direct[20];
	char *saveplod = plodstr;

	if (CV) {
		plodstr = direct;
		tputs(tgoto(CV, nrow, nrow), 0, plodput);
		*plodstr = '\0';
		plodstr = saveplod;
	}
	if (orow > nrow) {		/* cursor up */
		if (! UP)
			return -1;
		while (orow > nrow) {
			tputs(UP, 1, plodput);
			orow--;
		}
	}
	while (orow < nrow) {		/* cursor down */
		if (DO)
			tputs(DO, 1, plodput);
		else
			*plodstr++ = '\n';
		orow++;
	}
	if (CV && plodstr - saveplod >= strlen(direct)) {
		register char *p;
		plodstr = saveplod;
		for (p = direct ; *plodstr = *p++ ; plodstr++);
	}
	return 0;
}


_hmove(ocol, ncol, row) {
	char direct[20];
	char ret[MAXLLEN + 50];
	char *saveplod = plodstr;
	char *movstr;
	int cost, newcost;

	cost = 999;
	if (CH) {
		plodstr = direct;
		tputs(tgoto(CH, ncol, ncol), 0, plodput);
		cost = plodstr - direct;
		movstr = direct;
		plodstr = saveplod;
	}
	if (RET && ocol > ncol) {	/* consider doing carriage return */
		plodstr = ret;
		if (CR)
			tputs(CR, 1, plodput);
		else
			*plodstr++ = '\r';
		if (_relhmove(0, ncol, row) >= 0
		 && (newcost = plodstr - ret) < cost) {
			cost = newcost;
			movstr = ret;
		}
		plodstr = saveplod;
	}
	if (_relhmove(ocol, ncol, row) < 0) {
		if (cost == 999)
			return -1;
		goto copy;
	}
	if (plodstr - saveplod > cost) {
copy:		plodstr = saveplod;
		while (--cost >= 0)
			*plodstr++ = *movstr++;
	}
	return 0;
}



_relhmove(ocol, ncol, row) {
	int tab;

	if (ocol < ncol && PT && GT) {	/* tab (nondestructive) */
		while ((tab = (ocol + 8) & ~07) <= ncol) {
			if (TA)
				tputs(TA, 1, plodput);
			else
				*plodstr++ = '\t';
			ocol = tab;
		}
		if (tab < COLS && tab - ncol < ncol - ocol) {
			if (TA)
				tputs(TA, 1, plodput);
			else
				*plodstr++ = '\t';
			ocol = tab;
		}
	} else if (BT && GT && ocol > ncol) {	/* backwards tab */
		while ((tab = (ocol - 1) &~ 07) >= ncol) {
			if (BS && tab == ocol - 1) {
				if (BC)
					tputs(BC, 1, plodput);
				else
					*plodstr++ = '\b';
			} else
				tputs(BT, 1, plodput);
			ocol = tab;
		}
		if (ncol - tab + 1 < ocol - ncol) {
			tputs(BT, 1, plodput);
			ocol = tab;
		}
	}
	if (ocol > ncol) {			/* cursor left */
		if (! BS)
			return -1;
		while (ocol > ncol) {
			if (BC != NULL)
				tputs(BC, 1, plodput);
			else
				*plodstr++ = '\b';
			ocol--;
		}
	}
	if (ocol < ncol) {			/* cursor right */
		register struct line *lp = &_actual[row];
		/*
		 * This code doesn't move over underlined characters properly,
		 * but in practice this doesn't seem to matter.
		 */
		while (ocol < ncol) {
			if (ocol < lp->len)
				*plodstr++ = lp->l[ocol];
			else
				*plodstr++ = ' ';
			ocol++;
		}
	}
	return 0;
}



_aputc(c) {
	if (AM && _scol == COLS - 1 && _srow == ROWS - 1)
		return;
	_setul(c & ULINE);
	vputc(c & ~ULINE);
	if (++_scol >= COLS) {
		if (! AM) {
			_scol--;
		} else  if (XN) {
			_curjunked++;
		} else {
			_scol = 0;
			++_srow;
		}
	}
}


_setul(on) {
	if (on) {
		if (_uline == 0 && US != NULL) {
			tputs(US, 1, vputc);
			_uline = 1;
		}
	}
	else {
		if (_uline != 0 && UE != NULL) {
			tputs(UE, 1, vputc);
			_uline = 0;
		}
	}
}


/*
 * Initialize termcap strings for later use.
 */
initterm() {
	static char tcbuf[1024];	/* termcap buffer */
	register char  *cp;

	if ((cp = getenv("TERM")) == NULL)
		xerror("TERM not set in environment");

	switch (tgetent(tcbuf, cp)) {
		case 0: 
			xerror("Terminal not found in TERMCAP");
		case -1: 
			xerror("Can't open /etc/termcap");
		case 1: 
			break;
	}

	if ((ROWS = tgetnum("li")) == -1
	 || (COLS = tgetnum("co")) == -1)
		xerror("Can't get screen size");
	_zap();

	if (CL == NULL)
		xerror ("No clear screen defined");

	if (HO == NULL && CM == NULL)
		xerror("No home or cursor addressing");
	if (HO)
		HOlen = strlen(HO);
	else
		HOlen = 999;

	PC = xPC ? xPC[0] : 0;

	if (tgetnum("ug") > 0)
		US = UE = NULL;

	if (XT)				/* Destructive tab code not included */
		PT = 0;			/* to keep things simple */

	if (ROWS > MAXPLEN)
		ROWS = MAXPLEN;
	if (COLS > MAXLLEN) {
		COLS = MAXLLEN;
		AM = XN = 1;
	}

	/* Select article scrolling algorithm.  We prefer scrolling region
	   over insert/delete line because it's faster on the HP */
	hasscroll = 0;
	if (!NS) {
		hasscroll = 1;
		if (SR)
			hasscroll = 3;
		if (CS)
			hasscroll++;
	}
	if (AL && DL && hasscroll != 4)
		hasscroll = 5;
}


rawterm() {
	if (TI != NULL)
		tputs(TI, 0, vputc);
}


cookedterm() {
	if (TE != NULL)
		tputs(TE, 0, vputc);
}


/* get strings from termcap */
_zap() {
	static char tstrbuf[1024];
	static char *tp;
	register char  *namp, **sp, *bp;

	tp = tstrbuf;
	sp = _tstr;
	for (namp = sname; *namp; namp += 2) {
		*sp++ = tgetstr(namp, &tp);
	}
	bp = _tflg;
	for (namp = bname; *namp; namp += 2) {
		*bp++ = tgetflag(namp, &tp);
	}
}

-- 
>From the closet of anxieties of:			Chuq Von Rospach
{amd70,fortune,hplabs,ihnp4}!nsc!chuqui			(408) 733-2600 x242

I'm sure I have my death ray in here somewhere...

barry@muddcs.UUCP (06/07/84)

<>

	I tried implementing the -fixed- version of vnews that Chuq
posted.  I ran into problems where vnews would read in an article 
improperly and then core dump after printing illegal instruction.
Has anybody else run into this?  We are running 4.2 on a 750.  Any
info would be greatly appreciated.


				Barry Lustig

				{ihnp4,allegra,seismo}!scgvaxd!muddcs!barry

chuqui@nsc.UUCP (06/09/84)

This is the second or third report I've had on this. I DON'T have it at my
site which is a 780 running 4.2 and VT100 compatibles. The only thing I can
think that MIGHT be causing this is one change I made to the termcap
routines because they used the TS field instead of the TI field. Some
termcaps evidently are also set up incorrectly. I don't know if this is
it, but that is all I can think of. If you are having the problem and can
get me stack traces or any information at all on why it happens I'll try to
find a fix for it as soon as I can. 

There ARE some pieces missing to the vnews distribution that I will try to
take care of Real Soon Now. Specifically, there are two shell scripts
(follow and reply) and the help file. I've just finished a major rewrite of
the Makefile for 2.10.1 and as soon as I add a couple more minor bells and
whistles (it now knows about lint and sccs and reacts in a relatively
sane manner) I will post the rest of vnews stuff with the new Makefile so
people who didn't have it before can compile it.

Oh, yes, one final note: The version of recmail.c that went with 2.10.1
(and I believe before) has a problem that keeps it from working properly
with vnews. I'll post that turkey as well (this will all go out tomorrow or
Monday if I get it done).

*sigh* I should have KNOWN better than to run inews through lint...

chuq



-- 
>From the ledge of the seventh cornice:			Chuq Von Rospach
{amd70,fortune,hplabs,ihnp4}!nsc!chuqui			(408) 733-2600 x242

If you let a smile be your umbrella you will get rain up your nose.

joe@fluke.UUCP (Joe Kelsey) (06/15/84)

Well, after much weeping and thrashing and knashing of teeth (and a few
more lost hairs) I have finally found the problem with vnews dumping
core.  This problem was especially furstrating because *I* couldn't
make it dump core for me!  No matter what I tried, it just refused to
dump core for me, but I could walk over to any of a select group of
people and they could do it every time!  Add to that the fact that the
error reported was Illegal Instruction, but the stack trace showed
absolutely nothing wrong!  Finally, after three days of no progress, I
thought to check the terminal characteristics of the terminals those
lucky people used, and, viola!  They use 9600 baud and I use 4800!
Now, we use VT100s almost exclusively here, except I have a VT220.  My
termcap for the VT220 has no padding.  VT100 standard termcap has
padding which only really takes effect at 9600 baud and above.  Clues,
clues, kludge!  Look in virtterm.c, function _?move (basically _amove,
but there are others.)  Notice that _amove is trying to find the
shortest possible string to send to the terminal to position the
cursor.  Look up the tputs(3) routine in the manual.  It takes a
function parameter to actually do something with every character
generated.  The simple thing to do here is pass it the address of your
favorite character output routine.  However, you may want to save the
string or something else, so you provide another routine, namely
plodput() (what a name!)  Now look at the nice static array
declarations at the beginning of _amove().  Nice aren't they?  Very
nice, indeed, thank you, until you try to generate a position string
which overflows the bounds of the rel[] array and proceeds to trash the
stack, destroying the return data and causing the ret instruction at
the end of _amove() to cause an illegal instruction trap since the
hardware can't figure out what to do with the garbage that is on the
stack now!

Well, anyway, now that I've bored you all with the horrible discussion
of this problem, how about a solution?  Well, I think the solution is
to fix tputs so that it dynamically allocates the string space for the
output string, but that would cause vi to run even slower on PDP-11s,
and everyone LOVES static arrays that overflow their bounds anyway,
right?  I took the easy way out - increase the declared size of all o f
the arrays used by the _?move routines so we don't overflow anymore (or
at least not until someone tries to run it on something that is slower
than a VT100 and uses and even more baroque escape sequence than ANSI!)

Here are my estimates for the array sizes:

joe@fluke.UUCP (Joe Kelsey) (06/15/84)

> Here are my estimates for the array sizes:

Huh?  Where are those array sizes you ask?  Well, fumble finger me!
Here are the virterm.c diffs:

*** ../misc/virtterm.c	Wed Jun  6 14:58:41 1984
--- virtterm.c	Thu Jun 14 13:58:45 1984
***************
*** 565,573
  plodput(c) { *plodstr++ = c; }
  
  _amove(row, col) {
! 	char direct[20];
! 	char rel[MAXPLEN + MAXLLEN + 50];    /* longest move is full screen */
! 	char ho[MAXPLEN + MAXLLEN + 50];
  	int cost, newcost;
  	register char *movstr;
  

--- 565,573 -----
  plodput(c) { *plodstr++ = c; }
  
  _amove(row, col) {
! 	char direct[128];
! 	char rel[(MAXLLEN*10)+(MAXPLEN*10)]; /* longest move is full screen */
! 	char ho[(MAXLLEN*10)+(MAXPLEN*10)];
  	int cost, newcost;
  	register char *movstr;
  
***************
*** 614,620
  
  
  _vmove(orow, nrow) {
! 	char direct[20];
  	char *saveplod = plodstr;
  
  	if (CV) {

--- 614,620 -----
  
  
  _vmove(orow, nrow) {
! 	char direct[128];
  	char *saveplod = plodstr;
  
  	if (CV) {
***************
*** 648,655
  
  
  _hmove(ocol, ncol, row) {
! 	char direct[20];
! 	char ret[MAXLLEN + 50];
  	char *saveplod = plodstr;
  	char *movstr;
  	int cost, newcost;

--- 648,655 -----
  
  
  _hmove(ocol, ncol, row) {
! 	char direct[128];
! 	char ret[MAXLLEN * 10];
  	char *saveplod = plodstr;
  	char *movstr;
  	int cost, newcost;

Hopefully, that should fix everything right up.

/Joe