Vnews part 6 (of 7)

sources-request at genrad.UUCP sources-request at genrad.UUCP
Sun Jan 27 01:46:11 AEST 1985


# From: ka at hou3c
#
# Welcome to vnews release 2.11-B 1/17/85.
# This is part 6 out of 7.
# Feed me into sh (NOT csh).

if test ! -d postnews
then	mkdir postnews
fi

if test ! -d vnews
then	mkdir vnews
fi

cat > postnews/postnm5.c <<\!E!O!F!
/*
 * STRING SCANNING ROUTINES:
 *	These routines act on a subject string which is pointed to by scanp.
 *
 * scchr	Skip over characters contained in string.
 * scnchr	Skip over characters not in string.
 * skipbl	Skip over spaces, tabs, and newlines.
 * scomment	Skip to the end of a comment.
 * scqnchr	Like scnchr, but skips over any char if inside quotes.
 * skipsw	Skip over spaces, tabs, newlines, and comments.
 * parseaddr	Parse an address, setting up pointers to various parts.
 * getval	Return the characters between two pointers.
 *
 * Copyright 1984 by Kenneth Almquist.  All rights reserved.
 *     Permission is granted for anyone to use and distribute, but not
 *     sell, this program provided that this copyright notice is retained.
 */

#include "postnm.h"

char *scanp;

scchr(chars)
      char *chars;
      {
      int flag = 0;

      while (*scanp && index(chars, *scanp)) {
            flag++;
            scanp++;
      }
      return flag;
}


scnchr(chars)
      char *chars;
      {
      int flag = 0;

      while (index(chars, *scanp) == NULL) {
            flag++;
            scanp++;
      }
      return flag;
}



skipbl() {
      register char c;

      while ((c = *scanp) == ' ' || c == '\t' || c == '\n')
            scanp++;
}



scomment() {
      int nest;
      register char *p;

      if (*(p = scanp) != '(')
            return 0;
      nest = 0;
      for (;;) {
            if (*p == '(')
                  nest++;
            else if (*p == ')') {
                  if (--nest == 0)
                        break;
            } else {
                  if (*p == '\\')
                        p++;
                  if (*p == '\0')
                        jsynerr("Nonterminated comment");
            }
            p++;
      }
      scanp = p;
      return 1;
}


scqnchr(chars)
      char *chars;
      {
      register char *p = scanp;

      for (;;) {
            if (*p == '"') {
                  p++;
                  while (*p != '"') {
                        if (*p == '\\')
                              p++;
                        if (*p++ == '\0')
                              jsynerr("Nonterminated string");
                  }
            } else {
                  if (index(chars, *p))
                        break;
            }
            p++;
      }
      if (p == scanp)
            return 0;
      else {
            scanp = p;
            return 1;
      }
}



skipws(chars)
      char *chars;
      {
      for (;;) {
            if (*scanp == '(')
                  scomment();
            else if (*scanp == '\0' || index(chars, *scanp) == NULL)
                  break;
            scanp++;
      }
}



/*
 * Parse an address.
 */

char *begreal, *endreal;		/* real name */
char *begaddr, *endaddr;		/* machine address */
char *beglocal, *endlocal;		/* local part */
char *begdomain, *enddomain;		/* domain part */

parseaddr() {
      char *startp = scanp;
      char *p;

      begreal = begdomain = NULL;
      if (*scanp == '.')
syntax:     jsynerr("Illegal address syntax");
      begaddr = beglocal = scanp;
      if (*scanp == '<') {
routeaddr:
            scanp++;
            skipbl();
            begaddr = beglocal = scanp;
            scqnchr(":>");
            if (*scanp == ':') {
                  scanp++;
                  skipbl();
                  beglocal = scanp;
                  scqnchr(">");
            }
            if (*scanp != '>')
                  goto syntax;
            scanp = beglocal;
            scqnchr("@>");
            endlocal = scanp;
            while (index(" \t\n", *(endlocal - 1)))
                  endlocal--;
            if (endlocal <= beglocal)
                  goto syntax;
            if (*scanp == '@') {
                  scanp++;
                  skipbl();
                  begdomain = scanp;
                  scqnchr(">");
                  enddomain = scanp;
                  while (index(" \t\n", *(enddomain - 1)))
                        enddomain--;
                  if (enddomain <= begdomain)
                        goto syntax;
            }
            if (*scanp != '>')
                  goto syntax;
            scanp++;
      } else {
            scqnchr(" \t\n@(,<");
            if (scanp == startp)
                  jsynerr("Program bug");
            endlocal = scanp;
            skipbl();
            if (*scanp == '@') {
                  scanp++;
                  skipbl();
                  begdomain = scanp;
                  if (scnchr(" \t\n(,<") == 0)
                        goto syntax;
                  enddomain = scanp;
                  if (enddomain[-1] == '.')
                        goto syntax;
            } else {
                  do p = scanp, skipbl();
                     while (scqnchr(" \t\n@(,<"));
                  if (*scanp != '<') {
                        scanp = endlocal;
                  } else {
                        begreal = beglocal, endreal = p;
                        goto routeaddr;
                  }
            }
      }
      if (begdomain)
            endaddr = enddomain;
      else
            endaddr = endlocal;
      if (begreal == NULL) {
            p = scanp;
            skipbl();
            if (*scanp != '(') {
                  scanp = p;
            } else {
                  begreal = scanp + 1;
                  scomment();
                  endreal = scanp;
                  scanp++;		/* skip over ')' */
            }
      }
}



char *
getval(p, q, buf)
      char *p, *q, *buf;
      {

      if (buf == NULL)
            buf = ckmalloc(q - p + 1);
      bcopy(p, buf, q - p);
      buf[q - p] = '\0';
      return buf;
}
!E!O!F!

cat > postnews/postreply.c <<\!E!O!F!
/*
 * This program handles replies and followups for vnews.
 * Synopsys:
 *	reply -r file		# mail a reply
 *	reply -f file		# generate a followup
 */

#include <stdio.h>
#include <sys/types.h>
#include "config.h"
#include "newsdefs.h"
#include "libextern.h"
#include "arthead.h"
#include <sys/stat.h>
#include <signal.h>
#include <ctype.h>


#define NOPOST 22

int followup;
char *original;
char *references;
int vnews;
struct arthead h;
int checksum;
int nchars;
FILE *tfp;
char tempfile[16];
char tempout[16];
int outfd;

FILE *ckfopen();
char *savestr();
int nopost();



main(argc, argv)
	char **argv;
	{
	int c;
	int pid;
	extern char *optarg;

	signal(SIGQUIT, SIG_IGN);
	while ((c = getopt(argc, argv, "r:f:v")) != EOF) {
		switch (c) {
		case 'r':
			followup = 0;
			original = optarg;
			break;
		case 'f':
			followup = 1;
			original = optarg;
			break;
		case 'v':
			vnews = 1;
			break;
		}
	}
	if (original == NULL)
		xerror("-f or -r must be specified");

	signal(SIGINT, nopost);

	pathinit();
	getuser();

	writetemp(followup);

	edit();
	if (samefile())
		nopost();

	/* fork off a child and let parent terminate */
	signal(SIGINT, SIG_IGN);
	signal(SIGQUIT, SIG_IGN);
	signal(SIGHUP, SIG_IGN);
#ifdef SIGTSTP
	signal(SIGTSTP, SIG_IGN);	/* I hope this works */
#endif
	if ((pid = fork()) == -1)
		fprintf(stderr, "postreply: can't fork\n");
	if (pid > 0)
		_exit(0);

	nice(4);
	sprintf(bfr, "%s.BAK", tempfile);
	unlink(bfr);

	postit();
	xxit(0);
}


/*
 * Print a recorded message warning the poor luser what he is doing
 * and demand that he understands it before proceeding.  Only do
 * this for newsgroups listed in LIBDIR/recording.
 */
recording(ngrps)
char *ngrps;
{
	char recbuf[BUFLEN];
	FILE *fd;
	char nglist[BUFLEN], fname[BUFLEN];
	int  c, n, yes;

	sprintf(recbuf, "%s/recording", LIB);
	fd = fopen(recbuf, "r");
	if (fd == NULL)
		return 0;
	while ((fgets(recbuf, sizeof recbuf, fd)) != NULL) {
		sscanf(recbuf, "%s %s", nglist, fname);
		if (ngmatch(ngrps, nglist)) {
			fclose(fd);
			if (fname[0] == '/')
				strcpy(recbuf, fname);
			else
				sprintf(recbuf, "%s/%s", LIB, fname);
			fd = fopen(recbuf, "r");
			if (fd == NULL)
				return 0;
			while ((c = getc(fd)) != EOF)
				putc(c, stderr);
			fclose(fd);
			fprintf(stderr, "Do you understand this?  Hit <return> to proceed, <BREAK> to abort: ");
			n = read(2, recbuf, BUFLEN);
			c = recbuf[0];
			yes = (c=='y' || c=='Y' || c=='\n' || c=='\n' || c==0);
			if (n <= 0 || !yes)
				return -1;
			else
				return 0;
		}
	}
	fclose(fd);
	return 0;
}



/*
 * Generate replies and followups.
 */


writetemp() {
	FILE *artfp;
	char subj[132];
	char *p;
	char *replyname();

	checksum = 0;
	nchars = 0;
	artfp = ckfopen(original, "r");
	if (gethead(&h, artfp) == NULL)
		xerror("Original article has garbled header");
	fclose(artfp);


	scopyn(h.h_title, subj, 128);
	if (!prefix(subj, "Re:") && !prefix(subj, "re:")) {
		strcpy(bfr, subj);
		sprintf(subj, "Re: %s", bfr);
	}

	p = h.h_nbuf;
	if (hset(h.h_followto))
		p = h.h_followto;
	strcpy(bfr, p);
	launder(bfr);
	if (followup && recording(bfr))
		nopost();

	gentemp(tempfile);
	tfp = ckfopen(tempfile, "w");

	wstr("Command: %s\n", followup? "followup" : "reply");
	wstr("Newsgroups: %s\n", bfr);
	wstr("To: %s\n", replyname(&h, bfr));
	wstr("Subject: %s\n", subj);
	if (hset(h.h_keywords))
		wstr("Keywords: %s\n", h.h_keywords);
	wstr("Distribution: %s\n", hset(h.h_distribution)? h.h_distribution : "");
	bfr[0] = 0;				/* References */
	if (hset(h.h_references)) {
		strcpy(bfr, h.h_references);
		strcat(bfr, " ");
	}
	strcat(bfr, h.h_ident);
	references = savestr(bfr);
	wstr("References: %s\n", bfr);
	wstr("\n");
	fclose(tfp);
}



wstr(fmt, a1, a2, a3, a4)
	char *fmt;
	{
	char buf[1024];
	register char *p;

	sprintf(buf, fmt, a1, a2, a3, a4);
	for (p = buf ; *p ; ) {
		checksum += *p++;
		nchars++;
	}
	fputs(buf, tfp);
}



/*
 * Check to see if the newsgroup is moderated.  If so, the moderator's
 * name is returned in moderator.
 */
#ifdef notdef
check_mod(newsgroups, moderator)
char *newsgroups;
char *moderator;
{
	register FILE *fd;
	char ng[64], mod[BUFLEN];

	sprintf(bfr, "%s/%s", LIB, "moderators");
	if ((fd = fopen(bfr, "r")) == NULL)
		return 0;
	while (1) {
		if (fgets(bfr, LBUFLEN, fd) == NULL) {
			fclose(fd);
			return 0;
		}
		twosplit(bfr, ng, mod);
		if (ngmatch(newsgroups, ng)) {
			strcpy(moderator, mod);
			fclose(fd);
			return 1;
		}
	}
}



/*
 * Split a line of the form
 *		text whitespace text
 * into two strings.	Also trim off any trailing newline.
 * This is destructive on src.
 */
twosplit(src, dest1, dest2)
char *src, *dest1, *dest2;
{
	register char *p;

	nstrip(src);
	for (p=src; isalnum(*p) || ispunct(*p); p++)
		;
	*p++ = 0;
	for ( ; isspace(*p); p++)
		;
	strcpy(dest1, src);
	strcpy(dest2, p);
}
#endif



edit() {
	char *p;
	int pid;
	int status;
	char *arg[5];
	register char **ap;
	char *getenv(), *rindex();

	if ((arg[0] = getenv("EDITOR")) == NULL)
		arg[0] = DFTEDITOR;
	if ((p = rindex(arg[0], '/')) != NULL)
		p++;
	else
		p = arg[0];
	ap = &arg[1];
	if (strcmp(p, "vi") == 0) {
		*ap++ = "+";
		*ap++ = tempfile;
		*ap++ = original;
	} else if (strcmp(p, "gmacs") == 0
		|| strcmp(p, "gemacs") == 0
		|| strcmp(p, "gem") == 0) {
		*ap++ = original;
		*ap++ = tempfile;
	} else {
		*ap++ = tempfile;
		*ap++ = original;
	}
	*ap = NULL;

	if ((pid = fork()) == -1)
		xerror("Can't fork");
	if (pid == 0) {
		signal(SIGQUIT, SIG_DFL);
		execvp(arg[0], arg);
		_exit(124);
	}
	signal(SIGINT, SIG_IGN);
	status = pwait(pid);
	if (status == 124 << 8)
		xerror("Can't exec %s", arg[0]);
}



samefile() {
	struct stat statb;
	register FILE *fp;
	register int c;

	if (stat(tempfile, &statb) < 0)
		xerror("%s has vanished!", tempfile);
	if (statb.st_size == 0L)
		return 1;
	if (statb.st_size != nchars)
		return 0;
	fp = ckfopen(tempfile, "r");
	while ((c = getc(fp)) != EOF)
		checksum -= c;
	return (checksum == 0);
}



postit() {
	int artfd;
	int i;

	gentemp(tempout);
	if ((outfd = creat(tempout, 0644)) < 0) {
		fprintf(stderr, "\7\nCan't create %s\nreply failed\n\7", tempout);
		xxit(1);
	}
	if (followup)
		writestr("Subject: followup failed\n\n", outfd);
	else
		writestr("Subject: reply failed\n\n", outfd);

	if (runpost()) {
		if ((artfd = open(tempfile, 0)) < 0)
			writestr("postreply: can't reopen article\n", outfd);
		else {
			writestr("\nYour article follows:\n", outfd);
			while ((i = read(artfd, bfr, LBUFLEN)) > 0)
				write(outfd, bfr, i);
			close(artfd);
		}
		close(outfd);
		unlink(tempfile);
		close(0);
		if (open(tempout, 0) != 0) {
			fprintf(stderr, "\7Fatal error in reply!\nCan't reopen %s\7\n", tempout);
			exit(1);
		}
#ifdef MAILER
		execl(MAILER, MAILER, username, (char *)0);
#endif
		execl("/bin/mail", "mail", username, (char *)0);
		fprintf(stderr, "\7Fatal error in reply!\nCan't exec mail program!\7\n");
		exit(1);
	}
}



runpost() {
	int pid;
	int status;
	int fildes[2];
	FILE *fp;
	int inewsfail;
	static char prog[] = POSTNM;

	if (pipe(fildes) < 0) {
		writestr("can't create pipe\n", outfd);
		return -1;
	}		
	while ((pid = fork()) == -1)
		sleep(10);
	if (pid == 0) {
#ifdef notdef
		close(0);
		if (open(tempfile, 0) != 0) {
			sprintf(bfr, "postreply: can't open %s\n", tempfile);
			writestr(bfr, outfd);
			_exit(2);
		}
#endif
		if (fildes[1] != 1) {
			close(1);
			dup(fildes[1]);
			close(fildes[1]);
		}
		close(fildes[0]);
		close(2);
		dup(1);
		execl(prog, prog, "-wiR", references, tempfile, (char *)0);
		writestr("postreply: can't exec postnm\n", 1);
		_exit(2);
	}
	close(fildes[1]);
	fp = fdopen(fildes[0], "r");
	inewsfail = 0;
	while (fgets(bfr, LBUFLEN, fp) != NULL) {
		writestr(bfr, outfd);
		if (prefix(bfr, "inews:"))
			inewsfail = 1;
	}
	status = pwait(pid);
	if (status != 0 || inewsfail) {
		if ((status & 0377) == 0)
			sprintf(bfr, "Exit status %d from postnm\n", status >> 8);
		else
			sprintf(bfr, "postreply: postnm died with signal %d\n", status & 077);
		writestr(bfr, outfd);
		return -1;
	}
	return 0;
}


#ifdef notdef
/*
 * Save a copy of the article in the users NEWSARCHIVE file.
 * The article is saved only if the user explicitly sets NEWSARCHIVE.
 * Currently, we save both news and mail replies, which is
 * rather questionable.
 */
save_article()
{
	register FILE *in, *out;
	register int c;
	time_t timenow, time();
	char *today, *ctime();
	char *ccname;

	if ((ccname = getenv("NEWSARCHIVE")) == NULL || ccname[0] == '\0')
		return 0;
	if ((in = fopen(tempfile, "r")) == NULL) {
		writestr("Can't reopen temp file for article save\n", outfd);
		return -1;
	}
	if ((out = fopen(ccname, "a")) == NULL) {
		sprintf(bfr, "Can't open %s to save article\n", ccname);
		writestr(bfr, outfd);
		return -1;
	}
	timenow = time((time_t)0);
	today = ctime(&timenow);
	fprintf(out, "From postreply %s", today);
	while ((c=getc(in)) != EOF)
		putc(c, out);
	putc('\n', out);
	fclose(in);
	fclose(out);
	return 0;
}
#endif


writestr(s, fd)
	char *s;
	int fd;
	{
	write(fd, s, strlen(s));
}


pwait(pid) {
	int status;

	while (wait(&status) != pid);
	return status;
}



gentemp(s)
	char s[16];
	{
	strcpy(s, "/tmp/repXXXXXX");
	mktemp(s);
}



xerror(fmt, a1, a2, a3, a4)
	char *fmt;
	{
	int c;

	fputs("postreply: ", stderr);
	fprintf(stderr, fmt, a1, a2, a3, a4);
	fputs("\nContinue?", stderr);
	while ((c = getchar()) != '\n' && c != EOF);
	xxit(1);
}



xxit(status) {
	if (tempfile[0])
		unlink(tempfile);
	if (tempout[0])
		unlink(tempout);
	exit(status);
}



nopost() {
	xxit(NOPOST);
}
!E!O!F!

cat > vnews/2.11features <<\!E!O!F!
+ Erase and kill processing performed on count.

+ Newsgroups presented in the order in which they appear in .newsrc.  This
  allows newsgroup presentation order to be set on an individual basis.

+ Articles are now presented grouped by discussion.  Within a discussion,
  articles are sorted by date.

+ An index page is now available.  The -A option will cause the index page
  to be shown on entry to each group.  Scrolling off the end of the index
  page using space gets you to the first article in the newsgroup.  You can
  execute practically all vnews commands on the index page; the scrolling
  commands scroll the index page and the other commands apply to the current
  article.  The 'a' command will toggle you to/from the index page.

+ There is now an article list stack.  The "N newsgroup" command will save
  the current newsgroup on the stack so that when you reach the end of the
  newsgroup all the articles in the newsgroup you just left will still be
  there.  Currently, the "N newsgroup" command empties the stack before
  beginning in order to keep things simple for the user.  The parent and
  '<' commands create an article list containing one article.  Unless the
  current article list contains a single article, the current article list
  will be saved on the stack, so that you will automaticly return to where
  you were.

+ The "N newsgroup command" will now work even with unsubscribed groups.
!E!O!F!

cat > vnews/ToDo <<\!E!O!F!
Read in next newsgroups during idle time.  May not be able to store in
memory, but could write record to temp file, and do sort when actually
enter group.

Chdir to spool directory while running.

Add .rnlock checking.  (Use .newsrc.lck since rn use SIGEMT.)

Make N- get real previous group.

Make jd command save discussion for later.

Make t command get you to first unread article.

Make xerror do a longjmp to the code that resets the tty modes and updates
the .newsrc file.  Save a copy of the .newsrc file in .newsrc.bak.

!E!O!F!

cat > vnews/artseq.c <<\!E!O!F!
#include "vnews.h"

#ifdef SMALL			/* try to avoid running out of core */
#define MAXNUMARTS	100	/* max arts we can read in one newsgroup */
#endif

static struct svartlist alstack[10];	/* stack of article lists */
struct svartlist *alptr = alstack;	/* stack pointer for above */
static int readmode = NEXT;		/* currently unused */
static struct ngentry *ngindex;		/* current location in .newsrc file */


/*
 * set or clear the unread article flag
 */
setnew(on) {
	if (alptr->al_type != SVNEWSGROUP)
		return;
	if (on)
		setunread(curart->i_artnum);
	else
		clrunread(curart->i_artnum);
}


/*
 * Go to a particular article number
 */

setartno(num)
int num;
{
	if (num <= 0) {
		msg("Bad article no.");
		return;
	}
	if (num > numthisng) {
		msg("Not that many articles.");
		return;
	}
	curart = &thisng[num - 1];
	setupart();
}


/*
 * Advance the counter to the next unread article.
 */
nextart(count, zapthem) 
{
	if (count <= 0)
		return FALSE;
	while (--count >= 0) {
		if (zapthem)
			setnew(0);
		if (readmode == SPEC || xflag) {
			curart++, curind++;
		} else {
			while (++curart, ++curind <= numthisng && !isunread(curart->i_artnum))
				;
		}
		if (curind > numthisng)
			return getnxtng(FORWARD);
	}
	setupart();
	return FALSE;
}


/*
 * Vnews article list stack.  Pushal(type) pushes the current article list
 * onto the stack.  Type is the type of the replacement article list.  Popal
 * restores an article list from the stack.  Setupart should be called after
 * popal.
 */

struct alsave {
	int a_numthisng;
	int a_curind;
	struct ngentry *a_curng;
	int a_iwartlin;
	int a_indexpg;
	int a_maxartno;
};


pushal(type) {
	struct alsave als;

	if (alptr >= alstack + 9) {
		msg("newsgroups nested too deeply");
		alptr->al_type = type;
		return;
	}
	alptr->al_tfoffset = tfoffset;
	als.a_numthisng = numthisng;
	als.a_curind = curind;
	als.a_curng = curng;
	als.a_iwartlin = indexwin.w_artlin;
	als.a_indexpg = indexpg;
	als.a_maxartno = maxartno;
	fseek(tfp, (long)tfoffset, 0);
	fwrite((char *)&als, sizeof als, 1, tfp);
	fwrite((char *)thisng, sizeof *thisng, numthisng, tfp);
	tfoffset = ftell(tfp);
	tflinno = -1;
	(++alptr)->al_type = type;
}


popal() {
	struct alsave als;

	tfoffset = (--alptr)->al_tfoffset;
	fseek(tfp, (long)tfoffset, 0);
	fread((char *)&als, sizeof als, 1, tfp);
	numthisng = als.a_numthisng;
	curind = als.a_curind;
	indexwin.w_artlin = als.a_iwartlin;
	indexpg = als.a_indexpg;
	fread((char *)thisng, sizeof *thisng, numthisng, tfp);
	indexwin.w_force = 2;
	tflinno = -1;
	if ((alptr+1)->al_type == SVNEWSGROUP)
		setupgrp(als.a_curng, als.a_maxartno);
	setartno(curind);
}



switchng(ngp)		/* go to specific newsgroup */
	struct ngentry *ngp;
	{
	if (alptr >= alstack + 8) {
		msg("Nesting too deep.");
		return;
	}
	if (alptr->al_type == SVARTICLE)
		popal();
	pushal(SVNEWSGROUP);
	readinng(ngp, 1);
	if (numthisng <= 0) {
		popal();
		msg("group? %s: empty", ngp->ng_name);
	} else {
		ngrp++;
	}
	setupart();
}


spclgrp(dp, a)
	DPTR dp;
	struct artrec *a;
	{
	if (alptr->al_type != SVARTICLE)
		pushal(SVARTICLE);
	scopyn(a->a_title, thisng[0].i_title, 36);
	scopyn(a->a_from, thisng[0].i_from, 29);
	thisng[0].i_nlines = a->a_nlines;
	thisng[0].i_artnum = a->a_group[0].a_artno;
	thisng[0].i_dptr = dp;
	numthisng = 1;
	curart = thisng;
	setupart();
	sprintf(filename, "%s/%s", SPOOL, a->a_file);
	indexpg = 0;
}


getnxtng(direct) {
	if (direct == FORWARD && alptr > alstack) {	/* al stack stuff */
		popal();
		setupart();
		/* advance to next article if current one read */
		if ((alptr+1)->al_type == SVNEWSGROUP
		 && isunread(curart->i_artnum) == 0)
			return nextart(1, 0);
		return FALSE;
	}
	while (alptr > alstack)		/* for backwards, clear al stack */
		popal();

	for (;;) {
		if (direct == FORWARD) {
			if ((ngindex = nextgrp(ngindex)) == NULL) {
				quitflg++;
				return TRUE;
			}
		} else {
			if ((ngindex = prevgrp(ngindex)) == NULL) {
				msg("Can't back up.");
				direct = FORWARD;
				continue;
			}
		}
		if (ngindex->ng_unsub || !wewant(ngindex->ng_name))
			continue;
		readinng(ngindex, 0);
		if (numthisng > 0)
			break;
	}
	ngrp++;
	setupart();
	return FALSE;
}


setupart() {
	if (ngrp) {
		pngsize = maxartno;
		if (Aflag)
			indexpg = 1;
		if (indexpg)
			ngrp = 0;
	}
	curind = curart - thisng + 1;
#ifdef DEBUG
	fprintf(stderr, "getnextart settles on #%d, bit %d\n", curind, curart->i_artnum);
#endif
	dirname(curng->ng_name, filename);
	sprintf(filename + strlen(filename), "/%d", curart->i_artnum);
	if (fp != NULL) {
		fclose(fp);
		fp = NULL;
	}
	news = TRUE;
	indexwin.w_force |= 1;
	erased = 0;
}

/*
 * Read in the newsgroup.
 */
readinng(ngp, all)
struct ngentry *ngp;
{
	register struct artinfo *ip;
	struct artrec a, a2;
	DPTR dp, dp2;
	int ngnum = ng_num(ngp);
	ARTNO bit, oldbit;
	int first;
	int i;
	static int thisngsize = 20;
	int cmppart();
	char *ckmalloc(), *realloc();

	if (news) {
		curflag = CURHOME;
#ifdef CURSES
		move(0, 0);	/* let user know we are thinking */
		refresh();
#else
		_amove(0, 0);	/* let user know we are thinking */
		vflush();
#endif
	}
	if (thisng == NULL) {
		thisng = ckmalloc(20 * sizeof thisng[0]);
	}
	numthisng = 0;
	oldbit = 0;
	first = 1;
	BKWD_GROUP(ngnum, bit, dp, a) {
		if (first) {
			setupgrp(ngp, bit);
			first = 0;
		}
		while (--oldbit > bit)		/* clear intermediate bits */
			clrunread(oldbit);
		oldbit = bit;
		if (bit < minartno && !all)
			break;
		if (!(all || isunread(bit)))
			continue;
		/* later:  rethink the handling of all */
		if ((a.a_flags & A_NOFILE) || !aselect(&a, 0) || haveunsub(dp)) {
skipit:			clrunread(bit);
			continue;
		}
		a2.a_subtime = a.a_subtime;
		for (dp2 = a.a_parent, i = 0 ; dp2 != DNULL && ++i < 100 ; dp2 = a2.a_parent) {
			if (haveunsub(dp2))
				goto skipit;
			readrec(dp2, &a2);
		}
		if (i == 100)
			fprintf(stderr, "Parent loop %s %s \r\n", a.a_ident, a2.a_ident);	/*DEBUG*/
		if (numthisng >= thisngsize) {
			thisngsize += 4;
			if ((thisng = realloc((char *)thisng, thisngsize * sizeof thisng[0])) == NULL)
				xerror("Out of space");
		}
		ip = &thisng[numthisng];
		ip->i_artnum = bit;
		ip->i_subtime = a.a_subtime;
		ip->i_basetime = a2.a_subtime;
		scopyn(a.a_title, ip->i_title, 37);
		scopyn(a.a_from, ip->i_from, 29);
		ip->i_dptr = dp;
		ip->i_nlines = a.a_nlines;
#ifndef DEBUG
		if (debugging > 1)
#endif
			fprintf(stderr, "storing %d in %d, subtime %d\n", bit, numthisng, ip->i_subtime);
		numthisng++;
#ifdef MAXNUMARTS
		if (numthisng >= MAXNUMARTS) {
			printf("Warning - more than %d new articles, missing some.\n", MAXNUMARTS);
			goto nomore;		/* exit loop */
		}
#endif
	}
	while (--oldbit >= minartno)
		clrunread(oldbit);
nomore:
	if (numthisng > 0) {
		qsort((char *)thisng, numthisng, sizeof thisng[0], cmppart);
	}
	curart = thisng;		/* go to start of group */
	indexwin.w_artlin = 0;
	indexwin.w_force = 2;
}


/*
 * Check whether we ahve unsubscribed to the discussion.
 */
haveunsub(dp)
	DPTR dp;
	{
	register DPTR *p;
	register int i;

	for (i = ndunsub, p = dunsub ; --i >= 0 ; p++)
		if (*p == dp)
			return 1;
	return 0;
}


/*
 * Compare two articles, and determine if they are in the same discussion.
 * If not, we sort by time of base article.
 */
cmppart(n1, n2)
register struct artinfo *n1, *n2;
{
	if (n1->i_basetime > n2->i_basetime)
		return 1;			/* different discussions */
	else if (n1->i_basetime < n2->i_basetime)
		return -1;			/* different discussions */
	else if (n1->i_subtime > n2->i_subtime)
		return 1;			/* same discussion */
	else if (n1->i_subtime < n2->i_subtime)
		return -1;			/* same discussion */
	else
		return 0;			/* same discussion */
}



/*
 * Return TRUE if the user has not ruled out this article.
 */
aselect(a, insist)
register struct artrec *a;
int	insist;
{
	extern char *titles[];

	if (insist)
		return TRUE;
	if (tflag && !titmat(a->a_title, titles))
		return FALSE;
	if (aflag && a->a_rectime < atime)
		return FALSE;
	if (a->a_ngroups > 1 && !rightgroup(a))
		return FALSE;
	if (fflag && a->a_parent != DNULL)
		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(a)
	struct artrec *a;
	{
	int i, flag;
	struct ngentry *ngp;

	flag = 1;
	for (i = 0 ; i < a->a_ngroups ; i++) {
		ngp = ngtable + a->a_group[i].a_ngnum;
		if (ngp == curng)
			return flag;
		if (wewant(ngp->ng_name) && ngp->ng_unsub == 0) {
			flag = 0;
		}
	}
	/* "Can't happen" */
	return 1;
}



/*
 * Return true if the newsgroup was specified in the -n option.
 */

wewant(name)
	char *name;
	{
	return ngmatch(name, sublist);
}
!E!O!F!

cat > vnews/curterm.c <<\!E!O!F!
/*
 * Additions to curses.
 */

#include <stdio.h>
#include <curses.h>

/*
 * move to the bottom of the screen.
 */

botscreen() {
	move(LINES-1, 0);
	refresh();
	putchar('\n');
	fflush(stdout);
}


/*
 * Clear a line.
 */

clrline(linno) {
	move(linno, 0);
	clrtoeol();
}


#ifdef ACURSES
/*
 * Ring the bell on the terminal.
 */

beep() {
	putchar('\007');
	fflush(stdout);
}
#endif
!E!O!F!

cat > vnews/dispcntl.c <<\!E!O!F!
#include "vnews.h"

#ifdef STATTOP
#define PRLINE	0	/* prompter line */
#define SPLINE	1	/* secondary prompt line */
#define ARTWIN	2	/* first line of article window */
#else
#define PRLINE	(ROWS-1)/* prompter line */
#define SPLINE	(ROWS-2)/* secondary prompt line */
#define ARTWIN	0	/* first line of article window */
#endif

int dumpart(), dumpheaders(), dumphelp(), dumphdr();
int nullsub();

struct window artwin = {		/* window containing article */
	0, 0, dumpart, 2};
struct window indexwin = {		/* window containing article index */
	0, 0, dumpheaders, 2};
struct window helpwin = {		/* window containing help message */
	0, 0, dumphelp, 2};
struct window emptywin = {		/* empty window */
	0, 0, nullsub, 2};
struct window hdrwin = {		/* window containing header */
	0, 0, dumphdr, 2};
extern struct svartlist *alptr;		/* article list stack */



/*
 * Open the current article.  Returns nonzero on failure.
 */
openart() {
	struct stat statb;
	FILE *gethead();

	if (fp != NULL)
		return 0;
#ifdef CURSES
	move(0, 0);	/* let user know we are thinking */
	refresh();
#else
	_amove(0, 0);	/* let user know we are thinking */
	vflush();
#endif
	artwin.w_force = 2;
	lastlin = 0;
	fp = fopen(filename, "r");
	if (fp == NULL) {
		msg("Can't open %s", filename);
		artlines = 0;
		artread = 1;
		return TRUE;
	}
	fstat(fileno(fp), &statb);
	artlength = statb.st_size;
	if (gethead(&h, fp) == NULL) {
		msg("Garbled header");
		fclose(fp);
		fp = NULL;
		artlines = 0;
		artread = 1;
		return TRUE;
	}
	{	/* strip off any notesfile header */
		register c;
		register char *p = h.h_title + strlen(h.h_title) - 5;
		if (p > h.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;
	if (! cflag && hdrend < ARTWLEN)
		hdronly = 1;
	artwin.w_artlin = 0;
	return 0;
}



/*
 * Print out whatever the appropriate header is
 */
fmthdr() {
	char *vbriefdate();
	char date[64];

	if (ngrp) {
		ngrp = 0;
		if (!hflag) {
			sprintf(bfr, "Newsgroup %s", curng->ng_name);  tfappend(bfr);
			tfappend("");
		}
	}
	hdrstart = lastlin;
	if (!hflag) {
		sprintf(bfr, "Article %s %s",
			h.h_ident, vbriefdate(h.h_subdate, date));
		tfappend(bfr);
	}
	vhprint(&h, pflag ? 1 : 0);
	sprintf(bfr, "(%d lines)", curart->i_nlines); tfappend(bfr);
	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, date)
	register char *q;
	char *date;
	{
	register char *p;
	register i;
	char day[2];

	p = date;
	if (q[3] == ',') {
		for (i = 3 ; --i >= 0 ; )
			*p++ = *q++;
		*p++ = ' ';
		q += 2;
	}
	day[0] = *q++;
	if (*q != ' ' && *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 date;
}		

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

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

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

	sprintf(bfr, "Subject: %s", hp->h_title);
	if ((i = strlen(bfr) - 7) > 9
	 && strcmp(bfr + i, " - (nf)") == 0
	 && (strncmp(bfr+9, "Re: ", 4) != 0 || i < 9+39))
		bfr[i] = '\0';		/* clobber "- (nf)" */
	tfappend(bfr);
	if (!hflag && hset(hp->h_keywords))
		sprintf(bfr, "Keywords: %s", hp->h_keywords), tfappend(bfr);
	if (verbose) {
		sprintf(bfr, "From: %s", hp->h_from); tfappend(bfr);
		sprintf(bfr, "Path: %s", hp->h_path); tfappend(bfr);
		if (hset(hp->h_organization))
			sprintf(bfr, "Organization: %s", hp->h_organization), tfappend(bfr);
	}
	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(bfr, "From: %s", hp->h_from);
#else
		sprintf(bfr, "Path: %s", tailpath(hp));
#endif
		if (fname[0] != '\0') {
			strcat(bfr, " (");
			strcat(bfr, fname);
			if (hset(hp->h_organization) && !hflag) {
				strcat(bfr, " @ ");
				strcat(bfr, hp->h_organization);
			}
			strcat(bfr, ")");
		}
		tfappend(bfr);
		if (p1 != NULL)
			*p1 = ' ';
		if (hset(hp->h_ctlmsg)) {
			sprintf(bfr, "Control: %s", hp->h_ctlmsg);
			tfappend(bfr);
		}
	}

	if (verbose) {
		sprintf(bfr, "Newsgroups: %s", hp->h_nbuf); tfappend(bfr);
		sprintf(bfr, "Date: %s", hp->h_subdate); tfappend(bfr);
		if (hset(hp->h_sender))
			sprintf(bfr, "Sender: %s", hp->h_sender), tfappend(bfr);
		if (hset(hp->h_replyto))
			sprintf(bfr, "Reply-To: %s", hp->h_replyto), tfappend(bfr);
		if (hset(hp->h_followto))
			sprintf(bfr, "Followup-To: %s", hp->h_followto), tfappend(bfr);
	}
	else if (strcmp(hp->h_nbuf, curng->ng_name) != 0)
		sprintf(bfr, "Newsgroups: %s", hp->h_nbuf), tfappend(bfr);
}

/*
 * 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;	/* &bfr[0] <= icol <= maxcol */

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



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


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



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 == INTR) {
			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 if (r < buf + SECPRLEN - 1) {
			*r++ = c;
		} else {
			beep();
		}
		lastc = c;
	}
	curflag = CURHOME;
	secpr[0] = '\0';
	if (c == INTR) {
		nextwin = curwin;
		return 1;
	} else
		return 0;
}



/*** 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;
#if BSDREL > 7
	i = (COLS + 1) &~ 1;
#else
	i = COLS;
#endif
	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);
#if BSDREL > 7
	fread(line, (COLS + 1) &~ 1, 1, tfp);
#else
	fread(line, COLS, 1, tfp);
#endif
	line[COLS] = '\0';
	tflinno++;
}


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

	if (linno != tflinno || wrflag != last) {
#if BSDREL > 7
		fseek(tfp, (long)linno * (COLS + 1 &~ 1) + tfoffset, 0);
#else
		fseek(tfp, (long)linno * COLS + tfoffset, 0);
#endif
		tflinno = linno;
		last = wrflag;
	}
}



/*** display management ***/


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


scroll(amount) {
	register struct window *w;

	w = &artwin;
	if (indexpg)
		w = &indexwin;
	if ((w->w_artlin += amount) < 0)
		w->w_artlin = 0;
}



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

static struct window *savewin;		/* window on last call to updscr */

updscr() {
	int i;
	register struct window *w = curwin;
	char buf[40];

	if (checkin())
		return;
	if (w != savewin)
		w->w_force = 2;
	if ((w->w_force || w->w_artlin != w->w_svartlin)
	 && (quitflg == 0 || w == &emptywin)) {
#ifndef CURSES
		if (w->w_force < 2)
			ushift(ARTWIN, ARTWIN+ARTWLEN-1, w->w_artlin - w->w_svartlin);
#endif
		for (i = ARTWIN ; i < ARTWIN + ARTWLEN ; i++)
			clrline(i);
		(*w->w_dump)(w->w_artlin, ARTWIN, ARTWLEN);
		w->w_force = 0;
		w->w_svartlin = w->w_artlin;
		savewin = w;
	}
	clrline(SPLINE), clrline(PRLINE);
#ifdef STATTOP
	mvaddstr(PRLINE, 0, prompt);
#else
	if (strlen(secpr) <= COLS)
		mvaddstr(PRLINE, 0, prompt);
#endif
	mvaddstr(PRLINE, 48, timestr);
	if (alptr->al_type == SVARTICLE)
		sprintf(buf, "%.18s", curng->ng_name);
	else
		sprintf(buf, "%.18s %d/%d ", curng->ng_name, curind, numthisng);
	mvaddstr(PRLINE, 20, buf);
	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');
}



/*
 * Dump the article to the screen.  Updscr has already cleared the region.
 */

dumpart(artlin, scrlin, count) {
	register int i;

	if (hdronly && count > hdrend - artlin)
		count = hdrend - artlin;
#ifdef DIGPAGE
	if (endsuba > 0 && count > endsuba - artlin)
		count = endsuba - artlin;
#endif
	if (count > lastlin - artlin)
		count = lastlin - artlin;
	for (i = 0 ; i < count ; i++) {
		tfget(bfr, artlin + i);
		mvaddstr(scrlin + i, 0, bfr);
	}
}


/*
 * All headers command: show headers for this group.
 */
dumpheaders(artlin, scrlin, count)
{
	register int i;

	for (i = count ; --i >= 0 ; artlin++, scrlin++) {
		if (artlin == 0) {
			sprintf(bfr, " IND FILE LINES Newsgroup %s, %d articles", curng->ng_name, numthisng);
			mvaddstr(scrlin, 0, bfr);
		} else if (artlin >= 2 && artlin < numthisng + 2) {
			move(scrlin, 0);
			addch(artlin-1 == curind ? '>' :
			    (xflag || isunread(thisng[artlin-2].i_artnum) ? ' ': 'D'));
			sprintf(bfr, "%3d%5d%5d %-36s  %s",
				artlin-1, thisng[artlin-2].i_artnum, thisng[artlin-2].i_nlines,
				thisng[artlin-2].i_title, thisng[artlin-2].i_from);
			bfr[COLS] = '\0';
			addstr(bfr);
		}
	}
}


/*
 * Dump the help message to the screen.  The help msg can't be scrolled.
 */
dumphelp(artlin, scrlin, count) {
	FILE *helpf;
#ifndef VHELP
	char VHELP[FPATHLEN];
#endif

#ifndef VHELP
	sprintf(VHELP, "%s/vnews.help", LIB);
#endif
	if ((helpf = fopen(VHELP, "r")) == NULL) {
		addstr("Can't open help file");
		return;
	}
	while (fgets(bfr, LBUFLEN, helpf) != NULL) {
		nstrip(bfr);
		mvaddstr(scrlin, 0, bfr);
		scrlin++;
	}
	fclose(helpf);
}


/*
 * Dump the full header on the screen.  The header cannot be scrolled.
 * We use the routine appfile to append to the file, which forces us
 * to do some juggling to return to the article.
 */
dumphdr(artlin, scrlin, count) {
	long saveoff;
	int svartread, svartlines, svlastlin;
	int stoppos = scrlin + count - 1 ;
	register int i;

	saveoff = ftell(fp);
	fseek(fp, 0L, 0);
	svartread = artread, svartlines = artlines, svlastlin = lastlin;
	artread = 0;
	appfile(fp, artlines + count);
	if (count > lastlin - svlastlin)
		count = lastlin - svlastlin;
	for (i = 0 ; i < count ; i++) {
		tfget(bfr, svlastlin + i);
		if (bfr[0] == '\0')
			break;
		mvaddstr(scrlin + i, 0, bfr);
	}
	nochange(i + 1, stoppos) ;
	artread = svartread, artlines = svartlines, lastlin = svlastlin;
	fseek(fp, saveoff, 0);
}


nullsub() {;}
!E!O!F!

cat > vnews/genmakefile <<\!E!O!F!
: This shell procedure generates the vnews makefile.

LIB=../lib
if test ! -f $LIB/makedefs
then	echo "$LIB/makedefs not does not exist"
	exit 1
fi
exec > temp
. $LIB/makedefs
cat <<!
# Vnews makefile 2.11-B 1/18/85

# definitions

!
cat $LIB/makedefs
sed -e 's:$LIB:'$LIB:g <<\!
LOGDIR =

IBMFLAGS =
DEBUG = -O
CFLAGS = $(DEBUG) $(IBMFLAGS) -I$LIB
LFLAGS = -i $(DEBUG) $(IBMFLAGS)

VOBJECTS = readnews.o vextern.o $(LOGDIR) vreadr.o dispcntl.o artseq.o termio.o virtterm.o

# dependencies

all: makefile vnews

makefile: genmakefile $LIB/makedefs
	genmakefile
	@echo 'Makefile changed, so restart make.'
	@sh -c 'exit 22'

install: all
	-/bin/mv $(BINDIR)/vnews $(BINDIR)/ovnews
	/bin/cp vnews $(BINDIR)/vnews
	-/bin/mv $(LIBDIR)/vnews.help $(LIBDIR)/ovnews.help
	/bin/cp vnews.help $(LIBDIR)/vnews.help

clean:
	rm -f core *.o

readnews.o:  readnews.c rparams.h
	$(CC) $(CFLAGS) -c readnews.c

rfuncs.o:  rfuncs.c rparams.h
	$(CC) $(CFLAGS) -c rfuncs.c

logdir.o: logdir.c
	$(CC) $(CFLAGS) -c logdir.c

vnews:	$(VOBJECTS) $LIB/rpathinit.o $LIB/rlib.a
	$(CC) $(LFLAGS) $(VOBJECTS) $LIB/rpathinit.o $LIB/rlib.a $(TERMCAP) -o $@

vreadr.o dispcntl.o artseq.o termio.o: vnews.h rparams.h

vextern.o:  vextern.c rparams.h

VNEWSLINT = artseq.c dispcntl.c readnews.c vextern.c rfuncs.c\
termio.c virtterm.c vreadr.c\
$LIB/addrc.c $LIB/afopen.c $LIB/allgroups.c $LIB/bcopy.s\
$LIB/bfr.c $LIB/bzero.c $LIB/cancel.c $LIB/ckfopen.c\
$LIB/ckmalloc.c $LIB/dirname.c $LIB/findgroup.c\
$LIB/genafopen.c $LIB/gethead.c $LIB/getuser.c $LIB/gfopen.c\
$LIB/hash.c $LIB/hfgets.c $LIB/isadmin.c $LIB/lookup.c\
$LIB/makehimask.c $LIB/mypathinit.c $LIB/nextgrp.c\
$LIB/ngchain.c $LIB/ngmatch.c $LIB/nsavestr.c $LIB/nstrip.c\
$LIB/openrc.c $LIB/pgetuser.c $LIB/prefix.c $LIB/prevgrp.c\
$LIB/process.c $LIB/read.c $LIB/readinrc.c $LIB/rename.c\
$LIB/roptions.c $LIB/savestr.c $LIB/scopyn.c $LIB/setupgrp.c\
$LIB/titmat.c

vnewslint:
	lint -I$LIB -DSPOOLDIR=\"$(SPOOLDIR) -DLIBDIR=\"$(LIBDIR) $(VNEWSLINT)		# You must be masochistic
!

mv temp makefile
!E!O!F!
chmod +x vnews/genmakefile

cat > vnews/readnews.c <<\!E!O!F!
/*
 * readnews - read news articles.
 */

/* static char	*SccsId = "%W%	%G%"; */

#include "rparams.h"
#include "artfile.h"
#include "ng.h"

/*
 * readnews - article reading program
 */


/*
 *	Authors:
 *		Matt Glickman	ucbvax!glickman
 *		Mark Horton	cbosg!mark
 *		Stephen Daniels	duke!swd
 *		Tom Truscott	duke!trt
 */

main(argc, argv)
int	argc;
register char	**argv;
{
	FILE *rcfp;
	FILE *openrc();
	time_t convdate() ;

	/* set up defaults and initialize. */
	pathinit();

#ifndef SHELL
	if ((SHELL = getenv("SHELL")) == NULL)
		SHELL = "/bin/sh";
#endif
	getuser();

	rcfp = openrc();
	roptions(argv, rcfp);

	if (datebuf) {
		atime = convdate(datebuf) ;
		free(datebuf) ;
	}

	/*
	 * ALL of the command line has now been processed. (!)
	 */

	if (sflag) {
		printf("Subscription list:  %s\n", sublist);
		xxit(0);
	}

	afopen();
	if (xflag)
		readinrc((FILE *)NULL);
	else {
		fseek(rcfp, 0L, 0);
		readinrc(rcfp);
	}
	fclose(rcfp);

#ifdef DEBUG
	fprintf(stderr, "sublist = %s\n", sublist);
#endif

	readr();

	fflush(stdout);
	if (xflag || lflag || tflag)
		xxit(0);
	writeoutrc();
	xxit(0);

	/* Camel, R.O.H. */
}



/*
 * convert a date to UNIX internal format.
 */

time_t
convdate(s)
      char *s ;
      {
      char buf[512] ;
      FILE *datefp, *popen() ;
      long cgtdate() ;
      long atol() ;

      sprintf(buf, "%s/cgtdate '%s'", LIB, s) ;
      if ((datefp = popen(buf, "r")) == NULL || fgets(buf, 512, datefp) == NULL)
            xerror("Can't convert -a date") ;
      return atol(buf) ;
}
!E!O!F!

cat > vnews/rfuncs.c <<\!E!O!F!
/*
 * rfuncs - functions for readnews.
 */

static char	*SccsId = "@(#)rfuncs.c	2.9	3/7/83";

#include "rparams.h"
#include "newsrc.h"

#ifdef notdef
/*
 * Figure out the number of the largest article in newsgroup ng,
 * and return that value.
 */
long
findngsize(ng)
char *ng;
{
	FILE *af;
	long s;
	char buf[100], n[100];

	af = xfopen(ACTIVE, "r");
	while (fgets(buf, sizeof buf, af)) {
		sscanf(buf, "%s %ld", n, &s);
		if (strcmp(n, ng) == 0) {
			fclose(af);
			return s;
		}
	}
	return 0;
}
#endif



xxit(status)
int	status;
{
	exit(status);
}


/*
 * Return true if the newsgroup was specified in the -n option.
 */

wewant(name)
	char *name;
	{
	return ngmatch(name, sublist);
}
!E!O!F!

cat > vnews/rparams.h <<\!E!O!F!
/*
 * rparams.h - parameters for readnews, rfuncs, and readr.
 */

/* static char *Rparams = "@(#)rparams.h	2.8	5/28/83"; */

#include "config.h"
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#ifdef SIGXCPU
/* 4.2BSD moved this file, grumble grumble */
#include <sys/time.h>
#else
#include <time.h>
#endif
#include "defs.h"
#include "arthead.h"
#include "libextern.h"
#include "roptions.h"

#define	NEXT	0
#define SPEC	1

#define	FORWARD	0
#define BACKWARD 1

/* external declarations stolen from params.h */
#ifndef SHELL
extern char	*SHELL;
#endif

/* external function declarations */
extern	char	*strcpy(), *strncpy(), *strcat(), *index(), *rindex();
extern	char	*ctime(), *mktemp(), *malloc(), *realloc(), *getenv();
extern	char	*arpadate(), *dirname();
extern	time_t	time(), getdate(), cgtdate();
extern	int	broadcast(), save(), newssave(), ushell(), pshell(), onsig();
extern	long	atol();

char *savestr();

/* external declarations specific to readnews */
extern int	mode;
extern time_t	atime;
!E!O!F!

cat > vnews/termio.c <<\!E!O!F!
#include "vnews.h"
#if USGREL >= 30
#include <termio.h>
#include <fcntl.h>
#else
#include <sgtty.h>
#endif
#include <errno.h>
extern int errno;
static int alflag;			/* set if unprocessed alarm signal */
static int intflag;			/* set if interrupt received */
#ifdef SIGTSTP
static int inraw;			/* true if in raw mode */
#endif
int onstop();


/*** 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 */
#ifndef CURSES
#if USGREL >= 30
int oflags;				/* fcntl flags (for nodelay read) */
#endif
#endif


/*
 * Input a character
 */

vgetc() {
	register c;
#if BSDREL >= 42
	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;
#ifndef CURSES
#if USGREL >= 30
			if (oflags & O_NDELAY) {
				oflags &=~ O_NDELAY;
				fcntl(0, F_SETFL, oflags);
			}
#endif
#endif
#if BSDREL >= 42
			/* Use a select because can be interrupted */
			readfds = 1; exceptfds = 1;
			innleft = select(1, &readfds, 0, &exceptfds, 0);
			if (innleft > 0) {
				if ((innleft = read(0, inbuf, INBUFSIZ)) > 0)
					break;
			}
#else
			if ((innleft = read(0, inbuf, INBUFSIZ)) > 0)
				break;
#endif
			if (hupflag)
				return INTR;
			if (innleft == 0 || errno != EINTR)
				abort();	/* "Can't happen" */
			if (intflag) {
				intflag--;
				return INTR;
			}
		}
		innext = inbuf + 1;
		innleft--;
		c = inbuf[0];
	}
#ifdef V6
	c &= 0177;
	if (c == '\034')	/* FS character */
		onquit();
	if (c == '\177')	/* DEL */
		onint();
#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() {
#ifndef CURSES
#if BSDREL > 7
	long count;

#endif
#endif
#ifdef STATTOP
	if (innleft > 0)
#else
	if (innleft > 0 || alflag)
#endif
		return 1;
#if !defined(CURSES) && (USGREL >= 30 || BSDREL > 7)
	if (ospeed == B9600)
		return 0;
	vflush();
	if (ospeed <= B300)
		ttyowait();
#if USGREL >= 30
	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() {
#if USGREL >= 30
	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;
#if BSDREL <= 7 && USGREL < 30
	int svalarm = alarm(0);	    /* signals cause UNIX to drop characters */
#endif

	for (p = outbuf ; p < outnext ; p += i) {
		if (hupflag)
			break;
		if ((i = write(1, p, outnext - p)) < 0) {
			if (errno != EINTR)
				abort();	/* "Can't happen" */
			i = 0;
		}
	}
	outnleft = BUFSIZ;
	outnext = outbuf;
#if BSDREL <= 7 && USGREL < 30
	alarm(svalarm);
#endif
}




/*** terminal modes ***/

#ifndef CURSES
#if USGREL >= 30
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];
	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;
#if BSDREL >= 40 && BSDREL <= 41
static struct tchars oldtchars, newtchars;
#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.
	 */
#if BSDREL >= 42
	int tpgrp, getpgrp();
#else
	short tpgrp, getpgrp();
#endif

retry:
#if BSDREL >= 42
	sigblock(1<<(SIGTSTP-1) | 1<<(SIGTTIN-1) | 1<<(SIGTTOU-1));
#else
	sigset(SIGTSTP, SIG_HOLD);
	sigset(SIGTTIN, SIG_HOLD);
	sigset(SIGTTOU, SIG_HOLD);
#endif
	if (ioctl(2, TIOCGPGRP, &tpgrp) != 0)
		goto nottty;
	if (tpgrp != getpgrp(0)) { /* not in foreground */
		sigset(SIGTTOU, SIG_DFL);
#if BSDREL >= 42
		sigsetmask(sigblock(0) & ~(1<<(SIGTTOU-1)));
#endif
		kill(0, SIGTTOU);
		/* job stops here waiting for SIGCONT */
		goto retry;
	}
	sigset(SIGTTIN, onstop);
	sigset(SIGTTOU, onstop);
	sigset(SIGTSTP, onstop);
#if BSDREL >= 42
	sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1) | 1<<(SIGTTIN-1) | 1<<(SIGTTOU-0)));
#endif
#endif
	if (gtty(1, &oldtty) < 0)
nottty:		xerror("Can't get tty modes");
	newtty = oldtty;
	newtty.sg_flags &=~ (CRMOD|ECHO|XTABS);
#if BSDREL >= 7
	newtty.sg_flags |= CBREAK;
#else
	newtty.sg_flags |= RAW;
#endif
	cerase = oldtty.sg_erase;
	ckill = oldtty.sg_kill;
	ospeed = oldtty.sg_ospeed;
#if BSDREL >= 40 && BSDREL <= 41
	ioctl(1, TIOCGETC, (char *)&oldtchars);
	newtchars = oldtchars;
	if (oldtchars.t_intrc == '\7')
		newtchars.t_intrc = -1;
#endif
	initterm();
}


/*
 * Set tty modes for visual processing
 */

ttyraw() {
#ifdef SIGTSTP
	inraw = 1;
#endif
	while (stty(1, &newtty) < 0 && errno == EINTR);
#if BSDREL >= 40 && BSDREL <= 41
	ioctl(1, TIOCSETC, (char *) &newtchars);
#endif
	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);
#if BSDREL >= 40 && BSDREL <= 41
	ioctl(1, TIOCSETC, (char *) &oldtchars);
#endif
#ifdef SIGTSTP
	inraw = 0;
#endif
}

#endif
#else
#ifdef HCURSES

ttysave() {
	initscr();
	idlok(stdscr, 1);
	intrflush(curscr, 0);
	cerase = erasechar();
	ckill = killchar();
}


ttyraw() {
	reset_prog_mode();
	cbreak();
	nonl();
	noecho();
}


ttycooked() {
	reset_shell_mode();
}


int _endwin;		/* [expletives deleted] */

#else

ttysave() {
#if USGREL >= 30
	struct termio tty;
#else
	struct sgttyb tty;
#endif

	initscr();
#if USGREL >= 30
	ioctl(0, TCGETA, &tty);
	cerase = tty.c_cc[VERASE];
	ckill = tty.c_cc[VKILL];
#else
	gtty(0, &tty);
	cerase = tty.sg_erase;
	ckill = tty.sg_kill;
#endif
}


ttyraw() {
	/*
	 * Vnews really isn't designed to work with RAW mode, so if you
	 * have anything approaching CBREAK mode, use it.
	 */
#ifdef CBREAK
	crmode();
#else
	raw();
#endif
	nonl();
	noecho();
}


ttycooked() {
	resetty();
}

#endif
#endif CURSES



/*** signal handlers ***/

onint() {
	signal(SIGINT, onint);
	clearin();			/* flush input queue */
#if BSDREL >= 40 && BSDREL <= 41
	ioctl(0, TIOCSTI, "\7");
#else
	intflag++;
#endif
}


onhup() {
	signal(SIGHUP, onhup);
	hupflag++;
}


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

#ifdef SIGTSTP

onstop(signo)
	int signo;
{
	int restore = inraw;
	int e = errno;

	/* restore old terminal state */
	if (restore) {
		botscreen();
		vflush();
		ttycooked();
	}
	sigset(signo, SIG_DFL);
#if BSDREL >= 42
	sigsetmask(sigblock(0) & ~(1<<(signo-1)));
#endif
	kill(getpid(), signo);	/* stop here until continued */

	/* fprintf(stderr,"Vnews restarted."); */
	sigset(signo, onstop);
	/* restore our special terminal state */
	if (restore) {
		ttyraw();
#ifdef TIOCSTI
		ioctl(0, TIOCSTI, "\f");
#else
		clearok(curscr, 1);	/* doesn't seem to work */
#endif
	}
	errno = e;
}

#endif



/*** 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;
	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;
		needbeep++;
	} 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 */
			needbeep++;
		}
	} else {
		statb.st_size = 0L;
	}
	oldmsize = statb.st_size;
#endif
	if (uflag && --rccount < 0) {
		writeoutrc();
		if (secpr[0] == '\0')
			strcpy(secpr, ".newsrc updated");
		rccount = 10;
	}
}
!E!O!F!

cat > vnews/vextern.c <<\!E!O!F!
/*
 * rextern - external definitions for readnews
 */

/* static char	*SccsId = "@(#)rextern.c	2.5	3/30/83"; */

#include "rparams.h"

time_t	atime;

#ifndef SHELL
char	*SHELL;
#endif
!E!O!F!

echo Part 6 of 7 extracted.



More information about the Mod.sources mailing list