Home | History | Annotate | Line # | Download | only in src
      1 
      2 /***********************************************************
      3 Copyright 1987, 1988, 1990 by Digital Equipment Corporation, Maynard
      4 
      5                         All Rights Reserved
      6 
      7 Permission to use, copy, modify, and distribute this software and its
      8 documentation for any purpose and without fee is hereby granted,
      9 provided that the above copyright notice appear in all copies and that
     10 both that copyright notice and this permission notice appear in
     11 supporting documentation, and that the name Digital not be
     12 used in advertising or publicity pertaining to distribution of the
     13 software without specific, written prior permission.
     14 
     15 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
     16 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
     17 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
     18 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
     19 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
     20 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
     21 SOFTWARE.
     22 
     23 ******************************************************************/
     24 /*
     25 
     26 Copyright 1987, 1988, 1990, 1998  The Open Group
     27 
     28 Permission to use, copy, modify, distribute, and sell this software and its
     29 documentation for any purpose is hereby granted without fee, provided that
     30 the above copyright notice appear in all copies and that both that
     31 copyright notice and this permission notice appear in supporting
     32 documentation.
     33 
     34 The above copyright notice and this permission notice shall be included
     35 in all copies or substantial portions of the Software.
     36 
     37 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     38 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     39 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     40 IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
     41 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     42 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     43 OTHER DEALINGS IN THE SOFTWARE.
     44 
     45 Except as contained in this notice, the name of The Open Group shall
     46 not be used in advertising or otherwise to promote the sale, use or
     47 other dealings in this Software without prior written authorization
     48 from The Open Group.
     49 
     50 */
     51 
     52 #ifdef HAVE_CONFIG_H
     53 #include <config.h>
     54 #endif
     55 #include	<stdio.h>
     56 #include	<ctype.h>
     57 #include	"Xlibint.h"
     58 #include	<X11/Xresource.h>
     59 #include	"Xlcint.h"
     60 #ifdef XTHREADS
     61 #include	"locking.h"
     62 #endif
     63 #include	<X11/Xos.h>
     64 #include	<sys/stat.h>
     65 #include	<limits.h>
     66 #include "Xresinternal.h"
     67 #include "Xresource.h"
     68 
     69 /*
     70 
     71 These Xrm routines allow very fast lookup of resources in the resource
     72 database.  Several usage patterns are exploited:
     73 
     74 (1) Widgets get a lot of resources at one time.  Rather than look up each from
     75 scratch, we can precompute the prioritized list of database levels once, then
     76 search for each resource starting at the beginning of the list.
     77 
     78 (2) Many database levels don't contain any leaf resource nodes.  There is no
     79 point in looking for resources on a level that doesn't contain any.  This
     80 information is kept on a per-level basis.
     81 
     82 (3) Sometimes the widget instance tree is structured such that you get the same
     83 class name repeated on the fully qualified widget name.  This can result in the
     84 same database level occurring multiple times on the search list.  The code below
     85 only checks to see if you get two identical search lists in a row, rather than
     86 look back through all database levels, but in practice this removes all
     87 duplicates I've ever observed.
     88 
     89 Joel McCormack
     90 
     91 */
     92 
     93 /*
     94 
     95 The Xrm representation has been completely redesigned to substantially reduce
     96 memory and hopefully improve performance.
     97 
     98 The database is structured into two kinds of tables: LTables that contain
     99 only values, and NTables that contain only other tables.
    100 
    101 Some invariants:
    102 
    103 The next pointer of the top-level node table points to the top-level leaf
    104 table, if any.
    105 
    106 Within an LTable, for a given name, the tight value always precedes the
    107 loose value, and if both are present the loose value is always right after
    108 the tight value.
    109 
    110 Within an NTable, all of the entries for a given name are contiguous,
    111 in the order tight NTable, loose NTable, tight LTable, loose LTable.
    112 
    113 Bob Scheifler
    114 
    115 */
    116 
    117 static XrmQuark XrmQString, XrmQANY;
    118 
    119 typedef	Bool (*DBEnumProc)(
    120     XrmDatabase*	/* db */,
    121     XrmBindingList	/* bindings */,
    122     XrmQuarkList	/* quarks */,
    123     XrmRepresentation*	/* type */,
    124     XrmValue*		/* value */,
    125     XPointer		/* closure */
    126 );
    127 
    128 typedef struct _VEntry {
    129     struct _VEntry	*next;		/* next in chain */
    130     XrmQuark		name;		/* name of this entry */
    131     unsigned int	tight:1;	/* 1 if it is a tight binding */
    132     unsigned int	string:1;	/* 1 if type is String */
    133     unsigned int	size:30;	/* size of value */
    134 } VEntryRec, *VEntry;
    135 
    136 
    137 typedef struct _DEntry {
    138     VEntryRec		entry;		/* entry */
    139     XrmRepresentation	type;		/* representation type */
    140 } DEntryRec, *DEntry;
    141 
    142 /* the value is right after the structure */
    143 #define StringValue(ve) (XPointer)((ve) + 1)
    144 #define RepType(ve) ((DEntry)(ve))->type
    145 /* the value is right after the structure */
    146 #define DataValue(ve) (XPointer)(((DEntry)(ve)) + 1)
    147 #define RawValue(ve) (char *)((ve)->string ? StringValue(ve) : DataValue(ve))
    148 
    149 typedef struct _NTable {
    150     struct _NTable	*next;		/* next in chain */
    151     XrmQuark		name;		/* name of this entry */
    152     unsigned int	tight:1;	/* 1 if it is a tight binding */
    153     unsigned int	leaf:1;		/* 1 if children are values */
    154     unsigned int	hasloose:1;	/* 1 if has loose children */
    155     unsigned int	hasany:1;	/* 1 if has ANY entry */
    156     unsigned int	pad:4;		/* unused */
    157     unsigned int	mask:8;		/* hash size - 1 */
    158     unsigned int	entries:16;	/* number of children */
    159 } NTableRec, *NTable;
    160 
    161 /* the buckets are right after the structure */
    162 #define NodeBuckets(ne) ((NTable *)((ne) + 1))
    163 #define NodeHash(ne,q) NodeBuckets(ne)[(q) & (ne)->mask]
    164 
    165 /* leaf tables have an extra level of indirection for the buckets,
    166  * so that resizing can be done without invalidating a search list.
    167  * This is completely ugly, and wastes some memory, but the Xlib
    168  * spec doesn't really specify whether invalidation is OK, and the
    169  * old implementation did not invalidate.
    170  */
    171 typedef struct _LTable {
    172     NTableRec		table;
    173     VEntry		*buckets;
    174 } LTableRec, *LTable;
    175 
    176 #define LeafHash(le,q) (le)->buckets[(q) & (le)->table.mask]
    177 
    178 /* An XrmDatabase just holds a pointer to the first top-level table.
    179  * The type name is no longer descriptive, but better to not change
    180  * the Xresource.h header file.  This type also gets used to define
    181  * XrmSearchList, which is a complete crock, but we'll just leave it
    182  * and caste types as required.
    183  */
    184 typedef struct _XrmHashBucketRec {
    185     NTable table;
    186     XPointer mbstate;
    187     XrmMethods methods;
    188 #ifdef XTHREADS
    189     LockInfoRec linfo;
    190 #endif
    191 } XrmHashBucketRec;
    192 
    193 /* closure used in get/put resource */
    194 typedef struct _VClosure {
    195     XrmRepresentation	*type;		/* type of value */
    196     XrmValuePtr		value;		/* value itself */
    197 } VClosureRec, *VClosure;
    198 
    199 /* closure used in get search list */
    200 typedef struct _SClosure {
    201     LTable		*list;		/* search list */
    202     int			idx;		/* index of last filled element */
    203     int			limit;		/* maximum index */
    204 } SClosureRec, *SClosure;
    205 
    206 /* placed in XrmSearchList to indicate next table is loose only */
    207 #define LOOSESEARCH ((LTable)1)
    208 
    209 /* closure used in enumerate database */
    210 typedef struct _EClosure {
    211     XrmDatabase db;			/* the database */
    212     DBEnumProc proc;			/* the user proc */
    213     XPointer closure;			/* the user closure */
    214     XrmBindingList bindings;		/* binding list */
    215     XrmQuarkList quarks;		/* quark list */
    216     int mode;				/* XrmEnum<kind> */
    217 } EClosureRec, *EClosure;
    218 
    219 /* types for typecasting ETable based functions to NTable based functions */
    220 typedef Bool (*getNTableSProcp)(
    221     NTable              table,
    222     XrmNameList         names,
    223     XrmClassList        classes,
    224     SClosure            closure);
    225 typedef Bool (*getNTableVProcp)(
    226     NTable              table,
    227     XrmNameList         names,
    228     XrmClassList        classes,
    229     VClosure            closure);
    230 typedef Bool (*getNTableEProcp)(
    231     NTable              table,
    232     XrmNameList         names,
    233     XrmClassList        classes,
    234     register int        level,
    235     EClosure            closure);
    236 
    237 /* predicate to determine when to resize a hash table */
    238 #define GrowthPred(n,m) ((unsigned)(n) > (((m) + 1) << 2))
    239 
    240 #define GROW(prev) \
    241     if (GrowthPred((*prev)->entries, (*prev)->mask)) \
    242 	GrowTable(prev)
    243 
    244 /* pick a reasonable value for maximum depth of resource database */
    245 #define MAXDBDEPTH 100
    246 
    247 /* macro used in get/search functions */
    248 
    249 /* find an entry named ename, with leafness given by leaf */
    250 #define NFIND(ename) \
    251     q = ename; \
    252     entry = NodeHash(table, q); \
    253     while (entry && entry->name != q) \
    254 	entry = entry->next; \
    255     if (leaf && entry && !entry->leaf) { \
    256 	entry = entry->next; \
    257 	if (entry && !entry->leaf) \
    258 	    entry = entry->next; \
    259 	if (entry && entry->name != q) \
    260 	    entry = (NTable)NULL; \
    261     }
    262 
    263 /* resourceQuarks keeps track of what quarks have been associated with values
    264  * in all LTables.  If a quark has never been used in an LTable, we don't need
    265  * to bother looking for it.
    266  */
    267 
    268 static unsigned char *resourceQuarks = (unsigned char *)NULL;
    269 static XrmQuark maxResourceQuark = -1;
    270 
    271 /* determines if a quark has been used for a value in any database */
    272 #define IsResourceQuark(q)  ((q) > 0 && (q) <= maxResourceQuark && \
    273 			     resourceQuarks[(q) >> 3] & (1 << ((q) & 7)))
    274 
    275 typedef unsigned char XrmBits;
    276 
    277 #define BSLASH  ((XrmBits) (1 << 5))
    278 #define NORMAL	((XrmBits) (1 << 4))
    279 #define EOQ	((XrmBits) (1 << 3))
    280 #define SEP	((XrmBits) (1 << 2))
    281 #define ENDOF	((XrmBits) (1 << 1))
    282 #define SPACE	(NORMAL|EOQ|SEP|(XrmBits)0)
    283 #define RSEP	(NORMAL|EOQ|SEP|(XrmBits)1)
    284 #define EOS	(EOQ|SEP|ENDOF|(XrmBits)0)
    285 #define EOL	(EOQ|SEP|ENDOF|(XrmBits)1)
    286 #define BINDING	(NORMAL|EOQ)
    287 #define ODIGIT	(NORMAL|(XrmBits)1)
    288 
    289 #define next_char(ch,str) xrmtypes[(unsigned char)((ch) = *(++(str)))]
    290 #define next_mbchar(ch,len,str) xrmtypes[(unsigned char)(ch = (*db->methods->mbchar)(db->mbstate, str, &len), str += len, ch)]
    291 
    292 #define is_space(bits)		((bits) == SPACE)
    293 #define is_EOQ(bits)		((bits) & EOQ)
    294 #define is_EOF(bits)		((bits) == EOS)
    295 #define is_EOL(bits)		((bits) & ENDOF)
    296 #define is_binding(bits)	((bits) == BINDING)
    297 #define is_odigit(bits)		((bits) == ODIGIT)
    298 #define is_separator(bits)	((bits) & SEP)
    299 #define is_nonpcs(bits)		(!(bits))
    300 #define is_normal(bits)		((bits) & NORMAL)
    301 #define is_simple(bits)		((bits) & (NORMAL|BSLASH))
    302 #define is_special(bits)	((bits) & (ENDOF|BSLASH))
    303 
    304 /* parsing types */
    305 static XrmBits const xrmtypes[256] = {
    306     EOS,0,0,0,0,0,0,0,
    307     0,SPACE,EOL,0,0,
    308 #ifdef WIN32
    309                     EOL,	/* treat CR the same as LF, just in case */
    310 #else
    311                     0,
    312 #endif
    313                       0,0,
    314     0,0,0,0,0,0,0,0,
    315     0,0,0,0,0,0,0,0,
    316     SPACE,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,
    317     NORMAL,NORMAL,BINDING,NORMAL,NORMAL,NORMAL,BINDING,NORMAL,
    318     ODIGIT,ODIGIT,ODIGIT,ODIGIT,ODIGIT,ODIGIT,ODIGIT,ODIGIT,
    319     NORMAL,NORMAL,RSEP,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,
    320     NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,
    321     NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,
    322     NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,
    323     NORMAL,NORMAL,NORMAL,NORMAL,BSLASH,NORMAL,NORMAL,NORMAL,
    324     NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,
    325     NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,
    326     NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,
    327     NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,0
    328     /* The rest will be automatically initialized to zero. */
    329 };
    330 
    331 void XrmInitialize(void)
    332 {
    333     XrmQString = XrmPermStringToQuark("String");
    334     XrmQANY = XrmPermStringToQuark("?");
    335 }
    336 
    337 XrmDatabase XrmGetDatabase(
    338     Display *display)
    339 {
    340     XrmDatabase retval;
    341     LockDisplay(display);
    342     retval = display->db;
    343     UnlockDisplay(display);
    344     return retval;
    345 }
    346 
    347 void XrmSetDatabase(
    348     Display *display,
    349     XrmDatabase database)
    350 {
    351     LockDisplay(display);
    352     /* destroy database if set up implicitly by XGetDefault() */
    353     if (display->db && (display->flags & XlibDisplayDfltRMDB)) {
    354 	XrmDestroyDatabase(display->db);
    355 	display->flags &= ~XlibDisplayDfltRMDB;
    356     }
    357     display->db = database;
    358     UnlockDisplay(display);
    359 }
    360 
    361 void
    362 XrmStringToQuarkList(
    363     register _Xconst char  *name,
    364     register XrmQuarkList quarks)   /* RETURN */
    365 {
    366     register XrmBits		bits;
    367     register Signature  	sig = 0;
    368     register char       	ch, *tname;
    369     register int 		i = 0;
    370 
    371     if ((tname = (char *)name)) {
    372 	tname--;
    373 	while (!is_EOF(bits = next_char(ch, tname))) {
    374 	    if (is_binding (bits)) {
    375 		if (i) {
    376 		    /* Found a complete name */
    377 		    *quarks++ = _XrmInternalStringToQuark(name,tname - name,
    378 							  sig, False);
    379 		    i = 0;
    380 		    sig = 0;
    381 		}
    382 		name = tname+1;
    383 	    }
    384 	    else {
    385 		sig = (sig << 1) + ch; /* Compute the signature. */
    386 		i++;
    387 	    }
    388 	}
    389 	*quarks++ = _XrmInternalStringToQuark(name, tname - name, sig, False);
    390     }
    391     *quarks = NULLQUARK;
    392 }
    393 
    394 void
    395 XrmStringToBindingQuarkList(
    396     register _Xconst char   *name,
    397     register XrmBindingList bindings,   /* RETURN */
    398     register XrmQuarkList   quarks)     /* RETURN */
    399 {
    400     register XrmBits		bits;
    401     register Signature  	sig = 0;
    402     register char       	ch, *tname;
    403     register XrmBinding 	binding;
    404     register int 		i = 0;
    405 
    406     if ((tname = (char *)name)) {
    407 	tname--;
    408 	binding = XrmBindTightly;
    409 	while (!is_EOF(bits = next_char(ch, tname))) {
    410 	    if (is_binding (bits)) {
    411 		if (i) {
    412 		    /* Found a complete name */
    413 		    *bindings++ = binding;
    414 		    *quarks++ = _XrmInternalStringToQuark(name, tname - name,
    415 							  sig, False);
    416 
    417 		    i = 0;
    418 		    sig = 0;
    419 		    binding = XrmBindTightly;
    420 		}
    421 		name = tname+1;
    422 
    423 		if (ch == '*')
    424 		    binding = XrmBindLoosely;
    425 	    }
    426 	    else {
    427 		sig = (sig << 1) + ch; /* Compute the signature. */
    428 		i++;
    429 	    }
    430 	}
    431 	*bindings = binding;
    432 	*quarks++ = _XrmInternalStringToQuark(name, tname - name, sig, False);
    433     }
    434     *quarks = NULLQUARK;
    435 }
    436 
    437 #ifdef DEBUG
    438 
    439 static void PrintQuarkList(
    440     XrmQuarkList    quarks,
    441     FILE	    *stream)
    442 {
    443     Bool	    firstNameSeen;
    444 
    445     for (firstNameSeen = False; *quarks; quarks++) {
    446 	if (firstNameSeen) {
    447 	    (void) fprintf(stream, ".");
    448 	}
    449 	firstNameSeen = True;
    450 	(void) fputs(XrmQuarkToString(*quarks), stream);
    451     }
    452 } /* PrintQuarkList */
    453 
    454 #endif /* DEBUG */
    455 
    456 
    457 /*
    458  * Fallback methods for Xrm parsing.
    459  * Simulate a C locale. No state needed here.
    460  */
    461 
    462 static void
    463 c_mbnoop(
    464     XPointer state)
    465 {
    466 }
    467 
    468 static char
    469 c_mbchar(
    470     XPointer state,
    471     const char *str,
    472     int *lenp)
    473 {
    474     *lenp = 1;
    475     return *str;
    476 }
    477 
    478 static const char *
    479 c_lcname(
    480     XPointer state)
    481 {
    482     return "C";
    483 }
    484 
    485 static const XrmMethodsRec mb_methods = {
    486     c_mbnoop,	/* mbinit */
    487     c_mbchar,	/* mbchar */
    488     c_mbnoop,	/* mbfinish */
    489     c_lcname,	/* lcname */
    490     c_mbnoop	/* destroy */
    491 };
    492 
    493 
    494 static XrmDatabase NewDatabase(void)
    495 {
    496     register XrmDatabase db;
    497 
    498     db = Xmalloc(sizeof(XrmHashBucketRec));
    499     if (db) {
    500 	_XCreateMutex(&db->linfo);
    501 	db->table = (NTable)NULL;
    502 	db->mbstate = (XPointer)NULL;
    503 	db->methods = _XrmInitParseInfo(&db->mbstate);
    504 	if (!db->methods)
    505 	    db->methods = &mb_methods;
    506     }
    507     return db;
    508 }
    509 
    510 /* move all values from ftable to ttable, and free ftable's buckets.
    511  * ttable is guaranteed empty to start with.
    512  */
    513 static void MoveValues(
    514     LTable ftable,
    515     register LTable ttable)
    516 {
    517     register VEntry fentry, nfentry;
    518     register VEntry *prev;
    519     register VEntry *bucket;
    520     register VEntry tentry;
    521     register int i;
    522 
    523     for (i = ftable->table.mask, bucket = ftable->buckets; i >= 0; i--) {
    524 	for (fentry = *bucket++; fentry; fentry = nfentry) {
    525 	    prev = &LeafHash(ttable, fentry->name);
    526 	    tentry = *prev;
    527 	    *prev = fentry;
    528 	    /* chain on all with same name, to preserve invariant order */
    529 	    while ((nfentry = fentry->next) && nfentry->name == fentry->name)
    530 		fentry = nfentry;
    531 	    fentry->next = tentry;
    532 	}
    533     }
    534     Xfree(ftable->buckets);
    535 }
    536 
    537 /* move all tables from ftable to ttable, and free ftable.
    538  * ttable is quaranteed empty to start with.
    539  */
    540 static void MoveTables(
    541     NTable ftable,
    542     register NTable ttable)
    543 {
    544     register NTable fentry, nfentry;
    545     register NTable *prev;
    546     register NTable *bucket;
    547     register NTable tentry;
    548     register int i;
    549 
    550     for (i = ftable->mask, bucket = NodeBuckets(ftable); i >= 0; i--) {
    551 	for (fentry = *bucket++; fentry; fentry = nfentry) {
    552 	    prev = &NodeHash(ttable, fentry->name);
    553 	    tentry = *prev;
    554 	    *prev = fentry;
    555 	    /* chain on all with same name, to preserve invariant order */
    556 	    while ((nfentry = fentry->next) && nfentry->name == fentry->name)
    557 		fentry = nfentry;
    558 	    fentry->next = tentry;
    559 	}
    560     }
    561     Xfree(ftable);
    562 }
    563 
    564 /* grow the table, based on current number of entries */
    565 static void GrowTable(
    566     NTable *prev)
    567 {
    568     register NTable table;
    569     register int i;
    570 
    571     table = *prev;
    572     i = table->mask;
    573     if (i == 255) /* biggest it gets */
    574 	return;
    575     while (i < 255 && GrowthPred(table->entries, i))
    576 	i = (i << 1) + 1;
    577     i++; /* i is now the new size */
    578     if (table->leaf) {
    579 	register LTable ltable;
    580 	LTableRec otable;
    581 
    582 	ltable = (LTable)table;
    583 	/* cons up a copy to make MoveValues look symmetric */
    584 	otable = *ltable;
    585 	ltable->buckets = Xcalloc(i, sizeof(VEntry));
    586 	if (!ltable->buckets) {
    587 	    ltable->buckets = otable.buckets;
    588 	    return;
    589 	}
    590 	ltable->table.mask = i - 1;
    591 	MoveValues(&otable, ltable);
    592     } else {
    593 	register NTable ntable;
    594 
    595 	ntable = Xcalloc(1, sizeof(NTableRec) + (i * sizeof(NTable)));
    596 	if (!ntable)
    597 	    return;
    598 	*ntable = *table;
    599 	ntable->mask = i - 1;
    600 	*prev = ntable;
    601 	MoveTables(table, ntable);
    602     }
    603 }
    604 
    605 /* merge values from ftable into *pprev, destroy ftable in the process */
    606 static void MergeValues(
    607     LTable ftable,
    608     NTable *pprev,
    609     Bool override)
    610 {
    611     register VEntry fentry, tentry;
    612     register VEntry *prev;
    613     register LTable ttable;
    614     VEntry *bucket;
    615     int i;
    616     register XrmQuark q;
    617 
    618     ttable = (LTable)*pprev;
    619     if (ftable->table.hasloose)
    620 	ttable->table.hasloose = 1;
    621     for (i = ftable->table.mask, bucket = ftable->buckets;
    622 	 i >= 0;
    623 	 i--, bucket++) {
    624 	for (fentry = *bucket; fentry; ) {
    625 	    q = fentry->name;
    626 	    prev = &LeafHash(ttable, q);
    627 	    tentry = *prev;
    628 	    while (tentry && tentry->name != q)
    629 		tentry = *(prev = &tentry->next);
    630 	    /* note: test intentionally uses fentry->name instead of q */
    631 	    /* permits serendipitous inserts */
    632 	    while (tentry && tentry->name == fentry->name) {
    633 		/* if tentry is earlier, skip it */
    634 		if (!fentry->tight && tentry->tight) {
    635 		    tentry = *(prev = &tentry->next);
    636 		    continue;
    637 		}
    638 		if (fentry->tight != tentry->tight) {
    639 		    /* no match, chain in fentry */
    640 		    *prev = fentry;
    641 		    prev = &fentry->next;
    642 		    fentry = *prev;
    643 		    *prev = tentry;
    644 		    ttable->table.entries++;
    645 		} else if (override) {
    646 		    /* match, chain in fentry, splice out and free tentry */
    647 		    *prev = fentry;
    648 		    prev = &fentry->next;
    649 		    fentry = *prev;
    650 		    *prev = tentry->next;
    651 		    /* free the overridden entry */
    652 		    Xfree(tentry);
    653 		    /* get next tentry */
    654 		    tentry = *prev;
    655 		} else {
    656 		    /* match, discard fentry */
    657 		    prev = &tentry->next;
    658 		    tentry = fentry; /* use as a temp var */
    659 		    fentry = fentry->next;
    660 		    /* free the overpowered entry */
    661 		    Xfree(tentry);
    662 		    /* get next tentry */
    663 		    tentry = *prev;
    664 		}
    665 		if (!fentry)
    666 		    break;
    667 	    }
    668 	    /* at this point, tentry cannot match any fentry named q */
    669 	    /* chain in all bindings together, preserve invariant order */
    670 	    while (fentry && fentry->name == q) {
    671 		*prev = fentry;
    672 		prev = &fentry->next;
    673 		fentry = *prev;
    674 		*prev = tentry;
    675 		ttable->table.entries++;
    676 	    }
    677 	}
    678     }
    679     Xfree(ftable->buckets);
    680     Xfree(ftable);
    681     /* resize if necessary, now that we're all done */
    682     GROW(pprev);
    683 }
    684 
    685 /* merge tables from ftable into *pprev, destroy ftable in the process */
    686 static void MergeTables(
    687     NTable ftable,
    688     NTable *pprev,
    689     Bool override)
    690 {
    691     register NTable fentry, tentry;
    692     NTable nfentry;
    693     register NTable *prev;
    694     register NTable ttable;
    695     NTable *bucket;
    696     int i;
    697     register XrmQuark q;
    698 
    699     ttable = *pprev;
    700     if (ftable->hasloose)
    701 	ttable->hasloose = 1;
    702     if (ftable->hasany)
    703 	ttable->hasany = 1;
    704     for (i = ftable->mask, bucket = NodeBuckets(ftable);
    705 	 i >= 0;
    706 	 i--, bucket++) {
    707 	for (fentry = *bucket; fentry; ) {
    708 	    q = fentry->name;
    709 	    prev = &NodeHash(ttable, q);
    710 	    tentry = *prev;
    711 	    while (tentry && tentry->name != q)
    712 		tentry = *(prev = &tentry->next);
    713 	    /* note: test intentionally uses fentry->name instead of q */
    714 	    /* permits serendipitous inserts */
    715 	    while (tentry && tentry->name == fentry->name) {
    716 		/* if tentry is earlier, skip it */
    717 		if ((fentry->leaf && !tentry->leaf) ||
    718 		    (!fentry->tight && tentry->tight &&
    719 		     (fentry->leaf || !tentry->leaf))) {
    720 		    tentry = *(prev = &tentry->next);
    721 		    continue;
    722 		}
    723 		nfentry = fentry->next;
    724 		if (fentry->leaf != tentry->leaf ||
    725 		    fentry->tight != tentry->tight) {
    726 		    /* no match, just chain in */
    727 		    *prev = fentry;
    728 		    *(prev = &fentry->next) = tentry;
    729 		    ttable->entries++;
    730 		} else {
    731 		    if (fentry->leaf)
    732 			MergeValues((LTable)fentry, prev, override);
    733 		    else
    734 			MergeTables(fentry, prev, override);
    735 		    /* bump to next tentry */
    736 		    tentry = *(prev = &(*prev)->next);
    737 		}
    738 		/* bump to next fentry */
    739 		fentry = nfentry;
    740 		if (!fentry)
    741 		    break;
    742 	    }
    743 	    /* at this point, tentry cannot match any fentry named q */
    744 	    /* chain in all bindings together, preserve invariant order */
    745 	    while (fentry && fentry->name == q) {
    746 		*prev = fentry;
    747 		prev = &fentry->next;
    748 		fentry = *prev;
    749 		*prev = tentry;
    750 		ttable->entries++;
    751 	    }
    752 	}
    753     }
    754     Xfree(ftable);
    755     /* resize if necessary, now that we're all done */
    756     GROW(pprev);
    757 }
    758 
    759 void XrmCombineDatabase(
    760     XrmDatabase	from, XrmDatabase *into,
    761     Bool override)
    762 {
    763     register NTable *prev;
    764     register NTable ftable, ttable, nftable;
    765 
    766     if (!*into) {
    767 	*into = from;
    768     } else if (from) {
    769 	_XLockMutex(&from->linfo);
    770 	_XLockMutex(&(*into)->linfo);
    771 	if ((ftable = from->table)) {
    772 	    prev = &(*into)->table;
    773 	    ttable = *prev;
    774 	    if (!ftable->leaf) {
    775 		nftable = ftable->next;
    776 		if (ttable && !ttable->leaf) {
    777 		    /* both have node tables, merge them */
    778 		    MergeTables(ftable, prev, override);
    779 		    /* bump to into's leaf table, if any */
    780 		    ttable = *(prev = &(*prev)->next);
    781 		} else {
    782 		    /* into has no node table, link from's in */
    783 		    *prev = ftable;
    784 		    *(prev = &ftable->next) = ttable;
    785 		}
    786 		/* bump to from's leaf table, if any */
    787 		ftable = nftable;
    788 	    } else {
    789 		/* bump to into's leaf table, if any */
    790 		if (ttable && !ttable->leaf)
    791 		    ttable = *(prev = &ttable->next);
    792 	    }
    793 	    if (ftable) {
    794 		/* if into has a leaf, merge, else insert */
    795 		if (ttable)
    796 		    MergeValues((LTable)ftable, prev, override);
    797 		else
    798 		    *prev = ftable;
    799 	    }
    800 	}
    801 	(from->methods->destroy)(from->mbstate);
    802 	_XUnlockMutex(&from->linfo);
    803 	_XFreeMutex(&from->linfo);
    804 	Xfree(from);
    805 	_XUnlockMutex(&(*into)->linfo);
    806     }
    807 }
    808 
    809 void XrmMergeDatabases(
    810     XrmDatabase	from, XrmDatabase *into)
    811 {
    812     XrmCombineDatabase(from, into, True);
    813 }
    814 
    815 /* store a value in the database, overriding any existing entry */
    816 static void PutEntry(
    817     XrmDatabase		db,
    818     XrmBindingList	bindings,
    819     XrmQuarkList	quarks,
    820     XrmRepresentation	type,
    821     XrmValuePtr		value)
    822 {
    823     register NTable *pprev, *prev;
    824     register NTable table;
    825     register XrmQuark q;
    826     register VEntry *vprev;
    827     register VEntry entry;
    828     NTable *nprev, *firstpprev;
    829 
    830 #define NEWTABLE(q,i) \
    831     table = Xmalloc(sizeof(LTableRec)); \
    832     if (!table) \
    833 	return; \
    834     table->name = q; \
    835     table->hasloose = 0; \
    836     table->hasany = 0; \
    837     table->mask = 0; \
    838     table->entries = 0; \
    839     if (quarks[i]) { \
    840 	table->leaf = 0; \
    841 	nprev = NodeBuckets(table); \
    842     } else { \
    843 	table->leaf = 1; \
    844 	if (!(nprev = Xmalloc(sizeof(VEntry *)))) {\
    845 	    Xfree(table); \
    846 	    return; \
    847         } \
    848 	((LTable)table)->buckets = (VEntry *)nprev; \
    849     } \
    850     *nprev = (NTable)NULL; \
    851     table->next = *prev; \
    852     *prev = table
    853 
    854     if (!db || !*quarks)
    855 	return;
    856     table = *(prev = &db->table);
    857     /* if already at leaf, bump to the leaf table */
    858     if (!quarks[1] && table && !table->leaf)
    859 	table = *(prev = &table->next);
    860     pprev = prev;
    861     if (!table || (quarks[1] && table->leaf)) {
    862 	/* no top-level node table, create one and chain it in */
    863 	NEWTABLE(NULLQUARK,1);
    864 	table->tight = 1; /* arbitrary */
    865 	prev = nprev;
    866     } else {
    867 	/* search along until we need a value */
    868 	while (quarks[1]) {
    869 	    q = *quarks;
    870 	    table = *(prev = &NodeHash(table, q));
    871 	    while (table && table->name != q)
    872 		table = *(prev = &table->next);
    873 	    if (!table)
    874 		break; /* not found */
    875 	    if (quarks[2]) {
    876 		if (table->leaf)
    877 		    break; /* not found */
    878 	    } else {
    879 		if (!table->leaf) {
    880 		    /* bump to leaf table, if any */
    881 		    table = *(prev = &table->next);
    882 		    if (!table || table->name != q)
    883 			break; /* not found */
    884 		    if (!table->leaf) {
    885 			/* bump to leaf table, if any */
    886 			table = *(prev = &table->next);
    887 			if (!table || table->name != q)
    888 			    break; /* not found */
    889 		    }
    890 		}
    891 	    }
    892 	    if (*bindings == XrmBindTightly) {
    893 		if (!table->tight)
    894 		    break; /* not found */
    895 	    } else {
    896 		if (table->tight) {
    897 		    /* bump to loose table, if any */
    898 		    table = *(prev = &table->next);
    899 		    if (!table || table->name != q ||
    900 			!quarks[2] != table->leaf)
    901 			break; /* not found */
    902 		}
    903 	    }
    904 	    /* found that one, bump to next quark */
    905 	    pprev = prev;
    906 	    quarks++;
    907 	    bindings++;
    908 	}
    909 	if (!quarks[1]) {
    910 	    /* found all the way to a leaf */
    911 	    q = *quarks;
    912 	    entry = *(vprev = &LeafHash((LTable)table, q));
    913 	    while (entry && entry->name != q)
    914 		entry = *(vprev = &entry->next);
    915 	    /* if want loose and have tight, bump to next entry */
    916 	    if (entry && *bindings == XrmBindLoosely && entry->tight)
    917 		entry = *(vprev = &entry->next);
    918 	    if (entry && entry->name == q &&
    919 		(*bindings == XrmBindTightly) == entry->tight) {
    920 		/* match, need to override */
    921 		if ((type == XrmQString) == entry->string &&
    922 		    entry->size == value->size) {
    923 		    /* update type if not String, can be different */
    924 		    if (!entry->string)
    925 			RepType(entry) = type;
    926 		    /* identical size, just overwrite value */
    927 		    memcpy(RawValue(entry), (char *)value->addr, value->size);
    928 		    return;
    929 		}
    930 		/* splice out and free old entry */
    931 		*vprev = entry->next;
    932 		Xfree(entry);
    933 		(*pprev)->entries--;
    934 	    }
    935 	    /* this is where to insert */
    936 	    prev = (NTable *)vprev;
    937 	}
    938     }
    939     /* keep the top table, because we may have to grow it */
    940     firstpprev = pprev;
    941     /* iterate until we get to the leaf */
    942     while (quarks[1]) {
    943 	/* build a new table and chain it in */
    944 	NEWTABLE(*quarks,2);
    945 	if (*quarks++ == XrmQANY)
    946 	    (*pprev)->hasany = 1;
    947 	if (*bindings++ == XrmBindTightly) {
    948 	    table->tight = 1;
    949 	} else {
    950 	    table->tight = 0;
    951 	    (*pprev)->hasloose = 1;
    952 	}
    953 	(*pprev)->entries++;
    954 	pprev = prev;
    955 	prev = nprev;
    956     }
    957     /* now allocate the value entry */
    958     entry = Xmalloc(((type == XrmQString) ?
    959 		     sizeof(VEntryRec) : sizeof(DEntryRec)) + value->size);
    960     if (!entry)
    961 	return;
    962     entry->name = q = *quarks;
    963     if (*bindings == XrmBindTightly) {
    964 	entry->tight = 1;
    965     } else {
    966 	entry->tight = 0;
    967 	(*pprev)->hasloose = 1;
    968     }
    969     /* chain it in, with a bit of type cast ugliness */
    970     entry->next = *((VEntry *)prev);
    971     *((VEntry *)prev) = entry;
    972     entry->size = value->size;
    973     if (type == XrmQString) {
    974 	entry->string = 1;
    975     } else {
    976 	entry->string = 0;
    977 	RepType(entry) = type;
    978     }
    979     /* save a copy of the value */
    980     memcpy(RawValue(entry), (char *)value->addr, value->size);
    981     (*pprev)->entries++;
    982     /* this is a new leaf, need to remember it for search lists */
    983     if (q > maxResourceQuark) {
    984 	unsigned oldsize = (maxResourceQuark + 1) >> 3;
    985 	unsigned size = ((q | 0x7f) + 1) >> 3; /* reallocate in chunks */
    986 	if (resourceQuarks) {
    987 	    unsigned char *prevQuarks = resourceQuarks;
    988 
    989 	    resourceQuarks = Xrealloc(resourceQuarks, size);
    990 	    if (!resourceQuarks) {
    991 		Xfree(prevQuarks);
    992 	    }
    993 	} else
    994 	    resourceQuarks = Xmalloc(size);
    995 	if (resourceQuarks) {
    996 	    bzero((char *)&resourceQuarks[oldsize], size - oldsize);
    997 	    maxResourceQuark = (size << 3) - 1;
    998 	} else {
    999 	    maxResourceQuark = -1;
   1000 	}
   1001     }
   1002     if (q > 0 && resourceQuarks)
   1003 	resourceQuarks[q >> 3] |= 1 << (q & 0x7);
   1004     GROW(firstpprev);
   1005 
   1006 #undef NEWTABLE
   1007 }
   1008 
   1009 void XrmQPutResource(
   1010     XrmDatabase		*pdb,
   1011     XrmBindingList      bindings,
   1012     XrmQuarkList	quarks,
   1013     XrmRepresentation	type,
   1014     XrmValuePtr		value)
   1015 {
   1016     if (!*pdb) *pdb = NewDatabase();
   1017     _XLockMutex(&(*pdb)->linfo);
   1018     PutEntry(*pdb, bindings, quarks, type, value);
   1019     _XUnlockMutex(&(*pdb)->linfo);
   1020 }
   1021 
   1022 void
   1023 XrmPutResource(
   1024     XrmDatabase     *pdb,
   1025     _Xconst char    *specifier,
   1026     _Xconst char    *type,
   1027     XrmValuePtr	    value)
   1028 {
   1029     XrmBinding	    bindings[MAXDBDEPTH+1];
   1030     XrmQuark	    quarks[MAXDBDEPTH+1];
   1031 
   1032     if (!*pdb) *pdb = NewDatabase();
   1033     _XLockMutex(&(*pdb)->linfo);
   1034     XrmStringToBindingQuarkList(specifier, bindings, quarks);
   1035     PutEntry(*pdb, bindings, quarks, XrmStringToQuark(type), value);
   1036     _XUnlockMutex(&(*pdb)->linfo);
   1037 }
   1038 
   1039 void
   1040 XrmQPutStringResource(
   1041     XrmDatabase     *pdb,
   1042     XrmBindingList  bindings,
   1043     XrmQuarkList    quarks,
   1044     _Xconst char    *str)
   1045 {
   1046     XrmValue	value;
   1047 
   1048     if (!*pdb) *pdb = NewDatabase();
   1049     value.addr = (XPointer) str;
   1050     value.size = (unsigned) strlen(str) + 1;
   1051     _XLockMutex(&(*pdb)->linfo);
   1052     PutEntry(*pdb, bindings, quarks, XrmQString, &value);
   1053     _XUnlockMutex(&(*pdb)->linfo);
   1054 }
   1055 
   1056 /*	Function Name: GetDatabase
   1057  *	Description: Parses a string and stores it as a database.
   1058  *	Arguments: db - the database.
   1059  *                 str - a pointer to the string containing the database.
   1060  *                 filename - source filename, if any.
   1061  *                 doall - whether to do all lines or just one
   1062  */
   1063 
   1064 /*
   1065  * This function is highly optimized to inline as much as possible.
   1066  * Be very careful with modifications, or simplifications, as they
   1067  * may adversely affect the performance.
   1068  *
   1069  * Chris Peterson, MIT X Consortium		5/17/90.
   1070  */
   1071 
   1072 /*
   1073  * Xlib spec says max 100 quarks in a lookup, will stop and return if
   1074  * return if any single production's lhs has more than 100 components.
   1075  */
   1076 #define QLIST_SIZE 100
   1077 
   1078 /*
   1079  * This should be big enough to handle things like the XKeysymDB or biggish
   1080  * ~/.Xdefaults or app-defaults files. Anything bigger will be allocated on
   1081  * the heap.
   1082  */
   1083 #define DEF_BUFF_SIZE 8192
   1084 
   1085 static void GetIncludeFile(
   1086     XrmDatabase db,
   1087     _Xconst char *base,
   1088     _Xconst char *fname,
   1089     int fnamelen,
   1090     int depth);
   1091 
   1092 static void GetDatabase(
   1093     XrmDatabase db,
   1094     _Xconst char *str,
   1095     _Xconst char *filename,
   1096     Bool doall,
   1097     int depth)
   1098 {
   1099     char *rhs;
   1100     char *lhs, lhs_s[DEF_BUFF_SIZE];
   1101     XrmQuark quarks[QLIST_SIZE + 1];	/* allow for a terminal NullQuark */
   1102     XrmBinding bindings[QLIST_SIZE + 1];
   1103 
   1104     register char *ptr;
   1105     register XrmBits bits = 0;
   1106     register char c;
   1107     register Signature sig;
   1108     register char *ptr_max;
   1109     register int num_quarks;
   1110     register XrmBindingList t_bindings;
   1111 
   1112     int len, alloc_chars;
   1113     unsigned long str_len;
   1114     XrmValue value;
   1115     Bool only_pcs;
   1116     Bool dolines;
   1117 
   1118     if (!db)
   1119 	return;
   1120 
   1121     /*
   1122      * if strlen (str) < DEF_BUFF_SIZE allocate buffers on the stack for
   1123      * speed otherwise malloc the buffer. From a buffer overflow standpoint
   1124      * we can be sure that neither: a) a component on the lhs, or b) a
   1125      * value on the rhs, will be longer than the overall length of str,
   1126      * i.e. strlen(str).
   1127      *
   1128      * This should give good performance when parsing "*foo: bar" type
   1129      * databases as might be passed with -xrm command line options; but
   1130      * with larger databases, e.g. .Xdefaults, app-defaults, or KeysymDB
   1131      * files, the size of the buffers will be overly large. One way
   1132      * around this would be to double-parse each production with a resulting
   1133      * performance hit. In any event we can be assured that a lhs component
   1134      * name or a rhs value won't be longer than str itself.
   1135      */
   1136 
   1137     str_len = strlen (str);
   1138     if (DEF_BUFF_SIZE > str_len) lhs = lhs_s;
   1139     else if ((lhs = Xmalloc (str_len)) == NULL)
   1140 	return;
   1141 
   1142     alloc_chars = DEF_BUFF_SIZE < str_len ? str_len : DEF_BUFF_SIZE;
   1143     if ((rhs = Xmalloc (alloc_chars)) == NULL) {
   1144 	if (lhs != lhs_s) Xfree (lhs);
   1145 	return;
   1146     }
   1147 
   1148     (*db->methods->mbinit)(db->mbstate);
   1149     str--;
   1150     dolines = True;
   1151     while (!is_EOF(bits) && dolines) {
   1152 	dolines = doall;
   1153 
   1154 	/*
   1155 	 * First: Remove extra whitespace.
   1156 	 */
   1157 
   1158 	do {
   1159 	    bits = next_char(c, str);
   1160 	} while is_space(bits);
   1161 
   1162 	/*
   1163 	 * Ignore empty lines.
   1164 	 */
   1165 
   1166 	if (is_EOL(bits))
   1167 	    continue;		/* start a new line. */
   1168 
   1169 	/*
   1170 	 * Second: check the first character in a line to see if it is
   1171 	 * "!" signifying a comment, or "#" signifying a directive.
   1172 	 */
   1173 
   1174 	if (c == '!') { /* Comment, spin to next newline */
   1175 	    while (is_simple(bits = next_char(c, str))) {}
   1176 	    if (is_EOL(bits))
   1177 		continue;
   1178 	    while (!is_EOL(bits = next_mbchar(c, len, str))) {}
   1179 	    str--;
   1180 	    continue;		/* start a new line. */
   1181 	}
   1182 
   1183 	if (c == '#') { /* Directive */
   1184 	    /* remove extra whitespace */
   1185 	    only_pcs = True;
   1186 	    while (is_space(bits = next_char(c, str))) {}
   1187 	    /* only "include" directive is currently defined */
   1188 	    if (!strncmp(str, "include", 7)) {
   1189 		str += (7-1);
   1190 		/* remove extra whitespace */
   1191 		while (is_space(bits = next_char(c, str))) {}
   1192 		/* must have a starting " */
   1193 		if (c == '"') {
   1194 		    _Xconst char *fname = str+1;
   1195 		    len = 0;
   1196 		    do {
   1197 			if (only_pcs) {
   1198 			    bits = next_char(c, str);
   1199 			    if (is_nonpcs(bits))
   1200 				only_pcs = False;
   1201 			}
   1202 			if (!only_pcs)
   1203 			    bits = next_mbchar(c, len, str);
   1204 		    } while (c != '"' && !is_EOL(bits));
   1205 		    /* must have an ending " */
   1206 		    if (c == '"')
   1207 			GetIncludeFile(db, filename, fname, str - len - fname,
   1208 			    depth);
   1209 		}
   1210 	    }
   1211 	    /* spin to next newline */
   1212 	    if (only_pcs) {
   1213 		while (is_simple(bits))
   1214 		    bits = next_char(c, str);
   1215 		if (is_EOL(bits))
   1216 		    continue;
   1217 	    }
   1218 	    while (!is_EOL(bits))
   1219 		bits = next_mbchar(c, len, str);
   1220 	    str--;
   1221 	    continue;		/* start a new line. */
   1222 	}
   1223 
   1224 	/*
   1225 	 * Third: loop through the LHS of the resource specification
   1226 	 * storing characters and converting this to a Quark.
   1227 	 */
   1228 
   1229 	num_quarks = 0;
   1230 	t_bindings = bindings;
   1231 
   1232 	sig = 0;
   1233 	ptr = lhs;
   1234 	*t_bindings = XrmBindTightly;
   1235 	for(;;) {
   1236 	    if (!is_binding(bits)) {
   1237 		while (!is_EOQ(bits)) {
   1238 		    *ptr++ = c;
   1239 		    sig = (sig << 1) + c; /* Compute the signature. */
   1240 		    bits = next_char(c, str);
   1241 		}
   1242 
   1243 		quarks[num_quarks++] =
   1244 			_XrmInternalStringToQuark(lhs, ptr - lhs, sig, False);
   1245 
   1246 		if (num_quarks > QLIST_SIZE) {
   1247 		    Xfree(rhs);
   1248 		    if (lhs != lhs_s) Xfree (lhs);
   1249 		    (*db->methods->mbfinish)(db->mbstate);
   1250 		    return;
   1251 		}
   1252 
   1253 		if (is_separator(bits))  {
   1254 		    if (!is_space(bits))
   1255 			break;
   1256 
   1257 		    /* Remove white space */
   1258 		    do {
   1259 			*ptr++ = c;
   1260 			sig = (sig << 1) + c; /* Compute the signature. */
   1261 		    } while (is_space(bits = next_char(c, str)));
   1262 
   1263 		    /*
   1264 		     * The spec doesn't permit it, but support spaces
   1265 		     * internal to resource name/class
   1266 		     */
   1267 
   1268 		    if (is_separator(bits))
   1269 			break;
   1270 		    num_quarks--;
   1271 		    continue;
   1272 		}
   1273 
   1274 		if (c == '.')
   1275 		    *(++t_bindings) = XrmBindTightly;
   1276 		else
   1277 		    *(++t_bindings) = XrmBindLoosely;
   1278 
   1279 		sig = 0;
   1280 		ptr = lhs;
   1281 	    }
   1282 	    else {
   1283 		/*
   1284 		 * Magic unspecified feature #254.
   1285 		 *
   1286 		 * If two separators appear with no Text between them then
   1287 		 * ignore them.
   1288 		 *
   1289 		 * If anyone of those separators is a '*' then the binding
   1290 		 * will be loose, otherwise it will be tight.
   1291 		 */
   1292 
   1293 		if (c == '*')
   1294 		    *t_bindings = XrmBindLoosely;
   1295 	    }
   1296 
   1297 	    bits = next_char(c, str);
   1298 	}
   1299 
   1300 	quarks[num_quarks] = NULLQUARK;
   1301 
   1302 	/*
   1303 	 * Make sure that there is a ':' in this line.
   1304 	 */
   1305 
   1306 	if (c != ':') {
   1307 	    char oldc;
   1308 
   1309 	    /*
   1310 	     * A parsing error has occurred, toss everything on the line
   1311 	     * a new_line can still be escaped with a '\'.
   1312 	     */
   1313 
   1314 	    while (is_normal(bits))
   1315 		bits = next_char(c, str);
   1316 	    if (is_EOL(bits))
   1317 		continue;
   1318 	    bits = next_mbchar(c, len, str);
   1319 	    do {
   1320 		oldc = c;
   1321 		bits = next_mbchar(c, len, str);
   1322 	    } while (c && (c != '\n' || oldc == '\\'));
   1323 	    str--;
   1324 	    continue;
   1325 	}
   1326 
   1327 	/*
   1328 	 * I now have a quark and binding list for the entire left hand
   1329 	 * side.  "c" currently points to the ":" separating the left hand
   1330 	 * side for the right hand side.  It is time to begin processing
   1331 	 * the right hand side.
   1332 	 */
   1333 
   1334 	/*
   1335 	 * Fourth: Remove more whitespace
   1336 	 */
   1337 
   1338 	for(;;) {
   1339 	    if (is_space(bits = next_char(c, str)))
   1340 		continue;
   1341 	    if (c != '\\')
   1342 		break;
   1343 	    bits = next_char(c, str);
   1344 	    if (c == '\n')
   1345 		continue;
   1346 	    str--;
   1347 	    bits = BSLASH;
   1348 	    c = '\\';
   1349 	    break;
   1350 	}
   1351 
   1352 	/*
   1353 	 * Fifth: Process the right hand side.
   1354 	 */
   1355 
   1356 	ptr = rhs;
   1357 	ptr_max = ptr + alloc_chars - 4;
   1358 	only_pcs = True;
   1359 	len = 1;
   1360 
   1361 	for(;;) {
   1362 
   1363 	    /*
   1364 	     * Tight loop for the normal case:  Non backslash, non-end of value
   1365 	     * character that will fit into the allocated buffer.
   1366 	     */
   1367 
   1368 	    if (only_pcs) {
   1369 		while (is_normal(bits) && ptr < ptr_max) {
   1370 		    *ptr++ = c;
   1371 		    bits = next_char(c, str);
   1372 		}
   1373 		if (is_EOL(bits))
   1374 		    break;
   1375 		if (is_nonpcs(bits)) {
   1376 		    only_pcs = False;
   1377 		    bits = next_mbchar(c, len, str);
   1378 		}
   1379 	    }
   1380 	    while (!is_special(bits) && ptr + len <= ptr_max) {
   1381 		len = -len;
   1382 		while (len)
   1383 		    *ptr++ = str[len++];
   1384 		if (*str == '\0') {
   1385 		    bits = EOS;
   1386 		    break;
   1387 		}
   1388 		bits = next_mbchar(c, len, str);
   1389 	    }
   1390 
   1391 	    if (is_EOL(bits)) {
   1392 		str--;
   1393 		break;
   1394 	    }
   1395 
   1396 	    if (c == '\\') {
   1397 		/*
   1398 		 * We need to do some magic after a backslash.
   1399 		 */
   1400 		Bool read_next = True;
   1401 
   1402 		if (only_pcs) {
   1403 		    bits = next_char(c, str);
   1404 		    if (is_nonpcs(bits))
   1405 			only_pcs = False;
   1406 		}
   1407 		if (!only_pcs)
   1408 		    bits = next_mbchar(c, len, str);
   1409 
   1410 		if (is_EOL(bits)) {
   1411 		    if (is_EOF(bits))
   1412 			continue;
   1413 		} else if (c == 'n') {
   1414 		    /*
   1415 		     * "\n" means insert a newline.
   1416 		     */
   1417 		    *ptr++ = '\n';
   1418 		} else if (c == '\\') {
   1419 		    /*
   1420 		     * "\\" completes to just one backslash.
   1421 		     */
   1422 		    *ptr++ = '\\';
   1423 		} else {
   1424 		    /*
   1425 		     * pick up to three octal digits after the '\'.
   1426 		     */
   1427 		    char temp[3];
   1428 		    int count = 0;
   1429 		    while (is_odigit(bits) && count < 3) {
   1430 			temp[count++] = c;
   1431 			if (only_pcs) {
   1432 			    bits = next_char(c, str);
   1433 			    if (is_nonpcs(bits))
   1434 				only_pcs = False;
   1435 			}
   1436 			if (!only_pcs)
   1437 			    bits = next_mbchar(c, len, str);
   1438 		    }
   1439 
   1440 		    /*
   1441 		     * If we found three digits then insert that octal code
   1442 		     * into the value string as a character.
   1443 		     */
   1444 
   1445 		    if (count == 3) {
   1446 			*ptr++ = (unsigned char) ((temp[0] - '0') * 0100 +
   1447 						  (temp[1] - '0') * 010 +
   1448 						  (temp[2] - '0'));
   1449 		    }
   1450 		    else {
   1451 			int tcount;
   1452 
   1453 			/*
   1454 			 * Otherwise just insert those characters into the
   1455 			 * string, since no special processing is needed on
   1456 			 * numerics we can skip the special processing.
   1457 			 */
   1458 
   1459 			for (tcount = 0; tcount < count; tcount++) {
   1460 			    *ptr++ = temp[tcount]; /* print them in
   1461 						      the correct order */
   1462 			}
   1463 		    }
   1464 		    read_next = False;
   1465 		}
   1466 		if (read_next) {
   1467 		    if (only_pcs) {
   1468 			bits = next_char(c, str);
   1469 			if (is_nonpcs(bits))
   1470 			    only_pcs = False;
   1471 		    }
   1472 		    if (!only_pcs)
   1473 			bits = next_mbchar(c, len, str);
   1474 		}
   1475 	    }
   1476 
   1477 	    /*
   1478 	     * It is important to make sure that there is room for at least
   1479 	     * four more characters in the buffer, since I can add that
   1480 	     * many characters into the buffer after a backslash has occurred.
   1481 	     */
   1482 
   1483 	    if (ptr + len > ptr_max) {
   1484 		char * temp_str;
   1485 
   1486 		alloc_chars += BUFSIZ/10;
   1487 		temp_str = Xrealloc(rhs, sizeof(char) * alloc_chars);
   1488 
   1489 		if (!temp_str) {
   1490 		    Xfree(rhs);
   1491 		    if (lhs != lhs_s) Xfree (lhs);
   1492 		    (*db->methods->mbfinish)(db->mbstate);
   1493 		    return;
   1494 		}
   1495 
   1496 		ptr = temp_str + (ptr - rhs); /* reset pointer. */
   1497 		rhs = temp_str;
   1498 		ptr_max = rhs + alloc_chars - 4;
   1499 	    }
   1500 	}
   1501 
   1502 	/*
   1503 	 * Lastly: Terminate the value string, and store this entry
   1504 	 * 	   into the database.
   1505 	 */
   1506 
   1507 	*ptr++ = '\0';
   1508 
   1509 	/* Store it in database */
   1510 	value.size = ptr - rhs;
   1511 	value.addr = (XPointer) rhs;
   1512 
   1513 	PutEntry(db, bindings, quarks, XrmQString, &value);
   1514     }
   1515 
   1516     if (lhs != lhs_s) Xfree (lhs);
   1517     Xfree (rhs);
   1518 
   1519     (*db->methods->mbfinish)(db->mbstate);
   1520 }
   1521 
   1522 void
   1523 XrmPutStringResource(
   1524     XrmDatabase *pdb,
   1525     _Xconst char*specifier,
   1526     _Xconst char*str)
   1527 {
   1528     XrmValue	value;
   1529     XrmBinding	bindings[MAXDBDEPTH+1];
   1530     XrmQuark	quarks[MAXDBDEPTH+1];
   1531 
   1532     if (!*pdb) *pdb = NewDatabase();
   1533     XrmStringToBindingQuarkList(specifier, bindings, quarks);
   1534     value.addr = (XPointer) str;
   1535     value.size = (unsigned) strlen(str)+1;
   1536     _XLockMutex(&(*pdb)->linfo);
   1537     PutEntry(*pdb, bindings, quarks, XrmQString, &value);
   1538     _XUnlockMutex(&(*pdb)->linfo);
   1539 }
   1540 
   1541 
   1542 void
   1543 XrmPutLineResource(
   1544     XrmDatabase *pdb,
   1545     _Xconst char*line)
   1546 {
   1547     if (!*pdb) *pdb = NewDatabase();
   1548     _XLockMutex(&(*pdb)->linfo);
   1549     GetDatabase(*pdb, line, (char *)NULL, False, 0);
   1550     _XUnlockMutex(&(*pdb)->linfo);
   1551 }
   1552 
   1553 XrmDatabase
   1554 XrmGetStringDatabase(
   1555     _Xconst char    *data)
   1556 {
   1557     XrmDatabase     db;
   1558 
   1559     db = NewDatabase();
   1560     _XLockMutex(&db->linfo);
   1561     GetDatabase(db, data, (char *)NULL, True, 0);
   1562     _XUnlockMutex(&db->linfo);
   1563     return db;
   1564 }
   1565 
   1566 /*	Function Name: ReadInFile
   1567  *	Description: Reads the file into a buffer.
   1568  *	Arguments: filename - the name of the file.
   1569  *	Returns: An allocated string containing the contents of the file.
   1570  */
   1571 
   1572 static char *
   1573 ReadInFile(_Xconst char *filename)
   1574 {
   1575     register int fd, size;
   1576     char * filebuf;
   1577 
   1578 
   1579     /*
   1580      * MS-Windows and OS/2 note: Default open mode includes O_TEXT
   1581      */
   1582     if ( (fd = _XOpenFile (filename, O_RDONLY)) == -1 )
   1583 	return (char *)NULL;
   1584 
   1585     /*
   1586      * MS-Windows and OS/2 note: depending on how the sources are
   1587      * untarred, the newlines in resource files may or may not have
   1588      * been expanded to CRLF. Either way the size returned by fstat
   1589      * is sufficient to read the file into because in text-mode any
   1590      * CRLFs in a file will be converted to newlines (LF) with the
   1591      * result that the number of bytes actually read with be <=
   1592      * to the size returned by fstat.
   1593      */
   1594     {
   1595 	struct stat status_buffer;
   1596 	if ( ((fstat(fd, &status_buffer)) == -1 ) ||
   1597              (status_buffer.st_size >= INT_MAX) ) {
   1598 	    close (fd);
   1599 	    return (char *)NULL;
   1600 	} else
   1601 	    size = (int) status_buffer.st_size;
   1602     }
   1603 
   1604     if (!(filebuf = Xmalloc(size + 1))) { /* leave room for '\0' */
   1605 	close(fd);
   1606 	return (char *)NULL;
   1607     }
   1608     size = read (fd, filebuf, size);
   1609     if (size < 0) {
   1610 	close (fd);
   1611 	Xfree(filebuf);
   1612 	return (char *)NULL;
   1613     }
   1614     close (fd);
   1615 
   1616     filebuf[size] = '\0';	/* NULL terminate it. */
   1617     return filebuf;
   1618 }
   1619 
   1620 static void
   1621 GetIncludeFile(
   1622     XrmDatabase db,
   1623     _Xconst char *base,
   1624     _Xconst char *fname,
   1625     int fnamelen,
   1626     int depth)
   1627 {
   1628     int len;
   1629     char *str;
   1630     char realfname[BUFSIZ];
   1631 
   1632     if (fnamelen <= 0 || fnamelen >= BUFSIZ)
   1633 	return;
   1634     if (depth >= MAXDBDEPTH)
   1635 	return;
   1636     if (*fname != '/' && base && (str = strrchr(base, '/'))) {
   1637 	len = str - base + 1;
   1638 	if (len + fnamelen >= BUFSIZ)
   1639 	    return;
   1640 	strncpy(realfname, base, (size_t) len);
   1641 	strncpy(realfname + len, fname, (size_t) fnamelen);
   1642 	realfname[len + fnamelen] = '\0';
   1643     } else {
   1644 	strncpy(realfname, fname, (size_t) fnamelen);
   1645 	realfname[fnamelen] = '\0';
   1646     }
   1647     if (!(str = ReadInFile(realfname)))
   1648 	return;
   1649     GetDatabase(db, str, realfname, True, depth + 1);
   1650     Xfree(str);
   1651 }
   1652 
   1653 XrmDatabase
   1654 XrmGetFileDatabase(
   1655     _Xconst char    *filename)
   1656 {
   1657     XrmDatabase db;
   1658     char *str;
   1659 
   1660     if (!(str = ReadInFile(filename)))
   1661 	return (XrmDatabase)NULL;
   1662 
   1663     db = NewDatabase();
   1664     _XLockMutex(&db->linfo);
   1665     GetDatabase(db, str, filename, True, 0);
   1666     _XUnlockMutex(&db->linfo);
   1667     Xfree(str);
   1668     return db;
   1669 }
   1670 
   1671 Status
   1672 XrmCombineFileDatabase(
   1673     _Xconst char    *filename,
   1674     XrmDatabase     *target,
   1675     Bool             override)
   1676 {
   1677     XrmDatabase db;
   1678     char *str;
   1679 
   1680     if (!(str = ReadInFile(filename)))
   1681 	return 0;
   1682     if (override) {
   1683 	db = *target;
   1684 	if (!db)
   1685 	    *target = db = NewDatabase();
   1686     } else
   1687 	db = NewDatabase();
   1688     _XLockMutex(&db->linfo);
   1689     GetDatabase(db, str, filename, True, 0);
   1690     _XUnlockMutex(&db->linfo);
   1691     Xfree(str);
   1692     if (!override)
   1693 	XrmCombineDatabase(db, target, False);
   1694     return 1;
   1695 }
   1696 
   1697 /* call the user proc for every value in the table, arbitrary order.
   1698  * stop if user proc returns True.  level is current depth in database.
   1699  */
   1700 /*ARGSUSED*/
   1701 static Bool EnumLTable(
   1702     LTable		table,
   1703     XrmNameList		names,
   1704     XrmClassList 	classes,
   1705     register int	level,
   1706     register EClosure	closure)
   1707 {
   1708     register VEntry *bucket;
   1709     register int i;
   1710     register VEntry entry;
   1711     XrmValue value;
   1712     XrmRepresentation type;
   1713     Bool tightOk;
   1714 
   1715     closure->bindings[level] = (table->table.tight ?
   1716 				XrmBindTightly : XrmBindLoosely);
   1717     closure->quarks[level] = table->table.name;
   1718     level++;
   1719     tightOk = !*names;
   1720     closure->quarks[level + 1] = NULLQUARK;
   1721     for (i = table->table.mask, bucket = table->buckets;
   1722 	 i >= 0;
   1723 	 i--, bucket++) {
   1724 	for (entry = *bucket; entry; entry = entry->next) {
   1725 	    if (entry->tight && !tightOk)
   1726 		continue;
   1727 	    closure->bindings[level] = (entry->tight ?
   1728 					XrmBindTightly : XrmBindLoosely);
   1729 	    closure->quarks[level] = entry->name;
   1730 	    value.size = entry->size;
   1731 	    if (entry->string) {
   1732 		type = XrmQString;
   1733 		value.addr = StringValue(entry);
   1734 	    } else {
   1735 		type = RepType(entry);
   1736 		value.addr = DataValue(entry);
   1737 	    }
   1738 	    if ((*closure->proc)(&closure->db, closure->bindings+1,
   1739 				 closure->quarks+1, &type, &value,
   1740 				 closure->closure))
   1741 		return True;
   1742 	}
   1743     }
   1744     return False;
   1745 }
   1746 
   1747 static Bool EnumAllNTable(
   1748     NTable		table,
   1749     register int	level,
   1750     register EClosure	closure)
   1751 {
   1752     register NTable *bucket;
   1753     register int i;
   1754     register NTable entry;
   1755     XrmQuark empty = NULLQUARK;
   1756 
   1757     if (level >= MAXDBDEPTH)
   1758 	return False;
   1759     for (i = table->mask, bucket = NodeBuckets(table);
   1760 	 i >= 0;
   1761 	 i--, bucket++) {
   1762 	for (entry = *bucket; entry; entry = entry->next) {
   1763 	    if (entry->leaf) {
   1764 		if (EnumLTable((LTable)entry, &empty, &empty, level, closure))
   1765 		    return True;
   1766 	    } else {
   1767 		closure->bindings[level] = (entry->tight ?
   1768 					    XrmBindTightly : XrmBindLoosely);
   1769 		closure->quarks[level] = entry->name;
   1770 		if (EnumAllNTable(entry, level+1, closure))
   1771 		    return True;
   1772 	    }
   1773 	}
   1774     }
   1775     return False;
   1776 }
   1777 
   1778 /* recurse on every table in the table, arbitrary order.
   1779  * stop if user proc returns True.  level is current depth in database.
   1780  */
   1781 static Bool EnumNTable(
   1782     NTable		table,
   1783     XrmNameList		names,
   1784     XrmClassList 	classes,
   1785     register int	level,
   1786     register EClosure	closure)
   1787 {
   1788     register NTable	entry;
   1789     register XrmQuark	q;
   1790     register unsigned int leaf;
   1791     Bool (*get)(
   1792             NTable		table,
   1793             XrmNameList		names,
   1794             XrmClassList 	classes,
   1795             register int	level,
   1796             EClosure		closure);
   1797     Bool bilevel;
   1798 
   1799 /* find entries named ename, leafness leaf, tight or loose, and call get */
   1800 #define ITIGHTLOOSE(ename) \
   1801     NFIND(ename); \
   1802     if (entry) { \
   1803 	if (leaf == entry->leaf) { \
   1804 	    if (!leaf && !entry->tight && entry->next && \
   1805 		entry->next->name == q && entry->next->tight && \
   1806 		(bilevel || entry->next->hasloose) && \
   1807 		EnumLTable((LTable)entry->next, names+1, classes+1, \
   1808 			   level, closure)) \
   1809 		return True; \
   1810 	    if ((*get)(entry, names+1, classes+1, level, closure)) \
   1811 		return True; \
   1812 	    if (entry->tight && (entry = entry->next) && \
   1813 		entry->name == q && leaf == entry->leaf && \
   1814 		(*get)(entry, names+1, classes+1, level, closure)) \
   1815 		return True; \
   1816 	} else if (entry->leaf) { \
   1817 	    if ((bilevel || entry->hasloose) && \
   1818 		EnumLTable((LTable)entry, names+1, classes+1, level, closure))\
   1819 		return True; \
   1820 	    if (entry->tight && (entry = entry->next) && \
   1821 		entry->name == q && (bilevel || entry->hasloose) && \
   1822 		EnumLTable((LTable)entry, names+1, classes+1, level, closure))\
   1823 		return True; \
   1824 	} \
   1825     }
   1826 
   1827 /* find entries named ename, leafness leaf, loose only, and call get */
   1828 #define ILOOSE(ename) \
   1829     NFIND(ename); \
   1830     if (entry && entry->tight && (entry = entry->next) && entry->name != q) \
   1831 	entry = (NTable)NULL; \
   1832     if (entry) { \
   1833 	if (leaf == entry->leaf) { \
   1834 	    if ((*get)(entry, names+1, classes+1, level, closure)) \
   1835 		return True; \
   1836 	} else if (entry->leaf && (bilevel || entry->hasloose)) { \
   1837 	    if (EnumLTable((LTable)entry, names+1, classes+1, level, closure))\
   1838 		return True; \
   1839 	} \
   1840     }
   1841 
   1842     if (level >= MAXDBDEPTH)
   1843 	return False;
   1844     closure->bindings[level] = (table->tight ?
   1845 				XrmBindTightly : XrmBindLoosely);
   1846     closure->quarks[level] = table->name;
   1847     level++;
   1848     if (!*names) {
   1849 	if (EnumAllNTable(table, level, closure))
   1850 	    return True;
   1851     } else {
   1852 	if (names[1] || closure->mode == XrmEnumAllLevels) {
   1853 	    get = EnumNTable; /* recurse */
   1854 	    leaf = 0;
   1855 	    bilevel = !names[1];
   1856 	} else {
   1857 	    get = (getNTableEProcp)EnumLTable; /* bottom of recursion */
   1858 	    leaf = 1;
   1859 	    bilevel = False;
   1860 	}
   1861 	if (table->hasloose && closure->mode == XrmEnumAllLevels) {
   1862 	    NTable *bucket;
   1863 	    int i;
   1864 	    XrmQuark empty = NULLQUARK;
   1865 
   1866 	    for (i = table->mask, bucket = NodeBuckets(table);
   1867 		 i >= 0;
   1868 		 i--, bucket++) {
   1869 		q = NULLQUARK;
   1870 		for (entry = *bucket; entry; entry = entry->next) {
   1871 		    if (!entry->tight && entry->name != q &&
   1872 			entry->name != *names && entry->name != *classes) {
   1873 			q = entry->name;
   1874 			if (entry->leaf) {
   1875 			    if (EnumLTable((LTable)entry, &empty, &empty,
   1876 					   level, closure))
   1877 				return True;
   1878 			} else {
   1879 			    if (EnumNTable(entry, &empty, &empty,
   1880 					   level, closure))
   1881 				return True;
   1882 			}
   1883 		    }
   1884 		}
   1885 	    }
   1886 	}
   1887 
   1888 	ITIGHTLOOSE(*names);   /* do name, tight and loose */
   1889 	ITIGHTLOOSE(*classes); /* do class, tight and loose */
   1890 	if (table->hasany) {
   1891 	    ITIGHTLOOSE(XrmQANY); /* do ANY, tight and loose */
   1892 	}
   1893 	if (table->hasloose) {
   1894 	    while (1) {
   1895 		names++;
   1896 		classes++;
   1897 		if (!*names)
   1898 		    break;
   1899 		if (!names[1] && closure->mode != XrmEnumAllLevels) {
   1900 		    get = (getNTableEProcp)EnumLTable; /* bottom of recursion */
   1901 		    leaf = 1;
   1902 		}
   1903 		ILOOSE(*names);   /* loose names */
   1904 		ILOOSE(*classes); /* loose classes */
   1905 		if (table->hasany) {
   1906 		    ILOOSE(XrmQANY); /* loose ANY */
   1907 		}
   1908 	    }
   1909 	    names--;
   1910 	    classes--;
   1911 	}
   1912     }
   1913     /* now look for matching leaf nodes */
   1914     entry = table->next;
   1915     if (!entry)
   1916 	return False;
   1917     if (entry->leaf) {
   1918 	if (entry->tight && !table->tight)
   1919 	    entry = entry->next;
   1920     } else {
   1921 	entry = entry->next;
   1922 	if (!entry || !entry->tight)
   1923 	    return False;
   1924     }
   1925     if (!entry || entry->name != table->name)
   1926 	return False;
   1927     /* found one */
   1928     level--;
   1929     if ((!*names || entry->hasloose) &&
   1930 	EnumLTable((LTable)entry, names, classes, level, closure))
   1931 	return True;
   1932     if (entry->tight && entry == table->next && (entry = entry->next) &&
   1933 	entry->name == table->name && (!*names || entry->hasloose))
   1934 	return EnumLTable((LTable)entry, names, classes, level, closure);
   1935     return False;
   1936 
   1937 #undef ITIGHTLOOSE
   1938 #undef ILOOSE
   1939 }
   1940 
   1941 /* call the proc for every value in the database, arbitrary order.
   1942  * stop if the proc returns True.
   1943  */
   1944 Bool XrmEnumerateDatabase(
   1945     XrmDatabase		db,
   1946     XrmNameList		names,
   1947     XrmClassList	classes,
   1948     int			mode,
   1949     DBEnumProc		proc,
   1950     XPointer		closure)
   1951 {
   1952     XrmBinding  bindings[MAXDBDEPTH+2];
   1953     XrmQuark	quarks[MAXDBDEPTH+2];
   1954     register NTable table;
   1955     EClosureRec	eclosure;
   1956     Bool retval = False;
   1957 
   1958     if (!db)
   1959 	return False;
   1960     _XLockMutex(&db->linfo);
   1961     eclosure.db = db;
   1962     eclosure.proc = proc;
   1963     eclosure.closure = closure;
   1964     eclosure.bindings = bindings;
   1965     eclosure.quarks = quarks;
   1966     eclosure.mode = mode;
   1967     table = db->table;
   1968     if (table && !table->leaf && !*names && mode == XrmEnumOneLevel)
   1969 	table = table->next;
   1970     if (table) {
   1971 	if (!table->leaf)
   1972 	    retval = EnumNTable(table, names, classes, 0, &eclosure);
   1973 	else
   1974 	    retval = EnumLTable((LTable)table, names, classes, 0, &eclosure);
   1975     }
   1976     _XUnlockMutex(&db->linfo);
   1977     return retval;
   1978 }
   1979 
   1980 static void PrintBindingQuarkList(
   1981     XrmBindingList      bindings,
   1982     XrmQuarkList	quarks,
   1983     FILE		*stream)
   1984 {
   1985     Bool	firstNameSeen;
   1986 
   1987     for (firstNameSeen = False; *quarks; bindings++, quarks++) {
   1988 	if (*bindings == XrmBindLoosely) {
   1989 	    (void) fprintf(stream, "*");
   1990 	} else if (firstNameSeen) {
   1991 	    (void) fprintf(stream, ".");
   1992 	}
   1993 	firstNameSeen = True;
   1994 	(void) fputs(XrmQuarkToString(*quarks), stream);
   1995     }
   1996 }
   1997 
   1998 /* output out the entry in correct file syntax */
   1999 /*ARGSUSED*/
   2000 static Bool DumpEntry(
   2001     XrmDatabase		*db,
   2002     XrmBindingList      bindings,
   2003     XrmQuarkList	quarks,
   2004     XrmRepresentation   *type,
   2005     XrmValuePtr		value,
   2006     XPointer		data)
   2007 {
   2008     FILE			*stream = (FILE *)data;
   2009     register unsigned int	i;
   2010     register char		*s;
   2011     register char		c;
   2012 
   2013     if (*type != XrmQString)
   2014 	(void) putc('!', stream);
   2015     PrintBindingQuarkList(bindings, quarks, stream);
   2016     s = value->addr;
   2017     i = value->size;
   2018     if (*type == XrmQString) {
   2019 	(void) fputs(":\t", stream);
   2020 	if (i)
   2021 	    i--;
   2022     }
   2023     else
   2024 	(void) fprintf(stream, "=%s:\t", XrmRepresentationToString(*type));
   2025     if (i && (*s == ' ' || *s == '\t'))
   2026 	(void) putc('\\', stream); /* preserve leading whitespace */
   2027     while (i--) {
   2028 	c = *s++;
   2029 	if (c == '\n') {
   2030 	    if (i)
   2031 		(void) fputs("\\n\\\n", stream);
   2032 	    else
   2033 		(void) fputs("\\n", stream);
   2034 	} else if (c == '\\')
   2035 	    (void) fputs("\\\\", stream);
   2036 	else if ((c < ' ' && c != '\t') ||
   2037 		 ((unsigned char)c >= 0x7f && (unsigned char)c < 0xa0))
   2038 	    (void) fprintf(stream, "\\%03o", (unsigned char)c);
   2039 	else
   2040 	    (void) putc(c, stream);
   2041     }
   2042     (void) putc('\n', stream);
   2043     return ferror(stream) != 0;
   2044 }
   2045 
   2046 #ifdef DEBUG
   2047 
   2048 void PrintTable(
   2049     NTable table,
   2050     FILE *file)
   2051 {
   2052     XrmBinding  bindings[MAXDBDEPTH+1];
   2053     XrmQuark	quarks[MAXDBDEPTH+1];
   2054     EClosureRec closure;
   2055     XrmQuark	empty = NULLQUARK;
   2056 
   2057     closure.db = (XrmDatabase)NULL;
   2058     closure.proc = DumpEntry;
   2059     closure.closure = (XPointer)file;
   2060     closure.bindings = bindings;
   2061     closure.quarks = quarks;
   2062     closure.mode = XrmEnumAllLevels;
   2063     if (table->leaf)
   2064 	EnumLTable((LTable)table, &empty, &empty, 0, &closure);
   2065     else
   2066 	EnumNTable(table, &empty, &empty, 0, &closure);
   2067 }
   2068 
   2069 #endif /* DEBUG */
   2070 
   2071 void
   2072 XrmPutFileDatabase(
   2073     XrmDatabase db,
   2074     _Xconst char *fileName)
   2075 {
   2076     FILE	*file;
   2077     XrmQuark empty = NULLQUARK;
   2078 
   2079     if (!db) return;
   2080     if (!(file = fopen(fileName, "w"))) return;
   2081     if (XrmEnumerateDatabase(db, &empty, &empty, XrmEnumAllLevels,
   2082 			     DumpEntry, (XPointer) file))
   2083 	unlink((char *)fileName);
   2084     fclose(file);
   2085 }
   2086 
   2087 /* macros used in get/search functions */
   2088 
   2089 /* find entries named ename, leafness leaf, tight or loose, and call get */
   2090 #define GTIGHTLOOSE(ename,looseleaf) \
   2091     NFIND(ename); \
   2092     if (entry) { \
   2093 	if (leaf == entry->leaf) { \
   2094 	    if (!leaf && !entry->tight && entry->next && \
   2095 		entry->next->name == q && entry->next->tight && \
   2096 		entry->next->hasloose && \
   2097 		looseleaf((LTable)entry->next, names+1, classes+1, closure)) \
   2098 		return True; \
   2099 	    if ((*get)(entry, names+1, classes+1, closure)) \
   2100 		return True; \
   2101 	    if (entry->tight && (entry = entry->next) && \
   2102 		entry->name == q && leaf == entry->leaf && \
   2103 		(*get)(entry, names+1, classes+1, closure)) \
   2104 		return True; \
   2105 	} else if (entry->leaf) { \
   2106 	    if (entry->hasloose && \
   2107 		looseleaf((LTable)entry, names+1, classes+1, closure)) \
   2108 		return True; \
   2109 	    if (entry->tight && (entry = entry->next) && \
   2110 		entry->name == q && entry->hasloose && \
   2111 		looseleaf((LTable)entry, names+1, classes+1, closure)) \
   2112 		return True; \
   2113 	} \
   2114     }
   2115 
   2116 /* find entries named ename, leafness leaf, loose only, and call get */
   2117 #define GLOOSE(ename,looseleaf) \
   2118     NFIND(ename); \
   2119     if (entry && entry->tight && (entry = entry->next) && entry->name != q) \
   2120 	entry = (NTable)NULL; \
   2121     if (entry) { \
   2122 	if (leaf == entry->leaf) { \
   2123 	    if ((*get)(entry, names+1, classes+1, closure)) \
   2124 		return True; \
   2125 	} else if (entry->leaf && entry->hasloose) { \
   2126 	    if (looseleaf((LTable)entry, names+1, classes+1, closure)) \
   2127 		return True; \
   2128 	} \
   2129     }
   2130 
   2131 /* add tight/loose entry to the search list, return True if list is full */
   2132 /*ARGSUSED*/
   2133 static Bool AppendLEntry(
   2134     LTable		table,
   2135     XrmNameList		names,
   2136     XrmClassList 	classes,
   2137     register SClosure	closure)
   2138 {
   2139     /* check for duplicate */
   2140     if (closure->idx >= 0 && closure->list[closure->idx] == table)
   2141 	return False;
   2142     if (closure->idx == closure->limit)
   2143 	return True;
   2144     /* append it */
   2145     closure->idx++;
   2146     closure->list[closure->idx] = table;
   2147     return False;
   2148 }
   2149 
   2150 /* add loose entry to the search list, return True if list is full */
   2151 /*ARGSUSED*/
   2152 static Bool AppendLooseLEntry(
   2153     LTable		table,
   2154     XrmNameList		names,
   2155     XrmClassList 	classes,
   2156     register SClosure	closure)
   2157 {
   2158     /* check for duplicate */
   2159     if (closure->idx >= 0 && closure->list[closure->idx] == table)
   2160 	return False;
   2161     if (closure->idx >= closure->limit - 1)
   2162 	return True;
   2163     /* append it */
   2164     closure->idx++;
   2165     closure->list[closure->idx] = LOOSESEARCH;
   2166     closure->idx++;
   2167     closure->list[closure->idx] = table;
   2168     return False;
   2169 }
   2170 
   2171 /* search for a leaf table */
   2172 static Bool SearchNEntry(
   2173     NTable		table,
   2174     XrmNameList		names,
   2175     XrmClassList 	classes,
   2176     SClosure		closure)
   2177 {
   2178     register NTable	entry;
   2179     register XrmQuark	q;
   2180     register unsigned int leaf;
   2181     Bool		(*get)(
   2182             NTable		table,
   2183             XrmNameList		names,
   2184             XrmClassList 	classes,
   2185             SClosure		closure);
   2186 
   2187     if (names[1]) {
   2188 	get = SearchNEntry; /* recurse */
   2189 	leaf = 0;
   2190     } else {
   2191 	get = (getNTableSProcp)AppendLEntry; /* bottom of recursion */
   2192 	leaf = 1;
   2193     }
   2194     GTIGHTLOOSE(*names, AppendLooseLEntry);   /* do name, tight and loose */
   2195     GTIGHTLOOSE(*classes, AppendLooseLEntry); /* do class, tight and loose */
   2196     if (table->hasany) {
   2197 	GTIGHTLOOSE(XrmQANY, AppendLooseLEntry); /* do ANY, tight and loose */
   2198     }
   2199     if (table->hasloose) {
   2200 	while (1) {
   2201 	    names++;
   2202 	    classes++;
   2203 	    if (!*names)
   2204 		break;
   2205 	    if (!names[1]) {
   2206 		get = (getNTableSProcp)AppendLEntry; /* bottom of recursion */
   2207 		leaf = 1;
   2208 	    }
   2209 	    GLOOSE(*names, AppendLooseLEntry);   /* loose names */
   2210 	    GLOOSE(*classes, AppendLooseLEntry); /* loose classes */
   2211 	    if (table->hasany) {
   2212 		GLOOSE(XrmQANY, AppendLooseLEntry); /* loose ANY */
   2213 	    }
   2214 	}
   2215     }
   2216     /* now look for matching leaf nodes */
   2217     entry = table->next;
   2218     if (!entry)
   2219 	return False;
   2220     if (entry->leaf) {
   2221 	if (entry->tight && !table->tight)
   2222 	    entry = entry->next;
   2223     } else {
   2224 	entry = entry->next;
   2225 	if (!entry || !entry->tight)
   2226 	    return False;
   2227     }
   2228     if (!entry || entry->name != table->name)
   2229 	return False;
   2230     /* found one */
   2231     if (entry->hasloose &&
   2232 	AppendLooseLEntry((LTable)entry, names, classes, closure))
   2233 	return True;
   2234     if (entry->tight && entry == table->next && (entry = entry->next) &&
   2235 	entry->name == table->name && entry->hasloose)
   2236 	return AppendLooseLEntry((LTable)entry, names, classes, closure);
   2237     return False;
   2238 }
   2239 
   2240 Bool XrmQGetSearchList(
   2241     XrmDatabase     db,
   2242     XrmNameList	    names,
   2243     XrmClassList    classes,
   2244     XrmSearchList   searchList,	/* RETURN */
   2245     int		    listLength)
   2246 {
   2247     register NTable	table;
   2248     SClosureRec		closure;
   2249 
   2250     if (listLength <= 0)
   2251 	return False;
   2252     closure.list = (LTable *)searchList;
   2253     closure.idx = -1;
   2254     closure.limit = listLength - 2;
   2255     if (db) {
   2256 	_XLockMutex(&db->linfo);
   2257 	table = db->table;
   2258 	if (*names) {
   2259 	    if (table && !table->leaf) {
   2260 		if (SearchNEntry(table, names, classes, &closure)) {
   2261 		    _XUnlockMutex(&db->linfo);
   2262 		    return False;
   2263 		}
   2264 	    } else if (table && table->hasloose &&
   2265 		       AppendLooseLEntry((LTable)table, names, classes,
   2266 					 &closure)) {
   2267 		_XUnlockMutex(&db->linfo);
   2268 		return False;
   2269 	    }
   2270 	} else {
   2271 	    if (table && !table->leaf)
   2272 		table = table->next;
   2273 	    if (table &&
   2274 		AppendLEntry((LTable)table, names, classes, &closure)) {
   2275 		_XUnlockMutex(&db->linfo);
   2276 		return False;
   2277 	    }
   2278 	}
   2279 	_XUnlockMutex(&db->linfo);
   2280     }
   2281     closure.list[closure.idx + 1] = (LTable)NULL;
   2282     return True;
   2283 }
   2284 
   2285 Bool XrmQGetSearchResource(
   2286 	     XrmSearchList	searchList,
   2287     register XrmName		name,
   2288     register XrmClass		class,
   2289     	     XrmRepresentation	*pType,  /* RETURN */
   2290     	     XrmValue		*pValue) /* RETURN */
   2291 {
   2292     register LTable *list;
   2293     register LTable table;
   2294     register VEntry entry = NULL;
   2295     int flags;
   2296 
   2297 /* find tight or loose entry */
   2298 #define VTIGHTLOOSE(q) \
   2299     entry = LeafHash(table, q); \
   2300     while (entry && entry->name != q) \
   2301 	entry = entry->next; \
   2302     if (entry) \
   2303 	break
   2304 
   2305 /* find loose entry */
   2306 #define VLOOSE(q) \
   2307     entry = LeafHash(table, q); \
   2308     while (entry && entry->name != q) \
   2309 	entry = entry->next; \
   2310     if (entry) { \
   2311 	if (!entry->tight) \
   2312 	    break; \
   2313 	if ((entry = entry->next) && entry->name == q) \
   2314 	    break; \
   2315     }
   2316 
   2317     list = (LTable *)searchList;
   2318     /* figure out which combination of name and class we need to search for */
   2319     flags = 0;
   2320     if (IsResourceQuark(name))
   2321 	flags = 2;
   2322     if (IsResourceQuark(class))
   2323 	flags |= 1;
   2324     if (!flags) {
   2325 	/* neither name nor class has ever been used to name a resource */
   2326 	table = (LTable)NULL;
   2327     } else if (flags == 3) {
   2328 	/* both name and class */
   2329 	while ((table = *list++)) {
   2330 	    if (table != LOOSESEARCH) {
   2331 		VTIGHTLOOSE(name);  /* do name, tight and loose */
   2332 		VTIGHTLOOSE(class); /* do class, tight and loose */
   2333 	    } else {
   2334 		table = *list++;
   2335 		VLOOSE(name);  /* do name, loose only */
   2336 		VLOOSE(class); /* do class, loose only */
   2337 	    }
   2338 	}
   2339     } else {
   2340 	/* just one of name or class */
   2341 	if (flags == 1)
   2342 	    name = class;
   2343 	while ((table = *list++)) {
   2344 	    if (table != LOOSESEARCH) {
   2345 		VTIGHTLOOSE(name); /* tight and loose */
   2346 	    } else {
   2347 		table = *list++;
   2348 		VLOOSE(name); /* loose only */
   2349 	    }
   2350 	}
   2351     }
   2352     if (table) {
   2353 	/* found a match */
   2354 	if (entry->string) {
   2355 	    *pType = XrmQString;
   2356 	    pValue->addr = StringValue(entry);
   2357 	} else {
   2358 	    *pType = RepType(entry);
   2359 	    pValue->addr = DataValue(entry);
   2360 	}
   2361 	pValue->size = entry->size;
   2362 	return True;
   2363     }
   2364     *pType = NULLQUARK;
   2365     pValue->addr = (XPointer)NULL;
   2366     pValue->size = 0;
   2367     return False;
   2368 
   2369 #undef VTIGHTLOOSE
   2370 #undef VLOOSE
   2371 }
   2372 
   2373 /* look for a tight/loose value */
   2374 static Bool GetVEntry(
   2375     LTable		table,
   2376     XrmNameList		names,
   2377     XrmClassList 	classes,
   2378     VClosure		closure)
   2379 {
   2380     register VEntry entry;
   2381     register XrmQuark q;
   2382 
   2383     /* try name first */
   2384     q = *names;
   2385     entry = LeafHash(table, q);
   2386     while (entry && entry->name != q)
   2387 	entry = entry->next;
   2388     if (!entry) {
   2389 	/* not found, try class */
   2390 	q = *classes;
   2391 	entry = LeafHash(table, q);
   2392 	while (entry && entry->name != q)
   2393 	    entry = entry->next;
   2394 	if (!entry)
   2395 	    return False;
   2396     }
   2397     if (entry->string) {
   2398 	*closure->type = XrmQString;
   2399 	closure->value->addr = StringValue(entry);
   2400     } else {
   2401 	*closure->type = RepType(entry);
   2402 	closure->value->addr = DataValue(entry);
   2403     }
   2404     closure->value->size = entry->size;
   2405     return True;
   2406 }
   2407 
   2408 /* look for a loose value */
   2409 static Bool GetLooseVEntry(
   2410     LTable		table,
   2411     XrmNameList		names,
   2412     XrmClassList 	classes,
   2413     VClosure		closure)
   2414 {
   2415     register VEntry	entry;
   2416     register XrmQuark	q;
   2417 
   2418 #define VLOOSE(ename) \
   2419     q = ename; \
   2420     entry = LeafHash(table, q); \
   2421     while (entry && entry->name != q) \
   2422 	entry = entry->next; \
   2423     if (entry && entry->tight && (entry = entry->next) && entry->name != q) \
   2424 	entry = (VEntry)NULL;
   2425 
   2426     /* bump to last component */
   2427     while (names[1]) {
   2428 	names++;
   2429 	classes++;
   2430     }
   2431     VLOOSE(*names);  /* do name, loose only */
   2432     if (!entry) {
   2433 	VLOOSE(*classes); /* do class, loose only */
   2434 	if (!entry)
   2435 	    return False;
   2436     }
   2437     if (entry->string) {
   2438 	*closure->type = XrmQString;
   2439 	closure->value->addr = StringValue(entry);
   2440     } else {
   2441 	*closure->type = RepType(entry);
   2442 	closure->value->addr = DataValue(entry);
   2443     }
   2444     closure->value->size = entry->size;
   2445     return True;
   2446 
   2447 #undef VLOOSE
   2448 }
   2449 
   2450 /* recursive search for a value */
   2451 static Bool GetNEntry(
   2452     NTable		table,
   2453     XrmNameList		names,
   2454     XrmClassList 	classes,
   2455     VClosure		closure)
   2456 {
   2457     register NTable	entry;
   2458     register XrmQuark	q;
   2459     register unsigned int leaf;
   2460     Bool		(*get)(
   2461             NTable              table,
   2462             XrmNameList         names,
   2463             XrmClassList        classes,
   2464             VClosure            closure);
   2465     NTable		otable;
   2466 
   2467     if (names[2]) {
   2468 	get = GetNEntry; /* recurse */
   2469 	leaf = 0;
   2470     } else {
   2471 	get = (getNTableVProcp)GetVEntry; /* bottom of recursion */
   2472 	leaf = 1;
   2473     }
   2474     GTIGHTLOOSE(*names, GetLooseVEntry);   /* do name, tight and loose */
   2475     GTIGHTLOOSE(*classes, GetLooseVEntry); /* do class, tight and loose */
   2476     if (table->hasany) {
   2477 	GTIGHTLOOSE(XrmQANY, GetLooseVEntry); /* do ANY, tight and loose */
   2478     }
   2479     if (table->hasloose) {
   2480 	while (1) {
   2481 	    names++;
   2482 	    classes++;
   2483 	    if (!names[1])
   2484 		break;
   2485 	    if (!names[2]) {
   2486 		get = (getNTableVProcp)GetVEntry; /* bottom of recursion */
   2487 		leaf = 1;
   2488 	    }
   2489 	    GLOOSE(*names, GetLooseVEntry);   /* do name, loose only */
   2490 	    GLOOSE(*classes, GetLooseVEntry); /* do class, loose only */
   2491 	    if (table->hasany) {
   2492 		GLOOSE(XrmQANY, GetLooseVEntry); /* do ANY, loose only */
   2493 	    }
   2494 	}
   2495     }
   2496     /* look for matching leaf tables */
   2497     otable = table;
   2498     table = table->next;
   2499     if (!table)
   2500 	return False;
   2501     if (table->leaf) {
   2502 	if (table->tight && !otable->tight)
   2503 	    table = table->next;
   2504     } else {
   2505 	table = table->next;
   2506 	if (!table || !table->tight)
   2507 	    return False;
   2508     }
   2509     if (!table || table->name != otable->name)
   2510 	return False;
   2511     /* found one */
   2512     if (table->hasloose &&
   2513 	GetLooseVEntry((LTable)table, names, classes, closure))
   2514 	return True;
   2515     if (table->tight && table == otable->next) {
   2516 	table = table->next;
   2517 	if (table && table->name == otable->name && table->hasloose)
   2518 	    return GetLooseVEntry((LTable)table, names, classes, closure);
   2519     }
   2520     return False;
   2521 }
   2522 
   2523 Bool XrmQGetResource(
   2524     XrmDatabase         db,
   2525     XrmNameList		names,
   2526     XrmClassList 	classes,
   2527     XrmRepresentation	*pType,  /* RETURN */
   2528     XrmValuePtr		pValue)  /* RETURN */
   2529 {
   2530     register NTable table;
   2531     VClosureRec closure;
   2532 
   2533     if (db && *names) {
   2534 	_XLockMutex(&db->linfo);
   2535 	closure.type = pType;
   2536 	closure.value = pValue;
   2537 	table = db->table;
   2538 	if (names[1]) {
   2539 	    if (table && !table->leaf) {
   2540 		if (GetNEntry(table, names, classes, &closure)) {
   2541 		    _XUnlockMutex(&db->linfo);
   2542 		    return True;
   2543 		}
   2544 	    } else if (table && table->hasloose &&
   2545 		    GetLooseVEntry((LTable)table, names, classes, &closure)) {
   2546 		_XUnlockMutex (&db->linfo);
   2547 		return True;
   2548 	    }
   2549 	} else {
   2550 	    if (table && !table->leaf)
   2551 		table = table->next;
   2552 	    if (table && GetVEntry((LTable)table, names, classes, &closure)) {
   2553 		_XUnlockMutex(&db->linfo);
   2554 		return True;
   2555 	    }
   2556 	}
   2557 	_XUnlockMutex(&db->linfo);
   2558     }
   2559     *pType = NULLQUARK;
   2560     pValue->addr = (XPointer)NULL;
   2561     pValue->size = 0;
   2562     return False;
   2563 }
   2564 
   2565 Bool
   2566 XrmGetResource(XrmDatabase db, _Xconst char *name_str, _Xconst char *class_str,
   2567 	       XrmString *pType_str, XrmValuePtr pValue)
   2568 {
   2569     XrmName		names[MAXDBDEPTH+1];
   2570     XrmClass		classes[MAXDBDEPTH+1];
   2571     XrmRepresentation   fromType;
   2572     Bool		result;
   2573 
   2574     XrmStringToNameList(name_str, names);
   2575     XrmStringToClassList(class_str, classes);
   2576     result = XrmQGetResource(db, names, classes, &fromType, pValue);
   2577     (*pType_str) = XrmQuarkToString(fromType);
   2578     return result;
   2579 }
   2580 
   2581 /* destroy all values, plus table itself */
   2582 static void DestroyLTable(
   2583     LTable table)
   2584 {
   2585     register int i;
   2586     register VEntry *buckets;
   2587     register VEntry entry, next;
   2588 
   2589     buckets = table->buckets;
   2590     for (i = table->table.mask; i >= 0; i--, buckets++) {
   2591 	for (next = *buckets; (entry = next); ) {
   2592 	    next = entry->next;
   2593 	    Xfree(entry);
   2594 	}
   2595     }
   2596     Xfree(table->buckets);
   2597     Xfree(table);
   2598 }
   2599 
   2600 /* destroy all contained tables, plus table itself */
   2601 static void DestroyNTable(
   2602     NTable table)
   2603 {
   2604     register int i;
   2605     register NTable *buckets;
   2606     register NTable entry, next;
   2607 
   2608     buckets = NodeBuckets(table);
   2609     for (i = table->mask; i >= 0; i--, buckets++) {
   2610 	for (next = *buckets; (entry = next); ) {
   2611 	    next = entry->next;
   2612 	    if (entry->leaf)
   2613 		DestroyLTable((LTable)entry);
   2614 	    else
   2615 		DestroyNTable(entry);
   2616 	}
   2617     }
   2618     Xfree(table);
   2619 }
   2620 
   2621 const char *
   2622 XrmLocaleOfDatabase(
   2623     XrmDatabase db)
   2624 {
   2625     const char* retval;
   2626     _XLockMutex(&db->linfo);
   2627     retval = (*db->methods->lcname)(db->mbstate);
   2628     _XUnlockMutex(&db->linfo);
   2629     return retval;
   2630 }
   2631 
   2632 void XrmDestroyDatabase(
   2633     XrmDatabase   db)
   2634 {
   2635     register NTable table, next;
   2636 
   2637     if (db) {
   2638 	_XLockMutex(&db->linfo);
   2639 	for (next = db->table; (table = next); ) {
   2640 	    next = table->next;
   2641 	    if (table->leaf)
   2642 		DestroyLTable((LTable)table);
   2643 	    else
   2644 		DestroyNTable(table);
   2645 	}
   2646 	_XUnlockMutex(&db->linfo);
   2647 	_XFreeMutex(&db->linfo);
   2648 	(*db->methods->destroy)(db->mbstate);
   2649 	Xfree(db);
   2650     }
   2651 }
   2652