[comp.sources.misc] v09i076: newsclip 1.1, part 7 of 15

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

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

#! /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 7 (of 15)."
# Contents:  comp/check.c comp/symtab.c has.c pipe.c
# Wrapped by allbery@uunet on Tue Dec 19 20:09:59 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'comp/check.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'comp/check.c'\"
else
echo shar: Extracting \"'comp/check.c'\" \(11942 characters\)
sed "s/^X//" >'comp/check.c' <<'END_OF_FILE'
X
X
X#include "nc.h"
X
X/*
X * Typechecking routines for parse trees
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#define numtype(x)	((x)==T_INTEGER||(x)==T_DATE||(x)==T_NEWSGROUP)
X#define stringtype(x)	((x)==T_STRING||(x)==T_NEWSGROUP||(x)==T_USERNAME)
X
Xdtype
Xcheck( tp )
Xnodep tp;			/* the tree pointer */
X{
X	extern struct node_info node_table[];
X	int nt;			/* node type */
X	int i;
X	dtype rtype;		/* our return type */
X	dtype ktypes[MAX_KIDS];
X
X	if( !tp )
X		return 0;
X
X	nt = tp->ntype;
X	for( i = 0; i < node_table[nt].kids; i++ )
X		ktypes[i] = check( tp->kids[i] );
X
X	rtype = T_INTEGER;
X
X	/* deal with the special cases */
X	switch( nt ) {
X		case N_LIST:
X			{
X			listp ol;
X			/* check the parent for proc/func */
X			/* we must return to avoid setting type */
X			for( ol = (listp)tp; ol; ol = ol->next )
X				check( ol->kid );
X			return 0;
X			}
X		case N_FOREACH:
X
X			if( insist_variable( kid0(tp) ) )
X				break;
X
X			if( ktypes[1] & T_ARRAY ) {
X				if( (ktypes[1] & T_BASETYPE) != ktypes[0] ) {
X					terror( tp, "Loop variable and range are not of matching type" );
X					break;
X					}
X				}
X			 else if( ktypes[1] == T_DATABASE ) {
X				if( ktypes[0] != T_STRING ) {
X					terror( tp, "Database loop requires a string variable" );
X					break;
X					}
X				}
X			 else {
X				terror( tp, "for( xx in yy ) -- invalid 'yy' to search through" );
X				}
X			break;
X		case N_INDEX:
X			if( ktypes[0] == T_DATABASE ) {
X				if( stringtype(ktypes[1]) )
X					make_string( kid1(tp), ktypes[1] );
X				 else
X					terror( tp, "Database index must be a single string" );
X				rtype = T_INTEGER;
X				}
X			else {
X				if( !( ktypes[0] & T_ARRAY ) )
X				
X					terror( tp, "Indexing requires an array or database" );
X				else if( ktypes[1] != T_INTEGER )
X					terror( tp, "Array index must be an integer");
X				rtype = ktypes[0] & T_BASETYPE;
X				}
X			break;
X		case N_STRING:
X			rtype = T_STRING;
X			break;
X		case N_NGROUP:
X			rtype = T_NEWSGROUP;
X			break;
X		case N_PAREN:
X			rtype = ktypes[0];
X			break;
X			
X		case N_EQ:
X		case N_NE: 
X			if( ktypes[0] != ktypes[1] ) {
X				if( numtype(ktypes[0]) && numtype(ktypes[1]) ) {
X					rtype = T_INTEGER;		/* no cast */
X					break;
X					}
X				if( stringtype(ktypes[0]) && stringtype(ktypes[1]) ) {
X					make_string(kid0(tp), ktypes[0]);
X					make_string(kid1(tp), ktypes[1]);
X					rtype = T_INTEGER;
X					break;
X					}
X				if( (ktypes[0] == T_ARRAY || ktypes[1] ==
X						T_ARRAY) && ktypes[0] &
X						ktypes[1] & T_ARRAY ) {
X					rtype = T_INTEGER;
X					break;
X					}
X				/* check for comparison to NIL */
X				terror( tp, "Comparison of incompatible types");
X				}
X			 else if( ktypes[0] > T_STRING ) {
X				/* if one is a predeclared symbol, that's ok */
X				if( !predsym(kid0(tp)) && !predsym(kid1(tp)) )
X					terror( tp, "Comparison on uncomparable types");
X				}
X			rtype = T_INTEGER;
X			break;
X		case N_IN:
X		case N_NOT_IN:
X			if( ktypes[1] == T_DATABASE ) {
X				if( stringtype(ktypes[0]) )
X					make_string( kid0(tp), ktypes[0] );
X				else if( ktypes[0] != arrayof(T_STRING) )
X					terror( tp, "Can only check for strings and strings arrays in databases" );
X				}
X			else if( !( ktypes[1] & T_ARRAY ) )
X				terror( tp, "Can only check IN array or database" );
X			else if((ktypes[0]&T_BASETYPE)!=(ktypes[1]&T_BASETYPE))
X				terror( tp, "Types don't match on IN" );
X			rtype = T_INTEGER;
X			break;
X		case N_HAS:
X		case N_NOT_HAS:
X			if( stringtype(ktypes[1]) ) {
X				if( kid1(tp)->ntype == N_STRING ) {
X					/* turn the hard string into a
X						precompiled pat*/
X					nodep skid;
X					int patnum;
X					skid = kid1(tp);
X					patnum = pat_number((char *)kid0(skid));
X					free( (char *)kid0(skid) );
X					skid->kids[0] = (nodep) patnum;
X					skid->ntype = N_PATTERN;
X					}
X				 else
X					make_string( kid1(tp), ktypes[1] );
X				}
X			 else if( !( ktypes[1] & T_ARRAY && stringtype(ktypes[1]
X						& T_BASETYPE) ) && ktypes[1] !=
X						T_DATABASE ) 
X				terror( tp, "HAS pattern must be string or database" );
X			if( stringtype(ktypes[0]) ) 
X				make_string( kid0(tp), ktypes[0] );
X			else if( !stringtype(ktypes[0]&T_BASETYPE) &&
X					ktypes[0] != T_DATABASE &&
X					ktypes[0] != T_TEXT ) 
X				terror( tp, "HAS search area must be string, database or text region" );
X			rtype = T_INTEGER;
X			break;
X
X		case N_POSTINC:
X		case N_POSTDEC:
X		case N_PREINC:
X		case N_PREDEC:
X			insist_variable( kid0(tp) );
X			if( ktypes[0] != T_INTEGER ) 
X				terror(tp,"Increment and decrement allowed on numbers only" );
X			rtype = T_INTEGER;
X			break;
X		case N_QUERY:
X			if( ktypes[0] != T_INTEGER )
X				terror(tp, "Query condition must be numeric" );
X			 else if( ktypes[1] != ktypes[2] )
X				terror(tp, "Types of query clauses don't match" );
X			rtype = ktypes[1];
X			break;
X		case N_ID:
X			rtype = ((symptr)kid0(tp))->type;
X			break;
X		case N_CALL:
X			{
X			symptr prsym;
X			if( kid0(tp) && (prsym = (symptr)kid0(kid0(tp)) ) ) {
X				check_args( prsym, (listp)kid1(tp) );
X				if( prsym->decl_type != ST_PROC )
X					terror( tp, "'%s' is not a procedure",
X						prsym->name );
X				}
X			break;
X			}
X		case N_FUNCALL:
X			{
X			symptr funsym;
X			if( kid0(tp) && (funsym = (symptr)kid0(kid0(tp)) ) ) {
X				check_args( funsym, (listp)kid1(tp) );
X				rtype = funsym->type;
X				if( funsym->decl_type != ST_FUNC )
X					terror(tp, "'%s' is not a function",
X						funsym->name );
X				}
X			break;
X			}
X		case N_ASSIGN:
X			if( !insist_variable( kid0(tp) ) ) {
X				if( assign_check( ktypes[0], kid1(tp),
X								ktypes[1] ) )
X					terror( tp, "Incompatible types on assignment" );
X				}
X			rtype = 0;
X			break;
X		case N_PARSE:		/* assign array */
X			make_string( kid1(tp), ktypes[1] );
X			insist_variable( kid0(tp) );
X			if( ktypes[0] & T_ARRAY ) {
X				if( kid2(tp) )
X					make_string( kid2(tp), ktypes[2] );
X				 else
X					terror(tp,"Array parse requires delimiters" );
X				}
X			 else {
X				if( kid2(tp) )
X					terror(tp,"Delimiters are only meaningful on an array parse");
X				}
X			break;
X		case N_ARINIT:		/* init empty array */
X			insist_variable( kid0(tp) );
X			if( ktypes[0] & T_ARRAY ) {
X				if( ktypes[1] != T_INTEGER )
X					terror(tp,"Array size must be integer");
X				}
X			 else
X				terror(tp,"Array assign requires array variable");
X			break;
X		case N_FOR:
X			if( kid1(tp) != NIL && ktypes[1] != T_INTEGER )
X				terror( tp, "For loop condition requires bool/int" );
X			rtype = 0;
X			break;
X		case N_GOTO: {
X			symptr sym;
X			if( kid0(tp) && (sym = (symptr)kid0(kid0(tp))) &&
X					sym->decl_type != ST_LABEL )
X				terror( tp, "'%s' is not a label", sym->name );
X			break;
X			}
X		case N_RETURN: {
X			extern int in_routine;
X			if( in_routine == ST_FUNC ) {
X				extern dtype routine_type;
X				if( kid0(tp) == NIL )
X					terror( tp, "Function returns must return a value" );
X				else if( assign_check( routine_type, kid0(tp),
X							ktypes[0] ) )
X					terror( tp, "Invalid type for function return value" );
X				}
X			 else if( kid0(tp) != NIL )
X				terror( tp, "Only functions may return values");
X			
X			break;
X			}
X		default: {
X			byte nfl;		/* flags for node type */
X			extern int in_routine;
X
X			nfl = node_table[nt].flags;
X			if( nfl & TF_RET && in_routine == ST_FUNC )  {
X				terror( tp, "Accept and Reject are not allowed inside functions" );
X				break;
X				}
X			if( !(nfl & TF_RETINT) ) {
X				if( nfl & TF_ONEINT ) 
X					rtype = procint( tp, rtype, ktypes[0] );
X			 	else if( nfl & TF_2INT ) {
X					rtype = procint( tp, rtype, ktypes[0] );
X					rtype = procint( tp, rtype, ktypes[1] );
X					}
X				}
X			break;
X			}
X
X		}
X	tp->ndtype = rtype;
X	return rtype;
X
X}
X
X/* make sure a node returns a string */
X
Xmake_string( tp, tpt )
Xnodep tp;		/* tree pointer */
Xdtype tpt;		/* type of this tree */
X{
X	switch(tpt) {
X		case T_NEWSGROUP:
X			tp->nflags |= CAST_NGNAME;
X			tp->ndtype = T_STRING;
X			break;
X		case T_USERNAME:
X			tp->nflags |= CAST_MAILNAME;
X			tp->ndtype = T_STRING;
X			break;
X		case T_STRING:	
X			break;
X		default:
X			terror( tp, "String required" );
X			break;
X		}
X}
X
X/* Expect an integer or numeric argument */
X
Xdtype
Xprocint( tp, otype, newtype )
Xnodep tp;		/* field */
Xdtype otype;		/* old type */
Xdtype newtype;		/* type of argument */
X{
X	if( newtype == T_DATE || newtype == T_INTEGER )
X		return newtype;
X	 else if( newtype == T_NEWSGROUP )
X		return T_INTEGER;
X	 else
X		terror( tp, "Number type required" );
X	return T_INTEGER;
X}
X
X
X
X/* insist that the tree is a variable, return give error and return
X   TRUE if it is not */
X
Xbool
Xinsist_variable( tr )
Xnodep tr;		/* tree that must be a variable */
X{
X	if( is_variable(tr) ) {
X		tr->nflags |= NF_LVALUE;
X		return FALSE;
X		}
X	else {
X		terror( tr, "Assignment to non-variable" );
X		return TRUE;
X		}
X}
X
Xbool
Xis_variable( tr )
Xnodep tr;		/* variable tree */
X{
X	int ttype;
X
X	ttype = tr->ntype;
X	if( ttype == N_ID ) {
X		symptr tid;
X		tid = (symptr)kid0(tr);
X		return tid->decl_type == ST_VAR && !(tid->sflags & OSF_CONST);
X		}
X	else if( ttype == N_INDEX && is_variable(kid0(tr)) )
X		return TRUE;
X	return FALSE;
X}
X
X/*
X * Test if two types are assignment compatible and set cast flags
X * returns true if there is an error.
X */
X
X
Xbool
Xassign_check( destype, src, srctype )
Xdtype destype;			/* type of destination var */
Xnodep src;			/* source tree */
Xdtype srctype;			/* source type */
X{
X	if( destype != srctype ) {
X		if( destype & T_ARRAY && srctype == T_ARRAY )
X			return FALSE;
X		switch( destype ) {
X			case T_DATE:
X				if( srctype == T_INTEGER ) 
X					src->nflags |= CAST_DATE;
X				 else {
X					terror(src,"Integer or date required");
X					return TRUE;
X					}
X				break;
X			case T_INTEGER:
X				if( srctype == T_DATE )
X					src->nflags |= CAST_INT;
X				 else if( srctype != T_NEWSGROUP ) {
X					terror(src,"Numeric value required");
X					return TRUE;
X					}
X				break;
X			case T_STRING:
X				if( srctype == T_NEWSGROUP )
X					src->nflags |= CAST_NGNAME;
X				 else if( srctype == T_USERNAME )
X					src->nflags |= CAST_MAILNAME;
X				 else {
X					terror(src,"String value required");
X					return TRUE;
X					}
X				break;
X			case T_GENARRAY:
X				if( !(srctype & T_ARRAY) ) {
X					terror( src,"Array value required" );
X					return TRUE;
X					}
X				break;
X			case T_GENPTR:
X				if( !( srctype & T_ARRAY || srctype == T_STRING || srctype == T_DATABASE || srctype == T_USERNAME ) ){
X					terror( src,"Structured data value required" ); 
X					return TRUE;
X					}
X				break;
X			default:
X				return TRUE;
X			}
X		}
X	return FALSE;		/* types match */
X}
X
X/* Check the arguments on a call to a procedure or function */
X
Xcheck_args( funsym, funargs )
Xsymptr funsym;		/* the symbol for the subroutine */
Xlistp funargs;		/* the argument list */
X{
X	struct typelist *dtlist;	/* declared type list */
X	listp curarg;
X	int i;
X
X
X	dtlist = funsym->argtypes;
X
X	/* a null type list means an arbitrary argument list */
X	if( !dtlist )
X		return;
X
X	curarg = funargs;
X	for( i = 0; i < dtlist->argcount; i++ ) {
X		if( curarg ) {
X			if( assign_check( dtlist->args[i], curarg->kid,
X						curarg->kid->ndtype ) )
X				terror( curarg, "Type mismatch on argument %d of '%s'", i+1, funsym->name );
X			}
X		 else {
X			/* end of list */
X			if( i >= dtlist->argmin ) {
X				terror( funargs, "%s: Too few arguments",
X						funsym->name );
X				return;
X				}
X			break;		/* loop done */
X			}
X		curarg = curarg->next;
X		}
X	/* if there were still arguments left, count them */
X	while( curarg ) {
X		if( ++i >= dtlist->argmax ) {
X			terror( funargs, "%s: Too many arguments",funsym->name);
X			return;
X			}
X		curarg = curarg->next;
X		}
X
X}
X
X/* is this tree a reference to a predefined symbol? */
Xint
Xpredsym( tp )
Xnodep tp;
X{
X	return tp->ntype == N_ID && ((symptr)kid0(tp))->sflags & OSF_PREDEF;
X}
END_OF_FILE
if test 11942 -ne `wc -c <'comp/check.c'`; then
    echo shar: \"'comp/check.c'\" unpacked with wrong size!
fi
# end of 'comp/check.c'
fi
if test -f 'comp/symtab.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'comp/symtab.c'\"
else
echo shar: Extracting \"'comp/symtab.c'\" \(11785 characters\)
sed "s/^X//" >'comp/symtab.c' <<'END_OF_FILE'
X
X
X#include "nc.h"
X
X/*
X * Newsclip compiler symbol table routines.
X *
X * This code handles general symbol manipulation for the user program
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  * There are 3 levels of symbol table.  There's the table of special
X  * globals, the global symbol table and the local symbol table for
X  * each routine.  We do not support symbol tables inside compound
X  * statments.
X  */
X
X#define MAX_ST_INDEX 2
X
Xdbptr sym_stack[MAX_ST_INDEX+1];	/* stack of 3 symbol tables */
Xint cur_st_index = 0;
Xdbptr cur_symtab;		/* current symbol table */
X
Xdbptr outer_symtab;		/* the table of special symbols */
Xdbptr global_symtab;		/* the gloal symtab for the user */
X
Xstruct sym_entry Unknown = {
X"Unknown", 0, 0, 0, 0, 0, 0 };
X
X
Xsymtab_init()
X{
X	int i;
X	symptr thesym;
X	extern struct outsym predefs[];
X
X	outer_symtab = init_db( 40, sizeof( struct sym_entry ) );
X
X	for( i = 0; predefs[i].name; i++ ) {
X		thesym = (symptr)add_rec( outer_symtab, predefs[i].name,
X						AR_CREATE | AR_NOALLOC );
X		thesym->decl_type = predefs[i].odecl_type;
X		thesym->type = predefs[i].otype;
X		/* do something with flags */
X		thesym->sflags = predefs[i].flags;
X		thesym->argtypes = predefs[i].atlist;
X		}
X	/* Link up the arg lists for predefined routines */
X
X	global_symtab = init_db( 80, sizeof( struct sym_entry ) );
X	cur_symtab = global_symtab;
X	sym_stack[0] = outer_symtab;
X	sym_stack[1] = global_symtab;
X	cur_st_index = 1;
X}
X
Xnodep
Xextern_var( varname, type )
Xchar *varname;		/* name of variable */
Xdtype type;		/* type of variable */
X{
X	symptr thesym;
X
X	if( thesym = extern_decl(varname,ST_VAR,type,(listp)0) ) {
X		if( thesym->sflags & OSF_CONST ) 
X			return NIL;
X		 else
X			return tree( N_EXTERN, declid(thesym) );
X		}
X	 else
X		return tree( N_EXTERN, NIL );
X	
X}
X
X/* Do a general external declaration.
X * If the symbol exists at this level, complain about a
X * redeclaration. 
X * If the symbol exists at a higher level, check that things
X * match, and if so, create the symbol at this level.
X */
X
Xsymptr
Xextern_decl( name, sytype, type, argtlist )
Xchar *name;			/* name of external symbol */
Xint sytype;			/* type of symbol */
Xdtype type;			/* user type */
Xstruct typelist * argtlist;	/* arglist if needed */
X{
X	symptr thesym;
X	extern bool no_externals;	/* forbid undefined externals */
X
X	thesym = (symptr)add_rec( cur_symtab, name, AR_NEWONLY );
X	if( thesym ) {
X		symptr globname;
X		globname = (symptr)get_rec( outer_symtab, name );
X		/* should be an option to disable true externals */
X		if( globname ) {
X			int flags;
X			if( globname->decl_type != sytype ||
X					globname->type != type ) {
X				parerror( "External '%s' is of invalid type.",
X							name );
X				}
X			else if( (sytype == ST_FUNC || sytype == ST_PROC) &&
X				!arglists_match(argtlist, globname->argtypes )){
X					parerror("Invalid argument list for '%s'",
X							name );
X				}
X			 else {
X				handle_outer( globname, TRUE );
X				thesym->sflags = globname->sflags;
X				}
X			}
X		 else {
X			if( no_externals )
X				parerror( "Undefined external references disallowed" );
X			thesym->sflags = 0;
X			}
X		thesym->decl_type = sytype;
X		thesym->type = type;
X		thesym->argtypes = argtlist;
X		return thesym;
X		}
X	 else {
X		parerror( "Symbol '%s' redeclared", name );
X		return (symptr)0;
X		}
X}
X
X
Xnodep
Xextern_func( funcname, type, arglist, is_external )
Xchar *funcname;		/* name of proc or func */
Xdtype type;		/* return type or 0 for procedure */
Xlistp arglist;		/* list of arguments */
Xbool is_external;	/* is this an external or a forward declaration */
X{
X	symptr funcsym;		/* the symbol created for the function */
X	struct typelist *atlist;/* the argument type list */
X	int fstype;		/* the type of subroutine */
X
X	fstype = type ? ST_FUNC : ST_PROC;
X
X	atlist = buildargs( arglist );
X
X	if( is_external ) {
X		funcsym = extern_decl( funcname, fstype, type, atlist );
X		if( !funcsym )
X			return NIL;
X		}
X	 else {
X		if( cur_st_index != 1 ) {
X			parerror( "Forward declaration of '%s' must be a global declaration", funcname );
X			return NIL;
X			}
X		if( funcsym = declare_local( funcname, fstype, type ) ) {
X			funcsym->sflags |= SF_FORWARD;
X			funcsym->argtypes = atlist;
X			}
X		 else
X			return NIL;
X		}
X	if( funcsym->sflags & OSF_CONST )
X		return NIL;
X	 else
X		return tree( N_EXT_FUNC, declid(funcsym), arglist );
X}
X
Xsymptr
Xdeclare_local( name, sytype, type )
Xchar *name;		/* symbol name */
Xint sytype;
Xdtype type;
X{
X	symptr thesym;
X
X	thesym = (symptr)add_rec( cur_symtab, name, AR_NEWONLY );
X	if( thesym ) {
X		thesym->decl_type = sytype;
X		thesym->type = type;
X		thesym->sflags |= SF_LOCAL;
X		}
X	 else
X		parerror( "Symbol '%s' redeclared", name );
X	return thesym;
X}
X
Xnodep
Xdeclare_var( varname, type )
Xchar *varname;		/* name of variable */
Xdtype type;		/* type of variable */
X{
X	symptr sym;
X
X	if( sym = declare_local( varname, ST_VAR, type ) )
X		return tree( N_DECL_VAR, declid(sym) );
X	 else
X		return NIL;
X}
X
Xnodep
Xgen_declare( name )
Xchar *name;		/* name of user routine */
X{
X	symptr sym;
X	sym = (symptr)add_rec( cur_symtab, name, AR_CREATE );
X	/* is nil possible? */
X	return sym ? declid(sym) : NIL;
X}
X
Xnodep
Xdeclare_arg( name, type )
Xchar *name;		/* name of the argument */
Xdtype type;		/* type for the argument */
X{
X	/* for our purposes, these are just like variables */
X	return declare_var( name, type );
X}
X
Xnodep
Xdeclare_lab( name )
Xchar *name;		/* name of the label */
X{
X	symptr sym;
X
X	sym = (symptr)add_rec( cur_symtab, name, AR_CREATE );
X	if( sym->decl_type != 0 && sym->decl_type != ST_LABREF ) {
X		parerror( "Label '%s' redeclared", name );
X		return declid( &Unknown );
X		}
X	 else {
X		sym->decl_type = ST_LABEL;
X		return declid(sym);
X		}
X
X
X}
X
X/* special globals to use while checking a routine */
X
Xint in_routine = 0;
Xint routine_type = 0;
X
X/* General procedure to check and output a subroutine */
X
Xroutine_decl( rid, rargs, rcode, type, symtype )
Xnodep rid;		/* identifier for routine */
Xlistp rargs;		/* the argument list */
Xnodep rcode;		/* the code block for the routine */
Xdtype type;		/* type to give the routine */
Xint symtype;		/* type of symbol -- proc or function */
X{
X	symptr thesym;
X	struct typelist *atlist;/* the argument type list */
X	extern int got_error;	/* parsing error status */
X
X	if( !rid )
X		return;
X
X	thesym = (symptr)kid0(rid);
X
X	atlist = buildargs( rargs );
X
X	/* set up the symbol */
X
X	if( thesym->decl_type == 0 )  {
X		thesym->type = type;
X		thesym->sflags |= SF_LOCAL;
X		thesym->decl_type = symtype;
X		thesym->argtypes = atlist;
X		}
X	 else {
X		if( thesym->sflags & SF_FORWARD ) {
X			if( thesym->decl_type != symtype ||
X					thesym->type != type ||
X					!arglists_match( thesym->argtypes,
X					atlist ) )
X				terror( rargs, "Subroutine declaration does not match forward declaration" );
X			/* turn off forward for future */
X			thesym->sflags &= ~SF_FORWARD;
X			}
X		 else {
X			parerror( "Symbol '%s' redeclared", thesym->name );
X			return;
X			}
X		}
X
X	/* set special externals */	
X
X	if( got_error < SYNTAX_ERROR ) {
X		in_routine = symtype;
X		routine_type = type;
X
X		check( rcode );
X
X		outsubr( thesym, rargs, rcode );
X
X		/* clear special externals */
X
X		in_routine = 0;
X		routine_type = 0;
X		}
X	/* free up the code and arglist.  Symbol and type list stay in
X	   the symbol table */
X	treefree( rcode );
X	treefree( rargs );
X}
X
X
X/*
X * Look up a label.  Unusual in that it is possible the label has
X * not been declared yet.  We will create it if so and wait for it
X * to get declared.
X */
X
Xnodep
Xgoto_lookup( name )
Xchar *name;
X{
X	symptr sym;
X
X	sym = (symptr)get_rec( cur_symtab, name );
X	if( sym ) {
X		if( sym->decl_type == ST_LABEL || sym->decl_type == ST_LABREF )
X			return declid(sym);
X		 else {
X			parerror( "Symbol '%s' is not a label", name );
X			return declid( &Unknown );
X			}
X		}
X	 else {
X		/* the symbol was not found.  That's not an error yet */
X		sym = (symptr)add_rec( cur_symtab, name, AR_CREATE );
X		sym->decl_type = ST_LABREF;
X		return declid(sym);
X		}
X}
X
X/* 
X * General symbol lookup.
X */
X
Xnodep
Xsymlookup( symname, t1, t2 )
Xchar *symname;
Xdtype t1,t2;			/* possible types for symbol */
X{
X	int i;
X	symptr sym;
X
X	for( i = cur_st_index; i >= 0; i-- ) {
X		sym = (symptr)get_rec( sym_stack[i], symname );
X		if( sym ) {
X			if( i == 0 && !(sym->sflags & OSF_PREDEF) ) {
X				parerror( "Symbol '%s' must be declared external before it can be referenced", symname );
X				return declid( &Unknown );
X				}
X			if( sym->decl_type != t1 && sym->decl_type != t2 )
X				parerror( "Incorrect kind of identifier: '%s'",
X						symname );
X			return declid(sym);
X			}
X		}
X
X	/* never found it */
X	parerror( "Undeclared symbol: '%s'", symname );
X	return declid( &Unknown );
X}
X
Xnodep
Xdeclid(sym)
Xsymptr sym;
X{
X	return tree( N_ID, (nodep)sym );
X}
X
Xpush_table()
X{
X	if( cur_st_index < MAX_ST_INDEX ) {
X		/* create a new table */
X		cur_symtab = init_db( 20, sizeof( struct sym_entry ) );
X		sym_stack[++cur_st_index] = cur_symtab;
X		}
X}
X
Xpop_table()
X{
X	/* free the old table */
X	free_db( cur_symtab );
X	cur_symtab = sym_stack[--cur_st_index];
X}
X
Xstruct typelist *
Xbuildargs( alist )
Xlistp alist;		/* argument declaration list */
X{
X	dtype tempdt[255];		/* build arglist here */
X	struct typelist *ret;		/* final return pointer */
X	int anum;
X	int i;
X
X	for( anum = 0; alist; anum++,alist = alist->next ) {
X		nodep decvar, decid;
X		symptr arsym;
X
X		if( decvar = alist->kid ) {
X			if( decvar->ntype == N_INT )
X				tempdt[anum] = (int)kid0(decvar);
X			 else {
X				if( (decid = kid0(decvar)) &&
X						(arsym = (symptr)kid0(decid)) )
X					tempdt[anum] = arsym->type;
X				 else
X					anum--;	/* nil arg */
X				}
X			}
X		 else
X			anum--;		/* nil argument */
X		}
X
X	/* allocate room for a real arglist */
X
X	ret = (struct typelist *) checkalloc( sizeof(struct typelist) );
X	if( anum )
X		ret->args = (dtype *) checkalloc( anum * sizeof(dtype) );
X	 else
X		ret->args = (dtype *)0;
X
X	ret->argmin = ret->argmax = ret->argcount = anum;
X	/* copy over the found arguments */
X	for( i = 0; i < anum; i++ )
X		ret->args[i] = tempdt[i];
X
X	return ret;
X}
X
X/* handle references to predefined symbols */
X
Xhandle_outer( globname, complain )
Xsymptr globname;		/* global symbol */
Xint complain;			/* complain about local header refs */
X{
X
X	int flags;
X	extern bool needs_stat;
X	extern bool wants_dist;
X
X	flags = globname->sflags;
X
X	/* if we have already seen this symbol, ignore it */
X
X	if( flags & OSF_REFERENCED )
X		return;
X	globname->sflags |= OSF_REFERENCED;
X
X	switch( flags & OSF_SPECIAL_MASK ) {
X		case SPC_HEADER:
X			if( complain )
X				insist_global(globname);
X			hcreate( globname->name, globname->type );
X			break;
X		case SPC_STAT:
X			needs_stat = TRUE;
X			break;
X		case SPC_FROM:
X			if( complain )
X				insist_global(globname);
X			makeref( globname->name + 1 );
X			makeref( "from" );
X			break;
X		case SPC_NEWSGROUPS:
X			if( complain )
X				insist_global(globname);
X			makeref( globname->name + 1 );
X			/* newsgroups already pre-referenced */
X			break;
X		case SPC_REF:
X			makeref( "references" );
X			break;
X		case SPC_DIST:
X			makeref( "distribution" );
X			wants_dist = TRUE;
X			break;
X		}
X}
X
Xinsist_global(sym)
Xsymptr sym;
X{
X	if( cur_st_index > 1 ) 
X		parerror( "Header variable declaration for '%s' must be global, not local", sym->name );
X}
X
X/* Make as though an external reference has been made to a given name */
X
Xmakeref( name )
Xchar *name;
X{
X	symptr globname;
X	globname = (symptr)get_rec( outer_symtab, name );
X	if( globname )
X		handle_outer( globname, FALSE );
X}
END_OF_FILE
if test 11785 -ne `wc -c <'comp/symtab.c'`; then
    echo shar: \"'comp/symtab.c'\" unpacked with wrong size!
fi
# end of 'comp/symtab.c'
fi
if test -f 'has.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'has.c'\"
else
echo shar: Extracting \"'has.c'\" \(11799 characters\)
sed "s/^X//" >'has.c' <<'END_OF_FILE'
X/*
X * has.c
X *
X * Library of article body searching routines.
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#include "nl.h"
X#include "rei.h"
X#include "body.h"
X
Xextern char *arr_string AC(( array *, int, int ));
Xextern void  parse_body AC(( unsigned int, unsigned int ));
Xextern void  paragraphize AC(( area_type * ));
Xextern void  init_stats AC(( int ));
X
X#define fetch_rxp( idx )	user_rxps[idx-1]
X
Xextern long time_now;		/* Used to update dbase access times. */
X
Xextern int  paragraph_scan;	/* Indicates whether in paragraph mode */
X
Xextern area_type *Article;	/* Ptr to the paragrahed article structures */
Xextern area_type *RawText;	/* Ptr to the raw article structures */
X
Xextern char *include_prefix;	/* User-defined prefix for included lines */
Xextern char *signature_start;	/* User-defined start for signature */
Xextern int pattern_count;	/* Number of expressions in user_patterns */
Xextern char *user_patterns[];	/* User's RE patterns, as typed. */
Xstatic rxp_type *user_rxps = (rxp_type *) NULL;	/* Array of compiled user REs */
X
Xstatic int scan_text AC(( int, rxp_type ));
Xstatic int scan_array AC(( array *, rxp_type ));
Xstatic int scan_db AC(( dbptr, rxp_type ));
X
X/* str_has_str() - "string has string" pattern matching routine.
X * Returns TRUE if the first argument matches the RE pattern represented
X * by the second argument; otherwise, FALSE. */
X
Xint
Xstr_has_str( sptr, pptr )
Xchar *sptr;			/* Pointer to the searched string */
Xchar *pptr;			/* Pointer to the R.E. (non-compiled) */
X{
X	return( REG_EXEC( REG_COMP_S( pptr ), sptr ) ? H_TRUE : H_FALSE );
X}
X
X/* str_has_pat() - "string has pattern" pattern matching routine.
X * Performs identically to str_has_str() above, but the pattern is
X * represented as an index into the pre-compiled user_pattern array. */
X
Xint
Xstr_has_pat( sptr, pidx )
Xchar *sptr;			/* Pointer to the searched string */
Xint pidx;			/* R.E. index (static, non-compiled R.E) */
X{
X	return( REG_EXEC( fetch_rxp( pidx ), sptr ) ? H_TRUE : H_FALSE );
X}
X
X/* str_has_db() - "string has database" pattern matching routine.
X * Tests every key contained in the given database as a RE against the given
X * string argument, and returns TRUE if any key matches. */
X
Xint
Xstr_has_db( sptr, db )
Xchar *sptr;
Xdbptr db;
X{
X	register userdb *rec;
X	
X	for( rec = (userdb *) first_rec( db ); rec;
X	     rec = (userdb *) next_rec( db, (dbrec *) rec ) ) 
X		if( REG_EXEC( REG_COMP_S( rec->name ), sptr ) ) {
X			rec->access_date = time_now;
X			return( H_TRUE );
X			}
X
X	return( H_FALSE );
X}
X
X/* str_has_arr() - "string has array" pattern matching routine.
X * Tests every element in the given string array as a RE against the
X * given string argument, and returns TRUE if any matches occurs. */
X
Xint
Xstr_has_arr( sptr, aptr )
Xchar *sptr;
Xarray *aptr;
X{
X	register rxp_type rxp;
X	register int idx;
X	int asize = aptr->arsize + AR_LOW_IDX;
X	int atype = aptr->artype;
X
X	for( idx = AR_LOW_IDX; idx < asize; idx++ ) {
X		rxp = REG_COMP_S( arr_string( aptr, atype, idx ) );
X		if( REG_EXEC( rxp, sptr ) )
X			return( H_TRUE );
X		}
X
X	return( H_FALSE );
X}
X
X/* arr_has_str() - "array has string" pattern matching routine.
X * Tests the given string as a RE against every element in the array,
X * returning TRUE if a match occurs. */
X
Xint
Xarr_has_str( aptr, pptr )
Xarray *aptr;		/* Array whose elements are to be searched */
Xchar *pptr;		/* R.E. (noncompiled) being looked for */
X{
X	return( scan_array( aptr, REG_COMP_S( pptr ) ) );
X}
X
X/* arr_has_pat() - "array has pattern" pattern matching routine.
X * Performs identically to arr_has_str() above, but the pattern is
X * represented as an index into the pre-compiled user_pattern array. */
X
Xint
Xarr_has_pat( aptr, pidx )
Xarray *aptr;		/* Array whose elements are to be searched */
Xint pidx;		/* Index into R.E. table (noncompiled) */
X{
X	return( scan_array( aptr, fetch_rxp( pidx ) ) );
X}
X
X/* arr_has_db() - "array has database" pattern matching routine.
X * Treats each key in the database as a RE pattern to be searched for
X * in the given array. TRUE is returned if any match is detected. */
X
Xint
Xarr_has_db( aptr, db )
Xarray *aptr;
Xdbptr db;
X{
X	register userdb *rec; 
X	
X	for( rec = (userdb *) first_rec( db ); rec;
X	     rec = (userdb *) next_rec( db, (dbrec *) rec ) )
X		if( H_TRUE == scan_array( aptr, REG_COMP_S( rec->name ) ) ) {
X			rec->access_date = time_now;
X			return( H_TRUE );
X			}
X
X	return( H_FALSE );
X}
X
X/* arr_has_arr() - "array has array" pattern matching routine.
X * Treats each element of the second array as a RE pattern to be searched
X * for in the first array. TRUE is returned if any match is detected. */
X
Xint
Xarr_has_arr( aptr, apptr )
Xarray *aptr;		/* Array to be searched. */
Xarray *apptr;		/* Array of patterns for which to search */
X{
X	register rxp_type rxp;
X	register int idx;
X	int apsize = apptr->arsize + AR_LOW_IDX;
X	int aptype = apptr->artype;
X
X	for( idx = AR_LOW_IDX; idx < apsize; idx++ ) {
X		rxp = REG_COMP_S( arr_string( apptr, aptype, idx ) );
X		if( H_TRUE == scan_array( aptr, rxp ) )
X			return( H_TRUE );
X		}
X
X	return( H_FALSE );
X}
X
X/* scan_array() is just common code used in the process of scanning
X * an array for the given [compiled] regular expression. */
X
Xstatic int
Xscan_array( aptr, a_rxp )
Xarray *aptr;
Xrxp_type a_rxp;
X{
X	register rxp_type rxp = a_rxp;
X	register int idx;
X	int asize = aptr->arsize + AR_LOW_IDX;
X	int atype = aptr->artype;
X
X	if( rxp )
X		for( idx = AR_LOW_IDX; idx < asize; idx++ )
X			if( REG_EXEC( rxp, arr_string( aptr, atype, idx ) ) )
X				return( H_TRUE );
X
X	return( H_FALSE );
X}
X
X/* db_has_pat() - "database has pattern" pattern matching routine.
X * Takes the given index into the user_pattern array and searches for the
X * pattern in every key in the database, returning TRUE if a match is found. */
X
Xint
Xdb_has_pat( db, pidx )
Xdbptr db;		/* Database in which to search for patterns */
Xint pidx;		/* Index into R.E. table for which to search */
X{
X	return( scan_db( db, fetch_rxp( pidx ) ) );
X}
X
X/* db_has_str() - "database has string" pattern matching routine.
X * Functions as db_has_pat() above, except that the second argument is
X * a user-constructed string rather than a constant pattern. */
X
Xint
Xdb_has_str( db, pptr )
Xdbptr db;		/* Database in which to search for patterns */
Xchar *pptr;		/* Pointer to R.E. for which to search */
X{
X	return( scan_db( db, REG_COMP_S( pptr ) ) );
X}
X
X/* db_has_arr() - "database has array" pattern matching routine.
X * Searches the given database for each string contained in the array. */
X
Xint
Xdb_has_arr( db, apptr )
Xdbptr db;
Xarray *apptr;
X{
X	register rxp_type rxp;
X	register int idx;
X	int apsize = apptr->arsize + AR_LOW_IDX;
X	int aptype = apptr->artype;
X
X	for( idx = AR_LOW_IDX; idx < apsize; idx++ ) {
X		rxp = REG_COMP_S( arr_string( apptr, aptype, idx ) );
X		if( H_TRUE == scan_db( db, rxp ) )
X			return( H_TRUE );
X		}
X
X	return( H_FALSE );
X}
X
X/* scan_db() contains the common code called from database "has" routines. */
X
Xstatic int
Xscan_db( db, rxp )
Xdbptr db;
Xrxp_type rxp;
X{
X	register dbrec *rec;
X
X	if( rxp )
X		for( rec = first_rec( db ); rec; rec = next_rec( db, rec ) )
X			if( REG_EXEC( rxp, rec->key ) )
X				return( H_TRUE );
X
X	return( H_FALSE );
X}
X
X/* text_has_str() - "text has string" pattern matching routine.
X * Searches the specified portions of the article body for the given
X * regular expression, returning TRUE if found. */
X
Xint
Xtext_has_str( tid, pptr )
Xint tid;		/* Type of the text to be searched */
Xchar *pptr;		/* Pointer to the (non-compiled) R.E. string */
X{
X	return( scan_text( tid, REG_COMP_S( pptr ) ) );
X}
X
X/* text_has_pat() - "text has pattern" pattern matching routine.
X * As text_has_str(), but uses index into user_patterns[] to obtain
X * the RE to search with. */
X
Xint
Xtext_has_pat( tid, pidx )
Xint tid;		/* Type of the text to be searched */
Xint pidx;		/* Index into array of user R.E.s */
X{
X	return( scan_text( tid, fetch_rxp( pidx ) ) );
X}
X
X/* text_has_arr() - "text has array" pattern matching routine.
X * Searches articles for entire arrays of patterns. */
X
Xint
Xtext_has_arr( tid, apptr )
Xint tid;		/* Area of text to be searched. */
Xarray *apptr;		/* Array of patterns for which to search */
X{
X	register rxp_type rxp;
X	register int idx;
X	int apsize = apptr->arsize + AR_LOW_IDX;
X	int aptype = apptr->artype;
X
X	for( idx = AR_LOW_IDX; idx < apsize; idx++ ) {
X		rxp = REG_COMP_S( arr_string( apptr, aptype, idx ) );
X		if( H_TRUE == scan_text( tid, rxp ) )
X			return( H_TRUE );
X		}
X
X	return( H_FALSE );
X}
X
X/* text_has_db() - "text has database" pattern matching routine.
X * Searches articles for entire databases of patterns. */
X
Xint
Xtext_has_db( tid, db )
Xint tid;		/* Area of text to be scanned */
Xdbptr db;		/* Database containing patterns */
X{
X	register userdb *rec; 
X	
X	for( rec = (userdb *) first_rec( db ); rec;
X	     rec = (userdb *) next_rec( db, (dbrec *) rec ) )
X		if( H_TRUE == scan_text( tid, REG_COMP_S( rec->name ) ) ) {
X			rec->access_date = time_now;
X			return( H_TRUE );
X			}
X
X	return( H_FALSE );
X}
X
X/* scan_text() is the common code used by the text "has" functions. */
X
Xstatic int
Xscan_text( tid, rxptr )
Xint tid;
Xrxp_type rxptr;
X{
X	register u_list *ul;
X	register int j;
X	area_type *ap;
X
X	if( !rxptr )
X		return( H_FALSE );
X
X	if( tid != LT_BODY || paragraph_scan ) {
X		parse_body( 1, MAXINT );
X		ap = Article;
X		}
X	else {
X		read_body( 1, MAXINT );
X		ap = RawText;
X		}
X
X	for( ; ap; ap = ap->next ) {
X		if( ap->txt_typ & tid ) {
X			if( paragraph_scan ) {
X				if( !ap->para )
X					paragraphize( ap );
X				if( REG_EXEC( rxptr, ap->para ) )
X					return( H_TRUE );
X				}
X			else for( ul = ap->list; ul; ul = ul->next ) {
X				for( j = 0; j < ul->size; j++ )
X					if( REG_EXEC( rxptr, ul->u_txt[j] ) )
X						return( H_TRUE );
X				}
X			}
X		}
X
X	return( H_FALSE );
X}
X
X/* init_patterns() compiles the static user pattern array after
X * allocating the memory necessary to maintain the parallel array. */
X
Xvoid
Xinit_patterns()
X{
X	register int i;
X
X	if( pattern_count )
X		user_rxps = (rxp_type *)
X			    perm_alloc( sizeof(rxp_type)*pattern_count );
X
X	for( i = 0; i < pattern_count; i++ )
X		user_rxps[i] = REG_COMP_P( user_patterns[i] );
X
X	set_include_prefix( include_prefix );
X	set_signature_start( signature_start );
X}
X
X/* line_count() returns the number of lines contained in the
X * specified section of the article body. */
X
Xint
Xline_count( statid )
Xint statid;
X{
X	init_stats( statid );
X
X	return( makeint( ArticleStats[ID_LINES][statid] ) );
X}
X
X/* byte_count() returns the number of bytes contained in the
X * specified section of the article body. */
X
Xint
Xbyte_count( statid )
Xint statid;
X{
X	init_stats( statid );
X
X	return( makeint( ArticleStats[ID_BYTES][statid] ) );
X}
X
X/* literal_pattern() scans the given string and returns a pointer to a 
X * constructed copy of the string with all regular expression characters
X * escaped. */
X
Xstatic char *REMagic = ".*+|?[]()^$\\";
X
Xchar *
Xliteral_pattern( ptr )
Xchar *ptr;			/* String to be literalized (escaped) */
X{
X	int len = 0;		/* Length of escaped string */
X	char *retstr, *rptr = ptr;
X	char esc = FALSE;	/* Was last character an escape? */
X
X	for( len = 1; *ptr; len++, ptr++ )
X		if( !esc && strchr( REMagic, *ptr ) )
X			len++;
X		else
X			esc = ('\\' == *ptr);
X
X	ptr = rptr;
X	rptr = retstr = perm_alloc( len*sizeof(char) );
X
X	for( esc = FALSE; *ptr; ) {
X		if( !esc && strchr( REMagic, *ptr ) )
X			*retstr++ = '\\';
X		else
X			esc = ('\\' == *ptr);
X		*retstr++ = *ptr++;
X		}
X
X	*retstr = '\0';
X
X	return( rptr );
X}
END_OF_FILE
if test 11799 -ne `wc -c <'has.c'`; then
    echo shar: \"'has.c'\" unpacked with wrong size!
fi
# end of 'has.c'
fi
if test -f 'pipe.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'pipe.c'\"
else
echo shar: Extracting \"'pipe.c'\" \(12339 characters\)
sed "s/^X//" >'pipe.c' <<'END_OF_FILE'
X
X/*
X * The "PIPE" mode interface to the newsclip program.
X *
X * This interface, defined in our news filter program specification, is for
X * use by newsreaders that wish to talk directly to a newsclip program.
X *
X * In essence, when they have an article they might wish to present to the
X * user, they can first pass it to the newsclip program, to see if the
X * user really wants to read it or not.
X *
X * This 'passing' can range from just providing the filename to engaging in
X * a dialogue to send the article down a pipe if it doesn't exist in a
X * conventional file.
X *
X * At all times we must take care that we never try to read more from a pipe
X * than is in it, or we will block.
X *
X * In pipe mode, the newsclip program is called with stdin (desc 0) as a
X * command pipe from the caller to the newsclip program, and stdout as an
X * answer pipe from the newclip program to the caller.
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
X#include "nl.h"
X#include "pipemode.h"
X#include <signal.h>
X
X/* function that reads a command from the pipe.
X * This function returns the argument count.
X * The arguments are stored in a static buffer.  Pointers into that
X * buffer are stored into argv, which should be passed by the user.
X * The first argument, argv[0], will always be the command code.  This
X * is normally the letter 'C' followed by a command letter.
X *
X * The actual arguments will follow in argv[1]..argv[argc-1].  The first
X * argument will normally be a sequence number which must be returned in
X * answers.
X *
X * If there is a problem reading from the pipe, the arg count will be 0
X * If there is a problem in the argus, the arg count will be -1
X */
X
Xstruct command command_buf;		/* command input buffer */
Xchar argument_buf[MAX_ARGLEN];		/* buffer for arguments */
Xchar last_sequence[SEQ_SIZE+1] = "-1";		/* last sequence num */
Xchar cur_newsgroup[MAX_NGLEN];		/* last newsgroup */
X
XFILE *pipelog = 0;
Xint inpipe = -1;			/* log of all input down pipe */
X
Xint
Xread_command( argv, avsize, storeseq )
Xchar **argv;		/* pointer to array to store arg pointers in */
Xint avsize;		/* number of elements in argv */
Xbool storeseq;		/* store the sequence number? */
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 howmanbytes;
X
X	argc = 0;
X
X	howmanbytes = read( F_COMPIPE, &command_buf, sizeof(struct command) );
X	if( inpipe >= 0 )
X		write( inpipe, &command_buf, howmanbytes );
X	/* fprintf( stderr, "Filter: Read %d bytes\n", howmanbytes ); */
X	if( pipelog )
X		fprintf(pipelog,"F:Got: %s\n", &command_buf.comtype );
X	if( howmanbytes == sizeof(struct command) ) {
X		argv[argc++] = &command_buf.comtype;
X		command_buf.space = 0;
X		argsize = atoi( command_buf.arg_size );
X		/* save away the sequence for replies */
X		if( storeseq ) {
X			strncpy(last_sequence, command_buf.seq_num, SEQ_SIZE);
X			last_sequence[SEQ_SIZE] = 0;
X			}
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( F_COMPIPE, &c, 1 );
X					if( inpipe >= 0 )
X						write( inpipe, &c, 1 );
X					}
X				return ERRCODE;
X				}
X			 else {
X				int size;
X				size=read(F_COMPIPE,argument_buf,argsize);
X				if( inpipe > 0 )
X					write(inpipe,argument_buf,size);
X				if( size != argsize ) {
X					return 0;
X					}
X				/* if not null terminated, give an error */
X				if( argument_buf[argsize-1] != 0 )
X					return ERRCODE;
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			if( pipelog ) {
X				int i;
X				for( i = 1; i < argc; i++ )
X					fprintf(pipelog, "F:Got Arg %d: %s\n",
X							i, argv[i] );
X				}
X			}
X		}
X	 else {
X		warning( 2, "Error reading from command pipe\n" );
X		return 0;		/* end of file */
X		}
X
X	return argc;
X}
X
X
Xchar argcount[] = "Invalid argument count";
X
X/* The master loop for pipe mode.  Read commands from the pipe and act
X   upon them */
X
Xpipe_loop()
X{
X	int argc;			/* count of arguments to command */
X	char *argv[MAX_ARGS];		/* argument pointer vector */
X	bool running;			/* control for main loop */
X	extern int accept_all;		/* accept all articles in this group */
X	extern int reject_all;		/* reject all articles in this group */
X	char *debenv;			/* debug environment variable */
X	extern char *getenv();
X	extern bool do_debug;
X
X
X	/* debug log pipes */
X#ifdef DEBUG
X	debenv = getenv("NCLIPDEBUG");
X	debenv = "truepipe";		/* temp for now */
X	if( do_debug || (debenv && lowerlet(debenv[0]) == 't') ) {
X		char pipename[MAX_FNAME];
X		extern char *dotdir;
X		sprintf( pipename, "%s/pipelog", dotdir );
X		pipelog = fopen( pipename, "w" );
X		sprintf( pipename, "%s/inpipe", dotdir );
X		inpipe = creat( pipename, 0666 );
X		if( pipelog )
X			setbuf( pipelog, NULL );
X		}
X#endif
X
X	/* First tell the newsreader that we're alive and kicking */
X	reply_ok();		/* not really a 'reply' */
X
X	/* do simple init.  Perhaps we wish to read nglas file? */
X
X	initngs(FALSE);
X
X	Uinit();
X
X	/* ignore possible keyboard signals */
X
X	signal( SIGINT, SIG_IGN );
X	signal( SIGQUIT, SIG_IGN );
X
X	running = TRUE;
X	cur_newsgroup[0] = 0;
X
X	while( running ) {
X		argc = read_command( argv, MAX_ARGS, TRUE );
X		if( argc == 0 ) {
X			fprintf( stderr, "End of File from command process\n" );
X			if( pipelog )
X				fprintf( pipelog, "End of file from command process\n" );
X			break;		/* terminate and close */
X			}
X		if( argc < 0 ) {
X			/* send an error reply */
X			reply_err( "Invalid Command" );
X			continue;
X			}
X		reset_tempalloc();
X		/* switch on the various commands */
X		switch( argv[0][1] ) {
X			case 'V':	/* version number */
X				if( argc != ABASE+1 ) {
X					reply_err( argcount );
X					break;
X					}
X				reply_arg( 'V', "V100", "ABHNPQV", "ABEHORV",
X						"NULL", "100", NULL );
X				break;
X			case 'Q':	/* quit */
X				if( argc != ABASE ) {
X					reply_err( argcount );
X					/* terminate anyway */
X					}
X				reply_ok();
X				running = FALSE;
X				break;
X			case 'P': 	/* program command */
X				/* how many args? */
X				if( argc < ABASE+1 ) {
X					reply_err( argcount );
X					break;
X					}
X				/* handle kill commands */
X				handle_command( argv[ABASE] );
X				break;
X			case 'N': 
X				/* query a newsgroup */
X				if( argc != ABASE+1 ) {
X					reply_err( argcount );
X					break;
X					};
X				try_newgroup( argv[ABASE] );
X				/* check flags? */
X				if( accept_all )
X					reply_arg( 'A', "1000", NULL );
X				else if( reject_all )
X					reply_arg( 'R', "-1000", NULL );
X				 else
X					reply_ok();
X				break;
X			case 'A': 	/* article dialogue */
X				/* newsgroup artnum [filestyle filename] */
X				do_art_dialogue( argc, argv );
X				break;
X			default:
X				reply_err( "No such command" );
X				break;
X			}
X		}
X	/* terminate this newsgroup if there was one */
X	if( cur_newsgroup[0] )
X		finish_group();
X	Uterminate();
X}
X
Xextern int reading_mode;		/* style of reading articles */
X
X/* do an article dialogue.  To understand this, you really have to read
X   the spec, so a lot of comments here won't do a lot of good. */
X
Xdo_art_dialogue( argc, argv )
Xint argc;
Xchar **argv;
X{
X	newsgroup n;			/* newsgroup of dialogue */
X	char artname[MAX_FNAME];	/* filename for article file */
X	char scorebuf[10];		/* buffer to make ascii score string */
X	extern int score;
X	extern int article_number;
X	int stat;			/* status of article */
X	FILE *artfile;
X
X
X	if( argc != ABASE+2 && argc != ABASE+4 ) {
X		reply_err( argcount );
X		return;
X		}
X	/* fprintf( stderr, "Article group %s %s mode %s file %s\n",
X		argv[ABASE], argv[ABASE+1], argv[ABASE+2], argv[ABASE+3] ); */
X	try_newgroup( argv[ABASE] );
X	article_number = atoi(argv[ABASE+1]);
X
X	if( argc > ABASE+2 ) {		/* there is a file name */
X		
X		strcpy( artname, argv[ABASE+3] );
X
X		if( argv[ABASE+2][0] == 'R' )
X			reading_mode = FILE_REQUEST;
X		 else 
X			reading_mode = FILE_FULL;
X
X		}
X	 else {
X		/* pipe read currently unimplemented */
X		reading_mode = PIPE_READ;
X		artname[0] = 0;
X		}
X	stat = accept_article( artname );
X
X	if( stat == ERRCODE )
X		reply_err( "Bad Article Dialogue" );
X
X	sprintf( scorebuf, "%d", score );
X	reply_arg( stat ? 'A' : 'R', scorebuf, NULL );
X}
X
X/* Send a reply with a given command code and various string args.  The
X * list of string args (up to 4) is terminated by a null string
X */
X
Xreply_arg( code, a,b,c,d,e )
Xchar code;
Xchar *a,*b,*c,*d,*e;
X{
X	g_reply_arg( 'R', code, a,b,c,d,e );
X}
X
X /* low level reply routine */
X
Xg_reply_arg( rtype, code, a,b,c,d,e )
Xchar rtype;			/* reply type */
Xchar code;			/* reply code */
Xchar *a,*b,*c,*d,*e;		/* args */
X{
X	struct command reply_buf;
X	char repargs[MAX_ARGLEN];
X	char *vector[6];
X	int i, pos;		/* loop counter and position in reparts */
X
X	reply_buf.comtype = rtype;
X	reply_buf.comcode = code;
X	reply_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	vector[5] = NULL;
X
X	pos = 0;
X	for( i = 0; i < 5 && vector[i]; i++ ) {
X		strcpy( repargs+pos, vector[i] );
X		/* check overflow? */
X		pos += strlen(vector[i]) + 1;
X		}
X	sprintf( reply_buf.arg_size, "%03d", pos );
X	reply_buf.zerob =0;
X	/* copy back in the sequence number */
X	sprintf( reply_buf.seq_num, "%5.5s", last_sequence );
X	reply_buf.space2 = ' ';
X
X	if( pipelog ) {
X		int ic;
X		fprintf( pipelog, "F:Send %s\n", &reply_buf.comtype );
X		for( ic = 0; ic < 5 && vector[ic]; ic++ )
X			fprintf( pipelog, ":%s:", vector[ic] );
X		fprintf( pipelog, "\n" );
X		}
X
X
X	if( write(F_ANSPIPE, &reply_buf, sizeof(reply_buf))==sizeof(reply_buf)){
X		if( pos > 0 && write( F_ANSPIPE, repargs, pos ) != pos )
X			pipe_abort();
X		}
X	 else
X		pipe_abort();
X}
X
X/* A write failed to the pipe, we have to terminate */
X
Xpipe_abort()
X{
X	if( pipelog )
X		fprintf( pipelog, "Pipe Abort\n" );
X	warning( 2, "Pipe mode abort\n" );
X	if( cur_newsgroup[0] )
X		finish_group();
X	Uterminate();
X	wrapup();
X	exit(1);
X}
X
X/* check group name and if the group changed, call user routines if it did */
X/* The main group must stay around until it changes.  To do this, we
X   effectively reserve slot -1 for it using the ex_group_base variable. */
X
Xtry_newgroup( gname )
Xchar *gname;
X{
X	extern newsgroup main_newsgroup;
X	extern int extra_groups, ex_group_base;	/* count of un-named groups */
X	newsgroup new_newsgroup;
X
X	if( strcmp( gname, cur_newsgroup ) != 0 ) {
X		if( cur_newsgroup[0] ) {
X			finish_group();
X			}
X		strcpy( cur_newsgroup, gname );
X		/* allocate an extra group that stays until the next change */
X		ex_group_base = extra_groups = 0;
X		main_newsgroup = ng_number( cur_newsgroup );
X		ex_group_base = extra_groups;
X		Ustartgroup( 1 );
X		}
X}
X
Xreply_ok()
X{
X	reply_arg( 'O', NULL );
X}
X
Xreply_err( ermsg )
Xchar *ermsg;
X{
X	reply_arg( 'E', ermsg, NULL );
X}
X
Xhandle_command(com)
Xchar *com;
X{
X	extern int score;
X	score = 0;
X	Ucommand(com);
X	if( score > 0 )
X		reply_ok();
X	 else
X		reply_err( "Invalid Kill Command" );
X}
X
X/*
X * Send a query, and await a response to that query
X * Returns 0 for OK, -1 for error
X */
X
Xquery( wquery, qarg )
Xchar wquery;		/* what sort of query */
Xchar *qarg;		/* argument, if any */
X{
X	char *argv[MAX_ARGS];		/* argument pointer vector */
X	int argc;
X	g_reply_arg( 'Q', wquery, qarg, NULL );
X	/* Read response, do not set sequence number */
X	argc = read_command( argv, MAX_ARGS, FALSE );
X	/* we don't care about the argument */
X	if( argv[0][1] != wquery || argc > ABASE+2 )
X		return ERRCODE;
X	 else
X		return 0;
X}
X
X/* ask that the header be written to the file */
X/* This routine is not to be called in FILE_FULL mode */
X
Xint
Xask_for_header()
X{
X	return query( 'H', NULL );
X}
X
X/* ask that the body be written to the article file */
X/* Not much we can do with error status at this point, as we are too
X   deep in code, but we will return it regardless */
X/* This routine is not to be called in FILE_FULL mode */
X
Xint
Xask_for_body()
X{
X	return query( 'B', NULL );
X}
END_OF_FILE
if test 12339 -ne `wc -c <'pipe.c'`; then
    echo shar: \"'pipe.c'\" unpacked with wrong size!
fi
# end of 'pipe.c'
fi
echo shar: End of archive 7 \(of 15\).
cp /dev/null ark7isdone
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