[net.sources] news 2.10.2 src part 7

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

if test ! -d src
then
	mkdir src
	echo mkdir src
fi
echo x - src/visual.c
sed 's/^X//' >src/visual.c <<'*-*-END-of-src/visual.c-*-*'
X/*
X * readr - visual news interface.
X */
X
X#ifndef lint
Xstatic char	*SccsId = "@(#)visual.c	1.11	9/3/84";
Xstatic char Author[] = "@(#)visual interface written by Kenneth Almquist";
X#endif !lint
X
X#define GGRMAIL
X#ifdef USG
X#include <termio.h>
X#include <fcntl.h>
X#else
X#include <sgtty.h>
X#endif USG
X
X#include <errno.h>
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
X#ifdef BSD4_2
X#define BIT(_a) (1<<((_a)-1))
X#endif
X#ifdef MYDB
X#include "db.h"
X#endif MYDB
X
X#include <errno.h>
X
Xextern int errno;
X
X#ifdef SIGTSTP
X#include <setjmp.h>
X#endif
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 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
Xextern int errno;
XFILE *tmpfile();
Xchar *getmailname();
Xchar *findparent();
Xint onint(), onquit();
Xint onstop();
X
X/*
X * Kludge: space so that routines can access beyond
X * the end of arrays without messing me up.
X */
Xstatic char junk[64];
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 */
Xint ospeed;				/* terminal speed */
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
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 warrent 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  news = 0;
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	mktemp(tfname);
X	close(creat(tfname,0666));
X	if ((tfp = fopen(tfname, "w+")) == NULL)
X		xerror("Can't create temp file");
X	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	signal(SIGINT, onint);
X	signal(SIGQUIT, onquit);
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		strcpy(goodone, filename);
X		if (sigtrap)
X			return;
X		vcmd();
X	}
X
X	if (news)
X		botscreen();
X	ttycooked();
X	if (!news)
X		fprintf(stderr, "No news.\n");
X}
X
X/*
X * Read and execute a command.
X */
X
Xvcmd() {
X	register c;
X	char *p;
X	long count;
X	int countset;
X
X	appfile(fp, dlinno + ARTWLEN + 1);
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)
X			strcpy(prompt,"more? ");
X		else
X			sprintf(prompt,"more(%d%%)? ",((dlinno+ARTWLEN-hdrend)*100)/maxlinno);
X	} else {
X		atend = 1;
X		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	while ((c = vgetc()) >= '0' && c <= '9' || c==cerase || c ==ckill) {
X		if (c == cerase) {
X			count /= 10;
X			if (count == 0)
X				countset = 0;
X			continue;
X		}
X		if (c == ckill) {
X			countset = 0;
X			count = 0;
X			continue;
X		}
X		count = (count * 10) + (c - '0');
X		sprintf(p, "%ld", count);
X		countset = 1;
X	}
X	if (c == '\033') {			/* escape */
X		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);
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)
Xchar c;
Xlong count;
X{
X	int i;
X	long nart;
X	char *findhist();
X
X	switch (c) {
X
X	/* Show more of current article, or advance to next article */
X	case '\n':
X	case ' ':
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#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;
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 META|'v':
X	case '\2':	/* Control-B */
X		dlinno -= ARTWLEN * count;
X		if (dlinno < 0)
X			dlinno = 0;
X		break;
X
X	/* forward half a page */
X	case '\4':	/* Control-D, as in vi */
X		dlinno += ARTWLEN/2 * count;
X		break;
X
X	/* backward half a page */
X	case '\25':	/* Control-U */
X		dlinno -= ARTWLEN/2 * count;
X		if (dlinno < 0)
X			dlinno = 0;
X		break;
X
X	/* forward count lines */
X	case '\16':	/* Control-N */
X	case '\32':	/* Control-Z */
X		dlinno += count;
X		break;
X
X	/* backwards count lines */
X	case '\20':	/* Control-P */
X	case '\31':	/* Control-Y */
X		dlinno -= count;
X		if (dlinno < 0)
X			dlinno = 0;
X		break;
X
X	/* Turn displaying of article back on */
X	case 'l':
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				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' && c <= 'z')
X					*p = (ch - 'a' + 13) % 26 + 'a';
X				else if (ch >= 'A' && c <= '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	case 's':
X	case 'w': {
X		char *grn = groupdir;
X		int wflags;
X
X		msg("file: ");
X		curflag = CURP2;
X		while ((wflags = vgetc()) == ' ');
X		if (wflags == cintr) {
X			secpr[0] = '\0';
X			break;
X		}
X		if (wflags == '|') {
X			linebuf[0] = '|';
X			if (prget("| ", linebuf+1))
X				break;
X		} else {
X			pushback(wflags);
X			if (prget("file: ", linebuf))
X				break;
X		}
X		wflags = 0;
X		if (c == 's')
X			wflags |= SVHEAD;
X		if (count != 1)
X			wflags |= OVWRITE;
X		bptr = linebuf;
X		while( *bptr == ' ')
X			bptr++;	/* strip leading spaces */
X
X		if (*bptr != PIPECHAR && *bptr != '/') {
X			char	hetyped[BUFLEN];
X			char	*boxptr;
X			strcpy(hetyped, bptr);
X			if (boxptr = getenv("NEWSBOX"))
X				if (index(boxptr, '%'))
X					sprintf(bptr, boxptr, grn);
X				else
X					strcpy(bptr, boxptr);
X			else if (hetyped[0] == '~' && hetyped[1] == '/') {
X				strcpy(hetyped, bptr+2);
X				strcpy(bptr, userhome);
X			} else
X				bptr[0] = '\0';
X			if (bptr[0])
X				strcat(bptr, "/");
X			if (hetyped[0] != '\0')
X				strcat(bptr, hetyped);
X			else
X				strcat(bptr, "Articles");
X		}
X		vsave(bptr, wflags);
X		break;
X	}
X
X		/* back up  */
X	case '-':
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		strcpy(bfr, filename);
X		strcpy(filename, ofilename1);
X		strcpy(ofilename1, bfr);
X		obit = bit;
X		if (strcmp(groupdir, ogroupdir)) {
X			strcpy(bfr, groupdir);
X			selectng(ogroupdir, FALSE);
X			strcpy(groupdir, ogroupdir);
X			strcpy(ogroupdir, bfr);
X			ngrp = 1;
X			back();
X		}
X		bit = oobit;
X		oobit = obit;
X		obit = -1;
X		getnextart(TRUE);
X		return FALSE;
X
X		/* skip forwards */
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		onquit();
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		if (prget("!", p))
X			break;
X		flags = CWAIT;
X		if (*p == '\0') {
X			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 == '|') {
X			*p = '\0';
X			sprintf(bfr, "(%s)|mail '%s'", linebuf, username);
X			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();
X		break;
X
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		{
X		if (fp == NULL) {
X			msg("No current article");
X			break;
X		}
X		move(ARTWIN, 0);
X		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		for(;i<ARTWLEN; i++)
X			addstr(linebuf);
X		prflags |= HELPMSG|NEWART;
X		}
X		break;
X	case 'b':	/* backup 1 article */
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		if ((ptr1 = rindex(h->followid, ' ')) != NULL)
X			ptr1++;
X		else
X			ptr1 = h->followid;
X		strcpy(linebuf, ptr1);
X		msg("%s", linebuf);
X		curflag = CURP2;
X		updscr();		/* may take this out later */
X		goto searchid;
X
X	/* specific message ID. */
X	case '<':
X		/* could improve this */
X		linebuf[0] = '<';
X		if (prget("<", linebuf+1))
X			break;
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				sprintf(bfr, "<%s@%s.UUCP>", ptr2, ptr1);
X				strcpy(linebuf, bfr);
X			}
X		}
X		if (index(linebuf, '>') == NULL)
X			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		*ptr2++ = '\0';
X		sscanf(ptr2, "%ld", &nart);
X
X		/*
X		 * Go to a given article.  Ptr3 specifies the newsgroup
X		 * and nart specifies the article number.
X		 */
Xselectart:	aabs = TRUE;
X		FCLOSE(fp);
X		saveart;
X		strcpy(ogroupdir, ptr3);
X		if (strcmp(groupdir, ogroupdir)) {
X			strcpy(bfr, groupdir);
X			selectng(ogroupdir, TRUE);
X			strcpy(groupdir, ogroupdir);
X			strcpy(ogroupdir, bfr);
X			back();
X		}
X		bit = nart;
X		oobit = obit;
X		obit = -1;
X		getnextart(TRUE);
X		rfq = 0;
X		break;
X
X	/* follow-up article */
X	case 'f':
X		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			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			fclose(helpf);
X			prflags |= HELPMSG|NEWART;
X		}
X		break;
X
X	default:
X		if (c != ckill && c != cintr)
X			msg("Illegal command");
X		break;
X	}
X
X	return FALSE;
X}
X
Xcancel_command()
X{
X	tfilename = filename;
X	/*readmode = SPEC; bug? */
X	strcpy(rcbuf, h->path);
X	ptr1 = index(rcbuf, ' ');
X	if (ptr1)
X		*ptr1 = 0;
X	if (uid != ROOTID && strcmp(username, rcbuf)) {
X		msg("Can't cancel what you didn't write.");
X		return;
X	}
X	if (!cancel(stderr, h, 0)) {
X		clear(bit);
X		saveart;
X		nextbit();
X		obit = -1;
X		fp = NULL;
X	}
X	FCLOSE(fp);
X}
X
X/*
X * Generate replies
X */
X
Xreply()
X{
X	char *arg[4];
X	register FILE *rfp;
X	char subj[132];
X	extern char MAILPARSER[];
X	char *nogomsg;
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	strcpy(tf, tft);
X	mktemp(tf);
X	close(creat(tf,0666));
X	if ((rfp = fopen(tf, "w")) == NULL) {
X		msg("Can't create %s", tf) ;
X		return;
X	}
X	strcpy(subj, h->title);
X	if (!prefix(subj, "Re:")){
X		strcpy(bfr, subj);
X		sprintf(subj, "Re: %s", bfr);
X	}
X
X#ifdef INTERNET
X	if (h->sender[0])
X		p = h->sender;
X	else
X#endif
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	sprintf(rcbuf, "exec %s -t < %s; rm %s", MAILPARSER, tf, tf);
X	nogomsg = "Mail not sent";
X	putc('\n', rfp);
X	fstat(fileno(rfp), &statb);
X	creatm = statb.st_mtime;
X	fclose(rfp);
X
X	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		unlink(tf);
X		return;
X	}
X
X	if (access(tf, 4) || stat(tf, &statb)) {
X		msg("%s: no input file.", nogomsg);
X		unlink(tf);
X		return;
X	}
X	if (statb.st_mtime == creatm) {
X		msg("%s: cancelled.", nogomsg);
X		unlink(tf);
X		return;
X	}
X
X	arg[2] = rcbuf;
X	arg[3] = NULL;
X	prun(arg, BKGRND);
X	prflags |= NOPRT;
X}
X
Xnext_ng_command()
X{
X	obit = -1;
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	readmode = SPEC;
X	saveart;
X	back();
X	selectng(bptr, 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 || ((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	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 = fopen(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	{	/* strip off any notesfile header */
X		register c;
X		register char *p = h->title + strlen(h->title) - 5;
X		if (p > h->title
X		 && (strcmp(p, " (nf)") == 0 || strcmp(p, "(nf)\"") == 0)) {
X			if ((c = getc(fp)) != '#') {
X				ungetc(c, fp);
X			} else {
X				while ((c = getc(fp)) != '\n' && c != EOF);
X				while ((c = getc(fp)) != '\n' && c != EOF);
X				while ((c = getc(fp)) != '\n' && c != EOF);
X			}
X		}
X	}
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
X	lastlin = 0;
X	if (ngrp) {
X		pngsize = ngsize;
X		ngrp--;
X		if (!hflag) {
X			sprintf(linebuf, "Newsgroup %s", groupdir);  tfappend(linebuf);
X			tfappend("");
X		}
X	}
X	hdrstart = lastlin;
X	if (!hflag) {
X		sprintf(linebuf, "Article %s %s",
X			h->ident, briefdate(h->subdate));
X		tfappend(linebuf);
X	}
X	vhprint(h, pflag ? 1 : 0);
X	sprintf(linebuf, "(%d lines)", NLINES(h, fp)); tfappend(linebuf);
X	tfappend("");
X	hdrend = lastlin;
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	int	i;
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		strcpy(fname, p1+1);
X		p2 = index(fname, ')');
X		if (p2 != NULL)
X			*p2 = '\0';
X	}
X
X	sprintf(linebuf, "Subject: %s", hp->title);
X	if ((i = strlen(linebuf) - 7) > 9
X	 && strcmp(linebuf + i, " - (nf)") == 0
X	 && (strncmp(linebuf+9, "Re: ", 4) != 0 || i < 9+39))
X		linebuf[i] = '\0';		/* clobber "- (nf)" */
X	tfappend(linebuf);
X	if (!hflag && hp->keywords[0])
X		sprintf(linebuf, "Keywords: %s", hp->keywords), tfappend(linebuf);
X	if (verbose) {
X		sprintf(linebuf, "From: %s", hp->from); tfappend(linebuf);
X		sprintf(linebuf, "Path: %s", hp->path); tfappend(linebuf);
X		if (hp->organization[0])
X			sprintf(linebuf, "Organization: %s", hp->organization), tfappend(linebuf);
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		sprintf(linebuf, "From: %s", hp->from);
X#else
X		sprintf(linebuf, "Path: %s", tailpath(hp));
X#endif
X		if (fname[0] || hp->organization[0]) {
X			strcat(linebuf, " (");
X			if (fname[0] == '\0') {
X				strcpy(fname,hp->from);
X				p2 = index(fname,'@');
X				if (p2)
X					*p2 = '\0';
X			}
X			strcat(linebuf, fname);
X			if (hp->organization[0] && !hflag) {
X				strcat(linebuf, " @ ");
X				strcat(linebuf, hp->organization);
X			}
X			strcat(linebuf, ")");
X		}
X		tfappend(linebuf);
X		if (p1 != NULL)
X			*p1 = ' ';
X		if (hp->ctlmsg[0]) {
X			sprintf(linebuf, "Control: %s", hp->ctlmsg);
X			tfappend(linebuf);
X		}
X	}
X
X	strcpy(bfr, hp->nbuf);
X	ngdel(bfr);
X	if (verbose) {
X		sprintf(linebuf, "Newsgroups: %s", bfr); tfappend(linebuf);
X		sprintf(linebuf, "Date: %s", hp->subdate); tfappend(linebuf);
X		if (hp->sender[0])
X			sprintf(linebuf, "Sender: %s", hp->sender), tfappend(linebuf);
X		if (hp->replyto[0])
X			sprintf(linebuf, "Reply-To: %s", hp->replyto), tfappend(linebuf);
X		if (hp->followto[0])
X			sprintf(linebuf, "Followup-To: %s", hp->followto), tfappend(linebuf);
X	}
X	else if (strcmp(bfr, groupdir) != 0)
X		sprintf(linebuf, "Newsgroups: %s", bfr), tfappend(linebuf);
X}
X
X
X
X
X#ifdef MYDB
X
X
Xchar *
Xfindparent(id, num)
Xchar *id;
Xlong *num;
X{
X	register char *rcp;
X	struct artrec a;
X	char idbuf[BUFSIZE];
X	char *ngname();
X
X	strcpy(idbuf, id);
X	rcp = idbuf;
X	while(*++rcp)
X		if (isupper(*rcp))
X			*rcp = tolower(*rcp);
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				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 (*icol == ' ')
X				*icol++ = c;
X			else if (c == '_')
X				*icol++ |= ULINE;
X			else
X				*icol++ = (c | ULINE);
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
Xprget(prompter, buf)
Xchar *prompter, *buf;
X{
X	char *p, *q, *r;
X	int c, lastc;
X
X	curflag = CURP2;
X	r = buf;
X	lastc = '\0';
X	for (;;) {
X		*r = '\0';
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 >= ' ' && *p <= '~')
X				*p++ = *q;
X		}
X		*p = '\0';
X		c = vgetc();
X		if (c == '\n' || c == cintr) {
X			break;
X		}
X		if (c == cerase) {
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		} else {
X			*r++ = c;
X		}
X		lastc = c;
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;
X	char a[BUFLEN + 2];
X	extern char **environ;
X	int pstatus, retval;
X
X#ifdef SIGTSTP
X	signal(SIGTSTP, SIG_DFL);
X	signal(SIGTTIN, SIG_DFL);
X	signal(SIGTTOU, SIG_DFL);
X#endif
X	if (!(flags & BKGRND)) {
X		botscreen();
X		ttycooked();
X	}
X	while ((pid = fork()) == -1)
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			signal(SIGINT, SIG_IGN);
X			signal(SIGQUIT, SIG_IGN);
X			close(0), close(1), close(2);
X			open("/dev/null", 2);
X			dup(0), dup(0);
X		}
X		/* set $A */
X		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		umask(savmask);
X		execve(args[0], args, env);
X		fprintf(stderr, "%s: not found\n", 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, "continue? ");
X			while ((errno = 0, i = getchar()) != '\n'
X				&& (i != EOF || errno == EINTR));
X		}
X		signal(SIGQUIT, savequit);
X		ttyraw();
X		clearok(curscr, 1);
X#ifdef SIGTSTP
X		signal(SIGTSTP, onstop);
X		signal(SIGTTIN, onstop);
X		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 i;
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		if (p > linebuf + 24)
X			return i + 1;
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)
Xchar *tline;
X{
X	while (strlen(tline) > COLS) {
X		tfput(tline, lastlin++);
X		tline += COLS;
X		maxlinno++;
X	}
X	tfput(tline, lastlin++);
X}
X
X
Xtfput(tline, linno)
Xchar *tline;
X{
X	register char *p;
X	register FILE *rtfp;		/* try to make it a litte 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		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;
X{
X	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
Xupdscr()
X{
X	int count;
X	int i;
X
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, 48, timestr);
X	mvaddstr(PRLINE, 20, groupdir);
X	addch(' '); addnum(bit); addch('/'); addnum(pngsize); addch(' ');
X	if (ismail)
X		mvaddstr(PRLINE, 62, 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}
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
X/*** alarm handler ***/
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/*
X * Process alarm signal (or start clock)
X */
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
X	alflag = 0;
X	signal(SIGALRM, onalarm);
X	(void) time(&tod);
X	t = localtime(&tod);
X	i = 60 - t->tm_sec;
X	alarm(i > 30? 30 : i);			/* reset alarm */
X	hour = t->tm_hour % 12;
X	if (hour == 0)  hour = 12;
X	sprintf(timestr, "%.3s %d %d:%02d",
X		months + 3 * t->tm_mon, t->tm_mday, hour, t->tm_min);
X#ifdef GGRMAIL
X	if (mailf == NULL || stat(mailf, &statb) < 0) {
X		statb.st_size = 0;
X	}
X	if(statb.st_size > oldmsize) {
X		ismail = 1;
X		beep();
X	} else if (statb.st_size < oldmsize) {
X		ismail = 0;
X	}
X#else
X	ismail = 0;
X	if (mailf != NULL && stat(mailf, &statb) >= 0 && statb.st_size > 0L) {
X		ismail = 1;
X		if (oldmsize < statb.st_size) {
X			ismail = 2;		/* new mail */
X			beep();
X		}
X	} else {
X		statb.st_size = 0L;
X	}
X#endif
X	oldmsize = statb.st_size;
X	if (uflag && !xflag && --rccount < 0) {
X		writeoutrc();
X		if (secpr[0] == '\0')
X			strcpy(secpr, ".newsrc updated");
X		rccount = 10;
X	}
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	if (username[0] == '\0' || strlen(username) > 15)
X		return NULL;
X#ifdef USG
X	sprintf(mailname, "/usr/mail/%s", username);
X#else
X	sprintf(mailname, "/usr/spool/mail/%s", username);
X#endif
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/*
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		onquit();
X#endif
X#endif
X	if (c == '\f') {
X		clearok(curscr, 1);
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		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	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	ioctl(0, TCFLSH, (char *)0);
X#else
X#ifdef TIOCFLUSH
X	ioctl(0, TIOCFLUSH, (char *)0);
X#else
X	struct sgttyb tty;
X	ioctl(0, TIOCGETP, &tty);
X	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	unsigned oalarm;
X
X	oalarm = alarm(0);
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	alarm(oalarm);
X}
X
X
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 =

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

if test ! -d src
then
	mkdir src
	echo mkdir src
fi
echo x - src/visual.c
sed 's/^X//' >src/visual.c <<'*-*-END-of-src/visual.c-*-*'
X/*
X * readr - visual news interface.
X */
X
X#ifndef lint
Xstatic char	*SccsId = "@(#)visual.c	1.11	9/3/84";
Xstatic char Author[] = "@(#)visual interface written by Kenneth Almquist";
X#endif !lint
X
X#define GGRMAIL
X#ifdef USG
X#include <termio.h>
X#include <fcntl.h>
X#else
X#include <sgtty.h>
X#endif USG
X
X#include <errno.h>
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
X#ifdef BSD4_2
X#define BIT(_a) (1<<((_a)-1))
X#endif
X#ifdef MYDB
X#include "db.h"
X#endif MYDB
X
X#include <errno.h>
X
Xextern int errno;
X
X#ifdef SIGTSTP
X#include <setjmp.h>
X#endif
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 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
Xextern int errno;
XFILE *tmpfile();
Xchar *getmailname();
Xchar *findparent();
Xint onint(), onquit();
Xint onstop();
X
X/*
X * Kludge: space so that routines can access beyond
X * the end of arrays without messing me up.
X */
Xstatic char junk[64];
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 */
Xint ospeed;				/* terminal speed */
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
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 warrent 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  news = 0;
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	mktemp(tfname);
X	close(creat(tfname,0666));
X	if ((tfp = fopen(tfname, "w+")) == NULL)
X		xerror("Can't create temp file");
X	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	signal(SIGINT, onint);
X	signal(SIGQUIT, onquit);
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		strcpy(goodone, filename);
X		if (sigtrap)
X			return;
X		vcmd();
X	}
X
X	if (news)
X		botscreen();
X	ttycooked();
X	if (!news)
X		fprintf(stderr, "No news.\n");
X}
X
X/*
X * Read and execute a command.
X */
X
Xvcmd() {
X	register c;
X	char *p;
X	long count;
X	int countset;
X
X	appfile(fp, dlinno + ARTWLEN + 1);
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)
X			strcpy(prompt,"more? ");
X		else
X			sprintf(prompt,"more(%d%%)? ",((dlinno+ARTWLEN-hdrend)*100)/maxlinno);
X	} else {
X		atend = 1;
X		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	while ((c = vgetc()) >= '0' && c <= '9' || c==cerase || c ==ckill) {
X		if (c == cerase) {
X			count /= 10;
X			if (count == 0)
X				countset = 0;
X			continue;
X		}
X		if (c == ckill) {
X			countset = 0;
X			count = 0;
X			continue;
X		}
X		count = (count * 10) + (c - '0');
X		sprintf(p, "%ld", count);
X		countset = 1;
X	}
X	if (c == '\033') {			/* escape */
X		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);
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)
Xchar c;
Xlong count;
X{
X	int i;
X	long nart;
X	char *findhist();
X
X	switch (c) {
X
X	/* Show more of current article, or advance to next article */
X	case '\n':
X	case ' ':
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#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;
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 META|'v':
X	case '\2':	/* Control-B */
X		dlinno -= ARTWLEN * count;
X		if (dlinno < 0)
X			dlinno = 0;
X		break;
X
X	/* forward half a page */
X	case '\4':	/* Control-D, as in vi */
X		dlinno += ARTWLEN/2 * count;
X		break;
X
X	/* backward half a page */
X	case '\25':	/* Control-U */
X		dlinno -= ARTWLEN/2 * count;
X		if (dlinno < 0)
X			dlinno = 0;
X		break;
X
X	/* forward count lines */
X	case '\16':	/* Control-N */
X	case '\32':	/* Control-Z */
X		dlinno += count;
X		break;
X
X	/* backwards count lines */
X	case '\20':	/* Control-P */
X	case '\31':	/* Control-Y */
X		dlinno -= count;
X		if (dlinno < 0)
X			dlinno = 0;
X		break;
X
X	/* Turn displaying of article back on */
X	case 'l':
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				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' && c <= 'z')
X					*p = (ch - 'a' + 13) % 26 + 'a';
X				else if (ch >= 'A' && c <= '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	case 's':
X	case 'w': {
X		char *grn = groupdir;
X		int wflags;
X
X		msg("file: ");
X		curflag = CURP2;
X		while ((wflags = vgetc()) == ' ');
X		if (wflags == cintr) {
X			secpr[0] = '\0';
X			break;
X		}
X		if (wflags == '|') {
X			linebuf[0] = '|';
X			if (prget("| ", linebuf+1))
X				break;
X		} else {
X			pushback(wflags);
X			if (prget("file: ", linebuf))
X				break;
X		}
X		wflags = 0;
X		if (c == 's')
X			wflags |= SVHEAD;
X		if (count != 1)
X			wflags |= OVWRITE;
X		bptr = linebuf;
X		while( *bptr == ' ')
X			bptr++;	/* strip leading spaces */
X
X		if (*bptr != PIPECHAR && *bptr != '/') {
X			char	hetyped[BUFLEN];
X			char	*boxptr;
X			strcpy(hetyped, bptr);
X			if (boxptr = getenv("NEWSBOX"))
X				if (index(boxptr, '%'))
X					sprintf(bptr, boxptr, grn);
X				else
X					strcpy(bptr, boxptr);
X			else if (hetyped[0] == '~' && hetyped[1] == '/') {
X				strcpy(hetyped, bptr+2);
X				strcpy(bptr, userhome);
X			} else
X				bptr[0] = '\0';
X			if (bptr[0])
X				strcat(bptr, "/");
X			if (hetyped[0] != '\0')
X				strcat(bptr, hetyped);
X			else
X				strcat(bptr, "Articles");
X		}
X		vsave(bptr, wflags);
X		break;
X	}
X
X		/* back up  */
X	case '-':
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		strcpy(bfr, filename);
X		strcpy(filename, ofilename1);
X		strcpy(ofilename1, bfr);
X		obit = bit;
X		if (strcmp(groupdir, ogroupdir)) {
X			strcpy(bfr, groupdir);
X			selectng(ogroupdir, FALSE);
X			strcpy(groupdir, ogroupdir);
X			strcpy(ogroupdir, bfr);
X			ngrp = 1;
X			back();
X		}
X		bit = oobit;
X		oobit = obit;
X		obit = -1;
X		getnextart(TRUE);
X		return FALSE;
X
X		/* skip forwards */
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		onquit();
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		if (prget("!", p))
X			break;
X		flags = CWAIT;
X		if (*p == '\0') {
X			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 == '|') {
X			*p = '\0';
X			sprintf(bfr, "(%s)|mail '%s'", linebuf, username);
X			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();
X		break;
X
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		{
X		if (fp == NULL) {
X			msg("No current article");
X			break;
X		}
X		move(ARTWIN, 0);
X		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		for(;i<ARTWLEN; i++)
X			addstr(linebuf);
X		prflags |= HELPMSG|NEWART;
X		}
X		break;
X	case 'b':	/* backup 1 article */
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		if ((ptr1 = rindex(h->followid, ' ')) != NULL)
X			ptr1++;
X		else
X			ptr1 = h->followid;
X		strcpy(linebuf, ptr1);
X		msg("%s", linebuf);
X		curflag = CURP2;
X		updscr();		/* may take this out later */
X		goto searchid;
X
X	/* specific message ID. */
X	case '<':
X		/* could improve this */
X		linebuf[0] = '<';
X		if (prget("<", linebuf+1))
X			break;
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				sprintf(bfr, "<%s@%s.UUCP>", ptr2, ptr1);
X				strcpy(linebuf, bfr);
X			}
X		}
X		if (index(linebuf, '>') == NULL)
X			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		*ptr2++ = '\0';
X		sscanf(ptr2, "%ld", &nart);
X
X		/*
X		 * Go to a given article.  Ptr3 specifies the newsgroup
X		 * and nart specifies the article number.
X		 */
Xselectart:	aabs = TRUE;
X		FCLOSE(fp);
X		saveart;
X		strcpy(ogroupdir, ptr3);
X		if (strcmp(groupdir, ogroupdir)) {
X			strcpy(bfr, groupdir);
X			selectng(ogroupdir, TRUE);
X			strcpy(groupdir, ogroupdir);
X			strcpy(ogroupdir, bfr);
X			back();
X		}
X		bit = nart;
X		oobit = obit;
X		obit = -1;
X		getnextart(TRUE);
X		rfq = 0;
X		break;
X
X	/* follow-up article */
X	case 'f':
X		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			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			fclose(helpf);
X			prflags |= HELPMSG|NEWART;
X		}
X		break;
X
X	default:
X		if (c != ckill && c != cintr)
X			msg("Illegal command");
X		break;
X	}
X
X	return FALSE;
X}
X
Xcancel_command()
X{
X	tfilename = filename;
X	/*readmode = SPEC; bug? */
X	strcpy(rcbuf, h->path);
X	ptr1 = index(rcbuf, ' ');
X	if (ptr1)
X		*ptr1 = 0;
X	if (uid != ROOTID && strcmp(username, rcbuf)) {
X		msg("Can't cancel what you didn't write.");
X		return;
X	}
X	if (!cancel(stderr, h, 0)) {
X		clear(bit);
X		saveart;
X		nextbit();
X		obit = -1;
X		fp = NULL;
X	}
X	FCLOSE(fp);
X}
X
X/*
X * Generate replies
X */
X
Xreply()
X{
X	char *arg[4];
X	register FILE *rfp;
X	char subj[132];
X	extern char MAILPARSER[];
X	char *nogomsg;
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	strcpy(tf, tft);
X	mktemp(tf);
X	close(creat(tf,0666));
X	if ((rfp = fopen(tf, "w")) == NULL) {
X		msg("Can't create %s", tf) ;
X		return;
X	}
X	strcpy(subj, h->title);
X	if (!prefix(subj, "Re:")){
X		strcpy(bfr, subj);
X		sprintf(subj, "Re: %s", bfr);
X	}
X
X#ifdef INTERNET
X	if (h->sender[0])
X		p = h->sender;
X	else
X#endif
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	sprintf(rcbuf, "exec %s -t < %s; rm %s", MAILPARSER, tf, tf);
X	nogomsg = "Mail not sent";
X	putc('\n', rfp);
X	fstat(fileno(rfp), &statb);
X	creatm = statb.st_mtime;
X	fclose(rfp);
X
X	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		unlink(tf);
X		return;
X	}
X
X	if (access(tf, 4) || stat(tf, &statb)) {
X		msg("%s: no input file.", nogomsg);
X		unlink(tf);
X		return;
X	}
X	if (statb.st_mtime == creatm) {
X		msg("%s: cancelled.", nogomsg);
X		unlink(tf);
X		return;
X	}
X
X	arg[2] = rcbuf;
X	arg[3] = NULL;
X	prun(arg, BKGRND);
X	prflags |= NOPRT;
X}
X
Xnext_ng_command()
X{
X	obit = -1;
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	readmode = SPEC;
X	saveart;
X	back();
X	selectng(bptr, 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 || ((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	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 = fopen(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	{	/* strip off any notesfile header */
X		register c;
X		register char *p = h->title + strlen(h->title) - 5;
X		if (p > h->title
X		 && (strcmp(p, " (nf)") == 0 || strcmp(p, "(nf)\"") == 0)) {
X			if ((c = getc(fp)) != '#') {
X				ungetc(c, fp);
X			} else {
X				while ((c = getc(fp)) != '\n' && c != EOF);
X				while ((c = getc(fp)) != '\n' && c != EOF);
X				while ((c = getc(fp)) != '\n' && c != EOF);
X			}
X		}
X	}
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
X	lastlin = 0;
X	if (ngrp) {
X		pngsize = ngsize;
X		ngrp--;
X		if (!hflag) {
X			sprintf(linebuf, "Newsgroup %s", groupdir);  tfappend(linebuf);
X			tfappend("");
X		}
X	}
X	hdrstart = lastlin;
X	if (!hflag) {
X		sprintf(linebuf, "Article %s %s",
X			h->ident, briefdate(h->subdate));
X		tfappend(linebuf);
X	}
X	vhprint(h, pflag ? 1 : 0);
X	sprintf(linebuf, "(%d lines)", NLINES(h, fp)); tfappend(linebuf);
X	tfappend("");
X	hdrend = lastlin;
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	int	i;
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		strcpy(fname, p1+1);
X		p2 = index(fname, ')');
X		if (p2 != NULL)
X			*p2 = '\0';
X	}
X
X	sprintf(linebuf, "Subject: %s", hp->title);
X	if ((i = strlen(linebuf) - 7) > 9
X	 && strcmp(linebuf + i, " - (nf)") == 0
X	 && (strncmp(linebuf+9, "Re: ", 4) != 0 || i < 9+39))
X		linebuf[i] = '\0';		/* clobber "- (nf)" */
X	tfappend(linebuf);
X	if (!hflag && hp->keywords[0])
X		sprintf(linebuf, "Keywords: %s", hp->keywords), tfappend(linebuf);
X	if (verbose) {
X		sprintf(linebuf, "From: %s", hp->from); tfappend(linebuf);
X		sprintf(linebuf, "Path: %s", hp->path); tfappend(linebuf);
X		if (hp->organization[0])
X			sprintf(linebuf, "Organization: %s", hp->organization), tfappend(linebuf);
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		sprintf(linebuf, "From: %s", hp->from);
X#else
X		sprintf(linebuf, "Path: %s", tailpath(hp));
X#endif
X		if (fname[0] || hp->organization[0]) {
X			strcat(linebuf, " (");
X			if (fname[0] == '\0') {
X				strcpy(fname,hp->from);
X				p2 = index(fname,'@');
X				if (p2)
X					*p2 = '\0';
X			}
X			strcat(linebuf, fname);
X			if (hp->organization[0] && !hflag) {
X				strcat(linebuf, " @ ");
X				strcat(linebuf, hp->organization);
X			}
X			strcat(linebuf, ")");
X		}
X		tfappend(linebuf);
X		if (p1 != NULL)
X			*p1 = ' ';
X		if (hp->ctlmsg[0]) {
X			sprintf(linebuf, "Control: %s", hp->ctlmsg);
X			tfappend(linebuf);
X		}
X	}
X
X	strcpy(bfr, hp->nbuf);
X	ngdel(bfr);
X	if (verbose) {
X		sprintf(linebuf, "Newsgroups: %s", bfr); tfappend(linebuf);
X		sprintf(linebuf, "Date: %s", hp->subdate); tfappend(linebuf);
X		if (hp->sender[0])
X			sprintf(linebuf, "Sender: %s", hp->sender), tfappend(linebuf);
X		if (hp->replyto[0])
X			sprintf(linebuf, "Reply-To: %s", hp->replyto), tfappend(linebuf);
X		if (hp->followto[0])
X			sprintf(linebuf, "Followup-To: %s", hp->followto), tfappend(linebuf);
X	}
X	else if (strcmp(bfr, groupdir) != 0)
X		sprintf(linebuf, "Newsgroups: %s", bfr), tfappend(linebuf);
X}
X
X
X
X
X#ifdef MYDB
X
X
Xchar *
Xfindparent(id, num)
Xchar *id;
Xlong *num;
X{
X	register char *rcp;
X	struct artrec a;
X	char idbuf[BUFSIZE];
X	char *ngname();
X
X	strcpy(idbuf, id);
X	rcp = idbuf;
X	while(*++rcp)
X		if (isupper(*rcp))
X			*rcp = tolower(*rcp);
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				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 (*icol == ' ')
X				*icol++ = c;
X			else if (c == '_')
X				*icol++ |= ULINE;
X			else
X				*icol++ = (c | ULINE);
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
Xprget(prompter, buf)
Xchar *prompter, *buf;
X{
X	char *p, *q, *r;
X	int c, lastc;
X
X	curflag = CURP2;
X	r = buf;
X	lastc = '\0';
X	for (;;) {
X		*r = '\0';
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 >= ' ' && *p <= '~')
X				*p++ = *q;
X		}
X		*p = '\0';
X		c = vgetc();
X		if (c == '\n' || c == cintr) {
X			break;
X		}
X		if (c == cerase) {
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		} else {
X			*r++ = c;
X		}
X		lastc = c;
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;
X	char a[BUFLEN + 2];
X	extern char **environ;
X	int pstatus, retval;
X
X#ifdef SIGTSTP
X	signal(SIGTSTP, SIG_DFL);
X	signal(SIGTTIN, SIG_DFL);
X	signal(SIGTTOU, SIG_DFL);
X#endif
X	if (!(flags & BKGRND)) {
X		botscreen();
X		ttycooked();
X	}
X	while ((pid = fork()) == -1)
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			signal(SIGINT, SIG_IGN);
X			signal(SIGQUIT, SIG_IGN);
X			close(0), close(1), close(2);
X			open("/dev/null", 2);
X			dup(0), dup(0);
X		}
X		/* set $A */
X		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		umask(savmask);
X		execve(args[0], args, env);
X		fprintf(stderr, "%s: not found\n", 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, "continue? ");
X			while ((errno = 0, i = getchar()) != '\n'
X				&& (i != EOF || errno == EINTR));
X		}
X		signal(SIGQUIT, savequit);
X		ttyraw();
X		clearok(curscr, 1);
X#ifdef SIGTSTP
X		signal(SIGTSTP, onstop);
X		signal(SIGTTIN, onstop);
X		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 i;
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		if (p > linebuf + 24)
X			return i + 1;
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)
Xchar *tline;
X{
X	while (strlen(tline) > COLS) {
X		tfput(tline, lastlin++);
X		tline += COLS;
X		maxlinno++;
X	}
X	tfput(tline, lastlin++);
X}
X
X
Xtfput(tline, linno)
Xchar *tline;
X{
X	register char *p;
X	register FILE *rtfp;		/* try to make it a litte 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		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;
X{
X	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
Xupdscr()
X{
X	int count;
X	int i;
X
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, 48, timestr);
X	mvaddstr(PRLINE, 20, groupdir);
X	addch(' '); addnum(bit); addch('/'); addnum(pngsize); addch(' ');
X	if (ismail)
X		mvaddstr(PRLINE, 62, 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}
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
X/*** alarm handler ***/
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/*
X * Process alarm signal (or start clock)
X */
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
X	alflag = 0;
X	signal(SIGALRM, onalarm);
X	(void) time(&tod);
X	t = localtime(&tod);
X	i = 60 - t->tm_sec;
X	alarm(i > 30? 30 : i);			/* reset alarm */
X	hour = t->tm_hour % 12;
X	if (hour == 0)  hour = 12;
X	sprintf(timestr, "%.3s %d %d:%02d",
X		months + 3 * t->tm_mon, t->tm_mday, hour, t->tm_min);
X#ifdef GGRMAIL
X	if (mailf == NULL || stat(mailf, &statb) < 0) {
X		statb.st_size = 0;
X	}
X	if(statb.st_size > oldmsize) {
X		ismail = 1;
X		beep();
X	} else if (statb.st_size < oldmsize) {
X		ismail = 0;
X	}
X#else
X	ismail = 0;
X	if (mailf != NULL && stat(mailf, &statb) >= 0 && statb.st_size > 0L) {
X		ismail = 1;
X		if (oldmsize < statb.st_size) {
X			ismail = 2;		/* new mail */
X			beep();
X		}
X	} else {
X		statb.st_size = 0L;
X	}
X#endif
X	oldmsize = statb.st_size;
X	if (uflag && !xflag && --rccount < 0) {
X		writeoutrc();
X		if (secpr[0] == '\0')
X			strcpy(secpr, ".newsrc updated");
X		rccount = 10;
X	}
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	if (username[0] == '\0' || strlen(username) > 15)
X		return NULL;
X#ifdef USG
X	sprintf(mailname, "/usr/mail/%s", username);
X#else
X	sprintf(mailname, "/usr/spool/mail/%s", username);
X#endif
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/*
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		onquit();
X#endif
X#endif
X	if (c == '\f') {
X		clearok(curscr, 1);
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		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	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	ioctl(0, TCFLSH, (char *)0);
X#else
X#ifdef TIOCFLUSH
X	ioctl(0, TIOCFLUSH, (char *)0);
X#else
X	struct sgttyb tty;
X	ioctl(0, TIOCGETP, &tty);
X	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	unsigned oalarm;
X
X	oalarm = alarm(0);
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	alarm(oalarm);
X}
X
X
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	while (ioctl(1, TCSETAF, &oldtty) < 0 && errno == EINTR)
X		;
X	oflags &=~ O_NDELAY;
X	fcntl(0, F_SETFL, oflags) ;
X}
X
X#else
X
Xstatic struct sgttyb oldtty, newtty;
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	/* How to get/change terminal modes in a job control environment.
X	   This code is right from the 4.1 bsd jobs(3) manual page.
X	 */
X	int tpgrp, getpgrp();
X
Xretry:
X#ifdef BSD4_2
X	sigblock(BIT(SIGTSTP)|BIT(SIGTTIN)|BIT(SIGTTOU));
X#else !BSD4_2
X	signal(SIGTSTP, SIG_HOLD);
X	signal(SIGTTIN, SIG_HOLD);
X	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		signal(SIGTTOU, SIG_DFL);
X#ifdef BSD4_2
X		sigsetmask(sigblock(0) & ~BIT(SIGTTOU));
X#endif BSD4_2
X		kill(0, SIGTTOU);
X		/* job stops here waiting for SIGCONT */
X		goto retry;
X	}
X	signal(SIGTTIN, onstop);
X	signal(SIGTTOU, onstop);
X	signal(SIGTSTP, onstop);
X#ifdef BSD4_2
X	sigsetmask(sigblock(0) & ~(BIT(SIGTSTP)|BIT(SIGTTIN)|BIT(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	initterm();
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	rawterm();
X}
X
Xttyowait()
X{	/* wait for output queue to drain */
X#ifdef TIOCDRAIN	/* This ioctl is a local mod on linus */
X	ioctl(1, TIOCDRAIN, (char *)0);
X#endif
X}
X
X
X/*
X * Restore tty modes
X */
X
Xttycooked()
X{
X	cookedterm();
X	while (ioctl(1, TIOCSETN, (char *)&oldtty) < 0 && errno == EINTR)
X		;
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	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
Xonquit()
X{
X	botscreen();
X	vflush();
X	ttycooked();
X#ifdef SORTACTIVE
X	unlink(ACTIVE);
X#endif SORTACTIVE
X#ifdef COREDUMP
X	abort();
X#else
X	exit(0);
X#endif
X}
X
X#ifdef SIGTSTP
X
Xonstop(signo)
Xint signo;
X{
X	/* restore old terminal state */
X	botscreen();
X	vflush();
X	ttycooked();
X	signal(signo, SIG_DFL);
X#ifdef BSD4_2
X	sigblock(BIT(SIGALRM)|BIT(SIGINT));
X	sigsetmask(sigblock(0) & ~BIT(signo));
X#endif BSD4_2
X	kill(0, signo);	/* stop here until continued */
X
X	fprintf(stderr,"Vnews restarted.");
X	signal(signo, onstop);
X	/* restore our special terminal state */
X	ttyraw();
X	clearok(curscr, 1);
X	updscr();
X#ifdef BSD4_2
X	sigsetmask(sigblock(0) & ~(BIT(SIGALRM)|BIT(SIGINT)));
X#endif BSD4_2
X}
X
X#endif
X
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
X	saveoff = ftell(fp);
X	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		strcpy(temp, "/tmp/vnXXXXXX");
X		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			fclose(ufp);
X			isnew = 0;
X		}
X	}
X	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 V7MAIL
X		h->subtime = cgtdate(h->subdate);
X		fprintf(ufp, "From %s %s",
X#ifdef INTERNET
X				h->from,
X#else
X				h->path,
X#endif
X					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	fclose(ufp);
X	if (isprogram) {
X		sprintf(prog, "(%s)<%s", to + 1, fname);
X		shcmd(prog, CWAIT);
X		prflags |= NOPRT;
X	} else {
X		if ((flags & OVWRITE) == 0)
X			msg("file: %s %s", to, isnew ? "created" : "appended");
X		else
X			msg("file: %s written", to);
X	}
X
Xout:
X	if (isprogram) {
X		unlink(fname);
X	}
X	umask(N_UMASK);
X	fseek(fp, saveoff, 0);
X}
*-*-END-of-src/visual.c-*-*
echo x - src/vnews.help
sed 's/^X//' >src/vnews.help <<'*-*-END-of-src/vnews.help-*-*'
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              ^\  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                    K   Mark rest of newsgroup read
Xf   Post a followup article             b   Go back 1 article in same group
Xs   Save article in file
Xw   Save without header
XN   Go to newsgroup (next is default)
Xl   Display article (use after !, r, f, or ?)
X
X[Press l to see article again]
*-*-END-of-src/vnews.help-*-*
exit