Xrm.c revision eb411b4b
1
2/***********************************************************
3Copyright 1987, 1988, 1990 by Digital Equipment Corporation, Maynard
4
5                        All Rights Reserved
6
7Permission to use, copy, modify, and distribute this software and its
8documentation for any purpose and without fee is hereby granted,
9provided that the above copyright notice appear in all copies and that
10both that copyright notice and this permission notice appear in
11supporting documentation, and that the name Digital not be
12used in advertising or publicity pertaining to distribution of the
13software without specific, written prior permission.
14
15DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
16ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
17DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
18ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
19WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
20ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
21SOFTWARE.
22
23******************************************************************/
24/*
25
26Copyright 1987, 1988, 1990, 1998  The Open Group
27
28Permission to use, copy, modify, distribute, and sell this software and its
29documentation for any purpose is hereby granted without fee, provided that
30the above copyright notice appear in all copies and that both that
31copyright notice and this permission notice appear in supporting
32documentation.
33
34The above copyright notice and this permission notice shall be included
35in all copies or substantial portions of the Software.
36
37THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
38OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
39MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
40IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
41OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
42ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
43OTHER DEALINGS IN THE SOFTWARE.
44
45Except as contained in this notice, the name of The Open Group shall
46not be used in advertising or otherwise to promote the sale, use or
47other dealings in this Software without prior written authorization
48from 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
71These Xrm routines allow very fast lookup of resources in the resource
72database.  Several usage patterns are exploited:
73
74(1) Widgets get a lot of resources at one time.  Rather than look up each from
75scratch, we can precompute the prioritized list of database levels once, then
76search 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
79point in looking for resources on a level that doesn't contain any.  This
80information is kept on a per-level basis.
81
82(3) Sometimes the widget instance tree is structured such that you get the same
83class name repeated on the fully qualified widget name.  This can result in the
84same database level occuring multiple times on the search list.  The code below
85only checks to see if you get two identical search lists in a row, rather than
86look back through all database levels, but in practice this removes all
87duplicates I've ever observed.
88
89Joel McCormack
90
91*/
92
93/*
94
95The Xrm representation has been completely redesigned to substantially reduce
96memory and hopefully improve performance.
97
98The database is structured into two kinds of tables: LTables that contain
99only values, and NTables that contain only other tables.
100
101Some invariants:
102
103The next pointer of the top-level node table points to the top-level leaf
104table, if any.
105
106Within an LTable, for a given name, the tight value always precedes the
107loose value, and if both are present the loose value is always right after
108the tight value.
109
110Within an NTable, all of the entries for a given name are contiguous,
111in the order tight NTable, loose NTable, tight LTable, loose LTable.
112
113Bob Scheifler
114
115*/
116
117static XrmQuark XrmQString, XrmQANY;
118
119typedef	Bool (*DBEnumProc)(
120    XrmDatabase*	/* db */,
121    XrmBindingList	/* bindings */,
122    XrmQuarkList	/* quarks */,
123    XrmRepresentation*	/* type */,
124    XrmValue*		/* value */,
125    XPointer		/* closure */
126);
127
128typedef 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
137typedef 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
149typedef 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 */
171typedef 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 */
184typedef 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 */
194typedef struct _VClosure {
195    XrmRepresentation	*type;		/* type of value */
196    XrmValuePtr		value;		/* value itself */
197} VClosureRec, *VClosure;
198
199/* closure used in get search list */
200typedef 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 */
210typedef 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 */
220typedef Bool (*getNTableSProcp)(
221    NTable              table,
222    XrmNameList         names,
223    XrmClassList        classes,
224    SClosure            closure);
225typedef Bool (*getNTableVProcp)(
226    NTable              table,
227    XrmNameList         names,
228    XrmClassList        classes,
229    VClosure            closure);
230typedef 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
268static unsigned char *resourceQuarks = (unsigned char *)NULL;
269static 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
275typedef 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 */
305static XrmBits const xrmtypes[256] = {
306    EOS,0,0,0,0,0,0,0,
307    0,SPACE,EOL,0,0,
308#if defined(WIN32) || defined(__UNIXOS2__)
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
331void XrmInitialize(void)
332{
333    XrmQString = XrmPermStringToQuark("String");
334    XrmQANY = XrmPermStringToQuark("?");
335}
336
337XrmDatabase XrmGetDatabase(
338    Display *display)
339{
340    XrmDatabase retval;
341    LockDisplay(display);
342    retval = display->db;
343    UnlockDisplay(display);
344    return retval;
345}
346
347void XrmSetDatabase(
348    Display *display,
349    XrmDatabase database)
350{
351    LockDisplay(display);
352    /* destroy database if set up imlicitely 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
361void
362XrmStringToQuarkList(
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
394void
395XrmStringToBindingQuarkList(
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
439static 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
462static void
463c_mbnoop(
464    XPointer state)
465{
466}
467
468static char
469c_mbchar(
470    XPointer state,
471    const char *str,
472    int *lenp)
473{
474    *lenp = 1;
475    return *str;
476}
477
478static const char *
479c_lcname(
480    XPointer state)
481{
482    return "C";
483}
484
485static 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
494static 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 quaranteed empty to start with.
512 */
513static 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((char *)ftable->buckets);
535}
536
537/* move all tables from ftable to ttable, and free ftable.
538 * ttable is quaranteed empty to start with.
539 */
540static 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((char *)ftable);
562}
563
564/* grow the table, based on current number of entries */
565static 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 */
606static 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((char *)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((char *)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((char *)ftable->buckets);
680    Xfree((char *)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 */
686static 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((char *)ftable);
755    /* resize if necessary, now that we're all done */
756    GROW(pprev);
757}
758
759void 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((char *)from);
805	_XUnlockMutex(&(*into)->linfo);
806    }
807}
808
809void 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 */
816static 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((char *)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
1009void 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
1022void
1023XrmPutResource(
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
1039void
1040XrmQPutStringResource(
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 = 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
1085static void GetIncludeFile(
1086    XrmDatabase db,
1087    _Xconst char *base,
1088    _Xconst char *fname,
1089    int fnamelen,
1090    int depth);
1091
1092static 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 occured, 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 occured.
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
1522void
1523XrmPutStringResource(
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 = strlen(str)+1;
1536    _XLockMutex(&(*pdb)->linfo);
1537    PutEntry(*pdb, bindings, quarks, XrmQString, &value);
1538    _XUnlockMutex(&(*pdb)->linfo);
1539}
1540
1541
1542void
1543XrmPutLineResource(
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
1553XrmDatabase
1554XrmGetStringDatabase(
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
1572static char *
1573ReadInFile(_Xconst char *filename)
1574{
1575    register int fd, size;
1576    char * filebuf;
1577
1578#ifdef __UNIXOS2__
1579    filename = __XOS2RedirRoot(filename);
1580#endif
1581
1582    /*
1583     * MS-Windows and OS/2 note: Default open mode includes O_TEXT
1584     */
1585    if ( (fd = _XOpenFile (filename, O_RDONLY)) == -1 )
1586	return (char *)NULL;
1587
1588    /*
1589     * MS-Windows and OS/2 note: depending on how the sources are
1590     * untarred, the newlines in resource files may or may not have
1591     * been expanded to CRLF. Either way the size returned by fstat
1592     * is sufficient to read the file into because in text-mode any
1593     * CRLFs in a file will be converted to newlines (LF) with the
1594     * result that the number of bytes actually read with be <=
1595     * to the size returned by fstat.
1596     */
1597    {
1598	struct stat status_buffer;
1599	if ( ((fstat(fd, &status_buffer)) == -1 ) ||
1600             (status_buffer.st_size >= INT_MAX) ) {
1601	    close (fd);
1602	    return (char *)NULL;
1603	} else
1604	    size = (int) status_buffer.st_size;
1605    }
1606
1607    if (!(filebuf = Xmalloc(size + 1))) { /* leave room for '\0' */
1608	close(fd);
1609	return (char *)NULL;
1610    }
1611    size = read (fd, filebuf, size);
1612
1613#ifdef __UNIXOS2__
1614    { /* kill CRLF */
1615      int i,k;
1616      for (i=k=0; i<size; i++)
1617	if (filebuf[i] != 0x0d) {
1618	   filebuf[k++] = filebuf[i];
1619	}
1620	filebuf[k] = 0;
1621    }
1622#endif
1623
1624    if (size < 0) {
1625	close (fd);
1626	Xfree(filebuf);
1627	return (char *)NULL;
1628    }
1629    close (fd);
1630
1631    filebuf[size] = '\0';	/* NULL terminate it. */
1632    return filebuf;
1633}
1634
1635static void
1636GetIncludeFile(
1637    XrmDatabase db,
1638    _Xconst char *base,
1639    _Xconst char *fname,
1640    int fnamelen,
1641    int depth)
1642{
1643    int len;
1644    char *str;
1645    char realfname[BUFSIZ];
1646
1647    if (fnamelen <= 0 || fnamelen >= BUFSIZ)
1648	return;
1649    if (depth >= MAXDBDEPTH)
1650	return;
1651    if (*fname != '/' && base && (str = strrchr(base, '/'))) {
1652	len = str - base + 1;
1653	if (len + fnamelen >= BUFSIZ)
1654	    return;
1655	strncpy(realfname, base, len);
1656	strncpy(realfname + len, fname, fnamelen);
1657	realfname[len + fnamelen] = '\0';
1658    } else {
1659	strncpy(realfname, fname, fnamelen);
1660	realfname[fnamelen] = '\0';
1661    }
1662    if (!(str = ReadInFile(realfname)))
1663	return;
1664    GetDatabase(db, str, realfname, True, depth + 1);
1665    Xfree(str);
1666}
1667
1668XrmDatabase
1669XrmGetFileDatabase(
1670    _Xconst char    *filename)
1671{
1672    XrmDatabase db;
1673    char *str;
1674
1675    if (!(str = ReadInFile(filename)))
1676	return (XrmDatabase)NULL;
1677
1678    db = NewDatabase();
1679    _XLockMutex(&db->linfo);
1680    GetDatabase(db, str, filename, True, 0);
1681    _XUnlockMutex(&db->linfo);
1682    Xfree(str);
1683    return db;
1684}
1685
1686Status
1687XrmCombineFileDatabase(
1688    _Xconst char    *filename,
1689    XrmDatabase     *target,
1690    Bool             override)
1691{
1692    XrmDatabase db;
1693    char *str;
1694
1695    if (!(str = ReadInFile(filename)))
1696	return 0;
1697    if (override) {
1698	db = *target;
1699	if (!db)
1700	    *target = db = NewDatabase();
1701    } else
1702	db = NewDatabase();
1703    _XLockMutex(&db->linfo);
1704    GetDatabase(db, str, filename, True, 0);
1705    _XUnlockMutex(&db->linfo);
1706    Xfree(str);
1707    if (!override)
1708	XrmCombineDatabase(db, target, False);
1709    return 1;
1710}
1711
1712/* call the user proc for every value in the table, arbitrary order.
1713 * stop if user proc returns True.  level is current depth in database.
1714 */
1715/*ARGSUSED*/
1716static Bool EnumLTable(
1717    LTable		table,
1718    XrmNameList		names,
1719    XrmClassList 	classes,
1720    register int	level,
1721    register EClosure	closure)
1722{
1723    register VEntry *bucket;
1724    register int i;
1725    register VEntry entry;
1726    XrmValue value;
1727    XrmRepresentation type;
1728    Bool tightOk;
1729
1730    closure->bindings[level] = (table->table.tight ?
1731				XrmBindTightly : XrmBindLoosely);
1732    closure->quarks[level] = table->table.name;
1733    level++;
1734    tightOk = !*names;
1735    closure->quarks[level + 1] = NULLQUARK;
1736    for (i = table->table.mask, bucket = table->buckets;
1737	 i >= 0;
1738	 i--, bucket++) {
1739	for (entry = *bucket; entry; entry = entry->next) {
1740	    if (entry->tight && !tightOk)
1741		continue;
1742	    closure->bindings[level] = (entry->tight ?
1743					XrmBindTightly : XrmBindLoosely);
1744	    closure->quarks[level] = entry->name;
1745	    value.size = entry->size;
1746	    if (entry->string) {
1747		type = XrmQString;
1748		value.addr = StringValue(entry);
1749	    } else {
1750		type = RepType(entry);
1751		value.addr = DataValue(entry);
1752	    }
1753	    if ((*closure->proc)(&closure->db, closure->bindings+1,
1754				 closure->quarks+1, &type, &value,
1755				 closure->closure))
1756		return True;
1757	}
1758    }
1759    return False;
1760}
1761
1762static Bool EnumAllNTable(
1763    NTable		table,
1764    register int	level,
1765    register EClosure	closure)
1766{
1767    register NTable *bucket;
1768    register int i;
1769    register NTable entry;
1770    XrmQuark empty = NULLQUARK;
1771
1772    if (level >= MAXDBDEPTH)
1773	return False;
1774    for (i = table->mask, bucket = NodeBuckets(table);
1775	 i >= 0;
1776	 i--, bucket++) {
1777	for (entry = *bucket; entry; entry = entry->next) {
1778	    if (entry->leaf) {
1779		if (EnumLTable((LTable)entry, &empty, &empty, level, closure))
1780		    return True;
1781	    } else {
1782		closure->bindings[level] = (entry->tight ?
1783					    XrmBindTightly : XrmBindLoosely);
1784		closure->quarks[level] = entry->name;
1785		if (EnumAllNTable(entry, level+1, closure))
1786		    return True;
1787	    }
1788	}
1789    }
1790    return False;
1791}
1792
1793/* recurse on every table in the table, arbitrary order.
1794 * stop if user proc returns True.  level is current depth in database.
1795 */
1796static Bool EnumNTable(
1797    NTable		table,
1798    XrmNameList		names,
1799    XrmClassList 	classes,
1800    register int	level,
1801    register EClosure	closure)
1802{
1803    register NTable	entry;
1804    register XrmQuark	q;
1805    register unsigned int leaf;
1806    Bool (*get)(
1807            NTable		table,
1808            XrmNameList		names,
1809            XrmClassList 	classes,
1810            register int	level,
1811            EClosure		closure);
1812    Bool bilevel;
1813
1814/* find entries named ename, leafness leaf, tight or loose, and call get */
1815#define ITIGHTLOOSE(ename) \
1816    NFIND(ename); \
1817    if (entry) { \
1818	if (leaf == entry->leaf) { \
1819	    if (!leaf && !entry->tight && entry->next && \
1820		entry->next->name == q && entry->next->tight && \
1821		(bilevel || entry->next->hasloose) && \
1822		EnumLTable((LTable)entry->next, names+1, classes+1, \
1823			   level, closure)) \
1824		return True; \
1825	    if ((*get)(entry, names+1, classes+1, level, closure)) \
1826		return True; \
1827	    if (entry->tight && (entry = entry->next) && \
1828		entry->name == q && leaf == entry->leaf && \
1829		(*get)(entry, names+1, classes+1, level, closure)) \
1830		return True; \
1831	} else if (entry->leaf) { \
1832	    if ((bilevel || entry->hasloose) && \
1833		EnumLTable((LTable)entry, names+1, classes+1, level, closure))\
1834		return True; \
1835	    if (entry->tight && (entry = entry->next) && \
1836		entry->name == q && (bilevel || entry->hasloose) && \
1837		EnumLTable((LTable)entry, names+1, classes+1, level, closure))\
1838		return True; \
1839	} \
1840    }
1841
1842/* find entries named ename, leafness leaf, loose only, and call get */
1843#define ILOOSE(ename) \
1844    NFIND(ename); \
1845    if (entry && entry->tight && (entry = entry->next) && entry->name != q) \
1846	entry = (NTable)NULL; \
1847    if (entry) { \
1848	if (leaf == entry->leaf) { \
1849	    if ((*get)(entry, names+1, classes+1, level, closure)) \
1850		return True; \
1851	} else if (entry->leaf && (bilevel || entry->hasloose)) { \
1852	    if (EnumLTable((LTable)entry, names+1, classes+1, level, closure))\
1853		return True; \
1854	} \
1855    }
1856
1857    if (level >= MAXDBDEPTH)
1858	return False;
1859    closure->bindings[level] = (table->tight ?
1860				XrmBindTightly : XrmBindLoosely);
1861    closure->quarks[level] = table->name;
1862    level++;
1863    if (!*names) {
1864	if (EnumAllNTable(table, level, closure))
1865	    return True;
1866    } else {
1867	if (names[1] || closure->mode == XrmEnumAllLevels) {
1868	    get = EnumNTable; /* recurse */
1869	    leaf = 0;
1870	    bilevel = !names[1];
1871	} else {
1872	    get = (getNTableEProcp)EnumLTable; /* bottom of recursion */
1873	    leaf = 1;
1874	    bilevel = False;
1875	}
1876	if (table->hasloose && closure->mode == XrmEnumAllLevels) {
1877	    NTable *bucket;
1878	    int i;
1879	    XrmQuark empty = NULLQUARK;
1880
1881	    for (i = table->mask, bucket = NodeBuckets(table);
1882		 i >= 0;
1883		 i--, bucket++) {
1884		q = NULLQUARK;
1885		for (entry = *bucket; entry; entry = entry->next) {
1886		    if (!entry->tight && entry->name != q &&
1887			entry->name != *names && entry->name != *classes) {
1888			q = entry->name;
1889			if (entry->leaf) {
1890			    if (EnumLTable((LTable)entry, &empty, &empty,
1891					   level, closure))
1892				return True;
1893			} else {
1894			    if (EnumNTable(entry, &empty, &empty,
1895					   level, closure))
1896				return True;
1897			}
1898		    }
1899		}
1900	    }
1901	}
1902
1903	ITIGHTLOOSE(*names);   /* do name, tight and loose */
1904	ITIGHTLOOSE(*classes); /* do class, tight and loose */
1905	if (table->hasany) {
1906	    ITIGHTLOOSE(XrmQANY); /* do ANY, tight and loose */
1907	}
1908	if (table->hasloose) {
1909	    while (1) {
1910		names++;
1911		classes++;
1912		if (!*names)
1913		    break;
1914		if (!names[1] && closure->mode != XrmEnumAllLevels) {
1915		    get = (getNTableEProcp)EnumLTable; /* bottom of recursion */
1916		    leaf = 1;
1917		}
1918		ILOOSE(*names);   /* loose names */
1919		ILOOSE(*classes); /* loose classes */
1920		if (table->hasany) {
1921		    ILOOSE(XrmQANY); /* loose ANY */
1922		}
1923	    }
1924	    names--;
1925	    classes--;
1926	}
1927    }
1928    /* now look for matching leaf nodes */
1929    entry = table->next;
1930    if (!entry)
1931	return False;
1932    if (entry->leaf) {
1933	if (entry->tight && !table->tight)
1934	    entry = entry->next;
1935    } else {
1936	entry = entry->next;
1937	if (!entry || !entry->tight)
1938	    return False;
1939    }
1940    if (!entry || entry->name != table->name)
1941	return False;
1942    /* found one */
1943    level--;
1944    if ((!*names || entry->hasloose) &&
1945	EnumLTable((LTable)entry, names, classes, level, closure))
1946	return True;
1947    if (entry->tight && entry == table->next && (entry = entry->next) &&
1948	entry->name == table->name && (!*names || entry->hasloose))
1949	return EnumLTable((LTable)entry, names, classes, level, closure);
1950    return False;
1951
1952#undef ITIGHTLOOSE
1953#undef ILOOSE
1954}
1955
1956/* call the proc for every value in the database, arbitrary order.
1957 * stop if the proc returns True.
1958 */
1959Bool XrmEnumerateDatabase(
1960    XrmDatabase		db,
1961    XrmNameList		names,
1962    XrmClassList	classes,
1963    int			mode,
1964    DBEnumProc		proc,
1965    XPointer		closure)
1966{
1967    XrmBinding  bindings[MAXDBDEPTH+2];
1968    XrmQuark	quarks[MAXDBDEPTH+2];
1969    register NTable table;
1970    EClosureRec	eclosure;
1971    Bool retval = False;
1972
1973    if (!db)
1974	return False;
1975    _XLockMutex(&db->linfo);
1976    eclosure.db = db;
1977    eclosure.proc = proc;
1978    eclosure.closure = closure;
1979    eclosure.bindings = bindings;
1980    eclosure.quarks = quarks;
1981    eclosure.mode = mode;
1982    table = db->table;
1983    if (table && !table->leaf && !*names && mode == XrmEnumOneLevel)
1984	table = table->next;
1985    if (table) {
1986	if (!table->leaf)
1987	    retval = EnumNTable(table, names, classes, 0, &eclosure);
1988	else
1989	    retval = EnumLTable((LTable)table, names, classes, 0, &eclosure);
1990    }
1991    _XUnlockMutex(&db->linfo);
1992    return retval;
1993}
1994
1995static void PrintBindingQuarkList(
1996    XrmBindingList      bindings,
1997    XrmQuarkList	quarks,
1998    FILE		*stream)
1999{
2000    Bool	firstNameSeen;
2001
2002    for (firstNameSeen = False; *quarks; bindings++, quarks++) {
2003	if (*bindings == XrmBindLoosely) {
2004	    (void) fprintf(stream, "*");
2005	} else if (firstNameSeen) {
2006	    (void) fprintf(stream, ".");
2007	}
2008	firstNameSeen = True;
2009	(void) fputs(XrmQuarkToString(*quarks), stream);
2010    }
2011}
2012
2013/* output out the entry in correct file syntax */
2014/*ARGSUSED*/
2015static Bool DumpEntry(
2016    XrmDatabase		*db,
2017    XrmBindingList      bindings,
2018    XrmQuarkList	quarks,
2019    XrmRepresentation   *type,
2020    XrmValuePtr		value,
2021    XPointer		data)
2022{
2023    FILE			*stream = (FILE *)data;
2024    register unsigned int	i;
2025    register char		*s;
2026    register char		c;
2027
2028    if (*type != XrmQString)
2029	(void) putc('!', stream);
2030    PrintBindingQuarkList(bindings, quarks, stream);
2031    s = value->addr;
2032    i = value->size;
2033    if (*type == XrmQString) {
2034	(void) fputs(":\t", stream);
2035	if (i)
2036	    i--;
2037    }
2038    else
2039	(void) fprintf(stream, "=%s:\t", XrmRepresentationToString(*type));
2040    if (i && (*s == ' ' || *s == '\t'))
2041	(void) putc('\\', stream); /* preserve leading whitespace */
2042    while (i--) {
2043	c = *s++;
2044	if (c == '\n') {
2045	    if (i)
2046		(void) fputs("\\n\\\n", stream);
2047	    else
2048		(void) fputs("\\n", stream);
2049	} else if (c == '\\')
2050	    (void) fputs("\\\\", stream);
2051	else if ((c < ' ' && c != '\t') ||
2052		 ((unsigned char)c >= 0x7f && (unsigned char)c < 0xa0))
2053	    (void) fprintf(stream, "\\%03o", (unsigned char)c);
2054	else
2055	    (void) putc(c, stream);
2056    }
2057    (void) putc('\n', stream);
2058    return ferror(stream) != 0;
2059}
2060
2061#ifdef DEBUG
2062
2063void PrintTable(
2064    NTable table,
2065    FILE *file)
2066{
2067    XrmBinding  bindings[MAXDBDEPTH+1];
2068    XrmQuark	quarks[MAXDBDEPTH+1];
2069    EClosureRec closure;
2070    XrmQuark	empty = NULLQUARK;
2071
2072    closure.db = (XrmDatabase)NULL;
2073    closure.proc = DumpEntry;
2074    closure.closure = (XPointer)file;
2075    closure.bindings = bindings;
2076    closure.quarks = quarks;
2077    closure.mode = XrmEnumAllLevels;
2078    if (table->leaf)
2079	EnumLTable((LTable)table, &empty, &empty, 0, &closure);
2080    else
2081	EnumNTable(table, &empty, &empty, 0, &closure);
2082}
2083
2084#endif /* DEBUG */
2085
2086void
2087XrmPutFileDatabase(
2088    XrmDatabase db,
2089    _Xconst char *fileName)
2090{
2091    FILE	*file;
2092    XrmQuark empty = NULLQUARK;
2093
2094    if (!db) return;
2095    if (!(file = fopen(fileName, "w"))) return;
2096    if (XrmEnumerateDatabase(db, &empty, &empty, XrmEnumAllLevels,
2097			     DumpEntry, (XPointer) file))
2098	unlink((char *)fileName);
2099    fclose(file);
2100}
2101
2102/* macros used in get/search functions */
2103
2104/* find entries named ename, leafness leaf, tight or loose, and call get */
2105#define GTIGHTLOOSE(ename,looseleaf) \
2106    NFIND(ename); \
2107    if (entry) { \
2108	if (leaf == entry->leaf) { \
2109	    if (!leaf && !entry->tight && entry->next && \
2110		entry->next->name == q && entry->next->tight && \
2111		entry->next->hasloose && \
2112		looseleaf((LTable)entry->next, names+1, classes+1, closure)) \
2113		return True; \
2114	    if ((*get)(entry, names+1, classes+1, closure)) \
2115		return True; \
2116	    if (entry->tight && (entry = entry->next) && \
2117		entry->name == q && leaf == entry->leaf && \
2118		(*get)(entry, names+1, classes+1, closure)) \
2119		return True; \
2120	} else if (entry->leaf) { \
2121	    if (entry->hasloose && \
2122		looseleaf((LTable)entry, names+1, classes+1, closure)) \
2123		return True; \
2124	    if (entry->tight && (entry = entry->next) && \
2125		entry->name == q && entry->hasloose && \
2126		looseleaf((LTable)entry, names+1, classes+1, closure)) \
2127		return True; \
2128	} \
2129    }
2130
2131/* find entries named ename, leafness leaf, loose only, and call get */
2132#define GLOOSE(ename,looseleaf) \
2133    NFIND(ename); \
2134    if (entry && entry->tight && (entry = entry->next) && entry->name != q) \
2135	entry = (NTable)NULL; \
2136    if (entry) { \
2137	if (leaf == entry->leaf) { \
2138	    if ((*get)(entry, names+1, classes+1, closure)) \
2139		return True; \
2140	} else if (entry->leaf && entry->hasloose) { \
2141	    if (looseleaf((LTable)entry, names+1, classes+1, closure)) \
2142		return True; \
2143	} \
2144    }
2145
2146/* add tight/loose entry to the search list, return True if list is full */
2147/*ARGSUSED*/
2148static Bool AppendLEntry(
2149    LTable		table,
2150    XrmNameList		names,
2151    XrmClassList 	classes,
2152    register SClosure	closure)
2153{
2154    /* check for duplicate */
2155    if (closure->idx >= 0 && closure->list[closure->idx] == table)
2156	return False;
2157    if (closure->idx == closure->limit)
2158	return True;
2159    /* append it */
2160    closure->idx++;
2161    closure->list[closure->idx] = table;
2162    return False;
2163}
2164
2165/* add loose entry to the search list, return True if list is full */
2166/*ARGSUSED*/
2167static Bool AppendLooseLEntry(
2168    LTable		table,
2169    XrmNameList		names,
2170    XrmClassList 	classes,
2171    register SClosure	closure)
2172{
2173    /* check for duplicate */
2174    if (closure->idx >= 0 && closure->list[closure->idx] == table)
2175	return False;
2176    if (closure->idx >= closure->limit - 1)
2177	return True;
2178    /* append it */
2179    closure->idx++;
2180    closure->list[closure->idx] = LOOSESEARCH;
2181    closure->idx++;
2182    closure->list[closure->idx] = table;
2183    return False;
2184}
2185
2186/* search for a leaf table */
2187static Bool SearchNEntry(
2188    NTable		table,
2189    XrmNameList		names,
2190    XrmClassList 	classes,
2191    SClosure		closure)
2192{
2193    register NTable	entry;
2194    register XrmQuark	q;
2195    register unsigned int leaf;
2196    Bool		(*get)(
2197            NTable		table,
2198            XrmNameList		names,
2199            XrmClassList 	classes,
2200            SClosure		closure);
2201
2202    if (names[1]) {
2203	get = SearchNEntry; /* recurse */
2204	leaf = 0;
2205    } else {
2206	get = (getNTableSProcp)AppendLEntry; /* bottom of recursion */
2207	leaf = 1;
2208    }
2209    GTIGHTLOOSE(*names, AppendLooseLEntry);   /* do name, tight and loose */
2210    GTIGHTLOOSE(*classes, AppendLooseLEntry); /* do class, tight and loose */
2211    if (table->hasany) {
2212	GTIGHTLOOSE(XrmQANY, AppendLooseLEntry); /* do ANY, tight and loose */
2213    }
2214    if (table->hasloose) {
2215	while (1) {
2216	    names++;
2217	    classes++;
2218	    if (!*names)
2219		break;
2220	    if (!names[1]) {
2221		get = (getNTableSProcp)AppendLEntry; /* bottom of recursion */
2222		leaf = 1;
2223	    }
2224	    GLOOSE(*names, AppendLooseLEntry);   /* loose names */
2225	    GLOOSE(*classes, AppendLooseLEntry); /* loose classes */
2226	    if (table->hasany) {
2227		GLOOSE(XrmQANY, AppendLooseLEntry); /* loose ANY */
2228	    }
2229	}
2230    }
2231    /* now look for matching leaf nodes */
2232    entry = table->next;
2233    if (!entry)
2234	return False;
2235    if (entry->leaf) {
2236	if (entry->tight && !table->tight)
2237	    entry = entry->next;
2238    } else {
2239	entry = entry->next;
2240	if (!entry || !entry->tight)
2241	    return False;
2242    }
2243    if (!entry || entry->name != table->name)
2244	return False;
2245    /* found one */
2246    if (entry->hasloose &&
2247	AppendLooseLEntry((LTable)entry, names, classes, closure))
2248	return True;
2249    if (entry->tight && entry == table->next && (entry = entry->next) &&
2250	entry->name == table->name && entry->hasloose)
2251	return AppendLooseLEntry((LTable)entry, names, classes, closure);
2252    return False;
2253}
2254
2255Bool XrmQGetSearchList(
2256    XrmDatabase     db,
2257    XrmNameList	    names,
2258    XrmClassList    classes,
2259    XrmSearchList   searchList,	/* RETURN */
2260    int		    listLength)
2261{
2262    register NTable	table;
2263    SClosureRec		closure;
2264
2265    if (listLength <= 0)
2266	return False;
2267    closure.list = (LTable *)searchList;
2268    closure.idx = -1;
2269    closure.limit = listLength - 2;
2270    if (db) {
2271	_XLockMutex(&db->linfo);
2272	table = db->table;
2273	if (*names) {
2274	    if (table && !table->leaf) {
2275		if (SearchNEntry(table, names, classes, &closure)) {
2276		    _XUnlockMutex(&db->linfo);
2277		    return False;
2278		}
2279	    } else if (table && table->hasloose &&
2280		       AppendLooseLEntry((LTable)table, names, classes,
2281					 &closure)) {
2282		_XUnlockMutex(&db->linfo);
2283		return False;
2284	    }
2285	} else {
2286	    if (table && !table->leaf)
2287		table = table->next;
2288	    if (table &&
2289		AppendLEntry((LTable)table, names, classes, &closure)) {
2290		_XUnlockMutex(&db->linfo);
2291		return False;
2292	    }
2293	}
2294	_XUnlockMutex(&db->linfo);
2295    }
2296    closure.list[closure.idx + 1] = (LTable)NULL;
2297    return True;
2298}
2299
2300Bool XrmQGetSearchResource(
2301	     XrmSearchList	searchList,
2302    register XrmName		name,
2303    register XrmClass		class,
2304    	     XrmRepresentation	*pType,  /* RETURN */
2305    	     XrmValue		*pValue) /* RETURN */
2306{
2307    register LTable *list;
2308    register LTable table;
2309    register VEntry entry = NULL;
2310    int flags;
2311
2312/* find tight or loose entry */
2313#define VTIGHTLOOSE(q) \
2314    entry = LeafHash(table, q); \
2315    while (entry && entry->name != q) \
2316	entry = entry->next; \
2317    if (entry) \
2318	break
2319
2320/* find loose entry */
2321#define VLOOSE(q) \
2322    entry = LeafHash(table, q); \
2323    while (entry && entry->name != q) \
2324	entry = entry->next; \
2325    if (entry) { \
2326	if (!entry->tight) \
2327	    break; \
2328	if ((entry = entry->next) && entry->name == q) \
2329	    break; \
2330    }
2331
2332    list = (LTable *)searchList;
2333    /* figure out which combination of name and class we need to search for */
2334    flags = 0;
2335    if (IsResourceQuark(name))
2336	flags = 2;
2337    if (IsResourceQuark(class))
2338	flags |= 1;
2339    if (!flags) {
2340	/* neither name nor class has ever been used to name a resource */
2341	table = (LTable)NULL;
2342    } else if (flags == 3) {
2343	/* both name and class */
2344	while ((table = *list++)) {
2345	    if (table != LOOSESEARCH) {
2346		VTIGHTLOOSE(name);  /* do name, tight and loose */
2347		VTIGHTLOOSE(class); /* do class, tight and loose */
2348	    } else {
2349		table = *list++;
2350		VLOOSE(name);  /* do name, loose only */
2351		VLOOSE(class); /* do class, loose only */
2352	    }
2353	}
2354    } else {
2355	/* just one of name or class */
2356	if (flags == 1)
2357	    name = class;
2358	while ((table = *list++)) {
2359	    if (table != LOOSESEARCH) {
2360		VTIGHTLOOSE(name); /* tight and loose */
2361	    } else {
2362		table = *list++;
2363		VLOOSE(name); /* loose only */
2364	    }
2365	}
2366    }
2367    if (table) {
2368	/* found a match */
2369	if (entry->string) {
2370	    *pType = XrmQString;
2371	    pValue->addr = StringValue(entry);
2372	} else {
2373	    *pType = RepType(entry);
2374	    pValue->addr = DataValue(entry);
2375	}
2376	pValue->size = entry->size;
2377	return True;
2378    }
2379    *pType = NULLQUARK;
2380    pValue->addr = (XPointer)NULL;
2381    pValue->size = 0;
2382    return False;
2383
2384#undef VTIGHTLOOSE
2385#undef VLOOSE
2386}
2387
2388/* look for a tight/loose value */
2389static Bool GetVEntry(
2390    LTable		table,
2391    XrmNameList		names,
2392    XrmClassList 	classes,
2393    VClosure		closure)
2394{
2395    register VEntry entry;
2396    register XrmQuark q;
2397
2398    /* try name first */
2399    q = *names;
2400    entry = LeafHash(table, q);
2401    while (entry && entry->name != q)
2402	entry = entry->next;
2403    if (!entry) {
2404	/* not found, try class */
2405	q = *classes;
2406	entry = LeafHash(table, q);
2407	while (entry && entry->name != q)
2408	    entry = entry->next;
2409	if (!entry)
2410	    return False;
2411    }
2412    if (entry->string) {
2413	*closure->type = XrmQString;
2414	closure->value->addr = StringValue(entry);
2415    } else {
2416	*closure->type = RepType(entry);
2417	closure->value->addr = DataValue(entry);
2418    }
2419    closure->value->size = entry->size;
2420    return True;
2421}
2422
2423/* look for a loose value */
2424static Bool GetLooseVEntry(
2425    LTable		table,
2426    XrmNameList		names,
2427    XrmClassList 	classes,
2428    VClosure		closure)
2429{
2430    register VEntry	entry;
2431    register XrmQuark	q;
2432
2433#define VLOOSE(ename) \
2434    q = ename; \
2435    entry = LeafHash(table, q); \
2436    while (entry && entry->name != q) \
2437	entry = entry->next; \
2438    if (entry && entry->tight && (entry = entry->next) && entry->name != q) \
2439	entry = (VEntry)NULL;
2440
2441    /* bump to last component */
2442    while (names[1]) {
2443	names++;
2444	classes++;
2445    }
2446    VLOOSE(*names);  /* do name, loose only */
2447    if (!entry) {
2448	VLOOSE(*classes); /* do class, loose only */
2449	if (!entry)
2450	    return False;
2451    }
2452    if (entry->string) {
2453	*closure->type = XrmQString;
2454	closure->value->addr = StringValue(entry);
2455    } else {
2456	*closure->type = RepType(entry);
2457	closure->value->addr = DataValue(entry);
2458    }
2459    closure->value->size = entry->size;
2460    return True;
2461
2462#undef VLOOSE
2463}
2464
2465/* recursive search for a value */
2466static Bool GetNEntry(
2467    NTable		table,
2468    XrmNameList		names,
2469    XrmClassList 	classes,
2470    VClosure		closure)
2471{
2472    register NTable	entry;
2473    register XrmQuark	q;
2474    register unsigned int leaf;
2475    Bool		(*get)(
2476            NTable              table,
2477            XrmNameList         names,
2478            XrmClassList        classes,
2479            VClosure            closure);
2480    NTable		otable;
2481
2482    if (names[2]) {
2483	get = GetNEntry; /* recurse */
2484	leaf = 0;
2485    } else {
2486	get = (getNTableVProcp)GetVEntry; /* bottom of recursion */
2487	leaf = 1;
2488    }
2489    GTIGHTLOOSE(*names, GetLooseVEntry);   /* do name, tight and loose */
2490    GTIGHTLOOSE(*classes, GetLooseVEntry); /* do class, tight and loose */
2491    if (table->hasany) {
2492	GTIGHTLOOSE(XrmQANY, GetLooseVEntry); /* do ANY, tight and loose */
2493    }
2494    if (table->hasloose) {
2495	while (1) {
2496	    names++;
2497	    classes++;
2498	    if (!names[1])
2499		break;
2500	    if (!names[2]) {
2501		get = (getNTableVProcp)GetVEntry; /* bottom of recursion */
2502		leaf = 1;
2503	    }
2504	    GLOOSE(*names, GetLooseVEntry);   /* do name, loose only */
2505	    GLOOSE(*classes, GetLooseVEntry); /* do class, loose only */
2506	    if (table->hasany) {
2507		GLOOSE(XrmQANY, GetLooseVEntry); /* do ANY, loose only */
2508	    }
2509	}
2510    }
2511    /* look for matching leaf tables */
2512    otable = table;
2513    table = table->next;
2514    if (!table)
2515	return False;
2516    if (table->leaf) {
2517	if (table->tight && !otable->tight)
2518	    table = table->next;
2519    } else {
2520	table = table->next;
2521	if (!table || !table->tight)
2522	    return False;
2523    }
2524    if (!table || table->name != otable->name)
2525	return False;
2526    /* found one */
2527    if (table->hasloose &&
2528	GetLooseVEntry((LTable)table, names, classes, closure))
2529	return True;
2530    if (table->tight && table == otable->next) {
2531	table = table->next;
2532	if (table && table->name == otable->name && table->hasloose)
2533	    return GetLooseVEntry((LTable)table, names, classes, closure);
2534    }
2535    return False;
2536}
2537
2538Bool XrmQGetResource(
2539    XrmDatabase         db,
2540    XrmNameList		names,
2541    XrmClassList 	classes,
2542    XrmRepresentation	*pType,  /* RETURN */
2543    XrmValuePtr		pValue)  /* RETURN */
2544{
2545    register NTable table;
2546    VClosureRec closure;
2547
2548    if (db && *names) {
2549	_XLockMutex(&db->linfo);
2550	closure.type = pType;
2551	closure.value = pValue;
2552	table = db->table;
2553	if (names[1]) {
2554	    if (table && !table->leaf) {
2555		if (GetNEntry(table, names, classes, &closure)) {
2556		    _XUnlockMutex(&db->linfo);
2557		    return True;
2558		}
2559	    } else if (table && table->hasloose &&
2560		    GetLooseVEntry((LTable)table, names, classes, &closure)) {
2561		_XUnlockMutex (&db->linfo);
2562		return True;
2563	    }
2564	} else {
2565	    if (table && !table->leaf)
2566		table = table->next;
2567	    if (table && GetVEntry((LTable)table, names, classes, &closure)) {
2568		_XUnlockMutex(&db->linfo);
2569		return True;
2570	    }
2571	}
2572	_XUnlockMutex(&db->linfo);
2573    }
2574    *pType = NULLQUARK;
2575    pValue->addr = (XPointer)NULL;
2576    pValue->size = 0;
2577    return False;
2578}
2579
2580Bool
2581XrmGetResource(XrmDatabase db, _Xconst char *name_str, _Xconst char *class_str,
2582	       XrmString *pType_str, XrmValuePtr pValue)
2583{
2584    XrmName		names[MAXDBDEPTH+1];
2585    XrmClass		classes[MAXDBDEPTH+1];
2586    XrmRepresentation   fromType;
2587    Bool		result;
2588
2589    XrmStringToNameList(name_str, names);
2590    XrmStringToClassList(class_str, classes);
2591    result = XrmQGetResource(db, names, classes, &fromType, pValue);
2592    (*pType_str) = XrmQuarkToString(fromType);
2593    return result;
2594}
2595
2596/* destroy all values, plus table itself */
2597static void DestroyLTable(
2598    LTable table)
2599{
2600    register int i;
2601    register VEntry *buckets;
2602    register VEntry entry, next;
2603
2604    buckets = table->buckets;
2605    for (i = table->table.mask; i >= 0; i--, buckets++) {
2606	for (next = *buckets; (entry = next); ) {
2607	    next = entry->next;
2608	    Xfree((char *)entry);
2609	}
2610    }
2611    Xfree((char *)table->buckets);
2612    Xfree((char *)table);
2613}
2614
2615/* destroy all contained tables, plus table itself */
2616static void DestroyNTable(
2617    NTable table)
2618{
2619    register int i;
2620    register NTable *buckets;
2621    register NTable entry, next;
2622
2623    buckets = NodeBuckets(table);
2624    for (i = table->mask; i >= 0; i--, buckets++) {
2625	for (next = *buckets; (entry = next); ) {
2626	    next = entry->next;
2627	    if (entry->leaf)
2628		DestroyLTable((LTable)entry);
2629	    else
2630		DestroyNTable(entry);
2631	}
2632    }
2633    Xfree((char *)table);
2634}
2635
2636const char *
2637XrmLocaleOfDatabase(
2638    XrmDatabase db)
2639{
2640    const char* retval;
2641    _XLockMutex(&db->linfo);
2642    retval = (*db->methods->lcname)(db->mbstate);
2643    _XUnlockMutex(&db->linfo);
2644    return retval;
2645}
2646
2647void XrmDestroyDatabase(
2648    XrmDatabase   db)
2649{
2650    register NTable table, next;
2651
2652    if (db) {
2653	_XLockMutex(&db->linfo);
2654	for (next = db->table; (table = next); ) {
2655	    next = table->next;
2656	    if (table->leaf)
2657		DestroyLTable((LTable)table);
2658	    else
2659		DestroyNTable(table);
2660	}
2661	_XUnlockMutex(&db->linfo);
2662	_XFreeMutex(&db->linfo);
2663	(*db->methods->destroy)(db->mbstate);
2664	Xfree((char *)db);
2665    }
2666}
2667