v09i075: newsclip 1.1, part 6 of 15
Brad Templeton
brad at looking.ON.CA
Wed Dec 20 12:24:43 AEST 1989
Posting-number: Volume 9, Issue 75
Submitted-by: brad at 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 at 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 at 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
More information about the Comp.sources.misc
mailing list