Xrm.c revision 57f47464
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 = (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	    Xfree(table); \
847	    return; \
848        } \
849	((LTable)table)->buckets = (VEntry *)nprev; \
850    } \
851    *nprev = (NTable)NULL; \
852    table->next = *prev; \
853    *prev = table
854
855    if (!db || !*quarks)
856	return;
857    table = *(prev = &db->table);
858    /* if already at leaf, bump to the leaf table */
859    if (!quarks[1] && table && !table->leaf)
860	table = *(prev = &table->next);
861    pprev = prev;
862    if (!table || (quarks[1] && table->leaf)) {
863	/* no top-level node table, create one and chain it in */
864	NEWTABLE(NULLQUARK,1);
865	table->tight = 1; /* arbitrary */
866	prev = nprev;
867    } else {
868	/* search along until we need a value */
869	while (quarks[1]) {
870	    q = *quarks;
871	    table = *(prev = &NodeHash(table, q));
872	    while (table && table->name != q)
873		table = *(prev = &table->next);
874	    if (!table)
875		break; /* not found */
876	    if (quarks[2]) {
877		if (table->leaf)
878		    break; /* not found */
879	    } else {
880		if (!table->leaf) {
881		    /* bump to leaf table, if any */
882		    table = *(prev = &table->next);
883		    if (!table || table->name != q)
884			break; /* not found */
885		    if (!table->leaf) {
886			/* bump to leaf table, if any */
887			table = *(prev = &table->next);
888			if (!table || table->name != q)
889			    break; /* not found */
890		    }
891		}
892	    }
893	    if (*bindings == XrmBindTightly) {
894		if (!table->tight)
895		    break; /* not found */
896	    } else {
897		if (table->tight) {
898		    /* bump to loose table, if any */
899		    table = *(prev = &table->next);
900		    if (!table || table->name != q ||
901			!quarks[2] != table->leaf)
902			break; /* not found */
903		}
904	    }
905	    /* found that one, bump to next quark */
906	    pprev = prev;
907	    quarks++;
908	    bindings++;
909	}
910	if (!quarks[1]) {
911	    /* found all the way to a leaf */
912	    q = *quarks;
913	    entry = *(vprev = &LeafHash((LTable)table, q));
914	    while (entry && entry->name != q)
915		entry = *(vprev = &entry->next);
916	    /* if want loose and have tight, bump to next entry */
917	    if (entry && *bindings == XrmBindLoosely && entry->tight)
918		entry = *(vprev = &entry->next);
919	    if (entry && entry->name == q &&
920		(*bindings == XrmBindTightly) == entry->tight) {
921		/* match, need to override */
922		if ((type == XrmQString) == entry->string &&
923		    entry->size == value->size) {
924		    /* update type if not String, can be different */
925		    if (!entry->string)
926			RepType(entry) = type;
927		    /* identical size, just overwrite value */
928		    memcpy(RawValue(entry), (char *)value->addr, value->size);
929		    return;
930		}
931		/* splice out and free old entry */
932		*vprev = entry->next;
933		Xfree((char *)entry);
934		(*pprev)->entries--;
935	    }
936	    /* this is where to insert */
937	    prev = (NTable *)vprev;
938	}
939    }
940    /* keep the top table, because we may have to grow it */
941    firstpprev = pprev;
942    /* iterate until we get to the leaf */
943    while (quarks[1]) {
944	/* build a new table and chain it in */
945	NEWTABLE(*quarks,2);
946	if (*quarks++ == XrmQANY)
947	    (*pprev)->hasany = 1;
948	if (*bindings++ == XrmBindTightly) {
949	    table->tight = 1;
950	} else {
951	    table->tight = 0;
952	    (*pprev)->hasloose = 1;
953	}
954	(*pprev)->entries++;
955	pprev = prev;
956	prev = nprev;
957    }
958    /* now allocate the value entry */
959    entry = (VEntry)Xmalloc(((type == XrmQString) ?
960			     sizeof(VEntryRec) : sizeof(DEntryRec)) +
961			    value->size);
962    if (!entry)
963	return;
964    entry->name = q = *quarks;
965    if (*bindings == XrmBindTightly) {
966	entry->tight = 1;
967    } else {
968	entry->tight = 0;
969	(*pprev)->hasloose = 1;
970    }
971    /* chain it in, with a bit of type cast ugliness */
972    entry->next = *((VEntry *)prev);
973    *((VEntry *)prev) = entry;
974    entry->size = value->size;
975    if (type == XrmQString) {
976	entry->string = 1;
977    } else {
978	entry->string = 0;
979	RepType(entry) = type;
980    }
981    /* save a copy of the value */
982    memcpy(RawValue(entry), (char *)value->addr, value->size);
983    (*pprev)->entries++;
984    /* this is a new leaf, need to remember it for search lists */
985    if (q > maxResourceQuark) {
986	unsigned oldsize = (maxResourceQuark + 1) >> 3;
987	unsigned size = ((q | 0x7f) + 1) >> 3; /* reallocate in chunks */
988	if (resourceQuarks) {
989	    unsigned char *prevQuarks = resourceQuarks;
990
991	    resourceQuarks = (unsigned char *)Xrealloc((char *)resourceQuarks,
992						       size);
993	    if (!resourceQuarks) {
994		Xfree(prevQuarks);
995	    }
996	} else
997	    resourceQuarks = (unsigned char *)Xmalloc(size);
998	if (resourceQuarks) {
999	    bzero((char *)&resourceQuarks[oldsize], size - oldsize);
1000	    maxResourceQuark = (size << 3) - 1;
1001	} else {
1002	    maxResourceQuark = -1;
1003	}
1004    }
1005    if (q > 0 && resourceQuarks)
1006	resourceQuarks[q >> 3] |= 1 << (q & 0x7);
1007    GROW(firstpprev);
1008
1009#undef NEWTABLE
1010}
1011
1012void XrmQPutResource(
1013    XrmDatabase		*pdb,
1014    XrmBindingList      bindings,
1015    XrmQuarkList	quarks,
1016    XrmRepresentation	type,
1017    XrmValuePtr		value)
1018{
1019    if (!*pdb) *pdb = NewDatabase();
1020    _XLockMutex(&(*pdb)->linfo);
1021    PutEntry(*pdb, bindings, quarks, type, value);
1022    _XUnlockMutex(&(*pdb)->linfo);
1023}
1024
1025void
1026XrmPutResource(
1027    XrmDatabase     *pdb,
1028    _Xconst char    *specifier,
1029    _Xconst char    *type,
1030    XrmValuePtr	    value)
1031{
1032    XrmBinding	    bindings[MAXDBDEPTH+1];
1033    XrmQuark	    quarks[MAXDBDEPTH+1];
1034
1035    if (!*pdb) *pdb = NewDatabase();
1036    _XLockMutex(&(*pdb)->linfo);
1037    XrmStringToBindingQuarkList(specifier, bindings, quarks);
1038    PutEntry(*pdb, bindings, quarks, XrmStringToQuark(type), value);
1039    _XUnlockMutex(&(*pdb)->linfo);
1040}
1041
1042void
1043XrmQPutStringResource(
1044    XrmDatabase     *pdb,
1045    XrmBindingList  bindings,
1046    XrmQuarkList    quarks,
1047    _Xconst char    *str)
1048{
1049    XrmValue	value;
1050
1051    if (!*pdb) *pdb = NewDatabase();
1052    value.addr = (XPointer) str;
1053    value.size = strlen(str)+1;
1054    _XLockMutex(&(*pdb)->linfo);
1055    PutEntry(*pdb, bindings, quarks, XrmQString, &value);
1056    _XUnlockMutex(&(*pdb)->linfo);
1057}
1058
1059/*	Function Name: GetDatabase
1060 *	Description: Parses a string and stores it as a database.
1061 *	Arguments: db - the database.
1062 *                 str - a pointer to the string containing the database.
1063 *                 filename - source filename, if any.
1064 *                 doall - whether to do all lines or just one
1065 */
1066
1067/*
1068 * This function is highly optimized to inline as much as possible.
1069 * Be very careful with modifications, or simplifications, as they
1070 * may adversely affect the performance.
1071 *
1072 * Chris Peterson, MIT X Consortium		5/17/90.
1073 */
1074
1075/*
1076 * Xlib spec says max 100 quarks in a lookup, will stop and return if
1077 * return if any single production's lhs has more than 100 components.
1078 */
1079#define QLIST_SIZE 100
1080
1081/*
1082 * This should be big enough to handle things like the XKeysymDB or biggish
1083 * ~/.Xdefaults or app-defaults files. Anything bigger will be allocated on
1084 * the heap.
1085 */
1086#define DEF_BUFF_SIZE 8192
1087
1088static void GetIncludeFile(
1089    XrmDatabase db,
1090    _Xconst char *base,
1091    _Xconst char *fname,
1092    int fnamelen);
1093
1094static void GetDatabase(
1095    XrmDatabase db,
1096    _Xconst register char *str,
1097    _Xconst char *filename,
1098    Bool doall)
1099{
1100    char *rhs;
1101    char *lhs, lhs_s[DEF_BUFF_SIZE];
1102    XrmQuark quarks[QLIST_SIZE + 1];	/* allow for a terminal NullQuark */
1103    XrmBinding bindings[QLIST_SIZE + 1];
1104
1105    register char *ptr;
1106    register XrmBits bits = 0;
1107    register char c;
1108    register Signature sig;
1109    register char *ptr_max;
1110    register int num_quarks;
1111    register XrmBindingList t_bindings;
1112
1113    int len, alloc_chars;
1114    unsigned long str_len;
1115    XrmValue value;
1116    Bool only_pcs;
1117    Bool dolines;
1118
1119    if (!db)
1120	return;
1121
1122    /*
1123     * if strlen (str) < DEF_BUFF_SIZE allocate buffers on the stack for
1124     * speed otherwise malloc the buffer. From a buffer overflow standpoint
1125     * we can be sure that neither: a) a component on the lhs, or b) a
1126     * value on the rhs, will be longer than the overall length of str,
1127     * i.e. strlen(str).
1128     *
1129     * This should give good performance when parsing "*foo: bar" type
1130     * databases as might be passed with -xrm command line options; but
1131     * with larger databases, e.g. .Xdefaults, app-defaults, or KeysymDB
1132     * files, the size of the buffers will be overly large. One way
1133     * around this would be to double-parse each production with a resulting
1134     * performance hit. In any event we can be assured that a lhs component
1135     * name or a rhs value won't be longer than str itself.
1136     */
1137
1138    str_len = strlen (str);
1139    if (DEF_BUFF_SIZE > str_len) lhs = lhs_s;
1140    else if ((lhs = (char*) Xmalloc (str_len)) == NULL)
1141	return;
1142
1143    alloc_chars = DEF_BUFF_SIZE < str_len ? str_len : DEF_BUFF_SIZE;
1144    if ((rhs = (char*) Xmalloc (alloc_chars)) == NULL) {
1145	if (lhs != lhs_s) Xfree (lhs);
1146	return;
1147    }
1148
1149    (*db->methods->mbinit)(db->mbstate);
1150    str--;
1151    dolines = True;
1152    while (!is_EOF(bits) && dolines) {
1153	dolines = doall;
1154
1155	/*
1156	 * First: Remove extra whitespace.
1157	 */
1158
1159	do {
1160	    bits = next_char(c, str);
1161	} while is_space(bits);
1162
1163	/*
1164	 * Ignore empty lines.
1165	 */
1166
1167	if (is_EOL(bits))
1168	    continue;		/* start a new line. */
1169
1170	/*
1171	 * Second: check the first character in a line to see if it is
1172	 * "!" signifying a comment, or "#" signifying a directive.
1173	 */
1174
1175	if (c == '!') { /* Comment, spin to next newline */
1176	    while (is_simple(bits = next_char(c, str))) {}
1177	    if (is_EOL(bits))
1178		continue;
1179	    while (!is_EOL(bits = next_mbchar(c, len, str))) {}
1180	    str--;
1181	    continue;		/* start a new line. */
1182	}
1183
1184	if (c == '#') { /* Directive */
1185	    /* remove extra whitespace */
1186	    only_pcs = True;
1187	    while (is_space(bits = next_char(c, str))) {};
1188	    /* only "include" directive is currently defined */
1189	    if (!strncmp(str, "include", 7)) {
1190		str += (7-1);
1191		/* remove extra whitespace */
1192		while (is_space(bits = next_char(c, str))) {};
1193		/* must have a starting " */
1194		if (c == '"') {
1195		    _Xconst char *fname = str+1;
1196		    len = 0;
1197		    do {
1198			if (only_pcs) {
1199			    bits = next_char(c, str);
1200			    if (is_nonpcs(bits))
1201				only_pcs = False;
1202			}
1203			if (!only_pcs)
1204			    bits = next_mbchar(c, len, str);
1205		    } while (c != '"' && !is_EOL(bits));
1206		    /* must have an ending " */
1207		    if (c == '"')
1208			GetIncludeFile(db, filename, fname, str - len - fname);
1209		}
1210	    }
1211	    /* spin to next newline */
1212	    if (only_pcs) {
1213		while (is_simple(bits))
1214		    bits = next_char(c, str);
1215		if (is_EOL(bits))
1216		    continue;
1217	    }
1218	    while (!is_EOL(bits))
1219		bits = next_mbchar(c, len, str);
1220	    str--;
1221	    continue;		/* start a new line. */
1222	}
1223
1224	/*
1225	 * Third: loop through the LHS of the resource specification
1226	 * storing characters and converting this to a Quark.
1227	 */
1228
1229	num_quarks = 0;
1230	t_bindings = bindings;
1231
1232	sig = 0;
1233	ptr = lhs;
1234	*t_bindings = XrmBindTightly;
1235	for(;;) {
1236	    if (!is_binding(bits)) {
1237		while (!is_EOQ(bits)) {
1238		    *ptr++ = c;
1239		    sig = (sig << 1) + c; /* Compute the signature. */
1240		    bits = next_char(c, str);
1241		}
1242
1243		quarks[num_quarks++] =
1244			_XrmInternalStringToQuark(lhs, ptr - lhs, sig, False);
1245
1246		if (num_quarks > QLIST_SIZE) {
1247		    Xfree(rhs);
1248		    if (lhs != lhs_s) Xfree (lhs);
1249		    (*db->methods->mbfinish)(db->mbstate);
1250		    return;
1251		}
1252
1253		if (is_separator(bits))  {
1254		    if (!is_space(bits))
1255			break;
1256
1257		    /* Remove white space */
1258		    do {
1259			*ptr++ = c;
1260			sig = (sig << 1) + c; /* Compute the signature. */
1261		    } while (is_space(bits = next_char(c, str)));
1262
1263		    /*
1264		     * The spec doesn't permit it, but support spaces
1265		     * internal to resource name/class
1266		     */
1267
1268		    if (is_separator(bits))
1269			break;
1270		    num_quarks--;
1271		    continue;
1272		}
1273
1274		if (c == '.')
1275		    *(++t_bindings) = XrmBindTightly;
1276		else
1277		    *(++t_bindings) = XrmBindLoosely;
1278
1279		sig = 0;
1280		ptr = lhs;
1281	    }
1282	    else {
1283		/*
1284		 * Magic unspecified feature #254.
1285		 *
1286		 * If two separators appear with no Text between them then
1287		 * ignore them.
1288		 *
1289		 * If anyone of those separators is a '*' then the binding
1290		 * will be loose, otherwise it will be tight.
1291		 */
1292
1293		if (c == '*')
1294		    *t_bindings = XrmBindLoosely;
1295	    }
1296
1297	    bits = next_char(c, str);
1298	}
1299
1300	quarks[num_quarks] = NULLQUARK;
1301
1302	/*
1303	 * Make sure that there is a ':' in this line.
1304	 */
1305
1306	if (c != ':') {
1307	    char oldc;
1308
1309	    /*
1310	     * A parsing error has occured, toss everything on the line
1311	     * a new_line can still be escaped with a '\'.
1312	     */
1313
1314	    while (is_normal(bits))
1315		bits = next_char(c, str);
1316	    if (is_EOL(bits))
1317		continue;
1318	    bits = next_mbchar(c, len, str);
1319	    do {
1320		oldc = c;
1321		bits = next_mbchar(c, len, str);
1322	    } while (c && (c != '\n' || oldc == '\\'));
1323	    str--;
1324	    continue;
1325	}
1326
1327	/*
1328	 * I now have a quark and binding list for the entire left hand
1329	 * side.  "c" currently points to the ":" separating the left hand
1330	 * side for the right hand side.  It is time to begin processing
1331	 * the right hand side.
1332	 */
1333
1334	/*
1335	 * Fourth: Remove more whitespace
1336	 */
1337
1338	for(;;) {
1339	    if (is_space(bits = next_char(c, str)))
1340		continue;
1341	    if (c != '\\')
1342		break;
1343	    bits = next_char(c, str);
1344	    if (c == '\n')
1345		continue;
1346	    str--;
1347	    bits = BSLASH;
1348	    c = '\\';
1349	    break;
1350	}
1351
1352	/*
1353	 * Fifth: Process the right hand side.
1354	 */
1355
1356	ptr = rhs;
1357	ptr_max = ptr + alloc_chars - 4;
1358	only_pcs = True;
1359	len = 1;
1360
1361	for(;;) {
1362
1363	    /*
1364	     * Tight loop for the normal case:  Non backslash, non-end of value
1365	     * character that will fit into the allocated buffer.
1366	     */
1367
1368	    if (only_pcs) {
1369		while (is_normal(bits) && ptr < ptr_max) {
1370		    *ptr++ = c;
1371		    bits = next_char(c, str);
1372		}
1373		if (is_EOL(bits))
1374		    break;
1375		if (is_nonpcs(bits)) {
1376		    only_pcs = False;
1377		    bits = next_mbchar(c, len, str);
1378		}
1379	    }
1380	    while (!is_special(bits) && ptr + len <= ptr_max) {
1381		len = -len;
1382		while (len)
1383		    *ptr++ = str[len++];
1384		if (*str == '\0') {
1385		    bits = EOS;
1386		    break;
1387		}
1388		bits = next_mbchar(c, len, str);
1389	    }
1390
1391	    if (is_EOL(bits)) {
1392		str--;
1393		break;
1394	    }
1395
1396	    if (c == '\\') {
1397		/*
1398		 * We need to do some magic after a backslash.
1399		 */
1400		Bool read_next = True;
1401
1402		if (only_pcs) {
1403		    bits = next_char(c, str);
1404		    if (is_nonpcs(bits))
1405			only_pcs = False;
1406		}
1407		if (!only_pcs)
1408		    bits = next_mbchar(c, len, str);
1409
1410		if (is_EOL(bits)) {
1411		    if (is_EOF(bits))
1412			continue;
1413		} else if (c == 'n') {
1414		    /*
1415		     * "\n" means insert a newline.
1416		     */
1417		    *ptr++ = '\n';
1418		} else if (c == '\\') {
1419		    /*
1420		     * "\\" completes to just one backslash.
1421		     */
1422		    *ptr++ = '\\';
1423		} else {
1424		    /*
1425		     * pick up to three octal digits after the '\'.
1426		     */
1427		    char temp[3];
1428		    int count = 0;
1429		    while (is_odigit(bits) && count < 3) {
1430			temp[count++] = c;
1431			if (only_pcs) {
1432			    bits = next_char(c, str);
1433			    if (is_nonpcs(bits))
1434				only_pcs = False;
1435			}
1436			if (!only_pcs)
1437			    bits = next_mbchar(c, len, str);
1438		    }
1439
1440		    /*
1441		     * If we found three digits then insert that octal code
1442		     * into the value string as a character.
1443		     */
1444
1445		    if (count == 3) {
1446			*ptr++ = (unsigned char) ((temp[0] - '0') * 0100 +
1447						  (temp[1] - '0') * 010 +
1448						  (temp[2] - '0'));
1449		    }
1450		    else {
1451			int tcount;
1452
1453			/*
1454			 * Otherwise just insert those characters into the
1455			 * string, since no special processing is needed on
1456			 * numerics we can skip the special processing.
1457			 */
1458
1459			for (tcount = 0; tcount < count; tcount++) {
1460			    *ptr++ = temp[tcount]; /* print them in
1461						      the correct order */
1462			}
1463		    }
1464		    read_next = False;
1465		}
1466		if (read_next) {
1467		    if (only_pcs) {
1468			bits = next_char(c, str);
1469			if (is_nonpcs(bits))
1470			    only_pcs = False;
1471		    }
1472		    if (!only_pcs)
1473			bits = next_mbchar(c, len, str);
1474		}
1475	    }
1476
1477	    /*
1478	     * It is important to make sure that there is room for at least
1479	     * four more characters in the buffer, since I can add that
1480	     * many characters into the buffer after a backslash has occured.
1481	     */
1482
1483	    if (ptr + len > ptr_max) {
1484		char * temp_str;
1485
1486		alloc_chars += BUFSIZ/10;
1487		temp_str = Xrealloc(rhs, sizeof(char) * alloc_chars);
1488
1489		if (!temp_str) {
1490		    Xfree(rhs);
1491		    if (lhs != lhs_s) Xfree (lhs);
1492		    (*db->methods->mbfinish)(db->mbstate);
1493		    return;
1494		}
1495
1496		ptr = temp_str + (ptr - rhs); /* reset pointer. */
1497		rhs = temp_str;
1498		ptr_max = rhs + alloc_chars - 4;
1499	    }
1500	}
1501
1502	/*
1503	 * Lastly: Terminate the value string, and store this entry
1504	 * 	   into the database.
1505	 */
1506
1507	*ptr++ = '\0';
1508
1509	/* Store it in database */
1510	value.size = ptr - rhs;
1511	value.addr = (XPointer) rhs;
1512
1513	PutEntry(db, bindings, quarks, XrmQString, &value);
1514    }
1515
1516    if (lhs != lhs_s) Xfree (lhs);
1517    Xfree (rhs);
1518
1519    (*db->methods->mbfinish)(db->mbstate);
1520}
1521
1522void
1523XrmPutStringResource(
1524    XrmDatabase *pdb,
1525    _Xconst char*specifier,
1526    _Xconst char*str)
1527{
1528    XrmValue	value;
1529    XrmBinding	bindings[MAXDBDEPTH+1];
1530    XrmQuark	quarks[MAXDBDEPTH+1];
1531
1532    if (!*pdb) *pdb = NewDatabase();
1533    XrmStringToBindingQuarkList(specifier, bindings, quarks);
1534    value.addr = (XPointer) str;
1535    value.size = strlen(str)+1;
1536    _XLockMutex(&(*pdb)->linfo);
1537    PutEntry(*pdb, bindings, quarks, XrmQString, &value);
1538    _XUnlockMutex(&(*pdb)->linfo);
1539}
1540
1541
1542void
1543XrmPutLineResource(
1544    XrmDatabase *pdb,
1545    _Xconst char*line)
1546{
1547    if (!*pdb) *pdb = NewDatabase();
1548    _XLockMutex(&(*pdb)->linfo);
1549    GetDatabase(*pdb, line, (char *)NULL, False);
1550    _XUnlockMutex(&(*pdb)->linfo);
1551}
1552
1553XrmDatabase
1554XrmGetStringDatabase(
1555    _Xconst char    *data)
1556{
1557    XrmDatabase     db;
1558
1559    db = NewDatabase();
1560    _XLockMutex(&db->linfo);
1561    GetDatabase(db, data, (char *)NULL, True);
1562    _XUnlockMutex(&db->linfo);
1563    return db;
1564}
1565
1566/*	Function Name: ReadInFile
1567 *	Description: Reads the file into a buffer.
1568 *	Arguments: filename - the name of the file.
1569 *	Returns: An allocated string containing the contents of the file.
1570 */
1571
1572static char *
1573ReadInFile(_Xconst char *filename)
1574{
1575    register int fd, size;
1576    char * filebuf;
1577
1578#ifdef __UNIXOS2__
1579    filename = __XOS2RedirRoot(filename);
1580#endif
1581
1582    /*
1583     * MS-Windows and OS/2 note: Default open mode includes O_TEXT
1584     */
1585    if ( (fd = _XOpenFile (filename, O_RDONLY)) == -1 )
1586	return (char *)NULL;
1587
1588    /*
1589     * MS-Windows and OS/2 note: depending on how the sources are
1590     * untarred, the newlines in resource files may or may not have
1591     * been expanded to CRLF. Either way the size returned by fstat
1592     * is sufficient to read the file into because in text-mode any
1593     * CRLFs in a file will be converted to newlines (LF) with the
1594     * result that the number of bytes actually read with be <=
1595     * to the size returned by fstat.
1596     */
1597    {
1598	struct stat status_buffer;
1599	if ( (fstat(fd, &status_buffer)) == -1 ) {
1600	    close (fd);
1601	    return (char *)NULL;
1602	} else
1603	    size = status_buffer.st_size;
1604    }
1605
1606    if (!(filebuf = Xmalloc(size + 1))) { /* leave room for '\0' */
1607	close(fd);
1608	return (char *)NULL;
1609    }
1610    size = read (fd, filebuf, size);
1611
1612#ifdef __UNIXOS2__
1613    { /* kill CRLF */
1614      int i,k;
1615      for (i=k=0; i<size; i++)
1616	if (filebuf[i] != 0x0d) {
1617	   filebuf[k++] = filebuf[i];
1618	}
1619	filebuf[k] = 0;
1620    }
1621#endif
1622
1623    if (size < 0) {
1624	close (fd);
1625	Xfree(filebuf);
1626	return (char *)NULL;
1627    }
1628    close (fd);
1629
1630    filebuf[size] = '\0';	/* NULL terminate it. */
1631    return filebuf;
1632}
1633
1634static void
1635GetIncludeFile(
1636    XrmDatabase db,
1637    _Xconst char *base,
1638    _Xconst char *fname,
1639    int fnamelen)
1640{
1641    int len;
1642    char *str;
1643    char realfname[BUFSIZ];
1644
1645    if (fnamelen <= 0 || fnamelen >= BUFSIZ)
1646	return;
1647    if (*fname != '/' && base && (str = strrchr(base, '/'))) {
1648	len = str - base + 1;
1649	if (len + fnamelen >= BUFSIZ)
1650	    return;
1651	strncpy(realfname, base, len);
1652	strncpy(realfname + len, fname, fnamelen);
1653	realfname[len + fnamelen] = '\0';
1654    } else {
1655	strncpy(realfname, fname, fnamelen);
1656	realfname[fnamelen] = '\0';
1657    }
1658    if (!(str = ReadInFile(realfname)))
1659	return;
1660    GetDatabase(db, str, realfname, True);
1661    Xfree(str);
1662}
1663
1664XrmDatabase
1665XrmGetFileDatabase(
1666    _Xconst char    *filename)
1667{
1668    XrmDatabase db;
1669    char *str;
1670
1671    if (!(str = ReadInFile(filename)))
1672	return (XrmDatabase)NULL;
1673
1674    db = NewDatabase();
1675    _XLockMutex(&db->linfo);
1676    GetDatabase(db, str, filename, True);
1677    _XUnlockMutex(&db->linfo);
1678    Xfree(str);
1679    return db;
1680}
1681
1682Status
1683XrmCombineFileDatabase(
1684    _Xconst char    *filename,
1685    XrmDatabase     *target,
1686    Bool             override)
1687{
1688    XrmDatabase db;
1689    char *str;
1690
1691    if (!(str = ReadInFile(filename)))
1692	return 0;
1693    if (override) {
1694	db = *target;
1695	if (!db)
1696	    *target = db = NewDatabase();
1697    } else
1698	db = NewDatabase();
1699    _XLockMutex(&db->linfo);
1700    GetDatabase(db, str, filename, True);
1701    _XUnlockMutex(&db->linfo);
1702    Xfree(str);
1703    if (!override)
1704	XrmCombineDatabase(db, target, False);
1705    return 1;
1706}
1707
1708/* call the user proc for every value in the table, arbitrary order.
1709 * stop if user proc returns True.  level is current depth in database.
1710 */
1711/*ARGSUSED*/
1712static Bool EnumLTable(
1713    LTable		table,
1714    XrmNameList		names,
1715    XrmClassList 	classes,
1716    register int	level,
1717    register EClosure	closure)
1718{
1719    register VEntry *bucket;
1720    register int i;
1721    register VEntry entry;
1722    XrmValue value;
1723    XrmRepresentation type;
1724    Bool tightOk;
1725
1726    closure->bindings[level] = (table->table.tight ?
1727				XrmBindTightly : XrmBindLoosely);
1728    closure->quarks[level] = table->table.name;
1729    level++;
1730    tightOk = !*names;
1731    closure->quarks[level + 1] = NULLQUARK;
1732    for (i = table->table.mask, bucket = table->buckets;
1733	 i >= 0;
1734	 i--, bucket++) {
1735	for (entry = *bucket; entry; entry = entry->next) {
1736	    if (entry->tight && !tightOk)
1737		continue;
1738	    closure->bindings[level] = (entry->tight ?
1739					XrmBindTightly : XrmBindLoosely);
1740	    closure->quarks[level] = entry->name;
1741	    value.size = entry->size;
1742	    if (entry->string) {
1743		type = XrmQString;
1744		value.addr = StringValue(entry);
1745	    } else {
1746		type = RepType(entry);
1747		value.addr = DataValue(entry);
1748	    }
1749	    if ((*closure->proc)(&closure->db, closure->bindings+1,
1750				 closure->quarks+1, &type, &value,
1751				 closure->closure))
1752		return True;
1753	}
1754    }
1755    return False;
1756}
1757
1758static Bool EnumAllNTable(
1759    NTable		table,
1760    register int	level,
1761    register EClosure	closure)
1762{
1763    register NTable *bucket;
1764    register int i;
1765    register NTable entry;
1766    XrmQuark empty = NULLQUARK;
1767
1768    if (level >= MAXDBDEPTH)
1769	return False;
1770    for (i = table->mask, bucket = NodeBuckets(table);
1771	 i >= 0;
1772	 i--, bucket++) {
1773	for (entry = *bucket; entry; entry = entry->next) {
1774	    if (entry->leaf) {
1775		if (EnumLTable((LTable)entry, &empty, &empty, level, closure))
1776		    return True;
1777	    } else {
1778		closure->bindings[level] = (entry->tight ?
1779					    XrmBindTightly : XrmBindLoosely);
1780		closure->quarks[level] = entry->name;
1781		if (EnumAllNTable(entry, level+1, closure))
1782		    return True;
1783	    }
1784	}
1785    }
1786    return False;
1787}
1788
1789/* recurse on every table in the table, arbitrary order.
1790 * stop if user proc returns True.  level is current depth in database.
1791 */
1792static Bool EnumNTable(
1793    NTable		table,
1794    XrmNameList		names,
1795    XrmClassList 	classes,
1796    register int	level,
1797    register EClosure	closure)
1798{
1799    register NTable	entry;
1800    register XrmQuark	q;
1801    register unsigned int leaf;
1802    Bool (*get)(
1803            NTable		table,
1804            XrmNameList		names,
1805            XrmClassList 	classes,
1806            register int	level,
1807            EClosure		closure);
1808    Bool bilevel;
1809
1810/* find entries named ename, leafness leaf, tight or loose, and call get */
1811#define ITIGHTLOOSE(ename) \
1812    NFIND(ename); \
1813    if (entry) { \
1814	if (leaf == entry->leaf) { \
1815	    if (!leaf && !entry->tight && entry->next && \
1816		entry->next->name == q && entry->next->tight && \
1817		(bilevel || entry->next->hasloose) && \
1818		EnumLTable((LTable)entry->next, names+1, classes+1, \
1819			   level, closure)) \
1820		return True; \
1821	    if ((*get)(entry, names+1, classes+1, level, closure)) \
1822		return True; \
1823	    if (entry->tight && (entry = entry->next) && \
1824		entry->name == q && leaf == entry->leaf && \
1825		(*get)(entry, names+1, classes+1, level, closure)) \
1826		return True; \
1827	} else if (entry->leaf) { \
1828	    if ((bilevel || entry->hasloose) && \
1829		EnumLTable((LTable)entry, names+1, classes+1, level, closure))\
1830		return True; \
1831	    if (entry->tight && (entry = entry->next) && \
1832		entry->name == q && (bilevel || entry->hasloose) && \
1833		EnumLTable((LTable)entry, names+1, classes+1, level, closure))\
1834		return True; \
1835	} \
1836    }
1837
1838/* find entries named ename, leafness leaf, loose only, and call get */
1839#define ILOOSE(ename) \
1840    NFIND(ename); \
1841    if (entry && entry->tight && (entry = entry->next) && entry->name != q) \
1842	entry = (NTable)NULL; \
1843    if (entry) { \
1844	if (leaf == entry->leaf) { \
1845	    if ((*get)(entry, names+1, classes+1, level, closure)) \
1846		return True; \
1847	} else if (entry->leaf && (bilevel || entry->hasloose)) { \
1848	    if (EnumLTable((LTable)entry, names+1, classes+1, level, closure))\
1849		return True; \
1850	} \
1851    }
1852
1853    if (level >= MAXDBDEPTH)
1854	return False;
1855    closure->bindings[level] = (table->tight ?
1856				XrmBindTightly : XrmBindLoosely);
1857    closure->quarks[level] = table->name;
1858    level++;
1859    if (!*names) {
1860	if (EnumAllNTable(table, level, closure))
1861	    return True;
1862    } else {
1863	if (names[1] || closure->mode == XrmEnumAllLevels) {
1864	    get = EnumNTable; /* recurse */
1865	    leaf = 0;
1866	    bilevel = !names[1];
1867	} else {
1868	    get = (getNTableEProcp)EnumLTable; /* bottom of recursion */
1869	    leaf = 1;
1870	    bilevel = False;
1871	}
1872	if (table->hasloose && closure->mode == XrmEnumAllLevels) {
1873	    NTable *bucket;
1874	    int i;
1875	    XrmQuark empty = NULLQUARK;
1876
1877	    for (i = table->mask, bucket = NodeBuckets(table);
1878		 i >= 0;
1879		 i--, bucket++) {
1880		q = NULLQUARK;
1881		for (entry = *bucket; entry; entry = entry->next) {
1882		    if (!entry->tight && entry->name != q &&
1883			entry->name != *names && entry->name != *classes) {
1884			q = entry->name;
1885			if (entry->leaf) {
1886			    if (EnumLTable((LTable)entry, &empty, &empty,
1887					   level, closure))
1888				return True;
1889			} else {
1890			    if (EnumNTable(entry, &empty, &empty,
1891					   level, closure))
1892				return True;
1893			}
1894		    }
1895		}
1896	    }
1897	}
1898
1899	ITIGHTLOOSE(*names);   /* do name, tight and loose */
1900	ITIGHTLOOSE(*classes); /* do class, tight and loose */
1901	if (table->hasany) {
1902	    ITIGHTLOOSE(XrmQANY); /* do ANY, tight and loose */
1903	}
1904	if (table->hasloose) {
1905	    while (1) {
1906		names++;
1907		classes++;
1908		if (!*names)
1909		    break;
1910		if (!names[1] && closure->mode != XrmEnumAllLevels) {
1911		    get = (getNTableEProcp)EnumLTable; /* bottom of recursion */
1912		    leaf = 1;
1913		}
1914		ILOOSE(*names);   /* loose names */
1915		ILOOSE(*classes); /* loose classes */
1916		if (table->hasany) {
1917		    ILOOSE(XrmQANY); /* loose ANY */
1918		}
1919	    }
1920	    names--;
1921	    classes--;
1922	}
1923    }
1924    /* now look for matching leaf nodes */
1925    entry = table->next;
1926    if (!entry)
1927	return False;
1928    if (entry->leaf) {
1929	if (entry->tight && !table->tight)
1930	    entry = entry->next;
1931    } else {
1932	entry = entry->next;
1933	if (!entry || !entry->tight)
1934	    return False;
1935    }
1936    if (!entry || entry->name != table->name)
1937	return False;
1938    /* found one */
1939    level--;
1940    if ((!*names || entry->hasloose) &&
1941	EnumLTable((LTable)entry, names, classes, level, closure))
1942	return True;
1943    if (entry->tight && entry == table->next && (entry = entry->next) &&
1944	entry->name == table->name && (!*names || entry->hasloose))
1945	return EnumLTable((LTable)entry, names, classes, level, closure);
1946    return False;
1947
1948#undef ITIGHTLOOSE
1949#undef ILOOSE
1950}
1951
1952/* call the proc for every value in the database, arbitrary order.
1953 * stop if the proc returns True.
1954 */
1955Bool XrmEnumerateDatabase(
1956    XrmDatabase		db,
1957    XrmNameList		names,
1958    XrmClassList	classes,
1959    int			mode,
1960    DBEnumProc		proc,
1961    XPointer		closure)
1962{
1963    XrmBinding  bindings[MAXDBDEPTH+2];
1964    XrmQuark	quarks[MAXDBDEPTH+2];
1965    register NTable table;
1966    EClosureRec	eclosure;
1967    Bool retval = False;
1968
1969    if (!db)
1970	return False;
1971    _XLockMutex(&db->linfo);
1972    eclosure.db = db;
1973    eclosure.proc = proc;
1974    eclosure.closure = closure;
1975    eclosure.bindings = bindings;
1976    eclosure.quarks = quarks;
1977    eclosure.mode = mode;
1978    table = db->table;
1979    if (table && !table->leaf && !*names && mode == XrmEnumOneLevel)
1980	table = table->next;
1981    if (table) {
1982	if (!table->leaf)
1983	    retval = EnumNTable(table, names, classes, 0, &eclosure);
1984	else
1985	    retval = EnumLTable((LTable)table, names, classes, 0, &eclosure);
1986    }
1987    _XUnlockMutex(&db->linfo);
1988    return retval;
1989}
1990
1991static void PrintBindingQuarkList(
1992    XrmBindingList      bindings,
1993    XrmQuarkList	quarks,
1994    FILE		*stream)
1995{
1996    Bool	firstNameSeen;
1997
1998    for (firstNameSeen = False; *quarks; bindings++, quarks++) {
1999	if (*bindings == XrmBindLoosely) {
2000	    (void) fprintf(stream, "*");
2001	} else if (firstNameSeen) {
2002	    (void) fprintf(stream, ".");
2003	}
2004	firstNameSeen = True;
2005	(void) fputs(XrmQuarkToString(*quarks), stream);
2006    }
2007}
2008
2009/* output out the entry in correct file syntax */
2010/*ARGSUSED*/
2011static Bool DumpEntry(
2012    XrmDatabase		*db,
2013    XrmBindingList      bindings,
2014    XrmQuarkList	quarks,
2015    XrmRepresentation   *type,
2016    XrmValuePtr		value,
2017    XPointer		data)
2018{
2019    FILE			*stream = (FILE *)data;
2020    register unsigned int	i;
2021    register char		*s;
2022    register char		c;
2023
2024    if (*type != XrmQString)
2025	(void) putc('!', stream);
2026    PrintBindingQuarkList(bindings, quarks, stream);
2027    s = value->addr;
2028    i = value->size;
2029    if (*type == XrmQString) {
2030	(void) fputs(":\t", stream);
2031	if (i)
2032	    i--;
2033    }
2034    else
2035	(void) fprintf(stream, "=%s:\t", XrmRepresentationToString(*type));
2036    if (i && (*s == ' ' || *s == '\t'))
2037	(void) putc('\\', stream); /* preserve leading whitespace */
2038    while (i--) {
2039	c = *s++;
2040	if (c == '\n') {
2041	    if (i)
2042		(void) fputs("\\n\\\n", stream);
2043	    else
2044		(void) fputs("\\n", stream);
2045	} else if (c == '\\')
2046	    (void) fputs("\\\\", stream);
2047	else if ((c < ' ' && c != '\t') ||
2048		 ((unsigned char)c >= 0x7f && (unsigned char)c < 0xa0))
2049	    (void) fprintf(stream, "\\%03o", (unsigned char)c);
2050	else
2051	    (void) putc(c, stream);
2052    }
2053    (void) putc('\n', stream);
2054    return ferror(stream) != 0;
2055}
2056
2057#ifdef DEBUG
2058
2059void PrintTable(
2060    NTable table,
2061    FILE *file)
2062{
2063    XrmBinding  bindings[MAXDBDEPTH+1];
2064    XrmQuark	quarks[MAXDBDEPTH+1];
2065    EClosureRec closure;
2066    XrmQuark	empty = NULLQUARK;
2067
2068    closure.db = (XrmDatabase)NULL;
2069    closure.proc = DumpEntry;
2070    closure.closure = (XPointer)file;
2071    closure.bindings = bindings;
2072    closure.quarks = quarks;
2073    closure.mode = XrmEnumAllLevels;
2074    if (table->leaf)
2075	EnumLTable((LTable)table, &empty, &empty, 0, &closure);
2076    else
2077	EnumNTable(table, &empty, &empty, 0, &closure);
2078}
2079
2080#endif /* DEBUG */
2081
2082void
2083XrmPutFileDatabase(
2084    XrmDatabase db,
2085    _Xconst char *fileName)
2086{
2087    FILE	*file;
2088    XrmQuark empty = NULLQUARK;
2089
2090    if (!db) return;
2091    if (!(file = fopen(fileName, "w"))) return;
2092    if (XrmEnumerateDatabase(db, &empty, &empty, XrmEnumAllLevels,
2093			     DumpEntry, (XPointer) file))
2094	unlink((char *)fileName);
2095    fclose(file);
2096}
2097
2098/* macros used in get/search functions */
2099
2100/* find entries named ename, leafness leaf, tight or loose, and call get */
2101#define GTIGHTLOOSE(ename,looseleaf) \
2102    NFIND(ename); \
2103    if (entry) { \
2104	if (leaf == entry->leaf) { \
2105	    if (!leaf && !entry->tight && entry->next && \
2106		entry->next->name == q && entry->next->tight && \
2107		entry->next->hasloose && \
2108		looseleaf((LTable)entry->next, names+1, classes+1, closure)) \
2109		return True; \
2110	    if ((*get)(entry, names+1, classes+1, closure)) \
2111		return True; \
2112	    if (entry->tight && (entry = entry->next) && \
2113		entry->name == q && leaf == entry->leaf && \
2114		(*get)(entry, names+1, classes+1, closure)) \
2115		return True; \
2116	} else if (entry->leaf) { \
2117	    if (entry->hasloose && \
2118		looseleaf((LTable)entry, names+1, classes+1, closure)) \
2119		return True; \
2120	    if (entry->tight && (entry = entry->next) && \
2121		entry->name == q && entry->hasloose && \
2122		looseleaf((LTable)entry, names+1, classes+1, closure)) \
2123		return True; \
2124	} \
2125    }
2126
2127/* find entries named ename, leafness leaf, loose only, and call get */
2128#define GLOOSE(ename,looseleaf) \
2129    NFIND(ename); \
2130    if (entry && entry->tight && (entry = entry->next) && entry->name != q) \
2131	entry = (NTable)NULL; \
2132    if (entry) { \
2133	if (leaf == entry->leaf) { \
2134	    if ((*get)(entry, names+1, classes+1, closure)) \
2135		return True; \
2136	} else if (entry->leaf && entry->hasloose) { \
2137	    if (looseleaf((LTable)entry, names+1, classes+1, closure)) \
2138		return True; \
2139	} \
2140    }
2141
2142/* add tight/loose entry to the search list, return True if list is full */
2143/*ARGSUSED*/
2144static Bool AppendLEntry(
2145    LTable		table,
2146    XrmNameList		names,
2147    XrmClassList 	classes,
2148    register SClosure	closure)
2149{
2150    /* check for duplicate */
2151    if (closure->idx >= 0 && closure->list[closure->idx] == table)
2152	return False;
2153    if (closure->idx == closure->limit)
2154	return True;
2155    /* append it */
2156    closure->idx++;
2157    closure->list[closure->idx] = table;
2158    return False;
2159}
2160
2161/* add loose entry to the search list, return True if list is full */
2162/*ARGSUSED*/
2163static Bool AppendLooseLEntry(
2164    LTable		table,
2165    XrmNameList		names,
2166    XrmClassList 	classes,
2167    register SClosure	closure)
2168{
2169    /* check for duplicate */
2170    if (closure->idx >= 0 && closure->list[closure->idx] == table)
2171	return False;
2172    if (closure->idx >= closure->limit - 1)
2173	return True;
2174    /* append it */
2175    closure->idx++;
2176    closure->list[closure->idx] = LOOSESEARCH;
2177    closure->idx++;
2178    closure->list[closure->idx] = table;
2179    return False;
2180}
2181
2182/* search for a leaf table */
2183static Bool SearchNEntry(
2184    NTable		table,
2185    XrmNameList		names,
2186    XrmClassList 	classes,
2187    SClosure		closure)
2188{
2189    register NTable	entry;
2190    register XrmQuark	q;
2191    register unsigned int leaf;
2192    Bool		(*get)(
2193            NTable		table,
2194            XrmNameList		names,
2195            XrmClassList 	classes,
2196            SClosure		closure);
2197
2198    if (names[1]) {
2199	get = SearchNEntry; /* recurse */
2200	leaf = 0;
2201    } else {
2202	get = (getNTableSProcp)AppendLEntry; /* bottom of recursion */
2203	leaf = 1;
2204    }
2205    GTIGHTLOOSE(*names, AppendLooseLEntry);   /* do name, tight and loose */
2206    GTIGHTLOOSE(*classes, AppendLooseLEntry); /* do class, tight and loose */
2207    if (table->hasany) {
2208	GTIGHTLOOSE(XrmQANY, AppendLooseLEntry); /* do ANY, tight and loose */
2209    }
2210    if (table->hasloose) {
2211	while (1) {
2212	    names++;
2213	    classes++;
2214	    if (!*names)
2215		break;
2216	    if (!names[1]) {
2217		get = (getNTableSProcp)AppendLEntry; /* bottom of recursion */
2218		leaf = 1;
2219	    }
2220	    GLOOSE(*names, AppendLooseLEntry);   /* loose names */
2221	    GLOOSE(*classes, AppendLooseLEntry); /* loose classes */
2222	    if (table->hasany) {
2223		GLOOSE(XrmQANY, AppendLooseLEntry); /* loose ANY */
2224	    }
2225	}
2226    }
2227    /* now look for matching leaf nodes */
2228    entry = table->next;
2229    if (!entry)
2230	return False;
2231    if (entry->leaf) {
2232	if (entry->tight && !table->tight)
2233	    entry = entry->next;
2234    } else {
2235	entry = entry->next;
2236	if (!entry || !entry->tight)
2237	    return False;
2238    }
2239    if (!entry || entry->name != table->name)
2240	return False;
2241    /* found one */
2242    if (entry->hasloose &&
2243	AppendLooseLEntry((LTable)entry, names, classes, closure))
2244	return True;
2245    if (entry->tight && entry == table->next && (entry = entry->next) &&
2246	entry->name == table->name && entry->hasloose)
2247	return AppendLooseLEntry((LTable)entry, names, classes, closure);
2248    return False;
2249}
2250
2251Bool XrmQGetSearchList(
2252    XrmDatabase     db,
2253    XrmNameList	    names,
2254    XrmClassList    classes,
2255    XrmSearchList   searchList,	/* RETURN */
2256    int		    listLength)
2257{
2258    register NTable	table;
2259    SClosureRec		closure;
2260
2261    if (listLength <= 0)
2262	return False;
2263    closure.list = (LTable *)searchList;
2264    closure.idx = -1;
2265    closure.limit = listLength - 2;
2266    if (db) {
2267	_XLockMutex(&db->linfo);
2268	table = db->table;
2269	if (*names) {
2270	    if (table && !table->leaf) {
2271		if (SearchNEntry(table, names, classes, &closure)) {
2272		    _XUnlockMutex(&db->linfo);
2273		    return False;
2274		}
2275	    } else if (table && table->hasloose &&
2276		       AppendLooseLEntry((LTable)table, names, classes,
2277					 &closure)) {
2278		_XUnlockMutex(&db->linfo);
2279		return False;
2280	    }
2281	} else {
2282	    if (table && !table->leaf)
2283		table = table->next;
2284	    if (table &&
2285		AppendLEntry((LTable)table, names, classes, &closure)) {
2286		_XUnlockMutex(&db->linfo);
2287		return False;
2288	    }
2289	}
2290	_XUnlockMutex(&db->linfo);
2291    }
2292    closure.list[closure.idx + 1] = (LTable)NULL;
2293    return True;
2294}
2295
2296Bool XrmQGetSearchResource(
2297	     XrmSearchList	searchList,
2298    register XrmName		name,
2299    register XrmClass		class,
2300    	     XrmRepresentation	*pType,  /* RETURN */
2301    	     XrmValue		*pValue) /* RETURN */
2302{
2303    register LTable *list;
2304    register LTable table;
2305    register VEntry entry = NULL;
2306    int flags;
2307
2308/* find tight or loose entry */
2309#define VTIGHTLOOSE(q) \
2310    entry = LeafHash(table, q); \
2311    while (entry && entry->name != q) \
2312	entry = entry->next; \
2313    if (entry) \
2314	break
2315
2316/* find loose entry */
2317#define VLOOSE(q) \
2318    entry = LeafHash(table, q); \
2319    while (entry && entry->name != q) \
2320	entry = entry->next; \
2321    if (entry) { \
2322	if (!entry->tight) \
2323	    break; \
2324	if ((entry = entry->next) && entry->name == q) \
2325	    break; \
2326    }
2327
2328    list = (LTable *)searchList;
2329    /* figure out which combination of name and class we need to search for */
2330    flags = 0;
2331    if (IsResourceQuark(name))
2332	flags = 2;
2333    if (IsResourceQuark(class))
2334	flags |= 1;
2335    if (!flags) {
2336	/* neither name nor class has ever been used to name a resource */
2337	table = (LTable)NULL;
2338    } else if (flags == 3) {
2339	/* both name and class */
2340	while ((table = *list++)) {
2341	    if (table != LOOSESEARCH) {
2342		VTIGHTLOOSE(name);  /* do name, tight and loose */
2343		VTIGHTLOOSE(class); /* do class, tight and loose */
2344	    } else {
2345		table = *list++;
2346		VLOOSE(name);  /* do name, loose only */
2347		VLOOSE(class); /* do class, loose only */
2348	    }
2349	}
2350    } else {
2351	/* just one of name or class */
2352	if (flags == 1)
2353	    name = class;
2354	while ((table = *list++)) {
2355	    if (table != LOOSESEARCH) {
2356		VTIGHTLOOSE(name); /* tight and loose */
2357	    } else {
2358		table = *list++;
2359		VLOOSE(name); /* loose only */
2360	    }
2361	}
2362    }
2363    if (table) {
2364	/* found a match */
2365	if (entry->string) {
2366	    *pType = XrmQString;
2367	    pValue->addr = StringValue(entry);
2368	} else {
2369	    *pType = RepType(entry);
2370	    pValue->addr = DataValue(entry);
2371	}
2372	pValue->size = entry->size;
2373	return True;
2374    }
2375    *pType = NULLQUARK;
2376    pValue->addr = (XPointer)NULL;
2377    pValue->size = 0;
2378    return False;
2379
2380#undef VTIGHTLOOSE
2381#undef VLOOSE
2382}
2383
2384/* look for a tight/loose value */
2385static Bool GetVEntry(
2386    LTable		table,
2387    XrmNameList		names,
2388    XrmClassList 	classes,
2389    VClosure		closure)
2390{
2391    register VEntry entry;
2392    register XrmQuark q;
2393
2394    /* try name first */
2395    q = *names;
2396    entry = LeafHash(table, q);
2397    while (entry && entry->name != q)
2398	entry = entry->next;
2399    if (!entry) {
2400	/* not found, try class */
2401	q = *classes;
2402	entry = LeafHash(table, q);
2403	while (entry && entry->name != q)
2404	    entry = entry->next;
2405	if (!entry)
2406	    return False;
2407    }
2408    if (entry->string) {
2409	*closure->type = XrmQString;
2410	closure->value->addr = StringValue(entry);
2411    } else {
2412	*closure->type = RepType(entry);
2413	closure->value->addr = DataValue(entry);
2414    }
2415    closure->value->size = entry->size;
2416    return True;
2417}
2418
2419/* look for a loose value */
2420static Bool GetLooseVEntry(
2421    LTable		table,
2422    XrmNameList		names,
2423    XrmClassList 	classes,
2424    VClosure		closure)
2425{
2426    register VEntry	entry;
2427    register XrmQuark	q;
2428
2429#define VLOOSE(ename) \
2430    q = ename; \
2431    entry = LeafHash(table, q); \
2432    while (entry && entry->name != q) \
2433	entry = entry->next; \
2434    if (entry && entry->tight && (entry = entry->next) && entry->name != q) \
2435	entry = (VEntry)NULL;
2436
2437    /* bump to last component */
2438    while (names[1]) {
2439	names++;
2440	classes++;
2441    }
2442    VLOOSE(*names);  /* do name, loose only */
2443    if (!entry) {
2444	VLOOSE(*classes); /* do class, loose only */
2445	if (!entry)
2446	    return False;
2447    }
2448    if (entry->string) {
2449	*closure->type = XrmQString;
2450	closure->value->addr = StringValue(entry);
2451    } else {
2452	*closure->type = RepType(entry);
2453	closure->value->addr = DataValue(entry);
2454    }
2455    closure->value->size = entry->size;
2456    return True;
2457
2458#undef VLOOSE
2459}
2460
2461/* recursive search for a value */
2462static Bool GetNEntry(
2463    NTable		table,
2464    XrmNameList		names,
2465    XrmClassList 	classes,
2466    VClosure		closure)
2467{
2468    register NTable	entry;
2469    register XrmQuark	q;
2470    register unsigned int leaf;
2471    Bool		(*get)(
2472            NTable              table,
2473            XrmNameList         names,
2474            XrmClassList        classes,
2475            VClosure            closure);
2476    NTable		otable;
2477
2478    if (names[2]) {
2479	get = GetNEntry; /* recurse */
2480	leaf = 0;
2481    } else {
2482	get = (getNTableVProcp)GetVEntry; /* bottom of recursion */
2483	leaf = 1;
2484    }
2485    GTIGHTLOOSE(*names, GetLooseVEntry);   /* do name, tight and loose */
2486    GTIGHTLOOSE(*classes, GetLooseVEntry); /* do class, tight and loose */
2487    if (table->hasany) {
2488	GTIGHTLOOSE(XrmQANY, GetLooseVEntry); /* do ANY, tight and loose */
2489    }
2490    if (table->hasloose) {
2491	while (1) {
2492	    names++;
2493	    classes++;
2494	    if (!names[1])
2495		break;
2496	    if (!names[2]) {
2497		get = (getNTableVProcp)GetVEntry; /* bottom of recursion */
2498		leaf = 1;
2499	    }
2500	    GLOOSE(*names, GetLooseVEntry);   /* do name, loose only */
2501	    GLOOSE(*classes, GetLooseVEntry); /* do class, loose only */
2502	    if (table->hasany) {
2503		GLOOSE(XrmQANY, GetLooseVEntry); /* do ANY, loose only */
2504	    }
2505	}
2506    }
2507    /* look for matching leaf tables */
2508    otable = table;
2509    table = table->next;
2510    if (!table)
2511	return False;
2512    if (table->leaf) {
2513	if (table->tight && !otable->tight)
2514	    table = table->next;
2515    } else {
2516	table = table->next;
2517	if (!table || !table->tight)
2518	    return False;
2519    }
2520    if (!table || table->name != otable->name)
2521	return False;
2522    /* found one */
2523    if (table->hasloose &&
2524	GetLooseVEntry((LTable)table, names, classes, closure))
2525	return True;
2526    if (table->tight && table == otable->next) {
2527	table = table->next;
2528	if (table && table->name == otable->name && table->hasloose)
2529	    return GetLooseVEntry((LTable)table, names, classes, closure);
2530    }
2531    return False;
2532}
2533
2534Bool XrmQGetResource(
2535    XrmDatabase         db,
2536    XrmNameList		names,
2537    XrmClassList 	classes,
2538    XrmRepresentation	*pType,  /* RETURN */
2539    XrmValuePtr		pValue)  /* RETURN */
2540{
2541    register NTable table;
2542    VClosureRec closure;
2543
2544    if (db && *names) {
2545	_XLockMutex(&db->linfo);
2546	closure.type = pType;
2547	closure.value = pValue;
2548	table = db->table;
2549	if (names[1]) {
2550	    if (table && !table->leaf) {
2551		if (GetNEntry(table, names, classes, &closure)) {
2552		    _XUnlockMutex(&db->linfo);
2553		    return True;
2554		}
2555	    } else if (table && table->hasloose &&
2556		    GetLooseVEntry((LTable)table, names, classes, &closure)) {
2557		_XUnlockMutex (&db->linfo);
2558		return True;
2559	    }
2560	} else {
2561	    if (table && !table->leaf)
2562		table = table->next;
2563	    if (table && GetVEntry((LTable)table, names, classes, &closure)) {
2564		_XUnlockMutex(&db->linfo);
2565		return True;
2566	    }
2567	}
2568	_XUnlockMutex(&db->linfo);
2569    }
2570    *pType = NULLQUARK;
2571    pValue->addr = (XPointer)NULL;
2572    pValue->size = 0;
2573    return False;
2574}
2575
2576Bool
2577XrmGetResource(XrmDatabase db, _Xconst char *name_str, _Xconst char *class_str,
2578	       XrmString *pType_str, XrmValuePtr pValue)
2579{
2580    XrmName		names[MAXDBDEPTH+1];
2581    XrmClass		classes[MAXDBDEPTH+1];
2582    XrmRepresentation   fromType;
2583    Bool		result;
2584
2585    XrmStringToNameList(name_str, names);
2586    XrmStringToClassList(class_str, classes);
2587    result = XrmQGetResource(db, names, classes, &fromType, pValue);
2588    (*pType_str) = XrmQuarkToString(fromType);
2589    return result;
2590}
2591
2592/* destroy all values, plus table itself */
2593static void DestroyLTable(
2594    LTable table)
2595{
2596    register int i;
2597    register VEntry *buckets;
2598    register VEntry entry, next;
2599
2600    buckets = table->buckets;
2601    for (i = table->table.mask; i >= 0; i--, buckets++) {
2602	for (next = *buckets; (entry = next); ) {
2603	    next = entry->next;
2604	    Xfree((char *)entry);
2605	}
2606    }
2607    Xfree((char *)table->buckets);
2608    Xfree((char *)table);
2609}
2610
2611/* destroy all contained tables, plus table itself */
2612static void DestroyNTable(
2613    NTable table)
2614{
2615    register int i;
2616    register NTable *buckets;
2617    register NTable entry, next;
2618
2619    buckets = NodeBuckets(table);
2620    for (i = table->mask; i >= 0; i--, buckets++) {
2621	for (next = *buckets; (entry = next); ) {
2622	    next = entry->next;
2623	    if (entry->leaf)
2624		DestroyLTable((LTable)entry);
2625	    else
2626		DestroyNTable(entry);
2627	}
2628    }
2629    Xfree((char *)table);
2630}
2631
2632const char *
2633XrmLocaleOfDatabase(
2634    XrmDatabase db)
2635{
2636    const char* retval;
2637    _XLockMutex(&db->linfo);
2638    retval = (*db->methods->lcname)(db->mbstate);
2639    _XUnlockMutex(&db->linfo);
2640    return retval;
2641}
2642
2643void XrmDestroyDatabase(
2644    XrmDatabase   db)
2645{
2646    register NTable table, next;
2647
2648    if (db) {
2649	_XLockMutex(&db->linfo);
2650	for (next = db->table; (table = next); ) {
2651	    next = table->next;
2652	    if (table->leaf)
2653		DestroyLTable((LTable)table);
2654	    else
2655		DestroyNTable(table);
2656	}
2657	_XUnlockMutex(&db->linfo);
2658	_XFreeMutex(&db->linfo);
2659	(*db->methods->destroy)(db->mbstate);
2660	Xfree((char *)db);
2661    }
2662}
2663