blob: fa9e8aa5812cb7841f4d9dba1ad26a09d443df5a [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 */
robbiewf3a83d52002-05-28 16:26:16 +000033/* $Id: symbol.c,v 1.4 2002/05/28 16:26:16 robbiew 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;
alaffinf5589902000-09-21 21:35:06 +0000104
105 if((n=(struct sym *)malloc(sizeof(struct sym))) == NULL) {
106 sym_error="sym node malloc failed!";
107 return(NULL);
108 }
109
110 n->next = next;
111 n->key = strdup(key);
112 n->data = data;
113
114 if(n->key == NULL) {
115 sym_error="sym node strdup(key) failed!";
116 return(NULL);
117 }
118 return(n);
119}
120
121/*
122 * Search for a key in a single-level symbol table hierarchy.
123 */
124static struct sym *
125find_key1(struct sym *sym, char *key)
126{
127 while(sym != NULL)
128 if(strcmp(sym->key, key) == 0)
129 return(sym);
130 else
131 sym=sym->next;
132 return(NULL);
133}
134
135/*
136 * Create a new key node, add it to the *end* of this list
137 */
138static int
139add_key(SYM sym, char *key, void *data)
140{
141 register struct sym *sn;
142
143 if(sym->sym == NULL)
144 {
145 sym->sym = mknode(NULL, key, data);
146 if(sym->sym == NULL)
147 {
148 return(-1);
149 }
150 }
151 else
152 {
153 for( sn=sym->sym; sn!=NULL && sn->next != NULL; sn=sn->next );
154 sn->next = mknode(NULL, key, data);
155 assert(sn->next != NULL);
156 if(sn->next == NULL)
157 return(-1);
158 }
159 return(0);
160}
161
162/*
163 * Create a new symbol table
164 */
165SYM sym_open(int flags, int mode, int openinfo)
166{
167 return(newsym());
168}
169
170
171/*
172 * Add (key, data) to an existing symbol table
173 *
174 * If the key does not exist, a new key is added to the end of the list.
175 * If the key exists and the PUT_REPLACE flag is not supplied, return EEXIST.
176 * If a symbol table entry in a multi-part key is not a symbol table (i.e.
177 * element two of a three or more element key), return ENOTDIR.
178 *
179 * "data" is not duplicated and must not point to a static area that could
180 * go away before the element is deleted (such as a local string in a
181 * function)
182 *
183 * "key" is duplicated as needed, and is not modified.
184 *
185 * Code:
186 * chop up key on commas
187 *
188 * search until a key element isn't found in the key tree, the key list is
189 * exhausted, or a key's data element is not a sub-tree
190 *
191 * if the key list is exhausted, return a "duplicate entry" error
192 *
193 * if the last found key's data element is not a sub-tree, return
194 * something like "ENOTDIR".
195 *
196 * add new keys for sub-trees until key list is exhausted;
197 * last node gets 'data'.
198 *
199 */
200int
201sym_put(SYM sym, char *key, void *data, int flags)
202{
203 const char **keys; /* key split into a 2d string array */
204 char **kk;
robbiewf3a83d52002-05-28 16:26:16 +0000205 char *nkey; /* copy of 'key' -- before split */
alaffinf5589902000-09-21 21:35:06 +0000206 SYM csym, ncsym; /* search: current symbol table */
robbiewf3a83d52002-05-28 16:26:16 +0000207 struct sym *nsym = NULL; /* search: found symbol entry */
alaffinf5589902000-09-21 21:35:06 +0000208
209 if(sym == NULL)
210 return(EINVAL);
211
212 nkey = strdup(key);
213 keys = splitstr(key, ",",NULL);
214
215 if(keys == NULL)
216 return(EINVAL);
217
218 for(kk=(char **)keys, csym = sym;
219 *kk != NULL && (nsym=find_key1(csym->sym, *kk)) != NULL;
220 csym=nsym->data) {
221
222 if(*++kk == NULL)
223 break;
224
225 if(nsym->data == NULL) { /* fatal error */
226 free(nkey);
227 splitstr_free(keys);
228 return(ENOTDIR);
229 }
230 if( ((SYM) (nsym->data))->magic != SYM_MAGIC ) {
231 free(nkey);
232 splitstr_free(keys);
233 return(ENOTDIR);
234 }
235 }
236
237 if(*kk == NULL) { /* found a complete match */
238 free(nkey);
239 splitstr_free(keys);
240
241 if(flags == PUT_REPLACE) {
242 nsym->data = data;
243 return(0);
244 } else {
245 return(EEXIST);
246 }
247 }
248
249 /* csym is a ptr to a list */
250 for(;*kk != NULL; kk++) {
251 if(*(kk+1) != NULL) {
252 add_key(csym, *kk, (void *)(ncsym=newsym()));
253 csym = ncsym;
254 } else {
255 add_key(csym, *kk, data); /* last key */
256 }
257 }
258
259 free(nkey);
260 splitstr_free(keys);
261 return(0);
262}
263
264/*
265 * Retrieve a Symbol's Contents
266 *
267 * "key" is not modified.
268 * If the key cannot be found, NULL is returned
269 */
270void * sym_get(SYM sym, char *key)
271{
robbiewf3a83d52002-05-28 16:26:16 +0000272 char *nkey;
alaffinf5589902000-09-21 21:35:06 +0000273 const char **keys; /* key split into a 2d string array */
274 char **kk;
275 SYM csym; /* search: current symbol table */
robbiewf3a83d52002-05-28 16:26:16 +0000276 struct sym *nsym = NULL; /* search: found symbol entry */
alaffinf5589902000-09-21 21:35:06 +0000277
278 if(sym == NULL)
279 return(NULL);
280
281 nkey=strdup(key);
282 keys = splitstr(nkey, ",", NULL);
283 if(keys == NULL)
284 return(NULL);
285
286 for(kk=(char **)keys, csym = sym;
287 *kk != NULL && (nsym=find_key1(csym->sym, *kk)) != NULL;
288 csym=nsym->data) {
289
290 if(*++kk == NULL)
291 break;
292
293 if(nsym->data == NULL) { /* fatal error */
294 free(nkey);
295 splitstr_free(keys);
296 return(NULL);
297 }
298 if( ((SYM)(nsym->data))->magic != SYM_MAGIC ) {
299 free(nkey);
300 splitstr_free(keys);
301 return(NULL);
302 }
303 }
304
305 if(*kk == NULL) { /* found a complete match */
306 splitstr_free(keys);
307 free(nkey);
308 return(nsym->data);
309 } else {
310 splitstr_free(keys);
311 free(nkey);
312 return(NULL);
313 }
314}
315
316/*
317 * Step thru a symbol table list
318 *
319 * The cursor must be set by R_CURSOR, R_FIRST before using R_NEXT.
320 * NULL is returned when no more items are available.
321 */
322int
323 sym_seq(SYM sym, DBT *key, DBT *data, int flags)
324{
325 SYM csym;
326
327 switch(flags) {
328 /*
329 * A number of ways to do this:
330 * specificly: sym_seq( .., "key,key") sets to Nth element of the 2nd
331 * level symbol table
332 * sym_seq(.., "key,key,") sets to the first element of the 3rd
333 * level symbol table
334 *
335 * sym_seq(.., "key,key") where both must be complete keys, sets
336 * cursor to the first element of the 3rd level symbol table;
337 * if there is no 3rd level, return an error.
338 */
339 case R_CURSOR:
340 csym = (SYM) sym_get(sym, (char *)key->data);
341 if( csym == NULL || csym->magic != SYM_MAGIC ) {
342 return(2);
343 }
344 sym->cursor = csym->sym;
345 if(sym->cursor == NULL)
346 return(1);
347 key->data = sym->cursor->key;
348 data->data = sym->cursor->data;
349
350 return(0);
351
352 case R_FIRST:
353 sym->cursor = sym->sym;
354 if(sym->cursor == NULL)
355 return(1);
356 key->data = sym->cursor->key;
357 data->data = sym->cursor->data;
358
359 return(0);
360
361 case R_NEXT:
362 if(sym->cursor == NULL)
363 return(1);
364 sym->cursor = sym->cursor->next;
365
366 if(sym->cursor == NULL)
367 return(1);
368
369 key->data = sym->cursor->key;
370 data->data = sym->cursor->data;
371
372 return(0);
373
374 case R_LAST:
375 case R_PREV:
376 default:
377 return(-1);
378 }
379}
380
381/*
382 * Dump a symbol table's keys.
383 * Handles hierarchies, using a double quote to indicate depth, one
384 * double quote for each level.
385 */
386int
387sym_dump(SYM sym, int depth)
388{
389
390 register struct sym *se; /* symbol entry */
391 register int d;
392
393 if(sym == NULL || sym->magic != SYM_MAGIC)
394 return -1;
395
396 for(se=sym->sym;se != NULL;se=se->next) {
397 for(d=0;d < depth; d++) {
398 putchar('"'); putchar(' ');
399 }
400 printf("%s\n", se->key);
401 sym_dump((SYM)se->data, depth+1);
402 }
403 return 0;
404}
405
406/*
407 * sym dump, but data is _always_ a string (print it)
408 */
409int
410sym_dump_s(SYM sym, int depth)
411{
412
413 register struct sym *se; /* symbol entry */
414 register int d;
415
416 if(sym == NULL)
417 return 0;
418
419 if(sym->magic != SYM_MAGIC) {
420 for(d=0;d < depth; d++) {
421 putchar('"'); putchar(' ');
422 }
423 printf(" = %s\n", (char *)sym);
424 return 0;
425 }
426
427 for(se=sym->sym;se != NULL;se=se->next) {
428 for(d=0;d < depth; d++) {
429 putchar('"'); putchar(' ');
430 }
431 printf("%s", se->key);
432 if(((SYM)se->data)->magic == SYM_MAGIC) {
433 putchar('\n');
434 sym_dump_s((SYM)se->data, depth+1);
435 } else {
robbiew15226cd2002-04-10 16:10:40 +0000436 printf("(%p) = %s (%p)\n", se->key, (char *)se->data, se->data);
alaffinf5589902000-09-21 21:35:06 +0000437 }
438 }
439 return 0;
440}
441
442/*
443 * Remove an entire symbol table (done bottom up)
444 */
445int
446sym_rm(SYM sym, int flags)
447{
448 register struct sym *se, *nse; /* symbol entry */
449
450 if(sym == NULL)
451 return 0;
452
453 if(sym->magic != SYM_MAGIC) {
454 if(!(flags&RM_DATA))
455 free(sym);
456 return 0;
457 }
458
459 for(se=sym->sym;se != NULL;) {
460 sym_rm((SYM)se->data, flags);
461 nse=se->next;
462 if(flags & RM_KEY)
463 free(se->key);
464 if(flags & RM_DATA)
465 free(se->data);
466 free(se);
467 se=nse;
468 }
469 if(!(flags&RM_DATA))
470 free(sym);
471 return 0;
472}