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