[comp.sources.misc] v09i075: newsclip 1.1, part 6 of 15

brad@looking.ON.CA (Brad Templeton) (12/20/89)

Posting-number: Volume 9, Issue 75
Submitted-by: brad@looking.ON.CA (Brad Templeton)
Archive-name: newsclip/part06

#! /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 6 (of 15)."
# Contents:  comp/nl.y db.c main.c mknewsrc.c patch/filtpipe.c
# Wrapped by allbery@uunet on Tue Dec 19 20:09:58 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'comp/nl.y' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'comp/nl.y'\"
else
echo shar: Extracting \"'comp/nl.y'\" \(9958 characters\)
sed "s/^X//" >'comp/nl.y' <<'END_OF_FILE'
X/*
X * Yacc grammar for NewsClip
X */
X
X%{
X#include "nc.h"
X /*
X  * Newsclip(TM) Compiler Source Code.
X  * Copyright 1989 Looking Glass Software Limited.  All Rights Reserved.
X  * Unless otherwise licenced, the only authorized use of this source
X  * code is compilation into a binary of the newsclip compiler for the
X  * use of licenced Newsclip customers.  Minor source code modifications
X  * are allowed before compiling.
X  */
X%}
X
X/*
X * TERMINAL DECLARATIONS
X *
X */
X
X%union {
X	nodep	treeval;		/* maybe need tree and list types? */
X	char	*strval;
X	int	intval;
X}
X
X%token		YACCEPT		YARRAY		YBREAK
X		YCASE		YCONTINUE	YPARSE
X		YTDATABASE	YTDATE
X		YDEFAULT	YDUALCASE	YELSE
X		YEXTERN		YFOR		YGOTO
X		YHEADER		YIF		YIN
X		YHAS		YTINT
X		YTNEWSGROUP	YREJECT
X		YSWITCH 	YTUSERID
X		YWHILE 		YILLCH 
X		YADJUST		YPROCEDURE	
X		YFORWARD	YRETURN		YTSTRING
X
X
X%token 		EQ_OP	NE_OP	LE_OP	GE_OP
X		INC_OP	DEC_OP	AND_OP	OR_OP
X
X%token <intval>	YNEWSGROUP YINT	YQUERYGROUP
X
X%token <strval>	YID		YSTRING
X
X%type <treeval> argdec_list		variable	typelist
X		argdec	statement labeled_statement	compound_statement
X		statement_list simple_statement	assignment	routine
X		selection_statement	forstat		fortest
X		iteration_statement	jump_statement	explist	
X		elist	primary_expr	postfix_expr	unary_expr
X		cast_expr	multiplicative_expr	
X		additive_expr	relational_expr	equality_expr	and_expr	
X		exclusive_or_expr	inclusive_or_expr
X		logical_and_expr	logical_or_expr	conditional_expr
X		expr constant_expr	varident	routident
X		routid		decl	localdecs
X		local_decl	local_decl_list
X
X
X%type <intval>	type simtype
X
X
X
X
X%%
X/*
X * PRODUCTIONS
X */
X
Xprogram:
X	progel
X		|
X	program progel
X		;
X
Xprogel:
X	decl	{
X		extern int got_error;
X		if( got_error < SYNTAX_ERROR ) {
X			out( $1 );
X			coutc( '\n' );
X			}
X		treefree( $1 );
X		}
X		|
X	procedure
X		|
X	function
X		|
X	error {
X		parerror( "Invalid declaration or routine" );
X		}
X		;
X	
X
X
Xpush_table:
X	{ push_table(); }
X		;
Xpop_table:
X	{ pop_table(); }
X		;
X
X
Xlocal_decl:
X	YEXTERN type YID ';'
X	{ $$ = extern_var( $3, $2 ); }
X		|
X	YEXTERN type YID '(' typelist ')' ';'
X	{ $$ = extern_func( $3, (dtype)$2, (listp)$5, TRUE ); }
X		|
X	YEXTERN YPROCEDURE YID '(' typelist ')' ';'
X	{ $$ = extern_func( $3, (dtype)0, (listp)$5, TRUE ); }
X		|
X	type YID ';'
X	{ $$ = declare_var( $2, (dtype)$1 ); }
X		|
X	error ';'
X		{
X		parerror( "Invalid declaration" );
X		$$ = NIL;
X		}
X		;
Xdecl:
X	YFORWARD type YID '(' typelist ')' ';'
X	{ $$ = extern_func( $3, (dtype)$2, (listp)$5, FALSE ); }
X		|
X	YFORWARD YPROCEDURE YID '(' typelist ')' ';'
X	{ $$ = extern_func( $3, (dtype)0, (listp)$5, FALSE ); }
X		|
X	YHEADER simtype YID ':' YSTRING ';' {
X		$$ = declare_var( $3, (dtype)$2 ); 
X		select_header( $5, $$, "" );
X		}
X		|
X	YHEADER simtype YARRAY YID ':' YSTRING ',' YSTRING ';' {
X		$$ = declare_var( $4, (dtype)(arrayof($2)) ); 
X		select_header( $6, $$, $8 );
X		}
X		|
X	local_decl
X		;
X
Xlocal_decl_list:
X	local_decl
X	{ $$ = (nodep)freshlist( $1 ); }
X		|
X	local_decl_list local_decl
X	{ $$ = (nodep)llist( $1, $2 ); }
X		;
X
X
X
Xprocedure:
X	YPROCEDURE routid push_table '(' argdec_list ')' routine {
X		routine_decl( $2, $5, $7, 0, ST_PROC );
X		}
X		pop_table
X		;
Xfunction:
X	type routid push_table '(' argdec_list ')' routine {
X		routine_decl( $2, $5, $7, $1, ST_FUNC );
X		}
X		pop_table
X		;
X
Xroutid:
X	YID
X	{ $$ = gen_declare( $1 ); }
X		;
X
Xtypelist:
X	type
X	{ $$ = (nodep)freshlist( tree(N_INT, (nodep)$1 ) ); }
X		|
X	typelist ',' type
X	{ $$ = (nodep)llist( $1, tree(N_INT, (nodep)$3 ) ); }
X		|
X	{ $$ = NIL; }
X		;
X		
X
Xargdec_list:
X	argdec
X	{ $$ = (nodep)freshlist( $1 ); }
X		|
X	argdec_list ',' argdec
X	{ $$ = (nodep)llist( $1, $3 ); }
X		|
X	{ $$ = NIL; }
X		;
X
Xargdec:
X	type YID {
X		$$ = declare_arg( $2, (dtype)$1 );
X		}
X		|
X	error { 
X		parerror( "Invalid argument declaration" );
X		$$ = NIL;
X		}
X		;
X		
X
Xtype:
X	simtype
X		|
X	simtype YARRAY
X	{ $$ = arrayof($1); }
X		|
X	YTDATABASE
X	{ $$ = T_DATABASE; }
X		;
Xsimtype:
X	YTINT 
X	{ $$ = T_INTEGER; }
X	| YTDATE 
X	{ $$ = T_DATE; }
X	| YTUSERID 
X	{ $$ = T_USERNAME; }
X	| YTSTRING 
X	{ $$ = T_STRING; }
X	| YTNEWSGROUP
X	{ $$ = T_NEWSGROUP; }
X	;
X	
X
Xroutine:
X	'{' localdecs statement_list '}'
X		{
X 		$$ = tree( N_ROUTINE, $2, $3 );
X		}
X		|
X	'{' localdecs '}'
X		{
X 		$$ = tree( N_ROUTINE, $2, NIL );
X		}
X		;
X
Xlocaldecs:
X	local_decl_list
X		|
X	{ $$ = NIL; }
X		;
X
X/*
X * And now for general statement
X */
X
Xstatement:
X	labeled_statement
X		|
X	compound_statement
X		|
X	simple_statement
X		|
X	selection_statement
X		|
X	iteration_statement
X		|
X	jump_statement
X		|
X	error ';' {
X		parerror( "statement syntax error" );
X		$$ = NIL;
X		}
X		;
X
Xlabeled_statement:
X	YID ':' statement {
X		$$ = tree( N_LABELED, declare_lab( $1 ), $3 );
X		}
X		|
X	YCASE constant_expr ':' statement {
X		$$ = tree( N_CASE, $2, $4 );
X		}
X		|
X	YDEFAULT ':' statement {
X		$$ = tree( N_DEFAULT, $3 );
X		}
X		;
X
Xcompound_statement:
X	'{' '}' {
X		$$ = tree( N_COMPOUND, NIL );
X		}
X		|
X	'{' statement_list '}' {
X		$$ = tree( N_COMPOUND, $2 );
X		}
X		|
X	'{' error '}' {
X		parerror( "Invalid compound statement" );
X		$$ = NIL;
X		}
X		;
X
Xstatement_list:
X	statement
X	{ $$ = (nodep)freshlist( $1 ); }
X		|
X	statement_list statement {
X		$$ = (nodep)llist( $1, $2 );
X		}
X		;
Xsimple_statement:
X	assignment ';'
X		{ $$ = tree( N_SEMISTAT, $1 ); }
X		|
X	YADJUST expr ';' {
X		$$ = tree( N_ADJUST, $2 );
X		}
X		|
X	routident '(' explist ')' ';' {
X		$$ = tree( N_CALL, $1, $3 );
X		}
X		;
X
Xassignment:
X	variable '=' expr
X		{ $$ = tree( N_ASSIGN, $1, $3 ); }
X		|
X	YPARSE variable '=' expr
X		{ $$ = tree( N_PARSE, $2, $4, NIL ); }
X		|
X	YPARSE variable '=' expr ',' expr
X		{ $$ = tree( N_PARSE, $2, $4, $6 ); }
X		|
X	variable '=' YARRAY expr
X		{ $$ = tree( N_ARINIT, $1, $4 ); }
X		|
X	variable INC_OP
X		{ $$ = tree( N_POSTINC, $1 ); }
X		|
X	variable DEC_OP
X		{ $$ = tree( N_POSTDEC, $1 ); }
X		|
X	INC_OP variable
X		{ $$ = tree( N_PREINC, $2 ); }
X		|
X	DEC_OP variable
X		{ $$ = tree( N_PREDEC, $2 ); }
X		|
X		{ $$ = NIL; }
X		;
X
Xselection_statement:
X	YIF '(' expr ')' statement {
X		$$ = tree( N_IF, $3, $5 );
X		}
X		|
X	YIF '(' expr ')' statement YELSE statement {
X		$$ = tree( N_IFELSE, $3, $5, $7 );
X		}
X		|
X	YSWITCH '(' expr ')' compound_statement {
X		$$ = tree( N_SWITCH, $3, $5 );
X		}
X		;
X
Xforstat:
X	assignment
X		|
X	unary_expr
X		;
Xfortest:
X	expr
X		|
X	{ $$ = NIL; }
X		;
X
X
Xiteration_statement:
X	YWHILE '(' expr ')' statement {
X		$$ = tree( N_WHILE, $3, $5 );
X		}
X		|
X	YFOR '(' forstat ';' fortest ';' forstat ')' statement {
X		$$ = tree( N_FOR, $3, $5, $7, $9 );
X		}
X		|
X	YFOR '(' variable YIN expr ')' statement {
X		$$ = tree( N_FOREACH, $3, $5, $7 );
X		}
X		;
X
Xjump_statement:
X	YGOTO YID ';' {
X		$$ = tree( N_GOTO, goto_lookup($2) );
X		}
X		|
X	YCONTINUE ';' {
X		$$ = tree( N_CONTINUE );
X		}
X		|
X	YBREAK ';' {
X		$$ = tree( N_BREAK );
X		}
X		|
X	YRETURN ';' {
X		$$ = tree( N_RETURN, NIL);
X		}
X		|
X	YRETURN expr ';' {
X		$$ = tree( N_RETURN, $2 );
X		}
X		|
X	YACCEPT ';' {
X		$$ = tree( N_ACCEPT );
X		}
X		|
X	YACCEPT YIF expr ';' {
X		$$ = tree( N_IFACCEPT, $3 );
X		}
X		|
X	YREJECT ';' {
X		$$ = tree( N_REJECT );
X		}
X		|
X	YREJECT YIF expr ';' {
X		$$ = tree( N_IFREJECT, $3 );
X		}
X		;
X		
Xexplist:
X	elist
X		|
X		{ $$ = NIL; }
X		;
X
Xelist:
X	expr
X	{ $$ = (nodep)freshlist( $1 ); }
X		|
X	elist ',' expr {
X		$$ = (nodep)llist( $1, $3 );
X		}
X		;
X
Xvariable
X	: varident
X	| varident '[' expr ']'
X	{ $$ = tree( N_INDEX, $1, $3 ); }
X	;
X	
X
Xprimary_expr
X	: variable
X	| routident '(' explist ')'
X	{ $$ = tree( N_FUNCALL, $1, $3 ); }
X	| YINT
X	{ $$ = tree( N_INT, (nodep)$1 ); }
X	| YSTRING
X	{ $$ = tree( N_STRING, (nodep)allocstring($1) ); }
X	| YNEWSGROUP
X	{ $$ = tree( N_NGROUP, (nodep)$1 ); }
X	| YQUERYGROUP
X	{ $$ = tree( N_QGROUP, (nodep)$1 ); }
X	| '(' expr ')'
X	{ $$ = tree( N_PAREN, $2 ); }
X	;
X
Xpostfix_expr
X	: primary_expr
X	;
X
Xunary_expr
X	: postfix_expr
X	| '-' cast_expr
X	{ $$ = tree( N_UMINUS, $2 ); }
X	| '!' cast_expr
X	{ $$ = tree( N_NOT, $2 ); }
X	;
X
Xcast_expr
X	: unary_expr
X	;
X
Xmultiplicative_expr
X	: cast_expr
X	| multiplicative_expr '*' cast_expr
X	{ $$ = tree( N_MULT, $1, $3 ); }
X	| multiplicative_expr '/' cast_expr
X	{ $$ = tree( N_DIVIDE, $1, $3 ); }
X	| multiplicative_expr '%' cast_expr
X	{ $$ = tree( N_MODULUS, $1, $3 ); }
X	;
X
Xadditive_expr
X	: multiplicative_expr
X	| additive_expr '+' multiplicative_expr
X	{ $$ = tree( N_PLUS, $1, $3 ); }
X	| additive_expr '-' multiplicative_expr
X	{ $$ = tree( N_MINUS, $1, $3 ); }
X	;
X
X
Xrelational_expr
X	: additive_expr
X	| relational_expr '<' additive_expr
X	{ $$ = tree( N_LT, $1, $3 ); }
X	| relational_expr '>' additive_expr
X	{ $$ = tree( N_GT, $1, $3 ); }
X	| relational_expr LE_OP additive_expr
X	{ $$ = tree( N_LE, $1, $3 ); }
X	| relational_expr GE_OP additive_expr
X	{ $$ = tree( N_GE, $1, $3 ); }
X	;
X
Xequality_expr
X	: relational_expr
X	| equality_expr EQ_OP relational_expr
X	{ $$ = tree( N_EQ, $1, $3 ); }
X	| equality_expr NE_OP relational_expr
X	{ $$ = tree( N_NE, $1, $3 ); }
X	| equality_expr YIN relational_expr
X	{ $$ = tree( N_IN, $1, $3 ); }
X	| equality_expr '!' YIN relational_expr
X	{ $$ = tree( N_NOT_IN, $1, $4 ); }
X	| equality_expr YHAS relational_expr
X	{ $$ = tree( N_HAS, $1, $3 ); }
X	| equality_expr '!' YHAS relational_expr
X	{ $$ = tree( N_NOT_HAS, $1, $4 ); }
X	;
X
Xand_expr
X	: equality_expr
X	| and_expr '&' equality_expr
X	{ $$ = tree( N_BAND, $1, $3 ); }
X	;
X
Xexclusive_or_expr
X	: and_expr
X	| exclusive_or_expr '^' and_expr
X	{ $$ = tree( N_XOR, $1, $3 ); }
X	;
X
Xinclusive_or_expr
X	: exclusive_or_expr
X	| inclusive_or_expr '|' exclusive_or_expr
X	{ $$ = tree( N_BOR, $1, $3 ); }
X	;
X
Xlogical_and_expr
X	: inclusive_or_expr
X	| logical_and_expr AND_OP inclusive_or_expr
X	{ $$ = tree( N_AND, $1, $3 ); }
X	;
X
Xlogical_or_expr
X	: logical_and_expr
X	| logical_or_expr OR_OP logical_and_expr
X	{ $$ = tree( N_OR, $1, $3 ); }
X	;
X
Xconditional_expr
X	: logical_or_expr
X	| logical_or_expr '?' logical_or_expr ':' conditional_expr
X	{ $$ = tree( N_QUERY, $1, $3, $5 ); }
X	;
X
Xexpr
X	: conditional_expr
X	| error {
X		parerror( "Syntax error in expression" );
X		$$ = tree( N_INT, (nodep)0 );
X		}
X	;
X
Xconstant_expr
X	: conditional_expr
X	;
X
Xvarident:
X	YID {
X	$$ = symlookup( $1, ST_VAR, 0 );
X	}
X	;
Xroutident:
X	YID {
X	$$ = symlookup( $1, ST_FUNC, ST_PROC );
X	}
X	;
X%%
END_OF_FILE
if test 9958 -ne `wc -c <'comp/nl.y'`; then
    echo shar: \"'comp/nl.y'\" unpacked with wrong size!
fi
# end of 'comp/nl.y'
fi
if test -f 'db.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'db.c'\"
else
echo shar: Extracting \"'db.c'\" \(9874 characters\)
sed "s/^X//" >'db.c' <<'END_OF_FILE'
X/*
X * db.c
X *
X * Single-key in-memory database manager -- access by open hashing.
X *
X */
X
X /*
X  * Newsclip(TM) Library Source Code.
X  * Copyright 1989 Looking Glass Software Limited.  All Rights Reserved.
X  * Unless otherwise licenced, the only authorized use of this source
X  * code is compilation into a binary of the newsclip library for the
X  * use of licenced Newsclip customers.  Minor source code modifications
X  * are allowed.
X  * Use of this code for a short term evaluation of the product, as defined
X  * in the associated file, 'Licence', is permitted.
X  */
X
X#define static
X
X#include "nl.h"
X
X/* Activity codes for the hashing routines: */
X
X#define HA_FIND		1	/* Locate key in table.			  */
X#define HA_ADD		2	/* Add key in table, if not present.	  */
X#define HA_DEL		3	/* Delete key in table, if present.	  */
X
Xint guess_tbl_size AC((unsigned int));
Xdbrec *access_db AC((dbptr, int, char *, int));
Xdbrec *ante_db AC((dbptr, int));
Xdbrec *pred_db AC((dbptr, int));
Xunsigned int hash_key_fn AC((char *));
X
X#define check_valid(x)	if( !(x) ) error( "db -- nil database pointer")
X
X/*
X * Some definitions controlling the gathering of statistics.
X */
X#ifdef STATS_DB
Xlong Naccesses, Ncollisions;
X# define StatAccess()		Naccesses++
X# define StatCollision()	Ncollisions++
X# define InitStats()		Naccesses = Ncollisions = 0L;
X#else
X# define StatAccess()
X# define StatCollision()
X# define InitStats()
X#endif /*STATS_DB*/
X
X
X/* init_db() initializes a database with the given attributes of estimated
X * size,  size of contained data elements and other controls, such as whether
X * an access time is to be attached with every record.  A valid pointer to
X * the constructed database structure is returned, or NULL on error. */
X
Xdbptr
Xinit_db( est_size, data_size )
Xunsigned int est_size;		/* Estimated size of the database.        */
Xunsigned int data_size;		/* Size of data items attached to keys.   */
X{
X	dbptr dbvar;		/* New database, created.		  */
X	unsigned tbl_size;	/* Actual table size to be used.	  */
X
X	dbvar = (dbptr) perm_alloc( sizeof(db_t) );
X	dbvar->flags = 0;
X	dbvar->item_size = data_size;
X	dbvar->tbl_size = tbl_size = guess_tbl_size( est_size );
X	dbvar->table = (dbrec **) perm_alloc( sizeof(dbrec *)*tbl_size );
X	dbvar->n_elements = 0;
X	zero( dbvar->table, sizeof(dbrec *)*tbl_size );
X
X	InitStats();
X
X	return( dbvar );
X}
X
X/* free_database() frees all of the memory resources
X * required by the given database.  Zero is returned. */
X
Xint
Xfree_db( dbvar )
Xdbptr dbvar;
X{
X	register dbrec *buck, *obuck;
X	
X	check_valid( dbvar );
X
X	for( buck = first_rec( dbvar ); buck; buck = obuck ) {
X		obuck = next_rec( dbvar, buck );
X		if( !(buck->flags & RF_STATIC) ) {
X			/* Free the key memory if it is
X			 * not flagged as static memory. */
X			perm_free( buck->key );
X			}
X		perm_free( buck );
X		}
X
X	/* Free the database maintenance structures themselves. */
X	perm_free( dbvar->table );
X	perm_free( dbvar );
X
X	return( 0 );
X}
X
X/* add_rec() inserts a record with the given key into the specified
X * database, according to the actions implied by the given flag argument. */
X
Xdbrec *
Xadd_rec( dbvar, key, flags )
Xdbptr dbvar;
Xchar *key;
Xint flags;
X{
X	return( access_db( dbvar, HA_ADD, key, flags ) );
X}
X
X/* del_rec() deletes the record with the given key from the specified database.
X * If the record was found and deleted OK, zero is returned; otherwise, one. */
X
Xint
Xdel_rec( dbvar, key )
Xdbptr dbvar;
Xchar *key;
X{
X	return( access_db( dbvar, HA_DEL, key, 0 ) ? 0 : 1 );
X}
X
X/* get_rec() returns a pointer to the data field for the record corresponding
X * to the given key in the specified database, or NULL if the record wasn't
X * found */
X
Xdbrec *
Xget_rec( dbvar, key )
Xdbptr dbvar;
Xchar *key;
X{
X	return( dbvar ? access_db( dbvar, HA_FIND, key, 0 ) : (dbrec *) NULL );
X}
X
X/* first_rec() returns the first record in the database (that is, the head
X * of the linked list of database records as they exist in the database). */
X
Xdbrec *
Xfirst_rec( dbvar )
Xdbptr dbvar;
X{
X	return( dbvar ? ante_db( dbvar, -1 ) : (dbrec *) NULL );
X}
X
X
X/**************************************************************************
X *
X * Routines for accessing the database information, using open hashing.
X *
X **************************************************************************/
X
X/* access_db() accesses the given database, performing the given action
X * with the supplied data.  This routine represents the common code
X * required for adding, deleting and retrieving database entries. */
X
Xstatic dbrec *
Xaccess_db( dbvar, action, key, flags )
Xdbptr dbvar;			/* Hash table on which to operate. */
Xint action;			/* Action to perform in hash table. */
Xchar *key;			/* Key to search for or to store the data. */
Xint flags;			/* Various control flags. */
X{
X	unsigned int hash_num;	/* Computed hash number. */
X	unsigned int buck_num;	/* Computed bucket number. */
X	int found = 0;		/* Indicates whether key was found. */
X	dbrec *buck, *obuck;	/* Current and last bucket in search. */
X	int depth = 0;		/* Depth of the bucket search. */
X
X	check_valid( dbvar );
X
X	hash_num = hash_key_fn( key );
X	buck_num = hash_num % dbvar->tbl_size;
X
X	StatAccess();
X
X	if( buck = dbvar->table[buck_num] ) do {
X		/* Search the hash chain and look for our key.
X		 * If it seems necessary, a pre-screen based on
X 		 * hash number can be added in this step. */
X		if( !strcmp( buck->key, key ) ) {
X			found++;
X			break;
X			}
X		obuck = buck;
X		buck = buck->next;
X		depth++;
X		StatCollision();
X		} while( !(obuck->flags & RF_LAST) );
X
X	switch( action ) {
X	    case HA_ADD:
X		if( found ) {
X			/* Return either NULL or the located data record,
X			 * depending on the AR_NEWONLY flag setting. */
X			return( flags & AR_NEWONLY ? (dbrec *) NULL : buck );
X			}
X		else {
X			/* Allocate and zero memory for a new record. */
X			buck = (dbrec *) perm_alloc( dbvar->item_size );
X			zero( buck, dbvar->item_size );
X
X			if( flags & AR_NOALLOC ) {
X				/* Just copy the pointer to the static key. */
X				buck->key = key;
X				buck->flags |= RF_STATIC;
X				}
X			else {
X				/* Allocate and copy the key. */
X				buck->key = perm_alloc( 1 + strlen(key) );
X				strcpy( buck->key, key );
X				}
X
X			buck->flags |= RF_LAST;
X
X			/* Link the new item into the bucket list, as
X			 * either the first item in a new list, or as
X			 * the final item on an existing one. */
X			if( depth ) {
X				buck->next = obuck->next;
X				obuck->next = buck;
X				obuck->flags &= ~RF_LAST;
X				}
X			else {
X				dbvar->table[buck_num] = buck;
X				if( obuck = pred_db( dbvar, buck_num ) ) {
X					buck->next = obuck->next;
X					obuck->next = buck;
X					}
X				else
X					buck->next = ante_db( dbvar, buck_num );
X				}
X
X			dbvar->n_elements++;
X			dirty_db(dbvar);
X			return( buck );
X			}
X		/*NOTREACHED*/
X		break;
X	    case HA_FIND:
X		/* Simply return the located record or NULL. */
X		return( found ? buck : (dbrec *) NULL );
X		/*NOTREACHED*/
X		break;
X	    case HA_DEL:
X		if( !found )
X			return( (dbrec *) NULL );
X
X		/* Excise the item from the list of buckets -- either from
X		 * the head of the list, or from an internal position.  */
X
X		if( depth ) {
X			obuck->next = buck->next;
X			if( buck->flags & RF_LAST )
X				obuck->flags |= RF_LAST;
X			}
X		else {
X			dbvar->table[buck_num] = (buck->flags & RF_LAST) ?
X						 (dbrec *) NULL : buck->next;
X			if( obuck = pred_db( dbvar, buck_num ) )
X				obuck->next = buck->next;
X			}
X
X		if( !(buck->flags & RF_STATIC) )
X			perm_free( buck->key );
X
X		perm_free( buck );
X
X		dbvar->n_elements--;
X
X		/* This return value can only be tested against NULL. */
X		return( buck );
X		/*NOTREACHED*/
X		break;
X	    default:
X		error( "db -- illegal action code" );
X		break;
X	    }
X}
X
X/* hash_key_fn() defines the hash function for a given ASCIIZ key. */
X
Xstatic unsigned int
Xhash_key_fn( key )
Xchar *key;			/* Key on which to hash.		*/
X{
X	unsigned int fn = 0;	/* Result of the hash function.		*/
X
X	while( *key )
X		fn = fn*37 ^ (*key++ - ' ');
X
X	return( fn % 1048583 );
X}
X
X/* pred_db() skips _backward_ through the hash table bucket array,
X * looking for a hash chain; a pointer to the _final_ entry in the chain
X * is returned if one is found, otherwise NULL is the result. */
X
Xstatic dbrec *
Xpred_db( dbvar, num )
Xdbptr dbvar;
Xint num;
X{
X	dbrec *buck;
X
X	for( --num; num >= 0; num-- )
X		if( dbvar->table[num] )
X			break;
X
X	if( num >= 0 ) {
X		buck = dbvar->table[num];
X		for( ; !(buck->flags & RF_LAST); buck = buck->next )
X			;
X		return( buck );
X		}
X
X	return( (dbrec *) NULL );
X}
X
X/* ante_db() skips _forward_ through the hash table array, looking
X * for a hash chain; if found, a pointer to the _first_ entry on the
X * chain is returned, otherwise, NULL is the result. */
X
Xstatic dbrec *
Xante_db( dbvar, num )
Xdbptr dbvar;
Xint num;
X{
X	int size;
X
X	for( size = dbvar->tbl_size, num++ ; num < size; num++ )
X		if( dbvar->table[num] )
X			return( dbvar->table[num] );
X
X	return( (dbrec *) NULL );
X}
X
X#define MAX_ONE_PER_BUCKET   200  /* If estimate is less than this, use 1... */
X#define DEF_RECS_PER_BUCKET    3  /* ...otherwise, use 3 records/hash bucket. */
X
Xtypedef struct {
X	unsigned int estimate;
X	unsigned int prime;
X	} ptabent;
X
Xptabent primetab[] =  {	{ 51200, 51203 },
X			{ 25600, 25601 },
X			{ 12800, 12809 },
X			{  6400,  6421 },
X			{  3200,  3203 },
X			{  1600,  1601 },
X			{   800,   809 },
X			{   400,   401 },
X			{   200,   211 },
X			{   100,   101 },
X			{    50,    53 },
X			{     0,     0 } };
X
X/* guess_tbl_size() attempts to determine a good table size, given an
X * estimate of the number of records to be allocated within the database. */
X
Xstatic int
Xguess_tbl_size( est_size )
Xunsigned int est_size;
X{
X	int r_per_b = 1;		/* Number of records per table entry. */
X	int i, tsize;
X
X	if( est_size > MAX_ONE_PER_BUCKET )
X		r_per_b = DEF_RECS_PER_BUCKET;
X
X	est_size /= r_per_b;
X
X	for( i = 1, tsize = sizeof(primetab)/sizeof(ptabent); i < tsize; i++ ) {
X		if( primetab[i].estimate <= est_size )
X			break;
X		}
X	return( primetab[i-1].prime );
X}
END_OF_FILE
if test 9874 -ne `wc -c <'db.c'`; then
    echo shar: \"'db.c'\" unpacked with wrong size!
fi
# end of 'db.c'
fi
if test -f 'main.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'main.c'\"
else
echo shar: Extracting \"'main.c'\" \(9794 characters\)
sed "s/^X//" >'main.c' <<'END_OF_FILE'
X
X
X#include "nl.h"
X
X/* The main entry point of the user's newsclip program. */
X /*
X  * Newsclip(TM) Library Source Code.
X  * Copyright 1989 Looking Glass Software Limited.  All Rights Reserved.
X  * Unless otherwise licenced, the only authorized use of this source
X  * code is compilation into a binary of the newsclip library for the
X  * use of licenced Newsclip customers.  Minor source code modifications
X  * are allowed.
X  * Use of this code for a short term evaluation of the product, as defined
X  * in the associated file, 'Licence', is permitted.
X  */
X
X
X/* Some useful variables */
X#include <sys/timeb.h>
X
X
Xchar *uopts[MAX_OPTS];			/* command line options for user prog */
Xint opt_count = 0;			/* number of such options */
X
Xint io_mode = MODE_NONE;		/* article list source */
Xbool list_always = FALSE;		/* always list articles to stdout */
Xbool out_newsrc = FALSE;		/* do we write out a .newsrc file? */
Xbool only_las = FALSE;			/* only do groups in .las file */
Xbool allow_unsub = FALSE;		/* allow unsubscribed groups */
Xbool newsrc_allread = FALSE;		/* mark .newsrc as entirely read */
Xbool rnlock_present = FALSE;		/* is a .rnlock file there? */
Xbool do_debug = FALSE;			/* various debugging flags */
Xchar *proc_mode = 0;			/* processing mode name */
X
X#ifndef PAID			/* do not define until copy is registered */
Xstatic datehold fct =
X#include "cdate.h"
X;
X#endif
X
Xmain( argc, argv )
Xint argc;	/* If you don't know what these are, why are you reading */
Xchar **argv;	/* C source code?   Ok, they're the argument vector & count*/
X{
X	int argnum;			/* arg scan loop control variable */
X	char *strchr();
X	extern long time();		/* no C */
X	extern char *my_mail_address;	/* user's From: line address */
X	extern char *newsrcname;	/* location of .newsrc file */
X	extern char *lasname;		/* name of last art seen file */
X	extern bool normal_newsrc;	/* default .newsrc file? */
X	extern char *news_spool_dir;	/* to set different spool dir */
X	extern char *news_lib_dir;	/* and library dir */
X	extern char *dotdir;
X	extern int warning_level;	/* what warnings to give */
X
X	for( argnum = 1; argnum < argc; argnum++ ) {
X		char *argline;		/* whole argument */
X		char *argstr;		/* argument string */
X		int argval;		/* for decimal options */
X		int isplus;		/* boolean tells +arg vs -arg */
X
X		argline = argv[argnum];
X
X		if (argstr = strchr(argline, '=')) {
X			argstr++;
X			argval = atoi(argstr);
X			switch( argline[0] ) {
X				case 'm':
X					switch( argstr[0] ) {
X						case 'n':
X							/* newsrc to newsrc */
X							io_mode = MODE_NEWSRC;
X							out_newsrc = TRUE;
X							list_always = FALSE;
X							newsrc_allread = FALSE;
X							proc_mode = "newsrc";
X							/* debug */
X							break;
X						case 'f':
X							/* list to list */
X							io_mode = MODE_FLIST;
X							proc_mode = "filter";
X							break;
X						case 'p':
X							io_mode = MODE_PIPE;
X							proc_mode = "pipe";
X							break;
X						case 'b':
X							/* newsrc to both */
X							io_mode = MODE_NEWSRC;
X							out_newsrc = TRUE;
X							newsrc_allread = TRUE;
X							list_always = TRUE;
X							proc_mode = "batch";
X							break;
X						case 'l':
X							/* newsrc to list */
X							io_mode = MODE_NEWSRC;
X							out_newsrc = FALSE;
X							list_always = TRUE;
X							newsrc_allread = FALSE;
X							proc_mode = "list";
X							break;
X						case 'd': /* debug rc output*/
X							io_mode = MODE_NEWSRC;
X							out_newsrc = TRUE;
X							list_always = TRUE;
X							newsrc_allread = FALSE;
X							proc_mode = "debug";
X							break;
X						default:
X							error( "Unknown mode %s\n", argstr );
X							break;
X							}
X					break;
X				case 'o':	/* option for program */
X					if( opt_count < MAX_OPTS )
X						uopts[opt_count++] = argstr;
X					 else
X						error( "Too many user options\n");
X					break;
X				case 'd':
X					dotdir = argstr;
X					break;
X				case 'n':
X					newsrcname = argstr;
X					break;
X				case 'l':
X					lasname = argstr;
X					break;
X				case 'L':
X					news_lib_dir = argstr;
X					break;
X				case 'S':
X					news_spool_dir = argstr;
X					break;
X				case 'w':
X					warning_level = argval;
X					break;
X				default:
X					error( "Bad Option %s\n", argline );
X				}
X			}
X		else if( (isplus = argline[0] == '+') || argline[0] == '-' ) {
X			switch( argline[1] ) {
X				case 'o':
X					only_las = isplus;
X					break;
X				case 'u':
X					allow_unsub = isplus;
X					break;
X				case 'd':
X					do_debug = isplus;
X					break;
X				default:
X					error( "Bad Option %s\n", argline );
X				}
X			}
X		else {
X			/* code for untagged option */
X			;
X			}
X		}
X	if( io_mode == MODE_NONE ) {
X		fprintf( stderr, "NewsClip News Filtering Program, V1.01:\n" );
X#ifndef PAID
X		fprintf( stderr, "\t\t***   EVALUATION COPY  ***\n\n" );
X#endif
X		fprintf( stderr, "\tLibrary Copyright (c) 1989 Looking Glass Software Limited.\n\n" );
X		fprintf( stderr, "Usage: %s mode=<mode> [options]\n", argv[0] );
X		fprintf( stderr, "You must specify a mode of operation.  To work on your .newsrc, use:\n" );
X		fprintf( stderr, "\t%s mode=newsrc\n", argv[0] );
X		fprintf( stderr, "For other options see the manual page for 'nclip' or your manual.\n" );
X		exit(1);
X		}
X
X	/* record the time of day */
X	init_time();
X#ifndef PAID			/* do not define until copy is registered */
X	checkpaid();
X#endif
X	init_whoami();
X	lowercase( my_mail_address );
X	/* what about full name? */
X
X	/* if we plan to write out the newsrc, make sure rn isn't using it */
X	if( io_mode == MODE_NEWSRC && out_newsrc && normal_newsrc )
X		do_rnlock();
X
X	/* build the user 'options' array */
X
X	prep_uopts();
X
X	/* compile regular expressions */
X
X	init_patterns();
X
X	/* initialize temporary memory allocator */
X
X	init_tempalloc();
X
X	switch( io_mode ) {
X		case MODE_FLIST:
X			do_file_list();
X			break;
X		case MODE_NEWSRC:
X			process_newsrc();
X			break;
X		case MODE_PIPE:
X			pipe_loop();
X			break;
X		}
X	wrapup();
X	exit(0);
X}
X
X/* Issue an error message, do any wrapup, and abort */
X
Xerror( form, a, b, c, d, e, f, g, h )
Xchar *form;
X{
X	fprintf( stderr, form, a,b,c,d,e,f,g,h );
X	wrapup();
X	exit(1);
X}
X
X/* do any wrapup needed before terminating */
X
Xwrapup()
X{
X	if( rnlock_present ) {
X		chdir( dotdir );
X		unlink( ".rnlock" );
X		rnlock_present = FALSE;
X		}
X}
X
X
X#include <signal.h>
X
X/*
X * Check for a .rnlock file, and create one if not present
X *
X * RN stops two 'rn' processes from going at the same .newsrc by placing
X * the .rnlock file in the "dot" directory while it is running.  This file
X * has the process id of the newsreader, so that if another program finds
X * the file, it can check (via a pre-arranged signal) if that other
X * newsreader is still running, or has died.
X */
X
Xdo_rnlock()
X{
X	FILE *rnlock;			/* descriptor for rnlock locking file */
X	extern char *dotdir;
X	int pid;			/* process id of other newsreader */
X	int scanres;
X
X	rnlock = dfopen( dotdir, ".rnlock", "r" );
X	if( rnlock ) {
X		scanres = fscanf( rnlock, "%d", &pid );
X		fprintf( stderr, "A news reader is/was running, process %d\n", pid );
X		if( scanres != 1 || kill( pid, SIGEMT ) ) 
X			fprintf( stderr, "But it died, so we will proceed.\n" );
X		else
X			error( "You can't run this program in 'newsrc' mode while another program is\nusing that file." );
X		fclose( rnlock );
X		}
X	rnlock = dfopen( dotdir, ".rnlock", "w" );
X	if( !rnlock )
X		error( "Could not create .rnlock file.\n" );
X	rnlock_present = TRUE;
X	fprintf( rnlock, "%d\n", getpid() );
X	fclose( rnlock );
X	/* set up to ignore that signal so we can be tested */
X	signal( SIGEMT, SIG_IGN );
X}
X
X#ifndef PAID			/* do not define until copy is registered */
X	/* This program is EVALU-Ware.  That means that you get to try
X	   out this full working copy for a couple of weeks.  After that
X	   you must decide to either pay for a licence or stop using the
X	   program.   This code reminds you of this.   Nothing but your
X	   own honesty (and copyright law) stops you from removing this code.
X
X	   Please do not remove this code unless you have actually paid for
X	   a registration.  You can send a P.O. or pay by major credit card
X	   by phoning 519-884-7473, or in some areas, 800-265-2782.  See the
X	   file 'licence' for full details.   Many of you will be tempted
X	   to remove this code, saying, "I'll get around to paying eventually."
X
X	   Our experience shows that even highly honest people who say this
X	   often don't pay.  Not through dishonesty, but lack of time.  This
X	   filtering language can save you a lot of valuable time when reading
X	   news, and a lot of money and/or bandwidth if you filter news sent
X	   over network connections or long distance phone lines.  As such,
X	   we have set what we believe to be a fair price for ongoing use of
X	   this system.  Thank you for respecting that wish.
X	 */
X
Xcheckpaid()
X{
X	extern datehold time_now;	
X	if( time_now - fct > 3600L*24*23 ) {
X		fprintf( stderr, "You are using an evaluation copy of the NewsClip language.\n\n" );
X		fprintf( stderr, "We would like to remind you that the 20 day evaluation period is now over.\n" );
X		fprintf( stderr, "If you wish to continue using this system, you should purchase a licence.\n" );
X		fprintf( stderr, "You may phone 519-884-7473 to do so, or mail to 'newsclip@clarinet.com'" );
X		fprintf( stderr, "for more details.   Major credit cards and P.O.s are accpted.\n" );
X		fprintf( stderr, "See the file 'Licence' in the NewsClip source distribution for full\n" );
X		fprintf( stderr, "information.\n" );
X
X		if( time_now - fct > 3600L*24*31 )
X			fprintf( stderr, "\nYour evaluation period is now long over.  Please register.\n" );
X		}
X}
X
X#endif
X/* prepare user options */
X
X
Xarray *options;
X
X/* prepare the 'options' variable for the user program */
X
Xprep_uopts()
X{
X	int i;
X
X	options = (array*)perm_alloc(sizeof(array)+(opt_count-1)*sizeof(datau));
X	options->arsize = opt_count;
X	options->artype = T_STRING;
X	/* copy in each option */
X	for( i = 0; i < opt_count; i++ )
X		options->vals[i].ustring = uopts[i];
X}
X
END_OF_FILE
if test 9794 -ne `wc -c <'main.c'`; then
    echo shar: \"'main.c'\" unpacked with wrong size!
fi
# end of 'main.c'
fi
if test -f 'mknewsrc.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'mknewsrc.c'\"
else
echo shar: Extracting \"'mknewsrc.c'\" \(11039 characters\)
sed "s/^X//" >'mknewsrc.c' <<'END_OF_FILE'
X/*
X * mknewsrc.c
X * 
X * mknewsrc -- reads the active file and creates a .newsrc file containing
X * all newsgroups found in the file matching the default argument patterns,
X * with all active news articles marked as unread.   If the +n option is
X * specified, newsgroups are also required to be found in the user's
X * existing .newsrc file, sought for in the DOTDIR directory.
X *
X * The created .newsrc format file, is written to stdout, and coupled with
X * the newsclip software, is useful for searching all news on the machine.
X *
X * Copyright 1989 Looking Glass Software Limited.
X * Use of this code for a short term evaluation of the product, as defined
X * in the associated file, 'Licence', is permitted.
X *
X */
X
X#include <stdio.h>
X#include <string.h>
X#include "sysdefs.h"
X#include "rei.h"
X#include "db.h"
X
Xextern char *newsrcname;	/* Full path and name of the user's .newsrc */
Xextern char *dotdir;		/* Name of the user's DOTDIR */
Xextern char *news_lib_dir;	/* Name of the news directory */
X#define ACTIVE	"active"	/* Name of the active file in news directory */
X#define NEWSRC	".newsrc"	/* Name of the user newsrc file in his DOTDIR */
X
Xchar *Prgname;			/* Program invocation name. */
X
Xint  Percentage = 100;		/* Percentage of articles to place as uread. */
Xchar *ActiveName;		/* Full name and path of the active file */
XFILE *ActiveFile;		/* File descriptor for the active file */
Xchar *Newsrc = (char *) NULL;	/* Name and path of newsrc file to be used */
XFILE *NewsrcFile;		/* File descriptor for the newsrc file */
Xchar  UseNewsrc = 0;		/* Flag corresponding to setting of +n option */
X
Xrxp_type *Rxps = (rxp_type *) NULL;	/* Ptr to array of compiled REs */
X
X#define DB_FORMAT_R "%255s %ld %ld %c\n"   /* Read format for active file. */
X#define N_DB_FIELDS	  4		   /* 4 data fields in active file. */
X#define MAX_NAMELEN	255		   /* 255 max bytes in group names. */
X#define MAX_LINELEN     256		   /* .25K max line length. */
X
Xchar  ReadBuffer[MAX_LINELEN];	/* Generic buffer used when reading files. */
X
X#define GROUP_PAT   "^[^ \t:!,<]+:"  /* Pattern representing valid newsgroup */
X				     /* names in the user's newsrc file. */
X
Xtypedef struct _db {
X	char *key;			/* Name of the newsgroup */
X	struct _db *next;
X	unsigned char flags;
X	char status;			/* Status in active file */
X	long lownum;			/* Lowest acticle */
X	long highnum;			/* Highest acticle */
X	} act_rec;
X
Xextern void init_whoami AC(( void ));
Xextern char *perm_alloc AC(( int ));
Xextern char *getenv AC(( char * ));
X
Xvoid  open_files AC(( void ));
Xvoid  close_files AC(( void ));
Xchar *make_fullname AC(( char *, char * ));
Xvoid  do_process AC(( void ));
Xvoid  drive_newsrc AC(( FILE *, FILE * ));
Xvoid  drive_active AC(( FILE * ));
Xvoid  try_group AC(( char *, long, long ));
Xchar *allocstring AC(( char * ));
Xdbptr read_database AC(( FILE * ));
X
Xint
Xmain( argc, argv )
Xint argc;
Xchar *argv[];
X{
X	int i, isplus, nrexps = 0;
X	char *argstr, *argline;
X
X	if( !(Prgname = strrchr( argv[0], '/' )) )
X		Prgname = argv[0];
X	else
X		Prgname++;
X
X	if( argc > 1 ) {
X		/* If command line group patterns have been specified,
X		 * allocate space for and and compile them. */
X		Rxps = (rxp_type *) perm_alloc( sizeof(rxp_type)*argc );
X
X		for( i = 1; i < argc; i++ ) {
X			argline = argv[i];
X			if( argstr = strchr( argline, '=' ) ) {
X				if( !*++argstr )
X					error(
X			 "missing argument for %c= option", argline[0] );
X				switch( argline[0] ) {
X				    case 'p':
X					Percentage = atoi( argstr );
X					break;
X				    case 'n':
X					Newsrc = allocstring( argstr );
X					UseNewsrc = 1;
X					break;
X				    default:
X					error( "illegal option %s\n", argline );
X				    }
X				}
X			else if( (isplus = argline[0] == '+') ||
X				  argline[0] == '-' ) {
X				switch( argline[1] ) {
X				    case 'n':
X					UseNewsrc = isplus;
X					break;
X				    default:
X					error( "illegal option %s\n", argline );
X				    }
X				}
X			else {
X				/* Untagged options are assumed to be patterns
X				 * for regular expression compilation. */
X				if( !(Rxps[nrexps++] = REG_COMP_P( argv[i] )) )
X					/* An error was detected in the RE;
X					 * since regcomp already issued a
X					 * warning, so just leave quietly. */
X					exit( 2 );
X				}
X			}
X
X		if( nrexps ) {
X			/* If any regular expressions were found, terminate
X			 * the list of expressions with a null pointer. */
X			Rxps[nrexps] = (rxp_type) NULL;
X			}
X		else {
X			/* No regular expressions were specified on the command
X			 * line, so free the allocated memory, and indicate
X			 * that all groups are to be accepted. */
X			perm_free( Rxps );
X			Rxps = (rxp_type *) NULL;
X			}
X
X		if( Percentage > 100 )
X			Percentage = 100;
X		else if( Percentage < 0 )
X			Percentage = 0;
X		}
X
X	open_files();		/* Open the necessary input/output files. */
X
X	if( UseNewsrc )
X		drive_newsrc( ActiveFile, NewsrcFile );
X	else
X		drive_active( ActiveFile );
X
X	close_files();		/* Close the files and clean up. */
X
X	return( 0 );
X}
X
Xvoid
Xopen_files()
X{
X	init_whoami();
X
X	ActiveName = make_fullname( news_lib_dir, ACTIVE );
X
X	if( !(ActiveFile = fopen( ActiveName, "r" )) )
X		error( "cannot open active file \"%s\"", ActiveName );
X
X	if( UseNewsrc ) {
X		if( !Newsrc )
X			Newsrc = newsrcname;
X		if( !(NewsrcFile = fopen( Newsrc, "r" )) )
X			error( "cannot open newsrc file \"%s\"", Newsrc );
X		}
X}
X
Xchar *
Xmake_fullname( dir, name )
Xchar *dir, *name;
X{
X	int nlen, dlen, add_slash = 0;
X	char *fname;
X
X	dlen = strlen( dir );
X	if( dir[dlen] != '/' )
X		add_slash++;
X	nlen = strlen( name ) + dlen + add_slash + 1;
X	fname = (char *) perm_alloc( nlen*sizeof(char) );
X	strcpy( fname, dir );
X	if( add_slash )
X		fname[dlen++] = '/';
X	strcpy( fname + dlen, name );
X
X	return( fname );
X}
X
Xvoid
Xclose_files()
X{
X	fclose( ActiveFile );
X	perm_free( ActiveName );
X
X	if( UseNewsrc ) {
X		fclose( NewsrcFile );
X		perm_free( Newsrc );
X		}
X}
X
X/*
X * If in "read .newsrc" mode, then the entire active file is read into
X * a database for subsequent searches, and the user's newsrc file is
X * opened, and read line by line.  If the line matches any of the
X * optionally specified RE patterns,  then the active file information
X * is looked up in the database and used to create the new newsrc record.
X *
X * If not in "read newsrc" mode, then the active file is incrementally
X * read, and each group matched against the user specified RE patterns,
X * if any.
X */
X
Xvoid
Xdrive_active( afptr )
XFILE *afptr;		/* File descriptor for the active file. */
X{
X	char *groupname = ReadBuffer;
X	long lownum, highnum;
X	char status;
X	int i, fstatus, accept;
X
X	do {
X		if( N_DB_FIELDS == (fstatus = fscanf( afptr, DB_FORMAT_R,
X		      groupname, &highnum, &lownum, &status )) ) {
X			try_group( groupname, lownum, highnum );
X			}
X		else if( EOF != fstatus ) {
X			int ch;
X
X			warning( 1, "format error in active file" );
X			while( (ch = getc( afptr )) != '\n' ) {
X				if( ch == EOF ) {
X					fstatus = EOF;
X					break;
X					}
X				}
X			}
X		} while( EOF != fstatus );
X}
X
Xvoid
Xdrive_newsrc( afptr, nfptr )
XFILE *afptr;		/* File descriptor for the active file. */
XFILE *nfptr;		/* File descriptor for the user's newsrc file. */
X{
X	dbptr actdb;
X	char *groupname = ReadBuffer;
X	char *ptr;
X	act_rec *item;
X	rxp_type rxp;
X
X	actdb = read_database( afptr );
X	rxp = REG_COMP_P( GROUP_PAT );
X
X	while( ptr = fgets( groupname, MAX_LINELEN, nfptr ) ) {
X		if( !REG_EXEC( rxp, ptr ) ) {
X			/* Uncomment the following line to output the line,
X			 * if desired.  Note that the line will be truncated
X			 * at MAX_LINELEN characters, so it would likely be
X			 * advisable to increase MAX_LINELEN as well. */
X
X			/* Copy the unrecognized line to the output stream. */
X			/* fputs( groupname, stdout ); */
X			continue;
X			}
X
X		*(rxp->endp[0]-1) = '\0'; /* Null terminate newsgroup name */
X					  /* (by stomping on trailing ":") */
X
X		if( !(item = (act_rec *) get_rec( actdb, groupname )) ) {
X			warning( 2, "Ignoring bogus newsgroup \"%s\"", groupname );
X			continue;
X			}
X		try_group( groupname, item->lownum, item->highnum );
X		}
X
X	REG_FREE_P( rxp );
X}
X
Xvoid
Xtry_group( name, lownum, highnum )
Xchar *name;
Xlong lownum, highnum;
X{
X	int accept, i;
X
X	if( !Rxps )
X		accept = 1;
X	else {
X		accept = 0;
X		for( i = 0; Rxps[i]; i++ )
X			if( REG_EXEC( Rxps[i], name ) ) {
X				accept = 1;
X				break;
X				}
X		}
X
X	if( accept ) {
X		if( highnum < lownum ) {
X			/* A quick sanity check.  Perhaps a
X			 * warning should be issued here? */
X			highnum = lownum;
X			warning( 1, "\"%s\": bogus values in active file", name );
X			}
X
X		if( Percentage == 100 )
X			/* 100% of articles are to be marked as
X			 * unread, so take one less than low value. */
X			lownum--;
X
X		else if( Percentage == 0 )
X			/* All articles are to be marked as
X			 * read, so make limit the high value. */
X			lownum = highnum;
X
X		else
X			/* Oh, well -- have to do some actual calculations
X			 * to compute the number of articles. */
X			lownum += (long) (0.5 + (highnum - lownum)*
X				  ((double)(100 - Percentage)/100));
X
X		printf( "%s:", name );
X		if( lownum > 0 )
X			printf( " 1-%ld", lownum );
X		putchar( '\n' );
X		}
X}
X
Xdbptr
Xread_database( fptr )
XFILE *fptr;			/* Stream containing the active file. */
X{
X	dbptr dbvar;		/* Hash table used for this database.	*/
X	int fstatus;		/* File status indicator.		*/
X	char *strbuff = ReadBuffer;  /* Buffer for string entries.	*/
X	long low, high;
X	char status;
X	act_rec *item;
X
X	dbvar = init_db( 450, sizeof(act_rec) );
X
X	do {
X		/* If required, allocate new memory for
X		 * the next record from the database. */
X
X		if( N_DB_FIELDS == (fstatus = fscanf( fptr, DB_FORMAT_R,
X		      strbuff, &high, &low, &status )) ) {
X			if( item = (act_rec *)
X			    add_rec( dbvar, &strbuff[0], AR_CREATE ) ) {
X				item->lownum = low;
X				item->highnum = high;
X				item->status = status;
X				}
X			else
X				warning( 1, "Error adding database record" );
X			}
X		else if( EOF != fstatus ) {
X			int ch;
X
X			warning( 1, "Format error in active file" );
X			while( (ch = getc( fptr )) != '\n' ) {
X				if( ch == EOF ) {
X					fstatus = EOF;
X					break;
X					}
X				}
X			}
X		} while( EOF != fstatus );
X
X	return( (dbptr) dbvar );
X}
X
Xerror( ptr, a1, a2, a3, a4, a5, a6 )
Xchar *ptr;
Xint a1, a2, a3, a4, a5, a6;
X{
X	fprintf( stderr, "%s: ", Prgname );
X	fprintf( stderr, ptr, a1, a2, a3, a4, a5, a6 );
X	fputc( '\n', stderr );
X	exit( 1 );
X}
X
Xint warning_level = 1;
X
Xwarning( level, ptr, a1, a2, a3, a4, a5, a6 )
Xint level;		/* give warnings below a certain level */
Xchar *ptr;
Xint a1, a2, a3, a4, a5, a6;
X{
X	if( level <= warning_level ) {
X		fprintf( stderr, "%s: ", Prgname );
X		fprintf( stderr, ptr, a1, a2, a3, a4, a5, a6 );
X		fputc( '\n', stderr );
X		}
X}
X
Xchar *
Xallocstring( str )
Xchar *str;
X{
X	char *res;
X
X	res = perm_alloc( strlen(str) + 1 );
X	strcpy( res, str );
X
X	return( res );
X}
X
X/* Zero out a region of memory.  Your system may have a routine
X * around to do this faster.  If so, remove this and use your own. */
X
Xzero( mem, bytes )
Xchar *mem;
Xint bytes;
X{
X	register int *p;
X	register char *cp;
X	int words, extra;
X
X	words = bytes/sizeof(int);
X	extra = bytes - sizeof(int)*words;
X
X	p = (int *)mem;
X	while( words-- )
X		*p++ = 0;
X	cp = (char *)p;
X	while( extra-- )
X		*cp++ = 0;
X}
END_OF_FILE
if test 11039 -ne `wc -c <'mknewsrc.c'`; then
    echo shar: \"'mknewsrc.c'\" unpacked with wrong size!
fi
# end of 'mknewsrc.c'
fi
if test -f 'patch/filtpipe.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'patch/filtpipe.c'\"
else
echo shar: Extracting \"'patch/filtpipe.c'\" \(10981 characters\)
sed "s/^X//" >'patch/filtpipe.c' <<'END_OF_FILE'
X
X#include <stdio.h>
X#include <signal.h>
X#include "pipemode.h"
X/*
X *
X * Routines to insert into a news reader to use newsfilter pipe mode
X * By: Brad Templeton
X * This protocol is documented in 'filter.man' in the NewsClip doc
X * directory.
X */
X
X /* Unlike the other files in the NewsClip distribution, this file is
X  * released to the public domain.
X  */
X
Xstatic int clip_running = 0;		/* is a filter running? */
X
Xstatic int compipe;			/* device for the command pipe */
Xstatic int anspipe;			/* device for the answer pipe */
X
XFILE *pipelog = 0;			/* debugging log */
X
Xfilter_init( dotdir )
Xchar *dotdir;			/* the directory for user news programs */
X{
X	int com_pipe[2];	/* descriptor pair for command pipe */
X	int ans_pipe[2];	/* descriptor pair for answer pipe */
X	int child;		/* child pid */
X
X
X	if( pipe( com_pipe ) ) 
X		return -1;		/* failure opening pipes */
X	if( pipe( ans_pipe ) ) {
X		close( com_pipe[0] );
X		close( com_pipe[1] );
X		return -1;
X		}
X	/* the two pipes are open, now fork to run news filter */
X	if( child = fork() ) {
X		/* this is the parent */
X
X		/* close the unused ends of the pipes */
X		close( com_pipe[0] );	/* reading end of command pipe */
X		close( ans_pipe[1] );	/* writing end of answer pipe */
X		compipe = com_pipe[1];
X		anspipe = ans_pipe[0];
X		if( child == -1 ) {
X			bugerr( "Fork Failure" );
X			close( compipe );
X			close( anspipe );
X			return -1;
X			}
X		/* await the answer of the child */
X		return parent_init(child);
X		}
X	 else {
X		close( com_pipe[1] );	/* writing end of command pipe */
X		close( ans_pipe[0] );	/* reading end of answer pipe */
X		
X		filter_process(dotdir, com_pipe[0], ans_pipe[1]);
X		}
X
X}
X
X/* the code to execute the news filter */
X
Xstatic
Xfilter_process(dotdir, reader, writer)
Xchar *dotdir;				/* directory to run program in */
Xint reader, writer;			/* pipe descriptors */
X{
X	char *cliploc;
X	char ncdir[255];		/* enough to hold a directory name? */
X	char *getenv();
X	/* prepare stdin and stdout */
X
X	close( 1 );
X	if( dup( writer ) != 1 ) {
X		bugerr( "dup writer failure" );
X		fork_failed( writer );
X		}
X
X	close( 0 );
X	if( dup( reader ) != 0 ) {
X		bugerr( "Dup reader failure" );
X		fork_failed( writer );
X		}
X
X	close( reader );
X	close( writer );
X
X	/* we now read commands from stdin, write answers to stdout */
X
X	cliploc = getenv( "NCLIP" );
X	if( !cliploc ) {
X		if( chdir( dotdir ) ) {
X			bugerr( "Chdir failure" );
X			/* failed to chdir */
X			fork_failed(1);
X			}
X		cliploc = "nclip";
X		}
X	sprintf( ncdir, "dot=%s", dotdir );
X	/* You may want to filter off signals the parent is handling
X	   at this point */
X	execl( cliploc, "nclip", "mode=pipe", ncdir, 0 );
X	/* did that fail?  try execlp of nclip */
X	execlp( "nclip", "nclip", "mode=pipe", ncdir, 0 );
X	/* oh no, that failed too.  I guess we blew it */
X	/* bugerr( "Exec failure" ); */
X	fork_failed(1);
X}
X
X/* This is called if we find the end of the pipe broke off.  We assume
X   that the filter process is dead or funny */
X
Xdeadkid()
X{
X	clip_running = FALSE;		/* disable filtering */
X	if( compipe > 0 ) {
X		close( compipe );
X		compipe = -1;
X		}
X	if( anspipe >= 0 ) {
X		close( anspipe );
X		anspipe = -1;
X		}
X	fprintf( stderr, "\nNews Filter Process Aborted.\n" );
X}
X
Xstatic int filter_pid;
X
X/* Initialize the parent.
X * We use the ALARM signal to deal with a news filter process that never
X * starts up or otherwise acts funny.  This way the newsreader does not
X * block forever waiting for it.
X * If the newsreader uses the alarm, it must adapt this code a bit.
X */
X
Xstatic
Xparent_init(child)
Xint child;
X{
X
X	char repargs[MAX_ARGLEN];		/* reply arguments */
X	int argc;			/* count of arguments to command */
X	char *argv[MAX_ARGS];		/* argument pointer vector */
X
X	/* debug */
X	/* pipelog = fopen( "/tmp/rpipelog", "w" ); */
X	if( pipelog )
X		setbuf( pipelog, NULL );
X	/* set up to ignore alarm clock, as we only want it to halt the
X	   pipe read if it fails */
X	signal( SIGALRM, SIG_IGN );
X	/* in case child dies, avoid this signal */
X	signal( SIGPIPE, deadkid );
X
X	alarm( 20 );			/* give process 20 seconds to start */
X
X	argc = read_answer( repargs, argv, MAX_ARGS );
X
X	alarm( 0 );
X
X	if( argc > 0 && argv[0][1] == 'O' ) {
X		/* ok, we are in communication */
X		/* send version command */
X		gen_send( 'C', 'V', "V100", NULL );
X		argc = read_answer( repargs, argv, MAX_ARGS );
X		if( argc > 1 && argv[0][1] == 'V' && atoi(argv[1]+1) >= 100 ) {
X			/* we are in business! */
X			clip_running = TRUE;
X			filter_pid = child;
X			return 0;
X			}
X		bugerr( "Version response failure" );
X		}
X	 else {
X		if( argc > 0 ) {
X			printf( "\nNo News Filter Present\n" );
X			fflush(stdout);
X			}
X		 else
X			bugerr( "initial response failure -- no response" );
X		}
X	close( compipe );
X	close( anspipe );
X	/* something failed */
X	kill( child, SIGTERM );
X	return -1;
X}
X
X/* Routine to tell that the fork failed */
X
X/* a message of error */
Xstatic struct command error_buf = { 'R', 'E', ' ', "-1", ' ', "0", 0 };
X
Xstatic
Xfork_failed(desc)
Xint desc;		/* descriptor to describe failure with */
X{
X	write( desc, error_buf, sizeof(error_buf) );
X	exit(1);
X}
X
X
X /* low level reply routine */
Xstatic
Xgen_send( ctype, code, a,b,c,d,e )
Xchar ctype;			/* command type */
Xchar code;			/* command code */
Xchar *a,*b,*c,*d,*e;		/* args */
X{
X	struct command cmd_buf;
X	char comargs[MAX_ARGLEN];
X	char *vector[6];
X	int i, pos;		/* loop counter and position in comargs */
X
X	if( compipe < 0 )
X		return;
X
X	cmd_buf.comtype = ctype;
X	cmd_buf.comcode = code;
X	cmd_buf.space = ' ';
X
X	/* make a vector out of the various args, up to 5 of them */
X
X	vector[0] = a;
X	vector[1] = b;
X	vector[2] = c;
X	vector[3] = d;
X	vector[4] = e;
X
X	pos = 0;
X	for( i = 0; i < 5 && vector[i]; i++ ) {
X		strcpy( comargs+pos, vector[i] );
X		/* check overflow? */
X		pos += strlen(vector[i]) + 1;
X		}
X	sprintf( cmd_buf.arg_size, "%03d", pos );
X	cmd_buf.zerob =0;
X	/* sequence number is unimportant */
X	sprintf( cmd_buf.seq_num, "%5.5s", "" );
X	cmd_buf.space2 = ' ';
X
X	if( pipelog ) {
X		int ic;
X		fprintf( pipelog, "R:Send %s\n", &cmd_buf.comtype );
X		for( ic = 0; vector[ic]; ic++ )
X			fprintf( pipelog, ":%s:", vector[ic] );
X		fprintf( pipelog, "\n" );
X		}
X	if( write(compipe, &cmd_buf, sizeof(cmd_buf)) == sizeof(cmd_buf)){
X		if( pos > 0 && write( compipe, comargs, pos ) != pos ) {
X			pipe_abort();
X			return -1;
X			}
X		}
X	 else {
X		pipe_abort();
X		return -1;
X		}
X	return 0;
X}
X
Xpipe_abort()
X{
X	if( pipelog )
X		fprintf( pipelog, "Pipe abort\n" );
X	kill( filter_pid, SIGTERM );
X	deadkid();
X}
X
Xstatic struct command resp_buf;		/* command input buffer */
X
Xstatic int
Xread_answer( argument_buf, argv, avsize )
Xchar *argument_buf;	/* buffer to store arguments in */
Xchar **argv;		/* pointer to array to store arg pointers in */
Xint avsize;		/* number of elements in argv */
X{
X	int argsize;		/* size of argument buffer */
X	int argc;		/* argument count */
X	int scanloc;		/* where in the arg list we are */
X	int howmany;
X
X	argc = 0;
X
X	if( anspipe < 0 )
X		return 0;
X
X	howmany = read( anspipe, &resp_buf, sizeof(struct command) );
X	if( pipelog )
X		fprintf( pipelog, "R:Got %d bytes - %s\n", howmany,
X					&resp_buf.comtype );
X	if( howmany == sizeof(struct command) ) {
X		argv[argc++] = &resp_buf.comtype;
X		resp_buf.space = 0;
X		argsize = atoi( resp_buf.arg_size );
X		if( argsize > 0 ) {
X			if( argsize > MAX_ARGLEN ) {
X				int i;
X				char c;
X				/* read away the too long arguments */
X				for( i = 0; i < argsize; i++ )
X					read( anspipe, &c, 1 );
X				return -1;
X				}
X			 else {
X				if( read( anspipe, argument_buf, argsize )
X							!= argsize ) {
X					return 0;
X					}
X				/* if not null terminated, give an error */
X				if( argument_buf[argsize-1] != 0 )
X					return -1;
X				}
X			/* now scan the arguments into argv */
X			for( scanloc = 0; scanloc < argsize;
X				     scanloc += strlen(argument_buf+scanloc)+1 )
X				if( argc < avsize ) {
X					argv[argc++] = argument_buf+scanloc;
X					}
X			if( pipelog ) {
X				int i;
X				for( i = 1; i < argc; i++ )
X					fprintf(pipelog, "R:Got Arg %d: %s\n",
X							i, argv[i] );
X				}
X			}
X		}
X	 else {
X		bugerr( "End of file" );
X		deadkid();
X		return 0;		/* end of file */
X		}
X
X	return argc;
X}
X
X/* Called when the newsreader terminates */
X
Xfilter_quit()
X{
X	char repargs[MAX_ARGLEN];		/* reply arguments */
X	int argc;			/* count of arguments to command */
X	char *argv[MAX_ARGS];		/* argument pointer vector */
X
X	if( clip_running ) {
X		gen_send( 'C', 'Q', NULL );
X		argc = read_answer( repargs, argv, MAX_ARGS );
X		/* if not OK, what to do? */
X		close( compipe );
X		close( anspipe );
X		clip_running = FALSE;
X		if( argc != ABASE && argv[0][1] != 'O' ) {
X			bugerr( "Quit failure" );
X			kill( filter_pid, SIGTERM );
X			}
X		}
X}
X
X/* Now the routines that are used within the RN newsreader to query articles */
X
X/* Returns true if the article is accepted, false otherwise */
X
Xint
Xfilter_art( spool, ngdir, ngname, art )
Xchar *spool;		/* article spool dir or NULL */
Xchar *ngdir;		/* newsgroup directory name */
Xchar *ngname;		/* newsgroup name */
Xlong art;		/* article number */
X{
X	char repargs[MAX_ARGLEN];		/* reply arguments */
X	char artnum[20];			/* article number string */
X	int argc;			/* count of arguments to command */
X	char *argv[MAX_ARGS];		/* argument pointer vector */
X
X	if( !clip_running )
X		return TRUE;
X
X	sprintf( artnum, "%ld", art );
X	/* build article filename */
X	if( ngdir )
X		sprintf( repargs, "%s/%s/%s", spool, ngdir, artnum );
X	 else
X		strncpy( repargs, spool, sizeof(repargs) );
X
X	/* send off request on article */
X	/* We use the 'full' article mode */
X	gen_send( 'C', 'A', ngname, artnum, "F", repargs, NULL );
X
X	/* await response */
X	argc = read_answer( repargs, argv, MAX_ARGS );
X
X	/* any kind of error or message other than Reject means accept */
X	if( argc < 1 || argv[0][1] != 'R' )
X		return TRUE;
X	 else
X		return FALSE;
X
X	/* should we shut down on errors? */
X}
X
X/* indicate if we should filter this group or not */
X
Xno_filter_group( groupname )
X{
X	char repargs[MAX_ARGLEN];		/* reply arguments */
X	int argc;			/* count of arguments to command */
X	char *argv[MAX_ARGS];		/* argument pointer vector */
X
X	if( clip_running ) {
X		gen_send( 'C', 'N', groupname, NULL );
X
X		/* await response */
X		argc = read_answer( repargs, argv, MAX_ARGS );
X	
X		return argc < 1 || argv[0][1] != 'O';
X		}
X	 else
X		return TRUE;		/* do not filter */
X}
X
X
Xbugerr( str )
Xchar *str;
X{
X	fprintf( stderr, "BUG: %s\n", str );
X}
X
X/* accept a command for the filter.  Return true if the command was
X   valid, false if it was not a valid command */
X
Xfilter_command( com )
Xchar *com;
X{
X	char repargs[MAX_ARGLEN];		/* reply arguments */
X	int argc;			/* count of arguments to command */
X	char *argv[MAX_ARGS];		/* argument pointer vector */
X
X	if( clip_running ) {
X		/* truncate command to safe length */
X		strncpy( repargs, com, MAX_ARGLEN-1 );
X		repargs[MAX_ARGLEN-1] = 0;
X		gen_send( 'C', 'P', repargs, NULL );
X
X		/* await response */
X		argc = read_answer( repargs, argv, MAX_ARGS );
X	
X		/* return true if we got OK, false otherwise */
X		return argc > 0 && argv[0][1] == 'O';
X		}
X	 else
X		return FALSE;		/* command not accepted */
X}
END_OF_FILE
if test 10981 -ne `wc -c <'patch/filtpipe.c'`; then
    echo shar: \"'patch/filtpipe.c'\" unpacked with wrong size!
fi
# end of 'patch/filtpipe.c'
fi
echo shar: End of archive 6 \(of 15\).
cp /dev/null ark6isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 15 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0