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