head 1.19; access; symbols LMTP2NNTP_1_4_1:1.19 LMTP2NNTP_1_4_0:1.19 VAL_0_9_4:1.19 VAL_0_9_3:1.18 LMTP2NNTP_1_3_0:1.18 LMTP2NNTP_1_3b2:1.18 LMTP2NNTP_1_3b1:1.18 LMTP2NNTP_1_3a3:1.18 LMTP2NNTP_1_3a2:1.18 LMTP2NNTP_1_3a1:1.18 VAL_0_9_2:1.18 VAL_0_9_1:1.15 LMTP2NNTP_1_2_0:1.14 LMTP2NNTP_1_2b4:1.14 LMTP2NNTP_1_2b3:1.14 LMTP2NNTP_1_2b2:1.14 LMTP2NNTP_1_2b1:1.14 LMTP2NNTP_1_2a8:1.13 LMTP2NNTP_1_2a7:1.13 LMTP2NNTP_1_2a6:1.12 LMTP2NNTP_1_2a5:1.12 VAL_0_9_0:1.12 LMTP2NNTP_1_2a4:1.12 LMTP2NNTP_1_2a3:1.10; locks; strict; comment @ * @; 1.19 date 2005.10.03.07.22.13; author rse; state Exp; branches; next 1.18; 1.18 date 2004.04.04.10.45.29; author rse; state Exp; branches; next 1.17; 1.17 date 2004.04.04.10.40.50; author rse; state Exp; branches; next 1.16; 1.16 date 2003.07.31.07.29.51; author thl; state Exp; branches; next 1.15; 1.15 date 2003.02.17.14.35.25; author rse; state Exp; branches; next 1.14; 1.14 date 2003.02.06.16.58.40; author thl; state Exp; branches; next 1.13; 1.13 date 2003.02.02.14.22.43; author rse; state Exp; branches; next 1.12; 1.12 date 2002.03.13.18.41.30; author rse; state Exp; branches; next 1.11; 1.11 date 2002.03.13.18.35.58; author rse; state Exp; branches; next 1.10; 1.10 date 2002.01.30.19.31.32; author rse; state Exp; branches; next 1.9; 1.9 date 2002.01.30.18.55.23; author rse; state Exp; branches; next 1.8; 1.8 date 2002.01.24.15.30.58; author rse; state Exp; branches; next 1.7; 1.7 date 2002.01.18.18.12.33; author rse; state Exp; branches; next 1.6; 1.6 date 2002.01.17.12.19.09; author rse; state Exp; branches; next 1.5; 1.5 date 2002.01.16.20.32.23; author rse; state Exp; branches; next 1.4; 1.4 date 2002.01.16.20.24.09; author rse; state Exp; branches; next 1.3; 1.3 date 2002.01.16.16.10.34; author rse; state Exp; branches; next 1.2; 1.2 date 2002.01.16.14.17.56; author rse; state Exp; branches; next 1.1; 1.1 date 2002.01.09.10.44.28; author rse; state Exp; branches; next ; desc @@ 1.19 log @Bumped year in copyright messages for new year 2005 @ text @/* ** OSSP val - Value Access ** Copyright (c) 2002-2005 Ralf S. Engelschall ** Copyright (c) 2002-2005 The OSSP Project ** Copyright (c) 2002-2005 Cable & Wireless ** ** This file is part of OSSP val, a value access library which ** can be found at http://www.ossp.org/pkg/lib/val/. ** ** Permission to use, copy, modify, and distribute this software for ** any purpose with or without fee is hereby granted, provided that ** the above copyright notice and this permission notice appear in all ** copies. ** ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED ** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** ** val.c: library implementation */ /* include optional Autoconf header */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* include system API headers */ #include /* for "size_t" */ #include #include #include /* optional memory debugging support */ #if defined(HAVE_DMALLOC_H) && defined(WITH_DMALLOC) #include "dmalloc.h" #endif /* include own API header */ #include "val.h" /* boolean values */ #ifndef FALSE #define FALSE (0) #endif #ifndef TRUE #define TRUE (!FALSE) #endif /* unique library identifier */ const char val_id[] = "OSSP val"; /* support for OSSP ex based exception throwing */ #ifdef WITH_EX #include "ex.h" #define VAL_RC(rv) \ ( (rv) != VAL_OK && (ex_catching && !ex_shielding) \ ? (ex_throw(val_id, NULL, (rv)), (rv)) : (rv) ) #else #define VAL_RC(rv) (rv) #endif /* WITH_EX */ /* ** ____ ____ ** ____ LINEAR HASHING SUB-LIBRARY ____ ** ** This part implements a Dynamic Hash Table, based on WITOLD LITWIN ** and PER-AKE LARSON's ``Linear Hashing'' algorithm (1980/1988), ** implemented on top of a two-level virtual array with separate ** collision chains as the backend data structure. Some ideas were ** taken over from MIKAEL PETTERSON's Linear Hashing enhancements ** (1993). The code is derived from OSSP act. See there for details. */ struct lh_st; typedef struct lh_st lh_t; typedef int (*lh_cb_t)(void *ctx, const void *keyptr, int keylen, const void *datptr, int datlen); /* fixed size (number of pointers) of the directory and of each segment */ #define INITDIRSIZE 256 /* can be an arbitrary value */ #define SEGMENTSIZE 512 /* has to be a power of 2 for below arithmetic */ /* the borders for the hash table load */ #define MINLOADFCTR 1 /* should be between 0 and 1 */ #define MAXLOADFCTR 2 /* should be between 2 and 4 */ /* the per-element structure (keep as small as possible!) */ typedef struct element_st element_t; struct element_st { element_t *e_next; /* pointer to next element in collision chain */ unsigned long e_hash; /* cached hash value of element (rehashing optimization) */ void *e_keyptr; /* pointer to key (= also pointer to key+data memory chunk) */ void *e_datptr; /* pointer to data in key+data memory chunk */ void *e_endptr; /* pointer to end of key+data memory chunk */ }; /* the hash table segments */ typedef struct segment_st segment_t; struct segment_st { element_t *s_element[SEGMENTSIZE]; /* array of pointers to elements */ }; /* the top-level hash table structure */ struct lh_st { unsigned int h_p; /* pointer to start of unsplit region */ unsigned int h_pmax; /* pointer to end of unsplit region */ int h_slack; /* grow/shrink indicator */ unsigned h_dirsize; /* current size of directory */ segment_t **h_dir; /* pointer to directory */ }; /* on-the-fly calculate index into directory and segment from virtual array index */ #define DIRINDEX(addr) (int)((addr) / SEGMENTSIZE) #define SEGINDEX(addr) (int)((addr) % SEGMENTSIZE) /* on-the-fly calculate lengths of key and data to reduce memory in element_t */ #define el_keylen(el) ((char *)((el)->e_endptr)-(char *)((el)->e_keyptr)) #define el_datlen(el) ((char *)((el)->e_keyptr)-(char *)((el)->e_datptr)) /* * BJDDJ Hash Function (Bob Jenkins, Dr. Dobbs Journal): * This is a very complex but also very good hash function, as proposed * in the March'97 issue of Dr. Dobbs Journal (DDJ) by Bob Jenkins (see * http://burtleburtle.net/bob/hash/doobs.html for online version). He * showed that this hash function has both very good distribution and * performance and our own hash function comparison confirmed this. The * only difference to the original function of B.J. here is that our * version doesn't provide the `level' (= previous hash) argument for * consistency reasons with the other hash functions (i.e. same function * signature). It can be definetely recommended as a good general * purpose hash function. */ static long lh_hash( register const unsigned char *k, register size_t length) { register long a,b,c,len; /* some abbreviations */ #define ub4 long #define mix(a,b,c) { \ a -= b; a -= c; a ^= (c>>13); \ b -= c; b -= a; b ^= (a<< 8); \ c -= a; c -= b; c ^= (b>>13); \ a -= b; a -= c; a ^= (c>>12); \ b -= c; b -= a; b ^= (a<<16); \ c -= a; c -= b; c ^= (b>> 5); \ a -= b; a -= c; a ^= (c>> 3); \ b -= c; b -= a; b ^= (a<<10); \ c -= a; c -= b; c ^= (b>>15); \ } /* setup the internal state */ len = length; a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ c = 0; /* handle most of the key */ while (len >= 12) { a += (k[0] +((ub4)k[1]<<8) +((ub4)k[ 2]<<16) +((ub4)k[ 3]<<24)); b += (k[4] +((ub4)k[5]<<8) +((ub4)k[ 6]<<16) +((ub4)k[ 7]<<24)); c += (k[8] +((ub4)k[9]<<8) +((ub4)k[10]<<16) +((ub4)k[11]<<24)); mix(a,b,c); k += 12; len -= 12; } /* handle the last 11 bytes */ c += length; switch(len) { /* all the case statements fall through */ case 11: c+=((ub4)k[10]<<24); case 10: c+=((ub4)k[ 9]<<16); case 9 : c+=((ub4)k[ 8]<< 8); /* the first byte of c is reserved for the length */ case 8 : b+=((ub4)k[ 7]<<24); case 7 : b+=((ub4)k[ 6]<<16); case 6 : b+=((ub4)k[ 5]<< 8); case 5 : b+=k[4]; case 4 : a+=((ub4)k[ 3]<<24); case 3 : a+=((ub4)k[ 2]<<16); case 2 : a+=((ub4)k[ 1]<< 8); case 1 : a+=k[0]; /* case 0: nothing left to add */ } mix(a,b,c); #undef ub4 #undef mix /* report the result */ return c; } /* create the hash table structure */ static lh_t *lh_create(void) { lh_t *h; /* allocate hash structure */ if ((h = (lh_t *)malloc(sizeof(lh_t))) == NULL) return NULL; /* allocate and clear hash table directory */ h->h_dirsize = INITDIRSIZE; if ((h->h_dir = (segment_t **)malloc(h->h_dirsize * sizeof(segment_t *))) == NULL) { free(h); return NULL; } memset(h->h_dir, 0, h->h_dirsize * sizeof(segment_t *)); /* allocate and clear first segment of hash table array */ if ((h->h_dir[0] = (segment_t *)malloc(sizeof(segment_t))) == NULL) { free(h->h_dir); free(h); return NULL; } memset(h->h_dir[0], 0, sizeof(segment_t)); /* initialize hash table control attributes */ h->h_p = 0; h->h_pmax = SEGMENTSIZE; h->h_slack = SEGMENTSIZE * MAXLOADFCTR; return h; } /* expand the hash table */ static void lh_expand(lh_t *h) { unsigned int pmax0; unsigned int newaddr; segment_t *seg; element_t **pelold; element_t *el, *headofold, *headofnew, *next; unsigned int n; /* calculate next new address */ pmax0 = h->h_pmax; newaddr = pmax0 + h->h_p; /* have to enlarge directory? */ if (h->h_dirsize <= DIRINDEX(newaddr)) { n = h->h_dirsize * sizeof(segment_t *); h->h_dirsize *= 2; if ((h->h_dir = (segment_t **)realloc( h->h_dir, h->h_dirsize*sizeof(segment_t *))) == NULL) return; memset((char *)h->h_dir+n, 0, n); } /* have to create a new table segment? */ if (SEGINDEX(newaddr) == 0) { if ((seg = (segment_t *)malloc(sizeof(segment_t))) == NULL) return; memset(seg, 0, sizeof(segment_t)); h->h_dir[DIRINDEX(newaddr)] = seg; } /* locate P-element */ pelold = &h->h_dir[DIRINDEX(h->h_p)]->s_element[SEGINDEX(h->h_p)]; /* adjust the state variables */ if (++(h->h_p) >= h->h_pmax) { h->h_pmax = (h->h_pmax << 1); /* == h->h_pmax * 2 */ h->h_p = 0; } h->h_slack += MAXLOADFCTR; /* relocate and split between P-element new element */ headofold = NULL; headofnew = NULL; for (el = *pelold; el != NULL; el = next) { next = el->e_next; if (el->e_hash & pmax0) { el->e_next = headofnew; headofnew = el; } else { el->e_next = headofold; headofold = el; } } *pelold = headofold; h->h_dir[DIRINDEX(newaddr)]->s_element[SEGINDEX(newaddr)] = headofnew; return; } /* shrink hash table */ static void lh_shrink(lh_t *h) { segment_t *lastseg; element_t **pel; unsigned int oldlast; unsigned int dirsize; void *dir; /* calculate old last element */ oldlast = h->h_p + h->h_pmax - 1; /* we cannot shrink below one segment */ if (oldlast == 0) return; /* adjust the state variables */ if (h->h_p == 0) { h->h_pmax = (h->h_pmax >> 1); /* == h->h_pmax / 2 */; h->h_p = h->h_pmax - 1; } else h->h_p--; h->h_slack -= MAXLOADFCTR; /* relocate the lost old last element to the end of the P-element */ pel = &h->h_dir[DIRINDEX(h->h_p)]->s_element[SEGINDEX(h->h_p)]; while (*pel != NULL) pel = &((*pel)->e_next); lastseg = h->h_dir[DIRINDEX(oldlast)]; *pel = lastseg->s_element[SEGINDEX(oldlast)]; lastseg->s_element[SEGINDEX(oldlast)] = NULL; /* if possible, deallocate the last segment */ if (SEGINDEX(oldlast) == 0) { h->h_dir[DIRINDEX(oldlast)] = NULL; free(lastseg); } /* if possible, deallocate the end of the directory */ if (h->h_dirsize > INITDIRSIZE && DIRINDEX(oldlast) < h->h_dirsize/2) { dirsize = (h->h_dirsize >> 1); /* == h->h_dirsize / 2 */ if ((dir = (segment_t **)realloc( h->h_dir, dirsize*sizeof(segment_t *))) != NULL) { h->h_dirsize = dirsize; h->h_dir = dir; } } return; } /* insert element into hash table */ static int lh_insert(lh_t *h, const void *keyptr, int keylen, const void *datptr, int datlen, int override) { unsigned int hash, addr; element_t *el, **pel; int bFound; void *vp; /* argument consistency check */ if (h == NULL || keyptr == NULL || keylen <= 0) return FALSE; /* calculate hash address */ hash = lh_hash(keyptr, keylen); addr = hash % h->h_pmax; /* unsplit region */ if (addr < h->h_p) addr = hash % (h->h_pmax << 1); /* split region */ /* locate hash element's collision list */ pel = &h->h_dir[DIRINDEX(addr)]->s_element[SEGINDEX(addr)]; /* check whether element is already in the hash table */ bFound = FALSE; for (el = *pel; el != NULL; el = el->e_next) { if ( el->e_hash == hash && el_keylen(el) == keylen && memcmp(el->e_keyptr, keyptr, el_keylen(el)) == 0) { bFound = TRUE; break; } } /* only override on request */ if (bFound && !override) return FALSE; /* create a duplicate of key and data */ if ((vp = malloc(keylen+datlen)) == NULL) return FALSE; memmove(vp, datptr, datlen); memmove(((char *)vp)+datlen, keyptr, keylen); datptr = vp; keyptr = ((char *)vp)+datlen; if (bFound) { /* reuse existing element by freeing old contents */ if (el->e_datptr != NULL) free(el->e_datptr); } else { /* allocate new element and chain into list */ if ((el = (element_t *)malloc(sizeof(element_t))) == 0) { free(vp); return FALSE; } while (*pel != NULL) pel = &((*pel)->e_next); el->e_next = *pel; *pel = el; } /* insert contents into element structure */ el->e_keyptr = (void *)keyptr; el->e_datptr = (void *)datptr; el->e_endptr = (char *)keyptr+keylen; el->e_hash = hash; /* do we need to expand the table now? */ if (--(h->h_slack) < 0) lh_expand(h); return TRUE; } /* lookup an element in hash table */ static int lh_lookup(lh_t *h, const void *keyptr, int keylen, void **datptr, int *datlen) { unsigned int hash, addr; element_t *el, **pel; /* argument consistency check */ if (h == NULL || keyptr == NULL || keylen <= 0) return FALSE; /* calculate hash address */ hash = lh_hash(keyptr, keylen); addr = hash % h->h_pmax; /* unsplit region */ if (addr < h->h_p) addr = hash % (h->h_pmax << 1); /* split region */ /* locate hash element collision list */ pel = &h->h_dir[DIRINDEX(addr)]->s_element[SEGINDEX(addr)]; /* search for element in collision list */ for (el = *pel; el != NULL; el = el->e_next) { if ( el->e_hash == hash && el_keylen(el) == keylen && memcmp(el->e_keyptr, keyptr, el_keylen(el)) == 0) { /* provide results */ if (datptr != NULL) *datptr = el->e_datptr; if (datlen != NULL) *datlen = el_datlen(el); return TRUE; } } return FALSE; } /* delete an element in hash table */ static int lh_delete(lh_t *h, const void *keyptr, int keylen) { unsigned int hash, addr; element_t *el, *lel, **pel; int rv; /* argument consistency check */ if (h == NULL || keyptr == NULL || keylen <= 0) return FALSE; /* calculate hash address */ hash = lh_hash(keyptr, keylen); addr = hash % h->h_pmax; /* unsplit region */ if (addr < h->h_p) addr = hash % (h->h_pmax << 1); /* split region */ /* locate hash element collision chain */ pel = &h->h_dir[DIRINDEX(addr)]->s_element[SEGINDEX(addr)]; /* search for element in collision chain */ rv = FALSE; for (lel = NULL, el = *pel; el != NULL; lel = el, el = el->e_next) { if ( el->e_hash == hash && el_keylen(el) == keylen && memcmp(el->e_keyptr, keyptr, el_keylen(el)) == 0) { /* free key+data memory chunk */ if (el->e_datptr != NULL) free(el->e_datptr); /* remove element from collision chain */ if (lel == NULL) *pel = el->e_next; else lel->e_next = el->e_next; /* deallocate element structure */ free(el); rv = TRUE; break; } } /* do we need to shrink the table now? */ if (++(h->h_slack) > ((h->h_pmax + h->h_p) * (MAXLOADFCTR-MINLOADFCTR))) lh_shrink(h); return rv; } /* apply a callback for all elements in the hash table */ static int lh_apply(lh_t *h, lh_cb_t cb, void *ctx) { element_t *el; unsigned int i, j; /* argument consistency check */ if (h == NULL || cb == NULL) return FALSE; /* interate over all segment's entries */ for (i = 0; i < h->h_dirsize; i++) { if (h->h_dir[i] == NULL) continue; /* interate over all collision chains */ for (j = 0; j < SEGMENTSIZE; j++) { if (h->h_dir[i]->s_element[j] == NULL) continue; /* interate over all elements in collision chain */ el = h->h_dir[i]->s_element[j]; for (; el != NULL; el = el->e_next) { if (!cb(ctx, el->e_keyptr, el_keylen(el), el->e_datptr, el_datlen(el))) return FALSE; } } } return TRUE; } /* destroy the whole hash table */ static int lh_destroy(lh_t *h) { element_t *el, **pel, *el_next; unsigned int i, j; /* argument consistency check */ if (h == NULL) return FALSE; /* deallocate all segment's entries */ for (i = 0; i < h->h_dirsize; i++) { if (h->h_dir[i] == NULL) continue; /* deallocate all collision chains */ for (j = 0; j < SEGMENTSIZE; j++) { if (h->h_dir[i]->s_element[j] == NULL) continue; /* deallocate all elements in collision chain */ pel = &h->h_dir[i]->s_element[j]; for (el = *pel; el != NULL; ) { /* deallocate key+data memory chunk */ if (el->e_datptr != NULL) free(el->e_datptr); el_next = el->e_next; free(el); el = el_next; } } free(h->h_dir[i]); } /* deallocate hash table directory */ free(h->h_dir); /* deallocate hash table top-level structure */ free(h); return TRUE; } /* ** ____ ____ ** ____ VALUE LIBRARY ____ ** ** This part implements the actual Value library. Fortunately this ** is now easy because it internally is just based on the above ** full-featured linear hashing library. */ /* * usually val_object_t.data is a pointer val_object_t.data.p, * but VAL_INLINE signals val_object_t.data is actual data * val_object_t.data.[csilfd]. */ enum { VAL_INLINE = 1<<31 }; /* the internal representation of a value object */ typedef struct { int type; union { val_t *v; void *p; char c; short s; int i; long l; float f; double d; } data; char *desc; } val_object_t; /* the val_t internally is just a hash table */ struct val_s { lh_t *lh; }; /* determine address of an object's storage */ static void *val_storage(val_object_t *obj) { void *storage; /* argument consistency check */ if (obj == NULL) return NULL; /* address determination */ if (obj->type & VAL_INLINE) { switch (obj->type & ~VAL_INLINE) { case VAL_TYPE_VAL: storage = &obj->data.v; break; case VAL_TYPE_PTR: storage = &obj->data.p; break; case VAL_TYPE_CHAR: storage = &obj->data.c; break; case VAL_TYPE_SHORT: storage = &obj->data.s; break; case VAL_TYPE_INT: storage = &obj->data.i; break; case VAL_TYPE_LONG: storage = &obj->data.l; break; case VAL_TYPE_FLOAT: storage = &obj->data.f; break; case VAL_TYPE_DOUBLE: storage = &obj->data.d; break; default: storage = NULL; break; /* cannot happen */ } } else storage = obj->data.p; return storage; } /* create object */ val_rc_t val_create(val_t **pval) { val_t *val; /* argument consistency check */ if (pval == NULL) return VAL_RC(VAL_ERR_ARG); /* create top-level structure */ if ((val = (val_t *)malloc(sizeof(val_t))) == NULL) return VAL_RC(VAL_ERR_SYS); /* create hash table */ if ((val->lh = lh_create()) == NULL) { free(val); return VAL_RC(VAL_ERR_SYS); } /* pass result to caller */ *pval = val; return VAL_OK; } /* internal lh_apply() callback for use with val_destroy() */ static int val_destroy_cb(void *_ctx, const void *keyptr, int keylen, const void *datptr, int datlen) { val_object_t *obj; /* free description string */ if ((obj = (val_object_t *)datptr) != NULL) if (obj->desc != NULL) free(obj->desc); return TRUE; } /* destroy object */ val_rc_t val_destroy(val_t *val) { /* argument consistency check */ if (val == NULL) return VAL_RC(VAL_ERR_ARG); /* destroy all hash table elements */ lh_apply(val->lh, val_destroy_cb, NULL); /* destroy hash table */ if (!lh_destroy(val->lh)) return VAL_RC(VAL_ERR_SYS); /* destroy top-level structure */ free(val); return VAL_OK; } /* register a value */ val_rc_t val_reg(val_t *val, const char *name, int type, const char *desc, void *storage) { val_object_t *obj; val_object_t newobj; const char *cp; val_t *child; /* argument consistency check */ if (val == NULL || name == NULL || type == 0) return VAL_RC(VAL_ERR_ARG); /* recursive step-down on structured name */ if ((cp = strchr(name, '.')) != NULL) { if (!lh_lookup(val->lh, name, cp-name, (void **)(void *)&obj, NULL)) return VAL_RC(VAL_ERR_ARG); if (!(obj->type & VAL_TYPE_VAL)) return VAL_RC(VAL_ERR_USE); child = *(val_t **)(val_storage(obj)); return val_reg(child, cp+1, type, desc, storage); } /* create a new value object */ if (desc != NULL) newobj.desc = strdup(desc); else newobj.desc = NULL; if (storage == NULL) { newobj.type = (type | VAL_INLINE); newobj.data.l = 0; } else { newobj.type = (type & ~VAL_INLINE); newobj.data.p = storage; } /* insert value into hash table */ if (!lh_insert(val->lh, name, strlen(name), &newobj, sizeof(newobj), 1)) return VAL_RC(VAL_ERR_HSH); return VAL_OK; } val_rc_t val_unreg(val_t *val, const char *name) { val_object_t *obj; const char *cp; val_t *child; /* argument consistency check */ if (val == NULL || name == NULL) return VAL_RC(VAL_ERR_ARG); /* recursive step-down on structured name */ if ((cp = strchr(name, '.')) != NULL) { if (!lh_lookup(val->lh, name, cp-name, (void **)(void *)&obj, NULL)) return VAL_RC(VAL_ERR_ARG); if (!(obj->type & VAL_TYPE_VAL)) return VAL_RC(VAL_ERR_USE); child = *(val_t **)(val_storage(obj)); return val_unreg(child, cp+1); } /* try to lookup object in hash table */ if (!lh_lookup(val->lh, name, strlen(name), (void **)(void *)&obj, NULL)) return VAL_RC(VAL_ERR_ARG); /* destroy value object */ if (obj->desc != NULL) free(obj->desc); /* delete value from hash table */ if (!lh_delete(val->lh, name, strlen(name))) return VAL_RC(VAL_ERR_HSH); return VAL_OK; } /* query information about a value */ val_rc_t val_query(val_t *val, const char *name, int *ptype, char **pdesc, void **pstorage) { char *cp; val_object_t *obj; val_t *child; /* argument consistency check */ if (val == NULL || name == NULL) return VAL_RC(VAL_ERR_ARG); /* recursive step-down on structured name */ if ((cp = strchr(name, '.')) != NULL) { if (!lh_lookup(val->lh, name, cp-name, (void **)(void *)&obj, NULL)) return VAL_RC(VAL_ERR_ARG); if (!(obj->type & VAL_TYPE_VAL)) return VAL_RC(VAL_ERR_USE); child = *(val_t **)(val_storage(obj)); return val_query(child, cp+1, ptype, pdesc, pstorage); } /* try to lookup object in hash table */ if (!lh_lookup(val->lh, name, strlen(name), (void **)(void *)&obj, NULL)) return VAL_RC(VAL_ERR_ARG); /* pass queried information to caller */ if (ptype != NULL) *ptype = (obj->type & ~VAL_INLINE); if (pdesc != NULL) *pdesc = obj->desc; if (pstorage != NULL) *pstorage = val_storage(obj); return VAL_OK; } /* set a value (va_list variant) */ val_rc_t val_vset(val_t *val, const char *name, va_list ap) { val_object_t *obj; void *storage; const char *cp; val_t *child; /* argument consistency check */ if (val == NULL || name == NULL) return VAL_RC(VAL_ERR_ARG); /* recursive step-down on structured name */ if ((cp = strchr(name, '.')) != NULL) { if (!lh_lookup(val->lh, name, cp-name, (void **)(void *)&obj, NULL)) return VAL_RC(VAL_ERR_ARG); if (!(obj->type & VAL_TYPE_VAL)) return VAL_RC(VAL_ERR_USE); child = *(val_t **)(val_storage(obj)); return val_vset(child, cp+1, ap); } /* try to lookup object in hash table */ if (!lh_lookup(val->lh, name, strlen(name), (void **)(void *)&obj, NULL)) return VAL_RC(VAL_ERR_ARG); /* determine value storage */ if ((storage = val_storage(obj)) == NULL) return VAL_RC(VAL_ERR_INT); /* copy value from variable argument into storage location */ switch (obj->type & ~VAL_INLINE) { case VAL_TYPE_VAL: *(val_t **)storage = (val_t *)va_arg(ap, void *); break; case VAL_TYPE_PTR: *(char **)storage = (char *)va_arg(ap, void *); break; case VAL_TYPE_CHAR: *(char *)storage = (char )va_arg(ap, int ); break; case VAL_TYPE_SHORT: *(short *)storage = (short )va_arg(ap, int ); break; case VAL_TYPE_INT: *(int *)storage = (int )va_arg(ap, int ); break; case VAL_TYPE_LONG: *(long *)storage = (long )va_arg(ap, long ); break; case VAL_TYPE_FLOAT: *(float *)storage = (float )va_arg(ap, double); break; case VAL_TYPE_DOUBLE: *(double *)storage = (double )va_arg(ap, double); break; default: break; /* cannot happen */ } return VAL_OK; } /* set a value */ val_rc_t val_set(val_t *val, const char *name, ...) { val_rc_t rc; va_list ap; /* argument consistency check */ if (val == NULL || name == NULL) return VAL_RC(VAL_ERR_ARG); /* just pass-through to va_list variant */ va_start(ap, name); rc = val_vset(val, name, ap); va_end(ap); return VAL_RC(rc); } /* get a value (va_list variant) */ val_rc_t val_vget(val_t *val, const char *name, va_list ap) { val_object_t *obj; void *storage; const char *cp; val_t *child; /* argument consistency check */ if (val == NULL || name == NULL) return VAL_RC(VAL_ERR_ARG); /* recursive step-down on structured name */ if ((cp = strchr(name, '.')) != NULL) { if (!lh_lookup(val->lh, name, cp-name, (void **)(void *)&obj, NULL)) return VAL_RC(VAL_ERR_ARG); if (!(obj->type & VAL_TYPE_VAL)) return VAL_RC(VAL_ERR_USE); child = *(val_t **)(val_storage(obj)); return val_vget(child, cp+1, ap); } /* try to lookup object in hash table */ if (!lh_lookup(val->lh, name, strlen(name), (void **)(void *)&obj, NULL)) return VAL_RC(VAL_ERR_ARG); /* determine value storage */ if ((storage = val_storage(obj)) == NULL) return VAL_RC(VAL_ERR_INT); /* copy value from storage location into variable argument pointer location */ switch (obj->type & ~VAL_INLINE) { case VAL_TYPE_VAL: *((val_t **)va_arg(ap, void *)) = *(val_t **)storage; break; case VAL_TYPE_PTR: *((char **)va_arg(ap, void *)) = *(char **)storage; break; case VAL_TYPE_CHAR: *((char *)va_arg(ap, int *)) = *(char *)storage; break; case VAL_TYPE_SHORT: *((short *)va_arg(ap, int *)) = *(short *)storage; break; case VAL_TYPE_INT: *((int *)va_arg(ap, int *)) = *(int *)storage; break; case VAL_TYPE_LONG: *((long *)va_arg(ap, long *)) = *(long *)storage; break; case VAL_TYPE_FLOAT: *((float *)va_arg(ap, double *)) = *(float *)storage; break; case VAL_TYPE_DOUBLE: *((double *)va_arg(ap, double *)) = *(double *)storage; break; default: break; /* cannot happen */ } return VAL_OK; } /* get a value */ val_rc_t val_get(val_t *val, const char *name, ...) { val_rc_t rc; va_list ap; /* argument consistency check */ if (val == NULL || name == NULL) return VAL_RC(VAL_ERR_ARG); /* just pass-through to va_list variant */ va_start(ap, name); rc = val_vget(val, name, ap); va_end(ap); return VAL_RC(rc); } /* internal lh_apply() recursion callback context structure */ typedef struct { val_t *val; char *name; int prefixlen; int depth; val_cb_t cb; void *ctx; val_rc_t rc; } val_apply_ctx_t; /* forward declaration */ static val_rc_t val_apply_internal(val_t *, const char *, int, int, val_cb_t, void *); /* internal lh_apply() recursion callback for use with val_apply() */ static int (val_apply_cb)(void *_ctx, const void *keyptr, int keylen, const void *datptr, int datlen) { val_apply_ctx_t *ctx = (val_apply_ctx_t *)_ctx; char name[VAL_MAXNAME+1]; int prefixlen; /* on-the-fly create NUL-terminated concatenated name string */ if ((strlen(ctx->name) + 1 + keylen) > VAL_MAXNAME) { ctx->rc = VAL_ERR_MEM; return FALSE; } if (strlen(ctx->name) > 0) { strcpy(name, ctx->name); strcat(name, "."); prefixlen = ctx->prefixlen + 1; } else { *name = '\0'; prefixlen = ctx->prefixlen; } strncat(name, (char *)keyptr, keylen); /* recursive step-down */ if ((ctx->rc = val_apply_internal(ctx->val, name, prefixlen, ctx->depth, ctx->cb, ctx->ctx)) != VAL_OK) return FALSE; return TRUE; } /* internal API-increased variant of val_apply() */ static val_rc_t val_apply_internal(val_t *val, const char *name, int prefixlen, int depth, val_cb_t cb, void *ctx) { val_object_t *obj; val_t *child; char *cp; val_rc_t rc; val_apply_ctx_t val_ctx; if (name[prefixlen] == '\0') { /* CASE 1: apply to all elements prefix="foo.bar.", remainder="" */ val_ctx.val = val; val_ctx.name = (char *)name; val_ctx.prefixlen = prefixlen; val_ctx.depth = depth; val_ctx.cb = cb; val_ctx.ctx = ctx; val_ctx.rc = VAL_OK; if (!lh_apply(val->lh, val_apply_cb, &val_ctx)) return VAL_RC(VAL_ERR_SYS); } else if ((cp = strchr(name+prefixlen, '.')) != NULL) { /* CASE 2: still stepping-down for structured name prefix="foo.bar.", remainder="quux.baz" */ if (!lh_lookup(val->lh, name+prefixlen, cp-(name+prefixlen), (void **)(void *)&obj, NULL)) return VAL_RC(VAL_ERR_ARG); if (!(obj->type & VAL_TYPE_VAL)) return VAL_RC(VAL_ERR_USE); child = *(val_t **)(val_storage(obj)); if (depth == 0) return VAL_OK; return val_apply_internal(child, name, cp-name+1, depth-1, cb, ctx); } else { /* CASE 3: reached last component of structured name prefix="foo.bar.quux.", remainder="baz" */ if (!lh_lookup(val->lh, name+prefixlen, strlen(name+prefixlen), (void **)(void *)&obj, NULL)) return VAL_RC(VAL_ERR_ARG); if ((rc = cb(ctx, name, (obj->type & ~VAL_INLINE), obj->desc, val_storage(obj))) != VAL_OK) return VAL_RC(rc); if (obj->type & VAL_TYPE_VAL) { if (depth == 0) return VAL_OK; child = *(val_t **)(val_storage(obj)); return val_apply_internal(child, name, strlen(name), depth-1, cb, ctx); } } return VAL_OK; } /* apply a callback to each value */ val_rc_t val_apply(val_t *val, const char *name, int depth, val_cb_t cb, void *ctx) { /* argument consistency check */ if (val == NULL || name == NULL || depth < 0 || cb == NULL) return VAL_RC(VAL_ERR_ARG); /* just pass-through to internal API-extended variant */ return val_apply_internal(val, name, 0, depth, cb, ctx); } @ 1.18 log @Double-cast some variable passing to circumvent GCC warnings. @ text @d3 3 a5 3 ** Copyright (c) 2002-2004 Ralf S. Engelschall ** Copyright (c) 2002-2004 The OSSP Project ** Copyright (c) 2002-2004 Cable & Wireless @ 1.17 log @adjust copyrights @ text @d717 1 a717 1 if (!lh_lookup(val->lh, name, cp-name, (void **)&obj, NULL)) d758 1 a758 1 if (!lh_lookup(val->lh, name, cp-name, (void **)&obj, NULL)) d767 1 a767 1 if (!lh_lookup(val->lh, name, strlen(name), (void **)&obj, NULL)) d795 1 a795 1 if (!lh_lookup(val->lh, name, cp-name, (void **)&obj, NULL)) d804 1 a804 1 if (!lh_lookup(val->lh, name, strlen(name), (void **)&obj, NULL)) d832 1 a832 1 if (!lh_lookup(val->lh, name, cp-name, (void **)&obj, NULL)) d841 1 a841 1 if (!lh_lookup(val->lh, name, strlen(name), (void **)&obj, NULL)) d896 1 a896 1 if (!lh_lookup(val->lh, name, cp-name, (void **)&obj, NULL)) d905 1 a905 1 if (!lh_lookup(val->lh, name, strlen(name), (void **)&obj, NULL)) d1017 1 a1017 1 if (!lh_lookup(val->lh, name+prefixlen, cp-(name+prefixlen), (void **)&obj, NULL)) d1028 1 a1028 1 if (!lh_lookup(val->lh, name+prefixlen, strlen(name+prefixlen), (void **)&obj, NULL)) @ 1.16 log @va_list is not comparable against NULL (import from http://cvs.openpkg.org/chngview?cn=11246) @ text @d3 3 a5 3 ** Copyright (c) 2002-2003 Ralf S. Engelschall ** Copyright (c) 2002-2003 The OSSP Project ** Copyright (c) 2002-2003 Cable & Wireless Deutschland @ 1.15 log @remove trailing whitespaces from source files and apply standard OSSP header to documents @ text @d827 1 a827 1 if (val == NULL || name == NULL || ap == NULL) d891 1 a891 1 if (val == NULL || name == NULL || ap == NULL) @ 1.14 log @swap keyptr/datptr to fix datptr alignment problem on non-i386 architectures [mlelstv] @ text @d110 1 a110 1 }; d142 1 a142 1 static long d179 1 a179 1 switch(len) { d223 1 a223 1 free(h->h_dir); d246 1 a246 1 d260 1 a260 1 d278 1 a278 1 d372 1 a372 1 if ( el->e_hash == hash d443 1 a443 1 if ( el->e_hash == hash d480 1 a480 1 if ( el->e_hash == hash d488 1 a488 1 *pel = el->e_next; d584 1 a584 1 /* d637 1 a637 1 else d669 2 a670 2 static int val_destroy_cb(void *_ctx, const void *keyptr, int keylen, d674 1 a674 1 d730 1 a730 1 if (storage == NULL) { d782 1 a782 1 val_rc_t val_query(val_t *val, const char *name, d893 1 a893 1 d937 1 a937 1 d984 1 a984 1 if ((ctx->rc = val_apply_internal(ctx->val, name, prefixlen, d992 1 a992 1 static val_rc_t val_apply_internal(val_t *val, const char *name, int prefixlen, d1002 1 a1002 1 /* CASE 1: apply to all elements d1030 1 a1030 1 if ((rc = cb(ctx, name, (obj->type & ~VAL_INLINE), @ 1.13 log @bump year in copyright messages @ text @d126 2 a127 2 #define el_keylen(el) ((char *)((el)->e_datptr)-(char *)((el)->e_keyptr)) #define el_datlen(el) ((char *)((el)->e_endptr)-(char *)((el)->e_datptr)) d387 4 a390 4 memmove(vp, keyptr, keylen); memmove((char *)vp+keylen, datptr, datlen); keyptr = vp; datptr = (char *)vp+keylen; d394 2 a395 2 if (el->e_keyptr != NULL) free(el->e_keyptr); d412 1 a412 1 el->e_endptr = (char *)datptr+datlen; d484 2 a485 2 if (el->e_keyptr != NULL) free(el->e_keyptr); d556 2 a557 2 if (el->e_keyptr != NULL) free(el->e_keyptr); @ 1.12 log @add full dmalloc support @ text @d3 3 a5 3 ** Copyright (c) 2002 Ralf S. Engelschall ** Copyright (c) 2002 The OSSP Project ** Copyright (c) 2002 Cable & Wireless Deutschland @ 1.11 log @fix naming and URLs @ text @d42 5 @ 1.10 log @fix name @ text @d2 1 a2 1 ** val - OSSP Value Library d7 2 a8 2 ** This file is part of OSSP val, a Value library which ** can be found at http://www.ossp.org/pkg/val/. @ 1.9 log @add optional OSSP ex based exception handling support @ text @d54 1 a54 1 const char val_id[] = "OSSP sa"; @ 1.8 log @This one is really a typo ;-) PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d53 13 d645 1 a645 1 return VAL_ERR_ARG; d649 1 a649 1 return VAL_ERR_SYS; d654 1 a654 1 return VAL_ERR_SYS; d683 1 a683 1 return VAL_ERR_ARG; d690 1 a690 1 return VAL_ERR_SYS; d708 1 a708 1 return VAL_ERR_ARG; d713 1 a713 1 return VAL_ERR_ARG; d715 1 a715 1 return VAL_ERR_USE; d736 1 a736 1 return VAL_ERR_HSH; d749 1 a749 1 return VAL_ERR_ARG; d754 1 a754 1 return VAL_ERR_ARG; d756 1 a756 1 return VAL_ERR_USE; d763 1 a763 1 return VAL_ERR_ARG; d771 1 a771 1 return VAL_ERR_HSH; d786 1 a786 1 return VAL_ERR_ARG; d791 1 a791 1 return VAL_ERR_ARG; d793 1 a793 1 return VAL_ERR_USE; d800 1 a800 1 return VAL_ERR_ARG; d823 1 a823 1 return VAL_ERR_ARG; d828 1 a828 1 return VAL_ERR_ARG; d830 1 a830 1 return VAL_ERR_USE; d837 1 a837 1 return VAL_ERR_ARG; d841 1 a841 1 return VAL_ERR_INT; d867 1 a867 1 return VAL_ERR_ARG; d874 1 a874 1 return rc; d887 1 a887 1 return VAL_ERR_ARG; d892 1 a892 1 return VAL_ERR_ARG; d894 1 a894 1 return VAL_ERR_USE; d901 1 a901 1 return VAL_ERR_ARG; d905 1 a905 1 return VAL_ERR_INT; d931 1 a931 1 return VAL_ERR_ARG; d938 1 a938 1 return rc; d1007 1 a1007 1 return VAL_ERR_SYS; d1013 1 a1013 1 return VAL_ERR_ARG; d1015 1 a1015 1 return VAL_ERR_USE; d1024 1 a1024 1 return VAL_ERR_ARG; d1027 1 a1027 1 return rc; d1043 1 a1043 1 return VAL_ERR_ARG; @ 1.7 log @fix memory leak @ text @d54 2 a55 2 ** ____ ____ ** ____ LINEAR HASING SUB-LIBRARY ____ @ 1.6 log @add val_unreg() function @ text @d519 1 a519 1 element_t *el, **pel; d536 1 a536 1 for (el = *pel; el != NULL; el = el->e_next) { d540 3 @ 1.5 log @add general query function which can be used to get back all things which were stored on val_reg() and especially allows to query just the existance of the value. @ text @d725 35 @ 1.4 log @- fully document the source code - lots of additional cosmetic cleanups @ text @d725 37 @ 1.3 log @code cleanup @ text @a69 9 #if 0 lh_t *lh_create (void); int lh_insert (lh_t *h, const void *keyptr, int keylen, const void *datptr, int datlen, int override); int lh_lookup (lh_t *h, const void *keyptr, int keylen, void **datptr, int *datlen); int lh_delete (lh_t *h, const void *keyptr, int keylen); int lh_apply (lh_t *h, lh_cb_t cb, void *ctx); int lh_destroy(lh_t *h); #endif d122 1 a122 1 * purpuse hash function. d558 3 d563 4 a566 2 /* usually val_object_t.data is a pointer val_object_t.data.p, but VAL_INLINE * signals val_object_t.data is actual data val_object_t.data.[csilfd] d569 1 a569 1 VAL_INLINE = 1<<31 d572 1 d588 1 d593 1 d598 5 d605 9 a613 26 case VAL_TYPE_VAL: storage = &obj->data.v; break; case VAL_TYPE_PTR: storage = &obj->data.p; break; case VAL_TYPE_CHAR: storage = &obj->data.c; break; case VAL_TYPE_SHORT: storage = &obj->data.s; break; case VAL_TYPE_INT: storage = &obj->data.i; break; case VAL_TYPE_LONG: storage = &obj->data.l; break; case VAL_TYPE_FLOAT: storage = &obj->data.f; break; case VAL_TYPE_DOUBLE: storage = &obj->data.d; break; default: storage = NULL; d618 1 d622 2 a623 1 val_rc_t val_create(val_t **valp) d627 2 a628 1 if (valp == NULL) d630 2 d634 2 d640 4 a643 1 *valp = val; d647 4 a650 1 static int (val_destroy_cb)(void *_ctx, const void *keyptr, int keylen, const void *datptr, int datlen) d654 5 a658 3 obj = (val_object_t *)datptr; if (obj->desc != NULL) free(obj->desc); d662 1 d665 1 d668 2 d671 2 d675 2 d678 1 d682 1 d690 1 d694 1 a694 2 d704 1 a704 2 d717 2 d720 1 a720 1 return VAL_ERR_SYS; d725 1 d733 1 d736 2 d746 2 d750 6 a755 1 storage = val_storage(obj); d757 9 a765 24 case VAL_TYPE_VAL: *(val_t **)storage = (val_t *)va_arg(ap, void *); break; case VAL_TYPE_PTR: *(char **)storage = (char *)va_arg(ap, void *); break; case VAL_TYPE_CHAR: *(char *)storage = (char)va_arg(ap, int); break; case VAL_TYPE_SHORT: *(short *)storage = (short)va_arg(ap, int); break; case VAL_TYPE_INT: *(int *)storage = (int)va_arg(ap, int); break; case VAL_TYPE_LONG: *(long *)storage = (long)va_arg(ap, long); break; case VAL_TYPE_FLOAT: *(float *)storage = (float)va_arg(ap, double); break; case VAL_TYPE_DOUBLE: *(double *)storage = (double)va_arg(ap, double); break; d767 1 d771 1 d777 1 d780 2 d785 1 d789 1 d797 1 d800 2 d810 2 d814 6 a819 1 storage = val_storage(obj); d821 9 a829 24 case VAL_TYPE_VAL: *((val_t **)va_arg(ap, void *)) = *(val_t **)storage; break; case VAL_TYPE_PTR: *((char **)va_arg(ap, void *)) = *(char **)storage; break; case VAL_TYPE_CHAR: *((char *)va_arg(ap, int *)) = *(char *)storage; break; case VAL_TYPE_SHORT: *((short *)va_arg(ap, int *)) = *(short *)storage; break; case VAL_TYPE_INT: *((int *)va_arg(ap, int *)) = *(int *)storage; break; case VAL_TYPE_LONG: *((long *)va_arg(ap, long *)) = *(long *)storage; break; case VAL_TYPE_FLOAT: *((float *)va_arg(ap, double *)) = *(float *)storage; break; case VAL_TYPE_DOUBLE: *((double *)va_arg(ap, double *)) = *(double *)storage; break; d831 1 d835 1 d841 1 d844 2 d849 1 d853 1 a853 2 #define VAL_MAXNAME 1024 d855 7 a861 7 val_t *val; char *name; int prefixlen; int depth; val_cb_t cb; void *ctx; val_rc_t rc; d864 1 d867 1 d874 1 a874 1 /* on-the-fly create NUL-terminated name string */ d889 4 a892 1 if ((ctx->rc = val_apply_internal(ctx->val, name, prefixlen, ctx->depth, ctx->cb, ctx->ctx)) != VAL_OK) d894 1 d898 3 a900 1 static val_rc_t val_apply_internal(val_t *val, const char *name, int prefixlen, int depth, val_cb_t cb, void *ctx) d909 2 a910 1 /* prefix="foo.bar.", remainder="" */ d921 20 a940 8 else { if ((cp = strchr(name+prefixlen, '.')) != NULL) { /* prefix="foo.bar.", remainder="quux.baz" */ if (!lh_lookup(val->lh, name+prefixlen, cp-(name+prefixlen), (void **)&obj, NULL)) return VAL_ERR_ARG; if (!(obj->type & VAL_TYPE_VAL)) return VAL_ERR_USE; child = *(val_t **)(val_storage(obj)); d943 2 a944 15 return val_apply_internal(child, name, cp-name+1, --depth, cb, ctx); } else { /* prefix="foo.bar.quux.", remainder="baz" */ if (!lh_lookup(val->lh, name+prefixlen, strlen(name+prefixlen), (void **)&obj, NULL)) return VAL_ERR_ARG; /* execute VAL callback */ if ((rc = cb(ctx, name, (obj->type & ~VAL_INLINE), obj->desc, val_storage(obj))) != VAL_OK) return rc; if (obj->type & VAL_TYPE_VAL) { if (depth == 0) return VAL_OK; child = *(val_t **)(val_storage(obj)); return val_apply_internal(child, name, strlen(name), --depth, cb, ctx); } d950 1 d953 1 d957 1 @ 1.2 log @move val+lh code from OSSP lmtp2nntp to its dedicated source tree @ text @a883 1 fprintf(stderr, "DEBUG: val_apply_internal name=<%s>, prefixlen=%d, depth=%d\n", name, prefixlen, depth); d886 9 a894 11 //if (--depth > 0) { val_ctx.val = val; val_ctx.name = (char *)name; val_ctx.prefixlen = prefixlen; val_ctx.depth = depth; val_ctx.cb = cb; val_ctx.ctx = ctx; val_ctx.rc = VAL_OK; if (!lh_apply(val->lh, val_apply_cb, &val_ctx)) return VAL_ERR_SYS; //} a914 1 //fprintf(stderr, "DEBUG: depth=%d obj->type=%.8lx\n", depth, obj->type); @ 1.1 log @Create a fresh build environment for new born child OSSP val. This soon to be written library will provide a user-land val with O(1) time complexity and fixed size memory usage. @ text @d37 4 a40 1 #include /* for "s[n]printf()" */ d52 885 @