You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
468 lines
10 KiB
468 lines
10 KiB
/*
|
|
* Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of version 2 of the GNU General Public License as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it would be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
*
|
|
* Further, this software is distributed without any warranty that it is
|
|
* free of the rightful claim of any third person regarding infringement
|
|
* or the like. Any license provided herein, whether implied or
|
|
* otherwise, applies only to this software file. Patent licenses, if
|
|
* any, provided herein do not apply to combinations of this program with
|
|
* other software, or any other product whatsoever.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
|
|
* Mountain View, CA 94043, or:
|
|
*
|
|
* http://www.sgi.com
|
|
*
|
|
* For further information regarding this notice, see:
|
|
*
|
|
* http://oss.sgi.com/projects/GenInfo/NoticeExplan/
|
|
*
|
|
*/
|
|
/* $Id: symbol.c,v 1.4 2002/05/28 16:26:16 robbiew Exp $ */
|
|
/*
|
|
* Generic Symbol Table
|
|
*
|
|
* This is intended to look a lot like ndbm, except that all information
|
|
* is kept in memory, and a multi-key, hierarchical access mode is provided.
|
|
* This is largely patterned after the Berkeley "DB" package.
|
|
*
|
|
* Requirements
|
|
*
|
|
* - "key" is ASCII (a C string, in fact)
|
|
*
|
|
* Symbol Table Structure
|
|
*
|
|
* Two data structures:
|
|
* Symbol Table Header
|
|
* Symbol Table Node
|
|
*
|
|
* A symbol table header consists of a magic number, a pointer to
|
|
* the first node in the symbol table, and a cursor that is used
|
|
* when sequentialy stepping thru the entire list.
|
|
*
|
|
* Symbol table nodes contain a pointer to a key, a pointer to this
|
|
* key's data, and a pointer to the next node in the chain.
|
|
* Note that to create a hierarchical symbol table, a node is created
|
|
* whose data points to a symbol table header.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include "symbol.h"
|
|
#include "splitstr.h"
|
|
|
|
#define SYM_MAGIC 0xbadc0de
|
|
|
|
/*
|
|
* Some functions can report an error message by assigning it to this
|
|
* string.
|
|
*/
|
|
|
|
static char *sym_error = NULL;
|
|
|
|
/*
|
|
* Memory Allocators
|
|
*
|
|
* newsym() allocates a new symbol table header node
|
|
* mknode(...) allocates a new symbol table entry
|
|
*/
|
|
|
|
SYM newsym(void)
|
|
{
|
|
SYM h;
|
|
|
|
if ((h = malloc(sizeof(struct symh))) == NULL) {
|
|
sym_error = "sym header malloc failed!";
|
|
return (NULL);
|
|
}
|
|
|
|
h->magic = SYM_MAGIC;
|
|
h->sym = NULL;
|
|
h->cursor = NULL;
|
|
return (h);
|
|
}
|
|
|
|
static struct sym *mknode(struct sym *next, char *key, void *data)
|
|
{
|
|
struct sym *n;
|
|
|
|
if ((n = malloc(sizeof(struct sym))) == NULL) {
|
|
sym_error = "sym node malloc failed!";
|
|
return (NULL);
|
|
}
|
|
|
|
n->next = next;
|
|
n->key = strdup(key);
|
|
n->data = data;
|
|
|
|
if (n->key == NULL) {
|
|
sym_error = "sym node strdup(key) failed!";
|
|
free(n);
|
|
return (NULL);
|
|
}
|
|
return (n);
|
|
}
|
|
|
|
/*
|
|
* Search for a key in a single-level symbol table hierarchy.
|
|
*/
|
|
static struct sym *find_key1(struct sym *sym, char *key)
|
|
{
|
|
while (sym != NULL)
|
|
if (strcmp(sym->key, key) == 0)
|
|
return (sym);
|
|
else
|
|
sym = sym->next;
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Create a new key node, add it to the *end* of this list
|
|
*/
|
|
static int add_key(SYM sym, char *key, void *data)
|
|
{
|
|
register struct sym *sn;
|
|
|
|
if (sym->sym == NULL) {
|
|
sym->sym = mknode(NULL, key, data);
|
|
if (sym->sym == NULL) {
|
|
return (-1);
|
|
}
|
|
} else {
|
|
for (sn = sym->sym; sn != NULL && sn->next != NULL;
|
|
sn = sn->next) ;
|
|
sn->next = mknode(NULL, key, data);
|
|
assert(sn->next != NULL);
|
|
if (sn->next == NULL)
|
|
return (-1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Create a new symbol table
|
|
*/
|
|
SYM sym_open(int flags, int mode, int openinfo)
|
|
{
|
|
return (newsym());
|
|
}
|
|
|
|
/*
|
|
* Add (key, data) to an existing symbol table
|
|
*
|
|
* If the key does not exist, a new key is added to the end of the list.
|
|
* If the key exists and the PUT_REPLACE flag is not supplied, return EEXIST.
|
|
* If a symbol table entry in a multi-part key is not a symbol table (i.e.
|
|
* element two of a three or more element key), return ENOTDIR.
|
|
*
|
|
* "data" is not duplicated and must not point to a static area that could
|
|
* go away before the element is deleted (such as a local string in a
|
|
* function)
|
|
*
|
|
* "key" is duplicated as needed, and is not modified.
|
|
*
|
|
* Code:
|
|
* chop up key on commas
|
|
*
|
|
* search until a key element isn't found in the key tree, the key list is
|
|
* exhausted, or a key's data element is not a sub-tree
|
|
*
|
|
* if the key list is exhausted, return a "duplicate entry" error
|
|
*
|
|
* if the last found key's data element is not a sub-tree, return
|
|
* something like "ENOTDIR".
|
|
*
|
|
* add new keys for sub-trees until key list is exhausted;
|
|
* last node gets 'data'.
|
|
*
|
|
*/
|
|
int sym_put(SYM sym, char *key, void *data, int flags)
|
|
{
|
|
const char **keys; /* key split into a 2d string array */
|
|
char **kk;
|
|
char *nkey; /* copy of 'key' -- before split */
|
|
SYM csym, ncsym; /* search: current symbol table */
|
|
struct sym *nsym = NULL; /* search: found symbol entry */
|
|
|
|
if (sym == NULL)
|
|
return (EINVAL);
|
|
|
|
nkey = strdup(key);
|
|
keys = splitstr(key, ",", NULL);
|
|
|
|
if (keys == NULL) {
|
|
free(nkey);
|
|
return (EINVAL);
|
|
}
|
|
|
|
for (kk = (char **)keys, csym = sym;
|
|
*kk != NULL && (nsym = find_key1(csym->sym, *kk)) != NULL;
|
|
csym = nsym->data) {
|
|
|
|
if (*++kk == NULL)
|
|
break;
|
|
|
|
if (nsym->data == NULL) { /* fatal error */
|
|
free(nkey);
|
|
splitstr_free(keys);
|
|
return (ENOTDIR);
|
|
}
|
|
if (((SYM) (nsym->data))->magic != SYM_MAGIC) {
|
|
free(nkey);
|
|
splitstr_free(keys);
|
|
return (ENOTDIR);
|
|
}
|
|
}
|
|
|
|
if (*kk == NULL) { /* found a complete match */
|
|
free(nkey);
|
|
splitstr_free(keys);
|
|
|
|
if (flags == PUT_REPLACE) {
|
|
nsym->data = data;
|
|
return (0);
|
|
} else {
|
|
return (EEXIST);
|
|
}
|
|
}
|
|
|
|
/* csym is a ptr to a list */
|
|
for (; *kk != NULL; kk++) {
|
|
if (*(kk + 1) != NULL) {
|
|
add_key(csym, *kk, (void *)(ncsym = newsym()));
|
|
csym = ncsym;
|
|
} else {
|
|
add_key(csym, *kk, data); /* last key */
|
|
}
|
|
}
|
|
|
|
free(nkey);
|
|
splitstr_free(keys);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Retrieve a Symbol's Contents
|
|
*
|
|
* "key" is not modified.
|
|
* If the key cannot be found, NULL is returned
|
|
*/
|
|
void *sym_get(SYM sym, char *key)
|
|
{
|
|
char *nkey;
|
|
const char **keys; /* key split into a 2d string array */
|
|
char **kk;
|
|
SYM csym; /* search: current symbol table */
|
|
struct sym *nsym = NULL; /* search: found symbol entry */
|
|
|
|
if (sym == NULL)
|
|
return (NULL);
|
|
|
|
nkey = strdup(key);
|
|
keys = splitstr(nkey, ",", NULL);
|
|
if (keys == NULL)
|
|
return (NULL);
|
|
|
|
for (kk = (char **)keys, csym = sym;
|
|
*kk != NULL && (nsym = find_key1(csym->sym, *kk)) != NULL;
|
|
csym = nsym->data) {
|
|
|
|
if (*++kk == NULL)
|
|
break;
|
|
|
|
if (nsym->data == NULL) { /* fatal error */
|
|
free(nkey);
|
|
splitstr_free(keys);
|
|
return (NULL);
|
|
}
|
|
if (((SYM) (nsym->data))->magic != SYM_MAGIC) {
|
|
free(nkey);
|
|
splitstr_free(keys);
|
|
return (NULL);
|
|
}
|
|
}
|
|
|
|
if (*kk == NULL) { /* found a complete match */
|
|
splitstr_free(keys);
|
|
free(nkey);
|
|
return (nsym->data);
|
|
} else {
|
|
splitstr_free(keys);
|
|
free(nkey);
|
|
return (NULL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Step thru a symbol table list
|
|
*
|
|
* The cursor must be set by R_CURSOR, R_FIRST before using R_NEXT.
|
|
* NULL is returned when no more items are available.
|
|
*/
|
|
int sym_seq(SYM sym, DBT * key, DBT * data, int flags)
|
|
{
|
|
SYM csym;
|
|
|
|
switch (flags) {
|
|
/*
|
|
* A number of ways to do this:
|
|
* specificly: sym_seq( .., "key,key") sets to Nth element of the 2nd
|
|
* level symbol table
|
|
* sym_seq(.., "key,key,") sets to the first element of the 3rd
|
|
* level symbol table
|
|
*
|
|
* sym_seq(.., "key,key") where both must be complete keys, sets
|
|
* cursor to the first element of the 3rd level symbol table;
|
|
* if there is no 3rd level, return an error.
|
|
*/
|
|
case R_CURSOR:
|
|
csym = (SYM) sym_get(sym, (char *)key->data);
|
|
if (csym == NULL || csym->magic != SYM_MAGIC) {
|
|
return (2);
|
|
}
|
|
sym->cursor = csym->sym;
|
|
if (sym->cursor == NULL)
|
|
return (1);
|
|
key->data = sym->cursor->key;
|
|
data->data = sym->cursor->data;
|
|
|
|
return (0);
|
|
|
|
case R_FIRST:
|
|
sym->cursor = sym->sym;
|
|
if (sym->cursor == NULL)
|
|
return (1);
|
|
key->data = sym->cursor->key;
|
|
data->data = sym->cursor->data;
|
|
|
|
return (0);
|
|
|
|
case R_NEXT:
|
|
if (sym->cursor == NULL)
|
|
return (1);
|
|
sym->cursor = sym->cursor->next;
|
|
|
|
if (sym->cursor == NULL)
|
|
return (1);
|
|
|
|
key->data = sym->cursor->key;
|
|
data->data = sym->cursor->data;
|
|
|
|
return (0);
|
|
|
|
case R_LAST:
|
|
case R_PREV:
|
|
default:
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Dump a symbol table's keys.
|
|
* Handles hierarchies, using a double quote to indicate depth, one
|
|
* double quote for each level.
|
|
*/
|
|
int sym_dump(SYM sym, int depth)
|
|
{
|
|
|
|
register struct sym *se; /* symbol entry */
|
|
register int d;
|
|
|
|
if (sym == NULL || sym->magic != SYM_MAGIC)
|
|
return -1;
|
|
|
|
for (se = sym->sym; se != NULL; se = se->next) {
|
|
for (d = 0; d < depth; d++) {
|
|
putchar('"');
|
|
putchar(' ');
|
|
}
|
|
printf("%s\n", se->key);
|
|
sym_dump((SYM) se->data, depth + 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* sym dump, but data is _always_ a string (print it)
|
|
*/
|
|
int sym_dump_s(SYM sym, int depth)
|
|
{
|
|
|
|
register struct sym *se; /* symbol entry */
|
|
register int d;
|
|
|
|
if (sym == NULL)
|
|
return 0;
|
|
|
|
if (sym->magic != SYM_MAGIC) {
|
|
for (d = 0; d < depth; d++) {
|
|
putchar('"');
|
|
putchar(' ');
|
|
}
|
|
printf(" = %s\n", (char *)sym);
|
|
return 0;
|
|
}
|
|
|
|
for (se = sym->sym; se != NULL; se = se->next) {
|
|
for (d = 0; d < depth; d++) {
|
|
putchar('"');
|
|
putchar(' ');
|
|
}
|
|
printf("%s", se->key);
|
|
if (((SYM) se->data)->magic == SYM_MAGIC) {
|
|
putchar('\n');
|
|
sym_dump_s((SYM) se->data, depth + 1);
|
|
} else {
|
|
printf("(%p) = %s (%p)\n", se->key, (char *)se->data,
|
|
se->data);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Remove an entire symbol table (done bottom up)
|
|
*/
|
|
int sym_rm(SYM sym, int flags)
|
|
{
|
|
register struct sym *se, *nse; /* symbol entry */
|
|
|
|
if (sym == NULL)
|
|
return 0;
|
|
|
|
if (sym->magic != SYM_MAGIC) {
|
|
if (!(flags & RM_DATA))
|
|
free(sym);
|
|
return 0;
|
|
}
|
|
|
|
for (se = sym->sym; se != NULL;) {
|
|
sym_rm((SYM) se->data, flags);
|
|
nse = se->next;
|
|
if (flags & RM_KEY)
|
|
free(se->key);
|
|
if (flags & RM_DATA)
|
|
free(se->data);
|
|
free(se);
|
|
se = nse;
|
|
}
|
|
if (!(flags & RM_DATA))
|
|
free(sym);
|
|
return 0;
|
|
}
|