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