Xrm.c revision 07fb9b8f
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 occurring 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#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
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 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
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 guaranteed 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(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(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(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 */
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(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(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(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 = (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
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 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
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 = (unsigned) 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
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
1620static void
1621GetIncludeFile(
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
1653XrmDatabase
1654XrmGetFileDatabase(
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
1671Status
1672XrmCombineFileDatabase(
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*/
1701static 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
1747static 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 */
1781static 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 */
1944Bool 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
1980static 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*/
2000static 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
2048void 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
2071void
2072XrmPutFileDatabase(
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*/
2133static 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*/
2152static 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 */
2172static 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
2240Bool 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
2285Bool 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 */
2374static 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 */
2409static 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 */
2451static 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
2523Bool 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
2565Bool
2566XrmGetResource(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 */
2582static 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 */
2601static 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
2621const char *
2622XrmLocaleOfDatabase(
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
2632void 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