blob: 6303c76ccd229c08a24d1776a7f2e644c97eaf08 [file] [log] [blame]
alaffinf5589902000-09-21 21:35:06 +00001/*
2 * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of version 2 of the GNU General Public License as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it would be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 *
12 * Further, this software is distributed without any warranty that it is
13 * free of the rightful claim of any third person regarding infringement
14 * or the like. Any license provided herein, whether implied or
15 * otherwise, applies only to this software file. Patent licenses, if
16 * any, provided herein do not apply to combinations of this program with
17 * other software, or any other product whatsoever.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write the Free Software Foundation, Inc., 59
21 * Temple Place - Suite 330, Boston MA 02111-1307, USA.
22 *
23 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24 * Mountain View, CA 94043, or:
25 *
26 * http://www.sgi.com
27 *
28 * For further information regarding this notice, see:
29 *
30 * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
31 *
32 */
nstrazd5d51ca2001-02-28 17:41:59 +000033/* $Id: symbol.c,v 1.2 2001/02/28 17:42:00 nstraz Exp $ */
alaffinf5589902000-09-21 21:35:06 +000034/*
35 * Generic Symbol Table
36 *
37 * This is intended to look a lot like ndbm, except that all information
38 * is kept in memory, and a multi-key, hierarchical access mode is provided.
39 * This is largely patterned after the Berkeley "DB" package.
40 *
41 * Requirements
42 *
43 * - "key" is ASCII (a C string, in fact)
44 *
45 * Symbol Table Structure
46 *
47 * Two data structures:
48 * Symbol Table Header
49 * Symbol Table Node
50 *
51 * A symbol table header consists of a magic number, a pointer to
52 * the first node in the symbol table, and a cursor that is used
53 * when sequentialy stepping thru the entire list.
54 *
55 * Symbol table nodes contain a pointer to a key, a pointer to this
56 * key's data, and a pointer to the next node in the chain.
57 * Note that to create a hierarchical symbol table, a node is created
58 * whose data points to a symbol table header.
59 */
60
61#include <stdio.h>
62#include <errno.h>
63#include <stdlib.h>
nstrazd5d51ca2001-02-28 17:41:59 +000064#include <string.h>
alaffinf5589902000-09-21 21:35:06 +000065#include <assert.h>
66#include "symbol.h"
67#include "splitstr.h"
68
69#define SYM_MAGIC 0xbadc0de
70
71/*
72 * Some functions can report an error message by assigning it to this
73 * string.
74 */
75
76static char *sym_error=NULL;
77
78/*
79 * Memory Allocators
80 *
81 * newsym() allocates a new symbol table header node
82 * mknode(...) allocates a new symbol table entry
83 */
84
85SYM newsym()
86{
87 SYM h;
88
89 if((h=(SYM)malloc(sizeof(struct symh))) == NULL) {
90 sym_error="sym header malloc failed!";
91 return(NULL);
92 }
93
94 h->magic = SYM_MAGIC;
95 h->sym = NULL;
96 h->cursor = NULL;
97 return(h);
98}
99
100static struct sym *
101mknode(struct sym *next, char *key, void *data)
102{
103 struct sym *n;
104 char *strdup();
105
106 if((n=(struct sym *)malloc(sizeof(struct sym))) == NULL) {
107 sym_error="sym node malloc failed!";
108 return(NULL);
109 }
110
111 n->next = next;
112 n->key = strdup(key);
113 n->data = data;
114
115 if(n->key == NULL) {
116 sym_error="sym node strdup(key) failed!";
117 return(NULL);
118 }
119 return(n);
120}
121
122/*
123 * Search for a key in a single-level symbol table hierarchy.
124 */
125static struct sym *
126find_key1(struct sym *sym, char *key)
127{
128 while(sym != NULL)
129 if(strcmp(sym->key, key) == 0)
130 return(sym);
131 else
132 sym=sym->next;
133 return(NULL);
134}
135
136/*
137 * Create a new key node, add it to the *end* of this list
138 */
139static int
140add_key(SYM sym, char *key, void *data)
141{
142 register struct sym *sn;
143
144 if(sym->sym == NULL)
145 {
146 sym->sym = mknode(NULL, key, data);
147 if(sym->sym == NULL)
148 {
149 return(-1);
150 }
151 }
152 else
153 {
154 for( sn=sym->sym; sn!=NULL && sn->next != NULL; sn=sn->next );
155 sn->next = mknode(NULL, key, data);
156 assert(sn->next != NULL);
157 if(sn->next == NULL)
158 return(-1);
159 }
160 return(0);
161}
162
163/*
164 * Create a new symbol table
165 */
166SYM sym_open(int flags, int mode, int openinfo)
167{
168 return(newsym());
169}
170
171
172/*
173 * Add (key, data) to an existing symbol table
174 *
175 * If the key does not exist, a new key is added to the end of the list.
176 * If the key exists and the PUT_REPLACE flag is not supplied, return EEXIST.
177 * If a symbol table entry in a multi-part key is not a symbol table (i.e.
178 * element two of a three or more element key), return ENOTDIR.
179 *
180 * "data" is not duplicated and must not point to a static area that could
181 * go away before the element is deleted (such as a local string in a
182 * function)
183 *
184 * "key" is duplicated as needed, and is not modified.
185 *
186 * Code:
187 * chop up key on commas
188 *
189 * search until a key element isn't found in the key tree, the key list is
190 * exhausted, or a key's data element is not a sub-tree
191 *
192 * if the key list is exhausted, return a "duplicate entry" error
193 *
194 * if the last found key's data element is not a sub-tree, return
195 * something like "ENOTDIR".
196 *
197 * add new keys for sub-trees until key list is exhausted;
198 * last node gets 'data'.
199 *
200 */
201int
202sym_put(SYM sym, char *key, void *data, int flags)
203{
204 const char **keys; /* key split into a 2d string array */
205 char **kk;
206 char *nkey, *strdup(); /* copy of 'key' -- before split */
207 SYM csym, ncsym; /* search: current symbol table */
208 struct sym *nsym; /* search: found symbol entry */
209
210 if(sym == NULL)
211 return(EINVAL);
212
213 nkey = strdup(key);
214 keys = splitstr(key, ",",NULL);
215
216 if(keys == NULL)
217 return(EINVAL);
218
219 for(kk=(char **)keys, csym = sym;
220 *kk != NULL && (nsym=find_key1(csym->sym, *kk)) != NULL;
221 csym=nsym->data) {
222
223 if(*++kk == NULL)
224 break;
225
226 if(nsym->data == NULL) { /* fatal error */
227 free(nkey);
228 splitstr_free(keys);
229 return(ENOTDIR);
230 }
231 if( ((SYM) (nsym->data))->magic != SYM_MAGIC ) {
232 free(nkey);
233 splitstr_free(keys);
234 return(ENOTDIR);
235 }
236 }
237
238 if(*kk == NULL) { /* found a complete match */
239 free(nkey);
240 splitstr_free(keys);
241
242 if(flags == PUT_REPLACE) {
243 nsym->data = data;
244 return(0);
245 } else {
246 return(EEXIST);
247 }
248 }
249
250 /* csym is a ptr to a list */
251 for(;*kk != NULL; kk++) {
252 if(*(kk+1) != NULL) {
253 add_key(csym, *kk, (void *)(ncsym=newsym()));
254 csym = ncsym;
255 } else {
256 add_key(csym, *kk, data); /* last key */
257 }
258 }
259
260 free(nkey);
261 splitstr_free(keys);
262 return(0);
263}
264
265/*
266 * Retrieve a Symbol's Contents
267 *
268 * "key" is not modified.
269 * If the key cannot be found, NULL is returned
270 */
271void * sym_get(SYM sym, char *key)
272{
273 char *nkey, *strdup();
274 const char **keys; /* key split into a 2d string array */
275 char **kk;
276 SYM csym; /* search: current symbol table */
277 struct sym *nsym; /* search: found symbol entry */
278
279 if(sym == NULL)
280 return(NULL);
281
282 nkey=strdup(key);
283 keys = splitstr(nkey, ",", NULL);
284 if(keys == NULL)
285 return(NULL);
286
287 for(kk=(char **)keys, csym = sym;
288 *kk != NULL && (nsym=find_key1(csym->sym, *kk)) != NULL;
289 csym=nsym->data) {
290
291 if(*++kk == NULL)
292 break;
293
294 if(nsym->data == NULL) { /* fatal error */
295 free(nkey);
296 splitstr_free(keys);
297 return(NULL);
298 }
299 if( ((SYM)(nsym->data))->magic != SYM_MAGIC ) {
300 free(nkey);
301 splitstr_free(keys);
302 return(NULL);
303 }
304 }
305
306 if(*kk == NULL) { /* found a complete match */
307 splitstr_free(keys);
308 free(nkey);
309 return(nsym->data);
310 } else {
311 splitstr_free(keys);
312 free(nkey);
313 return(NULL);
314 }
315}
316
317/*
318 * Step thru a symbol table list
319 *
320 * The cursor must be set by R_CURSOR, R_FIRST before using R_NEXT.
321 * NULL is returned when no more items are available.
322 */
323int
324 sym_seq(SYM sym, DBT *key, DBT *data, int flags)
325{
326 SYM csym;
327
328 switch(flags) {
329 /*
330 * A number of ways to do this:
331 * specificly: sym_seq( .., "key,key") sets to Nth element of the 2nd
332 * level symbol table
333 * sym_seq(.., "key,key,") sets to the first element of the 3rd
334 * level symbol table
335 *
336 * sym_seq(.., "key,key") where both must be complete keys, sets
337 * cursor to the first element of the 3rd level symbol table;
338 * if there is no 3rd level, return an error.
339 */
340 case R_CURSOR:
341 csym = (SYM) sym_get(sym, (char *)key->data);
342 if( csym == NULL || csym->magic != SYM_MAGIC ) {
343 return(2);
344 }
345 sym->cursor = csym->sym;
346 if(sym->cursor == NULL)
347 return(1);
348 key->data = sym->cursor->key;
349 data->data = sym->cursor->data;
350
351 return(0);
352
353 case R_FIRST:
354 sym->cursor = sym->sym;
355 if(sym->cursor == NULL)
356 return(1);
357 key->data = sym->cursor->key;
358 data->data = sym->cursor->data;
359
360 return(0);
361
362 case R_NEXT:
363 if(sym->cursor == NULL)
364 return(1);
365 sym->cursor = sym->cursor->next;
366
367 if(sym->cursor == NULL)
368 return(1);
369
370 key->data = sym->cursor->key;
371 data->data = sym->cursor->data;
372
373 return(0);
374
375 case R_LAST:
376 case R_PREV:
377 default:
378 return(-1);
379 }
380}
381
382/*
383 * Dump a symbol table's keys.
384 * Handles hierarchies, using a double quote to indicate depth, one
385 * double quote for each level.
386 */
387int
388sym_dump(SYM sym, int depth)
389{
390
391 register struct sym *se; /* symbol entry */
392 register int d;
393
394 if(sym == NULL || sym->magic != SYM_MAGIC)
395 return -1;
396
397 for(se=sym->sym;se != NULL;se=se->next) {
398 for(d=0;d < depth; d++) {
399 putchar('"'); putchar(' ');
400 }
401 printf("%s\n", se->key);
402 sym_dump((SYM)se->data, depth+1);
403 }
404 return 0;
405}
406
407/*
408 * sym dump, but data is _always_ a string (print it)
409 */
410int
411sym_dump_s(SYM sym, int depth)
412{
413
414 register struct sym *se; /* symbol entry */
415 register int d;
416
417 if(sym == NULL)
418 return 0;
419
420 if(sym->magic != SYM_MAGIC) {
421 for(d=0;d < depth; d++) {
422 putchar('"'); putchar(' ');
423 }
424 printf(" = %s\n", (char *)sym);
425 return 0;
426 }
427
428 for(se=sym->sym;se != NULL;se=se->next) {
429 for(d=0;d < depth; d++) {
430 putchar('"'); putchar(' ');
431 }
432 printf("%s", se->key);
433 if(((SYM)se->data)->magic == SYM_MAGIC) {
434 putchar('\n');
435 sym_dump_s((SYM)se->data, depth+1);
436 } else {
437 printf("(%#x) = %s (%#x)\n", (unsigned int)se->key, (char *)se->data, (unsigned int)se->data);
438 }
439 }
440 return 0;
441}
442
443/*
444 * Remove an entire symbol table (done bottom up)
445 */
446int
447sym_rm(SYM sym, int flags)
448{
449 register struct sym *se, *nse; /* symbol entry */
450
451 if(sym == NULL)
452 return 0;
453
454 if(sym->magic != SYM_MAGIC) {
455 if(!(flags&RM_DATA))
456 free(sym);
457 return 0;
458 }
459
460 for(se=sym->sym;se != NULL;) {
461 sym_rm((SYM)se->data, flags);
462 nse=se->next;
463 if(flags & RM_KEY)
464 free(se->key);
465 if(flags & RM_DATA)
466 free(se->data);
467 free(se);
468 se=nse;
469 }
470 if(!(flags&RM_DATA))
471 free(sym);
472 return 0;
473}