[comp.sources.misc] v09i072: newsclip 1.1, part 3 of 15

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

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

#! /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 3 (of 15)."
# Contents:  alloc.c comp/hlist.c comp/ncc.c doc/ncc.1 patch/read.me
#   patch/rnpatch samples/myprog.nc samples/sample.nc sysdefs.x286
# Wrapped by allbery@uunet on Tue Dec 19 20:09:54 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'alloc.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'alloc.c'\"
else
echo shar: Extracting \"'alloc.c'\" \(5588 characters\)
sed "s/^X//" >'alloc.c' <<'END_OF_FILE'
X
X
X/*
X * Allocator routines.
X *
X * These include the temporary allocator, which allocates only for the
X * duration of a single article.  The temporary pool is reset to read
X * in each header.   It consists of a static block, followed by large
X * mallocated permanent blocks that are grabbed when there is not enough
X * room for temporary allocs
X *
X * The temporary allocator is very fast, and of course there is no overhead
X * in freeing it.
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#include "nl.h"
X
X/*
X * The structure for the blocks
X */
X
Xtypedef struct temp_block {
X	struct temp_block *next_block;		/* next block in chain */
X	unsigned int block_size;		/* size of this block */
X	unsigned int current_pos;		/* allocation limit this block*/
X	} *tbptr;
X
Xtbptr first_alloc = 0;			/* first perm_alloced temp block */
Xtbptr cur_block;			/* current block to alloc from*/
X					/* also the end block in chain*/
X
Xinit_tempalloc()
X{
X	extern char hdbuf[];
X	extern tbptr alloc_block AC((unsigned int));
X
X	first_alloc = alloc_block( 0 );
X	reset_tempalloc();
X}
X
X/*
X * Reset temporary allocator.
X * This frees all tempoarily allocated memory.
X */
X
X
X
Xreset_tempalloc( )
X{
X	tbptr loc;		/* new start of temp mem */
X	tbptr chain;		/* chain through temp mem to reset it */
X	extern tbptr alloc_block AC((unsigned int));
X
X	/* loop through blocks resetting them */
X	for( chain = first_alloc; chain; chain = chain->next_block )
X		chain->current_pos = sizeof(struct temp_block);
X	cur_block = first_alloc;
X}
X		
X
X/* Allocate a new block with at least the amount of memory given */
X
Xtbptr
Xalloc_block( alsize )
Xunsigned int alsize;			/* we need at least this much ram */
X{
X	unsigned int trysize;
X	tbptr newblock;
X	extern char *malloc AC((unsigned int));
X
X	/* adjust the size to include room for the header */
X	alsize += sizeof( struct temp_block );
X
X	/* normally get TBLOCK_SIZE bytes, unless asking for more */
X	trysize = max( TBLOCK_SIZE, alsize );
X
X	/* try to allocate blocks.  If it fails, keep cutting the block
X	   size in half, as long as we're bigger than the block the user
X	   wants and bigger than our minimum size */
X
X	while( trysize >= alsize && trysize >= MIN_TBLOCK ) {
X		newblock = (tbptr) malloc( trysize );
X		if( newblock ) {
X			newblock->block_size = trysize;
X			newblock->current_pos = sizeof(struct temp_block);
X			newblock->next_block = (tbptr)0;
X			return newblock;
X			}
X		}
X	/* we could not get it */
X	error( "Abort: Out of Memory\n" );
X}
X
X/* The actual temporary allocate call.  Normally it returns very quickly */
X
X
Xchar *
Xtemp_alloc( size )
Xunsigned int size;			/* size of region to allocate */
X{
X	char *ret;			/* returned memory */
X	tbptr scan;			/* scan through blocks */
X	tbptr newblock;			/* possible new block */
X
X	/* check if there is room in the end block */
X	if( cur_block->current_pos + size <= cur_block->block_size ) {
X		ret = (char *)cur_block + cur_block->current_pos;
X		cur_block->current_pos += size;
X		return ret;
X		}
X	/* didn't fit in this block.  Let's scan the blocks for a fit */
X
X	for( scan = first_alloc; scan; scan = scan->next_block ) 
X		if( scan->current_pos + size <= scan->block_size ) {
X			ret = (char *)scan + scan->current_pos;
X			scan->current_pos += size;
X			return ret;
X			}
X	/* did not find any memory anywhere.  Make a new block */
X
X	newblock = alloc_block( size );
X	if( first_alloc == 0 )
X		first_alloc = newblock;
X	/* chain it on the end */
X	cur_block->next_block = newblock;
X	cur_block = newblock;
X	/* allocate the ram in the new block */
X	ret = (char *)cur_block + cur_block->current_pos;
X	cur_block->current_pos += size;
X	return ret;
X}
X
X/* reallocate temporary memory.   This routine is particularly efficient
X   if the block to be reallocated is the last block that was allocated. */
X
Xchar *
Xtemp_realloc( buf, oldsize, newsize )
Xchar *buf;				/* buffer containing original memory */
Xunsigned int oldsize;			/* old size of buffer */
Xunsigned int newsize;			/* new size desired */
X{
X	char *newbuf;
X	/* check to see if this is the last guy we allocated */
X	if( buf + oldsize == ((char *)cur_block) + cur_block->current_pos ) {
X		/* it is the same guy */
X		if( newsize <= oldsize ) {	/* shrink block */
X			cur_block->current_pos -= oldsize - newsize;
X			return buf;
X			}
X		 else if( cur_block->current_pos - oldsize + newsize <=
X						cur_block->block_size ) {
X			/* expand the block */
X			cur_block->current_pos += newsize - oldsize;
X			return buf;
X			}
X		 else	/* free the block anyway */
X			cur_block->current_pos -= oldsize;
X		}
X	/* ah well, easy attempts at realloc failed */
X	/* this temp alloc can't go on top of our original buffer */
X	newbuf = temp_alloc( newsize );
X	/* block move the memory over */
X	{
X	register int i;
X	register char *p1, *p2;
X	i = oldsize;
X	p1 = buf;
X	p2 = newbuf;
X	while( i-- )
X		*p2++ = *p1++;
X	}
X	return newbuf;
X}
X
X/* Permanent allocation routines -- just an interface to malloc */
X
Xchar *
Xperm_alloc( size )
Xunsigned int size;		/* size of block */
X{
X	char *p;
X	extern char *malloc AC((unsigned int));
X
X	p = malloc( size );
X	if( !p )
X		error( "Out of memory\n" );
X	 else
X		return p;
X}
X
Xperm_free( block )
Xchar *block;			/* block allocated by perm_alloc */
X{
X	free( block );
X}
END_OF_FILE
if test 5588 -ne `wc -c <'alloc.c'`; then
    echo shar: \"'alloc.c'\" unpacked with wrong size!
fi
# end of 'alloc.c'
fi
if test -f 'comp/hlist.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'comp/hlist.c'\"
else
echo shar: Extracting \"'comp/hlist.c'\" \(5071 characters\)
sed "s/^X//" >'comp/hlist.c' <<'END_OF_FILE'
X
X#include "nc.h"
X
X/* structure for binary header tree */
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  * A short time evaluation of this product is also permitted.  See the file
X  * 'Licence' in the library source directory for details.
X  */
X
X/* The header tree is maintained so that we may keep track of which header
X * items the user wants to parse at run time.  At the end of the compile,
X * we output a data structure with the header items desired in sorted order.
X */
X
Xtypedef struct header_tree {
X	char *hname;
X	struct header_tree *left, *right;
X	char *varname;	/* header variable name */
X	char *delims;	/* delimiter code */
X	dtype htype;	/* type of element */
X	byte hflags;	/* flags, such as local variable flag */
X	} *htree;
X
X
X/*
X * Handle the definition of header items and header variables
X * Our output of the header items must be sorted for binary search,
X * so we might as well build them into a binary tree.  It doesn't really
X * matter if the tree gets really lopsided.
X */
X
Xselect_header( hstring,  hidecl, hdelims )
Xchar *hstring;		/* name of the header item */
Xnodep hidecl;		/* header variable declaration node */
Xchar *hdelims;		/* delimiter for header array processing */
X{
X	symptr hsym;		/* symbol node */
X	nodep idnode;		/* N_ID node */
X	int hflags;
X
X	if( !hidecl )
X		return;
X	if( !(idnode = kid0(hidecl)) )
X		return;
X
X	hsym = (symptr)kid0(idnode);
X
X	hflags = isupper( hstring[0] ) ? T_DUALCASE : 0;
X
X	lowercase( hstring );
X
X	add_hitem( hstring, hsym->name, hdelims, hsym->type | hflags,
X			hsym->sflags & SF_LOCAL );
X}
X
X/* the root of the header tree.  Newsgroups is always defined */
X
Xstruct header_tree hroot = {
X"newsgroups", 0, 0, "newsgroups", ", ", arrayof(T_NEWSGROUP), 0 
X};
X
Xhtree htree_root = (htree)0;
X
Xadd_hitem( name, varname, delim, type, flags )
Xchar *name;		/* name of header item */
Xchar *varname;		/* variable name */
Xchar *delim;		/* delimiter for array */
Xdtype type;		/* type of header variable */
Xbyte flags;		/* flags of header symbol for printing */
X{
X	htree ourh;	/* our header item, as we scan through tree */
X	htree *linkplace;	/* place to link in our new item */
X	int res;		/* result of string compare */
X	char *p;		/* name scanning pointer */
X
X	lowercase( name );
X	for( p = name; *p; p++ )
X		if( !isgraph( *p ) || *p ==':' ) {
X			parerror( "Invalid header field name %s", name );
X			return;
X			}
X
X	ourh = &hroot;
X	while( ourh ) {
X		res = strcmp( ourh->hname, name );
X		if( res > 0 )  {
X			linkplace = &ourh->left;
X			ourh = ourh->left;
X			}
X		 else if( res < 0 ) {
X			linkplace = &ourh->right;
X			ourh = ourh->right;
X			}
X		 else {
X			parerror( "Header item '%s:' already defined", name );
X			return;
X			}
X		}
X	ourh = (htree)checkalloc( sizeof(struct header_tree) );
X
X	ourh->hname = name;
X	ourh->varname = varname;
X	ourh->delims = delim;
X	ourh->htype = type;
X	ourh->hflags = flags;
X	ourh->left = ourh->right = (htree)0;
X
X	*linkplace = ourh;
X	
X}
X
X/* list of predefined headers and the delimiters they need */
Xstruct pdh {
X	char *headname;
X	char *delims;
X	byte dcflag;		/* dual case flag */
X	} pdhlist[] = {
X{ "distribution",	", \t", 0 },
X{ "followup-to",	", \t", 0 },
X{ "keywords",		"S,", 0 },
X{ "newsgroups",		", \t", 0 },
X{ "path",		"!:@%\t", 0 },
X{ "references",		" \t", T_DUALCASE },
X{ "xref",		" \t", T_DUALCASE },
X{ "message-id",		"", T_DUALCASE },
X{ 0, 0, 0 }
X};
X
X/* create a header line for a referenced header symbol */
X
Xhcreate( name, type )
Xchar *name;
Xdtype type;
X{
X	char *newname;			/* new buffer for modified name */
X	char *p;			/* pointer for mapping */
X	char *delim;			/* delimiter for array */
X	int i;
X	int dualcase;			/* case flag to add to type */
X
X	/* copy over the name, then map underbar to dash */
X	newname = allocstring( name );
X	/* that alloc is a waste if the name is a duplicate */
X	for( p = newname; *p; p++ )
X		if( *p == '_' )
X			*p = '-';
X
X	/* set delim to null to start */
X
X	delim = "";
X	dualcase = 0;
X	/* now check to see if there are predefined delimiters */
X	for( i = 0; pdhlist[i].headname; i++ )
X		if( strcmp( pdhlist[i].headname, newname ) == 0 ) {
X			delim = pdhlist[i].delims;
X			dualcase = pdhlist[i].dcflag;
X			break;
X			}
X	/* we now have the header name and delimiters */
X	add_hitem( newname, name, delim, type | dualcase, 0 );
X}
X
Xstatic hcount = 0;		/* number of header items */
X
Xdump_header()
X{
X	cprintf( "struct hitem_list header_items[] = {\n" );
X	hdump( &hroot );
X	cprintf( "{ 0, 0, 0, 0 }\n};\n" );
X	cprintf( "int num_headers = %d;\n", hcount );
X}
X
X/* recursive routine to dump header tree elements in sorted order */
X
Xhdump( ht )
Xhtree ht;
X{
X	if( !ht )
X		return;
X
X	hdump( ht->left );
X
X	cprintf( "{ \"%s\", %d, \"%s\", (datau *)&%s%s },\n", ht->hname,
X		ht->htype, ht->delims, ht->hflags ? "U" : "", ht->varname );
X	hcount++;
X	hdump( ht->right );
X}
X
X
END_OF_FILE
if test 5071 -ne `wc -c <'comp/hlist.c'`; then
    echo shar: \"'comp/hlist.c'\" unpacked with wrong size!
fi
# end of 'comp/hlist.c'
fi
if test -f 'comp/ncc.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'comp/ncc.c'\"
else
echo shar: Extracting \"'comp/ncc.c'\" \(6074 characters\)
sed "s/^X//" >'comp/ncc.c' <<'END_OF_FILE'
X#include "nc.h"
X
X/*
X * Main entry point for the compiler
X */
X
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  * A short time evaluation of this product is also permitted.  See the file
X  * 'Licence' in the library source directory for details.
X  */
X
X
XFILE *outstream;		/* output program */
Xchar *input_name = (char *)0;		/* input filename */
Xchar *output_name =(char *)0;		/* created C program */
Xchar *executable_name = "nclip";	/* created binary program */
X
Xchar syscom[MAX_LLEN];
X
Xchar *compopts[MAX_OPTS+10];		/* arg vector for C compiler */
Xint optcount = 1;			/* count of args */
Xbool link_flag = TRUE;			/* link together an executable */
Xbool no_externals = FALSE;		/* do not allow unknown externals */
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	extern FILE *yyin;
X	extern char *yyfilename;
X	extern bool got_error;
X	int argnum;
X	char *strchr(), *strrchr();
X
X	/* prepare to call preprocessor */
X	strcpy( syscom, CPP );
X
X	for( argnum = 1; argnum < argc; argnum++ ) {
X		char *argline;
X		char *argstr;		/* argument string */
X		int argval;
X		int isplus;		/* boolean tells +arg vs -arg */
X		argline = argv[argnum];
X
X		if (argstr = strchr(argline, '=')) {
X			argstr++;
X			argval = atoi(argstr);
X			switch( argline[0] ) {
X				case 'I':
X					if( strlen(argstr)+strlen(syscom)+4
X							>= MAX_LLEN )
X						fatal(FALSE,"Too many args" );
X					strcat( syscom, " -I" );
X					strcat( syscom, argstr );
X					break;
X				case 'D':
X					if( strlen(argstr)+strlen(syscom)+4
X							>= MAX_LLEN )
X						fatal(FALSE,"Too many args" );
X					strcat( syscom, "-D" );
X					strcat( syscom, argstr );
X					break;
X				case 'i':
X					output_name = argstr;
X					if( argstr[0] == 0 || strcmp( argstr+
X							strlen(argstr)-2,
X							".c" ) != 0 )
X						fatal(FALSE,"Intermediate name must end with '.c'" );
X					break;
X				case 'c':
X					if( optcount >= MAX_OPTS )
X						fatal( FALSE, "Too many C compiler options" );
X					compopts[optcount++] = argstr;
X					break;
X				case 'o':
X					executable_name = argstr;
X					break;
X				default:
X					fatal( FALSE, "Bad Option %s", argline);
X				}
X			}
X		else if( (isplus = argline[0] == '+') || argline[0] == '-' ) {
X			switch( argline[1] ) {
X				case 'l': /* link or do not link */
X					link_flag = isplus;
X					break;
X				case 'e':
X					no_externals = !isplus;
X					break;
X				default:
X					fatal( FALSE, "Bad Option %s", argline);
X				}
X			}
X		else {
X			char *p;
X			/* code for untagged option */
X			/* check extension on file, .c .o or .a */
X			p = strchr( argline, '.' );
X			if( p && p[2]==0 && (p[1]=='c'||p[1]=='o'||p[1]=='a')) {
X				if( optcount >= MAX_OPTS )
X					fatal( FALSE, "Too many C compiler options" );
X				compopts[optcount++] = argline;
X				break;
X				}
X			 else {
X				if( input_name != (char *)0 )
X					fatal( FALSE, "Only one file may be compiled at a time" );
X				input_name = argline;
X				}
X			}
X		}
X	/* body of program */
X
X
X	initialize();
X	symtab_init();
X
X	if( !input_name ) {
X		usage();
X		}
X		
X
X	if( access( input_name, 4 ) != 0 )
X		fatal( FALSE, "Could not open input %s", input_name );
X 
X	if( strlen(syscom)+strlen(input_name)+2 >= MAX_LLEN )
X		fatal(FALSE,"Too many args" );
X	strcat( syscom, " " );
X	strcat( syscom, input_name );
X
X	yyin = popen(syscom, "r");
X	if( !yyin )
X		fatal( FALSE, "Unable to preprocess input %s", input_name );
X	
X	if( !output_name ) {
X		char *dot;
X		int inlen;
X		inlen = strlen(input_name);
X		/* get 2 extra bytes in case we add .c */
X		output_name = checkalloc( inlen+2+1 );
X		strcpy( output_name, input_name );
X		dot = strrchr( output_name, '.' );
X		/* if no dot, append extension to the end */
X		if( !dot || strchr( dot, '/' ) != (char *)0 )
X			dot = output_name + inlen;
X		strcpy( dot, ".c" );
X		}
X	outstream = fopen( output_name, "w" );
X	if( !outstream )
X		fatal( FALSE, "Could not open output file '%s'", output_name );
X	yyfilename = input_name;
X
X	/* start output by putting in include data */
X	cout( "#include \"ucode.h\"\n" );
X
X	if( yyparse() || got_error ) {
X		fclose( outstream );
X		if( unlink( output_name ) )
X			fprintf( stderr, "Could not remove file '%s'\n", output_name );
X
X		fatal( FALSE, "Compilation of NEWSCLIP program failed\n" );
X		}
X	pclose( yyin );
X	write_null();			/* output null entry points as needed */
X	write_data();
X	fclose( outstream );
X	if( link_flag ) 
X		compile_it();		/* does an execv, does not return */
X	exit(0);
X}
X
Xinitialize()
X{
X	extern struct stringmap thegroups;
X	extern struct stringmap thepatterns;
X
X	thegroups.dbase = init_db( 80, sizeof(numstring) );
X	thegroups.counter = 0;
X	thepatterns.dbase = init_db( 80, sizeof(numstring) );
X	thepatterns.counter = 0;
X}
X
Xusage()
X{
X	fprintf( stderr, "NewsClip(TM) News Filter Compiler V0.9\n" );
X	fprintf( stderr, "Copyright (c) 1989 Looking Glass Software Limited.\n\n" );
X	fprintf( stderr, "Usage:\n\tnnc [I=includedir] [D=symbol=definition] filename\n" );
X	fatal( FALSE, "Missing source filename." );
X}
X
Xcompile_it()
X{
X	/* now compile the program and link with the library */
X	compopts[0] = "cc";			/* name the C compiler */
X	/* extra options defined in sysdefs.h */
X#ifdef EXTRAOPT1
X	compopts[optcount++] = EXTRAOPT1;
X#endif
X#ifdef EXTRAOPT2
X	compopts[optcount++] = EXTRAOPT2;
X#endif
X#ifdef EXTRAOPT3
X	compopts[optcount++] = EXTRAOPT3;
X#endif
X	/* directory to find ucode.h */
X	compopts[optcount++] = UCODEDIR;
X	/* name of the program */
X	compopts[optcount++] = output_name;
X	/* name of the library, if it exists */
X	if( access( CLIPLIB, 0 ) == 0 )
X		compopts[optcount++] = CLIPLIB;
X	 else if( access( CLIPBASE, 0 ) == 0 )
X		compopts[optcount++] = CLIPBASE;
X	compopts[optcount++] = "-o";
X	compopts[optcount++] = executable_name;
X	compopts[optcount++] = 0;
X	/* transfer to the C compiler */
X	execv( CCOMP, compopts );
X	/* should never get here */
X	fatal( FALSE, "Execution of the C compiler failed" );
X}
END_OF_FILE
if test 6074 -ne `wc -c <'comp/ncc.c'`; then
    echo shar: \"'comp/ncc.c'\" unpacked with wrong size!
fi
# end of 'comp/ncc.c'
fi
if test -f 'doc/ncc.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'doc/ncc.1'\"
else
echo shar: Extracting \"'doc/ncc.1'\" \(6503 characters\)
sed "s/^X//" >'doc/ncc.1' <<'END_OF_FILE'
X.TH NCC 1
X.SH NAME
Xncc - Newsclip(TM) Language compiler
X.SH SYNOPSIS
X.B ncc
X[ options ] filename [libfiles]
X.SH DESCRIPTION
X.I Ncc
Xis a compiler which generates Newsclip ``News filtering'' programs.
XGiven a program written in the
X.I newsclip
Xlanguage, it translates it into C, compiles it with cc(1) and links it
Xwith the
X.I newsclip
Xlibrary.
X.PP
XNews clipping programs allow you describe USENET news articles that you wish
Xto read or not read with the various news reading programs found on USENET
Xsystems such as readnews(1).  You can describe expressions, patterns and
Xgeneral program constructs to accept or reject news articles as desired.
XAs
X.I newsclip
Xis a fairly full featured programming language, you can filter your
Xarticles with descriptions as simple or as involved as you like.
X.PP
XThe compiled programs can be used in a number of ways.  These include
Xbackground operation, where they update a
X.I .newsrc
Xfile, marking as read articles that you don't wish to see.  Compiled programs
Xcan also filter a list of news article filenames to control batched feeds
Xto other systems.   In addition, some newsreaders know how to talk to
Xnews clipping programs through a special inter-process protocol using
Xpipes.  In this case, you can run a
X.I newsclip
Xfiltering program in parallel with your newsreader.
X.PP
XThere are several other modes of operation described in the
X.I
XNewsclip
Xmanuals.  The details and syntax of the language are described there.
X.SH OPTIONS
X.PP
XThe additional files on the command line may be .c, .o or .a files, which
Xwill be compiled and/or linked with your program.
X.PP
X(Note that while option names are displayed here in full, only the first
Xletter is actually required.  For +/\- options, using + turns the option on,
Xand using \- turns the option off.)
X.TP
X.B Include=directory
X.I Ncc
Xpasses source files through the C preprocessor, allowing you to use its
Xinclude and macro facilities.  This specifies a directory that the
Xpreprocessor should search for #include files.
X.TP
X.B Define=macroname=definition
XThis passes a macro definition to the preprocessor.  Everything after
Xthe first equals sign is passed as a
X.I -D
Xoption to the preprocessor.   This allows you to define symbols and
Xmacros that can be used in code and as arguments to #ifdef directives.
X.TP
X.B intermediate=file.c
XSpecifies the name for the generated C program.  Normally this will be
Xthe name of your source program with the extension replaced with
Xa ``.c''.  If there is no extension, the ``.c'' is added.  You may
Xspecify an alternate name, which must end with ``.c.''
X.TP
X.B ccoption=string
XSpecifies an option that is to be passed on to the cc(1) command when
Xit is executed to compile and link your program.
X.TP
X.B output=filename
XSpecifies a filename for the executable news clipping program.  The
Xdefault is ``nclip.''  This is the name used by newsreaders that
Xunderstand the news filter protocol and can talk to filter programs.
X.TP
X.B -link
XGenerate the C program from the source, but do not compile it or link
Xit with the newsclip library.   This is like the
X.I -c
Xoption of cc(1).
X.TP
X.B -externals
XDo not allow references in the newsclip program to externals other than
Xthose in the predefined external list maintained within the compiler.
XThis restricts the language to its pure form.  Please note that while
Xthis does make security breaches harder, THIS IS NOT AN ASSURED FORM OF
XSECURITY.  Newsclip is a general enough programming language that there
Xwill always be ways for malicious users to take control.  If you wish
Xto allow outsiders to submit newsclip programs to be run on your machine,
Xyou should take other security steps if you do not trust them.
X.SH OPERATION
X.PP
XThe
X.I
Xncc
Xcompiler takes your source program (file.nc) and passes it through the
XC preprocessor.  It then translates the output into a C program that
Xis designed to be linked with the newsclip library, usually found in
Xthe file /usr/lib/news/cliplib.a.
X.PP
XThe resulting program, usually called ``nclip,'' has several options
Xand several modes of operation.  These are documented in the manual
Xpage for nclip(1).
X.SH LANGUAGE
XThe
X.I newsclip
Xlanguage is a stripped down version of C, with various extensions added
Xto make it a special purpose language.  The whole purpose of the bulk of
Xa newsclip program is to examine a B news format article and give it
Xa
X.I score.
XAt the end, if that score is greater than zero, the article will be
Xaccepted and eventually presented for the user to read.  Otherwise it is
Xrejected.
X.PP
XNewsclip programs usually consist of various conditional statements which
Xexamine things about the article and decide whether to adjust the score
Xor accept/reject the article out of hand.  Conditional expressions can
Xexamine the various header fields of an article or do pattern matching
Xon header fields and various sections of the article's text.
X.PP
XThis ability is combined with general language facilities such as
Xsubroutines, loops, variables, arrays, case statements and a variety of
Xprocessing and control variables and functions.
X.PP
XOne particularly useful facility is the database facility.  Users can
Xmaintain databases in regular files, and perform searches in the databases
Xfor substrings and patterns from the article header and body.  Unique to
Xnewsclip is the ability for programmed update of the databases.
X.PP
XFor example, a user might have a database of Message-IDs that the user
Xhas `killed,' this indicating that the user does not wish to see followups
Xto those messages.  A newsclip program might detect undesired articles
X(such as those from an obnoxious user) and have their Message-IDs put
Xin the database under automatic program control.  Thus the user would
Xnever see articles by the hated user, nor any followups to those
Xarticles.  It would be as though the hated user did not exist on the net.
X.PP
XThe potential for custom control of news filtering is limitless.
X.SH AUTHOR
X.PP
XThe
X.I Newsclip
Xsystem was written by Brad Templeton and Tim Tyhurst.  It is copyright
X1989 by Looking Glass Software Limited.  All rights reserved.
XIt is a commercial product and is only to be used by authorized, licenced
Xusers who have purchased or arranged a licence with the copyright owner.
XThese programs and their associated documentation are not to be copied for
Xuse by unlicenced parties.
X.SH FILES
X/usr/lib/news/newsclip/cliplib.a,
X/usr/lib/news/newsclip/ucode.h
X.SH "SEE ALSO"
X.I cc(1),
X.I readnews(1),
X.I rn(1),
X.I cpp(1),
X.I nclip(1)
X.SH VERSION
XVersion 1.0
END_OF_FILE
if test 6503 -ne `wc -c <'doc/ncc.1'`; then
    echo shar: \"'doc/ncc.1'\" unpacked with wrong size!
fi
# end of 'doc/ncc.1'
fi
if test -f 'patch/read.me' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'patch/read.me'\"
else
echo shar: Extracting \"'patch/read.me'\" \(4568 characters\)
sed "s/^X//" >'patch/read.me' <<'END_OF_FILE'
X
XHow to patch newsreaders to talk to newsclip, and other filter programs:
X
XWe have devised a protocol for news reading programs to talk to general
Xnews filter programs.   NewsClip programs all talk this protocol, and
Xit isn't hard to set up readers to do this.
X
XThe full protocol is documented in the file filter.man, in this
Xdirectory.
X
XOur files 'filtpipe.c' and 'pipemode.h' serve to help you easily add
Xthe protocol to any non-NNTP newsreader, as well as simple NNTP
Xnewsreaders like RRN.  You put these two files in the newsreader's
Xsource directory, modify the newsreader's makefile to make and
Xlink in the filtpipe.o object module with the rest of the newsreader,
Xand insert calls to the filtering functions in 3 or 4 strategic places
Xinside the newsreader.
X
XA set of patches to do this for RN and RRN are provided.  They are
Xcontext diffs, so you should be able to feed them through Larry Wall's
X(the author of RN) automatic 'patch' program.
X
XThe patches you have to make are this:
X
XA) Initialization patch
X	In the newsreader initialization code, put a call to
X		filter_init(dotdir)
X		char *dotdir;
X	Dotdir is a string argument that gives the name of the directory
X	the newsreader uses to keep user files.  This is often just the
X	user's home directory.
X
X	This routine returns -1 if the news filter didn't start.  If
X	you want, you can use this to inform the user of how things went.
X
XB) Termination patch
X	In your general terminate and wrapup routine, put a call to
X		filter_quit();
X	This will tell the news filter to shut down when the newsreader
X	does, and give it a chance to write out files etc.
X
X	You may not want to put this call in if your newsreader does
X	a nasty abort, but execute it otherwise.
X
XC) Enter Newsgroup Patch
X	When your newsreader enters a new newsgroup to present new articles
X	to the user, you should call:
X
X		int
X		no_filter_group( groupname )
X		char *groupname;		/* name of newsgroup */
X
X	This function returns true if you should not bother filtering
X	articles in this group.  You can still filter them if you like,
X	but the newsfilter will just accept all of them.
X
X	This call is important even if you don't plan to make use of
X	this optimization.  It initializes the filter program for a
X	new group.  If you want to be able to issue commands to the
X	filter that depend on the current group, you have to set the
X	current group with this function.
X
XD) Query Article Patch
X	This is the one that does it all.  If your newsreader is about
X	to present a new article to the user, it should call:
X
X		int
X		filter_art( spool, ngdir, ngname, art )
X		char *spool;	/* news spool directory */
X		char *ngdir;	/* group directory in spool */
X		char *ngname;	/* newsgroup name */
X		long art;	/* article number */
X
X	This function returns true if the article should be presented
X	to the user, and false if it should be skipped.
X
X	If you don't have the directory of the group split up into
X	two strings, pass NULL ((char *)0) for ngdir and put the
X	full article name, including the article number part,
X	in 'spool'.   The article number should still be passed if it
X	has any meaning at all.
X
X	You can put this patch in just before an article is read, or
X	you can make a scanning pass of the entire newsgroup with a
X	loop at the start of code to read a newsgroup.  In this case,
X	the filter acts like a kill file.
X
XE) Command patch
X
X	To unlock the true power of the filter program, the user should
X	be able to send commands to it.  Add a command to your news
X	reader that allows the user to send a command string to the
X	news filter.  In this place, put:
X		int
X		filter_command( str )
X		char *str;		/* command string */
X
X	This returns NULL if the command was valid and accepted, and
X	false if it wasn't a good command.  If you get false, you should
X	possibly give an error message.
X
X
XRN Patches:
X
X	A set of diffs to apply these patches to RN and RRN is supplied
X	in the file 'rnpatches'.  There is one optional flag you can
X	define in config.h to control the patching.  If RTFILTER is
X	defined (as is the default) then filtering takes place right as
X	articles are read.  If it is not defined, filtering is done
X	at the start of each group, like a kill file.
X
X	The former is fast an transparent, but it doesn't fit RN's
X	kill file paradigm very well.  If you get a subject list, you
X	see articles in it that will be rejected.  The latter scheme
X	doesn't have this problem, but involves a pause at the start
X	of every group -- one of the problems people have with kill files.
X
X	Kill files and filters can work together.  The kill files get to
X	go first.
END_OF_FILE
if test 4568 -ne `wc -c <'patch/read.me'`; then
    echo shar: \"'patch/read.me'\" unpacked with wrong size!
fi
# end of 'patch/read.me'
fi
if test -f 'patch/rnpatch' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'patch/rnpatch'\"
else
echo shar: Extracting \"'patch/rnpatch'\" \(5747 characters\)
sed "s/^X//" >'patch/rnpatch' <<'END_OF_FILE'
XThe Makefile patch will vary from system to system.  Systems with
Xa common makefile for RN and RRN need different changes.  The change
Xis pretty trivial, just add 'filtpipe.o' to the object file list.
X
Xdiff -c old/Makefile ./Makefile
X*** old/Makefile	Tue Apr 25 16:15:43 1989
X--- ./Makefile	Tue Apr 25 16:15:51 1989
X***************
X*** 36,46 ****
X  
X  c = $(c1) $(c2) $(c3) $(c4)
X  
X  
X  obj1 = addng.o art.o artio.o artsrch.o backpage.o bits.o cheat.o
X  obj2 = final.o head.o help.o init.o intrp.o kfile.o last.o $(NDIRO) ng.o
X  obj3 = ngdata.o ngsrch.o ngstuff.o only.o rcln.o rcstuff.o
X! obj4 = respond.o rn.o search.o sw.o term.o util.o
X  
X  obj = $(obj1) $(obj2) $(obj3) $(obj4)
X  
X--- 36,47 ----
X  
X  c = $(c1) $(c2) $(c3) $(c4)
X  
X+ FILTO=filtpipe.o
X  
X  obj1 = addng.o art.o artio.o artsrch.o backpage.o bits.o cheat.o
X  obj2 = final.o head.o help.o init.o intrp.o kfile.o last.o $(NDIRO) ng.o
X  obj3 = ngdata.o ngsrch.o ngstuff.o only.o rcln.o rcstuff.o
X! obj4 = respond.o rn.o search.o sw.o term.o util.o $(FILTO)
X  
X  obj = $(obj1) $(obj2) $(obj3) $(obj4)
X  
Xdiff -c old/art.c ./art.c
X*** old/art.c	Wed Apr 26 22:10:33 1989
X--- ./art.c	Wed Apr 26 22:28:10 1989
X***************
X*** 751,756 ****
X--- 751,759 ----
X      case 'p':	case 'P':	case Ctl('p'):	
X  		case 'Q':
X      case 'r':	case 'R':	case Ctl('r'):
X+ #ifdef NEWSFILTER
X+     case 'T':
X+ #endif
X      case 'v':
X  		case 'Y':
X  #ifndef ROTATION
X***************
X*** 765,771 ****
X  	reread = FALSE;
X  	do_hiding = TRUE;
X  	if (index("nNpP",*buf) == Nullch &&
X! 	  index("wWsS!&|/?123456789.",*buf) != Nullch) {
X  	    setdfltcmd();
X  	    standout();		/* enter standout mode */
X  	    printf(prompt,mailcall,dfltcmd);
X--- 768,778 ----
X  	reread = FALSE;
X  	do_hiding = TRUE;
X  	if (index("nNpP",*buf) == Nullch &&
X! #ifdef NEWSFILTER
X! 	  index("wWsST!&|/?123456789.",*buf) != Nullch) {
X! #else
X! 	  index("wWsS!&|/?123456789.",*buf) != Nullch) {  /* } */
X! #endif
X  	    setdfltcmd();
X  	    standout();		/* enter standout mode */
X  	    printf(prompt,mailcall,dfltcmd);
Xdiff -c old/config.h ./config.h
X*** old/config.h	Tue Apr 25 16:16:42 1989
X--- ./config.h	Wed Apr 26 00:55:22 1989
X***************
X*** 77,79 ****
X--- 77,82 ----
X  #undef	GETHOSTNAME	/* do we have a gethostname function? */
X  #undef	DOUNAME		/* do we have a uname function? */
X  #define	PHOSTNAME "uuname -l"	/* how to get host name with popen */
X+ 
X+ #define NEWSFILTER 1	/* support news filter protocol */
X+ #define RTFILTER 1	/* filter as articles are read instead of in advance */
Xdiff -c old/final.c ./final.c
X*** old/final.c	Tue Apr 25 16:12:42 1989
X--- ./final.c	Tue Apr 25 16:13:09 1989
X***************
X*** 51,56 ****
X--- 51,59 ----
X  finalize(status)
X  int status;
X  {
X+ #ifdef NEWSFILTER
X+     filter_quit();
X+ #endif
X      if (bizarre)
X  	resetty();
X      UNLINK(lockname);
Xdiff -c old/init.c ./init.c
X*** old/init.c	Tue Apr 25 16:06:39 1989
X--- ./init.c	Tue Apr 25 16:06:41 1989
X***************
X*** 148,153 ****
X--- 148,156 ----
X  /*  sw_init();      already done */
X  /*  term_init();	already done */
X      util_init();
X+ #ifdef NEWSFILTER
X+ 	filter_init(dotdir);
X+ #endif
X  
X  #ifdef FINDNEWNG
X      fstat(actfp->_file,&filestat);	/* did active file grow? */
Xdiff -c old/ng.c ./ng.c
X*** old/ng.c	Wed May  3 00:16:13 1989
X--- ng.c	Wed May  3 01:15:11 1989
X***************
X*** 194,199 ****
X--- 194,225 ----
X  #ifdef CACHESUBJ
X      subj_list = Null(char **);		/* no subject list till needed */
X  #endif
X+ 
X+ #ifdef NEWSFILTER
X+ 	if( !no_filter_group( ngname ) )
X+ # ifdef RTFILTER
X+ 		;
X+ # else RTFILTER
X+ 
X+ 		{
X+ 		ART_NUM i;
X+ 		fputs( "\nFiltering...\n", stdout ) FLUSH;
X+ 		for( i = firstart; i < lastart; i++ )
X+ 			if( !was_read(i) ) {
X+ #ifdef SERVER
X+ 				/* Ideally we should use query filtering here
X+ 				   so that we only have to read the header */
X+ 	    			artopen(art);
X+ 	    			sprintf( artname, "/tmp/rrn%ld.%d", (long)i, our_pid );
X+ 				if( !filter_art( artname, (char*)0, ngname, i)) 
X+ #else
X+ 				if( !filter_art( spool, ngdir, ngname, i ) ) 
X+ #endif
X+ 					mark_as_read( i );
X+ 			}
X+ 		}
X+ # endif RTFILTER
X+ #endif
X      
X      /* initialize control bitmap */
X  
X***************
X*** 383,388 ****
X--- 409,430 ----
X  	    continue;
X  	}
X  	else {				/* we have a real live article */
X+ #ifdef NEWSFILTER
X+ # ifdef RTFILTER
X+ #  ifdef SERVER
X+ 	    artopen(art);	/* be sure it is open and created */
X+ 	    sprintf( artname, "/tmp/rrn%ld.%d", (long)art, our_pid );
X+ 	    if( !reread && !filter_art( artname, (char *)0, ngname, art ) ) {
X+ #  else SERVER
X+ 	    if( !reread && !filter_art( spool, ngdir, ngname, art ) ) {
X+ #  endif SERVER
X+ 		printf( "\nFiltered %ld", art ) FLUSH;
X+ 		mark_as_read( art );
X+ 		art++;
X+ 		continue;
X+ 	    }
X+ # endif RTFILTER
X+ #endif
X  	    skipstate = 0;		/* back to none skipped */
X  	    if (art != curr_art) {
X  		recent_art = curr_art;
X***************
X*** 657,663 ****
X      {		/* search for article by pattern */
X  	char cmd = *buf;
X  	
X! 	reread = TRUE;		/* assume this */
X  	switch (art_search(buf, (sizeof buf), TRUE)) {
X  	case SRCH_ERROR:
X  	    return AS_ASK;
X--- 699,706 ----
X      {		/* search for article by pattern */
X  	char cmd = *buf;
X  	
X! 	if( cmd != Ctl('n') )
X! 		reread = TRUE;		/* assume this */
X  	switch (art_search(buf, (sizeof buf), TRUE)) {
X  	case SRCH_ERROR:
X  	    return AS_ASK;
X***************
X*** 986,991 ****
X--- 1029,1044 ----
X  #ifdef STRICTCR
X      case '\n':
X  	fputs(badcr,stdout) FLUSH;
X+ 	return AS_ASK;
X+ #endif
X+ #ifdef NEWSFILTER
X+     case 'T':				/* talk to news filter */
X+ 	if( !finish_command(TRUE) )
X+ 		return AS_ASK;		/* aborted talk command */
X+ 	if( !filter_command( buf ) ) {
X+ 		printf("\n%s",hforhelp) FLUSH;
X+ 		settle_down();
X+ 	}
X  	return AS_ASK;
X  #endif
X      default:
X
END_OF_FILE
if test 5747 -ne `wc -c <'patch/rnpatch'`; then
    echo shar: \"'patch/rnpatch'\" unpacked with wrong size!
fi
# end of 'patch/rnpatch'
fi
if test -f 'samples/myprog.nc' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'samples/myprog.nc'\"
else
echo shar: Extracting \"'samples/myprog.nc'\" \(4079 characters\)
sed "s/^X//" >'samples/myprog.nc' <<'END_OF_FILE'
X
X	/* symbols from the header */
Xextern string	subject;		/* article subject line */
Xextern string	message_id;
Xextern string	array references;
Xextern userid	from;
Xextern int	lines;
X
Xdatabase badmes;		/* bad message-ids - by group */
Xdatabase baduser;		/* users I don't like - global */
Xdatabase gooduser;		/* users I do like - global */
Xdatabase badsubject;		/* subject line database - by group */
Xdatabase killpatterns;		/* kill patterns for subject lines - by group */
X
X
Xprocedure init()
X{
X	/* set up the good and bad user databases */
X	baduser = read_database( "~/News/baduser" );
X	gooduser = read_database( "~/News/gooduser" );
X	/* nil out the individual newsgroup databases */
X	badmes = nildatabase;
X	badsubject = nildatabase;
X	killpatterns = nildatabase;
X}
X
Xprocedure terminate()
X{
X	extern datetime time_now;
X
X	/* save out my updated bad user database */
X	write_database( baduser, "~/News/baduser", time_now-month );
X}
X
Xprocedure startgroup()
X{
X
X	/* load up the databases for this newsgroup */
X	badmes = read_database( "~/News/badmes/~N" );
X	badsubject = read_database( "~/News/badsubject/~N" );
X	killpatterns = read_database( "~/News/killpatterns/~N" );
X}
X
Xprocedure endgroup()
X{
X	extern datetime time_now;
X
X	/* write out the databases for this newsgroup */
X
X	write_database( badmes, "~/News/badmes/~N", time_now-2*week );
X	free_database( badmes );
X	badmes = nildatabase;
X	write_database( badsubject, "~/News/badsubject/~N", time_now-2*week );
X	free_database( badsubject );
X	badsubject = nildatabase;
X	write_database( killpatterns, "~/News/killpatterns/~N", time_now-2*week );
X	free_database( killpatterns );
X	killpatterns = nildatabase;
X}
X
Xprocedure
Xarticle()
X{
X	extern newsgroup main_newsgroup;
X	extern string drop_re( string );
X	extern int distribution_level;
X
X	/* accept articles from desired users immediately */
X	accept if from in gooduser;
X
X	/* is this a followup to a killed chain? */
X	reject if references != nilarray && references in badmes;
X
X
X	/* If from a hated user, reject and kill the chain too */
X	if( from in baduser ) {
X		/* reject all followups, too */
X		badmes[message_id] = true;
X		reject;
X		}
X
X	/* if the subject is explicitly a bad one, or a keyword from our
X	   pattern list is found in it, reject */
X	if( drop_re(subject) in badsubject || subject has killpatterns )
X		reject;
X
X	/* give me messages from local users */
X	accept if from has "waterloo.edu$" ||
X			distribution_level < dlevel(#province);
X
X	reject if count(newsgroups) > 4;	/* I hate crossposting */
X
X	/* do group sepecific rejection */
X	switch( main_newsgroup ) {
X		case #rec.arts.comics:
X			if( body has "templeton" || body has "stig.*inferno" || body has "league international" || body has "jli" )
X				accept;
X			 else
X				reject;
X		case #comp.sys.atari.st:
X			reject if is comp.sys.amiga;
X			break;
X		case #comp.lang.pascal:
X			accept if body has "alice" || body has "sytax.directed"
X					|| body has "interpret";
X			reject;
X		case #talk.politics.theory:
X			reject if lines / (1+line_count(included)) < 2;
X			break;
X		}
X}
X
X/* My processor for KILL commands */
X
Xprocedure
Xcommand( string com )
X{
X	userid fromwho;
X	string arg;
X	extern int chindex( string, int );
X	extern string lower( string );
X	extern string clipfront( string, int );
X	string array artrefs;
X
X	if( chindex( com,1 ) == 'K' ) {
X		arg = clipfront(com,4);
X		switch( chindex( com,2 ) ) {
X			case 'F':	/* followups */
X				badmes[arg] = true;
X				dprintf( "Adding %s to kill list\n", arg );
X				accept;
X			case 'T':	/* all followups of top parent */
X				parse artrefs = arg, " ";
X				if( count(artrefs) > 0 )
X					badmes[artrefs[0]] = true;
X				 else
X					reject;
X				dprintf( "Adding master %s to kill list\n",
X						artrefs[0] );
X				accept;
X				
X			case 'U':	/* a user */
X				parse fromwho = lower(arg);
X				baduser[fromwho] = true;
X				accept;
X			case 'S':	/* a subject re-less */
X				badsubject[lower(arg)] = true;
X				accept;
X			case 's':	/* subject pattern */
X				killpatterns[lower(arg)] = true;
X				accept;
X			case 'x':	/* test filter alive? */
X				dprintf( "Filter in operation\n" );
X				accept;
X			}
X		}
X	reject;
X}
END_OF_FILE
if test 4079 -ne `wc -c <'samples/myprog.nc'`; then
    echo shar: \"'samples/myprog.nc'\" unpacked with wrong size!
fi
# end of 'samples/myprog.nc'
fi
if test -f 'samples/sample.nc' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'samples/sample.nc'\"
else
echo shar: Extracting \"'samples/sample.nc'\" \(5676 characters\)
sed "s/^X//" >'samples/sample.nc' <<'END_OF_FILE'
X/* Sample NEWSCLIP program that shows what you can do */
X
X/* Please folks, this is not the newsclip program that I use, and I
X   don't advocate all the different filtering things here.  I am
X   just using them as examples of how to do certain things net people
X   have suggested they wanted done. */
X
X	/* You can include pre-defined header lines */
X	extern userid From;			/* the From: line */
X	extern newsgroup array newsgroups;	/* the Newsgroups: line */
X	extern int distribution_level;		/* max distr of article */
X	extern string array references;		/* parent articles */
X	extern string Subject;			/* subject line */
X	extern int followup;			/* is it a followup? */
X	extern int lines;			/* header variable */
X	/* or define your own header lines */
X	header string mess_id : "message-id";
X	/* declare variables */
X	int counter;
X	/* some databases I will look things up in */
X	database badmessages;			/* message-ids I don't want
X						to see followsup to */
X	database hated_users;			/* users I don't want to
X						   see articles from */
X	database my_articles;			/* message-ids that I
X						want to see ALL followups to */
X	/* declare external C functions from the Newsclip library or your
X	   own C libraries */
X	extern int strlen( string );
X
X	/* you can define procedures and functions */
X
Xint
Xnice_group( newsgroup n )
X{
X	extern string left( string, int );
X	/* you like all sci newsgroups and rec.humor.funny */
X	return n == #rec.humor.funny || left(n,1) == "sci";
X}
X
Xprocedure
XINIT() {
X	extern procedure set_include_prefix(string);
X	/* this code gets run when the program starts */
X	set_include_prefix( "[:>]" );
X	hated_users = read_database( "~./hatedusers" );
X	my_articles = read_database( "~./myarticles" );
X}
X
Xprocedure
XSTARTGROUP() {
X	/* This gets called when we begin to scan a new newsgroup */
X	/* read in the database of bad message-ids for this group */
X	badmessages = read_database( "~./kill/~n/killdb" );
X}
X
Xprocedure
XENDGROUP() {
X	/* this gets called to end the newsgroup */
X	extern datetime time_now;
X
X	/* write out the bad message database, delete all entries that
X	  are older than one month */
X	write_database( badmessages, "~./kill/~n/killdb", time_now - month );
X	free_database( badmessages );
X}
X
X	/* here is the main part.  The code that is executed for every
X	   article to accept or reject it */
X
Xprocedure
XARTICLE() {
X	newsgroup n;
X	extern string domain( string );
X	extern string right( string, int );
X	extern int dlevel( newsgroup );
X	extern string my_domain;
X	extern string my_mail_address;
X	
X	/* show me everything written by people at my own site */
X	if( domain(From) == my_domain ) {
X		/* Note my own articles in a database of good ones */
X		if( From == my_mail_address )
X			my_articles[mess_id] = true;
X		accept;
X		}
X	 else if( domain(From) == "hated.domain.com" )
X		reject;		/* never show me anything from THAT site */
X	/* also show me anything posted only for citywide distribution */
X	accept if distribution_level <= dlevel(#city);
X
X	reject if count(newsgroups) > 6;	/* I hate crossposting */
X
X	/* See if it's a followup to one of MY messages */
X	accept if References in my_articles;
X
X	/* See if any of the messages this is a followup of are in
X	   our database of bad messages.  If so, reject it */
X	reject if References in badmessages;
X
X	/* and of course, kill the bad guys */
X	reject if From in hated_users;
X
X	/* Now do the newsgroup specific code */
X	for( n in newsgroups ) switch( n ) {
X		case #rec.humor:
X			/* adjust the score of messages that are crossposted
X				to groups you don't like */
X			if( is talk.bizzare || is alt.flame )
X				adjust -10;
X			/* but I like local humour */
X			accept if distribution_level <= dlevel(#country);
X			break;
X
X		case #news.groups:
X			/* If you really don't like a user in a group,
X			   arrange to store the message id of every
X			   message he posts in your bad message database.
X			   You won't even see the followups, and it
X			   will be as though he didn't exist on the net. */
X			if( From == "karl@ddsw1.mcs.com" ) {
X				badmessages[mess_id] = true;
X				reject;
X				}
X			break;
X		case #sci.physics:
X			/* I only want to see messages that are crossposted
X			   to both sci.physics AND sci.astro, not just one
X			   of them */
X			reject if !is sci.astro;
X			break;
X		case #rec.arts.comics:
X			/* I only want articles that mention watchmen in
X				the subject */
X			if( subject has "watchmen" )
X				accept;
X			 else
X				reject;
X		case #news.admin:
X			/* bump the score of any article that mentions
X			   my name */
X			if( text has "brad.*templeton" || subject has "brad" )
X				adjust 1000;
X			break;
X		case #talk.politics.misc:
X			/* I hate long rebuttals.  If the article is mostly
X			   lines that are included from another, then can it */
X			if( followup && lines / (1+line_count(included)) < 2 )
X				reject;
X			/* I hate long signatures on short articles! */
X			if( lines < 25 && line_count(signature) > 7 )
X				reject;
X			break;
X		case #talk.politics.theory:
X			/* search for libertarian only in non-included text */
X			if( newtext has "libertarian" || newtext has "ncp" )
X				accept;
X			 else
X				reject;
X		case #comp.risks:
X		case #rec.arts.sf-lovers:
X			/* my favourite groups */
X			adjust 20;
X			break;
X		default:
X			if( nice_group(n) )
X				adjust 15;
X			break;
X		
X		}
X	if( is alt.flame )
X		adjust -5;		/* I would rather not see these */
X	/* default is a score of 1, which means accept */
X	/* here at the end, we accept if the score is greater than 0, or
X	   if there was an explicit accept, of course */
X
X}
X
X
Xprocedure
XTERMINATE() {
X	extern datetime time_now;
X	/* The program is done.  Write out global databases */
X	write_database( my_articles, "~./my_articles", time_now - 3 * week );
X}
X
END_OF_FILE
if test 5676 -ne `wc -c <'samples/sample.nc'`; then
    echo shar: \"'samples/sample.nc'\" unpacked with wrong size!
fi
# end of 'samples/sample.nc'
fi
if test -f 'sysdefs.x286' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'sysdefs.x286'\"
else
echo shar: Extracting \"'sysdefs.x286'\" \(4160 characters\)
sed "s/^X//" >'sysdefs.x286' <<'END_OF_FILE'
X/*
X * Various system dependent and fine-tuning #defines
X */
X
X
X/* define if ints are 32 bits, or more -- not on Xenix/286 */
X
X#undef	INTS_32		/* 1 */
X/*
X * Define this macro according to whether your compiler takes ANSI C
X * style function prototypes or not.  (ie.  int f( int, char * ); )
X */
X
X#define ANSI_PROTO 1
X
X#ifdef ANSI_PROTO
X# define AC(x)	x
X#else
X# define AC(x)	()
X#endif /*ANSI_PROTO*/
X
X/* Define these according to the directories of your system */
X
X#define NEWSLIB		"/usr/lib/news"		/* lib directory for news */
X#define	NEWSSPOOL	"/usr/spool/news"	/* spool dir for news */
X/* Define this to be the name of the file that contains the sizes for
X * the various distributions
X */
X#define DISTLIST	"distlist"	/* level of distrs */
X
X/*
X * Define this to be, in lower case, the domain name your news programs use
X * when they generate a From: address.  If you don't define this, you will
X * get your sitename appended with the default domain. (uucp unless you change
X * it below)
X */
X#undef MAILDOMAIN	"foo.uucp"		/* All lower case!!!! */
X
X#define DEFDOMAIN	 "uucp"
X
X
X/*
X * Some raw system-type defines
X */
X
X#undef NEED_SLIB 1			/* for systems without system V string
X					calls, such as V7 or BSD */
X
X#ifdef NEED_SLIB
X/* why don't the BSD people get with it and include these new names? */
X#define strchr index
X#define strrchr rindex
X#endif
X
X#define HAS_UNAME 1			/* for systems with the uname call */
X
X/*
X * Define this if you are a BSD system with the gethostname call.
X */
X#undef GETHOSTNAME 1				/* BSD gethostname call */
X
X/*
X * If you don't have this call, and you aren't a SYSV system with the
X * uname call, or a Xenix or other system with the /etc/systemid file,
X * then you should define the symbol 'sysname' to be a string with your
X * system name, as it appears in Xref: lines.  You can often do this on
X * older systems by saying '#include <whoami.h>'
X */
X
X#undef sysname "mysite"
X
X/* The maximum length of a single component in a filename */
X
X#ifndef MAX_FNAME_LEN
X
X# include <sys/types.h>
X# include <sys/dir.h>
X
X# ifdef DIRSIZ
X#  define MAX_FNAME_LEN DIRSIZ
X# else /*DIRSIZ*/
X#  define MAX_FNAME_LEN 14		/* normal for most unix */
X# endif /*DIRSIZ*/
X
X#endif /*MAX_FNAME_LEN*/
X
X/* System dependent definitions for calling the C compiler */
X
X/* How to call the C preprocessor on a file.  Usually either /lib/cpp or
X   cc -E, depending on the system */
X
X#define CPP "/lib/cpp"
X
X/* The pathname of the 'cc' C compiler */
X
X#define CCOMP "/bin/cc"
X
X/* Option to tell cc about where the "ucode.h" file can be found */
X/* Other system include files may go there as well */
X
X#define UCODEDIR "-I/usr/lib/news/newsclip"
X
X/* Location of the newsclip library -- if it isn't present the user
X   must provide it himself as a command line options */
X
X#define CLIPLIB "/usr/lib/news/newsclip/cliplib.a"
X/* The basename of the clip library, so that it can be found in the
X * current dir
X */
X#define CLIPBASE "cliplib.a"
X
X/* Up to 3 extra options for the C compiler.  For Xenix/286, they are
X   all used to set a stack size and request large model.  You can
X   remove the large model request if desired.  If you need more options,
X   you will have to change the source code in main.c of the compiler */
X
X#define EXTRAOPT1 "-Ml2"
X#define EXTRAOPT2 "-F"
X#define EXTRAOPT3 "2000"
X
X
X/* probably fixed, unless you have 64 bit ints */
X
X#ifdef INTS_32
X# define MAXINT	 2147483647
X#else
X# define MAXINT  32767				/* change this if need be */
X#endif
X
X/*
X * Don't change these unless you want to fine tune the program
X */
X
X#define MAX_FNAME 300			/* max length of file name */
X
X#define MAX_LLEN 300			/* max length of typical input line */	
X
X#define MAX_HLINES 50			/* max number of header lines */
X
X#define MAX_ARRAY 200			/* max elements in an array */
X
X#define MAX_NGLEN 60			/* max length of newgroup name */
X
X#define BITMAP_SIZE 1000		/* at most 8000 articles at a time? */
X
X#define MAX_OPTS 30			/* max options for C compiler or
X						user program */
X
X#define TBLOCK_SIZE 5000		/* size of chunks for use in temporary
X						allocator */
X#define MIN_TBLOCK TBLOCK_SIZE/64	/* smallest block to allocate */
X
X#define HLINE_SIZE 2500			/* buffer for header line */
X
END_OF_FILE
if test 4160 -ne `wc -c <'sysdefs.x286'`; then
    echo shar: \"'sysdefs.x286'\" unpacked with wrong size!
fi
# end of 'sysdefs.x286'
fi
echo shar: End of archive 3 \(of 15\).
cp /dev/null ark3isdone
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