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