#include "copyright.h"
/****************************************************************** 
 *
 * keysclass.c --
 *
 *     Implementation of keys storage/retrieval.
 *
 * $Author: mstoufer $
 * $Source: /home/g2/src/projects/NetLogger/src/nlv/keysclass.c,v $
 * $RCSfile: keysclass.c,v $
 * $Revision: 1.1.1.1 $
 * $Locker:  $
 * $Date: 2000/12/05 19:52:32 $
 *
 *
 ****************************************************************** 
 */
static char rcsid[] = "$Id: keysclass.c,v 1.1.1.1 2000/12/05 19:52:32 mstoufer Exp $";

#include "graphclass.h"
#include "util.h"
#include "errclass.h" 
#include "keysclass.h"

/*========= DATA =====================================================*/

#define PLUS '+'
#define MINUS '-'

#define T Nlv_ky
struct T {
  char            *title;          /* Title for this configuration,
				      (also used as graph title). */
  Tcl_HashTable    keyTbl;         /* Key (or "event") lookup table */
  Tcl_HashTable    keySetTbl;      /* Key set (or "event set") lookup table */
  int              numKeys;        /* Number of keys */
  int              numKeySets;     /* Number of key sets */
  char             fname[ 128 ];   /* Filename for configuration data */
  char          ***aliasFields;    /* 2-D array of aliases {alias synonyms..}.. */
  int             *numAliasFields; /* # of synonyms for each alias              */
  int              numAliases;     /* Number of alias synonyms.. lists          */
};
extern Tcl_Interp *Et_Interp; /* Tcl interpreter */
extern Nlv_err  eobj;        /* Error reporting object */
#define FILE_ERR_CODE 1100

/*========= FUNCTIONS ================================================*/

/*
 *----------------------------------------------------------------------
 *
 * New -- Allocate new object.
 *
 * Calls: malloc
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */
T Nlv_kyNew( )
{
  T this = (T)malloc( sizeof(struct Nlv_ky) );

  /* Set to default title */
  this->title = strdup( "NetLogger Visualization");
  /* init hashtables for keys and keysets */
  Tcl_InitHashTable( &(this->keyTbl), TCL_STRING_KEYS );
  Tcl_InitHashTable( &(this->keySetTbl), TCL_STRING_KEYS );
  /* init keys and keysets as empty */
  this->numKeys = 0;
  this->numKeySets = 0;
  /* init hostaliases as empty */
  this->numAliases = 0;
  this->numAliasFields = NULL;
  this->aliasFields = NULL;
  /* init filename as empty */
  memset( this->fname, 0, sizeof(this->fname) );

  return this;
}

/*
 *----------------------------------------------------------------------
 *
 * FromFile -- Read the keysets/keys from a file.
 *
 * Calls: pipe, nlvkyparse, calcparse
 *
 * Algorithm: Remember filename for next time (see ToFile).
 *
 *----------------------------------------------------------------------
 */
void Nlv_kyFromFile( T this, char *fname )
{
  char *func = "Nlv_kyFromFile";
  int fd, save_stdin;

  if ( !fname ) {
    Nlv_errPrintString( eobj, func, "No input filename.");
    exit( 1 );
  }
  strcpy( this->fname, fname );

  /* Open file and redirect to stdin */
  fd = open( fname, O_RDONLY );
  if ( fd < 0 ) {
    Nlv_errPrintString(eobj, func,"Unable to open configuration file for reading.");
    exit( 1 );
  }
  save_stdin = dup( STDIN_FILENO );
  if ( dup2( fd, STDIN_FILENO ) != 0 ) {
    Nlv_errPrintString( eobj, func, "Unable to duplicate standard input.");
    exit( 1 );
  }
  close( fd );
  
  /* Call yacc-generated parsing routine on stdin */
  nlvkyparse();

  /* Close file and reopen stdin */
  if ( 0 != dup2( save_stdin, STDIN_FILENO ) ) {
    Nlv_errPrintString( eobj, func, "Unable to reopen standard input.");
    exit( 1 );
  }
  close( save_stdin );
}

/*
 *----------------------------------------------------------------------
 *
 * ClearKeys -- Clear out key-set and key hash tables.
 *
 * Calls: Tcl_(hashtable functions)
 *
 * Algorithm: Use hash table iterators to get every entry and free
 *            its storage.
 *
 *----------------------------------------------------------------------
 */

void Nlv_kyClearKeys( T this )
{
  int             i;
  Nlv_Key        *keyPtr;
  Nlv_KeySet     *keySetPtr;
  Tcl_HashEntry  *entryPtr;
  Tcl_HashSearch  search;

  /* Free keysets */
  entryPtr = Tcl_FirstHashEntry( &(this->keySetTbl), &search );
  while ( entryPtr != NULL ) {
    keySetPtr = (Nlv_KeySet *)Tcl_GetHashValue( entryPtr );
    if ( keySetPtr->name )
      free( (char *)keySetPtr->name );
    if ( keySetPtr->calcp )
      free( keySetPtr->calcp );
    /* Tcl allocated these in a single chunk */
    free( (char *)&(keySetPtr->idFields[0]) );
    Tcl_DeleteHashEntry( entryPtr );
    entryPtr = Tcl_NextHashEntry( &search );
  }

  /* Free keys */
  entryPtr = Tcl_FirstHashEntry( &(this->keyTbl), &search );
  while ( entryPtr != NULL ) {
    keyPtr = (Nlv_Key *)Tcl_GetHashValue( entryPtr );
    if ( keyPtr->name )
      free( (char *)keyPtr->name );
    Tcl_DeleteHashEntry( entryPtr );
    entryPtr = Tcl_NextHashEntry( &search );
  }

}


/*
 *----------------------------------------------------------------------
 *
 * ToFile -- Write keyset info to a file.
 *
 * Calls    : <not implemented>
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */
int Nlv_kytoFile( T this, char *fname )
{
  return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * GetKeySet -- Retrieve a pointer to a keyset structure by name.
 *
 * Calls: Tcl_FindHashEntry, Tcl_GetHashValue
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */

Nlv_KeySet *Nlv_kyGetKeySet( T this, char *nm )
{
  Tcl_HashEntry *entryPtr;

  entryPtr = Tcl_FindHashEntry( &(this->keySetTbl), nm );
  if ( entryPtr == NULL )
    return NULL;
  return ( Nlv_KeySet *)Tcl_GetHashValue( entryPtr );
}


/*
 *----------------------------------------------------------------------
 *
 * GetKey -- Retrieve a pointer to a key structure by name.
 *
 * Calls: Tcl_FindHashEntry, Tcl_GetHashValue
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */

Nlv_Key *Nlv_kyGetKey( T this, char *nm )
{
  Tcl_HashEntry *entryPtr;

  entryPtr = Tcl_FindHashEntry( &(this->keyTbl), nm );
  if ( entryPtr == NULL )
    return NULL;
  return ( Nlv_Key *)Tcl_GetHashValue( entryPtr );  
}


/*
 *----------------------------------------------------------------------
 *
 * AddKeySet -- Constructor for a keyset structure that fills in
 *              the values with the parameter list, then puts
 *              the keyset into the hashtable used by the rest
 *              of the keysclass functions.
 *
 * Calls: malloc, Tcl_CreateHashEntry, Tcl_SetHashValue
 *
 * Algorithm: Allocate a keyset structure, assign values,
 *            then create and set a hashtable entry.
 *
 *            We make the assumption that the CALLER has allocated
 *            memory for all input strings, and arrays of strings. 
 *            Make it so; hell hath no fury like a pointer scorned.
 *
 *----------------------------------------------------------------------
 */
Nlv_KeySet* Nlv_kyAddKeySet( T      this,
			     char  *setName,
			     int    gtype,
			     int    index,
			     int    numids,
			     char **ids,
			     int    numhosts,
			     char **hosts,
			     int    numanns,
			     char **anns,
			     char  *valField,
			     double valMin,
			     double valMax,
			     Nlv_CalcParam *cparms,
			     int numcparms,
                             int timeordered,			     
                             int rotateClients,
			     int autoscale )
{
  char *func = "Nlv_kyAddKeySet";
  Tcl_HashEntry *entryPtr; /* Entry in hashtable of keyset pointers */
  int            newEntry; /* Return value: is this a new entry? */
  Nlv_KeySet    *ks;       /* Pointer to dynamically allocated keyset */

  /* Fill keyset struct */
  ks = (Nlv_KeySet *)malloc( sizeof (struct Nlv_KeySet) );

  /* Skip leading character in setName; this is a small leak,
   * but with a hardcoded limit of sets, we're talking about < 1K
   */
  ks->name = setName + sizeof(char); 
  ks->visible     = (*(setName) == PLUS ? 1 : 0 );

  /* Graph type */
  ks->gtype       = gtype;

  /* Id fields */
  ks->idFields    = ids;
  ks->numIdFields = numids;

  /* Hostid fields */
  ks->hostFields    = hosts;
  ks->numHostFields = numhosts;

  /* Annotation fields */
  ks->annFields = anns;
  ks->numAnnFields = numanns;

  /* Calculation parameters */
  ks->calcp    = cparms;
  ks->numCalcp = numcparms;
  
  /* Value field, min, max */
  ks->valField = valField;
  ks->valMin   = valMin;
  ks->valMax   = valMax;

  /* Time-ordered mode */
  ks->timeOrdered = timeordered;

  /* Rotate clients mode */
  ks->rotateClients = rotateClients;

  /* Autoscaling */
  switch( autoscale )
  {
  case 1: ks->autoScaleMax = 1, ks->autoScaleMin = 0; break;
  case 2: ks->autoScaleMin = ks->autoScaleMax = 1; break;
  case -1: ks->autoScaleMin = 1, ks->autoScaleMax = 0; break;
  case 0: /* fall-through */
  default: ks->autoScaleMin = ks->autoScaleMax = 0; break;
  }
  /* Debugging */
  /* printf("Autoscale: min=%d max=%d\n", ks->autoScaleMin, ks->autoScaleMax ); */

  /* Keyset index */
  ks->index   = index;

  /* Init numkeys */
  ks->numKeys = 0;

  /* Put into hash table */
  entryPtr = Tcl_CreateHashEntry( &(this->keySetTbl), ks->name, &newEntry );
  if ( newEntry != 1 ) {
    char msg[256];
    sprintf(msg,"Duplicate keyset from configuration file: %s.",ks->name);
    Nlv_errPrintString( eobj, func, msg );
    exit(1);
  }
  Tcl_SetHashValue( entryPtr, ks );

  /* # of keysets added so far */
  this->numKeySets++;

  /* Return pointer to new keyset */
  return ks;
}

/*
 *----------------------------------------------------------------------
 *
 * AddKey -- Add a key to an existing key set.
 *
 * Calls:
 *
 * Algorithm: Allocate a Nlv_Key structure, fill in the
 *            values according to the parameters, then
 *            create and set a hashtable entry in the
 *            keysclass variable 'keyTbl'.
 *
 *            We make the assumption that the CALLER has allocated
 *            memory for all input strings, and arrays of strings. 
 *
 *----------------------------------------------------------------------
 */

void Nlv_kyAddKey( T           this,
		   Nlv_KeySet *inSet,    /* Pointer to containing keyset */
		   char       *keyName ) /* Name of the key */
{
  char *func = "Nlv_kyAddKey";
  Tcl_HashEntry *entryPtr;
  int            newEntry;
  Nlv_Key       *k;

  /* Allocate key struct */
  k = (Nlv_Key *)malloc( sizeof( struct Nlv_Key ) );

  /* Visibility depends on 1st char '+' or '-' */
  k->visible = ( *(keyName) == PLUS ? 1 : 0 );

  /* Keyset name = 'keyName' without 1st char.
   * This is a little leak. If you are using a VIC-20, you may
   * want to avoid it.
   */
  k->name = (char *)(keyName + sizeof(char));

  /* Keyset we are in */
  k->ks    = inSet;

  /* Increment keyset's count of keys */
  k->index = inSet->numKeys++;

  /* Place key in hash table */
  entryPtr = Tcl_CreateHashEntry( &(this->keyTbl), k->name, &newEntry );
  if ( newEntry != 1 ) {
    char msg[256];
    sprintf(msg,"Duplicate key from configuration file: %s",k->name);
    Nlv_errPrintString( eobj, func, msg );
    exit(1);
  }
  Tcl_SetHashValue( entryPtr, k );
}
#undef PLUS
#undef MINUS

/*
 *----------------------------------------------------------------------
 *
 * AddUserCalc -- Add a user-defined calculation (formula) to the
 *                keyset.
 *
 * Calls:
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */

void Nlv_kyAddUserCalc( T this,
			Nlv_KeySet  *ks, /* keyset calc is in */
			char        *str, /* name of calc */
			Nlv_CalcVar *vars ) /* CHANGE!! */
{
  return;
}

/*
 *----------------------------------------------------------------------
 *
 * AddHostalias -- Add a line of hostaliases to the keys class.
 *                 The input argument 'strlist' is assumed to be
 *                 allocated by the caller & therefore the pointers
 *                 to the strings will be used WITHOUT MAKING A COPY.
 *
 * Calls    : realloc
 *
 * Algorithm: Use the three keyset variables: aliasfields, 
 *            numAliasFields, numAliases, to update the 2-D
 *            array of strings which comprise the list of
 *            lists (sic.) of hostaliases.
 *
 *----------------------------------------------------------------------
 */
void Nlv_kyAddHostalias( T this, int n, char **strlist, char *alias_target )
{
  int na = this->numAliases; /* save typing */
  int i;                     /* loop counter */

  na++;

  /* allocate pointer to a new row in aliasFields */
  this->aliasFields = ( char *** ) realloc ( this->aliasFields, na * sizeof(char*) );

  /* allocate n+1 char*'s to make up the row */
  this->aliasFields[ na-1 ] = ( char ** ) malloc ( (n+1) * sizeof(char *) );

  /* allocate new integer in numAliasFields <- n */
  this->numAliasFields = ( int * ) realloc ( this->numAliasFields, na * sizeof(int) );
  this->numAliasFields[ na-1 ] = n+1;

  /* copy the alias_target into the first element of the array */
  this->aliasFields[ na-1 ][ 0 ] = strdup(alias_target);

  /* copy the POINTERS to the strings in the input string list 'strlist' */
  for ( i=1; i < (n+1); i++ ) {
    this->aliasFields[ na-1 ][ i ] = strlist[ i-1 ];
  }

  /* update the number of host alias lists */
  this->numAliases = na;

  return;
}


/*
 *----------------------------------------------------------------------
 *
 * GetNumKeys -- Accessor for numKeys
 *
 * Calls:
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */

int Nlv_kyGetNumKeys( T this )
{
  return this->numKeys;
}


/*
 *----------------------------------------------------------------------
 *
 * GetNumKeySets -- Accessor for numKeySets
 *
 * Calls:
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */

int Nlv_kyGetNumKeySets( T this )
{
  return this->numKeySets;
}

/*
 *----------------------------------------------------------------------
 *
 * GetKeySetTbl -- Accessor for keySetTbl (return pointer).
 *
 * Calls:
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */
Tcl_HashTable *Nlv_kyGetKeySetTbl( T this )
{
  return &(this->keySetTbl);
}


/*
 *----------------------------------------------------------------------
 *
 * GetKeyTbl -- Accessor for keyTbl (return pointer)
 *
 * Calls:
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */
Tcl_HashTable *Nlv_kyGetKeyTbl( T this )
{
  return &(this->keyTbl);
}


/*
 *----------------------------------------------------------------------
 *
 * GetHostAlias -- Find if there is an aliased common name for
 *                 this host.
 *
 * Calls    :
 *
 * Algorithm: Search for the mofo in the aliasFields (2-D) array.
 *
 *----------------------------------------------------------------------
 */

char *Nlv_kyGetHostAlias( T this, char *nm )
{
  char *r;
  int i,j, done;

  done = 0;
  for ( i = 0; done == 0 && i < this->numAliases; i++ ) {
    for ( j=1; done == 0 && j < this->numAliasFields[i]; j++ ) {
      if ( strcasecmp( this->aliasFields[i][j], nm ) == 0 ) { 
	done = 1;
      }
    }
  }
  if ( done == 0 )
    r = nm;
  else {
    --i;
    r = this->aliasFields[i][0];
  }
  return r;
}

/*
 *----------------------------------------------------------------------
 *
 * GetCalcShow -- 1 = yes, 0 = no.
 *
 * Calls    :
 *
 * Algorithm: Search for the mofo in the Nlv_CalcParam structs.
 *            If not found, use default rules based on the
 *            "gtype" of the keyset.
 *
 *----------------------------------------------------------------------
 */
int Nlv_kyGetCalcShow( T this, Nlv_KeySet *ks, char *calcName )
{
  int i;
  int r = -1;

  for ( i=0; i < ks->numCalcp && r == -1; i++ ) {
    if ( strcmp(ks->calcp[i].name, calcName)==0 ) {
      r = ks->calcp[i].show;
    }
  }

  if ( r == -1 ) {
    switch ( ks->gtype ) {
    case NLV_LINE: 
      r = 1; 
      break;
    default: 
      r = 0;
    }
  }
  return r;
}

/*
 *----------------------------------------------------------------------
 *
 * KeysToTcl -- Put the key/keyset names and visibility into Tcl.
 *
 * Calls    : Tcl_SetVar2, Tcl_FirstHashEntry, Tcl_NextHashEntry,
 *            Tcl_GetHashValue
 *
 * Algorithm: - Iterate through the hashtabes, setting Tcl array
 *              values along the way.
 *            - Mapping:
 *              C                    Tcl
 *              -----------------    -------------------
 *              this->numKeySets     param(EVENT,NUMSETS)
 *              keyset[i]->numkeys   param(EVENT,NUMKEYS,i)
 *              keyset[i]->name      param(EVENT,i)
 *              keyset[i],key[j]     param(EVENT,i,j)
 *              visible = 0          -NAME
 *              visible = 1          +NAME
 *
 *----------------------------------------------------------------------
 */
void Nlv_kyKeysToTcl( T this )
{
  char *func = "Nlv_kyKeysToTcl";
  int  i,j;        /* loop counters */
  char elt[ 64 ];  /* array element in Tcl */
  char var[ 64 ];  /* string version of variable to set */
  char cmd[ 512 ]; /* command buffer */
  char *tclok;     /* Tcl return value from SetVar2 */
  Nlv_Key        *keyPtr; 
  Nlv_KeySet     *keySetPtr;
  Tcl_HashEntry  *entryPtr;
  Tcl_HashSearch  search;


  /* param(EVENT,NUMSETS) */
  sprintf( var, "%d", this->numKeySets );
  tclok = Tcl_SetVar2( Et_Interp, "param", "EVENT,NUMSETS", var, TCL_GLOBAL_ONLY );
  if ( tclok == NULL ) {
    Nlv_errPrintString( eobj, func, "Cannot set number of eventsets in Tcl.");
    Nlv_errFatalTcl( eobj, Et_Interp, NULL ); 
  }

  /* loop for each keyset */
  entryPtr = Tcl_FirstHashEntry( &(this->keySetTbl), &search );
  while ( entryPtr != NULL ) {
    keySetPtr = (Nlv_KeySet *)Tcl_GetHashValue( entryPtr );

    /* param(EVENT,NUMKEYS,i) */
    sprintf( elt,"EVENT,NUMKEYS,%d",keySetPtr->index );
    sprintf( var,"%d", keySetPtr->numKeys );
    tclok = Tcl_SetVar2( Et_Interp, "param", elt, var, TCL_GLOBAL_ONLY );
    if ( tclok == NULL ) {
      Nlv_errPrintString( eobj, func, "Cannot set number of events in Tcl.");
      Nlv_errFatalTcl( eobj, Et_Interp, NULL );
    }
    /* param(EVENT,i) */
    sprintf( elt, "EVENT,%d", keySetPtr->index );
    if ( keySetPtr->visible == 0 )
      sprintf( var, "-%s", keySetPtr->name );
    else
      sprintf( var, "+%s", keySetPtr->name );
    tclok = Tcl_SetVar2( Et_Interp, "param", elt, var, TCL_GLOBAL_ONLY );
    if ( tclok == NULL ) {
      Nlv_errPrintString( eobj, func, "Cannot set eventset name in Tcl.");
      Nlv_errFatalTcl( eobj, Et_Interp, NULL );
    }
    entryPtr = Tcl_NextHashEntry( &search );
  }
  
  /* loop for each key */
  entryPtr = Tcl_FirstHashEntry( &(this->keyTbl), &search );
  while ( entryPtr != NULL ) {
    keyPtr = (Nlv_Key *)Tcl_GetHashValue( entryPtr );

    /* param(EVENT,i,j) */
    sprintf(elt, "EVENT,%d,%d", keyPtr->ks->index, keyPtr->index );
    if ( keyPtr->visible == 0 )
      sprintf(var,"-%s", keyPtr->name );
    else
      sprintf(var,"+%s", keyPtr->name );
    tclok = Tcl_SetVar2( Et_Interp, "param", elt, var, TCL_GLOBAL_ONLY );
    if ( tclok == NULL ) {
      Nlv_errPrintString( eobj, func, "Cannot set event.");
      Nlv_errFatalTcl( eobj, Et_Interp, NULL );
    }
    entryPtr = Tcl_NextHashEntry( &search );    
  }
  
  return;

}

/*
 *----------------------------------------------------------------------
 *
 * KeysFromTcl -- Retrieve the (possibly modified) key values from
 *                Tcl variables and update C data structures.
 *
 * Calls    : Tcl_GetVar2
 *
 * Algorithm: - Read the ordering of the keysets back from
 *            Tcl, and modify the index #'s of the keysets
 *            and keys within the keysets
 *            - Use the 1st character of the string with a keyset or
 *            key name ('+' or '-') to update the "visibility" of
 *            the key/keyset.
 *            - Mapping:
 *              C                    Tcl
 *              -----------------    -------------------
 *              keyset[i]->name      param(EVENT,i)
 *              keyset[i],key[j]     param(EVENT,i,j)
 *              visible = 0          -NAME
 *              visible = 1          +NAME
 *
 *----------------------------------------------------------------------
 */
void Nlv_kyKeysFromTcl( T this )
{
  char           *func = "Nlv_kyKeysFromTcl";
  char           *val;       /* Value of variable from Tcl */
  char            elt[ 64 ]; /* Tcl array element name */
  int             new_ksnum; /* New keyset # for a given key name */
  int             new_vis;   /* Visibility of key/keyset */
  int             new_knum;  /* New key # for a given key name */
  Nlv_Key        *keyPtr;    /* points at a key */
  Nlv_KeySet     *keySetPtr; /* points at a keyset */
  Tcl_HashEntry  *entryPtr;  /* points at a hashtable entry */

  /*
   * loop for each keyset 
   */
  for ( new_ksnum = 0; new_ksnum < this->numKeySets; new_ksnum++ ) {
    /* get Tcl value for element # new_ksnum */
    sprintf( elt, "EVENT,%d", new_ksnum );
    val = Tcl_GetVar2( Et_Interp, "param", elt, TCL_GLOBAL_ONLY );
    if ( val == NULL ) {
      Nlv_errPrintString( eobj, func, "Cannot get eventset name." );
      Nlv_errFatalTcl( eobj, Et_Interp, NULL );
    }
    new_vis = ( *val == '+' ? 1 : 0 );
    /* get pointer to key set structure of that name */
    entryPtr = Tcl_FindHashEntry( &(this->keySetTbl), &val[1] );
    if ( entryPtr == NULL ) {
      Nlv_errPrintString( eobj, func, "Cannot find eventset in hash table.");
      exit( 1 );
    }
    keySetPtr = (Nlv_KeySet *)Tcl_GetHashValue( entryPtr );
    /* update the structure */
    keySetPtr->index   = new_ksnum;
    keySetPtr->visible = new_vis; 
    /*
     *  inner loop-- for each key in keyset 
     */
    for( new_knum=0; new_knum < keySetPtr->numKeys; new_knum++ ) {
      /* get Tcl value for element # new_knum */
      sprintf( elt, "EVENT,%d,%d", new_ksnum, new_knum );
      val = Tcl_GetVar2( Et_Interp, "param", elt, TCL_GLOBAL_ONLY );
      if ( val == NULL ) {
	Nlv_errPrintString( eobj, func, "Cannot get event name.");
	Nlv_errFatalTcl( eobj, Et_Interp, NULL );
      }
      new_vis = ( *val == '+' ? 1 : 0 );
      /* get pointer to key structure of that name */
      entryPtr = Tcl_FindHashEntry( &(this->keyTbl), &val[1] );
      if ( entryPtr == NULL ) {
	Nlv_errPrintString( eobj, func, "Cannot find event in hash table.");
	exit( 1 );
      }
      keyPtr = (Nlv_Key *)Tcl_GetHashValue( entryPtr );
      /* update the structure */
      keyPtr->index   = new_knum;
      keyPtr->visible = new_vis;
    }
  }

  return;

}

/*
 *----------------------------------------------------------------------
 *
 * Nlv_kySetTitle -- Set the title of the graph.
 *
 * Calls: Tcl_Eval
 *
 *----------------------------------------------------------------------
 */
void Nlv_kySetTitle( T this, char *title )
{
  char *func = "Nlv_kySetTitle";
  char cmd[ 116 ]; /* This limits the title to 100 chars */

  if ( title == NULL )
    this->title = "NetLogger Visualization";
  else
    this->title = title;

  if ( strlen(this->title) > sizeof(cmd) - 16 )
    this->title[sizeof(cmd) - 16] = '\0';
  sprintf( cmd, "SetGraphTitle \"%s\"", this->title );
  if ( TCL_OK != Tcl_Eval( Et_Interp, cmd ) ) {
    Nlv_errPrintString( eobj, func, "Cannot set graph title.");
    Nlv_errFatalTcl( eobj, Et_Interp, NULL );
  }
}

#undef T
#undef FILE_ERR_CODE

