[comp.sources.unix] v14i056: VN NNTP conversion kit

rsalz@bbn.com (Rich Salz) (04/21/88)

Submitted-by: Bob Mcqueer <rtech!bobm>
Posting-number: Volume 14, Issue 56
Archive-name: vn.nntp.pch

[  VN is a newsreader for netnews.  The latest version appeared here a
   few months ago.  This distribution allows VN to work with NNTP.
   There's also a hack to feed from different servers.  --r$ ]


This contains replacements for 3 files:

	std.c config_std.h and Makefile.

I'm not doing it as patches because the context diffs for std.c would be
much larger than the file in the first place, and the other 2 are small.

This source also allows building of a non-NNTP version, and any future
patches on std.c, etc. should be against these sources.

Read new file nntp.doc for details.

#! /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:
#	nntp.doc
#	Makefile
#	config_std.h
#	std.c
export PATH; PATH=/bin:$PATH
echo shar: extracting "'nntp.doc'" '(3844 characters)'
if test -f 'nntp.doc'
then
	echo shar: will not over-write existing file "'nntp.doc'"
else
cat << \SHAR_EOF > 'nntp.doc'
Notes concerning NNTP changes:

The NNTP changes only effect the vns_* routines, which are all defined
in std.c.  Rather than provide a different set, std.c was ifdef'ed for
NNTP, since a significant amount of code is shared with the non-NNTP
version.

Affected files: std.c config_std.h Makefile

The response_codes.h header file from NNTP distribution is needed, and
the server_init(), get_server(), put_server() and close_server()
routines (clientlib.o).

The NNTP changes reflect something done at rtech - we have multiple
news installations going on different machines.  Basically, local
newsgroups were setup on a different machine than our USENET gateway,
and one has to talk to the rtech newsserver to get USENET, or one of
2 other machines to get different sets of local newsgroups.  The way
the support folks here set up rrn appears to have been to make
differently configured executables, then cover with a shell script
that invokes the right one for the machine you wanted to go to.  You
wind up with a separate .newsrc for each machine, using a local naming
convention.

I wanted to make vn able to handle this in one executable, so I
introduced some command line flags aimed at specifying .newsrc file
and machine to talk to.  All of these flags begin with "+", distinguishing
them from the normal options.  The "+" flags are exclusively
command line, and can not be set in the .newsrc:

	+n <file>	specify .newsrc file.  If not beginning with /,
			taken with respect to HOME.  If this isn't
			specified, the normal NEWSRC variable / default
			logic applies.

	+m <machine>	specify machine to talk to.  There will be a site-
			determined default if this isn't specified, which may
			be overridden by setting a VNMACHINE variable.

	+l		rather than using NNTP, read the news locally.

	+t <file>	trace the NNTP server session in <file> for debugging.

If compiled for non-NNTP, only the '+n' flag applies.  Given these flags,
people can invoke vn as they see fit, set aliases for how they want to talk
to different machines, etc.

The machine name enters into interaction with the NNTP server only as the
argument to server_init().

The people setting up rrn here also made different inews executables for
posting to installations on different machines, again with a local naming
convention.   To handle this, the posting command may have a %s embedded
in it, which will get substituted with the machine name before being
spawned.  This way, it can either be a command flag, or a path to an
alternate executable, as in our case.  User POSTER variables will also
have to be aware of that fact, if appropriate.

Also note the KEEP_UNK definition in .newsrc - another hack for this
multiple installation stuff.

Differences between the distributed config_std.h, and what is run here,
apart from DEF_MACHINE, of course:

	DEF_POSTER points to a non-standard location for inews, with a %s
	in it for machine name substitution.

	SHOW_FROM is defined.

	KEEP_UNK is defined.

What I compile for a non-NNTP version (it runs that way on the rtech
gateway (ULTRIX), which is hardly anybody's "home" machine) is completely
standard.

Our local NNTP versions are running on Pyramid, CCI and Sun.  It has also
been built on a Convergent Technologies machine, but NNTP seems to have
some problems in that environment - after communicating to the server for
a while you get a "broken pipe".  Since it affects rrn on that machine
also, it's obviously not vn's problem.  It's just that rrn at least gets
part way through a session before dying, and vn invariably dies while
trying to read article headers at start up.  And it's only talking to
the ULTRIX microvax, not talking to the CCI machine which is where our
local newsgroups live.  If NFS or NNTP guru out there has any bright
ideas I'll pass them on to the guy who's looking at this.
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'Makefile'" '(3869 characters)'
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
# CFLAGS:
#	set -DJOBCONTROL if you have job control (BSD).
#	set -DSYSV -Dindex=strchr -Drindex=strrchr on Sytem V systems
#	set -Dregfree=free if you DON'T include reg.o (SYSV + some BSD)
#
# JOBCONTROL could be done as #ifndef SYSV, I suppose, but this clearly
# marks that particular difference.
#
# LIBS:
# should point to any extra libraries needed, such as termcap.  You
# may want to change this to use the curses termcap cover.  If you need
# to pull in another library to get regex / regcmp or strtok on non-SYSV
# systems, you may want to put that here.  I understand that -lPW is now
# required on many SYSV systems to pick up regex/regcmp, hence that is
# reflected here.
#
# EXTRAOBJS:
# may be used to include tmpnam.o, strtok.o, reg.o in the list.
#
# These objects implement SYSV utilities for BSD machines.
#
# strtok.o implements strtok() / strpbrk().  reg.o implements regex()/regcmp()
# on top of the BSD regular expression library (regex() allows multiple
# regular expressions).  tmpnam.o implements tmpnam() of course.
#
# If you have them, use your own regex/regcmp, because:
#
#	i) They should be faster than the reg.c code, which
#	recompiles the "current" ucb string every time you
#	switch regular expressions.
#
#	ii) I'm using reg.c myself on a couple systems, and it seems to
#	work.  But implementing one set of system calls on top of a
#	different set is always a bit suspect, and why add code when
#	you don't need to.
#
# if you DON'T include reg.o, be sure you set -Dregfree=free in CFLAGS.
#
# As with regex, if you have a system strtok(), it is likely more efficient -
# string routines will often be done in assembler on a given machine.
#
# Even if you have it, you can use tmpnam.o anyhow.  This version will tailor
# the temp file name to vnXXXXXX, instead of a generic tmpXXXXXX.
#
# "vanilla" BSD.  This has been used locally on Pyramids / CCI / Sun.
#LIBS = -ltermcap
#EXTRAOBJS = tmpnam.o strtok.o reg.o
#CFLAGS = -O -DJOBCONTROL
#
# "vanilla" SYSV:
#LIBS = -ltermcap -lPW
#EXTRAOBJS = tmpnam.o
#CFLAGS = -O -DSYSV -Dregfree=free -Dindex=strchr -Drindex=strrchr
#
# BSD with strtok() / regex(), such as ULTRIX.  These are the rules
# used for our non-NNTP installation (rtech is a microvax running ULTRIX):
#LIBS = -ltermcap
#EXTRAOBJS = tmpnam.o
#CFLAGS = -O -DJOBCONTROL -Dregfree=free

# SERVEROBJS defines the object(s) for the vns_xxxx "server" interface.
#
#	std.o is the version for "standard" news, making use of the
#	users .newsrc file, and  resident articles / active file.
#	If you compile std.c with NNTP defined you get the version
#	of vn which will talk to an NNTP server.  You will also have
#	to define where to pick up the response_codes.h header file,
#	and where the server library is (server_init, etc.).  It is also
#	99.9% certain that you will want to change DEF_MACHINE in
#	config_std.h.  For this reason, config_std.h has the definition
#	commented out, which will cause a syntax error until you fix it.
#	See the special NNTP version rule for std.o following the vn rule
#
SERVEROBJS = std.o
SERVERLIBS = 

# normal vn objects
#
VNOBJS=	hash.o envir_set.o pagefile.o reader.o storage.o sig_set.o term_set.o tty_set.o userlist.o vn.o vnglob.o digest.o strings.o session.o printex.o getch.o help.o newdisp.o stat.o svart.o

# This is to force you to read the makefile.  Once you have, comment this rule,
# and uncomment the "real" rule.  At the minimum, you will also have to
# uncomment one of the three sets of LIBS / EXTRAOBJS & CFLAGS definitions
# above.  For the NNTP version you will also have to uncomment and modify
# the std.o special rule, and pick up server_init, etc. from somewhere.
#
vn:
	@echo "PLEASE READ THE MAKEFILE"
#vn:	$(VNOBJS) $(EXTRAOBJS) $(SERVEROBJS)
#	cc -o vn $(VNOBJS) $(EXTRAOBJS) $(SERVEROBJS) $(SERVERLIBS) $(LIBS)
#
#std.o	:
#	cc -c $(CFLAGS) -DNNTP -I. std.c
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'config_std.h'" '(5941 characters)'
if test -f 'config_std.h'
then
	echo shar: will not over-write existing file "'config_std.h'"
else
cat << \SHAR_EOF > 'config_std.h'
/*
** default news poster
** If talking to an NNTP server, and there is a %s in this somewhere,
** the machine will be substituted at runtime
**
** LOC_POST only applies for NNTP.  It is the poster used in conjunction
** with the +l option or NULL for DEF_MACHINE, ie. when you are reading
** the local files directly instead of through NNTP.
*/
#define DEF_POST "/usr/lib/news/inews -h"
#define LOC_POST "/usr/lib/news/inews -h"

/*
** DEF_MACHINE
**
** this will affect only NNTP versions, and will have to differ from
** site to site.  shipped commented out so that you will be forced to set it
**
** NULL or empty string will result in direct reading of the local files
** rather than NNTP.
#define DEF_MACHINE "rtech"
*/

/*
** SHOW_FROM applies only to NNTP.  If defined, a FROM line will be
** placed in the user's file when posting followups.  FROM_CR_FORM
** is the format used for the From line, with arguments corresponding
** to the "From: " header, the user name, the machine name and the user's
** "gecos" string, in that order.
**
** This is distributed turned off, since the feature may encourage
** userid forgery.  If turned off, vn will prepend the user's article
** with the FROM line instead, effectively preventing the user from
** specifying one.  In the non-NNTP case, inews runs as the user,
** and constructs a FROM line if the user didn't
**
** If SHOW_FROM is not defined, REPLY_CR_FORM may be defined, which shows
** the user a "Reply-to" line to edit into a more appropriate mail reply path.
** If defined, REPLY_CR_FORM is the format used for the line with arguments
** corresponding to the "Reply-to: " header, the user name, and the machine
** name, in that order.
**
** An important point is that since the _FORM strings have the header as one
** of their arguments, they had better begin with "%s" to work right.
**
** The machine placed into these lines is the HOST machine, usually.
** If FROM_REAL is defined, the actual machine the user is logged into
** is used instead.  Using the host works well locally, since our news
** gateway also functions as a mail gateway.  If you ALSO define MACH_CONCAT,
** the machine placed into the from line will be <host>!<real>.  Using
** either of these alternatives may lead to problems since you are then
** displaying your internal machine names to the outside world, and the
** names are very likely conflict with some net site or another.
#define FROM_REAL
#define MACH_CONCAT
#define SHOW_FROM
#define REPLY_CR_FORM "%s%s@%s.UUCP"
*/
#define FROM_CR_FORM "%s%s@%s.UUCP (%s)"

/*
** default user .newsrc file
*/
#define DEF_NEWSRC ".newsrc"

/*
** If INLETTER is defined, the address line will be placed into the
** file editted by the user, and the mailer is assumed smart enough
** to understand about header lines in the file.  Otherwise the
** address is part of the mailer's command line.
**
** if MAILSMART is defined, The From: line will be used for mail replies,
** or overridden by a "Reply-to:" line if present - "Path:" will be used
** as a last resort.  If MAILSMART is not defined, "Path:" will simply be
** used.
**
** if MAILCHOOSE is defined, the user is prompted before edit with all
** of the address lines to choose from, or to input a new one.  MAILCHOOSE
** makes MAILSMART irrelevant, but the two are independent of INLETTER.
**
#define MAILCHOOSE
*/
#define MAILSMART
#define INLETTER

/*
** default mail sender.  If INLETTER, will be done as
** cat <file> | DEF_MAIL,  Otherwise, cat <file> | DEF_MAIL <address>
** user's MAILER variable will have to conform, too.
*/
#ifdef INLETTER
#define DEF_MAIL "/usr/lib/sendmail -t"
#else
#define DEF_MAIL "/bin/mail"
#endif

/*
** OLDRC defined for an apparently earlier news version which took unnamed
** command line options as synonyms for -n, and did not take ranges in
** the .newsrc file.  Probably useless, but kept in for historical reasons.
**
**#define OLDRC
*/

/*
** article spool directory
*/
#define SPOOLDIR "/usr/spool/news"

/*
** active file
*/
#define ACTFILE "/usr/lib/news/active"

/*
** maximum number of option lines in .newsrc
*/
#define OPTLINES 60

/*
** maximum number of filter options
*/
#define NUMFILTER 30

/*
** maximum number of file lines to search looking for header lines.
*/
#define HDR_LINES 36

/*
** When a newsgroup is scanned, we ignore articles less than <high spool> -
** MAXARTRANGE.  This is intended to prevent ridiculous numbers of article
** opening attempts the first time a user reads a new newsgroup which has a
** huge difference between the high and low spool numbers, perhaps due to
** some articles not getting expired.
*/
#define MAXARTRANGE 1600	/* about 2 weeks of soc.singles */

/*
** If we detect that the user has a higher number in .newsrc than the
** high article number, obviously the active file is out of synch with the
** .newsrc.  We set the user's number back to the low article number in
** this case, on the theory that it's better to repeat stuff than miss
** articles.  On such setbacks, we won't backdate the user by more than
** SYN_SETBACK articles, preventing floods of articles on large newsgroups
** if you don't define SYN_CHECK, the user's number won't be adjusted in
** this case, choosing to lose articles rather than show old ones.
*/
#define SYN_CHECK
#define SYN_SETBACK 60

/*
** Normally, unrecognized newsgroup names are silently removed from the
** user's .newsrc
**
** if KEEP_UNK is defined, unknown newsgroups will be left alone in
** the users .newsrc.  This is intended to allow the same .newsrc
** to be used against servers on different NNTP installations.  Of course,
** it works only if newsgroup names are unique across installations.
** Essentially, this is a hack for our local situation, which may come
** in handy for somebody else.  If this is defined, users will have to
** edit out bogus newsgroup names in their own .newsrc's if they wish
** to keep it accurate.
#define KEEP_UNK
*/
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'std.c'" '(31821 characters)'
if test -f 'std.c'
then
	echo shar: will not over-write existing file "'std.c'"
else
cat << \SHAR_EOF > 'std.c'
#include <stdio.h>
#include <pwd.h>
#include <ctype.h>
#include <sys/param.h>
#ifdef NNTP
#include <response_codes.h>
#endif
#include "server.h"
#include "config_std.h"
#include "std.h"

#ifndef MAXPATHLEN
#define MAXPATHLEN 240
#endif

extern NODE *hashfind();
extern FILE *fopen();
extern char *index(), *rindex();
extern char *malloc();
extern char *str_tstore(), *str_tpool(), *str_store();
extern char *strtok(), *strpbrk();
extern char *regex(), *regcmp();

#ifdef MAILCHOOSE
extern int (*Massage)();
#endif

/*
	global flags signifying options set
*/
#define GF_ALL 1	/* -x option - scan everything */
#define GF_SPEC 2	/* -n option(s) - user specified groups */
#define GF_OVER 4	/* command line specification - overide marks */

#ifdef NNTP
char *nntp_get();
static char *Machine;
static char *Fmach;
char *Vns_version = "nntp1.2";
static FILE *Fp_trace = NULL;
extern int (*Postfunc)();
static int Server_open = 0;
#else
char *Vns_version = "res1.2";
#endif

static char *Home;
static char *Username, *Userdesc;
static char *Onews, *Newsrc;
static int Ntopt, Nntopt, Nwopt, Nnwopt;

static char *Wopt[NUMFILTER];		/* regular expressions for -w options */
static char *Topt[NUMFILTER];		/* for -t options */
static char *Negwopt[NUMFILTER];	/* for negated -w options */
static char *Negtopt[NUMFILTER];	/* for negated -t options */

static char *Options[OPTLINES];
static int Max_name, Optlines;
static unsigned Gflags = 0;
static char **Active;
static int Actnum;
static char *Mailer, *Poster;

static char *RT_head = RTHEAD;
static char *P_head = PHEAD;
static char *M_head = MHEAD;
static char *R_head = RHEAD;
static char *TO_head = TOHEAD;
static char *F_head = FHEAD;
static char *FT_head = FTHEAD;
static char *T_head = THEAD;
static char *DIS_head = DISHEAD;
static char *L_head = LHEAD;
static char *N_head = NHEAD;

static char *Fpfix = FPFIX;

/*
**	environment setup.
*/
vns_envir()
{
 	char dbuf[MAXPATHLEN], *rcname;
	char *vn_env();
	struct passwd *ptr, *getpwuid();
	char *machine;
#ifdef NNTP
#ifndef SHOW_FROM
	int nntp_post();
#endif
#endif
#ifdef MAILCHOOSE
	int mail_prompt();

	Massage = mail_prompt;
#endif

	ptr = getpwuid (getuid());
	Home = str_store(ptr->pw_dir);
	Username = str_store(ptr->pw_name);
	Userdesc = str_store(ptr->pw_gecos);

#ifdef NNTP
	Machine = vn_env("VNMACHINE",DEF_MACHINE);
#ifndef SHOW_FROM
	Postfunc = nntp_post;
#endif
#endif

	rcname = vn_env("MAILER",DEF_MAIL);
#ifdef INLETTER
	sprintf(dbuf,"cat %%s | %s",rcname);
#else
	/* used as a format string TWICE (%%%% -> %% -> %) */
	sprintf(dbuf,"cat %%%%s | %s %%s",rcname);
#endif
	Mailer = str_store(dbuf);

	rcname = vn_env("NEWSRC",DEF_NEWSRC);
	if (*rcname != '/')
	{
		sprintf (dbuf, "%s/%s",Home,rcname);
		Newsrc = str_store (dbuf);
	}
	else
		Newsrc = str_store (rcname);
}

/*
	change directory to group
*/
vns_gset(grp)
char *grp;
{
	char dbuf [RECLEN];

#ifdef NNTP
	if (Machine != NULL)
	{
		sprintf(dbuf,"group %s",grp);
		if (nntp_put(dbuf,dbuf) < 0)
			printex("can't find newsgroup (server: %s)",dbuf);
		return;
	}
#endif
	g_dir (grp,dbuf);
	if (chdir(dbuf) < 0)
		printex("can't change to newsgroup directory");
}

/*
	g_dir converts newsgroup name to directory string
*/
static
g_dir(s,t)
char *s,*t;
{
	char *ptr;
	sprintf (t,"%s/%s",SPOOLDIR,s);
	for (ptr=t+strlen(SPOOLDIR)+1; (ptr = index(ptr,'.')) != NULL; *ptr = '/')
		;
}

/*
** myfind is used for hashfind() calls which aren't supposed to fail.
*/
static NODE *
myfind(name)
char *name;
{
	NODE *n;

	n = hashfind(name);
	if (n == NULL)
		printex("Unexpected table lookup failure");
	return (n);
}

vns_news(argc,argv,lfirst,nun)
int argc;
char **argv;
int *lfirst, *nun;
{
	FILE *fp;
	static char marks[] =
	{ 
		NEWS_ON, NEWS_OFF, '\0' 
	};
	int line, len, num;
	char buf [RECLEN], trail, submark, *fret, *ptr;
	int optpflag;

	++argv;
	--argc;

	/*
	** get .newsrc / server connection affecting options before 
	** calling fill_active.
	*/
	arg_opt(argc,argv);

	/*
	** fill table with active newsgroups - initiates server connection
	** if we're talking to NNTP.
	*/
	fill_active ();

	/*
	** handle options, now that fill_active has been called.
	** Set GF_OVER before calling newsrc_opt, since -S may override it.
	*/
	if (Optlines > 0)
	{
		optpflag = 1;
		Gflags |= GF_OVER;
		newsrc_opt(lfirst,nun);
	}
	else
		optpflag = 0;

	if ((fp = fopen (Newsrc,"r")) == NULL)
		printex ("can't open %s for reading",Newsrc);

	Optlines = 0;

	for (line = 1; (fret = fgets(buf,RECLEN-1,fp)) != NULL && emptyline(buf) == 1; ++line)
		;
	if (fret != NULL && strncmp (buf,"options",7) == 0)
	{
		Options[0] = str_store(buf);
		Optlines = 1;
		trail = buf [strlen(buf)-2];
		for ( ; (fret = fgets(buf,RECLEN-1,fp)) != NULL; ++line)
		{
			if (trail != '\\' && buf[0] != ' ' && buf[0] != '\t')
				break;
			if (Optlines >= OPTLINES)
				printex ("%s - too many option lines (%d allowed)",Newsrc,OPTLINES);
			Options[Optlines] = str_store(buf);
			++Optlines;
			if ((len = strlen(buf)) >= 2 && buf[len-2] != '\\')
				trail = buf[len-2];
			else
				trail = '\0';
		}
	}

	/* do the options from the newsrc file if there weren't command line args */
	if (Optlines > 0 && ! optpflag )
		newsrc_opt (lfirst,nun);

	for ( ; fret != NULL; ++line, fret = fgets(buf,RECLEN-1,fp))
	{
		if (emptyline(buf) == 1)
			continue;
		if ((ptr = strpbrk(buf,marks)) == NULL)
		{
			fprintf (stderr,"\nwarning: line %d of %s (%s) - bad syntax\n",
			line,Newsrc,buf);
			continue;
		}
		submark = *ptr;
		*ptr = '\0';
		++ptr;
		num = 0;
		for (ptr = strtok(ptr," ,-\n"); ptr != NULL; ptr = strtok(NULL," ,-\n"))
		{
			len = atoi (ptr);
			for ( ; *ptr >= '0' && *ptr <= '9'; ++ptr)
				;
			if (*ptr != '\0' || len < num)
			{
				num = -1;
				fprintf (stderr,"\nwarning: line %d of %s (%s) - bad syntax\n",
				line,Newsrc,buf);
				break;
			}
			num = len;
		}
		if (num < 0)
			continue;
		chkgroup (buf,submark,num,0);
	}
	fclose (fp);

	/* now take care of groups not specified in .newsrc */
	art_active();

	/* free up the option string storage */
	for (num=0; num < Ntopt; ++num)
		regfree (Topt[num]);
	for (num=0; num < Nwopt; ++num)
		regfree (Wopt[num]);
	for (num=0; num < Nntopt; ++num)
		regfree (Negtopt[num]);
	for (num=0; num < Nnwopt; ++num)
		regfree (Negwopt[num]);
	Ntopt = Nwopt = Nntopt = Nnwopt = 0;

	/* free the active list */
	free ((char *) Active);
}

static
emptyline(s)
char *s;
{
	while (isspace(*s))
		++s;
	if (*s == '\0')
		return (1);
	return (0);
}

/*
	fill hash table from active news group list
	This is needed to be able to process options
	before scanning user order.  Constructs an array
	of active newsgroup names for the rest of vns_nws().
*/
static
fill_active ()
{
	FILE *f;
	char *nread, act_rec[RECLEN];
	int num,lownum,rcount;
	char *(*getfn)();

	Max_name = 0;

#ifdef NNTP
	if (Machine != NULL)
	{
		if (server_init(Machine) < 0)
		{
			printex ("Cannot connect to NNTP server on %s",Machine);
		}
		Server_open = 1;
		getfn = nntp_get;
		if (nntp_put("list",act_rec) < 0)
			printex ("Couldn't get newsgroup list (server: %s)",act_rec);
	}
	else
	{
		getfn = fgets;
		if ((f = fopen (ACTFILE,"r")) == NULL)
			printex ("couldn't open %s\n",ACTFILE);
	}
#else
	getfn = fgets;
	if ((f = fopen (ACTFILE,"r")) == NULL)
		printex ("couldn't open %s\n",ACTFILE);
#endif

	/*
	** we do things this way so that we only examine active records
	** once, minimizing the window where changes could screw us up
	** at the cost of possibly alloc'ing a few extra bytes.  We start
	** with a count of one to have a positive rcount for alloc.
	*/
	for(rcount=1; (*getfn)(act_rec, RECLEN-1, f) != NULL; ++rcount)
		;
	if ((Active = (char **) malloc(rcount*sizeof(char *))) == NULL)
		printex("Memory allocation failure");

#ifdef NNTP
	if (Machine != NULL)
	{
		if (nntp_put("list",act_rec) < 0)
			printex ("Couldn't re-get newsgroup list (server: %s)",act_rec);
	}
	else
		rewind(f);
#else
	rewind(f);
#endif

	Actnum = 0;
	while (Actnum < rcount && (*getfn)(act_rec, RECLEN-1, f) != NULL)
	{
		if (strtok (act_rec," \n") == NULL)
			continue;
		nread = strtok (NULL, " \n");
		if (nread != NULL)
			num = atoi(nread);
		else
			num = 0;
		nread = strtok (NULL, " \n");
		if (nread != NULL)
			lownum = atoi(nread);
		else
			lownum = 0;
		if (lownum > 0)
			--lownum;
		if (strlen(act_rec) > Max_name)
			Max_name = strlen(act_rec);

		/* enter newsgroup, point to permanent copy of name */
		hashenter (act_rec, num, lownum);
		Active[Actnum] = (myfind(act_rec))->nd_name;
		++Actnum;
	}

#ifdef NNTP
	if (Machine == NULL)
		fclose (f);
#else
	fclose (f);
#endif
}

/*
	check active newsgroups not mentioned in NEWSRC file
	(SFLG_SCAN not set)
*/
static
art_active ()
{
	int i;
	NODE *ptr;

	for( i=0; i < Actnum; ++i)
	{
		ptr = myfind(Active[i]);
		if ((ptr->state & SFLG_SCAN) == 0)
			chkgroup (ptr->nd_name, NEWS_ON, 0, 1);
	}
}

/*
	check group for new articles:
	s - group
	c - subscription indicator from NEWSRC
	n - number read
	new - new newsgroup flag
*/
static
chkgroup (s,c,n,new)
char *s,c;
int n;
int new;
{
	NODE *ptr;
	char sub;
	int nrd;
	int lowart;
	int st;

	if ((ptr = hashfind(s)) != NULL && (ptr->state & SFLG_SCAN) == 0)
	{
		ptr->state |= SFLG_SCAN;

#ifdef SYN_CHECK
		/* if "read" more than exist, reset */
		if (n > ptr->highnum)
		{
			n = ptr->highnum - SYN_SETBACK;
			fgprintf("%s: .newsrc out of synch, resetting\n",s);
		}
#endif
		lowart = ptr->lownum;
		if (n < ptr->lownum)
			n = ptr->lownum;

		nrd = n;
		sub = c;

		/*
		** scan decision is rather complex, since GF_ALL setting
		** overides "n" value, GF_SPEC indicates SFLG_SPEC flag used.
		** if GF_OVER set, SFLG_SPEC overides subscription mark, else
		** SFLG_SPEC AND subscribed is neccesary.
		*/
		if ((Gflags & GF_SPEC) != 0)
		{
			if ((ptr->state & SFLG_SPEC) == 0)
				c = NEWS_OFF;
			else
			{
				if ((Gflags & GF_OVER) != 0)
					c = NEWS_ON;
			}
		}
		if ((Gflags & GF_ALL) != 0)
			n = lowart;
		fw_group(s, new, sub == NEWS_ON, nrd, c == NEWS_ON);
		if (c == NEWS_ON && ptr->highnum > n)
		{
			st = outgroup (s,n,ptr->highnum);
			if (st > nrd)
				fw_chg(new, sub == NEWS_ON, st, c == NEWS_ON);
		}
	}
#ifdef KEEP_UNK
	else
	{
		/*
		** enter unknown group into the list with no articles and
		** no scanning for statistics.  Simply a device so we'll
		** write it back out again after we're done.
		*/
		if (ptr == NULL)
		{
			hashenter (s, n, n);
			fw_group(s, 0, c == NEWS_ON, n, 0);
		}
	}
#endif
}

/*
	vns_write writes the .newsrc file
*/
vns_write(news,ncount)
NODE **news;
int ncount;
{
	FILE *fp;
	NODE *p;
	char c;
	int i,rc;

	if (link(Newsrc,Onews) < 0)
		printex ("can't backup %s to %s before writing",Newsrc,Onews);

	if (unlink(Newsrc) < 0 || (fp = fopen(Newsrc,"w")) == NULL)
		printex ("can't open %s for writing (backed up in %s)",Newsrc,Onews);
	else
	{
		clearerr(fp);
		for (i=0; (rc = ferror(fp)) == 0 && i < Optlines; ++i)
			fprintf (fp,"%s",Options[i]);
		for (i=0; rc == 0 && i < ncount; ++i)
		{
			p = news[i];
			if ((p->flags & FLG_SUB) == 0)
				c = NEWS_OFF;
			else
				c = NEWS_ON;
#ifdef OLDRC
			fprintf (fp,"%s%c %d\n",p->nd_name,c,p->rdnum);
#else
			if (p->rdnum > 0)
				fprintf(fp,"%s%c 1-%d\n",p->nd_name,c,p->rdnum);
			else
				fprintf(fp,"%s%c 0\n",p->nd_name,c);
#endif
			rc = ferror(fp);
		}
		fclose (fp);
		if (rc != 0)
			printex ("write of %s failed, old copy stored in %s",Newsrc,Onews);
		else
			unlink (Onews);
	}
}

/*
	arg_opt must be called prior to option scanning, since
	it uses the options array.  This is a bit of a kludge,
	but it saves a bunch of work.  NOTE - no command name argument

	This also processes the arguments which don't correspond to
	the .newsrc options - these arguments are all flagged with '+'
*/
static
arg_opt (argc,argv)
int argc;
char **argv;
{
	int i;
	char bufr[RECLEN];

	Optlines = 0;
	for (i=0; i < argc; ++i,++argv)
	{
		if (**argv == '+')
		{
			++(*argv);
			switch (**argv)
			{
#ifdef NNTP
			case 't':
				++(*argv);
				if (**argv == '\0')
				{
					++argv;
					++i;
				}
				Fp_trace = fopen(*argv,"w");
				break;
			case 'l':
				Machine = NULL;
				break;
			case 'm':
				++(*argv);
				if (*(Machine = *argv) == '\0')
				{
					++argv;
					++i;
					Machine = *argv;
				}
				break;
#endif
			case 'n':
				++(*argv);
				if (*(Newsrc = *argv) == '\0')
				{
					++argv;
					++i;
					Newsrc = *argv;
				}
				if (*Newsrc != '/')
				{
					sprintf(bufr,"%s/%s",Home,Newsrc);
					Newsrc = str_store(bufr);
				}
				break;
			default:
				printex("unknown option, +%c",**argv);
			}
			continue;
		}

		if (Optlines >= OPTLINES)
			printex ("too many command line options (%d allowed)\n",
							OPTLINES);
		Options[Optlines] = *argv;
		++Optlines;
	}

	/* now that we have machine name, resolve Poster command, Fmach */
#ifdef NNTP

	/* make an empty machine synonomous with NULL */
	if (Machine != NULL && *Machine == '\0')
		Machine = NULL;
	if (Machine != NULL)
	{
#ifdef FROM_REAL
		gethostname(bufr,RECLEN/2);
		Fmach = str_store(bufr);
#ifdef MACH_CONCAT
		sprintf(bufr,"%s!%s",Machine,Fmach);
		Fmach = str_store(bufr);
#endif
#else
		Fmach = Machine;
#endif
		Poster = vn_env("VNPOSTER",DEF_POST);
		sprintf(bufr,Poster,Machine);
		Poster = str_store(bufr);
	}
	else
	{
		Postfunc = NULL;
		Poster = vn_env("VNPOSTER",LOC_POST);
	}
#else
	Poster = vn_env("VNPOSTER",DEF_POST);
#endif /* NNTP */
	sprintf(bufr,"%s %%s",Poster);
	Poster = str_store(bufr);

	if (access (Newsrc,0) != 0)
		creat (Newsrc,0666);

	strcpy(bufr,Newsrc);
	strcpy(rindex(bufr,'/')+1,".vnXXXXXX");
	mktemp(bufr);
	Onews = str_store (bufr);
}

/*
	option setting routine:
	sets global flags: GF_ALL for -x option GF_SPEC for -n.
	sets up filter array for article scanning
*/
static
newsrc_opt(lfirst,nun)
int *lfirst, *nun;
{
	int i;
	char curopt,tmp[RECLEN],*tok;

	*nun = *lfirst = 0;
	Ntopt = Nwopt = Nnwopt = Nntopt = 0;
	curopt = '\0';
	for (i=0; i < Optlines; ++i)
	{
		strcpy(tmp,Options[i]);
		for (tok = strtok(tmp,",\\ \t\n"); tok != NULL; tok = strtok(NULL,",\\ \t\n"))
		{
			if (*tok != '-')
				do_opt (curopt,tok);
			else
			{
				for (++tok; index("nwt",*tok) == NULL; ++tok)
				{
					/* options with no strings */
					switch(*tok)
					{
					case 'S':
						Gflags &= ~GF_OVER;
						break;
					case '%':
						*lfirst = 1;
						break;
					case 'U':
						*nun = 1;
						break;
#ifdef OLDRC
					case 'i':
					/* Treat "-i" as synonym for "-x" */
#endif
					case 'x':
						Gflags |= GF_ALL;
					default:
						break;
					}
				}
				curopt = *tok;
				if (*(++tok) != '\0')
					do_opt (curopt,tok);
			}
		}
	}
}

/* do_opt is for options with strings attached */
static
do_opt (opt,str)
char opt, *str;
{
	switch (opt)
	{
	case 'n':
		Gflags |= GF_SPEC;
		specmark(str);
		break;
	case 'w':
		specfilter (FIL_AUTHOR,str);
		break;
	case 't':
		specfilter (FIL_TITLE,str);
		break;
	default:
#ifdef OLDRC
		Gflags |= GF_SPEC;	/* Assume anything else is newsgroup */
		specmark(str);
#endif
		break;
	}
}

static
specfilter (comp,str)
char comp,*str;
{
	int *count;
	char **rex;

	/*
	** we may set rex one past end of array.  we will error before
	** referencing it if that's the case, however.
	*/
	if (*str == '!')
	{
		if (comp == FIL_TITLE)
		{
			count = &Nntopt;
			rex = Negtopt + *count;
		}
		else
		{
			count = &Nnwopt;
			rex = Negwopt + *count;
		}
		++str;
	}
	else
	{
		if (comp == FIL_TITLE)
		{
			count = &Ntopt;
			rex = Topt + *count;
		}
		else
		{
			count = &Nwopt;
			rex = Wopt + *count;
		}
	}
	if (*count >= NUMFILTER)
		printex ("too many %c options, %d allowed",comp,NUMFILTER);
	if ((*rex = regcmp(str,(char *) 0)) == NULL)
		printex ("%c option regular expression syntax: %s",comp,str);
	++(*count);
}

/*
	handle the newsgroup specification string.
	("all" convention - braack!!!)
*/
static
specmark (s)
char *s;
{
	unsigned ormask,andmask;
	int i,len;
	char *ptr,*re,pattern[RECLEN];
	NODE *nptr;

	if (*s == '!')
	{
		++s;
		ormask = 0;
		andmask = ~SFLG_SPEC;
		if (*s == '\0')
			return;
	}
	else
	{
		ormask = SFLG_SPEC;
		andmask = 0xffff;
	}

	/* convert "all" not bounded by alphanumerics to ".*". ".all" becomes ".*" */
	for (ptr = s; (len = findall(ptr)) >= 0; ptr += len+1)
	{
		if (len > 0 && isalnum (ptr[len-1]))
			continue;
		if (isalnum (ptr[len+3]))
			continue;
		if (len > 0 && ptr[len-1] == '.')
		{
			--len;
			strcpy (ptr+len,ptr+len+1);
		}
		ptr[len] = '.';
		ptr[len+1] = '*';
		strcpy (ptr+len+2,ptr+len+3);
	}

	/* now use regular expressions */
	sprintf (pattern,"^%s$",s);
	if ((re = regcmp(pattern,(char *) 0)) == NULL)
		printex ("n option regular expression syntax: %s",s);
	for (i=0; i < Actnum; ++i)
	{
		nptr = myfind(Active[i]);
		if (regex(re,nptr->nd_name) != NULL)
		{
			nptr->state |= ormask;
			nptr->state &= andmask;
		}
	}
	regfree (re);
}

static
findall (s)
char *s;
{
	int len;
	for (len=0; *s != '\0'; ++s,++len)
	{
		if (*s == 'a' && strncmp(s,"all",3) == 0)
			return (len);
	}
	return (-1);
}

static
grp_indic (s,ok,serr)
char *s;
int ok;
char *serr;	/* only used for NNTP */
{
	if (ok)
	{
		fgprintf("    %s\n",s);
		return;
	}
#ifdef NNTP
	if (Machine != NULL)
	{
		fgprintf("    %s, server: %s\n",s,serr);
		return;
	}
#endif
	fgprintf("    %s - Can't access spool directory\n",s);
}

/*
	enter newsgroup articles.
	all articles between low and hi are to be included.

	Returns the highest number less than an OPENED (not neccesarily
	accepted) article to allow caller to revise "articles read"
	number beyond non-existent articles.
*/
outgroup (s,low,hi)
char *s;
int low,hi;
{
	int i;
	char subj[RECLEN], lines[RECLEN], auth[RECLEN], gd[RECLEN];
	int ret,op;

	ret = low;
	op = 1;

	if ((hi-low) > MAXARTRANGE)
		low = hi - MAXARTRANGE;

#ifdef NNTP
	if (Machine != NULL)
	{
		sprintf(gd,"group %s",s);
		if (nntp_put(gd,gd) < 0)
		{
			grp_indic(s,0,gd);
			return (ret);
		}
	}
	else
	{
		g_dir(s,gd);
		if (chdir(gd) < 0)
		{
			grp_indic(s,0);
			return (ret);
		}
	}
#else
	g_dir(s,gd);
	if (chdir(gd) < 0)
	{
		grp_indic(s,0);
		return (ret);
	}
#endif /* NNTP */

	grp_indic(s,1);

	for (i=low+1; i <= hi; ++i)
	{
		if (digname(i,subj,lines,auth,&op) >= 0)
		{
			fw_art(i,subj,lines,auth);
		}
		else
		{
			if (op)
				ret = i;
		}
	}

	return(ret);
}

/*
** open article and interpret options, if any.  The op parameter is set
** to ZERO if and only if an article is opened.  Used above as a flag to
** indicate no articles opened yet.
*/
static digname (n, subj, lines, auth, op)
int n;
char *subj, *lines, *auth;
int *op;
{
	int i,j;
	char t[RECLEN];
	FILE *fp;
	char *(*getfn)();
	char *nfgets();
	char *got;

	/* open article */
#ifdef NNTP
	if (Machine != NULL)
	{
		sprintf (t,"head %d", n);
		if (nntp_put(t,t) < 0)
			return (-1);
		getfn = nntp_get;
	}
	else
	{
		sprintf (t,"%d", n);
		if ((fp = fopen(t,"r")) == NULL)
			return (-1);
		getfn = nfgets;
	}
#else
	getfn = nfgets;
	sprintf (t,"%d", n);
	if ((fp = fopen(t,"r")) == NULL)
		return (-1);
#endif
	*op = 0;

	/* get subject, from and lines by reading article */
	subj[0] = lines[0] = auth[0] = '?';
	subj[1] = lines[1] = auth[1] = '\0';

	for (i = 0; i < HDR_LINES && (got = (*getfn)(t,RECLEN-1,fp)) != NULL; ++i)
	{
		if (index(CHFIRST,t[0]) == NULL)
			continue;
		t[strlen(t) - 1] = '\0';
		if (strncmp(T_head,t,THDLEN) == 0)
		{
			for (j=0; j < Nntopt; ++j)
			{
				if (regex(Negtopt[j],t+THDLEN) != NULL)
				{
					digclose(fp,got);
					return(-1);
				}
			}
			if (Ntopt > 0)
			{
				for (j=0; j < Ntopt; ++j)
				{
					if (regex(Topt[j],t+THDLEN) != NULL)
						break;
				}
				if (j >= Ntopt)
				{
					digclose(fp,got);
					return(-1);
				}
			}
			strcpy(subj,t+THDLEN);
			continue;
		}
		if (strncmp(F_head,t,FHDLEN) == 0)
		{
			for (j=0; j < Nnwopt; ++j)
			{
				if (regex(Negwopt[j],t+FHDLEN) != NULL)
				{
					digclose(fp,got);
					return(-1);
				}
			}
			if (Nwopt > 0)
			{
				for (j=0; j < Nwopt; ++j)
				{
					if (regex(Wopt[j],t+FHDLEN) != NULL)
						break;
				}
				if (j >= Nwopt)
				{
					digclose(fp,got);
					return(-1);
				}
			}
			strcpy(auth,t+FHDLEN);
			continue;
		}
		if (strncmp(L_head,t,LHDLEN) == 0)
		{
			strcpy(lines,t+LHDLEN);
			break;
		}
	}

	digclose(fp,got);

	/* reject empty or 1 line files */
	if (i < 2)
		return (-1);

	return (0);
}

/*
** "close" open article from digname - "got" is not used in non-NNTP case
*/
digclose(fp,got)
FILE *fp;
char *got;
{
#ifdef NNTP
	char bufr[RECLEN];

	if (Machine == NULL)
		fclose (fp);
	else
	{
		/*
		** have to finish getting the header lines from the
		** server
		*/
		while (got != NULL)
			got = nntp_get(bufr,RECLEN-1,fp);
	}
#else
	fclose (fp);
#endif
}

/*
** special fgets for reading header lines, which unfolds continued lines
** and throws away trailing stuff on buffer overflow.
*/
static char *
nfgets(buf, size, fp)
char	*buf;
int	size;
FILE	*fp;
{
	register int c;

	while (!feof(fp))
	{
		if ((c = getc(fp)) == '\n')
		{
			if ((c = getc(fp)) == '\t' || c == ' ')
				continue;
			ungetc(c, fp);
			*buf = '\n';
			++buf;
			*buf = '\0';
			++buf;
			return (buf);
		}

		/* prevent "terminal bombs" */
		if (c < ' ' || c == '\177')
		{
			switch(c)
			{
			case '\r':
			case '\010':
			case '\07':
				break;
			case '\177':
				c = '~';
				break;
			case '\t':
				c = ' ';
				break;
			default:
				if (size > 1)
				{
					*buf = '^';
					++buf;
					--size;
				}
				c += 'A' - 1;
				break;
			}
		}

		if (size > 0)
		{
			*buf = c;
			++buf;
			--size;
		}
		if (c == '\r')
		{
			if ((c = getc(fp)) != '\n')
			{
				ungetc(c, fp);
				continue;
			}
			if ((c = getc(fp)) != ' ' && c != '\t')
			{
				*buf = '\0';
				++buf;
				ungetc(c, fp);
				return (buf);
			}
			--buf;
			++size;
			continue;
		}
	}

	*buf = '\0';
	++buf;
	return (NULL);
}

static char *Mail[2], *Show[6], *Post[5];
static char *Priv[8];
static char *Pool = NULL;

FILE *
vns_aopen(art,hdr)
int art;
ARTHEADER *hdr;
{
	char buf[RECLEN];
	char *dist, *reply, *from, *ngrp, *flto, *path, *resubj;
	FILE *fp;
	FILE *a_open();
	int n;
	char *mail_trim();

	dist = resubj = path = reply = from = ngrp = flto = NULL;

	fp = a_open(art);
	if (fp == NULL)
		return(NULL);

	/*
	** we only really need a lot extra if MAILCHOOSE, but allocating
	** a temporary array of pointers isn't that much.  Similarly, a
	** few assignments, and the "Priv" declaration are only needed
	** with some define settings.  Not worth ifdef'ing.
	*/
	Pool = str_tpool(100);

	hdr->artid = "<some article>";
	hdr->from = "<somebody>";
	hdr->priv = Priv;
	hdr->postcmd = Poster;
	hdr->mail = Mail;
	hdr->show = Show;
	hdr->post = Post;
	hdr->priv_num = hdr->show_num = hdr->post_num = hdr->mail_num = 0;

	/* for conditional is abnormal - expected exit is break */
	for (n=0; n < HDR_LINES && fgets(buf,RECLEN-1,fp) != NULL; ++n)
	{
		/* bail out at first non-header line */
		if (buf[0] == '\n')
			break;
		if (strncmp(buf,RT_head,RTHDLEN) == 0)
		{
			buf [strlen(buf)-1] = '\0';
			reply = str_tstore(Pool,buf+RTHDLEN);
			continue;
		}
		if (strncmp(buf,P_head,PHDLEN) == 0)
		{
			buf [strlen(buf)-1] = '\0';
			path = str_tstore(Pool,buf+PHDLEN);
			continue;
		}
		if (strncmp(buf,DIS_head,DISHDLEN) == 0)
		{
			buf [strlen(buf)-1] = '\0';
			dist = str_tstore(Pool,buf);
			continue;
		}
		if (strncmp(buf,M_head,MHDLEN) == 0)
		{
			buf [strlen(buf)-1] = '\0';
			hdr->artid = str_tstore(Pool,buf+MHDLEN);
			continue;
		}
		if (strncmp(buf,F_head,FHDLEN) == 0)
		{
			buf [strlen(buf)-1] = '\0';
			(hdr->show)[hdr->show_num] = str_tstore(Pool,buf);
			from = hdr->from = (hdr->show)[hdr->show_num]+FHDLEN;
			++(hdr->show_num);
			continue;
		}
		if (strncmp(buf,T_head,THDLEN) == 0)
		{
			buf [strlen(buf)-1] = '\0';
			(hdr->show)[hdr->show_num] = str_tstore(Pool,buf);
			if (strncmp(buf+THDLEN,Fpfix,FPFLEN) != 0)
			{
				sprintf(buf,"%s%s%s",T_head,Fpfix,
					((hdr->show)[hdr->show_num])+THDLEN);
				resubj = str_tstore(Pool,buf);
			}
			else
				resubj = (hdr->show)[hdr->show_num];
			++(hdr->show_num);
			continue;
		}
		if (strncmp(buf,N_head,NHDLEN) == 0)
		{
			buf [strlen(buf)-1] = '\0';

			/* if multiple newsgroups, include in "show" */
			if (index(buf,',') != NULL)
			{
				(hdr->show)[hdr->show_num] = str_tstore(Pool,buf);
				ngrp = (hdr->show)[hdr->show_num] + NHDLEN;
				++(hdr->show_num);
			}
			else
				ngrp = str_tstore(Pool,buf+NHDLEN);
			continue;
		}
		if (strncmp(buf,FT_head,FTHDLEN) == 0)
		{
			buf [strlen(buf)-1] = '\0';
			(hdr->show)[hdr->show_num] = str_tstore(Pool,buf);
			flto = (hdr->show)[hdr->show_num] + FTHDLEN;
			++(hdr->show_num);
			continue;
		}
		if (strncmp(buf,L_head,LHDLEN) == 0)
		{
			buf [strlen(buf)-1] = '\0';
			hdr->lines = atoi(buf+LHDLEN);
			(hdr->show)[hdr->show_num] = str_tstore(Pool,buf);
			++(hdr->show_num);
			continue;
		}
	}

	hdr->hlines = n;

#ifdef MAILCHOOSE
	(hdr->priv)[hdr->priv_num] = resubj;
	++(hdr->priv_num);
	if (reply != NULL)
	{
		(hdr->priv)[hdr->priv_num] = mail_trim(reply);
		++(hdr->priv_num);
	}
	if (from != NULL)
	{
		(hdr->priv)[hdr->priv_num] = mail_trim(from);
		++(hdr->priv_num);
	}
	if (path != NULL)
	{
		(hdr->priv)[hdr->priv_num] = mail_trim(path);
		++(hdr->priv_num);
	}
#else
#ifdef MAILSMART
	if (reply == NULL)
		if (from != NULL)
			reply = from;
		else
		{
			if (path != NULL)
				reply = path;
		}
#else
	if (path != NULL)
		reply = path;
#endif
	if (reply != NULL)
		reply =  mail_trim(reply);
	mail_cmd(hdr,reply,resubj);
#endif /* MAILCHOOSE */

	if (flto == NULL)
	{
		if ((flto = ngrp) == NULL)
			flto = "group.unknown";
	}
	ngrp = rindex(flto,'.');

	if (strncmp("mod.",flto,4) == 0 ||
			(ngrp != NULL && strcmp(".announce",ngrp) == 0))
	{
		sprintf(buf,"Cannot post a follow-up to \"%s\", reply with mail to moderator",flto);
		hdr->post_err = str_tstore(Pool,buf);
		return (fp);
	}

	if (ngrp != NULL && strcmp(ngrp,".general") == 0)
	{
		*ngrp = '\0';
		sprintf(buf,"%s%s.followup",N_head,flto);
	}
	else
		sprintf(buf,"%s%s",N_head,flto);
	flto = str_tstore(Pool,buf);

	hdr->post_err = NULL;

	if (resubj != NULL)
	{
		(hdr->post)[hdr->post_num] = resubj;
		++(hdr->post_num);
	}

#ifdef NNTP
#ifdef SHOW_FROM
	if (Machine != NULL)
	{
		sprintf(buf,FROM_CR_FORM, F_head, Username, Fmach, Userdesc);
		(hdr->post)[hdr->post_num] = str_tstore(Pool,buf);
		++(hdr->post_num);
	}
#else
#ifdef REPLY_CR_FORM
	if (Machine != NULL)
	{
		sprintf(buf,REPLY_CR_FORM, RT_head, Username, Fmach);
		(hdr->post)[hdr->post_num] = str_tstore(Pool,buf);
		++(hdr->post_num);
	}
#endif
#endif
#endif

	(hdr->post)[hdr->post_num] = flto;
	++(hdr->post_num);

	sprintf(buf,"%s%s",R_head,hdr->artid);
	(hdr->post)[hdr->post_num] = str_tstore(Pool,buf);
	++(hdr->post_num);

	if (dist != NULL)
	{
		(hdr->post)[hdr->post_num] = dist;
		++(hdr->post_num);
	}

	return (fp);
}

#ifdef MAILCHOOSE
/*
** routine to prompt user for mail path approval
*/
static
mail_prompt(hdr)
ARTHEADER *hdr;
{
	int i;
	char buf[RECLEN],*ptr;

	tty_set(SAVEMODE);
	for (i=1; i < hdr->priv_num; ++i)
	{
		printf("%d - %s\n",i,(hdr->priv)[i]);
	}
	printf("\nType number to choose one of the above, or input address: ");
	fgets(buf,RECLEN-1,stdin);
	tty_set(RESTORE);

	ptr = strtok(buf," \t\n");
	if (ptr == NULL)
		ptr = "";

	i = strlen(ptr);
	if (i == 1)
	{
		i = atoi(ptr);
		if (i > 0 && i <= hdr->priv_num)
			ptr = (hdr->priv)[i];
		i = 1;
	}

	/*
	** If the user keeps cycling through here on the same article,
	** we will eventually run out of strings.  We made Pool large
	** enough to make it unlikely (user will have to retry about 80
	** times without switching articles).  Hardly elegant, but should
	** be sufficient.
	*/
	if (i > 1 && hdr->priv_num < 8)
	{
		(hdr->priv)[hdr->priv_num] = str_tstore(Pool,ptr);
		++(hdr->priv_num);
	}
	mail_cmd(hdr,ptr,(hdr->priv)[0]);
}
#endif

/*
** trim () off potential mail address, and make copy if needed.
** addr must be allocated string.
*/
static char *
mail_trim(addr)
char *addr;
{
	char buf[RECLEN];
	char *ptr;

	if (index(addr,'(') == NULL)
		return(addr);

	strcpy(buf,addr);
	ptr = index(buf,'(');
	for (--ptr; *ptr == ' ' || *ptr == '\t'; --ptr)
		;
	++ptr;
	*ptr = '\0';
	return (str_tstore(Pool,buf));
}

/*
** format mail command.  Subj must point to allocated string.
*/
static
mail_cmd(hdr,addr,subj)
ARTHEADER *hdr;
char *addr, *subj;
{
	char buf[RECLEN];

	if (addr == NULL || *addr == '\0')
	{
		hdr->mail_err = "No address";
		return;
	}

	hdr->mail_err = NULL;
			;

#ifdef INLETTER
	hdr->mailcmd = Mailer;
	sprintf(buf,"%s%s",TO_head,addr);
	(hdr->mail)[0] = str_tstore(Pool,buf);
	hdr->mail_num = 1;
#else
	sprintf(buf,Mailer,addr);
	hdr->mailcmd = str_tstore(Pool,buf);
	hdr->mail_num = 0;
#endif
	if (subj != NULL)
	{
		(hdr->mail)[hdr->mail_num] = subj;
		++(hdr->mail_num);
	}
}

static FILE
*a_open(art)
int art;
{
	FILE *fp;
	char buf[RECLEN];
#ifdef NNTP
	char cmd[RECLEN];

	if (Machine == NULL)
	{
		sprintf(buf,"%d",art);
		fp = fopen(buf,"r");
		return (fp);
	}

	tmpnam(buf);
	if ((fp = fopen(buf,"w")) == NULL)
		return (NULL);

	sprintf(cmd,"head %d",art);
	if (nntp_put(cmd,cmd) < 0)
	{
		fclose(fp);
		unlink(buf);
		return(NULL);
	}
	while (nntp_get(cmd,RECLEN) != NULL)
		fprintf(fp,"%s",cmd);
	fprintf(fp,"\n");
	sprintf(cmd,"body %d",art);
	if (nntp_put(cmd,cmd) >= 0)
	{
		while (nntp_get(cmd,RECLEN) != NULL)
			fprintf(fp,"%s",cmd);
	}
	fclose(fp);
	fp = fopen(buf,"r");

	/* "invisible" article - FP opened to unlinked file */
	unlink(buf);

	return(fp);
#else
	sprintf(buf,"%d",art);
	fp = fopen(buf,"r");
	return (fp);
#endif
}

vns_aclose(fp)
FILE *fp;
{
	if (Pool != NULL)
		str_tfree(Pool);
	Pool = NULL;
	fclose(fp);
}

/*
** we don't use the count / name / mode arguments because this doesn't
** implement any fancy article massaging
*/
vns_asave(art,fp)
int art;
FILE *fp;
{
	char buf[RECLEN];
	FILE *fin;

	fin = a_open(art);

	while (fgets(buf,RECLEN-1,fin) != NULL)
		fputs(buf,fp);
	fclose(fin);
}

vns_exit()
{
#ifdef NNTP
	if (Fp_trace != NULL)
	{
		fprintf(Fp_trace,"Close Server\n");
		fclose(Fp_trace);
	}
	if (Server_open)
		close_server();
#endif
}

#ifdef NNTP
/*
** nntp_get returns char* so it can be used interchangeably with fgets
** it appends a newline for the same reason.
*/
char *
nntp_get(buf,len)
char *buf;
int len;
{
	if (get_server(buf,len-1) < 0)
		return(NULL);

	len = strlen(buf);
	if (len == 0 || (len > 0 && buf[len-1] != '\n'))
		strcpy(buf+len,"\n");

	if (Fp_trace != NULL)
		fprintf(Fp_trace,"Got - %s",buf);

	if (buf[0] == '.' && buf[1] == '\n')
		return(NULL);

	return(buf);
}

/*
** returns failure explanation in estr - estr = str is legal
*/
nntp_put(str,estr)
char *str;
char *estr;
{
	if (Fp_trace != NULL)
		fprintf(Fp_trace,"Send - %s\n",str);

	put_server(str);
	estr[0] = CHAR_INF;
	while (estr[0] == CHAR_INF)
	{
		if (get_server(estr,RECLEN-1) < 0)
		{
			if (Fp_trace != NULL)
				fprintf(Fp_trace,"Ack - no response\n",estr);
			strcpy(estr,"no response");
			return(-1);
		}

		if (Fp_trace != NULL)
			fprintf(Fp_trace,"Ack - %s\n",estr);
	}

	if (estr[0] != CHAR_OK)
		return (-1);
	return(0);
}

/*
** if not letting the user edit the From line, we have to hack one in
** ourselves to prevent getting the thing posted from "news".  This
** will effectively defeat any that the user may have added, also.
*/
#ifndef SHOW_FROM
nntp_post(hdr,fn)
ARTHEADER *hdr;
char *fn;
{
	char tname[80];
	char bufr[RECLEN];
	FILE *fin,*fout;

	tmpnam(tname);

	if ((fin = fopen(fn,"r")) == NULL || (fout = fopen(tname,"w")) == NULL)
	{
		printf("Can't open article files\n");
		return;
	}

	fprintf(fout,FROM_CR_FORM, F_head, Username, Fmach, Userdesc);
	fprintf(fout,"\n");
	while (fgets(bufr,RECLEN-1,fin) != NULL)
		fprintf(fout,"%s",bufr);
	fclose(fin);
	fclose(fout);

	sprintf(bufr,hdr->postcmd,tname);
	system(bufr);
	printf("Given to posting program.\n");
	unlink(tname);
}
#endif /* SHOW_FROM */
#endif /* NNTP */
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0

-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.