[comp.sources.amiga] v91i007: BARN 2.01 - Usenet newsread for the Amiga, Part02/02

amiga-request@ab20.larc.nasa.gov (Amiga Sources/Binaries Moderator) (02/07/91)

Submitted-by: lordbah@amusing.bisco.kodak.COM (Jeff Van Epps)
Posting-number: Volume 91, Issue 007
Archive-name: news/barn-2.01/part02

#!/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 archive 2 (of 2)."
# Contents:  arn.c barn.doc
# Wrapped by tadguy@ab20 on Wed Feb  6 20:05:20 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'arn.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'arn.c'\"
else
echo shar: Extracting \"'arn.c'\" \(44397 characters\)
sed "s/^X//" >'arn.c' <<'END_OF_FILE'
X/*
X *	File Name:		arn.c
X *	Project:		BARN - Bah's Amiga ReadNews.
X *	Purpose:		Mainline code for Amiga News Reader.
X *	Functions:		main.
X *	Author:			Jeff Van Epps
X *	Created:		02 Sep 89
X *	Last Modified:	15 Jan 91
X *	Comments:
X *		barn [config_file]
X *
X *		config_file is optional, defaulting to "barn.config".
X *
X *	History:
X *		02 Sep 89/JVE	Created.
X *		03 Dec 89/JVE	Tries to cope with "Re: $x" subject lines when finding
X *						next article to read and when killing articles.
X *		16 Dec 89/JVE	Fixed a bug in the last mark in UpdateReadList if the
X *						last article was still unread.
X *		30 Dec 89/JVE	Implemented kill-list checking.
X *		05 Sep 90/JVE	Removed volume references -- run from base directory
X *						of news articles.
X *		12 Sep 90/JVE	Added parm to Paginate() to account for headers
X *						already printed.
X *		17 Oct 90/JVE	Added '-' option to go back to previous article.
X *						Made Paginate() pass up the 'K' option.
X *						Added help option.  Added Reply and Followup.
X *		19 Oct 90/JVE	UNIX (Sun) port.  Curses, readdir, etc.
X *		20 Oct 90/JVE	Added configuration file stuff.
X *		22 Oct 90/JVE	Use regular expressions for subject following and
X *						killing.
X *		24 Oct 90/JVE	Added scan subjects command "=".
X *		05 Nov 90/JVE	New opendir() and stat() functions from SAS/C used.
X *		10 Nov 90/JVE	Make sure articles shown in order of posting.
X *		11 Nov 90/JVE	Added LINES/COLS to config file.
X *		14 Nov 90/JVE	Added "m" - mark as unread, and use of "n" (next)
X *						within pager.  Added CR/NL to print one more line
X *						in pager, scanner.
X *		16 Nov 90/JVE	Scanner fix -- used to always show first article in
X *						group even if already read.
X *		22 Nov 90/JVE	End page at form feed.
X *		25 Nov 90/JVE	Config option to clear screen for each new page.
X *		28 Nov 90/JVE	Fix to MakePattern to escape backslash in regexp.
X *						Changed getcmd() to take array of strings for "help"
X *						to escape compiler string length limitation.  Changed
X *						MakePattern() to allow characters between 'Re:' and
X *						'text' being matched.
X *		05 Jan 91/JVE	Fixed line-wrap calculations in Paginate().  Put last
X *						line of previous page at top of next page when in
X *						NO_SCROLL mode.  Put 'subject' in article_info as an
X *						an optimization, so header list didn't have to be
X *						searched constantly.  Added '#' command to go to
X *						specific article by number.
X *		06 Jan 91/JVE	Added back-page capability in Paginate().  Changed
X *						name to "barn" to avoid conflict with another "arn"
X *						in the Amiga world which performs the same function.
X *						Changed getcmd() to accept one list of commands which
X *						gets displayed to the user and one which doesn't.
X *						Added "a - About BARN" command.
X *		15 Jan 91/JVE	Avoid regexec() on NULL subject.
X */
X
X# include	<stdio.h>
X# include	<stdlib.h>
X# include	<string.h>
X# include	<regexp.h>
X
X# ifdef sun
X# include	<dirent.h>	/* opendir & friends */
X# else  /* not sun */
X# include	<sys/types.h>
X# include	<sys/dir.h>
X# endif /* sun */
X
X# include	<sys/stat.h>
X
X# include	"standard.h"
X# include	"article.h"
X# include	"ng.h"
X# include	"kill.h"
X# include	"configure.h"
X# include	"variables.h"
X# include	"reply.h"
X# include	"screenstuff.h"
X
X# define	BARN_VERSION	"BARN version 2.01"
X
X# define	DEFAULT_CONFIG	"barn.config"	/* ARN default configuration file */
X# define	DEFAULT_NEWSRC	".newsrc"		/* default .newsrc filename */
X# define	DEFAULT_KILL	"KILL"			/* default name of kill files */
X# define	KILL			"KILL"			/* KILL filenames start with this */
X# define	REPLY_PATTERN	"^[RrEe: ]*"		/* regex for reply articles */
X# define	REPLY_REMOVER	"^[RrEe: ]*(.*)$"	/* regex for removing Re: */
X
X# ifdef sun
X
X/* LINES and COLS are variables set by curses. */
X
X# define	Lines		LINES
X# define	Columns		COLS
X
X# endif /* sun */
X
X# ifdef sun
X
X# define	COPY_CMD		"cp"
X# define	raw( x )		cbreak()
X# define	cooked( x )		nocbreak()
X# define	printf			printw
X# define	gets( x )		getstr( (x) )
X# define	getchar()		getch()
X
X# else  /* not sun */
X
X# define	COPY_CMD		"copy"
X
X# endif /* sun */
X
Xtypedef struct {
X	long			num;		/* article number */
X	ARTICLE_INFO	*pointer;	/* article structure */
X	} SORT_INFO;
X
Xstatic char			*currentdir;	/* current directory on startup */
X
X# ifndef sun
X
Xstatic int			Lines, Columns;	/* terminal characteristics */
X
X# endif
X
X/*
X *	Local function which creates the string form and internal form of a
X *	regular expression.  The string form should have all regexp-special
X *	characters escaped.
X */
X
Xstruct regexp		*MakePattern( char *pattern, char *original );
X
Xvoid				ScanSubjects( ARTICLE_INFO *arts );
X
X
Xvoid main( argc, argv )
X
Xint		argc;
Xchar	**argv;
X
X{
XNG_INFO			*alreadyread;	/* list of newsgroups/articles already read */
XNG_INFO			*ng;			/* ptr for newsgroup list traversal */
XKILL_INFO		*globalkill;	/* entries from global KILL file */
Xchar			*config_file;	/* name of configuration file to use */
Xchar			*newsrc_file;	/* name of .newsrc file */
Xchar			*global_kill_file;	/* name of global kill file */
Xvoid			Startup(), CheckConfiguration();
Xextern char		*getcwd();
X
Xcurrentdir = getcwd( NULLP( char ), BUFSIZ );
Xif ( argc < 2 )
X	config_file = DEFAULT_CONFIG;
Xelse
X	config_file = argv[1];
XConfigure( config_file );
XCheckConfiguration();
XStartup();
X
X/*
X *	Find all articles we've already read from the .newsrc file.
X */
X
Xif ( ( newsrc_file = GetVar( VAR_NEWSRC ) ) == NULL )
X	newsrc_file = DEFAULT_NEWSRC;
Xalreadyread = GetNewsRC( newsrc_file );
Xif ( ( global_kill_file = GetVar( VAR_KILL ) ) == NULL )
X	global_kill_file = DEFAULT_KILL;
Xglobalkill = GetKillList( global_kill_file );
X
X/*
X *	For each newsgroup in the .newsrc, handle the newsgroup.
X *	(Code needs to be added to notice newsgroups not in .newsrc)
X */
X
Xfor ( ng = alreadyread; ng != NULLP( NG_INFO ); ng = ng->next )
X	{
X	chdir( currentdir );
X	if ( ReadNewsgroup( ng, &globalkill ) )
X		break;
X	}
X
Xchdir( currentdir );
XPutNewsRC( newsrc_file, alreadyread );
XPutKillList( global_kill_file, globalkill );
X
XDestroyNGList( alreadyread );
XDestroyKillList( globalkill );
X
Xcooked( stdin );
X
X# ifdef sun
Xendwin();
X# endif
X}
X
X
X/****************************************************************************/
X/*	FUNCTION:	AboutBARN													*/
X/*																			*/
X/*	PURPOSE:	Print version of BARN.										*/
X/*																			*/
X/*	INPUT PARAMETERS:														*/
X/*		NAME		I/O		DESCRIPTION										*/
X/*		----		---		-----------										*/
X/*																			*/
X/*	RETURNS: none															*/
X/*																			*/
X/*	COMMENTS:																*/
X/*																			*/
X/*	HISTORY:																*/
X/*		1.	06 Jan 91		Created.										*/
X/*																			*/
X/****************************************************************************/
X
Xvoid AboutBARN()
X
X{
Xprintf( "\n\n%s written by Jeff Van Epps (aka Lord Bah)\n\n", BARN_VERSION );
Xprintf( "raw.c written by Chuck McManis\n" );
Xprintf( "sendpacket.c written by Phil Lindsay, Carolyn Scheppner, and Andy Finkel\n" );
Xprintf( "Uses regexp library written by Henry Spencer, copyrighted by the\n" );
Xprintf( "University of Toronto in 1986.\n" );
Xprintf( "\nExecutable freely redistributable with the restriction that\n" );
Xprintf( "the authors are not responsible for consequences of use.\n" );
Xprintf( "\nAddress comments to amusing!jeffv@bisco.kodak.com or lordbah@cup.portal.com.\n" );
Xprintf( "\nHit any key to continue" );
X(void) getchar();
Xprintf( "\n" );
X}
X
X
X/****************************************************************************/
X/*	FUNCTION:	Startup														*/
X/*																			*/
X/*	PURPOSE:	Get ready to run ARN.										*/
X/*																			*/
X/*	INPUT PARAMETERS:														*/
X/*		NAME		I/O		DESCRIPTION										*/
X/*		----		---		-----------										*/
X/*																			*/
X/*	RETURNS:																*/
X/*																			*/
X/*	COMMENTS:																*/
X/*																			*/
X/*	HISTORY:																*/
X/*		1.	05 Sep 89		Created.										*/
X/*																			*/
X/****************************************************************************/
X
Xvoid Startup()
X
X{
X# ifdef sun
X(void) initscr();
X# endif
Xraw( stdin );
X}
X
X
X/****************************************************************************/
X/*	FUNCTION:	CheckConfiguration											*/
X/*																			*/
X/*	PURPOSE:	Check to see that user has defined required variables in	*/
X/*				the configuration file.										*/
X/*																			*/
X/*	INPUT PARAMETERS:														*/
X/*		NAME		I/O		DESCRIPTION										*/
X/*		----		---		-----------										*/
X/*																			*/
X/*	RETURNS: none															*/
X/*																			*/
X/*	COMMENTS:																*/
X/*		Exits if any of the required variables are not set.					*/
X/*																			*/
X/*	HISTORY:																*/
X/*		1.	20 Oct 90		Created.										*/
X/*		2.	11 Nov 90		Added LINES & COLS.								*/
X/*																			*/
X/****************************************************************************/
X
Xstatic void CheckConfiguration()
X
X{
Xchar		*tmp;
X
Xif ( GetVar( VAR_USER ) == NULL || GetVar( VAR_NODE ) == NULL ||
X  GetVar( VAR_DOMAIN ) == NULL || GetVar( VAR_NAME ) == NULL )
X  	{
X  	fprintf( stderr, "Must define \"%s\", \"%s\", \"%s\", and \"%s\" in configuration file.\n",
X  	  VAR_USER, VAR_NODE, VAR_DOMAIN, VAR_NAME );
X  	exit( 1 );
X  	}
X
X# ifndef sun
X
Xif ( ( ( tmp = GetVar( VAR_LINES ) ) == NULL ) || 
X  ( Lines = atoi( tmp ) ) < 10 ||
X  Lines > 48 )
X  	{
X  	fprintf( stderr, "Missing/unreasonable '%s' value in config file.\n",
X  	  VAR_LINES );
X  	exit( 1 );
X  	}
Xif ( ( ( tmp = GetVar( VAR_COLS ) ) == NULL ) || 
X  ( Columns = atoi( tmp ) ) < 10 ||
X  Columns > 133 )
X  	{
X  	fprintf( stderr, "Missing/unreasonable '%s' value in config file.\n",
X  	  VAR_COLS );
X  	exit( 1 );
X  	}
X
X# endif
X
X}
X
X
X/****************************************************************************/
X/*	FUNCTION:	ReadNewsgroup												*/
X/*																			*/
X/*	PURPOSE:	Allow user to read articles within a newsgroup.				*/
X/*																			*/
X/*	INPUT PARAMETERS:														*/
X/*		NAME		I/O		DESCRIPTION										*/
X/*		----		---		-----------										*/
X/*		ng			I/O		List of articles read within newsgroup.			*/
X/*		gkill		I/O		Global kill list.								*/
X/*																			*/
X/*	RETURNS:																*/
X/*		TRUE				If user wants to quit.							*/
X/*		FALSE				Otherwise.										*/
X/*																			*/
X/*	COMMENTS:																*/
X/*																			*/
X/*	HISTORY:																*/
X/*		1.	04 Sep 89		Created.										*/
X/*		2.	06 Jan 91		Added "a" - About BARN command.					*/
X/*																			*/
X/****************************************************************************/
X
X# define	READ_CMDS	"acnqy "
Xstatic char *READ_HELP[] = {
X"<space>  Yes, read this newsgroup.",
X"   a     About BARN.",
X"   c     Catch up.  Mark everything in this newsgroup as read.",
X"   n     No, don't read this newsgroup.",
X"   q     Quit ARN.",
X"   y     Yes, read this newsgroup.",
X(char *) NULL
X};
X
XReadNewsgroup( ng, gkill )
X
XNG_INFO		*ng;
XKILL_INFO	**gkill;
X
X{
XKILL_INFO		*lkill;		/* local (within newsgroup) kill list */
XDIR				*dir;		/* pointer to directory structure */
X# ifdef sun
Xstruct dirent	*info;		/* pointer to each file's info */
X# else  /* not sun, Amiga */
Xstruct direct	*info;
X# endif /* sun */
Xstruct stat		stats;		/* file system information about file */
XARTICLE_INFO	*todo;		/* articles to be read */
XARTICLE_INFO	*art;		/* info on eligible article */
XARTICLE_INFO	**where;	/* where to link in next eligible article */
XARTICLE_INFO	*tmp;
Xchar			*fn;		/* name of file currently being checked */
Xint				narticles;	/* number of unread articles in newsgroup */
Xchar			*kill_file;	/* name of local kill file */
XARTICLE_INFO	*CheckArticle();
Xint				rc = FALSE, done = FALSE;
Xvoid			ReadArticles(), SortArticles(), UpdateReadList();
X
Xif ( chdir( ng->name ) != 0 )
X	fprintf( stderr, "Newsgroup directory %s missing!\n", ng->name );
Xelse
X	{
X	printf( "Checking %s ...\n", ng -> name );
X	if ( ( kill_file = GetVar( VAR_KILL ) ) == NULL )
X		kill_file = DEFAULT_KILL;
X	lkill = GetKillList( kill_file );
X	/*
X	 *	For each file in directory, check vs. already read (ng), check
X	 *	vs. global kill (gkill), check vs. local kill (lkill). If killed,
X	 *	add to already read (ng). If not read and not killed, put on
X	 *	unread list (todo).
X	 */
X# ifdef sun
X	if ( ( ( dir = opendir( "." ) ) == NULL ) || 
X# else  /* Amiga */
X	if ( ( ( dir = opendir( "" ) ) == NULL ) || 
X# endif
X	  ( info = readdir( dir ) ) == NULL )
X		printf( "%s is empty.\n", ng->name );
X	else
X		{
X		todo = NULLP( ARTICLE_INFO );
X		where = &todo;
X		narticles = 0;
X		do {
X			/*
X			 *	If filename starts with "KILL", this is a KILL file, not a
X			 *	news article.  Don't open anything but regular files.
X			 */
X			if ( strncmp( fn = info -> d_name, KILL, strlen(KILL) ) != 0 &&
X			  stat( fn, &stats ) == 0 && ( stats.st_mode & S_IFREG ) )
X				if ( art = CheckArticle( fn, ng->markers, *gkill, lkill ) )
X					{
X					*where = art;
X					where = & (*where)->next;
X					++narticles;
X					}
X			} while ( ( info = readdir( dir ) ) != NULL );
X		closedir( dir );
X		/* talk to user */
X		if ( narticles == 0 )
X			printf( "%s has no unread articles.\n", ng->name );
X		else while ( !done )
X			{
X			printf( "%s has %d unread articles. Read now? ", ng->name, narticles );
X			switch ( getcmd( READ_CMDS, READ_HELP, "" ) ) {
X				case ' ':
X				case 'y':
X					SortArticles( &todo, narticles );
X					for(art = todo; art != NULLP(ARTICLE_INFO); art = art->next)
X						if ( art->beenread )
X							--narticles;
X					ReadArticles( ng, todo, narticles, gkill, &lkill );
X					printf( "\n" );
X					UpdateReadList( ng, todo );
X					done = TRUE;
X					break;
X				case 'q':
X					rc = TRUE;
X					printf( "\n" );
X					done = TRUE;
X					break;
X				case 'c':	/* catch up by marking all articles read */
X					printf( "\n" );
X					for ( tmp = todo; tmp != NULLP( ARTICLE_INFO ); tmp = tmp->next )
X						tmp->beenread = TRUE;
X					UpdateReadList( ng, todo );
X					done = TRUE;
X					break;
X				case 'a':
X					AboutBARN();
X					break;
X				case 'n':	/* don't read now, just goto cleanup below */
X					printf( "\n" );
X					done = TRUE;
X					break;
X				default:
X					break;
X				}
X			}
X		/* kill any remaining todo list */
X		for ( ; todo != NULLP( ARTICLE_INFO ); todo = tmp )
X			{
X			tmp = todo->next;
X			DestroyArticle( todo );
X			}
X		}
X	/*
X	 *	Write the local kill file to disk in case we changed it.
X	 */
X	PutKillList( kill_file, lkill );
X	DestroyKillList( lkill );
X	}
Xreturn rc;
X}
X
X
X/****************************************************************************/
X/*	FUNCTION:	CheckArticle												*/
X/*																			*/
X/*	PURPOSE:	Check readability of an article.							*/
X/*																			*/
X/*	INPUT PARAMETERS:														*/
X/*		NAME		I/O		DESCRIPTION										*/
X/*		----		---		-----------										*/
X/*		filename	 I		Name of file containing article to be checked.	*/
X/*		markers		 I		Pointer to list of articles already read.		*/
X/*		gkill		 I		Global kill list.								*/
X/*		lkill		 I		Local kill list.								*/
X/*																			*/
X/*	RETURNS:																*/
X/*		(ARTICLE_INFO *)	Pointer to article, if readable.				*/
X/*		(NULL)				If not readable.								*/
X/*																			*/
X/*	COMMENTS:																*/
X/*																			*/
X/*	HISTORY:																*/
X/*		1.	04 Sep 89		Created.										*/
X/*		2.	30 Dec 89		Implemented kill-list checking.					*/
X/*		3.	22 Oct 90		Changed from string compare to regular 			*/
X/*							expression match.								*/
X/*		4.	24 Oct 90		Tells what it killed and why.					*/
X/*																			*/
X/****************************************************************************/
X
XARTICLE_INFO	*CheckArticle( filename, markers, gkill, lkill )
X
Xchar		*filename;
XMARKER		*markers;
XKILL_INFO	*gkill, *lkill;
X
X{
Xlong			article_number;
XARTICLE_INFO	*art;
XHEADER_INFO		*hdr;
XKILL_INFO		*kp;
Xint				done = FALSE;
X
Xarticle_number = atol( filename );
Xif ( NumberCovered( markers, article_number ) )
X	return NULLP( ARTICLE_INFO );
Xif ( ( art = ParseArticle( filename ) ) == NULL )
X	return art;
X
X/*
X *	Check kill lists.
X */
X
Xfor ( hdr = art->headers; hdr != NULLP(HEADER_INFO) && !done; hdr = hdr->next )
X	{
X	for ( kp = gkill; kp != NULLP( KILL_INFO ) && !done; kp = kp->next )
X		if ( strcmp( hdr->fieldname, kp->fieldname ) == 0 &&
X			 regexec( kp -> pattern, hdr -> fieldvalue ) == 1 )
X			 	{
X			 	done = art->beenread = TRUE;
X			 	printf( "Killed %ld, %s: %s\n", art -> number,
X			 	  hdr -> fieldname, hdr -> fieldvalue );
X			 	}
X	for ( kp = lkill; kp != NULLP( KILL_INFO ) && !done; kp = kp->next )
X		if ( strcmp( hdr->fieldname, kp->fieldname ) == 0 &&
X			 regexec( kp -> pattern, hdr -> fieldvalue ) == 1 )
X			 	{
X			 	done = art->beenread = TRUE;
X			 	printf( "Killed %ld, %s: %s\n", art -> number,
X			 	  hdr -> fieldname, hdr -> fieldvalue );
X			 	}
X	}
Xreturn art;
X}
X
X
X/****************************************************************************/
X/*	FUNCTION:	FindNextArticle												*/
X/*																			*/
X/*	PURPOSE:	Find next article on same subject thread.					*/
X/*																			*/
X/*	INPUT PARAMETERS:														*/
X/*		NAME		I/O		DESCRIPTION										*/
X/*		----		---		-----------										*/
X/*		current		 I		Last article displayed.							*/
X/*		sub			 I		Subject line of last article.					*/
X/*		found		 O		Indicate whether or not we found another art.	*/
X/*		top			 I		Head of list of articles.						*/
X/*																			*/
X/*	RETURNS:																*/
X/*		(ARTICLE_INFO *)	next article on subject thread.					*/
X/*																			*/
X/*	COMMENTS:																*/
X/*		Returns "current" if no match is found.								*/
X/*																			*/
X/*	HISTORY:																*/
X/*		1.	14 Nov 90		Created.										*/
X/*																			*/
X/****************************************************************************/
X
XARTICLE_INFO	*FindNextArticle( current, sub, found, top )
X
XARTICLE_INFO	*current;
Xchar			*sub;
Xint				*found;
XARTICLE_INFO	*top;
X
X{
XARTICLE_INFO		*p;
Xchar				temp[MAXLINE];
Xstruct regexp		*subre;		/* regular expression pattern for subj match */
X
X*found = FALSE;
Xif ( ( subre = MakePattern( temp, sub ) ) == NULL )
X	fprintf( stderr, "Can't compile regex '%s'\n", temp );
Xfor ( p = current->next; p != NULLP( ARTICLE_INFO ) && p != current; )
X	{
X	if ( ! p->beenread )
X		if ( p -> subject )
X			if ( regexec( subre, p -> subject ) == 1 )
X				{
X				current = p;
X				*found = TRUE;
X				}
X	if ( *found )
X		break;
X	if ( ( p = p->next ) == NULLP( ARTICLE_INFO ) )
X		p = top;
X	}
Xif ( subre != NULL )
X	free( (char *) subre );
Xreturn p;
X}
X
X/****************************************************************************/
X/*	FUNCTION:	ReadArticles												*/
X/*																			*/
X/*	PURPOSE:	Allow user to read articles within a newsgroup.				*/
X/*																			*/
X/*	INPUT PARAMETERS:														*/
X/*		NAME		I/O		DESCRIPTION										*/
X/*		----		---		-----------										*/
X/*		ng			I/O		List of articles read.							*/
X/*		todo		I/O		List of articles to be read.					*/
X/*		narticles	 I		Number of unread articles.						*/
X/*		gkill		I/O		Global kill list.								*/
X/*		lkill		I/O		Local kill list.								*/
X/*																			*/
X/*	RETURNS:																*/
X/*																			*/
X/*	COMMENTS:																*/
X/*																			*/
X/*	HISTORY:																*/
X/*		1.	04 Sep 89		Created.										*/
X/*		2.	17 Oct 90		Added '-' command to go to previous article.	*/
X/*		3.	10 Nov 90		Find next art from beginning if not following	*/
X/*							Subject thread.  Makes sure articles get shown	*/
X/*							in order of posting.							*/
X/*		4.	28 Nov 90		Added "/" to let user specify Subject to match.	*/
X/*		5.	05 Jan 91		Added "#" to go to specific article number.		*/
X/*		6.	06 Jan 91		Added "a" command for About BARN information.	*/
X/*																			*/
X/****************************************************************************/
X
X# define	CMDS		"acFfjKkmnqRrwy-=/# "
Xstatic char *CMDS_HELP[] = {
X"   a     About BARN.",
X"   c     Catch up.  Mark all articles in this newsgroup as read.",
X"   Ff    Followup, 'F' includes current article.",
X"   Kk    Kill articles with this Subject, 'K' adds to KILL file.",
X"   m     Mark this article as unread.",
X"   n,j   Mark this article as read.",
X"   q     Quit this newsgroup.",
X"   Rr    Reply via mail, 'R' includes current article.",
X"   w     Write this article.  Prompts for filename.",
X" y,<sp>  Read this article.",
X"   -     Go to previous article, marking it as unread.",
X"   =     Scan subjects.",
X"   /     Find next article with subject entered.",
X"   #     Go to numbered article.",
X(char *) NULL
X};
X
Xvoid	ReadArticles( ng, todo, narticles, gkill, lkill )
X
XNG_INFO				*ng;
XARTICLE_INFO		*todo;
Xint					narticles;
XKILL_INFO			**gkill, **lkill;
X
X{
Xint					cmd;		/* user's command */
XARTICLE_INFO		*current;	/* article currently being examined */
XARTICLE_INFO		*previous = NULLP( ARTICLE_INFO );	/* last art. examined */
XARTICLE_INFO		*last;		/* used to track previous */
XARTICLE_INFO		*p;
XKILL_INFO			*tmp;
Xchar				temp[BUFSIZ], buf[BUFSIZ], *sub, followsub[BUFSIZ];
Xint					readit;		/* did article get read? */
Xint					found;		/* found another article with same subject? */
Xint					follow = FALSE;	/* following subject thread? */
Xint					lines;		/* lines used on screen for header */
Xstruct regexp		*subre;		/* regular expression pattern for subj match */
Xvoid				DisplayHeaders();
XARTICLE_INFO		*FindNumberedArticle();
X
Xcurrent = todo;
Xwhile ( narticles > 0 )
X	{
X	while ( current->beenread )
X		if ( ( current = current->next ) == NULLP( ARTICLE_INFO ) )
X			current = todo;
X	Clear_Screen;
X	printf( "Article %ld (%d more) in %s\n", current->number, narticles - 1, ng->name );
X	lines = 1;
X	DisplayHeaders( current, &lines, &sub );
X	printf( "\nRead? " );
X	++lines;
X	readit = FALSE;
X	last = current;
X	if ( ( cmd = getcmd( CMDS, CMDS_HELP, "" ) ) == ' ' || cmd == 'y' )
X		{
X		cmd = Paginate( current, lines );
X		readit = TRUE;
X		}
X	found = FALSE;
X	switch ( cmd ) {
X		case ' ':
X		case 'y':
X			current->beenread = TRUE;
X			if ( --narticles == 0 )
X				break;
X			/*
X			 *	Find next article with same subject.
X			 */
X			if ( !follow )
X				strcpy( followsub, sub );
X			current = FindNextArticle( current, followsub, &found, todo );
X			follow = found;
X			break;
X		case 'a':
X			AboutBARN();
X			found = TRUE;	/* don't let us go to top of newsgroup */
X			break;
X		case '/':
X			cooked( stdin );
X			printf( "/" );
X			gets( buf );
X			current = FindNextArticle( current, buf, &found, todo );
X			if ( follow = found )
X				strcpy( followsub, buf );
X			else
X				printf( "Not found.\n" );
X			raw( stdin );
X			break;
X		case '#':
X			cooked( stdin );
X			printf( "#" );
X			gets( buf );
X			current = FindNumberedArticle( todo, (long) atol( buf ) );
X			if ( current == NULLP( ARTICLE_INFO ) )
X				{
X				printf( "Not found.\n" );
X				current = last;
X				}
X			else if ( current -> beenread )
X				{
X				current -> beenread = FALSE;
X				++narticles;
X				}
X			raw( stdin );
X			found = TRUE;	/* so we don't start at top of newsgroup */
X			break;
X		case 'k':
X		case 'K':
X			/* kill */
X			current->beenread = TRUE;
X			--narticles;
X			printf( "\nKilling %s ...\n", sub );
X			if ( ( subre = MakePattern( temp, sub ) ) == NULL )
X				fprintf( stderr, "Can't compile regex '%s'\n", temp );
X			for ( p = current->next; p != NULLP( ARTICLE_INFO ) && p != current; )
X				{
X				if ( ! p->beenread )
X					if ( p -> subject && regexec( subre, p -> subject ) == 1 )
X						{
X						p->beenread = TRUE;
X						--narticles;
X						}
X				if ( ( p = p->next ) == NULLP( ARTICLE_INFO ) )
X					p = todo;
X				}
X			if ( cmd == 'K' )
X				{
X				printf( "Adding to newsgroup KILL file..." );
X				tmp = (KILL_INFO *) malloc( sizeof( KILL_INFO ) );
X				tmp->fieldname = strdup( HDR_SUBJECT );
X				tmp->fieldvalue = strdup( temp );
X				tmp->pattern = subre;
X				tmp->next = *lkill;
X				*lkill = tmp;
X				printf( "Done\n" );
X				}
X			else if ( subre != NULL )
X				free( (char *) subre );
X			follow = FALSE;
X			break;
X		case 'n':
X		case 'j':
X			/*
X			 *	Mark this article as read and move on to the next one with
X			 *	the same subject.
X			 */
X			current->beenread = TRUE;
X			--narticles;
X			/* FALL THROUGH */
X		case 'm':
X			/*
X			 *	"Mark as unread".  This simply goes on to the next unread
X			 *	article without marking this one as read.  Better marking
X			 *	routines are possible.
X			 */
X			if ( !follow )
X				strcpy( followsub, sub );
X			current = FindNextArticle( current, followsub, &found, todo );
X			follow = found;
X			if ( !found && cmd == 'm' )
X				{
X				if ( ( current = current -> next ) == NULLP( ARTICLE_INFO ) )
X					current = todo;
X				}
X			else if ( !found )
X				current = todo;
X			break;
X		case 'q':
X			if ( readit )
X				current->beenread = TRUE;
X			narticles = 0;		/* break out of while loop */
X			break;
X		case 'c':		/* catch up by marking all articles read */
X			for ( current = todo; narticles > 0; current = current->next )
X				{
X				if ( ! current->beenread )
X					--narticles;
X				current->beenread = TRUE;
X				}
X			break;
X		case 'w':		/* write article to top directory */
X			if ( readit )
X				{
X				current->beenread = TRUE;
X				--narticles;
X				}
X			cooked( stdin );
X			printf( "\nFilename: " );
X			gets( buf );
X			raw( stdin );
X			sprintf( temp, "%s %ld %s/%s", COPY_CMD, current->number, 
X				currentdir, buf );
X			system( temp );
X			break;
X		case 'r':
X			Reply( current, FALSE );
X			break;
X		case 'R':
X			Reply( current, TRUE );
X			break;
X		case 'f':
X			Followup( ng -> name, current, FALSE );
X			break;
X		case 'F':
X			Followup( ng -> name, current, TRUE );
X			break;
X		case '-':
X			if ( previous != NULL )
X				{
X				current = previous;
X				if ( current -> beenread )
X					{
X					current -> beenread = FALSE;
X					++narticles;
X					}
X				}
X			break;
X		case '=':	/* subject scan */
X			ScanSubjects( todo );
X			break;
X		default:
X			break;
X		}
X	previous = last;
X	if ( !found )
X		current = todo;		/* if not following thread, start at top */
X	}
X}
X
X
X/****************************************************************************/
X/*	FUNCTION:	DisplayHeaders												*/
X/*																			*/
X/*	PURPOSE:	Print interesting article headers.							*/
X/*																			*/
X/*	INPUT PARAMETERS:														*/
X/*		NAME		I/O		DESCRIPTION										*/
X/*		----		---		-----------										*/
X/*		current		 I		Pointer to current article.						*/
X/*		lines		 O		Pointer to count of lines printed on screen.	*/
X/*		sub			 O		Pointer to current subject pointer.				*/
X/*																			*/
X/*	RETURNS: none															*/
X/*																			*/
X/*	COMMENTS:																*/
X/*		Prints all headers stored in the articles header list, keeping		*/
X/*		track of the number of lines printed on the screen and returning	*/
X/*		that in 'lines'.  When the 'Subject' header is encountered, a		*/
X/*		pointer to its value is stored in 'sub'.							*/
X/*																			*/
X/*	HISTORY:																*/
X/*		1.	05 Jan 91		Factored out of ReadArticles().					*/
X/*																			*/
X/****************************************************************************/
X
Xvoid DisplayHeaders( current, lines, sub )
X
XARTICLE_INFO	*current;
Xint				*lines;
Xchar			**sub;
X
X{
XHEADER_INFO		*ptr;
X
Xfor ( ptr = current->headers; ptr != NULLP( HEADER_INFO ); ptr = ptr->next )
X	{
X	++*lines;
X	if ( strcmp( ptr->fieldname, HDR_SUBJECT ) == 0 )
X		{
X		*sub = ptr->fieldvalue;
X		printf( "%s: ", ptr -> fieldname );
X		UL_ON;
X		printf( "%s", *sub );
X		UL_OFF;
X		printf( "\n" );
X		if ( strlen( ptr->fieldname ) + strlen( *sub ) + 2 > Columns )
X			++*lines;
X		}
X	else
X		{
X		printf( "%s: %s\n", ptr->fieldname, ptr->fieldvalue );
X		if ( strlen( ptr->fieldname ) + strlen( ptr->fieldvalue ) + 2 > Columns )
X			++*lines;
X		}
X	}
X}
X
X
X/****************************************************************************/
X/*	FUNCTION:	FindNumberedArticle											*/
X/*																			*/
X/*	PURPOSE:	Return pointer to article identified by given number.		*/
X/*																			*/
X/*	INPUT PARAMETERS:														*/
X/*		NAME		I/O		DESCRIPTION										*/
X/*		----		---		-----------										*/
X/*		head		 I		Pointer to head of article list.				*/
X/*		number		 I		Number of article to find.						*/
X/*																			*/
X/*	RETURNS:																*/
X/*		NULL				No such numbered article.						*/
X/*		ARTICLE_INFO *ptr	Pointer to numbered article.					*/
X/*																			*/
X/*	COMMENTS:																*/
X/*																			*/
X/*	HISTORY:																*/
X/*		1.	05 Jan 91		Created.										*/
X/*																			*/
X/****************************************************************************/
X
XARTICLE_INFO	*FindNumberedArticle( head, number )
X
XARTICLE_INFO	*head;
Xlong			number;
X
X{
XARTICLE_INFO	*p;
X
Xfor ( p = head; p != NULLP( ARTICLE_INFO ); p = p -> next )
X	if ( p -> number == number )
X		return p;
X	else if ( p -> number > number )
X		break;
Xreturn NULLP( ARTICLE_INFO );
X}
X
X/****************************************************************************/
X/*	FUNCTION:	SortArticles												*/
X/*																			*/
X/*	PURPOSE:	Sort article list by article number.						*/
X/*																			*/
X/*	INPUT PARAMETERS:														*/
X/*		NAME		I/O		DESCRIPTION										*/
X/*		----		---		-----------										*/
X/*		arts		I/O		Pointer to list of articles.					*/
X/*		narticles	 I		Number of articles in list.						*/
X/*																			*/
X/*	RETURNS: none															*/
X/*																			*/
X/*	COMMENTS:																*/
X/*		*arts is modified.													*/
X/*																			*/
X/*	HISTORY:																*/
X/*		1.	04 Sep 89		Created.										*/
X/*																			*/
X/****************************************************************************/
X
Xvoid SortArticles( arts, narticles )
X
XARTICLE_INFO		**arts;
Xint					narticles;
X
X{
XARTICLE_INFO		*ptr;
XARTICLE_INFO		**where;
XSORT_INFO			*articles;
Xint					i;
Xint					Compare();
X
Xarticles = (SORT_INFO *) malloc( sizeof( SORT_INFO ) * narticles );
Xfor ( ptr = *arts, i = 0; i < narticles; i++, ptr = ptr->next )
X	{
X	articles[i].pointer = ptr;
X	articles[i].num = ptr->number;
X	}
Xqsort( (char *) articles, narticles, sizeof( SORT_INFO ), Compare );
Xwhere = arts;
Xfor ( i = 0; i < narticles; i++ )
X	{
X	*where = articles[i].pointer;
X	where = &(*where)->next;
X	}
X*where = NULLP( ARTICLE_INFO );
Xfree( articles );
X}
X
X
X/****************************************************************************/
X/*	FUNCTION:	Compare														*/
X/*																			*/
X/*	PURPOSE:	Help function to sort articles.								*/
X/*																			*/
X/*	INPUT PARAMETERS:														*/
X/*		NAME		I/O		DESCRIPTION										*/
X/*		----		---		-----------										*/
X/*		as defined by qsort()												*/
X/*																			*/
X/*	RETURNS:																*/
X/*		as defined by qsort()												*/
X/*																			*/
X/*	COMMENTS:																*/
X/*																			*/
X/*	HISTORY:																*/
X/*		1.	04 Sep 89		Created.										*/
X/*																			*/
X/****************************************************************************/
X
XCompare( first, second, size )
X
XSORT_INFO		*first, *second;
Xint				size;
X
X{
Xif ( first->num < second->num )
X	return -1;
Xelse if ( first->num > second->num )
X	return 1;
Xelse
X	return 0;
X}
X
X
X/****************************************************************************/
X/*	FUNCTION:	getcmd														*/
X/*																			*/
X/*	PURPOSE:	Get a command from the user.								*/
X/*																			*/
X/*	INPUT PARAMETERS:														*/
X/*		NAME		I/O		DESCRIPTION										*/
X/*		----		---		-----------										*/
X/*		cmdlist		 I		List of valid commands.							*/
X/*		help		 I		Array of help strings to display for user.		*/
X/*		otherlist	 I		List of other valid commands that aren't shown	*/
X/*							to the user.									*/
X/*																			*/
X/*	RETURNS:																*/
X/*																			*/
X/*	COMMENTS:																*/
X/*		The commands string must not allow 'h', 'H', or '?' as these are	*/
X/*		used to view the help string.										*/
X/*																			*/
X/*	HISTORY:																*/
X/*		1.	04 Sep 89		Created.										*/
X/*		2.	17 Oct 90		Added help options.								*/
X/*		3.	14 Nov 90		Added CR/NL.									*/
X/*		4.	28 Nov 90		Made "help" array of strings to escape compiler	*/
X/*							string length limitations.						*/
X/*		5.	06 Jan 91		Generalized special cases of CR/NL to otherlist.*/
X/*																			*/
X/****************************************************************************/
X
Xgetcmd( cmdlist, help, otherlist )
X
Xchar			*cmdlist;
Xchar			**help;
Xchar			*otherlist;
X
X{
Xint				ch;
Xchar			**p;
X
Xch = '"';
Xprintf( "[%s%s] ", cmdlist, ( strchr( otherlist, (char) '\r' ) ? "<CR>" : "" ) );
Xwhile ( strchr( cmdlist, (char) ch ) == NULLP( char ) )
X	{
X	ch = getchar();
X	if ( ch == 'h' || ch == 'H' || ch == '?' )	/* user wants help */
X		{
X		INVERSE_OFF;
X		printf( "\n" );
X		for ( p = help; *p != (char *) NULL; p++ )
X			printf( "%s\n", *p );
X		printf( "\n" );
X		printf( "[%s%s] ", cmdlist, ( strchr( otherlist, (char) '\r' ) ? "<CR>" : "" ) );
X		}
X	else if ( strchr( otherlist, (char) ch ) != NULLP( char ) )
X		break;
X	}
Xreturn ch;
X}
X
X
X/****************************************************************************/
X/*	FUNCTION:	Paginate													*/
X/*																			*/
X/*	PURPOSE:	Display article one page at a time.							*/
X/*																			*/
X/*	INPUT PARAMETERS:														*/
X/*		NAME		I/O		DESCRIPTION										*/
X/*		----		---		-----------										*/
X/*		article		 I		Article to be displayed.						*/
X/*		lines		 I		Lines already used on display.					*/
X/*																			*/
X/*	RETURNS:																*/
X/*		Last command character hit by user if it needs to be processed		*/
X/*		by caller.															*/
X/*																			*/
X/*	COMMENTS:																*/
X/*																			*/
X/*	HISTORY:																*/
X/*		1.	28 Sep 89		Created.										*/
X/*		2.	30 Dec 89		Return command letter hit at end of article.	*/
X/*		3.	12 Sep 90		Added parm for lines already printed on display.*/
X/*		4.	24 Oct 90		Added percentage read display.					*/
X/*		5.	14 Nov 90		Added CR/NL to print one more line.				*/
X/*		6.	22 Nov 90		End page at form feed.							*/
X/*		7.	25 Nov 90		Added noscroll.  If set in config file, screen	*/
X/*							will be cleared for each new page rather than	*/
X/*							scrolling.										*/
X/*		8.	05 Jan 91		Fixed line-wrap calculation.					*/
X/*		9.	05 Jan 91		Put last line of previous page at top of next	*/
X/*							page in NO_SCROLL mode (for continuity).		*/
X/*	   10.	06 Jan 91		Added "b" - go back one page.					*/
X/*	   11.  06 Jan 91		Added "a" - About BARN.							*/
X/*																			*/
X/****************************************************************************/
X
X# define	PAGE_CMDS	" abFfjKkmnqRrw"
X# define	KICK_CMDS	"kKwRrFfmn"		/* get kicked up to calling function */
Xstatic char *PAGE_HELP[] = {
X"<space>   View next page of article.",
X"<return>  Next line.",
X"   a      About BARN.",
X" b,<bs>   Go back one page.",
X"  F,f     Followup, 'F' includes current article.",
X" j,n,q    Quit the pager, marking the article as read.",
X"   K      Same as 'k', and adds Subject to KILL file.",
X"   k      Kill (mark as read) all articles w/this Subject.",
X"   m      Mark as unread.",
X"  R,r     Reply via mail, 'R' includes current article.",
X"   w      Write the article to disk.  Prompts for a filename.",
X(char *) NULL
X};
X
X
XPaginate( article, lines )
X
XARTICLE_INFO	*article;
Xint				lines;
X
X{
Xchar		filename[MAXLINE], buf[BUFSIZ], lastbuf[BUFSIZ], *readstatus;
XFILE		*fp;
Xint			line_number;		/* what line of display the cursor is on */
Xint			cmd, rc = ' ';
Xint			length;
Xchar		*p;
X
Xprintf( "\r%*s\r", Columns, " " );
Xsprintf( filename, "%ld", article->number );
Xif ( ( fp = fopen( filename, "r" ) ) == NULLP( FILE ) )
X	printf( "ARTICLE UNREADABLE!\n" );
Xelse
X	{
X	fseek( fp, 0L, 2 );
X	length = ftell( fp );
X	fseek( fp, article->textpos, 0 );
X	line_number = lines;
X	while ( ( readstatus = fgets( buf, BUFSIZ, fp ) ) != NULLP( char ) )
X		{
X		if ( buf[strlen(buf)-1] == '\n' )	/* remove trailing newline */
X			 buf[strlen(buf)-1] = NULL;
X		line_number += 1 + ( strlen( buf ) - 1 ) / Columns;
X		if ( line_number > ( Lines - 1 ) || strchr( buf, '\f' ) != NULL )
X			{
X			/* inverse video please */
X			INVERSE_ON;
X			printf( "-- more (%d%%) --", (int) ((ftell( fp ) * 100) / length) );
X			cmd = getcmd( PAGE_CMDS, PAGE_HELP, "\r\n\b" );
X			INVERSE_OFF;
X			printf( "\r%40s\r", " " );
X			if ( ( p = strchr( buf, '\f' ) ) != NULL )
X				*p = ' ';
X			if ( cmd == ' ' )
X				{
X				line_number = 0;
X				if ( GetVar( VAR_NOSCROLL ) != NULL )
X					{
X					Clear_Screen;
X					printf( "%s\n", lastbuf );
X					line_number += 1 + ( strlen( lastbuf ) - 1 ) / Columns;
X					}
X				line_number += 1 + ( strlen( buf ) - 1 ) / Columns;
X				printf( "%s\n", buf );
X				}
X			else if ( cmd == '\n' || cmd == '\r' )
X				/*
X				 *	Print one line.  Don't adjust line_number because we
X				 *	want to prompt again for next line.
X				 */
X				printf( "%s\n", buf );
X			else if ( cmd == 'b' || cmd == '\b' )
X				{
X				long	currentpos, newpos;
X				char	*scratch, *ptr;
X				int		size, lines;
X
X				/* go back one page */
X				line_number = 0;
X				currentpos = ftell( fp ) - strlen( buf ) - 1;
X				newpos = currentpos - ( Lines * 2 ) * ( Columns + 1 );
X				if ( newpos < article -> textpos )
X					newpos = article -> textpos;
X				fseek( fp, newpos, 0 );
X				size = ( currentpos - ftell( fp ) ) * sizeof( char );
X				scratch = malloc( size );
X				(void) fread( scratch, sizeof( char ), size, fp );
X				ptr = scratch + size - 1;
X				lines = 0;
X				while ( ptr > scratch && lines < ( Lines - 1 ) * 2 )
X					{
X					if ( *ptr == '\n' )
X						++lines;
X					--ptr;
X					}
X				if ( ptr > scratch )
X					newpos += ( ptr + 2 - scratch );
X				fseek( fp, newpos, 0 );
X				free( scratch );
X				if ( GetVar( VAR_NOSCROLL ) != NULL )
X					{
X					Clear_Screen;
X					}
X				}
X			else if ( cmd == 'a' )
X				{
X				AboutBARN();
X				/* 
X				 *	Fool this fn into re-reading the same line and printing
X				 *	the prompt again.
X				 */
X				fseek( fp, 0 - ( strlen( buf ) + 1 ), 1 );
X				strcpy( buf, lastbuf );
X				}
X			else 
X				{
X				if ( strchr( KICK_CMDS, (char) cmd ) != NULL )
X					rc = cmd;	/* these commands get kicked up to caller */
X				break;
X				}
X			}
X		else
X			printf( "%s\n", buf );
X		strcpy( lastbuf, buf );
X		}
X	fclose( fp );
X	if ( readstatus == NULLP( char ) )
X		{
X		INVERSE_ON;
X		printf( "End of article %ld. ", article->number );
X		cmd = getcmd( PAGE_CMDS, PAGE_HELP, "" );
X		INVERSE_OFF;
X		if ( strchr( KICK_CMDS, (char) cmd ) != NULL )
X			rc = cmd;	/* these commands get kicked up to caller */
X		}
X	}
Xreturn rc;
X}
X
X
X/****************************************************************************/
X/*	FUNCTION:	MakePattern													*/
X/*																			*/
X/*	PURPOSE:	Attempt to create a valid regular expression pattern.		*/
X/*																			*/
X/*	INPUT PARAMETERS:														*/
X/*		NAME		I/O		DESCRIPTION										*/
X/*		----		---		-----------										*/
X/*		pattern		 O		Put string form of pattern here.				*/
X/*		text		 I		Text to be matched.								*/
X/*																			*/
X/*	RETURNS:																*/
X/*		(struct regexp *)	Regular expression internal form.				*/
X/*		(NULL)				Couldn't grok pattern.							*/
X/*																			*/
X/*	COMMENTS:																*/
X/*		Finds characters with special meaning to regexp and escapes them by	*/
X/*		preceding them with a backslash.									*/
X/*																			*/
X/*		Pattern had better be large enough to hold the resulting string.	*/
X/*																			*/
X/*	HISTORY:																*/
X/*		1.	22 Oct 90		Created.										*/
X/*		2.	28 Nov 90		Added backslash as char that needs to be		*/
X/*							backslashed.									*/
X/*							Match random text between optional 'Re:' stuff	*/
X/*							and 'text' pattern.								*/
X/*																			*/
X/****************************************************************************/
X
Xstruct regexp *MakePattern( pattern, text )
X
Xchar				*pattern;
Xchar				*text;
X
X{
Xchar				temp[BUFSIZ];	/* translated string pattern */
Xchar				base[BUFSIZ];	/* string without Re: stuff */
Xchar				*p, *pbase;
Xstruct regexp		*remover_re;
Xstruct regexp		*re;			/* resulting regular expression */
Xint					len;
X# ifdef DEBUG_REGSUB
Xint					i;
X# endif
X
Xstrcpy( base, "\\1" );
Xif ( ( remover_re = regcomp( REPLY_REMOVER ) ) == NULL )
X	fprintf( stderr, "Can't compile REPLY_REMOVER\n" );
X# ifdef DEBUG_REGSUB
Xif ( regexec( remover_re, text ) == 1 )
X	for ( i = 0; i < 10; i++ )
X		printf( "%d '%*s'\n", i, remover_re -> endp[1] - remover_re -> startp[1], 
X		  remover_re -> startp[1] );
Xregsub( remover_re, text, base );
Xprintf( "but base is '%s'\n", base );
Xgetchar();
X# else
X(void) regexec( remover_re, text );
Xlen = remover_re -> endp[1] - remover_re -> startp[1];
Xstrncpy( base, remover_re -> startp[1], len );
Xbase[len] = NULL;
X# endif
Xp = temp; pbase = base;
Xwhile ( *pbase != NULL )
X	{
X	switch ( *pbase )
X		{
X		case '.':
X		case '*':
X		case '+':
X		case '(':
X		case ')':
X		case '[':
X		case ']':
X		case '^':
X		case '$':
X		case '-':
X		case '?':
X		case '|':
X		case '\\':
X					*p++ = '\\';
X		default:
X					*p++ = *pbase;
X					break;
X		}
X	pbase++;
X	}
X*p = NULL;
X/*
X *	Try to match the Re: stuff that followups generate.
X */
Xstrcpy( pattern, REPLY_PATTERN );
Xstrcat( pattern, ".*" );
Xstrcat( pattern, temp );
Xre = regcomp( pattern );
Xif ( remover_re != NULL )
X	free( (char *) remover_re );
Xreturn re;
X}
X
X
X/****************************************************************************/
X/*	FUNCTION:	ScanSubjects												*/
X/*																			*/
X/*	PURPOSE:	List articles in newsgroup by subject.						*/
X/*																			*/
X/*	INPUT PARAMETERS:														*/
X/*		NAME		I/O		DESCRIPTION										*/
X/*		----		---		-----------										*/
X/*		arts		 I		Header of list of articles in newsgroup.		*/
X/*																			*/
X/*	RETURNS:																*/
X/*																			*/
X/*	COMMENTS:																*/
X/*																			*/
X/*	HISTORY:																*/
X/*		1.	24 Oct 90		Created.										*/
X/*		2.	14 Nov 90		Added CR to print one more line.				*/
X/*		3.	16 Nov 90		Bug fix: always showed first article even if	*/
X/*							it had already been read.						*/
X/*																			*/
X/****************************************************************************/
X
X# define	SCAN_CMDS		"q "
Xstatic char *SCAN_HELP[] = {
X"<space>   View next page of subjects.",
X"<return>  View one more line.",
X"   q      Quit scanning subjects.",
X(char *) NULL
X};
X
Xvoid ScanSubjects( arts )
X
XARTICLE_INFO		*arts;
X
X{
XARTICLE_INFO		*pbase;			/* ptr to base article of subject thread */
XARTICLE_INFO		*preply;		/* tmep ptr to reply articles */
Xint					n_replies;		/* # of replies to a base article */
Xchar				*sub;			/* subject */
Xchar				dummy[MAXLINE];	/* not used */
Xstruct regexp		*pat;			/* pattern to match subjects */
Xint					printed = 0;	/* # subjects printed this screenful */
Xint					cmd;
X
Xprintf( "\n" );
Xfor ( pbase = arts; pbase != NULL; pbase = pbase -> next )
X	if ( ! pbase -> beenread )
X		pbase -> done = FALSE;
X	else
X		pbase -> done = TRUE;
X/*
X *	Find first unread base article.
X */
Xfor ( pbase = arts; pbase != NULL && pbase -> done; pbase = pbase -> next ) ;
Xwhile ( pbase != NULL )
X	{
X	if ( pbase -> subject )
X		sub = strdup( pbase -> subject );
X	else
X		sub = strdup( "<None>" );
X	pbase -> done = TRUE;
X	/*
X	 *	Count replies.
X	 */
X	n_replies = 0;
X	pat = MakePattern( dummy, sub );
X	for ( preply = pbase -> next; preply != NULL; preply = preply -> next )
X		if ( ! preply -> done && preply -> subject && 
X		  regexec( pat, preply -> subject ) == 1 )
X			{
X			n_replies++;
X			preply -> done = TRUE;
X			}
X	printf( "%5ld: (%2d replies) %-55.55s\n", pbase -> number, n_replies, sub );
X	if ( pat != NULL )
X		free( (char *) pat );
X	if ( sub != NULL )
X		free( sub );
X	/*
X	 *	Find next base article we haven't processed.
X	 */
X	for ( pbase = pbase -> next; pbase != NULL && pbase -> done;
X	  pbase = pbase -> next )
X	  	;
X	if ( ++printed >= Lines - 1 )
X		{
X		/* inverse video please */
X		INVERSE_ON;
X		printf( "--more--" );
X		cmd = getcmd( SCAN_CMDS, SCAN_HELP, "\r\n" );
X		INVERSE_OFF;
X		printf( "\r%40s\r", " " );
X		if ( cmd != '\n' && cmd != '\r' )
X			printed = 0;
X		if ( cmd == 'q' )
X			break;
X		}
X	}
Xif ( printed > 0 )
X	{
X	/* inverse video please */
X	INVERSE_ON;
X	printf( "--more--" );
X	(void) getcmd( SCAN_CMDS, SCAN_HELP, "" );
X	INVERSE_OFF;
X	printf( "\r%40s\r", " " );
X	}
Xfor ( pbase = arts; pbase != NULL; pbase = pbase -> next )
X	if ( ! pbase -> beenread )
X		pbase -> done = FALSE;
X	else
X		pbase -> done = TRUE;
X}
END_OF_FILE
if test 44397 -ne `wc -c <'arn.c'`; then
    echo shar: \"'arn.c'\" unpacked with wrong size!
fi
chmod +x 'arn.c'
# end of 'arn.c'
fi
if test -f 'barn.doc' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'barn.doc'\"
else
echo shar: Extracting \"'barn.doc'\" \(15480 characters\)
sed "s/^X//" >'barn.doc' <<'END_OF_FILE'
X			  BARN - Bah's Amiga ReadNews
X
X			 Version 2.0 - January 6, 1991
X
X		    Written by Jeff Van Epps (aka Lord Bah)
X
X
X1 OVERVIEW
X
X1.1 Purpose
X
X    BARN is a Usenet news-reader designed to replace "Anews", which comes
Xwith AmigaUUCP 1.03D.  It is intended to behave quite a bit like "rn" from
Xthe UNIX world.
X
X1.2 Features
X
X    Follows subject threads while reading articles.  This means that after
Xyou read one article, you are next presented with the next article in the
Xnewsgroup with the same subject.
X
X    Kill files allow you to weed out messages regarding subject which you
Xaren't interested in, or from people whose messages waste your time.
X
X    Article pager behaves like "more", with forward and backward paging and
Xdisplay of percentage of article read.
X
X    Email reply function and post followup article function use editor
Xspecified by the user in a configuration file.	Both functions have an
X"include article" option.
X
X    Configuration files allow multiple users to read news on the same
Xmachine, from the same directory, at the same time.
X
X
X2 REQUIREMENTS
X
X2.1 Hardware
X
X    There are no specific hardware requirements.
X
X2.2 Software
X
X    So far, BARN has only been used on AmigaUUCP 1.03D systems.  But it
Xshould understand any future versions with no problems.
X
X    It does require the "rmail" program to send email.
X
X
X3 INSTALLATION
X
X3.1 Executable
X
X    Place the "barn" executable wherever you want, most conveniently
Xsomewhere in your path.
X
X3.2 Configuration File
X
X    Edit the supplied "barn.config" file for your site.  Blank lines are
Xignored, and any line beginning with a "#" in column 1 is a comment.  Each
Xline is of the form "variable=value", with no spaces allowed around the
Xequal sign.
X
X    user=username
X
X    This is the equivalent of UserName in UULIB:Config.  It is the user
Xname by which the mail system addresses mail to you.  It is used in the
XFrom: line of outgoing mail and news postings.
X
X    node=machinename
X
X    This is the equivalent of NodeName in UULIB:Config.  It is the name by
Xwhich the outside world knows your machine.  It is used in the From: line
Xof outgoing mail and news postings.
X
X    name=Joe User
X
X    This is the equivalent of RealName in UULIB:Config.  It is your full
Xname.  It is used in the From: line of outgoing mail and news postings.
X
X    domain=.server.company.com
X
X    This is the equivalent of DomainName in UULIB:Config.  It is used in
Xconstructing the From: line of outgoing mail and news postings.  The full
Xconstructed line is:  From: $node!$user@$domain ($name).  I don't consider
Xthis general enough, but I haven't had to come up with anything better yet.
X
X    editor=stevie
X
X    The editor which will be invoked to compose email and followups.
X
X    newsrc=.newsrc
X
X    The name of the file which keeps track of which articles you have read
Xin each newsgroup.
X
X    kill=KILL
X
X    The name of the kill files.  There will be one global kill file in the
Xroot of the news directory structure whose kill rules will apply to all
Xarticles in all newsgroups.  There is also a local kill file within each
Xnewsgroup which applies to only that newsgroup.  This filename must start
Xwith the letters "KILL".
X
X    signature=UULIB:.signature
X
X    The contents of the signature file will be appended to the outgoing
Xemail and news articles which you compose before you are placed in the
Xeditor.  The contents will NOT be appended after you exit the editor, so
Xyou may delete the signature if you want.
X
X    lines=23
X    columns=80
X
X    These define the dimensions of the window in which you will run the
Xprogram.  I'm new to Amiga programming and haven't yet bothered to figure
Xout how to obtain the current window size or adjust to dynamic resizing
Xduring execution.
X
X    noscroll=true
X
X    Setting this variable to any value (even false!) will cause the pager
Xto start each new page at the top of the window rather than scrolling.
X
X3.3 Multiple Users
X
X    If you will have multiple users certain rules should be followed
Xregarding configuration files.
X
X    There will be one configuration file for each user.  I recommend naming
Xthe files <username>.config or barn.<usename>.
X
X    Within each config file, the variables "user", "name", "newsrc",
X"kill", and "signature" will change.  I recommend setting "newsrc" to
X<username>.newsrc and "kill" to KILL.<username>.  The others are up to you.
X
X    The variables "lines", "columns", "noscroll", and "editor" may be
Xdifferent in each config file depending on individual user preference.
X
X    When the config file to be used is not named "barn.config", you must
Xspecify it on the command line, i.e.
X
X    barn <username>.config
X
X3.4 Initial .newsrc file
X
X    Create a text file called ".newsrc" in the base directory where news is
Xstored (usually UUNEWS:).  Each line must have the name of a newsgroup
X(e.g. comp.sys.amiga), a space and a "0".  This file will be used by BARN
Xto keep track of which articles have already been read in each newsgroup.
X
X    If you have been using a different newsreader, you may indicate to BARN
Xwhich articles you have already read in each newsgroup.  Each line in the
Xnewsrc file contains the newsgroup name, one space, and a sequence of
Xmarkers separated by commas.  Each marker is either one number or an
Xinclusive range of two numbers separated by a dash.  As an example:
X
X    comp.sys.amiga 1-500,512,520-534
X
X    indicates that in newsgroup comp.sys.amiga, articles numbered 1 thru
X500, 512, and 520 thru 534 have been read.  Articles numbered 501 thru 511,
X513 thru 519, and 535 or higher have not been read.
X
X
X4 OPERATION
X
X4.1 Newsgroup Selection Level
X
X    BARN will scan the newsgroups listed in the newsrc file in the order in
Xwhich they are listed.	Upon encountering each newsgroup, it will descend
Xinto that directory.  For each file in the directory whose name does not
Xbegin with "KILL", it will interpret the filename as an article number and
Xdetermine whether or not that article has already been read, based on the
Xmarkers for that newsgroup in the newsrc file.	If the article has not been
Xread, then the headers of the article are read and compared against the
Xglobal and local kill files.  If the article is not killed, it is added to
Xthe list of articles to be read.
X
X    When finished scanning the directory, BARN presents the newsgroup name
Xand the number of unread articles to the user and asks if the user wishes
Xto read the newsgroup now.  If there are no unread articles, BARN proceeds
Xto the next newsgroup in the newsrc file without user intervention.
X
X    User commands at the newsgroup selection level:
X
X    a	    About BARN.  Prints credits and version.
X    c	    Catch up.  Mark all articles in the newsgroup as read.
X    n	    No, don't read this newsgroup now.  Markers are not updated.
X    q	    Quit BARN.	Writes the newsrc file and exits.
X    y	    Yes, read this newsgroup now.  Goes to article selection level.
X <space>    Same as Yes.
X h,H,or ?   Prints help for this level.
X
X4.2 Article Selection Level
X
X    The interesting headers of the lowest numbered article are presented to
Xthe user along with the article selection level prompt.
X
X    User commands at the article selection level:
X
X    a	    About BARN.  Prints credits and version.
X    c	    Catch up.  Mark all articles in the newsgroup as read.
X    f	    Post followup news article.  Uses user's $editor from config.
X    F	    Followup, including contents of current article in new article.
X    j	    Junk this article (mark it as read).
X    k	    Kill articles matching the current subject pattern.
X    K	    Kill, placing this kill pattern in the local kill file.
X    m	    Mark this article as unread.
X    n	    No, don't read this article.  Mark it as read.
X    q	    Quit this newsgroup.
X    r	    Reply to article originator via email.  Uses $editor from config.
X    R	    Reply, including contents of current article.
X    w	    Write this article.  Prompts for filename.
X    y	    Yes, read this article now.
X <space>    Yes, read this article now.
X <minus>    Go to previously displayed article, marking it as unread.
X <equal>    Scan subject threads.
X    /	    Find next article with subject which BARN will prompt for.
X    #	    Go to article with number BARN will prompt for (if unread).
X h,H,or ?   Print help for this level.
X
X    Unless directed otherwise, BARN will present the next article matching
Xthe current subject thread.  If there is no such article, it will present
Xthe lowest numbered article still unread.
X
X    There are some commands which are less than completely clear, so I'll
Xgo over them.
X
X4.2.1 Followups
X
X    The Subject header is copied from the current article, and other
Xheaders are generated from sources such as the config file, the system date
Xand time, etc.	These headers are placed into a temporary file named
XT:arnreply.  The user's signature is appended to the file, and the user's
Xeditor specified in the config file is started, given the temporary
Xfilename as an argument on the command line.  If the user specified an
Xincluding followup (capital F command), then the text of the current
Xarticle is placed in the temporary file between the headers and the
Xsignature, with each line prefixed by "> ".
X
X    When the editor returns, the last modification time of the temporary
Xfile is checked.  If it was not modified by the user while in the editor,
Xthe followup operation is aborted.  If it was modified, then the contents
Xof the temporary file are sent as an email message to "inews@bisco".  This
Xis a cheap way to post news for me, but I realize that no one else will be
Xable to use it.  For now, you will have to obtain the source for BARN and
Xmodify this address to one which you can reach which will let you post
Xnews.  Eventually, either I will fix this or someone else will fix this and
XI will include the fix in a subsequent release of BARN.
X
X4.2.2 Replies
X
X    Replies are almost identical to followups.	The only difference is that
Xa To: field is inserted into the temporary file addressing the mail to the
Xoriginator of the article before invoking the editor, and the To: field is
Xextracted again after exiting the editor (allowing the user to change the
Xdestination of the message in the editor, in case the From: field from the
Xoriginal article was bad).  The "rmail" program is then fed the contents of
Xthe temporary file and given the address as an argument.
X
X    This should probably use the Reply-To: field if one exists, but I
Xhaven't gotten around to it yet.
X
X4.2.3 Kill Files
X
X    Each line of a kill file contains a header name and a regular
Xexpression, such as:
X
X1	Subject: MS-DOS
X2	Sender: fool@school
X3	Subject: ^Re:
X4	Subject: word.*process
X
X(1) kills any article whose subject line contains the string "MS-DOS".  (2)
Xkills any article whose sender header line contains the string
X"fool@school".  (3) kills any article whose subject line begins with "Re:".
X(4) kills any article whose subject line contains the string "word",
Xfollowed by any number of characters, followed by the string "process".
X
X    Any "interesting" header may be used in a kill file.  Currently,
X"interesting" is defined as one of: "Subject", "From", "To", "Date",
X"Sender".  A future version of BARN will allow these to be specified in a
Xfile referenced from the config file.
X
X    For a full description of regular expressions, see the regexp man page.
X
X    Any kill file can be edited with a normal editor, but don't leave any
Xblank lines.  Do not edit the global kill file while BARN is running, and
Xdo not edit a local kill file while BARN is in that newsgroup, or your
Xchanges to the file will be lost when BARN writes out its idea of what the
Xkill file is.
X
X    When you use the 'K' command to add a subject to the local kill file,
XBARN will escape with a backslash any characters in the subject which would
Xhave special meaning to regexp and add a pattern on the front of the
Xsubject which matches any number of "Re:"-type prependages.
X
X4.2.4 Mark as Unread
X
X    The 'm' command is not well-implemented at this time.  It should keep
Xthe article out of the user's sight until all other articles in the
Xnewsgroup have been dealt with.  Instead the user will see the article
Xagain right after the next subject thread is exhausted.
X
X    It will be improved.
X
X4.2.5 Write Article
X
X    This prompts for a filename and then writes the current article to that
Xfilename in the directory from which BARN was executed using the AmigaDOS
X"copy" command.
X
X4.2.6 Previous Article
X
X    Returns to the previously displayed article and unmarks ONLY that
Xarticle, even if the user has just executed a 'k' - kill command on that
Xsubject.
X
X4.2.7 Scan Subjects
X
X    For each unique subject still unread, displays the first article number
Xusing that subject, the number of reply articles under the same subject,
Xand the subject text.
X
X4.2.8 Find Subject
X
X    The '/' command sets the current subject thread to whatever the user
Xenters.  The system behaves just as if an article with that subject had
Xbeen read.  This includes using the user-entered subject as a kill pattern.
X
X4.3 Pager Level
X
X    BARN's built-in pager displays the article one page at a time, using
Xthe 'lines' and 'columns' variables from the config file.  Most of the
Xcommands valid at the article selection level are also valid within the
Xpager.
X
X    User commands at the pager level:
X
X    a	    About BARN.  Prints credits and version.
X    b	    Go backward one page.
X <space>    Go forward one page.
X <return>   Go forward one line.
X   <bs>     Go backward one page.
X h,H,or ?   Print help for the pager level.
X
X    and  [fFjkKmnqrRw]	from the article selection level.
X
X
X5 LIMITATIONS
X
X5.1 Followup
X
X    As noted in section 4.2.1, you will probably not be able to post news
Xfrom your system unless you can modify the source code.  There is no method
Xfor posting "base" articles, only followups.
X
X5.2 From Name
X
X    The "From:" line constructed may not be in a form suitable for your
Xsite.
X
X5.3 Article Expiration
X
X    I consider article expiration to be a separate function.  I've supplied
Xa short shell script that I use to do this; modify to suit.
X
X
X6 FUTURE ENHANCEMENTS
X
X    Junking a range of article numbers.
X
X    Verbose command to display all article headers.
X
X    Customizable list of "interesting" headers, not just "From", "Date", and
X"Subject".
X
X    Use Message-ID to avoid multiple display of cross-posted articles.  I
Xcurrently do this with an external program.
X
X    More interesting commands to handle articles which have already been read.
X
X
X7 SOURCE CODE MODIFICATION
X
X    I use SAS C 5.10.  There are probably some library functions that I use
Xwhich Manx does not provide.  I also use Miles Bader's "cc" front-end to
X"lc" to sort out all of the compiler options.  You will also need to find
XHenry Spencer's "regexp" library to recompile.
X
X    If you do make modifications, please send them to me rather than
Xreleasing them yourself so that I can try to keep track of different
Xversions.  Thank you for your cooperation.
X
X
X8 CREDITS
X
X    Uses sendpacket.c and raw.c from the AmigaUUCP distribution, which is
Xapparently now in the hands of an entity called Dynamyx.  The mentioned source
Xfiles were written by CBM and Chuck McManis, respectively.  I'll put my source
Xin the public domain eventually, but I want to make sure there are no bugs
Xfirst.
X
X    Uses the regexp library written by Henry Spencer and copyrighted by
Xthe University of Toronto in 1986.
X
X    All else written by Jeff Van Epps.
X
X	Compiled with SAS C 5.10.
END_OF_FILE
if test 15480 -ne `wc -c <'barn.doc'`; then
    echo shar: \"'barn.doc'\" unpacked with wrong size!
fi
chmod +x 'barn.doc'
# end of 'barn.doc'
fi
echo shar: End of archive 2 \(of 2\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked both archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Mail submissions (sources or binaries) to <amiga@uunet.uu.net>.
Mail comments to the moderator at <amiga-request@uunet.uu.net>.
Post requests for sources, and general discussion to comp.sys.amiga.misc.