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