/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* perf720 src/perf/perfagent/usr/samples/perfagent/server/lfiltd.c 1.8   */
/*                                                                        */
/*                                                                        */
/*                                                                        */
/* OBJECT CODE ONLY SOURCE MATERIALS                                      */
/*                                                                        */
/* COPYRIGHT International Business Machines Corp. 1992,1993              */
/* All Rights Reserved                                                    */
/*                                                                        */
/* The source code for this program is not published or otherwise         */
/* divested of its trade secrets, irrespective of what has been           */
/* deposited with the U.S. Copyright Office.                              */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
static char *Sccs_id = "@(#)23  1.8  src/perf/perfagent/usr/samples/perfagent/server/lfiltd.c, perfagent, perf720 12/3/96 06:50:21";

/*
 * COMPONENT_NAME:   (PERFAGENT) - Performance Agent
 *
 * FUNCTIONS: op_mult(), op_div(), op_rem(), op_add(), op_sub(),
 *            op_top(), op_bot(), op_cnt(), log_check(), doexit(), must_exit(),
 *            free_Res(), free_all(), doerror(), input(), unput(), yywrap(),
 *            add_entry(), strccvt(), add_const(), add_var(), put_oper(),
 *            add_oper(), step_up(), step_down(), shine_up(), add_wild(),
 *            dump_it(), verify_name(), check_names(), add_to_set(), do_calc(),
 *            feeding(), main()
 *
 * ORIGINS: 30
 *
 * (C) COPYRIGHT International Business Machines Corp. 1992, 1993
 * All Rights Reserved
 * Licensed Materials - Property of IBM
 *
 * US Government Users Restricted Rights - Use, duplication or
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 *
 *
       NOTICE TO USERS OF THE SOURCE CODE EXAMPLES

 THE SOURCE CODE EXAMPLES PROVIDED BY IBM ARE ONLY INTENDED TO ASSIST IN THE
 DEVELOPMENT OF A WORKING SOFTWARE PROGRAM.  THE SOURCE CODE EXAMPLES DO NOT
 FUNCTION AS WRITTEN:  ADDITIONAL CODE IS REQUIRED.  IN ADDITION, THE SOURCE
 CODE EXAMPLES MAY NOT COMPILE AND/OR BIND SUCCESSFULLY AS WRITTEN.

 INTERNATIONAL BUSINESS MACHINES CORPORATION PROVIDES THE SOURCE CODE
 EXAMPLES, BOTH INDIVIDUALLY AND AS ONE OR MORE GROUPS, "AS IS" WITHOUT
 WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
 LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
 OF THE SOURCE CODE EXAMPLES, BOTH INDIVIDUALLY AND AS ONE OR MORE GROUPS,
 IS WITH YOU.  SHOULD ANY PART OF THE SOURCE CODE EXAMPLES PROVE
 DEFECTIVE, YOU (AND NOT IBM OR AN AUTHORIZED RISC System/6000* WORKSTATION
 DEALER) ASSUME THE ENTIRE COST OF ALL NECESSARY SERVICING, REPAIR OR
 CORRECTION.

 IBM does not warrant that the contents of the source code examples, whether
 individually or as one or more groups, will meet your requirements or that
 the source code examples are error-free.

 IBM may make improvements and/or changes in the source code examples at
 any time.

 Changes may be made periodically to the information in the source code
 examples; these changes may be reported, for the sample device drivers
 included herein, in new editions of the examples.

 References in the source code examples to IBM products, programs, or
 services do not imply that IBM intends to make these available in all
 countries in which IBM operates.  Any reference to an IBM licensed
 program in the source code examples is not intended to state or imply
 that only IBM's licensed program may be used.  Any functionally equivalent
 program may be used.

 * RISC System/6000 is a trademark of International Business Machines
   Corporation.
*/

/*
	This sample program demonstrates how a program using the Spmi API
	can, at the same time, read data values from the Spmi and add its
	own data values to the default set of statistics defined by Spmi.

	The program used lex to define the semantics of a configuration
	file, which by default is in /usr/samples/perfagent/server/lfilter.cf.

	The configuration file defines new statistics from existing Spmi
	statistics, using the following general format:


   target = expression description

   target:
      Unqualified name of non-existing variable.  Must start with alpha and
      contain only alpha-numeric characters and percent sign.

   expression:
      {variable|wildcard|const} operator {variable|wildcard|const} ...

   variable:
      Fully qualified xmperf variable name with slashes replaced by under-
      scores; valid names have at least one underscore.  The first name compo-
      nent must start with an alpha character, subsequent ones may also begin
      with a percent sign.  All must contain only alpha-numeric characters and
      percent sign.  The referenced variable must already exist (can NOT be
      defined in this configuration file).

   wildcard:
      Fully qualified xmperf variable name with slashes replaced by under-
      scores; valid names have at least one underscore.  The first name compo-
      nent must start with an alpha character, subsequent ones may also begin
      with a percent sign.  All must contain only alpha-numeric characters and
      percent sign or must be a wildcard.  The wildcard character must appear
      in place of a context name, must only appear once, and must be one of
      the characters '+', '*', '#', '>', '<'.

   operator:
      One of {*, /, %, +, -}

   const:
      [[digits].digits]

   description:
      Text describing the defined target variable.  Must be enclosed in
      double quotes.

	The use of "wildcards is a way of referring to multiple instances
   of a given &stat. with one name but, more important, it makes your
   expression independent of the actual configuration of the system it is
   used on.  For example, the expression:

        allreads = Disk_+_rblk

	could evaluate to different expressions on different machines, such as:

        allreads = ((Disk/cd0/rblk + Disk/hdisk1/rblk) + Disk/hdisk0/rblk)
        allreads = Disk/hdisk0/rblk

	Examples of defining new statistics:

		running = CPU_cpu0_kern + CPU_cpu0_user "CPU running"
      notbusy = CPU_cpu0_wait + CPU_cpu0_idle "CPU not running"
      packsize = NetIf_tr0_ooctet / NetIf_tr0_opacket "Average packet size"
      localpct = (NetIf_lo0_ipacket + NetIf_lo0_opacket) * 100 \
                 / (NetIf_+_ipacket + NetIf_+_opacket) \
                 "Percent of network packets on loopback interface"

	These statistics would show up as:
		DDS/IBM/Custom/running
		DDS/IBM/Custom/notbusy
		DDS/IBM/Custom/packsize
		DDS/IBM/Custom/localpct

*/

#include <stdio.h>
#include <fcntl.h>
#include <malloc.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/utsname.h>
#ifdef _AIX
#include <sys/events.h>
#endif
#include <sys/Spmidef.h>
#include "lfiltd.h"
#if sun
#include <errno.h>
#define  SEEK_SET 0
#define  SEEK_CUR 1
#define  SEEK_END 2
#endif

#ifdef _SOLARIS
int yylex(void);
#endif /* _SOLARIS */
extern char				yytext[];
extern char          SpmiErrmsg[];
extern int           SpmiErrno;

int  						yychar;
long						pos, cur = 0;
char						*dat;
parse_state				state;
var_state				vstate;
int						brc1 = 0, brc2 = 0;
int						curcount = 0;
int						totcount = 0;
int						arraycount;
int						trace = 0;
int						loggings = 0;
FILE						*lf;
struct itimerval		t_value;    /* time to set                      */
struct itimerval		o_value;    /* time to expiration               */
char						log1[128] = lfilter_log;
char						log2[128] = lfilter_log;
char						*log;
struct ResStr			*tab[20] = {NULL};		/* level table used to build the */
															/* multi-level network of nodes	*/
															/* for a value.						*/
struct LVarStr			*vptr = NULL;				/* anchor of base nodes, each of	*/
															/* which defines a new value		*/
struct LVarStr			*lvptr = NULL;
boolean					name_error = FALSE;
struct SpmiStatSet	*curssp = NULL;
int						interval = LINTERVAL;

struct dat
{
	float		val[1];
};

static SpmiShare  	*dataarea = NULL;        /* Shared memory pointer      */
static struct dat		*d = NULL;               /* Pointer to stats data area */
struct SpmiRawStat	*FiltStats;

static cx_create FiltContext[] = {
	{"DDS/IBM", "IBM-defined Dynamic Data Suppliers", 2, 0,
      NULL, 0, NULL, 0, NULL, SiNoInst},
   {"DDS/IBM/Custom", "Custom values defined by \"lfiltd\"", 180, 0,
      NULL, 0, NULL, 0, NULL, SiNoInst},
};

/*
	Define function called strmake() to be used when porting to systems
	that do not know the strdup() function.
*/
#ifdef _AIX
#define strmake strdup
#else
#ifdef _NO_PROTO
char *strmake(str)
char *str;
#else
char *strmake(char *str)
#endif
{
   char     *p;

   p = (char *)malloc(strlen(str)+1);
   strcpy(p, str);
   return (p);
}
#endif

/*
	Define functions to perform basic arithmetic on float values.  These
	functions are invoked as required from the structures that are used
	to define the new statistics values from arithmetic expressions.
	All functions are defined for function prototyping and old-style
	function declarations.
*/
#ifdef _NO_PROTO
float	op_mult(a, b)
float a;
float b;
{
	return(a * b);
}

float	op_div(a, b)
float a;
float b;
{
	if (b)
		return(a / b);
	return(0.0);
}

float	op_rem(a, b)
float a;
float b;
{
	long	aa = a, bb = b;

	if (bb)
		return(aa % bb);
	return(0.0);
}

float	op_add(a, b)
float a;
float b;
{
	return(a + b);
}

float	op_sub(a, b)
float a;
float b;
{
	return(a - b);
}

float	op_top(a, b)
float a;
float b;
{
	if (a > b)
		return(a);
	return(b);
}

float	op_bot(a, b)
float a;
float b;
{
	if (a < b)
		return(a);
	return(b);
}

float	op_cnt(a, b)
float a;
float b;
{
	return(++a);
}
#else
float	op_mult(float a, float b)
{
	return(a * b);
}

float	op_div(float a, float b)
{
	if (b)
		return(a / b);
	return(0.0);
}

float	op_rem(float a, float b)
{
	long	aa = a, bb = b;

	if (bb)
		return(aa % bb);
	return(0.0);
}

float	op_add(float a, float b)
{
	return(a + b);
}

float	op_sub(float a, float b)
{
	return(a - b);
}

float	op_top(float a, float b)
{
	if (a > b)
		return(a);
	return(b);
}

float	op_bot(float a, float b)
{
	if (a < b)
		return(a);
	return(b);
}

float	op_cnt(float a, float b)
{
	return(++a);
}
#endif

/*
	This function is used to flush the log file.  The log file is always
	used and will by default flip-flop between /tmp/lfilter.log1 and
	/tmp/lfilter.log2.
*/
void log_check()
{
   long  pos;

   pos = ftell(lf);
   fclose(lf);
   if (pos >= MAX_FFILE_SIZE)
   {
      if (!(strcmp(log, log1)))
         log = log2;
      else
         log = log1;
      lf = fopen(log, "w");
   }
   else
      lf = fopen(log, "a");
}

/*
	This function is called to exit the program.  It does the following:

	1. Calls SpmiExit to release all references to shared memory and to
	   undefine its statistics.

	2. Writes the date and time of the last shared memory update to the log.

	3. Closes the log file and exits.
*/
#ifdef _NO_PROTO
void doexit(rc)
int	rc;
#else
void doexit(int rc)
#endif
{
	char				t1[40], *t;
	struct timeval	tid;
	
	gettimeofday(&tid, NULL);
	t = (char *)ctime((time_t *)&tid.tv_sec);
	strcpy(t1, t);
	if (dataarea)
	{
   	t = (char *)ctime((time_t *)&tid.tv_sec);
		fprintf(lf, "Last shared memory update:  %s\n", t);
	}
   SpmiExit();
	fprintf(lf, "Exiting at:  %s\n", t1);
	fprintf(stderr, "lfiltd: Exiting at:  %s\n", t1);
	fclose(lf);
	exit(rc);
}

/*
	Function that is invoked in case the program receives a signal.
	It writes a message to the log and then calls doexit() to exit.
*/
#ifdef _NO_PROTO
void must_exit(sig)
int sig;
#else
void must_exit(int sig)
#endif
{
	fprintf(lf, "Signal %d received\n", sig);
	fprintf(stderr, "Signal %d received\n", sig);
   doexit(-9);
}

/*
	Function to delete a node in the network that defines a new value
*/
#ifdef _NO_PROTO
void free_Res(rp)
struct ResStr *rp;
#else
void free_Res(struct ResStr *rp)
#endif
{
	if (rp->var1)
		free(rp->var1);
	if (rp->var2)
		free(rp->var2);
	free(rp);
}

/*
	Function to free all nodes from and including a the node given
	by the argument.
*/
#ifdef _NO_PROTO
void free_all(rp)
struct ResStr *rp;
#else
void free_all(struct ResStr *rp)
#endif
{
	if (rp->type1 == temp)
		free_all((struct ResStr *)rp->source1);
	if (rp->type2 == temp)
		free_all((struct ResStr *)rp->source2);
	free_Res(rp);
}

/*
	Function that is called when encountering errors during parsing of
	the configuration file.

	It outputs a message to the log and then cleanc out the node(s) as
	required and resets the state switches of the parser.
*/
#ifdef _NO_PROTO
void doerror(text)
char *text;
#else
void doerror(char *text)
#endif
{
	struct LVarStr			*tvptr;

	if (dat[cur-1] != '\n')
	{
		fprintf(lf, "%s \"%s\"\n", text, yytext);
		while((dat[cur]) && (cur < pos))
		{
			if ((dat[cur] == '\\') && (dat[cur+1] == '\n'))
				cur += 2;
			else
				cur++;
			if (dat[cur] == '\n')
			{
				cur++;
				break;
			}
		}
	}
	else
		fprintf(lf, "%s\n", text);
	if  (lvptr)
	{
		tvptr = lvptr;
		lvptr = tvptr->prev;
		if (lvptr)
			lvptr->next = NULL;
		else
			vptr = NULL;
		if (tvptr->newvar)
			free(tvptr->newvar);
		free_all(tvptr->rsp);
		free(tvptr);
	}
	state = nostate;
	vstate = novar;
	brc1 = brc2 = 0;
}

/*
	Function required by lex; used to pass one character at a time to
	the code generated by lex.
*/
int input()
{
	if (cur >= pos)
		return(0);
	return(dat[cur++]);
}

/*
	Function required by lex; used to return one character at a time from
	the code generated by lex.
*/
#ifdef _NO_PROTO
int unput(c)
int	c;
#else
int unput(int c)
#endif
{
	if (cur)
		dat[--cur] = c;
	return(0);
}

/*
	Null function required by lex.
*/
int yywrap()
{
	return(1);
}

/*
	Function to add the base node of a new variable to the list of defined
	variables anchored in vptr.
*/
int add_entry()
{
	struct LVarStr			*tvptr;

	if (!(tvptr = (struct LVarStr *)calloc(1, sizeof(struct LVarStr))))
	{
		fprintf(lf, "Out of memory -- exiting\n");
		return(-1);
	}
	if (!vptr)
		lvptr = vptr = tvptr;
	else
	{
		lvptr->next = tvptr;
		tvptr->prev = lvptr;
		lvptr = tvptr;
	}
	if (!(lvptr->rsp = (struct ResStr *)calloc(1, sizeof(struct ResStr))))
	{
		fprintf(lf, "Out of memory -- exiting\n");
		return(-1);
	}
	tab[0] = lvptr->rsp;
	return(0);
}

/*
	Function to convert underscore characters in variable names from the
	configuration file to slashes.  The underscore character is used in
	place of slashes when naming variables to avoid clashes with the sign
	for division, "/".
*/
#ifdef _NO_PROTO
char *strccvt(s)
u_char	*s;
#else
char *strccvt(char *s)
#endif
{
	char		localc[128], *t = localc;

	while(*t = *s++)
	{
		if (*t == '_')
			*t++ = '/';
		else
			t++;
	}
	t = (char *)strmake(localc);
	if (!t)
		fprintf(lf, "Out of memory -- exiting\n");
	return(t);
}

/*
	Function to add a constant value as either the first or second operand
	in a given node.  The node is identified by an index into the "tab"
	table where a node is identified.  The index is in the global variable
	bcr2.
*/
#ifdef _NO_PROTO
int add_const(value)
float	value;
#else
int add_const(float value)
#endif
{
	float		localv = value;

	if (tab[brc2]->type1)
	{
		/*
			First operand has been added, now see how to handle second
		*/
		if (tab[brc2]->type2)
		{
			/*
				Second is already added, so this is an error
			*/
			fprintf(lf, "Invalid call to add_const\n");
			return(-1);
		}
		else
		{
			/*
				Second operand wasn't here so go add it
			*/
			memcpy((char *)&tab[brc2]->source2, &localv, sizeof(localv));
			tab[brc2]->type2 = constant;
		}
	}
	else
	{
		/*
			We have to add the first operand here
		*/
		memcpy((char *)&tab[brc2]->source1, &localv, sizeof(localv));
		tab[brc2]->type1 = constant;
	}
	return(0);
}

/*
	Function to add a statistic name as either the first or second operand
	in a given node.  The node is identified by an index into the "tab"
	table where a node is identified.  The index is in the global variable
	bcr2.
*/
#ifdef _NO_PROTO
int add_var(name)
char	*name;
#else
int add_var(char *name)
#endif
{
	if (tab[brc2]->type1)
	{
		/*
			First operand has been added, now see how to handle second
		*/
		if (!tab[brc2]->type2)
		{
			/*
				Second operand wasn't here so go add it
			*/
			if (!(tab[brc2]->var2 = (char *)strmake(name)))
				return(-1);
			tab[brc2]->type2 = variable;
		}
		else
		{
			/*
				Second is already added, so this is an error
			*/
			fprintf(lf, "Invalid call to add_var\n");
			return(-1);
		}
	}
	else
	{
		/*
			We have to add the first operand here
		*/
		if (!(tab[brc2]->var1 = (char *)strmake(name)))
			return(-1);
		tab[brc2]->type1 = variable;
	}
	return(0);
}

/*
	Function to insert the operator in a node.
*/
#ifdef _NO_PROTO
void put_oper(rp, opr)
struct ResStr *rp;
int	opr;
#else
void put_oper(struct ResStr *rp, int opr)
#endif
{
	switch(opr)
	{
		case 'T':	rp->op = op_top;
						rp->prec = 2;
						break;
		case 'B':	rp->op = op_bot;
						rp->prec = 2;
						break;
		case '#':	rp->op = op_cnt;
						rp->prec = 2;
						break;
		case '*':	rp->op = op_mult;
						rp->prec = 4;
						break;
		case '/':	rp->op = op_div;
						rp->prec = 4;
						break;
		case '%':	rp->op = op_rem;
						rp->prec = 4;
						break;
		case '+':	rp->op = op_add;
						rp->prec = 6;
						break;
		case '-':	rp->op = op_sub;
						rp->prec = 6;
						break;
	}
}

/*
	Function to add an operator to a given node. The node is identified by
	an index into the "tab" table where a node is identified.  The index is
	in the global variable bcr2.

	If two operands are already present, a new level is created and the
	precedence level of the new and previous operator is used to place
	the nodes in the network so that the expression is evaluated in order
	of operator precedence.
*/
#ifdef _NO_PROTO
int add_oper(opr)
int	opr;
#else
int add_oper(int opr)
#endif
{
	if (tab[brc2]->type2)
	{
		/*
			Oops! two operands are already present so we must split
			the table entry and go to the next level
		*/
		struct ResStr *rp1 = tab[brc2], *rp2;;

		brc2++;
		if (!(tab[brc2] = (struct ResStr *)calloc(1, sizeof(struct ResStr))))
		{
			fprintf(lf, "Out of memory -- exiting\n");
			doexit(9);
		}
		rp2 = tab[brc2];
		put_oper(rp2, opr);
		if ((long)rp2->prec < (long)rp1->prec)
		{
			rp2->type1   = rp1->type2;
			rp2->var1    = rp1->var2;
			rp2->source1 = rp1->source2;
			rp1->var2    = NULL;
			rp1->type2   = temp;
			rp1->source2 = rp2;
		}
		else
		{
			*rp2 = *rp1;	/* structure copy	*/
			put_oper(rp1, opr);
			rp1->var1    = NULL;
			rp1->type1   = temp;
			rp1->source1 = rp2;
			rp1->var2    = NULL;
			rp1->type2   = notype;
			rp1->source2 = NULL;
			brc2--;
		}
	}
	else
	{
		put_oper(tab[brc2], opr);
	}
	return(0);
}

/*
	Function used to increase the node level by one.  The function is called
	when we see a left parentheses or when evaluating wildcards (which implies
	parentheses around the evaluated wildcard).
*/
void step_up()
{
	if (!(tab[brc2 + 1] =
		(struct ResStr *)calloc(1, sizeof(struct ResStr))))
	{
		fprintf(lf, "Out of memory -- exiting\n");
		doexit(9);
	}
	if (!tab[brc2]->type1)
	{
		tab[brc2]->var1    = NULL;
		tab[brc2]->type1   = constant;
		tab[brc2]->source1 = NULL;
		tab[brc2]->op      = op_add;
	}
	brc1++;
	brc2++;
}

/*
	Function used to decrease the node level by one.  The function is called
	when we see a right parentheses or when we finished the evaluating of
	wildcards (which implies parentheses around the evaluated wildcard).
*/
#ifdef _NO_PROTO
void step_down(k)
int	k;
#else
void step_down(int k)
#endif
{
	int	i;
	struct ResStr *rp1, *rp2;;

	while(brc2 > brc1)
	{
		rp2 = tab[brc2];
		brc2--;
		rp1 = tab[brc2];
		rp1->var2    = NULL;
		rp1->type2   = temp;
		rp1->source2 = rp2;
	}
	while(brc1 > k)
	{
		rp2 = tab[brc2];
		brc2--;
		rp1 = tab[brc2];
		rp1->var2    = NULL;
		rp1->type2   = temp;
		rp1->source2 = rp2;
		brc1--;
	} 
}

/*
	This function is called to finish processing of a network created
	to define a new variable.  It will get rid of any half-finished
	nodes by pushing their contents down into lower levels, thereby
	reducing the number of levels used to define the value.
*/
#ifdef _NO_PROTO
int shine_up(prp)
struct ResStr **prp;
#else
int shine_up(struct ResStr **prp)
#endif
{
	struct ResStr	*rp = *prp;
	float				tmp1;

	memcpy((char *)&tmp1, (char *)&rp->source1, sizeof(tmp1));
	if ((rp->type1 == constant) && (rp->op == op_add) && (tmp1 == 0.0))
	{
		if (!rp->type2)
		{
			doerror("No expression found");
			return(-1);
		}
		if (rp->type2 == temp)
		{
			*prp = (struct ResStr *)rp->source2;
			free_Res(rp);
			rp = *prp;
			shine_up(prp);
			return(0);
		}
	}
	if (rp->type1 == temp)
		shine_up((struct ResStr **)&rp->source1);
	if (rp->type2 == temp)
		shine_up((struct ResStr **)&rp->source2);
	return(0);
}

/*
	This function adds one or more instances of a wildcard to the network
	defining a new value.  This is done by traversing the Spmi data hierarchy
	and making a linked list of all instances.  The functions add_var() and
	add_oper() are then called to create the nodes required to evaluate
	the wildcard expression.
*/
#ifdef _NO_PROTO
int add_wild(opr)
int	opr;
#else
int add_wild(int opr)
#endif
{
	struct wild
	{
		char					path[128];
		struct wild			*next;
	};
	struct wild				*wa = NULL, *w;
	char						path[128];
	char						*t, *s;
   SpmiCxHdl     			cxh;
	struct SpmiCx			*cxp;
   struct SpmiCxLink     *cxlink, *ocxlink;
	int						cc = 0;
	int						rc = 0;
	float						k = 0;
		
	if (!(s = strccvt(yytext)))
		return(-1);
	if (!(t = (char *)strchr(s, opr)))
	{
		if (opr == 'T')
			t = (char *)strchr(s, '>');
		else if (opr == 'B')
			t = (char *)strchr(s, '<');
	}
	*(t-1) = '\0';
	t++;

	strcpy(path, s);
   cxh = SpmiPathGetCx((char *)path, NULL);
   if (!cxh)
   {
   	if (!(cxh = SpmiPathGetCx((char *)path, NULL)))
		{
      	if (strlen(SpmiErrmsg))
         	fprintf(lf, "%s\n", SpmiErrmsg);
      	fprintf(lf, "SpmiPathGetCx can\'t find context for \"%s\"\n", path);
			return(-1);
		}
   }   
   if (!(cxlink = SpmiFirstCx(cxh)))
   	cxlink = SpmiFirstCx(cxh);
   while (cxlink)
   {
		if (!(w = (struct wild *)calloc(1, sizeof(struct wild))))
		{
			fprintf(lf, "Out of memory -- exiting\n");
			return(-1);
		}
		if (!(cxp = SpmiGetCx(cxlink->context)))
			break;
		if (opr == '#')
			k++;
		else
		{
			w->next = wa;
			wa = w;
			strcpy(w->path, s);
			strcat(w->path, "/");
			strcat(w->path, cxp->name);
			strcat(w->path, t);
			cc++;
		}
		ocxlink = cxlink;
      if (!(cxlink = SpmiNextCx(cxlink)))
      	cxlink = SpmiNextCx(ocxlink);
   }
	if (opr == '#')
	{
		if (add_const(k))
			rc = -1;
		free(s);
		return(rc);
	}

	/*
		we now have a table of all instances with full path names;
		we go through the table and add variables and operators as
		required by using the functions add_var() and add_oper()
	*/
	w = wa;
	while(w)
	{
		cc--;
		if (add_var((char *)w->path))
			rc = -1;
		else
		{
			if (cc)
				if (add_oper(opr))
					rc = -1;
		}
		wa = w;
		w = w->next;
		free(wa);
	}
	free(s);
	return(rc);
}

/*
	Function used to dump the network of nodes defining a new variable.
	It is invoked for each defined new value if the command line argument
	"-p" is set to 5 or above.  The output goes to the log file and can
	be used to validate the evaluation of expressions because it inserts
	parentheses to illustrate the exact evaluation sequence.
*/
#ifdef _NO_PROTO
void dump_it(rp, level)
struct ResStr *rp;
int 	level;
#else
void dump_it(struct ResStr *rp, int level)
#endif
{
	float					tmp;

	if ((rp) && (!lvptr->dumped))
	{
		if (!level)
		{
			if (lvptr->newvar)
			{
				if (lvptr->descr)
					fprintf(lf, "\n%s \"%s\" = ", lvptr->newvar, lvptr->descr);
				else
					fprintf(lf, "\n%s = ", lvptr->newvar);
			}
			else
				fprintf(lf, "%08x: = ", (long)rp);
		}
		if (rp->type1 == temp)
		{
			fprintf(lf, "(");
			dump_it((struct ResStr *)rp->source1, level + 1);
			fprintf(lf, ")");
		}
		else if (rp->type1 == variable)
			fprintf(lf, "%s", rp->var1);
		else if (rp->type1 == constant)
		{
			memcpy((char *)&tmp, (char *)&rp->source1, sizeof(tmp));
			fprintf(lf, "%f", tmp);
		}
		else
			fprintf(lf, "******** ");
		if (rp->op == op_mult)
			fprintf(lf, " * ");
		else if (rp->op == op_div)
			fprintf(lf, " / ");
		else if (rp->op == op_rem)
			fprintf(lf, " % ");
		else if (rp->op == op_add)
			fprintf(lf, " + ");
		else if (rp->op == op_sub)
			fprintf(lf, " - ");
		else if (rp->op == op_top)
			fprintf(lf, " MAX ");
		else if (rp->op == op_bot)
			fprintf(lf, " MIN ");
		else if (rp->op == op_cnt)
			fprintf(lf, " # ");
		else
		{
			if (!level)
			{
				fprintf(lf, "\n");
				return;
			}
			fprintf(lf, " ??? ");
		}
		if (rp->type2 == temp)
		{
			fprintf(lf, "(");
			dump_it((struct ResStr *)rp->source2, level + 1);
			fprintf(lf, ")");
		}
		else if (rp->type2 == variable)
			fprintf(lf, "%s", rp->var2);
		else if (rp->type2 == constant)
		{
			memcpy((char *)&tmp, (char *)&rp->source2, sizeof(tmp));
			fprintf(lf, "%f", tmp);
		}
		else
			fprintf(lf, "******** ");
		if (!level)
			fprintf(lf, "\n\n");
		if (!level)
			lvptr->dumped = TRUE;
	}
}

/*
	Function used to verify whether a name used in an expression exists
	in the Spmi data hierarchy.  The result of the test is placed in the
	global variable "name_error".  It is called only from check_names().
*/
#ifdef _NO_PROTO
void verify_name(s)
char	*s;
#else
void verify_name(char *s)
#endif
{
	int						i;
	char						tmp[256], *t, er[256], *navn;
   SpmiCxHdl 	         cxh;
	struct SpmiStat		*stp;
   struct SpmiStatLink  *statlink, *ostatlink;


	strcpy(tmp, s);
	i = strlen(tmp) - 1;
	while(tmp[i] != '/')
		i--;
	tmp[i] = '\0';
   cxh = SpmiPathGetCx(tmp, NULL);
   if (!cxh)
   {
   	if (!(cxh = SpmiPathGetCx(tmp, NULL)))
		{
			tmp[i] = '/';
      	if (strlen(SpmiErrmsg))
         	fprintf(lf, "%s\n", SpmiErrmsg);
      	fprintf(lf, "SpmiPathGetCx cannot find context for \"%s\"\n", tmp);
			name_error = TRUE;
			return;
		}
   }   
	tmp[i] = '/';
	if (((struct SpmiCx *)cxh)->inst_freq >= SiContInst)
   {
      SpmiInstantiate(cxh);
   }
   if (!(statlink = SpmiFirstStat(cxh)))
   	statlink = SpmiFirstStat(cxh);
   while (statlink)
   {
		if (!(stp = SpmiGetStat(statlink->stat)))
			break;
		navn = stp->name;
      if (!strcmp(navn, tmp+i+1))
			return;
		ostatlink = statlink;
      if (!(statlink = SpmiNextStat(statlink)))
      	statlink = SpmiNextStat(ostatlink);
   }
	fprintf(lf, "Value name \"%s\" not found\n", tmp);
	name_error = TRUE;
}

/*
	Function used to check all variables in an expression.  It uses the
	verify_name() function to look-up the names with Spmi and proceeds
	through the network of nodes that define a new variable by recursively
	invoking itself.
*/
#ifdef _NO_PROTO
int check_names(rp, i)
struct ResStr *rp;
int	i;
#else
int check_names(struct ResStr *rp, int i)
#endif
{
	int	n = i;

	if (rp->type1 == variable)
	{
		verify_name(rp->var1);
		n++;
	}
	if (rp->type2 == variable)
	{
		verify_name(rp->var2);
		n++;
	}
	if (rp->type1 == temp)
		n = check_names((struct ResStr *)rp->source1, n);
	if (rp->type2 == temp)
		n = check_names((struct ResStr *)rp->source2, n);
	return(n);
}

/*
	This function invokes itself recursively to traverse the network defining
	a new value and add all the Spmi statistics to a StatSet so that the data
	values required to evaluate the defined new value will be extracted from
	the Spmi each time an SpmiGetStatSet() call is issued.
*/
#ifdef _NO_PROTO
void add_to_set(rp)
struct ResStr *rp;
#else
void add_to_set(struct ResStr *rp)
#endif
{
	char					tmp[256];

	if (rp->type1 == variable)
	{
		strcpy(tmp, rp->var1);
		if (!(rp->source1 = SpmiPathAddSetStat(curssp, tmp, NULL)))
		{
			if (!(rp->source1 = SpmiPathAddSetStat(curssp, tmp, NULL)))
			{
				name_error = TRUE;
				return;
			}
		}
		curcount++;
		/*
		free(rp->var1);
		rp->var1 = NULL;
		*/
	}
	if (rp->type2 == variable)
	{
		strcpy(tmp, rp->var2);
		if (!(rp->source2 = SpmiPathAddSetStat(curssp, tmp, NULL)))
		{
			if (!(rp->source2 = SpmiPathAddSetStat(curssp, tmp, NULL)))
			{
				name_error = TRUE;
				return;
			}
		}
		curcount++;
		/*
		free(rp->var2);
		rp->var2 = NULL;
		*/
	}
	if (rp->type1 == temp)
		add_to_set((struct ResStr *)rp->source1);
	if (rp->type2 == temp)
		add_to_set((struct ResStr *)rp->source2);
}

/*
	Whenever a node in the network must be evaluated, this function is
	called to do the evaluation.  Whenever an operand is of type "temp",
	the function invokes itself recursively, thereby traversion the
	entire network of a new variable.

	Evaluation takes place in a sequence that ensures that operator
	precedence is respected.
*/
#ifdef _NO_PROTO
float do_calc(rp)
struct ResStr *rp;
#else
float do_calc(struct ResStr *rp)
#endif
{
	float		res, res1, res2;
	boolean	pr = FALSE;

	switch(rp->type1)
	{
		case variable:
				res1 = SpmiGetValue(curssp, (struct SpmiStatVals *)rp->source1);
				if (res1 < -1.1)
				{
					if (++rp->loggings < 3)
					{
						pr = TRUE;
						fprintf(lf, "Negative 1st value:");
						loggings++;
						if (rp->var1)
							fprintf(lf, "   %s=%f, ", rp->var1, res1);
						else
							fprintf(lf, "   ...=%f, ", res1);
					}
					res1 = 0.0;
				}
				break;
		case constant:
				memcpy((char *)&res1, (char *)&rp->source1, sizeof(res1));
				break;
		case temp:
				res1 = do_calc((struct ResStr *)rp->source1);
				break;
		default:
				if (++rp->loggings < 3)
				{
					fprintf(lf, "Missing type....\n");
					loggings++;
				}
				return(0.0);
	}
	if (!rp->op)
		return(res1);
	switch(rp->type2)
	{
		case variable:
				res2 = SpmiGetValue(curssp, (struct SpmiStatVals *)rp->source2);
				if (res2 < -1.1)
				{
					if (++rp->loggings < 3)
					{
						pr = TRUE;
						fprintf(lf, "Negative 2nd value:");
						loggings++;
						if (rp->var2)
							fprintf(lf, "%s=%f, ", rp->var2, res2);
						else
							fprintf(lf, "...=%f, ", res2);
					}
					res2 = 0.0;
				}
				break;
		case constant:
				memcpy((char *)&res2, (char *)&rp->source2, sizeof(res1));
				break;
		case temp:
				res2 = do_calc((struct ResStr *)rp->source2);
				break;
		default:
				if (++rp->loggings < 3)
				{
					fprintf(lf, "Missing type....\n");
					loggings++;
				}
				return(0.0);
	}
	if (trace == 9)
	{
		res = rp->op(res1, res2);
		if (rp->var1)
			fprintf(lf, "   %s=%f, ", rp->var1, res1);
		else
			fprintf(lf, "   ...=%f, ", res1);
		if (rp->var2)
			fprintf(lf, "%s=%f, ", rp->var2, res2);
		else
			fprintf(lf, "...=%f, ", res2);
		fprintf(lf, " =  %f\n", res);  
		loggings++;
		return(res);
	}
	else
	{
		if (pr)
			fprintf(lf, "\n");  
		return(rp->op(res1, res2));
	}
}

/*
	This function is called by an interval timer and is used to issue
	an SpmiGetStatSet() call to extract the values required to evaluate
	the defined new values.

	It invokes do_calc() for each of the new values defined.
*/
#ifdef _NO_PROTO
void feeding()
#else
void feeding()
#endif
{
   int      				i;
   float   					f;
   long     				v;
	static int				k = 0, iv = 0;
	struct LVarStr			*tvptr;
	struct SpmiStatSet	*thisssp = curssp;

	if (dataarea->SiShGoAway)
	{
		fprintf(lf, "Server requested that we die\n");
		fprintf(stderr, "Server requested that we die\n");
		doexit(0);
	}
	signal(SIGALRM, SIG_IGN);
	if (loggings > 20)
	{
		log_check();
		loggings = 0;
	}

	gettimeofday(&dataarea->SiShT, NULL);
	tvptr = vptr;
	if (SpmiGetStatSet(curssp, TRUE))
	{
		if (trace)
		{
			fprintf(lf, "%s\n", SpmiErrmsg);
		}
		if (SpmiErrno == SiLocked)
		{
#ifdef _NO_PROTO
			signal(SIGALRM, (void(*)())feeding);
#else
			signal(SIGALRM, (void(*)(int))feeding);
#endif /* _NO_PROTO */
			return;
		}
		fprintf(stderr, "%s\n", SpmiErrmsg);
		doexit(0);
	}
	while(tvptr)
	{
		if (thisssp != tvptr->ssp)
		{
			SpmiGetStatSet(thisssp, TRUE);
			thisssp = tvptr->ssp;
		}
		if (tvptr->type == newvalue)
			d->val[tvptr->ix] = do_calc(tvptr->rsp);
		if (trace == 9)
		{
			fprintf(lf, "%s = %f\n", tvptr->newvar, d->val[tvptr->ix]);
			loggings++;
		}
		tvptr = tvptr->next;
	}
#ifdef _NO_PROTO
	signal(SIGALRM, (void(*)())feeding);
#else
	signal(SIGALRM, (void(*)(int))feeding);
#endif /* _NO_PROTO */
}

#ifdef _NO_PROTO
int main(argc, argv)
int 	argc;
char	**argv;
#else
int main(int argc, char **argv)
#endif
{
	struct ResStr		*base;
	struct ResStr		*curr;
	uid_t					uid;
	int					ff, c;
	int					p, i, n;
	float					wrkf;
	int					wrki;
	int					newcount;
	char					fil[128];
	char					*lp, *r, *s;
	char					*t;
	extern char			*optarg;
	struct timeval		tid;
	boolean				other;
	struct LVarStr		*tvptr;

	
	strcpy(fil, lfilter_inp);
	/*
		Parsing of command line and reading of configuration file into memory
	*/
   while ((c = getopt(argc, argv, "hf:l:p:i:")) != EOF)
   {
      switch (c)
      {
      case 'f':   if (strlen(optarg) < 2)
							break;
						strcpy(fil, optarg);
                  break;
      case 'l':   if (strlen(optarg) < 2)
							break;
						strcpy(log1, optarg);
                  break;
      case 'p':   trace = atoi(optarg);
                  if (trace > 9)
                     trace = 9;
                  if (trace < 0)
                     trace = 0;
                  break;
      case 'i':   i = atoi(optarg);
                  if (i > 300)
                     i = interval;
                  if (i < 1)
                     i = interval;
						interval = i * 1000;
                  break;
		default:
						printf("usage:\t%s\t-f filter file name\n", argv[0]);
                  printf("\t\t-l log file name\n");
                  printf("\t\t-p detail level of logging (0-9)\n");
                  printf("\t\t-i update interval in seconds (1-300)\n");
						if (c == 'h')
							exit(0);
                  printf("\t\t...continuing anyway...\n\n");
						break;
		}
	}

	uid = geteuid();
	if (uid)
	{
		fprintf(stderr,"%s: Root authority req'd, effective UID is %d\n",
			argv[0], uid);
		exit(9);
	}
	strcat(log1, "1");
	strcat(log2, "2");
	log = log1;
	lf = fopen(log, "a");
	log_check();
	gettimeofday(&tid, NULL);
	s = (char *)ctime((time_t *)&tid.tv_sec);
	fprintf(lf, "\n============= %s started with UID= %d at: %s =============\n",
		uid, s, argv[0]);

	if ((ff = open(fil, O_RDONLY, 0)) <= 0)
	{
		fprintf(lf, "Can\'t open filter file \"%s\"\n", fil);
		fprintf(stderr, "%s: Can\'t open filter file \"%s\"\n", argv[0], fil);
		fclose(lf);
		exit(9);
	}
   pos = lseek(ff, (off_t)0, SEEK_END);
   if (pos < 5)
   {
      fprintf(lf, "Filter file \"%s\" is empty\n", fil);
      fprintf(stderr, "%s: Filter file \"%s\" is empty\n", argv[0], fil);
		fclose(lf);
   	close(ff);
      exit(8);
   }
   i = lseek(ff, (off_t)0, SEEK_SET);
   dat = (char *)calloc(1, pos + 2);
   if (!dat)
   {
      fprintf(lf,"Unable to grab memory for filter file \"%s\"\n", fil);
      fprintf(stderr,"%s: Unable to grab memory for filter file \"%s\"\n",
				argv[0], fil);
		fclose(lf);
   	close(ff);
      exit(7);
   }
   pos = read(ff, dat, pos);
   close(ff);

	/*
		Attach to Spmi
	*/
	
	if (SpmiInit(30))
   {
      fprintf(lf, "Unable to initialize Spmi interface\n");
      fprintf(stderr, "%s: Unable to initialize Spmi interface\n",
				argv[0]);
		if (SpmiErrmsg[0])
		{
      	fprintf(lf, "%s\n", SpmiErrmsg);
      	fprintf(stderr, "%s\n", SpmiErrmsg);
		}
      exit(8);
   }

	/*
		Make sure we detect serious signals that would otherwise kill us off
	*/
#ifdef _NO_PROTO
   signal(SIGINT,  (void(*)())must_exit);
   signal(SIGTERM, (void(*)())must_exit);
   signal(SIGSEGV, (void(*)())must_exit);
   signal(SIGQUIT, (void(*)())must_exit);
#else
   signal(SIGINT,  (void(*)(int))must_exit);
   signal(SIGTERM, (void(*)(int))must_exit);
   signal(SIGSEGV, (void(*)(int))must_exit);
   signal(SIGQUIT, (void(*)(int))must_exit);
#endif /* _NO_PROTO */

	/*
		Here begins the parsing of the configuration file.  It is done by
		code generated by lex from the file lfiltd.lex and is invoked by
		successive calls of the function yylex().
	*/
	while ((yychar = yylex()) > 0)
	{
		if (trace == 9)
			fprintf(lf, "state = %d, vstate = %d, type = %3d, string = \"%s\"\n",
				state, vstate, yychar, yytext);
		switch(yychar)
		{
			case COMMENT:
			{
				if (trace > 7)
					fprintf(lf, "COMMENT %s\n", yytext);
				break;
			}
			case EOL:
			{
				if (trace > 7)
						fprintf(lf, "EOL    %s\n", yytext);
				if (brc1)
				{
					doerror("Missing closing parenteses");
					break;
				}
				if ((lvptr) && (!lvptr->ready))
				{
					shine_up(&lvptr->rsp);
					if (trace >= 5)
						dump_it(lvptr->rsp, 0);
					name_error = FALSE;
					if ((lvptr) && (lvptr->type == newvalue))
					{
						newcount = check_names(lvptr->rsp, 0);
						if (name_error)
							doerror("Errors found in name lookup");
                  if (!curssp)
                  {
                     /*
                        Not current StatSet; create one
                     */
                     if (!(curssp = SpmiCreateStatSet()))
                     {
                        if (!(curssp = SpmiCreateStatSet()))
                        {
                           doerror("Can\'t create StatSet -- exiting");
                           doexit(27);
                        }
                     }
                     curcount = 0;
                  }
						if (!name_error)
						{
							lvptr->ssp = curssp;
							add_to_set(lvptr->rsp);
							if (name_error)
							{
								doerror("Unable to add to StatSet -- exiting");
								doexit(28);
							}
						}
						if (lvptr)
						{
							totcount++;
							lvptr->ready = TRUE;
						}
					}
				}
				state = nostate;
				vstate = novar;
				brc1 = brc2 = 0;
				break;
			}
			case ASSIGN:
			{
				if (trace > 7)
						fprintf(lf, "ASSIGN %s\n", yytext);
				if (state != assign1)
				{
					doerror("Unexpected assignment operator");
					break;
				}
				state = assign2;
				lvptr->type = newvalue;
				break;
			}
			case OPER:
				if (trace > 7)
						fprintf(lf, "OPER   %s\n", yytext);
			{
				if ((vstate == var) &&
					((state == assign2) || (state == assign2))) 
				{
					if (add_oper(yytext[0]))
						doexit(9);
					vstate = oper;
					break;
				}
				doerror("Unexpected operator");
				break;
			}
			case LEFTBR:
			{
				if (trace > 7)
						fprintf(lf, "LEFTBR %s\n", yytext);
				if (state != assign2)
				{
					doerror("Unexpected character");
					break;
				}
				if (vstate == var)
				{
					doerror("Character out of context");
					break;
				}
				step_up();
				break;
			}
			case RGHTBR:
			{
				if (trace > 7)
						fprintf(lf, "RGHTBR %s\n", yytext);
				if (state != assign2)
				{
					doerror("Unexpected character");
					break;
				}
				if (!brc1)
				{
					doerror("Unmatched");
					break;
				}
				if (vstate != var)
				{
					doerror("Character out of context");
					break;
				}
				step_down(brc1 - 1);
				break;
			}
			case NUMBER:
			case FLOAT1:
			case FLOAT2:
			case FLOAT3:
			{
				if (trace > 7)
						fprintf(lf, "NUMBER %s\n", yytext);
				if (state != assign2)
				{
					doerror("Unexpected numeric constant");
					break;
				}
				if (vstate == var)
				{
					doerror("Numeric constant out of context");
					break;
				}
				if (yychar == FLOAT1)
				{
					wrki = atoi(yytext+1);
					wrkf = wrki;
					while(wrkf >= 1.0)
						wrkf = wrkf / 10;
				}
				else if (yychar == FLOAT3)
				{
					t = (char *)strchr((char *)yytext, '.') + 1;
					wrki = atoi(t);
					wrkf = wrki;
					while(wrkf >= 1.0)
						wrkf = wrkf / 10;
					wrki = atoi(yytext);
					wrkf = wrkf + wrki;
				}
				else
				{
					wrki = atoi(yytext);
					wrkf = wrki;
				}
				if (add_const(wrkf))
					doexit(9);
				vstate = var;
				break;
			}
			case TARGET:
			{
				if (trace > 7)
						fprintf(lf, "TARGET %s\n", yytext);
				/*
					Initial processing for new assignment of variable to add
				*/
				if (state == nostate)
				{
					state = assign1;
					if (add_entry())
						doexit(9);
					if (!(lvptr->newvar = strccvt(yytext)))
					{
						doerror("Out of memory");
						doexit(9);
					}
					if (strlen(lvptr->newvar) >= EXCEPT_NAME_LEN)
						lvptr->newvar[EXCEPT_NAME_LEN -1] = '\0';
					break;
				}
				/* fall through	*/
			}
			case VARBL:
			{
				if (trace > 7)
						fprintf(lf, "VARBL  %s\n", yytext);
				if (state != assign2)
				{
					doerror("Variable out of context");
					break;
				}
				if (vstate == var)
				{
					doerror("Variable with no preceeding operator");
					break;
				}
				if (add_var(strccvt(yytext)))
					doexit(9);
				vstate = var;
				break;
			}
			case WCCNT:
			case WCTOP:
			case WCBOT:
			case WCMUL:
			case WCADD:
			{
				if (trace > 7)
						fprintf(lf, "WCxxx  %s\n", yytext);
				if (state != assign2)
				{
					doerror("Variable out of context");
					break;
				}
				if (vstate == var)
				{
					doerror("Variable with no preceeding operator");
					break;
				}
				if (yychar == WCCNT)
				{
					if (add_wild('#'))
						doexit(9);
					vstate = var;
					break;
				}
				step_up();
				if (yychar == WCADD)
				{
					if (add_wild('+'))
						doexit(9);
				}
				else if (yychar == WCMUL)
				{
					if (add_wild('*'))
						doexit(9);
				}
				else if (yychar == WCTOP)
				{
					if (add_wild('T'))
						doexit(9);
				}
				else if (yychar == WCBOT)
				{
					if (add_wild('B'))
						doexit(9);
				}
				else
					doexit(9);
				step_down(brc1 - 1);
				vstate = var;
				break;
			}
			case DESCR:
			{
				if (trace > 7)
						fprintf(lf, "DESCR  %s\n", yytext);
				if (state == nostate)
				{
					doerror("Variable description out of context");
					break;
				}
				lvptr->descr = (char *)strmake((char *)yytext + 1);
				lvptr->descr[strlen(lvptr->descr) - 1] = '\0';
				if (strlen(lvptr->descr) >= EXCEPT_DESC_LEN)
					lvptr->descr[EXCEPT_DESC_LEN -1] = '\0';
				break;
			}
			default:
				break;
		}
	}

	/*
		All new values have now been defined.  We go through the table
		and insert them into a table of Stat structures, which we allocate. 
	*/
	FiltStats = (struct SpmiRawStat *)calloc(totcount+1,
					 sizeof(struct SpmiRawStat));
	if (!FiltStats)
	{
		lvptr = vptr;
		doerror("Unable to allocate memory for Stats -- exiting");
		doexit(30);
	}
	if (!vptr)
	{
		lvptr = vptr;
		doerror("No statistics have been defined -- exiting");
		doexit(31);
	}
	else
	{
		struct SpmiRawStat	*fsp = FiltStats;

		arraycount = 0;
		tvptr = vptr;
		while(tvptr)
		{
			if (tvptr->type == newvalue)
			{
				strcpy(fsp->name, tvptr->newvar);
				if (tvptr->descr)
					strcpy(fsp->description, tvptr->descr);
				else
					strcpy(fsp->description, tvptr->newvar);
				fsp->min 			= 0;
				fsp->max 			= 100;
				fsp->value_type	= SiQuantity;
#ifdef mips
				fsp->data_type		= SiLong;
				fsp->size			= sizeof(long);
#else
				fsp->data_type		= SiFloat;
				fsp->size			= sizeof(float);
#endif
				fsp->offset			= fsp->size * arraycount;
				fsp->type			= SiFloat;
				tvptr->ix			= arraycount++;
				fsp->asnno			= arraycount;
				fsp++;
			}
			tvptr = tvptr->next;
		}
	}

	/*
		Well, if we did add values, "arraycount" is non-zero, and we
		should make sure we update the context with the count
	*/
	if (arraycount)
	{
		FiltContext[1].num_stats = arraycount;
		FiltContext[1].stats = FiltStats;

		/*
			Now initialize the Dynamic Data Supplier interface
		*/
#ifdef _AIX
		dataarea = SpmiDdsInit(FiltContext, 2, NULL, 0, "/etc/LFilterDDS.shm");
#else
		dataarea = SpmiDdsInit(FiltContext, 2, NULL, 0, "/etc/LFilterDDS.shm",
					  16 * 1024);
#endif
		if (!dataarea)
		{
        	fprintf(lf, "%s (%s)\n", SpmiErrmsg, FiltContext[1].path);
        	doexit(33);
		}
   	d = (struct dat *)&dataarea->SiShArea[0];
	}
	log_check();

	/*
		Now we should be finished with the parsing and table building
		and ready to go for the start-feed sequences to the daemon.
	*/
	tvptr = vptr;
	curssp = NULL;
	if (trace)
		fprintf(lf, "Interval = %d\n", interval);

	while(tvptr)
	{
		if ((tvptr->ready) && (tvptr->ssp != curssp))
		{
		   t_value.it_interval.tv_sec =  interval/1000;
			if (t_value.it_interval.tv_sec < 1)
		   	t_value.it_interval.tv_sec =  1;
   		t_value.it_interval.tv_usec = 0;
   		t_value.it_value.tv_sec =  t_value.it_interval.tv_sec;
   		t_value.it_value.tv_usec = 0;
			if (setitimer(ITIMER_REAL, &t_value, &o_value) < 0)
   		{
      		printf("error no %d while setting interval timer\n", errno);
				must_exit(8);
   		}
			curssp = tvptr->ssp;
#ifdef _NO_PROTO
			signal(SIGALRM, (void(*)())feeding);
#else
			signal(SIGALRM, (void(*)(int))feeding);
#endif /* _NO_PROTO */
			break;
		}
		tvptr = tvptr->next;
	}
	while (TRUE)
	{
		gettimeofday(&dataarea->SiShT, NULL);
		pause();
	}
}
