v13i022: VN newsreader, 1/88 version, Part04/05

Rich Salz rsalz at bbn.com
Sat Feb 6 02:47:42 AEST 1988


Submitted-by: Bob Mcqueer <amdahl!rtech!rtech!bobm at UUNET.UU.NET>
Posting-number: Volume 13, Issue 22
Archive-name: vn.jan.88/part04

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	vn.c
#	help.c
#	pagefile.c
#	digest.c
#	brk.h
#	head.h
#	node.h
#	page.h
#	reader.h
#	server.h
#	tty.h
#	tune.h
#	vn.h
export PATH; PATH=/bin:$PATH
echo shar: extracting "'vn.c'" '(8371 characters)'
if test -f 'vn.c'
then
	echo shar: will not over-write existing file "'vn.c'"
else
cat << \SHAR_EOF > 'vn.c'
/*
vn news reader for visual page oriented display of news
aimed at scanning large numbers of articles.

Original program by Bob McQueer in several versions 1983-1986.  Released
into the public domain in 1986.  While no copyright notice appears, the
original author asks that a history of changes crediting the proper people
be maintained.

Bob McQueer
{amdahl, sun, mtxinu, hoptoad, cpsc6a}!rtech!bobm

History:

	(bobm at rtech) 5/86 - first "public" version

	(bobm at rtech) 12/86 - version incorporates:

		bug fixes:
			str_store NULL string bug
			not picking up first article in newsgroup
			RESTART terminal reset for exit to editor.
			skip whitespace in "empty" digest lines while unpacking
			DISTRIBUTION line in followups.
			:100%: prompt on last line in reader.
			interpretation of multiple negations -w -t options.

			Many thanks to several people who noted the first
			two bugs as well as fixes.

			Thank you to Karl Williamson for helpful information
			tracking down the "concept terminal" bug (RESTART).

		SYSV ifdef's adapted from those done by Larry Tepper
		at ATT Denver - sent in by Karl Williamson, drutx!khw.
		Many people submitted SYSV ifdef's - thank you all.

		print capability from reader from Karl Williamson,
		drutx!khw

		Changes to use alternate header lines for mail, from changes
		by Andy Marrinson, andy at icom.UUCP (ihnp4!icom!andy).  Ifdef'ed
		to allow local configuration (bobm at rtech).

		"author_copy" file for followups, prevention of multiple
		"re: "'s, insert blank line and original author line
		before excerpted text from Andy Marrinson, andy at icom.UUCP

		Search string capability in reader, from Lawrie Brown,
		seismo!munnari!cdsadfa.oz!lpb (Australia).  Somewhat
		modified by interaction with the :100%: bug.

		Arrow key support, adapted from changes by Lawrie Brown.
		Modified to simply not allow control keys for arrows (allowing
		SOME controls is too prone to problems, esp. with .vnkey), and
		to allow the PAGEARROW ifdef (bobm at rtech)

		prevention of followups to "mod" and "announce", from
		Lawrie Brown.

		OLDRC ifdef adapted from changes by Lawrie Brown.  ADDRMUNGE
		added to allow OZ domain addressing changes from Australia
		to be grafted back in, and provide a hook for anybody else
		wanting to do something similar.

		Bob McQueer, bobm at rtech:

			a menu selection from the % command to jump to
			a new newsgroup

			linked list on hash table - no longer a compiled
			in limit for number of newsgroups

			.vnkey keystroke mapping file.

			options to get the % command list on entry, and to
			change how unsubscribed groups are handled for updates.

			allow configurable use of vs / ve pair for terminal
			handling.

	8/87 (bobm at rtech)

		Server interface changes.  Creation of vns_xxx routines
		formally defining how to attach vn to an abstracted
		news server, rearranged existing code to use that
		abstraction.  Sourcefile list altered a good bit by
		the rearrangement.  Some of this was splitting some
		routines out from vn.c into smaller modules.  Reader
		code was altered a good bit to work through the
		ARTHEADER abstraction, rather than searching for
		header lines.  Temp file writing code was the other
		area heavily affected.  The std.c server interface
		essentially incorporates the old newsrc.c code, plus
		the old outgroup() and gethead() routines.

		vns_changes also included:

			Modification of mailer interface for MAILCHOOSE,
			getting rid of ADDRMUNGE (superseded by vns
			interface definition)

			Moved readstr() into the session loop code,
			allowing a lot of static declarations to be
			placed therein.  Also made it possible to
			use strtok() underneath readstr().

			Made "save" write directly into file, rather
			than forking a cat (gee, that sounds perverse).

			Also fixes it so that "|" save convention works
			from reader.

			Digest unpacking has to know about header lines,
			unfortunately, so it has local definitions for
			some header lines.

		Incorporates:

			The much-discussed "continued header line" bug
			fix, of course.  Includes the multiple header
			line nfgets() by Andrew Worsley, andrew at munnari.oz,
			with a couple cosmetic changes.

			Top / Middle / Bottom keys from Karl Williamson
			print capability from reader from Karl Williamson,
			drutx!khw

			Edit old save file changes from George Pavel,
			gp at lll-lcc.arpa.  Used it to allow edit of
			the some other old strings, too.

			Fix for the bad number input bug on the choose
			new group from list feature, as reported by
			Dave Tallman, tallman at hc.arpa.  His fix with
			a few minor changes.

			A couple save file tweaks - allow a "%s" in VNSAVE
			to specify individual directories by newsgroup.
			Allow a "w:" prefix on filename to write instead
			of appending, allow %d for article number in name.

			The VNEDITOR variable.

			Statistics collection ability.

			Update of .newsrc "read" number to reflect removed
			articles, preventing rescanning of group on next
			session.

			take out SIGHUP catching to avoid problems with
			message being output.

			key to print version being used.

	Bug fixes following 12/87 posting.  Made this version 1/88 / res1.1
	to distinguish.

		Digest extraction in reader.c, fix from steve at mahendo.  Thanks
		to steve at mahendo & greg at mahendo for tracking down digest
		extraction bugs.  I obviously didn't beat on the feature enough
		after rearranging things to abstract the server interface.
		The digest extraction display is a little less informative,
		the price paid to allow mail & followup.

		SYS V ifdefs - svart.c, independently arrived at by several
		people - thank you.  At the behest of a couple people I made
		it spawn a "mkdir" instead of punting by calling mknod.

		Fix bug which would cause vn to crash if article has
		no "From" / "Reply-to" / "Path" line. - std.c

		Fix bug preventing assignment of .newsrc to filesystem other
		than that containing user's HOME.

		Fix excessive allocation in hash.c

		Handle duplicate active list records more gracefully.

		term_set(RESTART) call added to recovery from being
		backgrounded in sig_set.c, in case something you ran while
		backgrounded messed up your terminal state.  I lost the name
		of the person reporting the problem & fix - my apologies.

		display optimization in reader.c which repaints instead of
		scrolling if indicated by user's MORE variable.  This came
		from Greg Earle, earle at mahendo.  Modified slightly for
		cosmetic reasons & to fix a small folded-line bug.

Known bugs:

	If your terminal init string contains a newline, I suspect you will
	get an initial "stopped on tty output" if you fire up backgrounded.
	Cooked mode until session is started probably saves us in a lot of
	cases where the init string contains no newlines.  Can be fixed, but
	it's esoteric enough that I don't want to add another file to the
	"patched" list.

	non-erasure of stuff on prompt line when the new
	string includes an escape sequence (like PS1 maybe)
	because it doesn't realize that the escape sequence
	won't overprint the existing stuff

	control-w and update on exit may not update pages which have been
	scanned in funny orders by jumping into the middle of groups

	inaccurate numbers on '%' command results - reflect ranges, not
	actual numbers of articles.

	no arrow keys recognized which don't begin with <escape>

	doesn't know about the version 2.11 'm' in active list, or
	use the 'y' / 'n' either.

	crash due to embedding $\(\) type substring specifiers in regular
	expressions.  Obscure and hard to fix in a proper and portable way.
*/
#include <stdio.h>
#include "node.h"
#include "tty.h"
#include "brk.h"

extern int Lrec;

extern NODE **Newsorder;
extern int Ncount;

extern int Listfirst, Nounsub;

main(argc,argv)
int argc;
char **argv;
{
	/*
		initialize environment variables,
		scan .newsrc file, using any command line options present.
	 */
	term_set (START);
	envir_set ();
	sig_set (BRK_IN);

	stat_start();

	hashinit();
	temp_open();

	vns_news (argc,argv,&Listfirst,&Nounsub);

	fw_done ();

	make_newsorder();

	tty_set (BACKSTOP);

	if (Lrec >= 0)
		session ();
	else
	{
		new_groups ();
		fprintf (stderr,"\nNo News\n");
	}

	tty_set (COOKED);

	/* exiting, don't worry about FLG_ECHG resetting */
	vns_write(Newsorder,Ncount);

	term_set (STOP);
	vns_exit(0);

	stat_end( Lrec >= 0 ? 1 : 0 );
}
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'help.c'" '(5499 characters)'
if test -f 'help.c'
then
	echo shar: will not over-write existing file "'help.c'"
else
cat << \SHAR_EOF > 'help.c'
/*
** vn news reader.
**
** help.c - print help
**
** see copyright disclaimer / history in vn.c source file
*/
#include <stdio.h>
#include <setjmp.h>
#include "config.h"
#include "tty.h"
#include "tune.h"
#include "node.h"
#include "page.h"
#include "reader.h"
#include "vn.h"

extern int L_allow;
extern int C_allow;
extern int Digest;
extern char *Contstr;
extern char Cxptoi[], Cxrtoi[];

/*
	Help message table.  Character for command, plus its help
	message.  Table order is order of presentation to user.
*/
static struct HELPTAB
{
	char cmd, *msg;
	int dig;
	char *amsg;
} 
Helptab [] =
{
	{ QUIT, "quit", 1, NULL},
	{ UP, "(or up arrow) move up [number of lines]", 1, NULL},
	{ DOWN, "(or down arrow) move down [number of lines]", 1, NULL},
#ifdef PAGEARROW
	{ BACK, "(or left arrow) previous page [number of pages]", 1, NULL},
	{ FORWARD, "(or right arrow) next page [number of pages]", 1, NULL},
#else
	{ BACK, "previous page [number of pages]", 1, NULL},
	{ FORWARD, "next page [number of pages]", 1, NULL},
#endif
	{ TOPMOVE, "move to top of page", 1, NULL},
	{ BOTMOVE, "move to bottom of page", 1, NULL},
	{ ALTBOTTOM, "move to bottom of page (alternate L)", 1, NULL},
	{ MIDMOVE, "move to middle of page", 1, NULL},
	{ DIGEST, "unpack digest", 1, "exit digest"},
	{ READ, "read article [number of articles]", 1, NULL},
	{ ALTREAD, "read article (alternate 'r')", 1, NULL},
	{ READALL, "read all articles on page", 1, NULL},
	{ READSTRING, "specify articles to read", 1, NULL},
	{ SAVE, "save or pipe article [number of articles]", 1, NULL},
	{ SAVEALL, "save or pipe all articles on page", 1, NULL},
	{ SAVESTRING, "specify articles to save", 1, NULL},
	{ ALTSAVE, "specify articles to save (alternate ctl-s)", 1, NULL},
	{ PRINT, "print article [number of articles]", 1, NULL},
	{ PRINTALL, "print all article on page", 1, NULL},
	{ PRINTSTRING, "specify articles to print", 1, NULL},
	{ UPDATE, "update .newsrc status to cursor", 0, NULL},
	{ UPALL, "update .newsrc status for whole newsgroup", 0, NULL},
	{ UPSEEN, "update .newsrc status for all pages displayed", 0, NULL},
	{ ORGGRP, "recover original .newsrc status for newsgroup", 0, NULL},
	{ ORGSTAT, "recover all original .newsrc status", 0, NULL},
	{ SSTAT, "display count of groups and pages - shown and total", 0, NULL},
	{ GRPLIST, "list newsgroups with new article, updated counts", 0, NULL},
	{ NEWGROUP, "specify newsgroup to display and/or resubscribe to", 1, NULL},
	{ UNSUBSCRIBE, "unsubscribe from group", 0, NULL},
	{ MARK, "mark/unmark article [number of articles]", 1, NULL},
	{ ART_MARK, "mark/unmark article [number of articles]", 1, NULL},
	{ UNMARK, "erase marks on articles", 1, NULL},
	{ HEADTOG, "toggle flag for display of headers when reading", 1, NULL},
	{ SETROT, "toggle rotation for reading", 1, NULL},
	{ REDRAW, "redraw screen", 1, NULL},
	{ UNESC, "escape to UNIX to execute a command", 1, NULL},
	{ PRTVERSION, "show vn version", 1, NULL},
	{ HELP, "show this help menu", 1, NULL}
};

#define HTSIZE (sizeof(Helptab)/sizeof(struct HELPTAB))

/*
	help from main screen
*/
help ()
{
	int i,lcount,lim; 
	term_set (ERASE);
	lim = L_allow + RECBIAS - 2;
	printf("%s\n",HELP_HEAD);
	lcount = HHLINES;
	for (i=0; i < (sizeof(Helptab))/(sizeof(struct HELPTAB)); ++i)
	{
		if (Digest && !(Helptab[i].dig))
			continue;
		++lcount;
		if (Digest && Helptab[i].amsg != NULL)
			h_print (Cxptoi[Helptab[i].cmd],Helptab[i].amsg);
		else
			h_print (Cxptoi[Helptab[i].cmd],Helptab[i].msg);
		if (lcount >= lim)
		{
			printf ("\n%s",Contstr);
			getnoctl ();
			term_set (MOVE,0,lim+1);
			term_set (ZAP,0,strlen(Contstr));
			term_set (MOVE,0,lim-1);
			putchar ('\n');
			lcount = 0;
		}
	}
	if (lcount > 0)
	{
		printf ("\n%s",Contstr);
		getnoctl ();
	}
}

/*
	help from reader
*/
help_rd()
{
	h_print (Cxrtoi[PG_NEXT],HPG_NEXT);
	h_print (Cxrtoi[PG_QUIT],HPG_QUIT);
	h_print (Cxrtoi[PG_FLIP],HPG_FLIP);
	h_print (Cxrtoi[PG_REWIND],HPG_REWIND);
	h_print (Cxrtoi[PG_WIND],HPG_WIND);
	h_print (Cxrtoi[PG_SEARCH],HPG_SEARCH);
	h_print (Cxrtoi[PG_STEP],HPG_STEP);
	h_print (Cxrtoi[PG_REPLY],HPG_REPLY);
	h_print (Cxrtoi[PG_FOLLOW],HPG_FOLLOW);
	h_print (Cxrtoi[SAVE],HPG_SAVE);
	h_print (Cxrtoi[PRINT],HPG_PRINT);
	h_print (Cxrtoi[SETROT],HPG_ROT);
	h_print (Cxrtoi[HEADTOG],HPG_HEAD);
	h_print (Cxrtoi[PG_HELP],HPG_HELP);
	printf ("%s\n",HPG_DEF);
}

srch_help(c,dig)
char c;
int *dig;
{
	int i;

	for (i=0; i < HTSIZE; ++i)
		if (Helptab[i].cmd == c)
			break;
	if (i < HTSIZE)
	{
		*dig = Helptab[i].dig;
		return (0);
	}
	return(-1);
}

/*
	h_print prints a character and a legend for a help menu.
*/
static
h_print(c,s)
char c,*s;
{
	if (strlen(s) > (C_allow - 14))
		s [C_allow - 14] = '\0';
	if (c > ' ' && c != '\177')
		printf ("	 %c - %s\n",c,s);
	else
	{
		switch (c)
		{
		case '\177':
			printf ("  <delete> - %s\n",s);  
			break;
		case '\040':
			printf ("   <space> - %s\n",s);  
			break;
		case '\033':
			printf ("  <escape> - %s\n",s);  
			break;
		case '\n':
			printf ("  <return> - %s\n",s);  
			break;
		case '\t':
			printf ("     <tab> - %s\n",s);  
			break;
		case '\b':
			printf (" <back sp> - %s\n",s);  
			break;
		case '\f':
			printf ("<formfeed> - %s\n",s);  
			break;
		case '\07':
			printf ("    <bell> - %s\n",s);  
			break;
		case '\0':
			printf ("    <null> - %s\n",s);  
			break;
		default:
			if (c < '\033')
			{
				c += 'a' - 1;
				printf(" control-%c - %s\n",c,s);
			}
			else
				printf("       %c0%o - %s\n",'\\',(int) c,s);
			break;
		}
	}
}
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'pagefile.c'" '(4093 characters)'
if test -f 'pagefile.c'
then
	echo shar: will not over-write existing file "'pagefile.c'"
else
cat << \SHAR_EOF > 'pagefile.c'
/*
** vn news reader.
**
** pagefile.c - routines to deal with page display tempfile
**
** see copyright disclaimer / history in vn.c source file
*/

#include <stdio.h>

#ifdef SYSV
#include <sys/types.h>
#include <fcntl.h>
#endif

#include <sys/file.h>

#include "tune.h"
#include "node.h"
#include "page.h"

extern int Ncount,Lrec,L_allow,Cur_page,C_allow;
extern NODE **Newsorder;
extern PAGE Page;
extern int Digest;

extern char *Aformat;

extern char *T_head, *F_head, *L_head;

static int Tdes;	/* temp file descriptor */
static int Pgsize;	/* block size for seeking file */

static NODE *Curgp = NULL;	/* current newsgroup being written */
static int Order = 0;		/* order counter */

/*
	routines which deal with the temp file containing
	display pages.  Note the "invisible" file feature -
	tempfile is unlinked from /usr/tmp immediately.  when
	Tdes is closed by UNIX the disk space will be given back.
*/

temp_open ()
{
	char tmpart [L_tmpnam];
	Lrec = -1;
	tmpnam (tmpart);
	Pgsize = sizeof (HEAD) + L_allow * sizeof(BODY);
	if ((Tdes = open(tmpart,O_RDWR|O_CREAT)) < 0)
		printex ("can't open %s",tmpart);
	unlink (tmpart);
}

/*
** set newsgroup for tempfile write
*/
fw_group(ng,new,sub,rd,look)
char *ng;
int new;
int sub;
int rd;
int look;
{
	NODE *hashfind();

	if (Curgp != NULL && Page.h.artnum > 0)
		fw_flush();
	
	if ((Curgp = hashfind(ng)) == NULL)
		printex("fw_group - non-existent newsgroup, \"%s\"",ng);
	if (Curgp->order >= 0)
		printex("fw_group - repeat call on newsgroup, \"%s\"",ng);
	Curgp->order = Order;
	++Order;
	fw_chg(new,sub,rd,look);
	Curgp->pages = 0;
	Curgp->pnum = Lrec+1;
	Page.h.name = Curgp->nd_name;
	Page.h.group = Curgp;
	Page.h.artnum = 0;
}

fw_chg(new,sub,rd,look)
int new;
int sub;
int rd;
int look;
{
	Curgp->flags &= ~(FLG_NEW|FLG_SUB|FLG_SEARCH);
	if (new)
		Curgp->flags |= FLG_NEW;
	if (sub)
		Curgp->flags |= FLG_SUB;
	if (look)
		Curgp->flags |= FLG_SEARCH;
	Curgp->rdnum = Curgp->orgrd = Curgp->pgrd = rd;
}

/*
** write article to temp file.
*/
fw_art(anum,subj,lines,author)
int anum;
char *subj;
char *lines;
char *author;
{
	char tbuf[RECLEN];
	int idx;

	form_title(tbuf,subj,lines,author,anum);
	idx = Page.h.artnum;
	strcpy((Page.b)[idx].art_t, tbuf);
	(Page.b)[idx].art_id = anum;
	(Page.b)[idx].art_mark = ' ';

	++(Page.h.artnum);
	if (Page.h.artnum >= L_allow)
		fw_flush();
}

fw_done()
{
	if (Curgp != NULL && Page.h.artnum > 0)
	{
		/* correct if server was lying at fw_group() */
		Curgp->flags |= FLG_SEARCH;
		fw_flush();
	}
}

static
fw_flush()
{
	++(Curgp->pages);
	++Lrec;
	Curgp->flags |= FLG_PAGE;
	do_write();
	Page.h.artnum = 0;
}

find_page (n)
int n;
{
	long off,lseek();
	int i,last;
	Cur_page = n;
	off = Pgsize;
	off *= (long) n;
	lseek (Tdes, off, 0);
	if (read(Tdes, (char *) &(Page.h), sizeof(HEAD)) < sizeof(HEAD))
		printex("bad temp file read");
	i = Pgsize - sizeof(HEAD);
	if (read(Tdes, (char *) Page.b, i) < i)
		printex("bad temp file read");
	last = -1;
	for (i=0; i < Ncount; ++i)
	{
		if ((Newsorder[i])->pages > 0)
		{
			if ((Newsorder[i])->pnum > n)
				break;
			last = i;
		}
	}
	if (last < 0)
		printex ("can't find page %d",n);
	Page.h.group = Newsorder[last];
	Page.h.name = (Page.h.group)->nd_name;
	vns_gset(Page.h.name);
}

write_page ()
{
	long off,lseek();
	if (!Digest)
	{
		off = Pgsize;
		off *= (long) Cur_page;
		lseek (Tdes, off, 0);
		do_write();
	}
}

static do_write()
{
	int num;

	if (write(Tdes, (char *) &(Page.h), sizeof(HEAD)) < sizeof(HEAD))
		printex ("Bad temp file write");
	num = L_allow * sizeof(BODY);
	if (write(Tdes, (char *) Page.b, num) < num)
		printex ("Bad temp file write");
}

form_title (t,fn,fl,ff,n)
char *t,*fn,*fl,*ff;
int n;
{
	char *ptr,*index();
	int i;

	if ((ptr = index(ff,'(')) != NULL && strlen(ptr) > 3)
		ff = ptr;
	sprintf (t,TFORMAT,fn,fl,ff);
	sprintf(ff,Aformat,' ',' ',n);
	i = C_allow - strlen(ff) + 1;	/* remember newline in Aformat */
	t[i] = '\0';
	ctl_xlt(t);
	return (0);
}

/* replace control characters in titles */
static ctl_xlt(s)
char *s;
{
	while (*s != '\0')
	{
		if (*s < ' ')
			*s += 'A' - 1;
		++s;
	}
}
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'digest.c'" '(5687 characters)'
if test -f 'digest.c'
then
	echo shar: will not over-write existing file "'digest.c'"
else
cat << \SHAR_EOF > 'digest.c'
/*
** vn news reader.
**
** digest.c - digest unpacking routines
**
** see copyright disclaimer / history in vn.c source file
*/

#include <stdio.h>
#include "config.h"
#include "head.h"
#include "tune.h"
#include "node.h"
#include "page.h"

extern int Digest;
extern int L_allow;
extern int C_allow;
extern PAGE Page;

extern FILE *vns_aopen();

static char *Ext_pool = NULL;
static char *Show[3];

static char *Dhead = "Date: ";
static char *Fhead = "From: ";
static char *Lhead = "Lines: ";
static char *Thead = "Subject: ";

#define THDLEN 9
#define LHDLEN 7
#define FHDLEN 6
#define DHDLEN 6

digest_page (idx,skip)
int idx;
{
	char name[24];
	FILE *fp;
	int i,len,hl;
	char subj[RECLEN],date[RECLEN],from[RECLEN],junk[RECLEN],*str_store();
	long pos;
	ARTHEADER hdr;

	Digest = Page.b[idx].art_id;

	if ((fp = vns_aopen(Digest,&hdr)) == NULL)
		return (-1);

	subj[0] = date[0] = from[0] = junk[0] = '\0';

	/* skip over some articles if requested to */
	for (i=skip; i > 0; --i)
	{
		if (dig_advance(fp,from,subj,date,junk,&pos,&hl) < 0)
			return (-1);
	}

	for (i=0; i < L_allow &&
			(len = dig_advance(fp,from,subj,date,junk,&pos,&hl)) >= 0; ++i)
	{
		Page.b[i].art_id = i+1+skip;
		Page.b[i].art_mark = ' ';
		subj [C_allow] = '\0';
		from [C_allow] = '\0';
		sprintf (name,"%d",len);
		form_title (date,subj,name,from,100);
		strcpy (Page.b[i].art_t,date);
	}

	vns_aclose (fp);

	if (i == 0)
		return (-1);

	Page.h.artnum = i;
	return (i);
}

/*
	returns name of file containing "article", NULL for failure
*/
char * digest_extract (s,art,hdr,start)
char *s;
int art;
ARTHEADER *hdr;
long *start;
{
	FILE *fout,*fin;
	long pos;
	int lines,hl;
	char subj[RECLEN],date[RECLEN],from[RECLEN],bufr[RECLEN];
	char extra[RECLEN];
	char *index();
	long ftell();
	char *str_tpool(), *str_tstore();

	if (Ext_pool != NULL)
		str_tfree(Ext_pool);
	Ext_pool = str_tpool(3);

	if ((fin = vns_aopen(Digest,hdr)) == NULL)
		return (NULL);

	for ( ; art > 0; --art)
	{
		from[0] = subj[0] = date[0] = '\0';
		if ((lines = dig_advance(fin,from,subj,date,extra,&pos,&hl)) < 0)
		{
			vns_aclose(fin);
			return (NULL);
		}
	}

	tmpnam(s);

	if ((fout = fopen(s,"w")) == NULL)
	{
		vns_aclose(fin);
		unlink (s);
		return (NULL);
	}

	fseek(fin,0L,0);

	hdr->show_num = 0;
	hdr->show = Show;
	hdr->lines = lines;
	hdr->hlines = hl;
	if (subj[0] != '\0')
	{
		sprintf (bufr,"%s%s",Thead,subj);
		Show[hdr->show_num] = str_tstore(Ext_pool,bufr);
		++(hdr->show_num);
	}
	if (from[0] != '\0')
	{
		sprintf (bufr,"%s%s",Fhead,from);
		Show[hdr->show_num] = str_tstore(Ext_pool,bufr);
		++(hdr->show_num);
	}
	if (date[0] != '\0')
	{
		sprintf (bufr,"%s%s",Dhead,date);
		Show[hdr->show_num] = str_tstore(Ext_pool,bufr);
		++(hdr->show_num);
	}

	while (fgets(bufr,RECLEN-1,fin) != NULL && bufr[0] != '\n')
	{
		if (strncmp(bufr,Fhead,FHDLEN) == 0)
		{
			fprintf (fout,"%s%s\n",Fhead,from);
			continue;
		}
		if (strncmp(bufr,Thead,THDLEN) == 0)
		{
			fprintf (fout,"%s%s\n",Thead,subj);
			continue;
		}
		if (strncmp(bufr,Dhead,DHDLEN) == 0)
		{
			fprintf (fout,"%s%s\n",Dhead,date);
			continue;
		}
		/* defer line count header - it comes last */
		if (strncmp(bufr,Lhead,LHDLEN) == 0)
			continue;
		fprintf (fout,"%s",bufr);
	}

	/* toss in extra header lines, line count header, extra newline */
	fprintf (fout,"%s%s%d\n\n",extra,Lhead,lines);
	*start = ftell(fout);

	fseek (fin,pos,0);

	while (fgets(bufr,RECLEN-1,fin) != NULL && strncmp(bufr,"--------",8) != 0)
		fprintf(fout,"%s",bufr);

	vns_aclose (fin);
	fclose (fout);
	return (s);
}

dig_list (s)
char *s;
{
	char *ptr,*out,*new,ns[L_tmpnam],tmp[RECLEN],*strtok();
	ARTHEADER hdr;
	long pos;
	int i;

	prinfo ("Extracting articles .....");
	strcpy (tmp,s);
	out = s;

	for (ptr = strtok(tmp," "); ptr != NULL; ptr = strtok(NULL," "))
	{
		i = atoi(ptr);
		if ((new = digest_extract(ns,i,&hdr,&pos)) != NULL)
		{
			sprintf (out,"%s ",new);
			out += strlen(new) + 1;
		}
	}

	*out = '\0';

	if (*s == '\0')
		strcpy (s,"NULLDIGEST");
}

dig_ulist (s)
char *s;
{
	char *strtok();
	for (s = strtok(s," "); s != NULL; s = strtok(NULL," "))
		unlink (s);
}

/*
	returns # lines in article, -1 for failure
	scans past article, returns position of start.
	also returns "extra" header lines encountered, WITH newlines.
	and counts total header lines.
*/
static dig_advance (fp,from,subj,date,extra,pos,hcount)
FILE *fp;
char *from,*subj,*date,*extra;
long *pos;
int *hcount;
{
	char buf[RECLEN];
	char *ptr, *index();
	int len,state,lcount;

	*hcount = lcount = state = 0;
	*extra = '\0';

	while (fgets(buf,RECLEN-1,fp) != NULL)
	{
		buf[(len = strlen(buf) - 1)] = '\0';
		for (--len ; len >= 0 && buf[len] == ' ' || buf[len] == '\t'; --len)
			buf[len] = '\0';
		++len;

		switch(state)
		{
		case 0:
			/* skip blank lines before header */
			if (len == 0)
				break;
			state = 1;	/* fall through */
		case 1:
			++(*hcount);
			if (strncmp(buf,Fhead,FHDLEN) == 0)
			{
				strcpy (from,buf+FHDLEN);
				break;
			}
			if (strncmp(buf,Thead,THDLEN) == 0)
			{
				strcpy (subj,buf+THDLEN);
				break;
			}
			if (strncmp(buf,Dhead,DHDLEN) == 0)
			{
				strcpy (date,buf+DHDLEN);
				break;
			}
			/* put wierd header lines in extra */
			if ((ptr = index(buf,':')) != NULL)
			{
				*ptr = '\0';
				if (index(buf, ' ') == NULL)
				{
					*ptr = ':';
					sprintf(extra,"%s\n",buf);
					extra += strlen(extra);
					break;
				}
				*ptr = ':';
			}
			state = 2;
			--(*hcount);

			/* remember the newline we lopped off */
			*pos = ftell(fp)-strlen(buf)-1;	/* fall through */
		case 2:
			++lcount;
			if (strncmp("--------",buf,8) == 0)
			{
				--lcount;
				return (lcount);
			}
			break;
		}
	}

	return (-1);
}
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'brk.h'" '(750 characters)'
if test -f 'brk.h'
then
	echo shar: will not over-write existing file "'brk.h'"
else
cat << \SHAR_EOF > 'brk.h'
/*
** vn news reader.
**
** brk.h - codes for sig_set routine
**
** see copyright disclaimer / history in vn.c source file
*/

/*
	state flags for handling breaks / values for sig_set calls.
	BRK_IN, BRK_SESS, BRK_READ and BRK_OUT are the states.  All
	but BRK_INIT are used as calls to sig_set.  BRK_RFIN indicates
	a return from BRK_READ to BRK_SESS (no jump location passed),
*/
#define BRK_INIT 0		/* initial value, indicating uncaught signals */
#define BRK_IN 1		/* in NEWSRC / article scanning phase */
#define BRK_SESS 2		/* in page interactive session */
#define BRK_READ 3		/* reading articles */
#define BRK_RFIN 4		/* finished reading, return to old mode */
#define BRK_OUT 5		/* NEWSRC updating phase */

#define BRK_PR "really quit ? "
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'head.h'" '(2530 characters)'
if test -f 'head.h'
then
	echo shar: will not over-write existing file "'head.h'"
else
cat << \SHAR_EOF > 'head.h'
/*
** vn news reader.
**
** head.h - header line structure
**
** see copyright disclaimer / history in vn.c source file
*/

/*
** How this thing works:
**
** this structure is filled in by vns_aopen when opening an article.
** lines & hlines items will be used in providing percentage read prompts
**
** show_num & show are the article information lines presented for the user
** when the "show headers" flag is turned off.
**
** from and artid are used for mail salutations, etc.
**
** The items used for mail replies, FOLLOWING the call to the mail massager
** if there is one, are mailcmd, mail_num, and mail.  These are the items
** the massager should fill in.  If no massager exists, vns_aopen will
** fill these in directly.  If mail_err is non-null, the user won't be
** able to mail a reply to the article, and the item should be an error
** message explaining why.  If there is a mailer function, the mailcmd
** item is not used.
**
** The priv and priv_num items are for sole use of the server layer in
** the mail massager, mailer and poster functions.
**
** The postcmd, post_num, and post arguments are used in treatment of
** followups.  If post_err is non-null, followup won't be allowed, for
** the reason described therein.  If there is a poster function, the
** postcmd item isn't used.
**
** The header lines for inclusion in mail / followup files will open
** the file, and will be followed by one blank line.  The lines are literal -
** all appropriate headers should be prepended, etc.
**
** postcmd / mailcmd are used as format strings which are assumed to have a
** single %s in them some place for the placement of the users editted file.
** The result will be spawned as a command.
*/

typedef struct
{
	int lines;		/* number of lines in article */
	int hlines;		/* number of header lines in article */
	char *from;		/* authors name */
	char *artid;		/* article id */
	int show_num;		/* number of extra lines for reader display */
	char **show;		/* extra header lines */
	int priv_num;		/* number of private arguments */
	char **priv;		/* private server arguments */
	char *mail_err;		/* mail reply error message */
	char *mailcmd;		/* command line for mailer */
	int mail_num;		/* number of header lines in mail reply file */
	char **mail;		/* mail reply header lines */
	char *post_err;		/* follow-up posting error message */
	char *postcmd;		/* command line for followup poster */
	int post_num;		/* number of header lines for followup file */
	char **post;		/* followup header lines */
} ARTHEADER;
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'node.h'" '(1580 characters)'
if test -f 'node.h'
then
	echo shar: will not over-write existing file "'node.h'"
else
cat << \SHAR_EOF > 'node.h'
/*
** vn news reader.
**
** node.h - NODE structure
**
** see copyright disclaimer / history in vn.c source file
*/

/* newsgroup status flags */
#define FLG_SUB 1	/* user subscribed to newsgroup */
#define FLG_PAGE 2	/* a page exists to display */
#define FLG_NEW 4	/* new newsgroup */
#define FLG_ECHG 8	/* edit change by user */
#define FLG_SEARCH 16	/* newsgroup was searched */
#define FLG_ACC 32	/* newsgroup had articles accessed */
#define FLG_STAT 64	/* stat's written */

/*
	newsgroup information (hash table node)

	items unaccessed by server interface:
		next - hashtable link
		pnum - page number
		pages - number of pages for news display
		pgshwn - pages shown mask
		pgrd - article number on highest conecutively shown page
		order - order of appearance in Newsorder array.
		orgrd - original articles read number

	may be read following hashfind by server interface, but not written:
		nd_name - name of newsgroup (key to reach node by)
			this will be a permanent copy of the name.
		highnum - high article number in group
		lownum - low article number in group

	legal for vns_write to read, but not written by server interface:
		flags - bit mask of FLG_xxxx flags.
		rdnum - articles read.

	unused by vn user interface, intended for use by server interface:
		state - state variable.  initted 0.
		data - arbitrary data pointer.  initted NULL.
*/
typedef struct _node
{
	struct _node *next;
	char *nd_name;
	int highnum, lownum;
	int pnum, pages, rdnum, orgrd, pgrd;
	unsigned long pgshwn;
	unsigned flags;
	int order;
	unsigned state;
	char *data;
} NODE;
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'page.h'" '(1152 characters)'
if test -f 'page.h'
then
	echo shar: will not over-write existing file "'page.h'"
else
cat << \SHAR_EOF > 'page.h'
/*
** vn news reader.
**
** page.h - display page structure
**
** see copyright disclaimer / history in vn.c source file
*/

/*
	page display format and dependent parameters
*/
#define HFORMAT "\n%s (page %d of %d):"
#define DHFORMAT "\n%s (DIGEST EXTRACTION):"
#define TFORMAT "%s ~ %s %s"
#define AFORMAT "\n%c%c%d) "	/* begin with newline - see show routine */
#define AFLEN 5		/* min. char. in article id - depends on AFORMAT */
#define CFORMAT "page %d of %d (%d shown), newsgroup %d of %d"
#define RECBIAS 2	/* lines before articles - depends on HFORMAT */
#define WRCOL 1		/* column of written mark.  depends on AFORMAT */
#define INFOLINE 0	/* HFORMAT TFORMAT leaves for use */
#define REQLINES 7	/* required terminal lines for display, incl. help */

/*
	newsgroup information for page display
	name - of group
	group - pointer to table entry
	artnum - number of articles
*/
typedef struct
{
	char *name;
	NODE *group;
	int artnum;
} HEAD;

/*
	article information - id (spool) number, title string, mark, written.
*/
typedef struct
{
	int art_id;
	char art_mark;
	char art_t[MAX_C-AFLEN];
} BODY;

typedef struct
{
	HEAD h;
	BODY *b;
} PAGE;
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'reader.h'" '(1537 characters)'
if test -f 'reader.h'
then
	echo shar: will not over-write existing file "'reader.h'"
else
cat << \SHAR_EOF > 'reader.h'
/*
** vn news reader.
**
** reader.h - article reading interface definitions
**
** see copyright disclaimer / history in vn.c source file
*/

#define PAGE_MID ":more (%2d%%):"
#define PAGE_NEXT ":next article:"
#define PAGE_END ":end:"
#define PAGE_NO ":?:"
#define PPR_MAX 18	/* maximum length of PAGE prompts */

/*
	reading commands: no control chars, add help message to helppg
	SAVE, PRINT, HEADTOG and SETROT are also recognized
*/
#define HPG_HEAD "toggle header print flag"
#define HPG_ROT "toggle rotation"
#define HPG_SAVE "save article in a file"
#define HPG_PRINT "print article"
#define PG_NEXT 'n'
#define HPG_NEXT "next article, if any"
#define PG_QUIT 'q'
#define HPG_QUIT "quit reading articles, if any more to read"
#define PG_FLIP 'Q'
#define HPG_FLIP "quit reading, and turn to next page of articles"
#define PG_FOLLOW 'f'
#define HPG_FOLLOW "post followup to article"
#define PG_REPLY 'm'
#define HPG_REPLY "send mail to author of article"
#define PG_HELP '?'
#define HPG_HELP "see this help menu"
#define PG_REWIND 'r'
#define HPG_REWIND "rewind article to beginning"
#define PG_WIND 'e'
#define HPG_WIND "seek to end of article (to next/end prompt)"
#define PG_STEP '\n'
#define HPG_STEP "next line"
#define PG_SEARCH '/'
#define HPG_SEARCH "search for regular expression in remainder of article"
#define SEARCHFORM "search pattern (%s) ? "
#define HPG_DEF "\n anything else to continue normal reading"
#define HPG_EDEF "\n anything else to try reading next article, if any"

#define ANFORM ":%s - %c for help:\n"
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'server.h'" '(151 characters)'
if test -f 'server.h'
then
	echo shar: will not over-write existing file "'server.h'"
else
cat << \SHAR_EOF > 'server.h'

/*
**	header files shared between vn and vns_xxxx server interface routines
*/
#include "tune.h"
#include "tty.h"
#include "node.h"
#include "head.h"
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'tty.h'" '(404 characters)'
if test -f 'tty.h'
then
	echo shar: will not over-write existing file "'tty.h'"
else
cat << \SHAR_EOF > 'tty.h'
/*
** vn news reader.
**
** tty.h - codes for tty_set and term_set
**
** see copyright disclaimer / history in vn.c source file
*/

#define MOVE 100
#define ERASE 101
#define START 102
#define STOP 103
#define RUBSEQ 104
#define ZAP 105
#define ONREVERSE 106
#define OFFREVERSE 107
#define RESTART 108

#define RAWMODE 200
#define COOKED 201
#define SAVEMODE 202
#define RESTORE 203
#define BACKSTOP 204
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'tune.h'" '(2198 characters)'
if test -f 'tune.h'
then
	echo shar: will not over-write existing file "'tune.h'"
else
cat << \SHAR_EOF > 'tune.h'
/*
** vn news reader.
**
** tune.h - system tuning parameters
**
** see copyright disclaimer / history in vn.c source file
*/

/*
**	buffer size needed for tmpnam()
*/
#ifndef L_tmpnam
#define L_tmpnam 48
#endif

/*
** hash table size.  linked list type of table which can expand to
** arbitrary density, including densities > 100%.  Number of entries
** will be number of newsgroups in active list.  This should be a prime
** number ("long division" of string modulo table size hash function).
*/
#define HASHSIZE 809

/*
**	maximum number of columns on terminal.  If made smaller, there
**	will be a savings in the size of the temporary file used
**	for holding displays, at the penalty of not being able to use
**	the entire screen width on terminals actually possessing more
**	columns than this.  A block roughly on the order of this value
**	times the number of lines the terminal has is maintained per page in
**	the temp file, and read / written as displays are interacted
**	with.  MIN_C put here because MAX_C > MIN_C.  MIN_C is the minumum
**	number of columns for which a "reasonable" display can be produced.
**	before making it smaller, look at all uses of C_allow and variable
**	to see that a setting that small won't screw up array bounds.
*/
#define MAX_C 132
#define MIN_C 36

/*
**	large size for general purpose local buffers.  only used in automatic
**	variable declarations.  Used with fgets for buffer size when reading
**	file records, to hold pathnames, commands, etc.  Reduce if you blow
**	out stack storage.  If reduced too far, may eventually show up
**	as syntax errors on interacting with vns_ routines, or command line
**	botches.
*/
#define RECLEN 1200

/* block sizes for allocation routines */
#define STRBLKSIZE 1800	/* string storage allocation block */
#define NDBLKSIZE 50	/* NODE structures to allocate at a time */

/*
** maximum number of articles to allow for processing in a single user
** list.  Used only to declare an array of pointers on the stack, so it
** can be fair sized without much problem.  In practicality, there is
** no use for it being larger than the greatest line length available
** on the CRT's being used.
*/
#define MAXARTLIST 200
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'vn.h'" '(1654 characters)'
if test -f 'vn.h'
then
	echo shar: will not over-write existing file "'vn.h'"
else
cat << \SHAR_EOF > 'vn.h'
/*
** vn news reader.
**
** vn.h - general parameters
**
** see copyright disclaimer / history in vn.c source file
*/

#define TRUE 1
#define FALSE 0

#define ED_MARK '>'
#define ART_MARK '*'
#define ART_WRITTEN '_'
#define ART_UNWRITTEN ' '


#define ANFORM ":%s - %c for help:\n"
#define ANFLINES 1
#define UDKFORM "undefined key - %c for help"
#define HELPFORM "%c for help"

/*
	command characters - don't use numerics or <ESC>
	ALTSAVE is a hack to avoid having to use ctl-s - XON/XOFF.
	Wanted to preserve "s" pneumonic and lower / control /cap
	convention.
*/
#define DIGEST 'd'
#define UP 'k'
#define DOWN 'j'
#define FORWARD '\012'
#define BACK '\010'
#define READ 'r'
#define ALTREAD ' '
#define READALL 'R'
#define READSTRING '\022'
#define SAVE 's'
#define SAVEALL 'S'
#define SAVESTRING '\023'
#define ALTSAVE '\024'
#define PRINT 'p'
#define PRINTALL 'P'
#define PRINTSTRING '\020'
#define MARK 'x'
#define UNMARK 'X'
#define REDRAW '\014'
#define QUIT 'q'
#define SSTAT '#'
#define GRPLIST '%'
#define ORGGRP 'o'
#define ORGSTAT 'O'
#define UPDATE 'w'
#define UNSUBSCRIBE 'u'
#define UPALL 'W'
#define UPSEEN '\027'
#define UNESC '!'
#define NEWGROUP 'n'
#define HEADTOG 'h'
#define SETROT 'z'
#define HELP '?'
#define TOPMOVE 'H'
#define BOTMOVE 'L'
#define ALTBOTTOM 'G'
#define MIDMOVE 'M'
#define PRTVERSION '"'
#define HELP_HEAD "[...] = effect of optional number preceding command\n\
pipes are specified by filenames beginning with |\n\
articles specified as a list of numbers, title search string, or\n\
	* to specify marked articles.  ! may be used to negate any\n"

#define HHLINES 5	/* lines (CRs + 1) contained in HELP_HEAD */
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0
-- 
For comp.sources.unix stuff, mail to sources at uunet.uu.net.



More information about the Comp.sources.unix mailing list