[comp.os.minix] Vn for Minix, part 3 of 4

nick@ultima.cs.uts.oz (Nick Andrew) (11/26/89)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  server.doc server.h session.c sig_set.c
# Wrapped by nick@ultima on Sun Nov 26 22:16:30 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'server.doc' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'server.doc'\"
else
echo shar: Extracting \"'server.doc'\" \(18433 characters\)
sed "s/^X//" >'server.doc' <<'END_OF_FILE'
Xvns_envir()
X
X	called to allow server layer to set environment.  Will be called
X	before anything else.  See the vn_env() routine if the server
X	interface intends to use any environment variables.
X
X	There are some procedure pointers in vn which may optionally
X	be set by the server interface.  By default, all of these are
X	NULL:
X
X	(*Massage)(hdr)
X	ARTHEADER *hdr;
X
X		Mail path massaging function.  Allows server interface to
X		interact with the user prior to editing the reply file
X		on mail replies.  See vns_aopen().
X
X	(*Postfunc)(hdr,fn)
X	(*Mailfunc)(hdr,fn)
X	ARTHEADER *hdr;
X	char *fn;
X
X		If set, these functions will be called for mail / post
X		instead of spawning a command.  They are intended for
X		the instance in which these operations can be handled
X		within the executable rather than forking another process.
X		If forking another process has to be done, you might as
X		well set up postcmd / mailcmd, and let vn do it.  fn is
X		the name of a file containing the user's article or mail
X		reply.
X
X	FILE *(*Saveopen)(fn,msg,mode)
X	char *fn;
X	char *msg,
X	char **mode;
X
X		If set, this function allows the server interface to
X		implement its own semantics for the savefile string
X		typed by the user.  It should return an open file
X		pointer, NULL for failure.  msg is a message buffer
X		which will be displayed to the user, replacing the
X		normal "can't open ..." or "appended|created ..."
X		messages.  It MUST be filled in.  mode is a returned
X		pointer to the mode the file was opened with, and
X		will only really be used to pass on to vns_asave(),
X		so it may not really matter.  If untouched, it will
X		be "a".  Note that if this function is defined, the
X		user still can specify a pipe by typing "|<stuff>", but
X		all other interpretation of the savefile string such as
X		expansion of tildes becomes the responsibility of this
X		routine.  This is really intended as the hook to hang
X		something like the concept of "mail-folders" from.
X
X	(*Digsaver)(x,fp,count,name,mode)
X	char *x;
X	FILE *fp;
X	int count;
X	char *name;
X	char *mode;
X
X		If defined, this will be use to save digest extraction
X		articles.  For digest extractions, this will be called
X		instead of vns_asave, and will have to be defined to
X		perform any special massaging similar to vns_asave if
X		the features are expected to work for digest extractions.
X		If not defined, vn will simply shovel digest extractions
X		out to fp with no massaging.  x is the name of a temporary
X		file containing the extracted "article", the other arguments
X		are as for vns_asave.
X
X	char (*Marker)(group,art,oldmark,userkey)
X	NODE *group;
X	int art;
X	char oldmark;
X	char userkey;
X
X		This allows the server interface access to user mark
X		operations.  If defined, it will be called whenever the
X		user marks an article.  It returns the new mark to be
X		placed on the article, which should be a printable
X		character.  The space character is to be used to mean
X		that the article is unmarked.  This is the intended hook
X		to allow the server interface to implement any special
X		actions based on the user marks, such as retention of
X		marked articles when vns_write() is called.  The user
X		will still print/save/read all marked articles by
X		specifying "*", whatever the actual mark returned by
X		this routine, "marked" meaning marked with something
X		other than <space>.  Userkey is the character entered
X		by the user which resulted in the mark action ('x', '^x',
X		'X' or '*').  Remember that 'X' means "unmark".
X
X	(*Hookfunc)(key,reader,go,msg)
X	char key;
X	int reader;
X	int go;
X	char *msg;
X
X		This allows the vns_ layer to hook arbitrary commands
X		into the vn interaction.  If defined, this will be called
X		with every user command key.  reader will be TRUE if called
X		from reading interaction, FALSE if called from page.
X
X		Since this will be called with EVERY user command key, if
X		defined, it is very important that it return <0 for all
X		unrecognized keys, or vn won't be able to do anything.
X
X		Attempts to define most control keys as commands will be
X		ineffective from the reader - they simply will not
X		reach Hookfunc.  They are OK from the page display.
X		You may not use <escape> or numeric characters as commands.
X
X		If go is FALSE, msg argument should not be used, and no
X		real action should be taken.  In this case, the routine
X		should return >= 0 if the key is an additional command,
X		< 0 otherwise.  This is the call vn may make to see if
X		the vns_ layer has a use for a key, without wanting to
X		really invoke that action.
X
X		If go is TRUE, the action is actually to be taken, if
X		applicable.  Return < 0 for undefined key.  If it returns 0,
X		this indicates that the procedure did nothing to the screen,
X		and msg MUST be filled in with a message to display.  This
X		is the way to implement a simple toggle.  If interaction
X		with the user is desired, this routine should call
X		term_set(ERASE), which clears the screen, and return
X		> 0 after performing the interaction.  It should leave
X		the cursor in the desired screen location for continuation
X		when called while reading an article.
X
X		This hook can be used to implement user interaction for
X		any purpose which is server specific.  The one currently
X		being contemplated is control of the way in which articles
X		are saved in files (mailbox format feature).
X
X		Another which suggests itself is allowing the edit of
X		which header lines to display.  In this case, it is OK
X		to take "h" key which only handles this function in a
X		rudimentary fashion.  If this is done, be sure to take
X		the key BOTH on the page display and in the reader - vn
X		will never rewind an article past the point returned by
X		vns_aopen if this is done.
X
X	char *
X	(*Hookhelp)(reader,first,key)
X	int reader, first;
X	char *key;
X
X		This allows help messages for the commands set by Hookfunc,
X		to be shown.  reader is TRUE if from reading an article,
X		FALSE if from page.  first is set TRUE to start list.
X		returns help message, and places corresponding key character
X		in key.  vn will keep calling with first = FALSE until
X		a NULL return indicates the end of the list.
X
X		Note that command keys defined by vns_ layer may be
X		translated via a user .vnkeys file just like the other
X		commands.  These routines work strictly in terms of the
X		translated keys - vn will handle translating back to
X		the users keys for the help display.
X
X		If you define a key already used by vn, it works, but
X		is obviously not a good practice.  Only the "quit" key from
X		the page display can't be superceded.
X
X	All of the procedures will be called with the user's terminal in
X	raw mode and will be expected to leave it that way on return.
X	If these routines need to control ioctl settings or send control
X	sequences to the terminal, they should do so through the tty_set /
X	term_set calls used by the rest of vn.  These routines basically
X	hide ioctl / termcap from everybody.
X
Xvns_news (argc,argv,lfirst,unsub)
Xint argc;
Xchar **argv;
Xint *lfirst;
Xint *unsub;
X
X	called to let server layer process options and enter known newsgroups
X	in vn structure.  arguments are vn's command line.   It calls four
X	specific support routines:
X
X	hashenter(group,highnum,lownum)
X	char *group;
X	int highnum, lownum;
X
X		Enter information about a newsgroup. Group is newsgroup
X		name, highnum the highest article number, lownum the lowest.
X
X	NODE *
X	hashfind(group)
X	char *group;
X
X		Return information on entered group.  See node.h header
X		file for structure and legal usage of its items by server
X		interface routines.  May be called by other vns_ rouines
X		as well.
X
X	fw_group(group,new,sub,read,look)
X	char *group;
X	int new, sub, read, look;
X
X		prepare to enter display information for a new newsgroup.
X		new is non-zero if this is to be considered a "new" newsgroup
X		since the user's last session.  sub is non-zero if user is
X		to be considered "subscribed" to it.  read is the initial
X		"articles read" number for the newsgroup.  look is non-zero
X		if server interface intends to look for articles in this
X		newsgroup.  This parameter is open to interpretation, but
X		should indicate that user's options mean group is to be looked
X		at.  This should NOT reflect the fact that no articles will
X		be found because "read" is up to date.  The parameter is only
X		used for statistical collection, anyway.
X
X	fw_art(anum,subj,lines,author)
X	int anum;
X	char *subj, *lines, *author;
X
X		enter information for an article in the newsgroup last set
X		with fw_group().  anum's should be entered in ascending
X		sequence.
X
X	fw_chg(new,sub,read,look)
X	int new, sub, read, look;
X
X		change the parameters for the current group.  To allow the
X		server interface to modify its setting for the group based
X		on what it discovered while scanning the articles.
X
X	fw_group() should be called once and only once for each group
X	hashenter()'ed.  The order of fw_group() calls determines the
X	order of vn's display to the user.  fw_group() calls may be made
X	after all the hashenter() calls have been made, or they may be
X	interleaved.  If no fw_art() calls are made following a particular
X	fw_group() call, no display page will be generated for that group.
X	If vns_news does not stick to the rules regarding calls to fw_group,
X	vn will exit with a fatal error message.
X
X	Rationale for this is for the server layer to have no knowledge
X	of device dependencies (the calls to fw_art will split up display
X	pages), or any of the vn structures beyond the newsgroup information.
X	And the newsgroup NODE structure has clearly documented pieces
X	which are legal for the server to read or use, and others which are
X	out-of-bounds.  It isolates things fairly well.
X
X	Note that vns_news hides all knowledge of the user's .newsrc file or
X	similar concept, and all knowledge of where articles live and how
X	they are administered.  Error handling is brutally simple.  If the
X	server doesn't obey the rules, vn will exit with a fatal error message
X	if it detects the fact.
X
X	lfirst and unsub are returned to the caller based on the command
X	line:
X
X		lfirst - non-zero if vn is supposed to go directly to
X			the newsgroup summary list on startup (-% option).
X
X		unsub - set non-zero if unsubscribed newsgroups are to
X			be updated on "write alls" (-U option).
X
X	We COULD have vn look for these, and nail them down, but most command
X	line options are things which make sense only to the server interface.
X	Different environments may have different options which make sense.
X	Might as well give the server interface control of the command line
X	exclusively, and advise to consistently use -% and -U if possible.
X
X	See below for some other support routines which might be useful for
X	vns_news().
X
X	In summary, what vns_news should do:
X
X		Call hashenter() for all the legitimate newsgroups.
X		After this, hashfind() may be called at any time to retrieve
X		the information about a group.
X
X		Call fw_group() once and only once for each group, in the
X		order newsgroups should be displayed to the user.  The order
X		need bear no relation to the order of hashenter calls.
X
X		After each fw_group call, shovel in article information with
X		fw_art calls.  Optionally call fw_chg() if anything ought to
X		be changed from the original fw_group call.
X
X		If there are fatal errors call printex() with an explanation.
X
X		To produce non-fatal diagnostics / informational messages,
X		call fgprintf().  If you absolutely want to produce the message,
X		backgrounded or not, you CAN simply output to stderr / stdout,
X		but think before doing it.  It's obnoxious to have a
X		backgrounded program stop on output before it's ready to
X		interact with you.
X
X		ISN'T THAT EASY?
X
XFILE *vns_aopen(art,hdr)
Xint art;
XARTHEADER *hdr;
X
X	Returns an open file pointer to article text for vn's use, or
X	NULL for failure.  Fills in the ARTHEADER argument, containing
X	information conceptually thought of as extracted from the "header"
X	lines.  File pointer should be positioned at the start of the
X	bonafide article text, if headers are contained in the file.  See
X	head.h for the items to be filled in.
X
X	If (*Massage)(), (*Postfunc)() or (*Mailfunc)() are defined, they
X	will be called with this same ARTHEADER instance when they are
X	called.  vns_aopen may set up any arguments in the priv item
X	for the convenience of these routines.
X
X	CAUTION:
X		hdr contains string pointers.  vns_aopen must be sure to
X		point to legitimate storage, not its stack.  And it SHOULD
X		NOT simply malloc() the strings every time without freeing
X		the old ones somehow - this will lose storage every time the
X		user reads a new article.  You MAY NOT assume that the same
X		instance of an ARTHEADER structure is passed to each
X		vns_aopen() call, either.  See str_tpool, and associated
X		routines, below.
X
Xvns_close(fp)
XFILE *fp;
X
X	Called to close article opened with vns_open().  In many instances
X	will simply be an fclose().  vn will NEVER have multiple articles
X	open, so vns_aopen / vns_aclose may maintain static information if
X	they like.
X
Xvns_gset(grp)
Xchar *grp;
X
X	Called as the user moves from page to page of the display.  vns_aopen
X	and vns_aclose calls will refer to this newsgroup.  May do chdir()'s
X	if it likes.
X
Xvns_exit(stat)
Xint stat;
X
X	Called before exit to allow the server layer to clean up.  Called
X	on normal exit & from within printex() (ie. DON'T call printex in
X	unconditionally in this routine)
X
Xvns_write(news,count)
XNODE **news;
Xint count;
X
X	Called to do whatever is the conceptual equivalent of updating the
X	user's .newsrc file.  news is an array of newsgroup nodes in the
X	order that fw_group was called, count is the length of the array.
X	Vn may have modified the rdnum items & the FLG_SUB flag based on
X	user interaction.  User exit, "write" commands, and "subscription"
X	commands will result in this call.  vn will set a flag bit, FLG_ECHG,
X	in the flag item of all newsgroups whose data it changed since last
X	call.  This bit may be used by vns_write to do selective updates, and
X	will be cleared for all groups following return from vns_write().
X
Xvns_asave(art,fp,count,name,mode)
Xint art;
XFILE *fp;
Xint count;
Xchar *name,*mode;
X
X	Called to shovel an article out to an open file pointer.  Intended
X	to allow the server interface to implement any kind of massaging
X	of the article text that is called for.  count starts with 0, and
X	counts the articles being written on this opening of the file.
X	name is the file name, and mode is the mode it was opened with.
X	This information is being passed in to allow implementation of
X	selective copy of header lines, etc. based on the state of the
X	file.  Note that it WILL exist since it's already been opened.  On
X	count 0, this routine may be interested in whether it's empty or not.
X	This routine WILL get called between vns_aopen and vns_aclose.
X
Xchar *Vns_version;
X
X	The server layer must define this string.  It should be a
X	short string which describes the server type / version (something
X	like "NNTP-1.0").  Will be printed as part of the vn version message.
X
XSome support routines:
X
X	regex(), regcmp(), strtok() and strpbrk() are standard SYSV
X	routines not normally available for BSD.  vn uses them, so
X	they are implemented in vn for those systems.  Thus, they are
X	available for use in the server interface.  Be careful with
X	strtok(), which retains context from call to call, if you are
X	not familiar with its use.  To free storage handed you by
X	regcmp(), use regfree() instead of free().
X
X	char *vn_env(var, def)
X	char *var, *def;
X
X	Use this in place of getenv() to be consistent with the rest of
X	vn.  This routine checks first for the variable prefixed with "VN"
X	if it doesn't already begin with "VN".  If neither the prefixed
X	or unprefixed name exists, it returns the def argument.  This allows
X	consistent override of EDITOR with VNEDITOR, etc.
X
X	printex(....)
X
X	printf style argument list.  Generates a fatal error and exits.
X	No newline necessary in the message, and printex() adds the error
X	number to its output automatically.  printex() may be called from
X	anywhere in the server interface to generate a fatal error, except
X	from vns_exit().
X
X	fgprintf(....)
X
X	nonfatal diagnostic / informational message.  printf style argument
X	list.  No extra formatting printed.  Will only print if program is
X	running in the foreground.  fgprintf() should be called only from
X	vns_envir() and vns_news(), as all the other routines come after
X	user interaction has begun.
X
X	char *
X	str_store(s)
X	char *s;
X
X	Returns a pointer to a permanent copy of a string.  DO NOT FREE THAT
X	POINTER!!  This routine is intended to save malloc() overhead, and
X	returns pointers into larger buffer allocated with a single request.
X
X	char *
X	str_tstore(ptr,s)
X	char *ptr;
X	char *s;
X
X	char *
X	str_tpool(n)
X	int n;
X
X	char **
X	str_taptr(ptr)
X	char *ptr;
X
X	str_tfree(ptr)
X	char *ptr;
X
X	Temporary string storage.  str_tpool is called with the maximum
X	number of strings to be allocated, and it returns a pointer to use
X	with the other calls (ptr argument).  str_tstore returns allocated
X	copies of strings.  str_taptr returns an address which may be treated
X	as a string array containing the str_tstore allocated strings
X	beginning with the NEXT str_tstore() call.  May be called several
X	times to get several array pointers.  str_tfree() frees all the strings
X	allocated on the given ptr returned from str_tpool, as well as the
X	pool pointer itself.  The returned addresses are no longer valid, nor
X	is the pool pointer.  Multiple pool pointers may be maintained.
X	Pool pointer contents should not be touched by the caller.  These
X	routines are intended for the convenience of vns_aopen().
X
XHEADER FILES:
X
Xserver.h includes the header files used by vn which are intended to be known
Xto server interface routines.  The server interface routines may include
Xsystem headers, this one, and their own headers.  Among their own headers
Xshould be a config_*.h containing parameters which are likely to be changed
Xfrom installation to installation, such as file paths.  server.h contains
Xnode.h and head.h, defining the NODE and ARTHEADER structures.
X
XA useful constant defined in server.h is RECLEN.  This should be used only
Xas an array dimension for a stack character buffer, intended to be large
Xenough to hold an arbitrary string.  If you use this constant, you are
Xmaking "large enough" the same value used in vn.  Use it for fgets()
Xbuffers, or sprintf buffers prior to making allocated copies of the string,
Xand you'll be working about the way vn does in a lot of places.
END_OF_FILE
if test 18433 -ne `wc -c <'server.doc'`; then
    echo shar: \"'server.doc'\" unpacked with wrong size!
fi
# end of 'server.doc'
fi
if test -f 'server.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'server.h'\"
else
echo shar: Extracting \"'server.h'\" \(151 characters\)
sed "s/^X//" >'server.h' <<'END_OF_FILE'
X
X/*
X**	header files shared between vn and vns_xxxx server interface routines
X*/
X#include "tune.h"
X#include "tty.h"
X#include "node.h"
X#include "head.h"
END_OF_FILE
if test 151 -ne `wc -c <'server.h'`; then
    echo shar: \"'server.h'\" unpacked with wrong size!
fi
# end of 'server.h'
fi
if test -f 'session.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'session.c'\"
else
echo shar: Extracting \"'session.c'\" \(21343 characters\)
sed "s/^X//" >'session.c' <<'END_OF_FILE'
X/*
X** vn news reader.
X**
X** session.c - top session loop
X**
X** see copyright disclaimer / history in vn.c source file
X*/
X#include <stdio.h>
X#include <setjmp.h>
X#include <ctype.h>
X#include "config.h"
X#include "tty.h"
X#include "brk.h"
X#include "head.h"
X#include "tune.h"
X#include "node.h"
X#include "page.h"
X#include "vn.h"
X
Xextern NODE **Newsorder;
Xextern char Erasekey, Killkey;
Xextern int Rot;
Xextern char *Ps1,*Printer;
Xextern char *Orgdir,*Savefile;
Xextern int Ncount, Cur_page, Lrec, L_allow, C_allow;
Xextern int Headflag;
Xextern PAGE Page;
Xextern int Digest;
Xextern char *No_msg;
Xextern char *Hdon_msg;
Xextern char *Hdoff_msg;
Xextern char *Roton_msg;
Xextern char *Rotoff_msg;
Xextern char Cxitop[], Cxptoi[];
Xextern char *Aformat;
Xextern char *Contstr;
Xextern char *Kl,*Kr,*Ku,*Kd;
Xextern int Nounsub, Listfirst;
Xextern char *List_sep;
Xextern char *Version, *Vns_version;
X
Xextern char (*Marker)();
Xextern int (*Hookfunc)();
X
Xstatic int C_info;
Xstatic int Dskip, Drec;
X
Xstatic char *Unsub_msg = "Unsubscribed";
Xstatic char *Egroup_msg = "Entire newsgroup";
X
Xstatic int Crec;
Xstatic int Highrec;
X
X/*
X	main session handler processing input commands
X
X	NOTE: this is where a setjmp call is made to set the break reentry
X		location.  Keep the possible user states in mind.
X*/
Xsession ()
X{
X	char alist [RECLEN], c;
X	int newg, i, j, count;
X	jmp_buf brkbuf;
X
X	newg = new_groups();
X	tty_set (RAWMODE);
X	find_page (0);
X	Digest = 0;
X
X	/* reentry point for break from within session interaction */
X	setjmp (brkbuf);
X	sig_set (BRK_SESS,brkbuf);
X	Headflag = FALSE;
X	Rot = 0;
X
X	/* done this way so that user gets "really quit?" break treatment */
X	if (newg > 0)
X	{
X		printf ("\n%s",Contstr);
X		getnoctl();
X		newg = 0;
X	}
X
X	/* list preview option - clear after first time for long jumps */
X	if (Listfirst)
X	{
X		/* tot_list settings will be overwritten in this case */
X		tot_list();
X		Listfirst = 0;
X	}
X
X	/* if breaking from a digest, recover original page */
X	if (Digest)
X	{
X		find_page(Cur_page);
X		Digest = 0;
X	}
X	show ();
X	Crec = RECBIAS;
X	Highrec = Page.h.artnum + RECBIAS;
X	term_set (MOVE,0,Crec);
X
X	/*
X		handle commands until QUIT, update global/local status
X		and display for each.
X	 */
X	for (count = getkey(&c); c != QUIT; count = getkey(&c))
X	{
X		if (Hookfunc != NULL)
X		{
X			if ((i = (*Hookfunc)(c,0,1,alist)) >= 0)
X			{
X				if (i == 0)
X					prinfo(alist);
X				else
X				{
X					term_set(ERASE);
X					show();
X				}
X				term_set (MOVE,0,Crec);
X				continue;
X			}
X		}
X
X		if ( srch_help(c,&i) != 0 || (Digest != 0 && i == 0))
X		{
X			preinfo (UDKFORM,Cxptoi[HELP]);
X			term_set (MOVE, 0, Crec);
X			continue;
X		}
X
X		switch (c)
X		{
X		case HEADTOG:
X			if (Headflag)
X			{
X				Headflag = FALSE;
X				prinfo (Hdoff_msg);
X			}
X			else
X			{
X				Headflag = TRUE;
X				prinfo (Hdon_msg);
X			}
X			term_set (MOVE,0,Crec);
X			break;
X		case SETROT:
X			if (Rot == 0)
X			{
X				Rot = 13;
X				prinfo (Roton_msg);
X			}
X			else
X			{
X				Rot = 0;
X				prinfo (Rotoff_msg);
X			}
X			term_set (MOVE,0,Crec);
X			break;
X		case SSTAT:
X			count_msg ();
X			term_set (MOVE,0,Crec);
X			break;
X		case GRPLIST:
X			tot_list ();
X			show();
X			term_set (MOVE,0,Crec);
X			break;
X		case REDRAW:
X			show();
X			term_set (MOVE,0,Crec);
X			break;
X		case UNSUBSCRIBE:
X			new_sub(Page.h.group,0);
X			do_update(Unsub_msg);
X			term_set (MOVE,0,Crec);
X			break;
X
X		case UPDATE:
X			new_read(Page.h.group,Page.b[Crec-RECBIAS].art_id);
X			wr_show ();
X			do_update("Updated to cursor");
X			term_set (MOVE,0,Crec);
X			break;
X		case UPALL:
X			new_read(Page.h.group,(Page.h.group)->highnum);
X			wr_show();
X			do_update(Egroup_msg);
X			term_set (MOVE,0,Crec);
X			break;
X		case ORGGRP:
X			new_read(Page.h.group,(Page.h.group)->orgrd);
X			wr_show();
X			do_update(Egroup_msg);
X			term_set (MOVE,0,Crec);
X			break;
X		case UPSEEN:
X			up_seen();
X			wr_show();
X			do_update("All pages displayed to this point updated");
X			term_set (MOVE,0,Crec);
X			break;
X		case ORGSTAT:
X			for (i = 0; i < Ncount; ++i)
X				new_read(Newsorder[i],(Newsorder[i])->orgrd);
X			wr_show();
X			do_update("Original data recovered");
X			term_set (MOVE,0,Crec);
X			break;
X 		case TOPMOVE:
X 			Crec = RECBIAS;
X 			term_set (MOVE, 0, Crec);
X 			break;
X 		case BOTMOVE:
X 		case ALTBOTTOM:
X 			Crec = Highrec - 1;
X 			term_set (MOVE, 0, Crec);
X 			break;
X 		case MIDMOVE:
X 			Crec = (RECBIAS + Highrec - 1) / 2;
X 			if (Crec < RECBIAS)
X 				Crec = RECBIAS;
X 			if (Crec >= Highrec)
X 				Crec = Highrec - 1;
X 			term_set (MOVE, 0, Crec);
X 			break;
X		case UP:
X			if (Crec != RECBIAS)
X			{
X				Crec -= count;
X				if (Crec < RECBIAS)
X					Crec = RECBIAS;
X				term_set (MOVE, 0, Crec);
X			}
X			else
X				putchar ('\07');
X			break;
X		case DOWN:
X			if (Crec < (Highrec - 1))
X			{
X				Crec += count;
X				if (Crec >= Highrec)
X					Crec = Highrec - 1;
X				term_set (MOVE, 0, Crec);
X			}
X			else
X				putchar ('\07');
X			break;
X		case MARKSTRING:
X			(Page.h.group)->flags |= FLG_ACC;
X			userlist (alist);
X			markstr (alist,c);
X			term_set (MOVE, 0, Crec);
X			write_page ();
X			break;
X		case MARK:
X		case ART_MARK:
X			genlist (alist,Crec-RECBIAS,count);
X			markstr (alist,c);
X			term_set (MOVE, 0, Crec);
X			write_page ();
X			break;
X		case UNMARK:
X			for (i=0; i < Page.h.artnum; ++i)
X			{
X				if (Page.b[i].art_mark != ' ')
X				{
X					if (Marker == NULL)
X						Page.b[i].art_mark = ' ';
X					else
X						Page.b[i].art_mark =
X						(*Marker)(Page.h.group,
X							Page.b[i].art_id,
X							Page.b[i].art_mark, c);
X					term_set (MOVE, 0, i+RECBIAS);
X					putchar (' ');
X				}
X			}
X			term_set (MOVE, 0, Crec);
X			write_page ();
X			break;
X		case BACK:
X			count *= -1;	/* fall through */
X		case FORWARD:
X			if (forward (count) >= 0)
X				show();
X			else
X				preinfo ("No more pages");
X			term_set (MOVE,0,Crec);
X			break;
X		case GRPBACK:
X			j = (Page.h.group)->order;
X			for (i = j-1; count > 0 && i >= 0; --i)
X			{
X				if ((Newsorder[i])->pages > 0)
X				{
X					j = i;
X					--count;
X				}
X			}
X			if (j == (Page.h.group)->order)
X				preinfo ("No more newsgroups");
X			else
X			{
X				forward ((Newsorder[j])->pnum - Cur_page);
X				show();
X			}
X			term_set (MOVE,0,Crec);
X			break;
X		case GRPFORWARD:
X			j = (Page.h.group)->order;
X			for (i= j+1; count > 0 && i < Ncount; ++i)
X			{
X				if ((Newsorder[i])->pages > 0)
X				{
X					j = i;
X					--count;
X				}
X			}
X			if (j == (Page.h.group)->order)
X				preinfo ("No more newsgroups");
X			else
X			{
X				forward ((Newsorder[j])->pnum - Cur_page);
X				show();
X			}
X			term_set (MOVE,0,Crec);
X			break;
X		case DIGEST:
X			if (Digest)
X			{
X				Digest = 0;
X				find_page (Cur_page);
X				show();
X				Crec = Drec + RECBIAS + 1;
X				Highrec = Page.h.artnum + RECBIAS;
X				if (Crec >= Highrec)
X					Crec = Highrec - 1;
X				term_set (MOVE,0,Crec);
X				break;
X			}
X			(Page.h.group)->flags |= FLG_ACC;
X			Dskip = count - 1;
X			Drec = Crec - RECBIAS;
X			if (dige_page(Drec,Dskip) >= 0)
X			{
X				show();
X				Crec = RECBIAS;
X				Highrec = Page.h.artnum + RECBIAS;
X				term_set (MOVE,0,Crec);
X				break;
X			}
X			Digest = 0;
X			preinfo ("Can't unpack the article");
X			term_set (MOVE,0,Crec);
X			break;
X		case NEWGROUP:
X			if ((i = spec_group()) < 0)
X			{
X				term_set (MOVE,0,Crec);
X				break;
X			}
X			Digest = 0;
X			show();
X			Crec = RECBIAS;
X			Highrec = Page.h.artnum + RECBIAS;
X			term_set (MOVE,0,Crec);
X			break;
X
X		case SAVE:
X			(Page.h.group)->flags |= FLG_ACC;
X			genlist (alist,Crec-RECBIAS,count);
X			savestr (alist);
X			term_set (MOVE,0,Crec);
X			break;
X		case SAVEALL:
X			(Page.h.group)->flags |= FLG_ACC;
X			genlist (alist,0,L_allow);
X			savestr (alist);
X			term_set (MOVE,0,Crec);
X			break;
X		case SAVESTRING:
X		case ALTSAVE:
X			(Page.h.group)->flags |= FLG_ACC;
X			userlist (alist);
X			savestr (alist);
X			term_set (MOVE,0,Crec);
X			break;
X		case READ:
X		case ALTREAD:
X			(Page.h.group)->flags |= FLG_ACC;
X			genlist (alist,Crec-RECBIAS,count);
X			readstr (alist,count);
X			break;
X		case READALL:
X			(Page.h.group)->flags |= FLG_ACC;
X			genlist (alist,0,L_allow);
X			readstr (alist,0);
X			break;
X		case READSTRING:
X			(Page.h.group)->flags |= FLG_ACC;
X			userlist (alist);
X			readstr (alist,0);
X			break;
X		case PRINT:
X			(Page.h.group)->flags |= FLG_ACC;
X			genlist (alist,Crec-RECBIAS,count);
X			printstr (alist);
X			term_set (MOVE,0,Crec);
X			break;
X		case PRINTALL:
X			(Page.h.group)->flags |= FLG_ACC;
X			genlist (alist,0,L_allow);
X			printstr (alist);
X			term_set (MOVE, 0, Crec);
X			break;
X		case PRINTSTRING:
X			(Page.h.group)->flags |= FLG_ACC;
X			userlist (alist);
X			printstr (alist);
X			term_set (MOVE, 0, Crec);
X			break;
X
X		case HELP:
X			help ();
X			show ();
X			term_set (MOVE, 0, Crec);
X			break;
X		case UNESC:
X			user_str (alist,Ps1,1,"");
X			term_set (ERASE);
X			fflush (stdout);
X			tty_set (SAVEMODE);
X			if (chdir(Orgdir) < 0)
X				printf ("change to original directory, %s, failed",Orgdir);
X			else
X			{
X				system (alist);
X				tty_set (RESTORE);
X				term_set (RESTART);
X			}
X			printf (Contstr);
X			getnoctl ();
X			vns_gset(Page.h.name);
X			show ();
X			term_set (MOVE, 0, Crec);
X			break;
X		case PRTVERSION:
X			prinfo("%s %s", Version, Vns_version);
X			term_set (MOVE, 0, Crec);
X			break;
X		default:
X			preinfo("Unhandled key: %c", c);
X			break;
X		}
X	}
X
X	Digest = 0;
X	for (i=0; i < Ncount; ++i)
X	{
X		if ((Newsorder[i])->rdnum < (Newsorder[i])->pgrd)
X			break;
X	}
X	if (i < Ncount)
X	{
X		user_str (alist,"Some displayed pages not updated - update ? ",
X							1, QUIT_ANSWER);
X		if (alist[0] == 'y')
X			up_seen();
X	}
X	sig_set (BRK_OUT);
X}
X
X/*
X** update status of Newsgroups to all seen pages
X*/
Xup_seen()
X{
X	int i;
X
X	for (i = 0; i < Ncount; ++i)
X	{
X		if (Nounsub && ((Newsorder[i])->flags & FLG_SUB) == 0)
X		{
X			new_read(Newsorder[i],(Newsorder[i])->highnum);
X			continue;
X		}
X		if ((Newsorder[i])->rdnum < (Newsorder[i])->pgrd)
X			new_read(Newsorder[i],(Newsorder[i])->pgrd);
X	}
X}
X
X/*
X	count_msg displays count information
X*/
Xcount_msg ()
X{
X	int i, gpnum, gscan, gpage;
X	unsigned long mask;
X	gpnum = 1;
X	for (gscan = gpage = i = 0; i<Ncount; ++i)
X	{
X		if (((Newsorder[i])->flags & FLG_PAGE) != 0)
X		{
X			if (((Newsorder[i])->pnum + (Newsorder[i])->pages - 1) < Cur_page)
X				++gpnum;
X			++gpage;
X			for (mask=1; mask != 0L; mask <<= 1)
X				if (((Newsorder[i])->pgshwn & mask) != 0L)
X					++gscan;
X		}
X	}
X	prinfo (CFORMAT,Cur_page+1,Lrec+1,gscan,gpnum,gpage);
X}
X
X/*
X	forward utility handles paging
X*/
Xstatic
Xforward (count)
Xint count;
X{
X	if (!Digest)
X	{
X		if ((count < 0 && Cur_page <= 0) || (count > 0 && Cur_page >= Lrec))
X			return (-1);
X		Cur_page += count;
X		if (Cur_page < 0)
X			Cur_page = 0;
X		if (Cur_page > Lrec)
X			Cur_page = Lrec;
X		find_page (Cur_page);
X		Crec = RECBIAS;
X		Highrec = Page.h.artnum + RECBIAS;
X		return (0);
X	}
X	/*
X	** in digests, paging past the end of the digest returns to
X	** page extracted from.
X	*/
X	if (Dskip > 0 && (Dskip + count*L_allow) < 0)
X		Dskip = 0;
X	else
X		Dskip += count * L_allow;
X	find_page (Cur_page);
X	if (Dskip >= 0)
X	{
X		if (dige_page(Drec,Dskip) >= 0)
X		{
X			Crec = RECBIAS;
X			Highrec = Page.h.artnum + RECBIAS;
X			return (0);
X		}
X	}
X	Digest = 0;
X	Crec = Drec + RECBIAS + 1;
X	Highrec = Page.h.artnum + RECBIAS;
X	if (Crec >= Highrec)
X		Crec = Highrec - 1;
X	return (0);
X}
X
X/*
X	generate list of articles on current page,
X	count articles, starting with first.
X*/
Xstatic
Xgenlist (list,first,count)
Xchar *list;
Xint first,count;
X{
X	int i;
X	for (i=first; i < Page.h.artnum && count > 0; ++i)
X	{
X		sprintf (list,"%d ",Page.b[i].art_id);
X		list += strlen(list);
X		--count;
X	}
X}
X
X/*
X	send list of articles to printer
X*/
Xstatic
Xprintstr (s)
Xchar *s;
X{
X	char cmd [RECLEN];
X	char fn[L_tmpnam];
X	char dsave[RECLEN];	/* save list for unlinking */
X
X	prinfo ("preparing print command ....");
X
X	if (Digest)
X	{
X		dig_list (s);
X		strcpy(dsave,s);
X	}
X
X	if (*s != '\0')
X	{
X		tmpnam(fn);
X		if (art_xfer(fn,s,"w",NULL) != 0)
X		{
X			preinfo("Couldn't open temporary file");
X			return;
X		}
X		sprintf (cmd,"%s %s 2>/dev/null",Printer,fn);
X		if (system (cmd) == 0)
X			prinfo ("Sent to printer");
X		else
X			preinfo ("Print failed");
X		unlink(fn);
X	}
X	else
X		preinfo (No_msg);
X
X	if (Digest)
X		dig_ulist (dsave);
X}
X
X/*
X	read a list of articles.
X*/
Xreadstr (s,count)
Xchar *s;
Xint count;
X{
X	char *strtok();
X	char *fn[MAXARTLIST+1];
X	int pc, num, i;
X
X	/* we pre-process tokens to release strtok() for further use */
X	fn[0] = strtok(s,List_sep);
X	for (num=0; fn[num] != NULL; fn[(++num)] = strtok(NULL,List_sep))
X		if (num >= MAXARTLIST)
X			break;
X	fn[num] = NULL;
X
X	if (fn[0] != NULL)
X	{
X		term_set (ERASE);
X		for (i=0; i < num && readfile(fn[i], fn[i+1] ,&pc) >= 0; ++i)
X		{
X#ifndef	MINIX
X/* I don't believe this code is correct at all!!! */
X			if (Digest)
X				unlink (fn[i]);
X#endif	!MINIX
X		}
X
X#ifndef	MINIX
X/* I don't believe this code is correct at all!!! */
X		if (Digest && fn[i] != NULL)
X			unlink (fn[i]);
X#endif	!MINIX
X		if (pc != 0)
X			forward (pc);
X		else
X		{
X			Crec += count;
X			if (Crec >= Highrec)
X				Crec = Highrec - 1;
X		}
X		show ();
X		term_set (MOVE, 0, Crec);
X	}
X	else
X	{
X		preinfo ("%s",No_msg);
X		term_set (MOVE, 0, Crec);
X	}
X}
X
X/*
X	concatenate articles to save file with appropriate infoline messages.
X	prompt for save file, giving default.  If save file begins with "|"
X	handle as a filter to pipe to.  NOTE - every user specification of
X	a new Savefile "loses" some storage, but it shouldn't be a very great
X	amount.
X*/
Xstatic
Xsavestr (s)
Xchar *s;
X{
X	char *ptr, newfile [MAX_C+1], msg[RECLEN];
X	char *str_store();
X	char dsave[RECLEN];
X
X	if (Digest)
X	{
X		dig_list (s);
X		strcpy(dsave,s);
X	}
X
X	if (*s != '\0')
X	{
X		user_str (newfile,"save file ? ",1,Savefile);
X		ptr = newfile;
X		if (*ptr == '|')
X		{
X			term_set (ERASE);
X			fflush (stdout);
X			save_art(s,ptr,msg);
X			printf ("%s\n%s",msg,Contstr);
X			getnoctl ();
X			show ();
X		}
X		else
X		{
X			prinfo("saving ....");
X			if (*ptr == '\0')
X				ptr = Savefile;
X			else
X				Savefile = str_store(ptr);
X			if (save_art(s,Savefile,msg) != 0)
X				preinfo(msg);
X			else
X				prinfo(msg);
X		}
X	}
X	else
X		preinfo (No_msg);
X
X	if (Digest)
X		dig_ulist (dsave);
X}
X
X/*
X	basic page display routine.  erase screen and format current page
X*/
Xstatic
Xshow ()
X{
X	int i;
X	unsigned long mask;
X	char helpstr[40]; 
X
X	term_set (ERASE);
X	C_info = 0;
X	i = Cur_page - (Page.h.group)->pnum + 1;
X	if (Digest)
X		printf (DHFORMAT,Page.h.name);
X	else
X		printf (HFORMAT,Page.h.name,i,(Page.h.group)->pages);
X
X	mask = 1L << (i-1);
X	(Page.h.group)->pgshwn |= mask;
X	mask = 1;
X	for (--i; i > 0 && (mask & (Page.h.group)->pgshwn) != 0 ; --i)
X		mask <<= 1;
X	if (i <= 0)
X		(Page.h.group)->pgrd = Page.b[(Page.h.artnum)-1].art_id;
X
X	for (i=0; i < Page.h.artnum; ++i)
X	{
X		if (Digest)
X		{
X			printf(Aformat,Page.b[i].art_mark,ART_UNWRITTEN,Page.b[i].art_id);
X			printf("%s",Page.b[i].art_t);
X			continue;
X		}
X
X		if ((Page.h.group)->rdnum >= Page.b[i].art_id)
X			printf(Aformat,Page.b[i].art_mark,ART_WRITTEN,Page.b[i].art_id);
X		else
X			printf(Aformat,Page.b[i].art_mark,ART_UNWRITTEN,Page.b[i].art_id);
X		printf("%s",Page.b[i].art_t);
X	}
X
X	sprintf(helpstr,HELPFORM,Cxptoi[HELP]);
X	if (!Digest && ((Page.h.group)->flags & FLG_SUB) == 0)
X		prinfo ("%s, %s",Unsub_msg,helpstr);
X	else
X		prinfo (helpstr);
X}
X
X/*
X	update written status marks on screen
X*/
Xstatic
Xwr_show ()
X{
X	int i,row;
X	char c;
X
X	row = RECBIAS;
X	for (i=0; i < Page.h.artnum; ++i)
X	{
X		term_set (MOVE,WRCOL,row);
X		if ((Page.h.group)->rdnum >= Page.b[i].art_id)
X			c = ART_WRITTEN;
X		else
X			c = ART_UNWRITTEN;
X		printf("%c",c);
X		++row;
X	}
X}
X
X/*
X	obtain user input of group name, becomes current page if valid.
X	returns -1 or page number.  calling routine does the show, if needed
X*/
Xstatic
Xspec_group ()
X{
X	char nbuf [MAX_C + 1];
X	NODE *p, *hashfind();
X
X	user_str(nbuf,"Newsgroup ? ",1,"");
X
X	if (*nbuf == '\0' || (p = hashfind(nbuf)) == NULL)
X	{
X		preinfo ("Not a newsgroup");
X		return (-1);
X	}
X	if ((p->flags & FLG_PAGE) == 0)
X	{
X		if ((p->flags & FLG_SUB) == 0)
X		{
X			new_sub(p,FLG_SUB);
X			do_update("Not subscribed: resubscribed for next reading session");
X		}
X		else
X			prinfo ("No news for that group");
X		return (-1);
X	}
X	if ((p->flags & FLG_SUB) == 0)
X	{
X		new_sub(p,FLG_SUB);
X		do_update("Resubscribed");
X	}
X	find_page (p->pnum);
X	return (p->pnum);
X}
X
X/*
X	obtain user input with prompt p.  Optionally on info line.
X	handle erase and kill characters, suppresses leading
X	white space.  Use defstr as the editable default user input.
X	If on info line, cursor is not moved anywhere whe done, otherwise
X	a <CR><LF> is done after input.  Should be in raw mode to use
X	this routine.  Used from outside this source file so that we
X	only have to do erase / kill key stuff one place.
X*/
Xuser_str (s,p,iline,defstr)
Xchar *s;
Xchar *p;
Xint iline;
Xchar *defstr;
X{
X	int i,idx,len;
X	char c;
X
X	if (iline)
X	{
X 		prinfo ("%s%s",p,defstr);
X		idx = C_info;
X	}
X	else
X	{
X 		printf ("%s%s",p,defstr);
X		idx = strlen(p);
X	}
X
X	len = strlen(defstr);
X 	for (i=0; i < len; i++)
X 		s[i] = defstr[i];
X 
X 	for (i=len; idx < C_allow; ++i)
X	{
X		s[i] = getchar();
X		c = s[i] & CHMASK;
X		if (c == '\015' || c == '\012')
X			break;
X		if (c == Erasekey)
X		{
X			if (i > 0)
X			{
X				term_set (RUBSEQ);
X				i -= 2;
X				--idx;
X			}
X			else
X				i = -1;
X			continue;
X		}
X		if (c == Killkey)
X		{
X 			if (iline)
X 			{
X 				prinfo ("%s",p);
X 				idx = C_info;
X 			}
X 			else
X 			{
X 				printf ("\r%s",p);
X 				term_set(ZAP,strlen(p),idx);
X 				fflush(stdout);
X 				idx = strlen(p);
X 			}
X			i = -1;
X			continue;
X		}
X		/* no leading spaces */
X		if (c == ' ' && i == 0)
X		{
X			i = -1;
X			putchar('\07');
X			continue;
X		}
X		/* no controls */
X 		if (iscntrl(c))
X		{
X			--i;
X			putchar('\07');
X			continue;
X		}
X		++idx;
X		putchar (s[i]);
X#ifdef	MINIX
X		fflush(stdout);
X#endif
X	}
X
X	if (iline)
X		C_info = idx;
X	else
X		printf("\r\n");
X
X	s[i] = '\0';
X}
X/*
X	print something on the information line,
X	clearing any characters not overprinted.
X	preinfo includes reverse video and a bell for error messages.
X*/
Xpreinfo (s,a,b,c,d,e,f)
X{
X	int l;
X	char buf[RECLEN];
X
X	term_set (MOVE,0,INFOLINE);
X	putchar ('\07');
X	term_set (ONREVERSE);
X	sprintf (buf,s,a,b,c,d,e,f);
X	printf (" %s ",buf);
X	term_set (OFFREVERSE);
X	l = strlen(buf) + 2;
X	if (l < C_info)
X		term_set (ZAP,l,C_info);
X	C_info = l;
X	fflush(stdout);
X}
X
Xprinfo (s,a,b,c,d,e,f)
Xchar *s;
Xlong a,b,c,d,e,f;
X{
X	int l;
X	char buf[RECLEN];
X	term_set (MOVE,0,INFOLINE);
X	sprintf (buf,s,a,b,c,d,e,f);
X	printf ("%s",buf);
X	l = strlen(buf);
X	if (l < C_info)
X		term_set (ZAP,l,C_info);
X	C_info = l;
X	fflush(stdout);
X}
X
Xstatic
Xtot_list ()
X{
X	int i,max,len;
X	char c;
X	char ff[MAX_C+1];
X
X	term_set (ERASE);
X
X	for (max=i=0; i < Ncount; ++i)
X	{
X		if ((Newsorder[i])->pages == 0)
X			continue;
X		if ((len = strlen((Newsorder[i])->nd_name)) > max)
X			max = len;
X	}
X
X	sprintf (ff,"%%4d %%%ds: %%3d new %%3d updated\n",max);
X
X	for (len=i=0; i < Ncount; ++i)
X	{
X		if ((Newsorder[i])->pages == 0)
X			continue;
X		printf (ff, i, (Newsorder[i])->nd_name,
X				(Newsorder[i])->highnum - (Newsorder[i])->orgrd,
X				(Newsorder[i])->rdnum - (Newsorder[i])->orgrd);
X		++len;
X		if (len == L_allow && i < (Ncount-1))
X		{
X			printf("\nr - return, n - new group, other to continue ... ");
X			if ((c = getnoctl()) == 'r' || c == 'n')
X				break;
X			printf ("\n\n");
X			len = 0;
X		}
X	}
X	if (i >= Ncount)
X	{
X		printf("n - new group, other to return ... ");
X		c = getnoctl();
X	}
X
X	/* c will remain 'n' while user chooses bad newsgroups */
X	while (c == 'n')
X	{
X		printf("\n");
X		user_str(ff,"Newsgroup number ? ",0,"");
X		i = atoi(ff);
X 		if (i < 0 || i >= Ncount || (Newsorder[i])->pages == 0)
X		{
X 			printf("\nBad newsgroup number\n");
X 			printf("n - new group, other to return ... ");
X			c = getnoctl();
X			continue;
X 		}
X 		find_page((Newsorder[i])->pnum);
X		Crec = RECBIAS;
X		Highrec = Page.h.artnum + RECBIAS;
X		c = '\0';
X	}
X}
X
X/*
X** call vns_write if anything has changed, then wipe FLG_ECHG bits
X** also produce message(s)
X*/
Xstatic
Xdo_update(msg)
Xchar *msg;
X{
X	int i;
X
X	for (i=0; i < Ncount; ++i)
X		if(((Newsorder[i])->flags & FLG_ECHG) != 0)
X			break;
X	if (i < Ncount)
X	{
X		prinfo("Writing news status");
X		vns_write(Newsorder,Ncount);
X		for (i=0; i < Ncount; ++i)
X			(Newsorder[i])->flags &= ~FLG_ECHG;
X	}
X	prinfo(msg);
X}
X
X/*
X** set a new rdnum value.  If a change, set FLG_ECHG
X*/
Xstatic
Xnew_read(n,rd)
XNODE *n;
Xint rd;
X{
X	if (n->rdnum != rd)
X	{
X		n->rdnum = rd;
X		n->flags |= FLG_ECHG;
X	}
X}
X
X/*
X** set a new subscription bit.  bit argument is either 0 or FLG_SUB.
X*/
Xstatic
Xnew_sub(n,bit)
XNODE *n;
Xunsigned bit;
X{
X	/*
X	** since bit is 0 or FLG_SUB, we could get tricky with ^
X	** but this is clearer
X	*/
X	if (bit != 0 && (n->flags & FLG_SUB) == 0)
X		n->flags |= FLG_SUB|FLG_ECHG;
X	else
X	{
X		if (bit == 0 && (n->flags & FLG_SUB) != 0)
X		{
X			n->flags &= ~FLG_SUB;
X			n->flags |= FLG_ECHG;
X		}
X	}
X}
X
X/*
X** utility to mark articles.  depends on fact that article numbers are
X** in ascending order on page.
X*/
Xstatic
Xmarkstr (list,ckey)
Xchar *list;
Xchar ckey;
X{
X	char *tok;
X	int art, count;
X	BODY *b;
X
X	count = 1;
X	b = Page.b;
X	for (tok = strtok(list,List_sep); tok != NULL;
X					tok = strtok(NULL,List_sep))
X	{
X		art = atoi(tok);
X		while (count < Page.h.artnum && b->art_id < art)
X		{
X			++count;
X			++b;
X		}
X		if (b->art_id == art)
X		{
X			/*
X			** Can't call Marker inside digests
X			*/
X			if (Marker == NULL || Digest)
X			{
X				if (b->art_mark != ART_MARK)
X					b->art_mark = ART_MARK;
X				else
X					b->art_mark = ' ';
X			}
X			else
X			{
X				b->art_mark =
X					(*Marker)(Page.h.group, art,
X						b->art_mark, ckey);
X			}
X			term_set (MOVE, 0, count-1+RECBIAS);
X			printf ("%c\010",b->art_mark);
X		}
X	}
X}
END_OF_FILE
if test 21343 -ne `wc -c <'session.c'`; then
    echo shar: \"'session.c'\" unpacked with wrong size!
fi
# end of 'session.c'
fi
if test -f 'sig_set.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'sig_set.c'\"
else
echo shar: Extracting \"'sig_set.c'\" \(4537 characters\)
sed "s/^X//" >'sig_set.c' <<'END_OF_FILE'
X/*
X** vn news reader.
X**
X** sig_set.c - signal handler
X**
X** see copyright disclaimer / history in vn.c source file
X*/
X
X#include <stdio.h>
X
X#ifndef	MINIX
X/* why? */
X#include <sys/signal.h>
X#else
X#include <signal.h>
X#endif
X
X#include <sgtty.h>
X#include <setjmp.h>
X#include "tty.h"
X#include "config.h"
X#include "brk.h"
X#include "tune.h"
X#include "node.h"
X#include "page.h"
X
Xextern int L_allow;
Xextern char *Version;
Xextern char *Brk_fmt;
Xextern char *Vns_version;
X
Xstatic int Sigflag=BRK_INIT;	/* phase of interaction */
Xstatic FILE **Fpseek;		/* article reading file pointer pointer */
Xstatic int Foreground;
Xstatic jmp_buf Jumploc;		/* for BRK_SESS phase */
Xstatic int Need_restart = 0;
X
X/*
X	interrupt handler - unusual termination (longjmp and printex aborts)
X	if not abort, remember to reset signal trap
X	CAUTION - the passing of a jump buffer is a little dicey - assumes
X	type jump_buf is an array.
X
X	sigcatch and sig_set control a lot of i/o on stderr also, since
X	it is so intimately related to signal interaction.  Note that the
X	SIGTSTP action causes a "stopped on tty output" if raw terminal
X	mode is restored by tty_set(RESTORE).  We don't get it if we were
X	already cooked since tty_set avoids calling ioctl if it doesn't
X	have to.
X*/
Xstatic sigcatch (sig)
Xint sig;
X{
X	char buf [MAX_C+1];
X	int pgrp;
X
X	/* disable signal while processing it */
X	signal (sig,SIG_IGN);
X
X	switch (sig)
X	{
X	case SIGINT:
X	case SIGQUIT:
X		break;
X
X#ifdef JOBCONTROL
X	case SIGTSTP:
X		/* ignore SIGTTOU so we don't get stopped if [kc]sh grabs the tty */
X		signal(SIGTTOU, SIG_IGN);
X		tty_set (SAVEMODE);
X		term_set (MOVE,0,L_allow+RECBIAS-1);
X		printf ("\n");
X		Foreground = 0;
X		fflush (stdout);
X		fflush (stderr);
X		signal(SIGTTOU, SIG_DFL);
X
X		/* Send the TSTP signal to suspend our process group */
X		signal(SIGTSTP, SIG_DFL);
X		sigsetmask(0);
X		kill (0, SIGTSTP);
X
X		/* WE ARE NOW STOPPED */
X
X		/*
X				WELCOME BACK!
X				if terminals process group is ours, we are foregrounded again
X				and can turn newsgroup name printing back on
X			*/
X		tty_set (RESTORE);
X
X		/*
X		** Note concerning RESTART.  If in state BRK_IN, we simply
X		** set a flag to do it upon switch to state BRK_SESS - we
X		** don't want to send i/o to the terminal when we
X		** background during BRK_IN phase ("stopped on tty output")
X		*/
X		switch (Sigflag)
X		{
X		case BRK_SESS:
X			signal (SIGTSTP,sigcatch);
X			term_set (RESTART);
X			longjmp (Jumploc,1);
X		case BRK_IN:
X			Need_restart = 1;
X			ioctl (1,TIOCGPGRP,&pgrp);
X			if (pgrp == getpgrp(0))
X				Foreground = 1;
X			break;
X		default:
X			term_set (RESTART);
X			break;
X		}
X		signal (SIGTSTP,sigcatch);
X		return;
X#endif
X	default:
X		printex (Brk_fmt,sig);
X	}
X
X	/* QUIT and INTERRUPT signals */
X	switch (Sigflag)
X	{
X	case BRK_SESS:
X		/* if in session, ask if really a quit, do longjump if not */
X		term_set (ERASE);
X		tty_set (RAWMODE);
X		user_str (buf, BRK_PR, 1, "");
X		if (buf[0] == 'y')
X			printex (Brk_fmt,sig);
X		signal (sig,sigcatch);
X		longjmp (Jumploc,1);
X	case BRK_READ:
X		/* if reading seek file to end to abort page printing */
X		printf ("\n");
X		if (*Fpseek == NULL || fseek(*Fpseek,0L,2) < 0)
X			putchar ('\07');
X		break;
X	default:
X		printex (Brk_fmt,sig);
X	}
X	signal (sig,sigcatch);
X}
X
X/*
X	sig_set controls what will be done with a signal when picked up by
X	sigcatch.  fgprintf is included here to keep knowledge
X	of TSTP state localized.
X*/
X/* VARARGS */
Xsig_set (flag,dat)
Xint flag, *dat;
X{
X	int i, *xfer, pgrp;
X	if (Sigflag == BRK_INIT)
X	{
X		signal (SIGINT,sigcatch);
X		signal (SIGQUIT,sigcatch);
X		signal (SIGTERM,sigcatch);
X#ifdef JOBCONTROL
X		signal (SIGTSTP,sigcatch);
X		ioctl (1,TIOCGPGRP,&pgrp);
X		if (pgrp == getpgrp(0))
X		{
X			Foreground = 1;
X			fgprintf ("Visual News, %s(%s), reading:\n",
X					Version, Vns_version);
X		}
X		else
X			Foreground = 0;
X#else
X		Foreground = NOJOB_FG;
X#endif
X	}
X	switch (flag)
X	{
X	case BRK_IN:
X	case BRK_OUT:
X		Sigflag = flag;
X		break;
X	case BRK_READ:
X		if (Sigflag != BRK_SESS)
X			printex ("unexpected read state, sig_set\n");
X		Fpseek = (FILE **) dat;
X		Sigflag = BRK_READ;
X		break;
X	case BRK_SESS:
X		if (Need_restart)
X			term_set(RESTART);
X		xfer = (int *) Jumploc;
X		for (i=0; i < sizeof(Jumploc) / sizeof(int); ++i)
X			xfer[i] = dat[i];
X		Sigflag = BRK_SESS;
X		break;
X	case BRK_RFIN:
X		if (Sigflag != BRK_READ)
X			printex ("unexpected finish state, sig_set\n");
X		Sigflag = BRK_SESS;
X		break;
X	default:
X		printex ("bad state %d, sig_set\n",flag);
X	}
X}
X
Xfgprintf (fs,a,b,c,d,e)
Xchar *fs;
Xint a,b,c,d,e;
X{
X	if (Foreground)
X		fprintf (stderr,fs,a,b,c,d,e);
X	fflush (stderr);
X}
END_OF_FILE
if test 4537 -ne `wc -c <'sig_set.c'`; then
    echo shar: \"'sig_set.c'\" unpacked with wrong size!
fi
# end of 'sig_set.c'
fi
echo shar: End of shell archive.
exit 0
-- 
			"Zeta Microcomputer Software"
ACSnet:    nick@nswitgould.cs.uts.oz	nick@ultima.cs.uts.oz
UUCP:      ...!uunet!munnari!ultima.cs.uts.oz!nick
Fidonet:   Nick Andrew on 3:713/602 (Zeta)