Xrm.c revision b4ee4795
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 	"XrmI.h"
64#include	<X11/Xos.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 = (VEntry *)Xmalloc(i * sizeof(VEntry));
585	if (!ltable->buckets) {
586	    ltable->buckets = otable.buckets;
587	    return;
588	}
589	ltable->table.mask = i - 1;
590	bzero((char *)ltable->buckets, i * sizeof(VEntry));
591	MoveValues(&otable, ltable);
592    } else {
593	register NTable ntable;
594
595	ntable = (NTable)Xmalloc(sizeof(NTableRec) + i * sizeof(NTable));
596	if (!ntable)
597	    return;
598	*ntable = *table;
599	ntable->mask = i - 1;
600	bzero((char *)NodeBuckets(ntable), i * sizeof(NTable));
601	*prev = ntable;
602	MoveTables(table, ntable);
603    }
604}
605
606/* merge values from ftable into *pprev, destroy ftable in the process */
607static void MergeValues(
608    LTable ftable,
609    NTable *pprev,
610    Bool override)
611{
612    register VEntry fentry, tentry;
613    register VEntry *prev;
614    register LTable ttable;
615    VEntry *bucket;
616    int i;
617    register XrmQuark q;
618
619    ttable = (LTable)*pprev;
620    if (ftable->table.hasloose)
621	ttable->table.hasloose = 1;
622    for (i = ftable->table.mask, bucket = ftable->buckets;
623	 i >= 0;
624	 i--, bucket++) {
625	for (fentry = *bucket; fentry; ) {
626	    q = fentry->name;
627	    prev = &LeafHash(ttable, q);
628	    tentry = *prev;
629	    while (tentry && tentry->name != q)
630		tentry = *(prev = &tentry->next);
631	    /* note: test intentionally uses fentry->name instead of q */
632	    /* permits serendipitous inserts */
633	    while (tentry && tentry->name == fentry->name) {
634		/* if tentry is earlier, skip it */
635		if (!fentry->tight && tentry->tight) {
636		    tentry = *(prev = &tentry->next);
637		    continue;
638		}
639		if (fentry->tight != tentry->tight) {
640		    /* no match, chain in fentry */
641		    *prev = fentry;
642		    prev = &fentry->next;
643		    fentry = *prev;
644		    *prev = tentry;
645		    ttable->table.entries++;
646		} else if (override) {
647		    /* match, chain in fentry, splice out and free tentry */
648		    *prev = fentry;
649		    prev = &fentry->next;
650		    fentry = *prev;
651		    *prev = tentry->next;
652		    /* free the overridden entry */
653		    Xfree((char *)tentry);
654		    /* get next tentry */
655		    tentry = *prev;
656		} else {
657		    /* match, discard fentry */
658		    prev = &tentry->next;
659		    tentry = fentry; /* use as a temp var */
660		    fentry = fentry->next;
661		    /* free the overpowered entry */
662		    Xfree((char *)tentry);
663		    /* get next tentry */
664		    tentry = *prev;
665		}
666		if (!fentry)
667		    break;
668	    }
669	    /* at this point, tentry cannot match any fentry named q */
670	    /* chain in all bindings together, preserve invariant order */
671	    while (fentry && fentry->name == q) {
672		*prev = fentry;
673		prev = &fentry->next;
674		fentry = *prev;
675		*prev = tentry;
676		ttable->table.entries++;
677	    }
678	}
679    }
680    Xfree((char *)ftable->buckets);
681    Xfree((char *)ftable);
682    /* resize if necessary, now that we're all done */
683    GROW(pprev);
684}
685
686/* merge tables from ftable into *pprev, destroy ftable in the process */
687static void MergeTables(
688    NTable ftable,
689    NTable *pprev,
690    Bool override)
691{
692    register NTable fentry, tentry;
693    NTable nfentry;
694    register NTable *prev;
695    register NTable ttable;
696    NTable *bucket;
697    int i;
698    register XrmQuark q;
699
700    ttable = *pprev;
701    if (ftable->hasloose)
702	ttable->hasloose = 1;
703    if (ftable->hasany)
704	ttable->hasany = 1;
705    for (i = ftable->mask, bucket = NodeBuckets(ftable);
706	 i >= 0;
707	 i--, bucket++) {
708	for (fentry = *bucket; fentry; ) {
709	    q = fentry->name;
710	    prev = &NodeHash(ttable, q);
711	    tentry = *prev;
712	    while (tentry && tentry->name != q)
713		tentry = *(prev = &tentry->next);
714	    /* note: test intentionally uses fentry->name instead of q */
715	    /* permits serendipitous inserts */
716	    while (tentry && tentry->name == fentry->name) {
717		/* if tentry is earlier, skip it */
718		if ((fentry->leaf && !tentry->leaf) ||
719		    (!fentry->tight && tentry->tight &&
720		     (fentry->leaf || !tentry->leaf))) {
721		    tentry = *(prev = &tentry->next);
722		    continue;
723		}
724		nfentry = fentry->next;
725		if (fentry->leaf != tentry->leaf ||
726		    fentry->tight != tentry->tight) {
727		    /* no match, just chain in */
728		    *prev = fentry;
729		    *(prev = &fentry->next) = tentry;
730		    ttable->entries++;
731		} else {
732		    if (fentry->leaf)
733			MergeValues((LTable)fentry, prev, override);
734		    else
735			MergeTables(fentry, prev, override);
736		    /* bump to next tentry */
737		    tentry = *(prev = &(*prev)->next);
738		}
739		/* bump to next fentry */
740		fentry = nfentry;
741		if (!fentry)
742		    break;
743	    }
744	    /* at this point, tentry cannot match any fentry named q */
745	    /* chain in all bindings together, preserve invariant order */
746	    while (fentry && fentry->name == q) {
747		*prev = fentry;
748		prev = &fentry->next;
749		fentry = *prev;
750		*prev = tentry;
751		ttable->entries++;
752	    }
753	}
754    }
755    Xfree((char *)ftable);
756    /* resize if necessary, now that we're all done */
757    GROW(pprev);
758}
759
760void XrmCombineDatabase(
761    XrmDatabase	from, XrmDatabase *into,
762    Bool override)
763{
764    register NTable *prev;
765    register NTable ftable, ttable, nftable;
766
767    if (!*into) {
768	*into = from;
769    } else if (from) {
770	_XLockMutex(&from->linfo);
771	_XLockMutex(&(*into)->linfo);
772	if ((ftable = from->table)) {
773	    prev = &(*into)->table;
774	    ttable = *prev;
775	    if (!ftable->leaf) {
776		nftable = ftable->next;
777		if (ttable && !ttable->leaf) {
778		    /* both have node tables, merge them */
779		    MergeTables(ftable, prev, override);
780		    /* bump to into's leaf table, if any */
781		    ttable = *(prev = &(*prev)->next);
782		} else {
783		    /* into has no node table, link from's in */
784		    *prev = ftable;
785		    *(prev = &ftable->next) = ttable;
786		}
787		/* bump to from's leaf table, if any */
788		ftable = nftable;
789	    } else {
790		/* bump to into's leaf table, if any */
791		if (ttable && !ttable->leaf)
792		    ttable = *(prev = &ttable->next);
793	    }
794	    if (ftable) {
795		/* if into has a leaf, merge, else insert */
796		if (ttable)
797		    MergeValues((LTable)ftable, prev, override);
798		else
799		    *prev = ftable;
800	    }
801	}
802	(from->methods->destroy)(from->mbstate);
803	_XUnlockMutex(&from->linfo);
804	_XFreeMutex(&from->linfo);
805	Xfree((char *)from);
806	_XUnlockMutex(&(*into)->linfo);
807    }
808}
809
810void XrmMergeDatabases(
811    XrmDatabase	from, XrmDatabase *into)
812{
813    XrmCombineDatabase(from, into, True);
814}
815
816/* store a value in the database, overriding any existing entry */
817static void PutEntry(
818    XrmDatabase		db,
819    XrmBindingList	bindings,
820    XrmQuarkList	quarks,
821    XrmRepresentation	type,
822    XrmValuePtr		value)
823{
824    register NTable *pprev, *prev;
825    register NTable table;
826    register XrmQuark q;
827    register VEntry *vprev;
828    register VEntry entry;
829    NTable *nprev, *firstpprev;
830
831#define NEWTABLE(q,i) \
832    table = (NTable)Xmalloc(sizeof(LTableRec)); \
833    if (!table) \
834	return; \
835    table->name = q; \
836    table->hasloose = 0; \
837    table->hasany = 0; \
838    table->mask = 0; \
839    table->entries = 0; \
840    if (quarks[i]) { \
841	table->leaf = 0; \
842	nprev = NodeBuckets(table); \
843    } else { \
844	table->leaf = 1; \
845	if (!(nprev = (NTable *)Xmalloc(sizeof(VEntry *)))) \
846	    return; \
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    GetSizeOfFile(fd, size);
1596
1597    if (!(filebuf = Xmalloc(size + 1))) { /* leave room for '\0' */
1598	close(fd);
1599	return (char *)NULL;
1600    }
1601    size = read (fd, filebuf, size);
1602
1603#ifdef __UNIXOS2__
1604    { /* kill CRLF */
1605      int i,k;
1606      for (i=k=0; i<size; i++)
1607	if (filebuf[i] != 0x0d) {
1608	   filebuf[k++] = filebuf[i];
1609	}
1610	filebuf[k] = 0;
1611    }
1612#endif
1613
1614    if (size < 0) {
1615	close (fd);
1616	Xfree(filebuf);
1617	return (char *)NULL;
1618    }
1619    close (fd);
1620
1621    filebuf[size] = '\0';	/* NULL terminate it. */
1622    return filebuf;
1623}
1624
1625static void
1626GetIncludeFile(
1627    XrmDatabase db,
1628    _Xconst char *base,
1629    _Xconst char *fname,
1630    int fnamelen)
1631{
1632    int len;
1633    char *str;
1634    char realfname[BUFSIZ];
1635
1636    if (fnamelen <= 0 || fnamelen >= BUFSIZ)
1637	return;
1638    if (*fname != '/' && base && (str = strrchr(base, '/'))) {
1639	len = str - base + 1;
1640	if (len + fnamelen >= BUFSIZ)
1641	    return;
1642	strncpy(realfname, base, len);
1643	strncpy(realfname + len, fname, fnamelen);
1644	realfname[len + fnamelen] = '\0';
1645    } else {
1646	strncpy(realfname, fname, fnamelen);
1647	realfname[fnamelen] = '\0';
1648    }
1649    if (!(str = ReadInFile(realfname)))
1650	return;
1651    GetDatabase(db, str, realfname, True);
1652    Xfree(str);
1653}
1654
1655XrmDatabase
1656XrmGetFileDatabase(
1657    _Xconst char    *filename)
1658{
1659    XrmDatabase db;
1660    char *str;
1661
1662    if (!(str = ReadInFile(filename)))
1663	return (XrmDatabase)NULL;
1664
1665    db = NewDatabase();
1666    _XLockMutex(&db->linfo);
1667    GetDatabase(db, str, filename, True);
1668    _XUnlockMutex(&db->linfo);
1669    Xfree(str);
1670    return db;
1671}
1672
1673Status
1674XrmCombineFileDatabase(
1675    _Xconst char    *filename,
1676    XrmDatabase     *target,
1677    Bool             override)
1678{
1679    XrmDatabase db;
1680    char *str;
1681
1682    if (!(str = ReadInFile(filename)))
1683	return 0;
1684    if (override) {
1685	db = *target;
1686	if (!db)
1687	    *target = db = NewDatabase();
1688    } else
1689	db = NewDatabase();
1690    _XLockMutex(&db->linfo);
1691    GetDatabase(db, str, filename, True);
1692    _XUnlockMutex(&db->linfo);
1693    Xfree(str);
1694    if (!override)
1695	XrmCombineDatabase(db, target, False);
1696    return 1;
1697}
1698
1699/* call the user proc for every value in the table, arbitrary order.
1700 * stop if user proc returns True.  level is current depth in database.
1701 */
1702/*ARGSUSED*/
1703static Bool EnumLTable(
1704    LTable		table,
1705    XrmNameList		names,
1706    XrmClassList 	classes,
1707    register int	level,
1708    register EClosure	closure)
1709{
1710    register VEntry *bucket;
1711    register int i;
1712    register VEntry entry;
1713    XrmValue value;
1714    XrmRepresentation type;
1715    Bool tightOk;
1716
1717    closure->bindings[level] = (table->table.tight ?
1718				XrmBindTightly : XrmBindLoosely);
1719    closure->quarks[level] = table->table.name;
1720    level++;
1721    tightOk = !*names;
1722    closure->quarks[level + 1] = NULLQUARK;
1723    for (i = table->table.mask, bucket = table->buckets;
1724	 i >= 0;
1725	 i--, bucket++) {
1726	for (entry = *bucket; entry; entry = entry->next) {
1727	    if (entry->tight && !tightOk)
1728		continue;
1729	    closure->bindings[level] = (entry->tight ?
1730					XrmBindTightly : XrmBindLoosely);
1731	    closure->quarks[level] = entry->name;
1732	    value.size = entry->size;
1733	    if (entry->string) {
1734		type = XrmQString;
1735		value.addr = StringValue(entry);
1736	    } else {
1737		type = RepType(entry);
1738		value.addr = DataValue(entry);
1739	    }
1740	    if ((*closure->proc)(&closure->db, closure->bindings+1,
1741				 closure->quarks+1, &type, &value,
1742				 closure->closure))
1743		return True;
1744	}
1745    }
1746    return False;
1747}
1748
1749static Bool EnumAllNTable(
1750    NTable		table,
1751    register int	level,
1752    register EClosure	closure)
1753{
1754    register NTable *bucket;
1755    register int i;
1756    register NTable entry;
1757    XrmQuark empty = NULLQUARK;
1758
1759    if (level >= MAXDBDEPTH)
1760	return False;
1761    for (i = table->mask, bucket = NodeBuckets(table);
1762	 i >= 0;
1763	 i--, bucket++) {
1764	for (entry = *bucket; entry; entry = entry->next) {
1765	    if (entry->leaf) {
1766		if (EnumLTable((LTable)entry, &empty, &empty, level, closure))
1767		    return True;
1768	    } else {
1769		closure->bindings[level] = (entry->tight ?
1770					    XrmBindTightly : XrmBindLoosely);
1771		closure->quarks[level] = entry->name;
1772		if (EnumAllNTable(entry, level+1, closure))
1773		    return True;
1774	    }
1775	}
1776    }
1777    return False;
1778}
1779
1780/* recurse on every table in the table, arbitrary order.
1781 * stop if user proc returns True.  level is current depth in database.
1782 */
1783static Bool EnumNTable(
1784    NTable		table,
1785    XrmNameList		names,
1786    XrmClassList 	classes,
1787    register int	level,
1788    register EClosure	closure)
1789{
1790    register NTable	entry;
1791    register XrmQuark	q;
1792    register unsigned int leaf;
1793    Bool (*get)(
1794            NTable		table,
1795            XrmNameList		names,
1796            XrmClassList 	classes,
1797            register int	level,
1798            EClosure		closure);
1799    Bool bilevel;
1800
1801/* find entries named ename, leafness leaf, tight or loose, and call get */
1802#define ITIGHTLOOSE(ename) \
1803    NFIND(ename); \
1804    if (entry) { \
1805	if (leaf == entry->leaf) { \
1806	    if (!leaf && !entry->tight && entry->next && \
1807		entry->next->name == q && entry->next->tight && \
1808		(bilevel || entry->next->hasloose) && \
1809		EnumLTable((LTable)entry->next, names+1, classes+1, \
1810			   level, closure)) \
1811		return True; \
1812	    if ((*get)(entry, names+1, classes+1, level, closure)) \
1813		return True; \
1814	    if (entry->tight && (entry = entry->next) && \
1815		entry->name == q && leaf == entry->leaf && \
1816		(*get)(entry, names+1, classes+1, level, closure)) \
1817		return True; \
1818	} else if (entry->leaf) { \
1819	    if ((bilevel || entry->hasloose) && \
1820		EnumLTable((LTable)entry, names+1, classes+1, level, closure))\
1821		return True; \
1822	    if (entry->tight && (entry = entry->next) && \
1823		entry->name == q && (bilevel || entry->hasloose) && \
1824		EnumLTable((LTable)entry, names+1, classes+1, level, closure))\
1825		return True; \
1826	} \
1827    }
1828
1829/* find entries named ename, leafness leaf, loose only, and call get */
1830#define ILOOSE(ename) \
1831    NFIND(ename); \
1832    if (entry && entry->tight && (entry = entry->next) && entry->name != q) \
1833	entry = (NTable)NULL; \
1834    if (entry) { \
1835	if (leaf == entry->leaf) { \
1836	    if ((*get)(entry, names+1, classes+1, level, closure)) \
1837		return True; \
1838	} else if (entry->leaf && (bilevel || entry->hasloose)) { \
1839	    if (EnumLTable((LTable)entry, names+1, classes+1, level, closure))\
1840		return True; \
1841	} \
1842    }
1843
1844    if (level >= MAXDBDEPTH)
1845	return False;
1846    closure->bindings[level] = (table->tight ?
1847				XrmBindTightly : XrmBindLoosely);
1848    closure->quarks[level] = table->name;
1849    level++;
1850    if (!*names) {
1851	if (EnumAllNTable(table, level, closure))
1852	    return True;
1853    } else {
1854	if (names[1] || closure->mode == XrmEnumAllLevels) {
1855	    get = EnumNTable; /* recurse */
1856	    leaf = 0;
1857	    bilevel = !names[1];
1858	} else {
1859	    get = (getNTableEProcp)EnumLTable; /* bottom of recursion */
1860	    leaf = 1;
1861	    bilevel = False;
1862	}
1863	if (table->hasloose && closure->mode == XrmEnumAllLevels) {
1864	    NTable *bucket;
1865	    int i;
1866	    XrmQuark empty = NULLQUARK;
1867
1868	    for (i = table->mask, bucket = NodeBuckets(table);
1869		 i >= 0;
1870		 i--, bucket++) {
1871		q = NULLQUARK;
1872		for (entry = *bucket; entry; entry = entry->next) {
1873		    if (!entry->tight && entry->name != q &&
1874			entry->name != *names && entry->name != *classes) {
1875			q = entry->name;
1876			if (entry->leaf) {
1877			    if (EnumLTable((LTable)entry, &empty, &empty,
1878					   level, closure))
1879				return True;
1880			} else {
1881			    if (EnumNTable(entry, &empty, &empty,
1882					   level, closure))
1883				return True;
1884			}
1885		    }
1886		}
1887	    }
1888	}
1889
1890	ITIGHTLOOSE(*names);   /* do name, tight and loose */
1891	ITIGHTLOOSE(*classes); /* do class, tight and loose */
1892	if (table->hasany) {
1893	    ITIGHTLOOSE(XrmQANY); /* do ANY, tight and loose */
1894	}
1895	if (table->hasloose) {
1896	    while (1) {
1897		names++;
1898		classes++;
1899		if (!*names)
1900		    break;
1901		if (!names[1] && closure->mode != XrmEnumAllLevels) {
1902		    get = (getNTableEProcp)EnumLTable; /* bottom of recursion */
1903		    leaf = 1;
1904		}
1905		ILOOSE(*names);   /* loose names */
1906		ILOOSE(*classes); /* loose classes */
1907		if (table->hasany) {
1908		    ILOOSE(XrmQANY); /* loose ANY */
1909		}
1910	    }
1911	    names--;
1912	    classes--;
1913	}
1914    }
1915    /* now look for matching leaf nodes */
1916    entry = table->next;
1917    if (!entry)
1918	return False;
1919    if (entry->leaf) {
1920	if (entry->tight && !table->tight)
1921	    entry = entry->next;
1922    } else {
1923	entry = entry->next;
1924	if (!entry || !entry->tight)
1925	    return False;
1926    }
1927    if (!entry || entry->name != table->name)
1928	return False;
1929    /* found one */
1930    level--;
1931    if ((!*names || entry->hasloose) &&
1932	EnumLTable((LTable)entry, names, classes, level, closure))
1933	return True;
1934    if (entry->tight && entry == table->next && (entry = entry->next) &&
1935	entry->name == table->name && (!*names || entry->hasloose))
1936	return EnumLTable((LTable)entry, names, classes, level, closure);
1937    return False;
1938
1939#undef ITIGHTLOOSE
1940#undef ILOOSE
1941}
1942
1943/* call the proc for every value in the database, arbitrary order.
1944 * stop if the proc returns True.
1945 */
1946Bool XrmEnumerateDatabase(
1947    XrmDatabase		db,
1948    XrmNameList		names,
1949    XrmClassList	classes,
1950    int			mode,
1951    DBEnumProc		proc,
1952    XPointer		closure)
1953{
1954    XrmBinding  bindings[MAXDBDEPTH+2];
1955    XrmQuark	quarks[MAXDBDEPTH+2];
1956    register NTable table;
1957    EClosureRec	eclosure;
1958    Bool retval = False;
1959
1960    if (!db)
1961	return False;
1962    _XLockMutex(&db->linfo);
1963    eclosure.db = db;
1964    eclosure.proc = proc;
1965    eclosure.closure = closure;
1966    eclosure.bindings = bindings;
1967    eclosure.quarks = quarks;
1968    eclosure.mode = mode;
1969    table = db->table;
1970    if (table && !table->leaf && !*names && mode == XrmEnumOneLevel)
1971	table = table->next;
1972    if (table) {
1973	if (!table->leaf)
1974	    retval = EnumNTable(table, names, classes, 0, &eclosure);
1975	else
1976	    retval = EnumLTable((LTable)table, names, classes, 0, &eclosure);
1977    }
1978    _XUnlockMutex(&db->linfo);
1979    return retval;
1980}
1981
1982static void PrintBindingQuarkList(
1983    XrmBindingList      bindings,
1984    XrmQuarkList	quarks,
1985    FILE		*stream)
1986{
1987    Bool	firstNameSeen;
1988
1989    for (firstNameSeen = False; *quarks; bindings++, quarks++) {
1990	if (*bindings == XrmBindLoosely) {
1991	    (void) fprintf(stream, "*");
1992	} else if (firstNameSeen) {
1993	    (void) fprintf(stream, ".");
1994	}
1995	firstNameSeen = True;
1996	(void) fputs(XrmQuarkToString(*quarks), stream);
1997    }
1998}
1999
2000/* output out the entry in correct file syntax */
2001/*ARGSUSED*/
2002static Bool DumpEntry(
2003    XrmDatabase		*db,
2004    XrmBindingList      bindings,
2005    XrmQuarkList	quarks,
2006    XrmRepresentation   *type,
2007    XrmValuePtr		value,
2008    XPointer		data)
2009{
2010    FILE			*stream = (FILE *)data;
2011    register unsigned int	i;
2012    register char		*s;
2013    register char		c;
2014
2015    if (*type != XrmQString)
2016	(void) putc('!', stream);
2017    PrintBindingQuarkList(bindings, quarks, stream);
2018    s = value->addr;
2019    i = value->size;
2020    if (*type == XrmQString) {
2021	(void) fputs(":\t", stream);
2022	if (i)
2023	    i--;
2024    }
2025    else
2026	(void) fprintf(stream, "=%s:\t", XrmRepresentationToString(*type));
2027    if (i && (*s == ' ' || *s == '\t'))
2028	(void) putc('\\', stream); /* preserve leading whitespace */
2029    while (i--) {
2030	c = *s++;
2031	if (c == '\n') {
2032	    if (i)
2033		(void) fputs("\\n\\\n", stream);
2034	    else
2035		(void) fputs("\\n", stream);
2036	} else if (c == '\\')
2037	    (void) fputs("\\\\", stream);
2038	else if ((c < ' ' && c != '\t') ||
2039		 ((unsigned char)c >= 0x7f && (unsigned char)c < 0xa0))
2040	    (void) fprintf(stream, "\\%03o", (unsigned char)c);
2041	else
2042	    (void) putc(c, stream);
2043    }
2044    (void) putc('\n', stream);
2045    return ferror(stream) != 0;
2046}
2047
2048#ifdef DEBUG
2049
2050void PrintTable(
2051    NTable table,
2052    FILE *file)
2053{
2054    XrmBinding  bindings[MAXDBDEPTH+1];
2055    XrmQuark	quarks[MAXDBDEPTH+1];
2056    EClosureRec closure;
2057    XrmQuark	empty = NULLQUARK;
2058
2059    closure.db = (XrmDatabase)NULL;
2060    closure.proc = DumpEntry;
2061    closure.closure = (XPointer)file;
2062    closure.bindings = bindings;
2063    closure.quarks = quarks;
2064    closure.mode = XrmEnumAllLevels;
2065    if (table->leaf)
2066	EnumLTable((LTable)table, &empty, &empty, 0, &closure);
2067    else
2068	EnumNTable(table, &empty, &empty, 0, &closure);
2069}
2070
2071#endif /* DEBUG */
2072
2073void
2074XrmPutFileDatabase(
2075    XrmDatabase db,
2076    _Xconst char *fileName)
2077{
2078    FILE	*file;
2079    XrmQuark empty = NULLQUARK;
2080
2081    if (!db) return;
2082    if (!(file = fopen(fileName, "w"))) return;
2083    if (XrmEnumerateDatabase(db, &empty, &empty, XrmEnumAllLevels,
2084			     DumpEntry, (XPointer) file))
2085	unlink((char *)fileName);
2086    fclose(file);
2087}
2088
2089/* macros used in get/search functions */
2090
2091/* find entries named ename, leafness leaf, tight or loose, and call get */
2092#define GTIGHTLOOSE(ename,looseleaf) \
2093    NFIND(ename); \
2094    if (entry) { \
2095	if (leaf == entry->leaf) { \
2096	    if (!leaf && !entry->tight && entry->next && \
2097		entry->next->name == q && entry->next->tight && \
2098		entry->next->hasloose && \
2099		looseleaf((LTable)entry->next, names+1, classes+1, closure)) \
2100		return True; \
2101	    if ((*get)(entry, names+1, classes+1, closure)) \
2102		return True; \
2103	    if (entry->tight && (entry = entry->next) && \
2104		entry->name == q && leaf == entry->leaf && \
2105		(*get)(entry, names+1, classes+1, closure)) \
2106		return True; \
2107	} else if (entry->leaf) { \
2108	    if (entry->hasloose && \
2109		looseleaf((LTable)entry, names+1, classes+1, closure)) \
2110		return True; \
2111	    if (entry->tight && (entry = entry->next) && \
2112		entry->name == q && entry->hasloose && \
2113		looseleaf((LTable)entry, names+1, classes+1, closure)) \
2114		return True; \
2115	} \
2116    }
2117
2118/* find entries named ename, leafness leaf, loose only, and call get */
2119#define GLOOSE(ename,looseleaf) \
2120    NFIND(ename); \
2121    if (entry && entry->tight && (entry = entry->next) && entry->name != q) \
2122	entry = (NTable)NULL; \
2123    if (entry) { \
2124	if (leaf == entry->leaf) { \
2125	    if ((*get)(entry, names+1, classes+1, closure)) \
2126		return True; \
2127	} else if (entry->leaf && entry->hasloose) { \
2128	    if (looseleaf((LTable)entry, names+1, classes+1, closure)) \
2129		return True; \
2130	} \
2131    }
2132
2133/* add tight/loose entry to the search list, return True if list is full */
2134/*ARGSUSED*/
2135static Bool AppendLEntry(
2136    LTable		table,
2137    XrmNameList		names,
2138    XrmClassList 	classes,
2139    register SClosure	closure)
2140{
2141    /* check for duplicate */
2142    if (closure->idx >= 0 && closure->list[closure->idx] == table)
2143	return False;
2144    if (closure->idx == closure->limit)
2145	return True;
2146    /* append it */
2147    closure->idx++;
2148    closure->list[closure->idx] = table;
2149    return False;
2150}
2151
2152/* add loose entry to the search list, return True if list is full */
2153/*ARGSUSED*/
2154static Bool AppendLooseLEntry(
2155    LTable		table,
2156    XrmNameList		names,
2157    XrmClassList 	classes,
2158    register SClosure	closure)
2159{
2160    /* check for duplicate */
2161    if (closure->idx >= 0 && closure->list[closure->idx] == table)
2162	return False;
2163    if (closure->idx >= closure->limit - 1)
2164	return True;
2165    /* append it */
2166    closure->idx++;
2167    closure->list[closure->idx] = LOOSESEARCH;
2168    closure->idx++;
2169    closure->list[closure->idx] = table;
2170    return False;
2171}
2172
2173/* search for a leaf table */
2174static Bool SearchNEntry(
2175    NTable		table,
2176    XrmNameList		names,
2177    XrmClassList 	classes,
2178    SClosure		closure)
2179{
2180    register NTable	entry;
2181    register XrmQuark	q;
2182    register unsigned int leaf;
2183    Bool		(*get)(
2184            NTable		table,
2185            XrmNameList		names,
2186            XrmClassList 	classes,
2187            SClosure		closure);
2188
2189    if (names[1]) {
2190	get = SearchNEntry; /* recurse */
2191	leaf = 0;
2192    } else {
2193	get = (getNTableSProcp)AppendLEntry; /* bottom of recursion */
2194	leaf = 1;
2195    }
2196    GTIGHTLOOSE(*names, AppendLooseLEntry);   /* do name, tight and loose */
2197    GTIGHTLOOSE(*classes, AppendLooseLEntry); /* do class, tight and loose */
2198    if (table->hasany) {
2199	GTIGHTLOOSE(XrmQANY, AppendLooseLEntry); /* do ANY, tight and loose */
2200    }
2201    if (table->hasloose) {
2202	while (1) {
2203	    names++;
2204	    classes++;
2205	    if (!*names)
2206		break;
2207	    if (!names[1]) {
2208		get = (getNTableSProcp)AppendLEntry; /* bottom of recursion */
2209		leaf = 1;
2210	    }
2211	    GLOOSE(*names, AppendLooseLEntry);   /* loose names */
2212	    GLOOSE(*classes, AppendLooseLEntry); /* loose classes */
2213	    if (table->hasany) {
2214		GLOOSE(XrmQANY, AppendLooseLEntry); /* loose ANY */
2215	    }
2216	}
2217    }
2218    /* now look for matching leaf nodes */
2219    entry = table->next;
2220    if (!entry)
2221	return False;
2222    if (entry->leaf) {
2223	if (entry->tight && !table->tight)
2224	    entry = entry->next;
2225    } else {
2226	entry = entry->next;
2227	if (!entry || !entry->tight)
2228	    return False;
2229    }
2230    if (!entry || entry->name != table->name)
2231	return False;
2232    /* found one */
2233    if (entry->hasloose &&
2234	AppendLooseLEntry((LTable)entry, names, classes, closure))
2235	return True;
2236    if (entry->tight && entry == table->next && (entry = entry->next) &&
2237	entry->name == table->name && entry->hasloose)
2238	return AppendLooseLEntry((LTable)entry, names, classes, closure);
2239    return False;
2240}
2241
2242Bool XrmQGetSearchList(
2243    XrmDatabase     db,
2244    XrmNameList	    names,
2245    XrmClassList    classes,
2246    XrmSearchList   searchList,	/* RETURN */
2247    int		    listLength)
2248{
2249    register NTable	table;
2250    SClosureRec		closure;
2251
2252    if (listLength <= 0)
2253	return False;
2254    closure.list = (LTable *)searchList;
2255    closure.idx = -1;
2256    closure.limit = listLength - 2;
2257    if (db) {
2258	_XLockMutex(&db->linfo);
2259	table = db->table;
2260	if (*names) {
2261	    if (table && !table->leaf) {
2262		if (SearchNEntry(table, names, classes, &closure)) {
2263		    _XUnlockMutex(&db->linfo);
2264		    return False;
2265		}
2266	    } else if (table && table->hasloose &&
2267		       AppendLooseLEntry((LTable)table, names, classes,
2268					 &closure)) {
2269		_XUnlockMutex(&db->linfo);
2270		return False;
2271	    }
2272	} else {
2273	    if (table && !table->leaf)
2274		table = table->next;
2275	    if (table &&
2276		AppendLEntry((LTable)table, names, classes, &closure)) {
2277		_XUnlockMutex(&db->linfo);
2278		return False;
2279	    }
2280	}
2281	_XUnlockMutex(&db->linfo);
2282    }
2283    closure.list[closure.idx + 1] = (LTable)NULL;
2284    return True;
2285}
2286
2287Bool XrmQGetSearchResource(
2288	     XrmSearchList	searchList,
2289    register XrmName		name,
2290    register XrmClass		class,
2291    	     XrmRepresentation	*pType,  /* RETURN */
2292    	     XrmValue		*pValue) /* RETURN */
2293{
2294    register LTable *list;
2295    register LTable table;
2296    register VEntry entry = NULL;
2297    int flags;
2298
2299/* find tight or loose entry */
2300#define VTIGHTLOOSE(q) \
2301    entry = LeafHash(table, q); \
2302    while (entry && entry->name != q) \
2303	entry = entry->next; \
2304    if (entry) \
2305	break
2306
2307/* find loose entry */
2308#define VLOOSE(q) \
2309    entry = LeafHash(table, q); \
2310    while (entry && entry->name != q) \
2311	entry = entry->next; \
2312    if (entry) { \
2313	if (!entry->tight) \
2314	    break; \
2315	if ((entry = entry->next) && entry->name == q) \
2316	    break; \
2317    }
2318
2319    list = (LTable *)searchList;
2320    /* figure out which combination of name and class we need to search for */
2321    flags = 0;
2322    if (IsResourceQuark(name))
2323	flags = 2;
2324    if (IsResourceQuark(class))
2325	flags |= 1;
2326    if (!flags) {
2327	/* neither name nor class has ever been used to name a resource */
2328	table = (LTable)NULL;
2329    } else if (flags == 3) {
2330	/* both name and class */
2331	while ((table = *list++)) {
2332	    if (table != LOOSESEARCH) {
2333		VTIGHTLOOSE(name);  /* do name, tight and loose */
2334		VTIGHTLOOSE(class); /* do class, tight and loose */
2335	    } else {
2336		table = *list++;
2337		VLOOSE(name);  /* do name, loose only */
2338		VLOOSE(class); /* do class, loose only */
2339	    }
2340	}
2341    } else {
2342	/* just one of name or class */
2343	if (flags == 1)
2344	    name = class;
2345	while ((table = *list++)) {
2346	    if (table != LOOSESEARCH) {
2347		VTIGHTLOOSE(name); /* tight and loose */
2348	    } else {
2349		table = *list++;
2350		VLOOSE(name); /* loose only */
2351	    }
2352	}
2353    }
2354    if (table) {
2355	/* found a match */
2356	if (entry->string) {
2357	    *pType = XrmQString;
2358	    pValue->addr = StringValue(entry);
2359	} else {
2360	    *pType = RepType(entry);
2361	    pValue->addr = DataValue(entry);
2362	}
2363	pValue->size = entry->size;
2364	return True;
2365    }
2366    *pType = NULLQUARK;
2367    pValue->addr = (XPointer)NULL;
2368    pValue->size = 0;
2369    return False;
2370
2371#undef VTIGHTLOOSE
2372#undef VLOOSE
2373}
2374
2375/* look for a tight/loose value */
2376static Bool GetVEntry(
2377    LTable		table,
2378    XrmNameList		names,
2379    XrmClassList 	classes,
2380    VClosure		closure)
2381{
2382    register VEntry entry;
2383    register XrmQuark q;
2384
2385    /* try name first */
2386    q = *names;
2387    entry = LeafHash(table, q);
2388    while (entry && entry->name != q)
2389	entry = entry->next;
2390    if (!entry) {
2391	/* not found, try class */
2392	q = *classes;
2393	entry = LeafHash(table, q);
2394	while (entry && entry->name != q)
2395	    entry = entry->next;
2396	if (!entry)
2397	    return False;
2398    }
2399    if (entry->string) {
2400	*closure->type = XrmQString;
2401	closure->value->addr = StringValue(entry);
2402    } else {
2403	*closure->type = RepType(entry);
2404	closure->value->addr = DataValue(entry);
2405    }
2406    closure->value->size = entry->size;
2407    return True;
2408}
2409
2410/* look for a loose value */
2411static Bool GetLooseVEntry(
2412    LTable		table,
2413    XrmNameList		names,
2414    XrmClassList 	classes,
2415    VClosure		closure)
2416{
2417    register VEntry	entry;
2418    register XrmQuark	q;
2419
2420#define VLOOSE(ename) \
2421    q = ename; \
2422    entry = LeafHash(table, q); \
2423    while (entry && entry->name != q) \
2424	entry = entry->next; \
2425    if (entry && entry->tight && (entry = entry->next) && entry->name != q) \
2426	entry = (VEntry)NULL;
2427
2428    /* bump to last component */
2429    while (names[1]) {
2430	names++;
2431	classes++;
2432    }
2433    VLOOSE(*names);  /* do name, loose only */
2434    if (!entry) {
2435	VLOOSE(*classes); /* do class, loose only */
2436	if (!entry)
2437	    return False;
2438    }
2439    if (entry->string) {
2440	*closure->type = XrmQString;
2441	closure->value->addr = StringValue(entry);
2442    } else {
2443	*closure->type = RepType(entry);
2444	closure->value->addr = DataValue(entry);
2445    }
2446    closure->value->size = entry->size;
2447    return True;
2448
2449#undef VLOOSE
2450}
2451
2452/* recursive search for a value */
2453static Bool GetNEntry(
2454    NTable		table,
2455    XrmNameList		names,
2456    XrmClassList 	classes,
2457    VClosure		closure)
2458{
2459    register NTable	entry;
2460    register XrmQuark	q;
2461    register unsigned int leaf;
2462    Bool		(*get)(
2463            NTable              table,
2464            XrmNameList         names,
2465            XrmClassList        classes,
2466            VClosure            closure);
2467    NTable		otable;
2468
2469    if (names[2]) {
2470	get = GetNEntry; /* recurse */
2471	leaf = 0;
2472    } else {
2473	get = (getNTableVProcp)GetVEntry; /* bottom of recursion */
2474	leaf = 1;
2475    }
2476    GTIGHTLOOSE(*names, GetLooseVEntry);   /* do name, tight and loose */
2477    GTIGHTLOOSE(*classes, GetLooseVEntry); /* do class, tight and loose */
2478    if (table->hasany) {
2479	GTIGHTLOOSE(XrmQANY, GetLooseVEntry); /* do ANY, tight and loose */
2480    }
2481    if (table->hasloose) {
2482	while (1) {
2483	    names++;
2484	    classes++;
2485	    if (!names[1])
2486		break;
2487	    if (!names[2]) {
2488		get = (getNTableVProcp)GetVEntry; /* bottom of recursion */
2489		leaf = 1;
2490	    }
2491	    GLOOSE(*names, GetLooseVEntry);   /* do name, loose only */
2492	    GLOOSE(*classes, GetLooseVEntry); /* do class, loose only */
2493	    if (table->hasany) {
2494		GLOOSE(XrmQANY, GetLooseVEntry); /* do ANY, loose only */
2495	    }
2496	}
2497    }
2498    /* look for matching leaf tables */
2499    otable = table;
2500    table = table->next;
2501    if (!table)
2502	return False;
2503    if (table->leaf) {
2504	if (table->tight && !otable->tight)
2505	    table = table->next;
2506    } else {
2507	table = table->next;
2508	if (!table || !table->tight)
2509	    return False;
2510    }
2511    if (!table || table->name != otable->name)
2512	return False;
2513    /* found one */
2514    if (table->hasloose &&
2515	GetLooseVEntry((LTable)table, names, classes, closure))
2516	return True;
2517    if (table->tight && table == otable->next) {
2518	table = table->next;
2519	if (table && table->name == otable->name && table->hasloose)
2520	    return GetLooseVEntry((LTable)table, names, classes, closure);
2521    }
2522    return False;
2523}
2524
2525Bool XrmQGetResource(
2526    XrmDatabase         db,
2527    XrmNameList		names,
2528    XrmClassList 	classes,
2529    XrmRepresentation	*pType,  /* RETURN */
2530    XrmValuePtr		pValue)  /* RETURN */
2531{
2532    register NTable table;
2533    VClosureRec closure;
2534
2535    if (db && *names) {
2536	_XLockMutex(&db->linfo);
2537	closure.type = pType;
2538	closure.value = pValue;
2539	table = db->table;
2540	if (names[1]) {
2541	    if (table && !table->leaf) {
2542		if (GetNEntry(table, names, classes, &closure)) {
2543		    _XUnlockMutex(&db->linfo);
2544		    return True;
2545		}
2546	    } else if (table && table->hasloose &&
2547		    GetLooseVEntry((LTable)table, names, classes, &closure)) {
2548		_XUnlockMutex (&db->linfo);
2549		return True;
2550	    }
2551	} else {
2552	    if (table && !table->leaf)
2553		table = table->next;
2554	    if (table && GetVEntry((LTable)table, names, classes, &closure)) {
2555		_XUnlockMutex(&db->linfo);
2556		return True;
2557	    }
2558	}
2559	_XUnlockMutex(&db->linfo);
2560    }
2561    *pType = NULLQUARK;
2562    pValue->addr = (XPointer)NULL;
2563    pValue->size = 0;
2564    return False;
2565}
2566
2567Bool
2568XrmGetResource(XrmDatabase db, _Xconst char *name_str, _Xconst char *class_str,
2569	       XrmString *pType_str, XrmValuePtr pValue)
2570{
2571    XrmName		names[MAXDBDEPTH+1];
2572    XrmClass		classes[MAXDBDEPTH+1];
2573    XrmRepresentation   fromType;
2574    Bool		result;
2575
2576    XrmStringToNameList(name_str, names);
2577    XrmStringToClassList(class_str, classes);
2578    result = XrmQGetResource(db, names, classes, &fromType, pValue);
2579    (*pType_str) = XrmQuarkToString(fromType);
2580    return result;
2581}
2582
2583/* destroy all values, plus table itself */
2584static void DestroyLTable(
2585    LTable table)
2586{
2587    register int i;
2588    register VEntry *buckets;
2589    register VEntry entry, next;
2590
2591    buckets = table->buckets;
2592    for (i = table->table.mask; i >= 0; i--, buckets++) {
2593	for (next = *buckets; (entry = next); ) {
2594	    next = entry->next;
2595	    Xfree((char *)entry);
2596	}
2597    }
2598    Xfree((char *)table->buckets);
2599    Xfree((char *)table);
2600}
2601
2602/* destroy all contained tables, plus table itself */
2603static void DestroyNTable(
2604    NTable table)
2605{
2606    register int i;
2607    register NTable *buckets;
2608    register NTable entry, next;
2609
2610    buckets = NodeBuckets(table);
2611    for (i = table->mask; i >= 0; i--, buckets++) {
2612	for (next = *buckets; (entry = next); ) {
2613	    next = entry->next;
2614	    if (entry->leaf)
2615		DestroyLTable((LTable)entry);
2616	    else
2617		DestroyNTable(entry);
2618	}
2619    }
2620    Xfree((char *)table);
2621}
2622
2623const char *
2624XrmLocaleOfDatabase(
2625    XrmDatabase db)
2626{
2627    const char* retval;
2628    _XLockMutex(&db->linfo);
2629    retval = (*db->methods->lcname)(db->mbstate);
2630    _XUnlockMutex(&db->linfo);
2631    return retval;
2632}
2633
2634void XrmDestroyDatabase(
2635    XrmDatabase   db)
2636{
2637    register NTable table, next;
2638
2639    if (db) {
2640	_XLockMutex(&db->linfo);
2641	for (next = db->table; (table = next); ) {
2642	    next = table->next;
2643	    if (table->leaf)
2644		DestroyLTable((LTable)table);
2645	    else
2646		DestroyNTable(table);
2647	}
2648	_XUnlockMutex(&db->linfo);
2649	_XFreeMutex(&db->linfo);
2650	(*db->methods->destroy)(db->mbstate);
2651	Xfree((char *)db);
2652    }
2653}
2654