
/****************************************************************************/
/*                                                                          */
/*      NNstat -- Internet Statistics Collection Package                    */
/*                                                                          */
/*            Written by: Bob Braden & Annette DeSchon                      */
/*            USC Information Sciences Institute                            */
/*            Marina del Rey, California                                    */
/*                                                                          */
/*      Copyright (c) 1991 University of Southern California.               */
/*      All rights reserved.                                                */
/*                                                                          */
/*      Redistribution and use in source and binary forms are permitted     */
/*      provided that the above copyright notice and this paragraph are     */
/*      duplicated in all such forms and that any documentation,            */
/*      advertising materials, and other materials related to such          */
/*      distribution and use acknowledge that the software was              */
/*      developed by the University of Southern California, Information     */
/*      Sciences Institute.  The name of the University may not be used     */
/*      to endorse or promote products derived from this software           */
/*      without specific prior written permission.                          */
/*      THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR        */
/*      IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED      */
/*      WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR          */
/*      PURPOSE.                                                            */
/*                                                                          */
/****************************************************************************/

static char rcsid[]=
  "$Header: /tmp_mnt/r/jove_staff3/mogul/alpha/code/NNstat/RCS/enum.c,v 1.3 1993/08/31 23:42:23 mogul Exp $";
 

/*
 * CHANGES: 
 *   13Oct89 Merit: For RT, work around compiler bug.
 *   Aug93/DECWRL: Alpha/OSF port.
 *
 */
 
/*                          enum.c
 *
 *  This module defines the operations required to define and access
 *  the "enumerations" of label strings for particular objects.  These
 *  label strings are used in formatting displays of the values recorded
 *  by these objects, to identify the semantics of these values.
 *  
 *  This module contains the following externally-callable routines:
 *
 *
 *      FindEnum(enum-handle, &value, length)
 *
 *          This routine is called to map an (object, value) pair into
 *          a pointer to the corresponding label string, if any.  It uses
 *          a hash table. The object is specified by the enum-handle, 
 *          which was obtained from an ExistEnumObj() call.
 * 
 *      EnumHandle ExistEnumObj(objname)
 *
 *          This routine returns a non-zero enum-handle if the
 *          the specified object matches any object namespecs
 *          recorded in the hash table.  If not, it returns 0.
 *
 *      AddEnum(objname, label, value-ptr, length) 
 *
 *          This routine is called to add specified triplet to hash table.
 *
 *      char *NextEnumObj(&handle)
 *
 *          This routine sequences through the set of defined 
 *          object-spec strings. To start, set handle = NULL.
 *
 *
 *  The implementation assumes that the hash table will be built at
 *  initialization time and never freed; there is no provision for
 *  deleting or reclaiming space it uses. However, a later AddEnum call
 *  for the same (object, value) pair will override the earlier definition.
 *
 */
 
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>

#ifndef BSD4_3
#include <netinet/in.h> 
#endif

#include "stat.h"
#include "sobj.h"

        
    /*
     *  Define an element of hash table chain
     */
#define EE struct enum_element
EE {
    EE    *ee_next; /* Label => hash chain pointer;
                       Object=> next in linear chain of objects*/
    EE    *ee_optr; /* Label => Ptr to next label this object;
                       Object=> Ptr to first label this object. */
    short  ee_objno; /* Object number */ 
    short  ee_lng;   /* Length of following value (0 for object entry) */
    
/*------------------------------------------------------------  
    Followed by: variable-length value string   
/*------------------------------------------------------------
    Followed by null-terminated char string, and padded to fullword.
/*------------------------------------------------------------*/
};

    /*
     *  Define hash table itself ... heads of chains
     */
#define HASHSIZE 128
EE *hasht[HASHSIZE];

    /*
     * Global object number
     */
int ObjNumber = 0;
  
    /*
     *  Head of linear chain of object entries. Object entries themselves 
     *  are allocated from same space as label entries. (We don't hash objects
     *  because there may be wild-cards in ObjNameSpec.  We don't like
     *  linear searches, but fortunately references are clustered by
     *  object, and only have to do linear search when object changes).
     */
EE *ObjHead = NULL;

    
#define  AREASIZE  1024
struct Area {
    struct Area *Area_next;
    char   Area_area[AREASIZE];
};
struct Area *AreaHead = NULL;  /* First of list of areas */
struct Area *AreaCurr = NULL;  /* Current area */
char  *Areap; /* Next avail location in Current Area */

#define hashfunc(x) (t=(x), ((t>>1)+t)%HASHSIZE)

#define Valp(x)  ((char *)(x) + sizeof(EE))
#define Stringp(x) ((char *)(x) + sizeof(EE) + (x)->ee_lng)

char *getspace();
EE  **findobj();            
#ifndef	__osf__
char *malloc();
#else
/*
 * malloc() should be void *, not char *.  Anyway, we never use it
 * without an explicit cast
 */
#endif
            
    
    /*
     *  char *FindEnum(enum-handle, &value, length)
     *
     *     If length <= 4, match as 32-bit integers.
     *
     */
char *FindEnum(enumh, valuep, length)
    EE    *enumh;
    char  *valuep;
    int    length;
    {
    register EE *eep;
    register u_int32 t;  /* Temp for hash func */
    int objno;
    union v_union Value;
        
    if (enumh == NULL) return(NULL);
    objno = enumh->ee_objno;

        /*   Do hash lookup.  We hash on the given value plus
         *   the object number... so the same value under different
         *   objects hashes into different bins. (I first saw this trick
         *   in the Burroughs 220 Algol-58 compiler called BALGOL,
         *   written in Pasadena in 1959 and commented by Don Knuth,
         *   who was then a student at Cal Tech.)
         */ 
    ZERO_UNION(&Value);
    memcpy(&Value.v_byte[PARMALIGN(length)], valuep, length);
    eep = hasht[ hashfunc( SUM_UNION(&Value) + objno) ];
    while (eep) {
        if (objno == eep->ee_objno  
              && eep->ee_lng >= length  
              && !memcmp(valuep, Valp(eep)+(eep->ee_lng-length), length))
            return(Stringp(eep));
        eep = eep->ee_next;
    }
    return(NULL);
} /* char *FindEnum() */    


    /* Find matching object entry... specifically, return address of 
     * predecessor pointer in linear chain of object entries.  If 
     * no match is found, returns address of NULL ptr on end of list.
     */
EE **findobj(objname)
    char *objname;
    {
    EE **objpp = &ObjHead, *eep;
        
    while (eep = *objpp) {
        if (match(Stringp(eep), objname))  break;
        objpp = &eep->ee_next;
    }
    return(objpp);
}


    /* 
     *  EnumHandle ExistEnumObj(object) -- return enum-handle or zero.
     */
EnumHandle ExistEnumObj(object)
    char *object;
    {  
    return((EnumHandle)*findobj(object));
}
    
    /*
     *  char *NextEnumObj(&handle)
     *
     */
char *NextEnumObj(objpp)
    register EE **objpp;
{
    *objpp = (*objpp == NULL)? ObjHead: (*objpp)->ee_next;
    return((*objpp == NULL)? NULL: Stringp(*objpp));
}   
        
     
    /*
     *  AddEnum(object-name-string, label-string, parmp, length)
     *
     *     parmp is a ptr to a parameter value, with mixed alignment--
     *        right-justified in fullword for length<=4, left-justified
     *        for longer values.
     *
     */
AddEnum(objname, label, parmp, length)
    char *objname, *label;
    char *parmp;
    int   length;
    {
    register EE *eep, **hp;
    register u_int32 t;  /* Temp for hash func */
    EE *objp;
    int n, objno;
    union v_union Value;
        
        /* Look up obj name, and add it to list if it is
         *   not there already.
         */ 
    hp = &ObjHead;
    while (eep = *hp) {
        if (!strcmp(Stringp(eep), objname))  break;
        hp = &eep->ee_next;
    }
    
    if (*hp == NULL) {
        n = strlen(objname) + 1;
        if (NULL == (eep = (EE *) getspace(n + sizeof(EE)))) {
            return;
        }
        *hp = eep;  /* Append to end of list */
        eep->ee_next = eep->ee_optr = NULL;
        
        memcpy(Stringp(eep), objname, n);
        eep->ee_objno = ++ObjNumber;
        eep->ee_lng = 0;
    }
    objno =  (objp = *hp)->ee_objno;
    
        /*
         *    Now hash the label, using subset of hash table
         *      for this object...
         *
         */
    ZERO_UNION(&Value);
    memcpy(&Value.v_byte[0], parmp, length);    
    hp = &hasht[ hashfunc( SUM_UNION(&Value)+objno ) ];
    
    while (eep = *hp) {
        if ( objno == eep->ee_objno
                 && !memcmp(Value.v_byte, Valp(eep), length) ) 
            break;  /* Override duplicate label for this object */
        hp = &eep->ee_next;
    }
    n = strlen(label)+1;
    if (NULL == (eep = (EE *) getspace(n + length + sizeof(EE)))) {
        return;
    }
    eep->ee_next = *hp;
    *hp = eep;          /* splice label entry into/onto hash chain */
    
    eep->ee_optr =  objp->ee_optr;
    objp->ee_optr = eep; /* append to all-label chain for this object */
    eep->ee_objno = objno;
    
    memcpy(Valp(eep), Value.v_byte, eep->ee_lng = length);
    memcpy(Stringp(eep), label, n);
} /* AddEnum() */


    /*
     *  int GetEnumVal(enum-handle, label-string, &result)
     *
     *     Perform reverse mapping: label->value. 
     *     Returns length of value, or zero if label string is not found.
     *
     *     Does LINEAR search (sigh!)
     */
int GetEnumVal(enumh, label, resultp)
    EE     *enumh;
    char   *label;
    char   **resultp;
    {
    register EE *eep = enumh->ee_optr;
        
    while (eep) {
        if (!strcmp(label, Stringp(eep))) {
            bcopy(Valp(eep), resultp, eep->ee_lng);
            return(eep->ee_lng);
        }
        eep = eep->ee_optr;
    }           
return(0);
} /* int GetEnumVal() */


char *getspace(size)
    register int size;
    {
    register char *p;
#ifdef IBMRTPC
    static struct Area *newarea;   /* Bloody compiler bug...*/
#else IBMRTPC
    register struct Area *newarea;
#endif IBMRTPC
    
    /* Round size up to multiple of natural word size of machine */
    size = (~(sizeof(char *)-1)) & ((int)(size)+sizeof(char *)-1);

    if ((AreaCurr == NULL) || (size+Areap > &AreaCurr->Area_area[AREASIZE])) {
            /*
             *   If necessary, buy a new area
             */
        if (NULL == (newarea = (struct Area *) malloc(sizeof(struct Area)))) {
            fprintf(stderr, "Memory Full Error\n");
            return(NULL);
        }
        AreaCurr =  ((AreaCurr)? (AreaCurr->Area_next = newarea):
                                             (AreaHead = newarea));
        Areap = newarea->Area_area;
    }
    p = Areap;
    Areap += size;
    return(p);
} /* char *getspace() */            
                
#ifndef FALSE
#define FALSE   0       /* logical false */
#define TRUE    !FALSE  /* logical true */
#endif
#define boolean int

/*
 *    boolean match( template, string ) 
 *
 *    Do wild-card match of template (which may contain *(s)) and string
 *
 */
boolean
match(templ, label)
    register char *templ, *label;
    {
    char text[MAX_OBJNAME*2],  /* *2 for extra margin of safety */
         *cp;
    int len;
    
    if (strlen(templ) > sizeof(text))
       return(FALSE);
    
    /* before the first '*' */
    text[0] = '\0';
    cp = text;
    while (*templ != '*' && *templ != '\0')
       *cp++ = *templ++;
    *cp = '\0';
    if (strlen(text))
       for (cp=text;*cp != '\0'; cp++)
           if (*cp != *label++)
              return(FALSE);
    for (cp = templ; *cp == '*'; )
       if (*++cp == '\0')
          return(TRUE);
    if (*label == '\0' && *templ == '\0')
       return(TRUE);

    /* after a '*' */
    while (*templ != '\0') {
        text[0] = '\0';
        cp = text;
        templ++; /* bypass '*' */
        while (*templ != '*' && *templ != '\0')
           *cp++ = *templ++;
        *cp = '\0';
        if (len = strlen(text))
           for (cp = label; ; ) {
              if (!strncmp(text, cp, len)) /* they are equal */
                 {
                 label = cp + len;
                 break;
                 }
              if (strlen(++cp) < len)
                 return(FALSE);
              }
        for (cp = templ; *cp == '*'; )
           if (*++cp == '\0')
              return(TRUE);
        if (*label == '\0' && *templ == '\0')
               return(TRUE);
        }
    return(FALSE);
} /* match */
