home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume26
/
tripwire
/
part03
/
tripwire-1.0
/
src
/
list.c
Wrap
C/C++ Source or Header
|
1993-04-19
|
17KB
|
759 lines
#ifndef lint
static char rcsid[] = "$Id: list.c,v 1.3 92/11/03 04:49:45 genek Exp $";
#endif
/*
* list.c
*
* generic linked list routines.
*
* These routines generally use a (struct list **) as an argument
* (ie: a pointer to a pointer to the head of the list). This way,
* a NULL list pointer will automatically be malloc()'d into existence.
*
* These routines started as extremely simple routines. Unfortunately, the
* O(n) search times made Tripwire extremely slow. So, v3.1 of
* the linked list routines incorporate a hash table into each of
* the list structures. *whew* It's faster, but it's not simple
* anymore. (The addition of back pointers didn't help either...)
*
* Why? Well, we need to preserve order for the list of generated files.
* So, a hash table won't do, and a simple linked list is too slow.
*
* However, the neat thing is that the object-oriented nature of
* the list routines is preserved.
*
* Gene Kim
* Purdue University
*/
#include "../include/config.h"
#include <stdio.h>
#ifdef STDLIBH
#include <stdlib.h>
#endif
#include <assert.h>
#ifdef MALLOCH
# include <malloc.h>
#endif
#ifdef STRINGH
#include <string.h>
#else
#include <strings.h>
#endif
#include "../include/list.h"
/* prototypes */
static unsigned int string_hash ();
static int listdebug = 0;
#define LISTDEBUG(x) if (listdebug >= (x))
/*
* list_set(pc_name, pc_value, prioirty, pp_list)
*
* insert structure with (name=pc_name) and (value=pc_value)
* into the specified list
*/
void
list_set(pc_name, pc_value, priority, pp_list)
int priority;
char *pc_name, *pc_value;
struct list **pp_list;
{
struct list_elem *p;
struct list_hash *phash, *qhash;
int clobber = 0;
struct list_elem *head;
unsigned int hindex; /* hash index */
struct list_hash *hentry;
/* get the hash value */
hindex = string_hash(pc_name);
/* were we handed a NULL list pointer? */
if (*pp_list == NULL)
goto INSERT;
else
head = (*pp_list)->p_head;
hentry = &( ((*pp_list)->hashtable)[hindex]);
/*
* 1) if pc_name is already in the list, then we compare priority
* levels. replace only if new priority is higher than
* existing priority.
*
* 2) if pc_name is not on the list, then we just add it to the
* end of the list
*/
/* walk through hash chain */
for (phash = hentry; phash && phash->used; qhash = phash,
phash = phash->next) {
if (strcmp(phash->key, pc_name) == 0) {
/*
* if existing priority is equal or less than this one,
* then go ahead and clobber it.
*/
p = phash->lptr;
if (p->priority <= priority) {
LISTDEBUG(10)
fprintf(stderr, "list_set(): '%s' variable already found. Clobbering...\n",
pc_name);
clobber++;
goto INSERT;
} /* end if clobber */
else {
LISTDEBUG(10)
fprintf(stderr, "list_set(): variable already found, but not clobbering...\n");
return;
} /* end if not clobber */
}
}
/* back up one if we're not at head of chain */
if (phash == (struct list_hash *) NULL)
phash = qhash;
INSERT:
/* insert the element into the list */
/* do we first need to create the list object? */
if (*pp_list == NULL) {
int i;
struct list *pl;
/* create the list structure pointer */
if ((pl = *pp_list = (struct list *) malloc(sizeof(struct list)))
== NULL) {
fprintf(stderr, "list_insert(): malloc() failed!\n");
exit(1);
}
/* create the pointers inside the structure */
if ((p = (struct list_elem *) malloc(sizeof(struct list_elem)))
== NULL) {
fprintf(stderr, "list_insert(): malloc() failed!\n");
exit(1);
}
/* cauterize the prev pointer */
p->prev = NULL;
p->varname = NULL;
/* attach to list structure */
pl->p_head = head = pl->p_tail = p;
head->next = NULL;
/* initialize the hash table */
for (i = 0; i < LIST_HASHSZ; i++) {
hentry = &( ((*pp_list)->hashtable)[i]);
hentry->key = (char *) NULL;
hentry->used = 0;
hentry->lptr = (struct list_elem *) NULL;
hentry->next = (struct list_hash *) NULL;
}
/* get correct hash bucket */
hentry = &( ((*pp_list)->hashtable)[hindex]);
}
else if (clobber) {
free(p->varname);
free(p->varvalue);
}
else
{
/* add a list entry at tail of list */
p = (*pp_list)->p_tail;
if ((p->next = (struct list_elem *)
malloc(sizeof(struct list_elem))) == NULL) {
fprintf(stderr, "list_insert(): malloc() failed!\n");
exit(1);
}
/* attach the prev pointer */
p->next->prev = p;
/* now the rest */
p = p->next;
p->next = NULL;
/* bind to tail */
(*pp_list)->p_tail = p;
if (phash->used) {
/* now create the hash chain entry */
/* do we need a new structure to chain on? */
if ((qhash = (struct list_hash *) malloc(sizeof(struct list_hash)))
== NULL) {
fprintf(stderr, "list_insert(): malloc() failed!\n");
exit(1);
}
qhash->used = 0;
qhash->next = NULL;
phash->next = qhash;
hentry = phash = qhash;
}
}
/* start filling in fields */
if ((p->varname = (char *) malloc((unsigned) (strlen(pc_name) + 1)))
== NULL) {
fprintf(stderr, "list_insert(): malloc() failed!\n");
exit(1);
}
(void) strcpy(p->varname, pc_name);
if ((p->varvalue = (char *) malloc((unsigned) (strlen(pc_value) + 1)))
== NULL) {
fprintf(stderr, "list_insert(): malloc() failed!\n");
exit(1);
}
(void) strcpy(p->varvalue, pc_value);
p->flag = 0;
p->priority = 0;
/* fill in hash chain structure */
hentry->key = p->varname;
if (hentry->used == 0)
hentry->used++;
assert(hentry->used == 1);
hentry->lptr = p;
hentry->next = NULL;
return;
}
/*
* char *
* list_lookup(pc_name, pp_list)
*
* return the string value assigned to the environment value named
* pc_name in the specified list.
*
* you must copy the contents of the (char *).
*/
char *
list_lookup(pc_name, pp_list)
char *pc_name;
struct list **pp_list;
{
struct list_elem *p;
struct list_hash *phash;
char *s;
unsigned int hindex;
struct list_hash *hentry;
/*
* 1) if *pp_list is NULL, then we know it's emtpy
* 2) if it's not in the hash table, then return NULL
* 3) search hash table chain
*/
/* check for empty list */
if (*pp_list == NULL) {
return NULL;
}
/* look in hash table */
hindex = string_hash(pc_name);
hentry = &(((*pp_list)->hashtable)[hindex]);
if (hentry->used == 0) {
return NULL;
}
/* now search through hash chain */
for (phash = hentry; phash; phash = phash->next) {
if (strcmp(phash->key, pc_name) == 0) {
p = phash->lptr;
/*
s = (char *) malloc((unsigned) (strlen(p->varvalue) + 1));
(void) strcpy(s, p->varvalue);
*/
s = p->varvalue;
return s;
}
}
return NULL;
}
/*
* int
* list_isthere(pc_name, pp_list)
*
* returns (1) if pc_name is in the specified list.
* else returns (0).
*/
int
list_isthere(pc_name, pp_list)
char *pc_name;
struct list **pp_list;
{
struct list_hash *phash;
unsigned int hindex;
struct list_hash *hentry;
/*
* 1) if *pp_list is NULL, then we know it's emtpy
* 2) if it's not in the hash table, then return NULL
* 3) search hash table chain
*/
/* check for empty list */
if (*pp_list == NULL) {
return 0;
}
/* look in hash table */
hindex = string_hash(pc_name);
hentry = &(((*pp_list)->hashtable)[hindex]);
if (hentry->used == 0) {
return 0;
}
/* now search through hash chain */
for (phash = hentry; phash; phash = phash->next) {
if (strcmp(phash->key, pc_name) == 0) {
return 1;
}
}
return 0;
}
/*
* list_unset(pc_name, pp_list)
* remove the list entry with (varname == pcname) from the
* environment
*/
void
list_unset(pc_name, pp_list)
char *pc_name;
struct list **pp_list;
{
struct list_hash *phash, *qhash = (struct list_hash *) NULL;
struct list_elem *plist;
unsigned int hindex;
struct list_hash *hentry;
if (*pp_list == NULL)
return;
/*
* 1) if pc_name isn't found in the hash chain, return
* 2) if found, remove the element from the list, and then remove
* from hash chain.
* check to see if we're the only element on the hash chain,
* too.
*/
/* look in hash table */
hindex = string_hash(pc_name);
hentry = &(((*pp_list)->hashtable)[hindex]);
/* if not in hash table, return */
if (hentry->used == 0) {
LISTDEBUG(0)
fprintf(stderr, "list_unset(): couldn't find '%s' in environment\n", pc_name);
return;
}
/* find the element, but playing pointer tag w/two pointers */
for (phash = hentry; phash; qhash = phash, phash = phash->next) {
assert(qhash == NULL || qhash->next == phash);
if (strcmp(phash->key, pc_name) == 0) {
/* remove the element from the list */
plist = phash->lptr;
/* prev->next = this->next
* next->prev = this->prev
*/
/* are we at the head of the list? */
if (plist->prev) {
plist->prev->next = plist->next;
}
/* are we at the end of the list? */
if (plist->next) {
plist->next->prev = plist->prev;
}
free((char *) plist);
/* now remove from hash chain */
/* if it was at top of list */
if (qhash == NULL) {
hentry->used = 0;
hentry->next = (struct list_hash *) NULL;
} else {
qhash->next = phash->next;
free((char *) phash);
}
return;
}
}
return;
}
/*
* list_setflag(pc_name, flag, pp_list)
*
* OR the the specified flag to the existing flag value.
*/
int
list_setflag(pc_name, flag, pp_list)
char *pc_name;
int flag;
struct list **pp_list;
{
struct list_elem *plist;
struct list_hash *phash, *hentry;
unsigned int hindex;
if (*pp_list == NULL)
return -1;
/*
* 1) look in hash table for entry. if not found, return with error.
* 2) walk down hash chain until entry is found, then modify the
* list entry
*/
/* look in hash table */
hindex = string_hash(pc_name);
hentry = &(((*pp_list)->hashtable)[hindex]);
/* walk down chain */
for (phash = hentry; phash && phash->used; phash = phash->next) {
if (strcmp(phash->key, pc_name) == 0) {
plist = phash->lptr;
plist->flag |= flag;
return 0;
}
}
return 0;
}
/*
* list_getflag(pc_name, pp_list)
* return the flag value embedded in structure.
*/
int
list_getflag(pc_name, pp_list)
char *pc_name;
struct list **pp_list;
{
struct list_elem *plist;
struct list_hash *phash, *hentry;
unsigned int hindex;
if (*pp_list == NULL)
return -1;
/*
* 1) look in hash table for entry. if not found, return with error.
* 2) walk down hash chain until entry is found, then modify the
* list entry
*/
/* look in hash table */
hindex = string_hash(pc_name);
hentry = &(((*pp_list)->hashtable)[hindex]);
/* walk down chain */
for (phash = hentry; phash && phash->used; phash = phash->next) {
if (strcmp(phash->key, pc_name) == 0) {
plist = phash->lptr;
return plist->flag;
}
}
return -1;
}
/*
* list_print()
* print out the entire contents of the linked list
*/
void
list_print(pp_list)
struct list **pp_list;
{
struct list_elem *p;
struct list_elem *head;
/* check to see if list is empty */
if (*pp_list == NULL)
return;
head = (*pp_list)->p_head;
/* walk down entire list */
for (p = head; p; p = p->next) {
printf("%-40s\t%20s %d\n", p->varname, p->varvalue, p->flag);
}
return;
}
/*
* list_reset()
*
* given a pointer to a list, delete the entire list, and set the
* pointer to NULL;
*/
void
list_reset (pp_list)
struct list **pp_list;
{
struct list_elem *p, *q;
struct list_hash *phash, *qhash;
int i;
if (*pp_list == NULL)
return;
/* walk down the list, deleting the element that we just came from */
for (p = (*pp_list)->p_head; p; q = p, p = p->next, free((char *) q)) ;
/* walk down the hash table, and remove its hash chain */
for (i = 0; i < LIST_HASHSZ; i++) {
phash = &(((*pp_list)->hashtable)[i]);
if (phash->used) {
/* don't delete the first entry! it's static! */
for (phash = phash->next; phash; qhash = phash,
phash = qhash->next, free((char *) qhash)) ;
}
}
/* now free up the list structure */
free((char *) *pp_list);
/* now invalidate the list structure pointer */
*pp_list = NULL;
return;
}
/*
* list_init ()
* list_open (struct list **pp_list)
* list_get (struct list **pp_list)
* list_close(struct list **pp_list)
*
* this allows the retrieval of individual list elements through
* successive calls to list_get().
*
* 0) list_init() initializes the tables.
* 1) list_open() creates a table entry with *pp_head as the key
* 2) any calls to list_get() will get the next element. the
* index is stored in the table, with *pp_head as the
* key.
* 3) list_close() removes the table entry, with *pp_head as the
* key.
*/
#define LIST_TABLE_SZ 16
struct list_table_entry {
struct list *p_key; /* pointer to head is used as key */
struct list_elem *p_pindex; /* pointer to current element */
};
static struct list_table_entry list_table[LIST_TABLE_SZ];
int
list_init()
{
int i;
struct list_table_entry *p;
/* clear all keys and indexes */
for (i = 0; i < LIST_TABLE_SZ; i++) {
p = &list_table[i];
p->p_key = NULL;
p->p_pindex = NULL;
}
return 0;
}
/*
* list_open(struct list **pp_list)
*
* create a table entry with *pp_head as a key.
* returns (-1) if an entry with the specified key was already
* in the table, else (0).
*/
int
list_open (pp_list)
struct list **pp_list;
{
int i;
struct list_table_entry *p;
/* is the list NULL? */
if (*pp_list == NULL) {
return 0; /* we'll fake it later on */
}
/* is there already an entry with a matching key? is there any
* an empty table entry for us?
*/
for (i = 0; i < LIST_TABLE_SZ; i++) {
p = &list_table[i];
if (p->p_key == NULL)
break;
if (p->p_key == *pp_list)
break;
} /* end for loop */
/*
* return with error if there was a collision. (if the index rolled
* all the way to the top, and its index value was non-null, then
* we overflowed the table.)
*/
if (i == LIST_TABLE_SZ && p->p_key != NULL)
return -1;
/*
* create the table entry. assertion: p already points to an empty
* table entry. Have index point to the head.
*/
p->p_key = *pp_list;
p->p_pindex = (*pp_list)->p_head;
return 0;
}
/*
* struct list_elem *
* list_get(struct list **pp_list)
*
* get the next entry in the specified list (using *pp_list as the key),
* and bump the internal pointer to the next element, ready
* for the next call to list_get().
* we return NULL if we're sitting on the tail end of the list.
*/
struct list_elem *
list_get (pp_list)
struct list **pp_list;
{
int i;
struct list_table_entry *p, *q;
struct list_elem *p_elem;
/* fake it if you pass it a NULL */
if (*pp_list == NULL) {
return NULL;
}
/* find entry with matching key */
for (i = 0; i < LIST_TABLE_SZ; i++) {
p = &list_table[i];
if (p->p_key == *pp_list)
break;
} /* end for loop */
/* bounds checking. if we rolled through the entire array, then
* we never found the key!
*/
if (i == LIST_TABLE_SZ)
return NULL;
/* are we already at the end of the list? */
if (p->p_pindex == NULL)
return NULL;
/* if not, return a pointer to the current list element, and increment
* the table pointer.
*/
p_elem = p->p_pindex;
q = p;
q->p_pindex = q->p_pindex->next; /* walk through pointer */
return p_elem;
}
/*
* list_close(struct list **pp_list)
*
* remove the table entry with (*pp_list) as the key.
* return -1 if entry not found. else 0.
*/
int
list_close (pp_list)
struct list **pp_list;
{
int i;
struct list_table_entry *p;
/* fake it if you pass it a NULL */
if (*pp_list == NULL) {
return 0;
}
/* find entry with matching key */
for (i = 0; i < LIST_TABLE_SZ; i++) {
p = &list_table[i];
if (p->p_key == *pp_list)
break;
} /* end for loop */
/* bounds checking. if we rolled through the entire array, then
* we never found the key!
*/
if (i == LIST_TABLE_SZ)
return -1;
/* remove the entry. assertion: p is pointing to our entry */
p->p_key = NULL;
p->p_pindex = NULL;
return 0;
}
static unsigned int
string_hash (string)
char *string;
{
unsigned int hindex;
char *pc = string;
hindex = *pc;
while (*pc) {
hindex = ((hindex << 9) ^ *pc++) % LIST_HASHSZ;
/*
hindex = ((hindex << 7) | (string[i] + len)) % LIST_HASHSZ;
*/
}
return hindex;
}