Xrm.c revision ebe525bc
11ab64890Smrg
21ab64890Smrg/***********************************************************
31ab64890SmrgCopyright 1987, 1988, 1990 by Digital Equipment Corporation, Maynard
41ab64890Smrg
51ab64890Smrg                        All Rights Reserved
61ab64890Smrg
761b2299dSmrgPermission to use, copy, modify, and distribute this software and its
861b2299dSmrgdocumentation for any purpose and without fee is hereby granted,
91ab64890Smrgprovided that the above copyright notice appear in all copies and that
1061b2299dSmrgboth that copyright notice and this permission notice appear in
111ab64890Smrgsupporting documentation, and that the name Digital not be
121ab64890Smrgused in advertising or publicity pertaining to distribution of the
1361b2299dSmrgsoftware without specific, written prior permission.
141ab64890Smrg
151ab64890SmrgDIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
161ab64890SmrgALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
171ab64890SmrgDIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
181ab64890SmrgANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
191ab64890SmrgWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
201ab64890SmrgARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
211ab64890SmrgSOFTWARE.
221ab64890Smrg
231ab64890Smrg******************************************************************/
241ab64890Smrg/*
251ab64890Smrg
261ab64890SmrgCopyright 1987, 1988, 1990, 1998  The Open Group
271ab64890Smrg
281ab64890SmrgPermission to use, copy, modify, distribute, and sell this software and its
291ab64890Smrgdocumentation for any purpose is hereby granted without fee, provided that
301ab64890Smrgthe above copyright notice appear in all copies and that both that
311ab64890Smrgcopyright notice and this permission notice appear in supporting
321ab64890Smrgdocumentation.
331ab64890Smrg
341ab64890SmrgThe above copyright notice and this permission notice shall be included
351ab64890Smrgin all copies or substantial portions of the Software.
361ab64890Smrg
371ab64890SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
381ab64890SmrgOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
391ab64890SmrgMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
401ab64890SmrgIN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
411ab64890SmrgOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
421ab64890SmrgARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
431ab64890SmrgOTHER DEALINGS IN THE SOFTWARE.
441ab64890Smrg
451ab64890SmrgExcept as contained in this notice, the name of The Open Group shall
461ab64890Smrgnot be used in advertising or otherwise to promote the sale, use or
471ab64890Smrgother dealings in this Software without prior written authorization
481ab64890Smrgfrom The Open Group.
491ab64890Smrg
501ab64890Smrg*/
511ab64890Smrg
521ab64890Smrg#ifdef HAVE_CONFIG_H
531ab64890Smrg#include <config.h>
541ab64890Smrg#endif
551ab64890Smrg#include	<stdio.h>
561ab64890Smrg#include	<ctype.h>
571ab64890Smrg#include	"Xlibint.h"
581ab64890Smrg#include	<X11/Xresource.h>
591ab64890Smrg#include	"Xlcint.h"
601ab64890Smrg#ifdef XTHREADS
611ab64890Smrg#include	"locking.h"
621ab64890Smrg#endif
631ab64890Smrg#include	<X11/Xos.h>
6457f47464Smrg#include	<sys/stat.h>
65eb411b4bSmrg#include	<limits.h>
661ab64890Smrg#include "Xresinternal.h"
671ab64890Smrg#include "Xresource.h"
681ab64890Smrg
691ab64890Smrg/*
701ab64890Smrg
711ab64890SmrgThese Xrm routines allow very fast lookup of resources in the resource
721ab64890Smrgdatabase.  Several usage patterns are exploited:
731ab64890Smrg
741ab64890Smrg(1) Widgets get a lot of resources at one time.  Rather than look up each from
751ab64890Smrgscratch, we can precompute the prioritized list of database levels once, then
761ab64890Smrgsearch for each resource starting at the beginning of the list.
771ab64890Smrg
781ab64890Smrg(2) Many database levels don't contain any leaf resource nodes.  There is no
791ab64890Smrgpoint in looking for resources on a level that doesn't contain any.  This
801ab64890Smrginformation is kept on a per-level basis.
811ab64890Smrg
821ab64890Smrg(3) Sometimes the widget instance tree is structured such that you get the same
831ab64890Smrgclass name repeated on the fully qualified widget name.  This can result in the
849c019ec5Smayasame database level occurring multiple times on the search list.  The code below
851ab64890Smrgonly checks to see if you get two identical search lists in a row, rather than
861ab64890Smrglook back through all database levels, but in practice this removes all
871ab64890Smrgduplicates I've ever observed.
881ab64890Smrg
891ab64890SmrgJoel McCormack
901ab64890Smrg
911ab64890Smrg*/
921ab64890Smrg
931ab64890Smrg/*
941ab64890Smrg
951ab64890SmrgThe Xrm representation has been completely redesigned to substantially reduce
961ab64890Smrgmemory and hopefully improve performance.
971ab64890Smrg
981ab64890SmrgThe database is structured into two kinds of tables: LTables that contain
991ab64890Smrgonly values, and NTables that contain only other tables.
1001ab64890Smrg
1011ab64890SmrgSome invariants:
1021ab64890Smrg
1031ab64890SmrgThe next pointer of the top-level node table points to the top-level leaf
1041ab64890Smrgtable, if any.
1051ab64890Smrg
1061ab64890SmrgWithin an LTable, for a given name, the tight value always precedes the
1071ab64890Smrgloose value, and if both are present the loose value is always right after
1081ab64890Smrgthe tight value.
1091ab64890Smrg
1101ab64890SmrgWithin an NTable, all of the entries for a given name are contiguous,
1111ab64890Smrgin the order tight NTable, loose NTable, tight LTable, loose LTable.
1121ab64890Smrg
1131ab64890SmrgBob Scheifler
1141ab64890Smrg
1151ab64890Smrg*/
1161ab64890Smrg
1171ab64890Smrgstatic XrmQuark XrmQString, XrmQANY;
1181ab64890Smrg
1191ab64890Smrgtypedef	Bool (*DBEnumProc)(
1201ab64890Smrg    XrmDatabase*	/* db */,
1211ab64890Smrg    XrmBindingList	/* bindings */,
1221ab64890Smrg    XrmQuarkList	/* quarks */,
1231ab64890Smrg    XrmRepresentation*	/* type */,
1241ab64890Smrg    XrmValue*		/* value */,
1251ab64890Smrg    XPointer		/* closure */
1261ab64890Smrg);
1271ab64890Smrg
1281ab64890Smrgtypedef struct _VEntry {
1291ab64890Smrg    struct _VEntry	*next;		/* next in chain */
1301ab64890Smrg    XrmQuark		name;		/* name of this entry */
1311ab64890Smrg    unsigned int	tight:1;	/* 1 if it is a tight binding */
1321ab64890Smrg    unsigned int	string:1;	/* 1 if type is String */
1331ab64890Smrg    unsigned int	size:30;	/* size of value */
1341ab64890Smrg} VEntryRec, *VEntry;
1351ab64890Smrg
1361ab64890Smrg
1371ab64890Smrgtypedef struct _DEntry {
1381ab64890Smrg    VEntryRec		entry;		/* entry */
1391ab64890Smrg    XrmRepresentation	type;		/* representation type */
1401ab64890Smrg} DEntryRec, *DEntry;
1411ab64890Smrg
1421ab64890Smrg/* the value is right after the structure */
1431ab64890Smrg#define StringValue(ve) (XPointer)((ve) + 1)
1441ab64890Smrg#define RepType(ve) ((DEntry)(ve))->type
1451ab64890Smrg/* the value is right after the structure */
1461ab64890Smrg#define DataValue(ve) (XPointer)(((DEntry)(ve)) + 1)
1471ab64890Smrg#define RawValue(ve) (char *)((ve)->string ? StringValue(ve) : DataValue(ve))
1481ab64890Smrg
1491ab64890Smrgtypedef struct _NTable {
1501ab64890Smrg    struct _NTable	*next;		/* next in chain */
1511ab64890Smrg    XrmQuark		name;		/* name of this entry */
1521ab64890Smrg    unsigned int	tight:1;	/* 1 if it is a tight binding */
1531ab64890Smrg    unsigned int	leaf:1;		/* 1 if children are values */
1541ab64890Smrg    unsigned int	hasloose:1;	/* 1 if has loose children */
1551ab64890Smrg    unsigned int	hasany:1;	/* 1 if has ANY entry */
1561ab64890Smrg    unsigned int	pad:4;		/* unused */
1571ab64890Smrg    unsigned int	mask:8;		/* hash size - 1 */
1581ab64890Smrg    unsigned int	entries:16;	/* number of children */
1591ab64890Smrg} NTableRec, *NTable;
1601ab64890Smrg
1611ab64890Smrg/* the buckets are right after the structure */
1621ab64890Smrg#define NodeBuckets(ne) ((NTable *)((ne) + 1))
1631ab64890Smrg#define NodeHash(ne,q) NodeBuckets(ne)[(q) & (ne)->mask]
1641ab64890Smrg
1651ab64890Smrg/* leaf tables have an extra level of indirection for the buckets,
1661ab64890Smrg * so that resizing can be done without invalidating a search list.
1671ab64890Smrg * This is completely ugly, and wastes some memory, but the Xlib
1681ab64890Smrg * spec doesn't really specify whether invalidation is OK, and the
1691ab64890Smrg * old implementation did not invalidate.
1701ab64890Smrg */
1711ab64890Smrgtypedef struct _LTable {
1721ab64890Smrg    NTableRec		table;
1731ab64890Smrg    VEntry		*buckets;
1741ab64890Smrg} LTableRec, *LTable;
1751ab64890Smrg
1761ab64890Smrg#define LeafHash(le,q) (le)->buckets[(q) & (le)->table.mask]
1771ab64890Smrg
1781ab64890Smrg/* An XrmDatabase just holds a pointer to the first top-level table.
1791ab64890Smrg * The type name is no longer descriptive, but better to not change
1801ab64890Smrg * the Xresource.h header file.  This type also gets used to define
1811ab64890Smrg * XrmSearchList, which is a complete crock, but we'll just leave it
1821ab64890Smrg * and caste types as required.
1831ab64890Smrg */
1841ab64890Smrgtypedef struct _XrmHashBucketRec {
1851ab64890Smrg    NTable table;
1861ab64890Smrg    XPointer mbstate;
1871ab64890Smrg    XrmMethods methods;
1881ab64890Smrg#ifdef XTHREADS
1891ab64890Smrg    LockInfoRec linfo;
1901ab64890Smrg#endif
1911ab64890Smrg} XrmHashBucketRec;
1921ab64890Smrg
1931ab64890Smrg/* closure used in get/put resource */
1941ab64890Smrgtypedef struct _VClosure {
1951ab64890Smrg    XrmRepresentation	*type;		/* type of value */
1961ab64890Smrg    XrmValuePtr		value;		/* value itself */
1971ab64890Smrg} VClosureRec, *VClosure;
1981ab64890Smrg
1991ab64890Smrg/* closure used in get search list */
2001ab64890Smrgtypedef struct _SClosure {
2011ab64890Smrg    LTable		*list;		/* search list */
2021ab64890Smrg    int			idx;		/* index of last filled element */
2031ab64890Smrg    int			limit;		/* maximum index */
2041ab64890Smrg} SClosureRec, *SClosure;
2051ab64890Smrg
2061ab64890Smrg/* placed in XrmSearchList to indicate next table is loose only */
2071ab64890Smrg#define LOOSESEARCH ((LTable)1)
2081ab64890Smrg
2091ab64890Smrg/* closure used in enumerate database */
2101ab64890Smrgtypedef struct _EClosure {
2111ab64890Smrg    XrmDatabase db;			/* the database */
2121ab64890Smrg    DBEnumProc proc;			/* the user proc */
2131ab64890Smrg    XPointer closure;			/* the user closure */
2141ab64890Smrg    XrmBindingList bindings;		/* binding list */
2151ab64890Smrg    XrmQuarkList quarks;		/* quark list */
2161ab64890Smrg    int mode;				/* XrmEnum<kind> */
2171ab64890Smrg} EClosureRec, *EClosure;
2181ab64890Smrg
2191ab64890Smrg/* types for typecasting ETable based functions to NTable based functions */
2201ab64890Smrgtypedef Bool (*getNTableSProcp)(
2211ab64890Smrg    NTable              table,
2221ab64890Smrg    XrmNameList         names,
2231ab64890Smrg    XrmClassList        classes,
2241ab64890Smrg    SClosure            closure);
2251ab64890Smrgtypedef Bool (*getNTableVProcp)(
2261ab64890Smrg    NTable              table,
2271ab64890Smrg    XrmNameList         names,
2281ab64890Smrg    XrmClassList        classes,
2291ab64890Smrg    VClosure            closure);
2301ab64890Smrgtypedef Bool (*getNTableEProcp)(
2311ab64890Smrg    NTable              table,
2321ab64890Smrg    XrmNameList         names,
2331ab64890Smrg    XrmClassList        classes,
2341ab64890Smrg    register int        level,
2351ab64890Smrg    EClosure            closure);
2361ab64890Smrg
2371ab64890Smrg/* predicate to determine when to resize a hash table */
2381ab64890Smrg#define GrowthPred(n,m) ((unsigned)(n) > (((m) + 1) << 2))
2391ab64890Smrg
2401ab64890Smrg#define GROW(prev) \
2411ab64890Smrg    if (GrowthPred((*prev)->entries, (*prev)->mask)) \
2421ab64890Smrg	GrowTable(prev)
2431ab64890Smrg
2441ab64890Smrg/* pick a reasonable value for maximum depth of resource database */
2451ab64890Smrg#define MAXDBDEPTH 100
2461ab64890Smrg
2471ab64890Smrg/* macro used in get/search functions */
2481ab64890Smrg
2491ab64890Smrg/* find an entry named ename, with leafness given by leaf */
2501ab64890Smrg#define NFIND(ename) \
2511ab64890Smrg    q = ename; \
2521ab64890Smrg    entry = NodeHash(table, q); \
2531ab64890Smrg    while (entry && entry->name != q) \
2541ab64890Smrg	entry = entry->next; \
2551ab64890Smrg    if (leaf && entry && !entry->leaf) { \
2561ab64890Smrg	entry = entry->next; \
2571ab64890Smrg	if (entry && !entry->leaf) \
2581ab64890Smrg	    entry = entry->next; \
2591ab64890Smrg	if (entry && entry->name != q) \
2601ab64890Smrg	    entry = (NTable)NULL; \
2611ab64890Smrg    }
2621ab64890Smrg
2631ab64890Smrg/* resourceQuarks keeps track of what quarks have been associated with values
2641ab64890Smrg * in all LTables.  If a quark has never been used in an LTable, we don't need
2651ab64890Smrg * to bother looking for it.
2661ab64890Smrg */
2671ab64890Smrg
2681ab64890Smrgstatic unsigned char *resourceQuarks = (unsigned char *)NULL;
2691ab64890Smrgstatic XrmQuark maxResourceQuark = -1;
2701ab64890Smrg
2711ab64890Smrg/* determines if a quark has been used for a value in any database */
2721ab64890Smrg#define IsResourceQuark(q)  ((q) > 0 && (q) <= maxResourceQuark && \
2731ab64890Smrg			     resourceQuarks[(q) >> 3] & (1 << ((q) & 7)))
2741ab64890Smrg
2751ab64890Smrgtypedef unsigned char XrmBits;
2761ab64890Smrg
2771ab64890Smrg#define BSLASH  ((XrmBits) (1 << 5))
2781ab64890Smrg#define NORMAL	((XrmBits) (1 << 4))
2791ab64890Smrg#define EOQ	((XrmBits) (1 << 3))
2801ab64890Smrg#define SEP	((XrmBits) (1 << 2))
2811ab64890Smrg#define ENDOF	((XrmBits) (1 << 1))
2821ab64890Smrg#define SPACE	(NORMAL|EOQ|SEP|(XrmBits)0)
2831ab64890Smrg#define RSEP	(NORMAL|EOQ|SEP|(XrmBits)1)
2841ab64890Smrg#define EOS	(EOQ|SEP|ENDOF|(XrmBits)0)
2851ab64890Smrg#define EOL	(EOQ|SEP|ENDOF|(XrmBits)1)
2861ab64890Smrg#define BINDING	(NORMAL|EOQ)
2871ab64890Smrg#define ODIGIT	(NORMAL|(XrmBits)1)
2881ab64890Smrg
2891ab64890Smrg#define next_char(ch,str) xrmtypes[(unsigned char)((ch) = *(++(str)))]
2901ab64890Smrg#define next_mbchar(ch,len,str) xrmtypes[(unsigned char)(ch = (*db->methods->mbchar)(db->mbstate, str, &len), str += len, ch)]
2911ab64890Smrg
2921ab64890Smrg#define is_space(bits)		((bits) == SPACE)
2931ab64890Smrg#define is_EOQ(bits)		((bits) & EOQ)
2941ab64890Smrg#define is_EOF(bits)		((bits) == EOS)
2951ab64890Smrg#define is_EOL(bits)		((bits) & ENDOF)
2961ab64890Smrg#define is_binding(bits)	((bits) == BINDING)
2971ab64890Smrg#define is_odigit(bits)		((bits) == ODIGIT)
2981ab64890Smrg#define is_separator(bits)	((bits) & SEP)
2991ab64890Smrg#define is_nonpcs(bits)		(!(bits))
3001ab64890Smrg#define is_normal(bits)		((bits) & NORMAL)
3011ab64890Smrg#define is_simple(bits)		((bits) & (NORMAL|BSLASH))
3021ab64890Smrg#define is_special(bits)	((bits) & (ENDOF|BSLASH))
3031ab64890Smrg
3041ab64890Smrg/* parsing types */
3051ab64890Smrgstatic XrmBits const xrmtypes[256] = {
3061ab64890Smrg    EOS,0,0,0,0,0,0,0,
3071ab64890Smrg    0,SPACE,EOL,0,0,
308ebe525bcSmrg#ifdef WIN32
3091ab64890Smrg                    EOL,	/* treat CR the same as LF, just in case */
3101ab64890Smrg#else
3111ab64890Smrg                    0,
3121ab64890Smrg#endif
3131ab64890Smrg                      0,0,
3141ab64890Smrg    0,0,0,0,0,0,0,0,
3151ab64890Smrg    0,0,0,0,0,0,0,0,
3161ab64890Smrg    SPACE,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,
3171ab64890Smrg    NORMAL,NORMAL,BINDING,NORMAL,NORMAL,NORMAL,BINDING,NORMAL,
3181ab64890Smrg    ODIGIT,ODIGIT,ODIGIT,ODIGIT,ODIGIT,ODIGIT,ODIGIT,ODIGIT,
3191ab64890Smrg    NORMAL,NORMAL,RSEP,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,
3201ab64890Smrg    NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,
3211ab64890Smrg    NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,
3221ab64890Smrg    NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,
3231ab64890Smrg    NORMAL,NORMAL,NORMAL,NORMAL,BSLASH,NORMAL,NORMAL,NORMAL,
3241ab64890Smrg    NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,
3251ab64890Smrg    NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,
3261ab64890Smrg    NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,
3271ab64890Smrg    NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,0
3281ab64890Smrg    /* The rest will be automatically initialized to zero. */
3291ab64890Smrg};
3301ab64890Smrg
3311ab64890Smrgvoid XrmInitialize(void)
3321ab64890Smrg{
3331ab64890Smrg    XrmQString = XrmPermStringToQuark("String");
3341ab64890Smrg    XrmQANY = XrmPermStringToQuark("?");
3351ab64890Smrg}
3361ab64890Smrg
3371ab64890SmrgXrmDatabase XrmGetDatabase(
3381ab64890Smrg    Display *display)
3391ab64890Smrg{
3401ab64890Smrg    XrmDatabase retval;
3411ab64890Smrg    LockDisplay(display);
3421ab64890Smrg    retval = display->db;
3431ab64890Smrg    UnlockDisplay(display);
3441ab64890Smrg    return retval;
3451ab64890Smrg}
3461ab64890Smrg
3471ab64890Smrgvoid XrmSetDatabase(
3481ab64890Smrg    Display *display,
3491ab64890Smrg    XrmDatabase database)
3501ab64890Smrg{
3511ab64890Smrg    LockDisplay(display);
3520f8248bfSmrg    /* destroy database if set up implicitly by XGetDefault() */
3531ab64890Smrg    if (display->db && (display->flags & XlibDisplayDfltRMDB)) {
3541ab64890Smrg	XrmDestroyDatabase(display->db);
3551ab64890Smrg	display->flags &= ~XlibDisplayDfltRMDB;
3561ab64890Smrg    }
3571ab64890Smrg    display->db = database;
3581ab64890Smrg    UnlockDisplay(display);
3591ab64890Smrg}
3601ab64890Smrg
3611ab64890Smrgvoid
3621ab64890SmrgXrmStringToQuarkList(
3631ab64890Smrg    register _Xconst char  *name,
3641ab64890Smrg    register XrmQuarkList quarks)   /* RETURN */
3651ab64890Smrg{
3661ab64890Smrg    register XrmBits		bits;
3671ab64890Smrg    register Signature  	sig = 0;
3681ab64890Smrg    register char       	ch, *tname;
3691ab64890Smrg    register int 		i = 0;
3701ab64890Smrg
3711ab64890Smrg    if ((tname = (char *)name)) {
3721ab64890Smrg	tname--;
3731ab64890Smrg	while (!is_EOF(bits = next_char(ch, tname))) {
3741ab64890Smrg	    if (is_binding (bits)) {
3751ab64890Smrg		if (i) {
3761ab64890Smrg		    /* Found a complete name */
3771ab64890Smrg		    *quarks++ = _XrmInternalStringToQuark(name,tname - name,
3781ab64890Smrg							  sig, False);
3791ab64890Smrg		    i = 0;
3801ab64890Smrg		    sig = 0;
3811ab64890Smrg		}
3821ab64890Smrg		name = tname+1;
3831ab64890Smrg	    }
3841ab64890Smrg	    else {
3851ab64890Smrg		sig = (sig << 1) + ch; /* Compute the signature. */
3861ab64890Smrg		i++;
3871ab64890Smrg	    }
3881ab64890Smrg	}
3891ab64890Smrg	*quarks++ = _XrmInternalStringToQuark(name, tname - name, sig, False);
3901ab64890Smrg    }
3911ab64890Smrg    *quarks = NULLQUARK;
3921ab64890Smrg}
3931ab64890Smrg
3941ab64890Smrgvoid
3951ab64890SmrgXrmStringToBindingQuarkList(
3961ab64890Smrg    register _Xconst char   *name,
3971ab64890Smrg    register XrmBindingList bindings,   /* RETURN */
3981ab64890Smrg    register XrmQuarkList   quarks)     /* RETURN */
3991ab64890Smrg{
4001ab64890Smrg    register XrmBits		bits;
4011ab64890Smrg    register Signature  	sig = 0;
4021ab64890Smrg    register char       	ch, *tname;
4031ab64890Smrg    register XrmBinding 	binding;
4041ab64890Smrg    register int 		i = 0;
4051ab64890Smrg
4061ab64890Smrg    if ((tname = (char *)name)) {
4071ab64890Smrg	tname--;
4081ab64890Smrg	binding = XrmBindTightly;
4091ab64890Smrg	while (!is_EOF(bits = next_char(ch, tname))) {
4101ab64890Smrg	    if (is_binding (bits)) {
4111ab64890Smrg		if (i) {
4121ab64890Smrg		    /* Found a complete name */
4131ab64890Smrg		    *bindings++ = binding;
4141ab64890Smrg		    *quarks++ = _XrmInternalStringToQuark(name, tname - name,
4151ab64890Smrg							  sig, False);
4161ab64890Smrg
4171ab64890Smrg		    i = 0;
4181ab64890Smrg		    sig = 0;
4191ab64890Smrg		    binding = XrmBindTightly;
4201ab64890Smrg		}
4211ab64890Smrg		name = tname+1;
4221ab64890Smrg
4231ab64890Smrg		if (ch == '*')
4241ab64890Smrg		    binding = XrmBindLoosely;
4251ab64890Smrg	    }
4261ab64890Smrg	    else {
4271ab64890Smrg		sig = (sig << 1) + ch; /* Compute the signature. */
4281ab64890Smrg		i++;
4291ab64890Smrg	    }
4301ab64890Smrg	}
4311ab64890Smrg	*bindings = binding;
4321ab64890Smrg	*quarks++ = _XrmInternalStringToQuark(name, tname - name, sig, False);
4331ab64890Smrg    }
4341ab64890Smrg    *quarks = NULLQUARK;
4351ab64890Smrg}
4361ab64890Smrg
4371ab64890Smrg#ifdef DEBUG
4381ab64890Smrg
4391ab64890Smrgstatic void PrintQuarkList(
4401ab64890Smrg    XrmQuarkList    quarks,
4411ab64890Smrg    FILE	    *stream)
4421ab64890Smrg{
4431ab64890Smrg    Bool	    firstNameSeen;
4441ab64890Smrg
4451ab64890Smrg    for (firstNameSeen = False; *quarks; quarks++) {
4461ab64890Smrg	if (firstNameSeen) {
4471ab64890Smrg	    (void) fprintf(stream, ".");
4481ab64890Smrg	}
4491ab64890Smrg	firstNameSeen = True;
4501ab64890Smrg	(void) fputs(XrmQuarkToString(*quarks), stream);
4511ab64890Smrg    }
4521ab64890Smrg} /* PrintQuarkList */
4531ab64890Smrg
4541ab64890Smrg#endif /* DEBUG */
4551ab64890Smrg
4561ab64890Smrg
4571ab64890Smrg/*
4581ab64890Smrg * Fallback methods for Xrm parsing.
4591ab64890Smrg * Simulate a C locale. No state needed here.
4601ab64890Smrg */
4611ab64890Smrg
4621ab64890Smrgstatic void
4631ab64890Smrgc_mbnoop(
4641ab64890Smrg    XPointer state)
4651ab64890Smrg{
4661ab64890Smrg}
4671ab64890Smrg
4681ab64890Smrgstatic char
4691ab64890Smrgc_mbchar(
4701ab64890Smrg    XPointer state,
4711ab64890Smrg    const char *str,
4721ab64890Smrg    int *lenp)
4731ab64890Smrg{
4741ab64890Smrg    *lenp = 1;
4751ab64890Smrg    return *str;
4761ab64890Smrg}
4771ab64890Smrg
4781ab64890Smrgstatic const char *
4791ab64890Smrgc_lcname(
4801ab64890Smrg    XPointer state)
4811ab64890Smrg{
4821ab64890Smrg    return "C";
4831ab64890Smrg}
4841ab64890Smrg
4851ab64890Smrgstatic const XrmMethodsRec mb_methods = {
4861ab64890Smrg    c_mbnoop,	/* mbinit */
4871ab64890Smrg    c_mbchar,	/* mbchar */
4881ab64890Smrg    c_mbnoop,	/* mbfinish */
4891ab64890Smrg    c_lcname,	/* lcname */
4901ab64890Smrg    c_mbnoop	/* destroy */
4911ab64890Smrg};
4921ab64890Smrg
4931ab64890Smrg
4941ab64890Smrgstatic XrmDatabase NewDatabase(void)
4951ab64890Smrg{
4961ab64890Smrg    register XrmDatabase db;
4971ab64890Smrg
498eb411b4bSmrg    db = Xmalloc(sizeof(XrmHashBucketRec));
4991ab64890Smrg    if (db) {
5001ab64890Smrg	_XCreateMutex(&db->linfo);
5011ab64890Smrg	db->table = (NTable)NULL;
5021ab64890Smrg	db->mbstate = (XPointer)NULL;
5031ab64890Smrg	db->methods = _XrmInitParseInfo(&db->mbstate);
5041ab64890Smrg	if (!db->methods)
5051ab64890Smrg	    db->methods = &mb_methods;
5061ab64890Smrg    }
5071ab64890Smrg    return db;
5081ab64890Smrg}
5091ab64890Smrg
5101ab64890Smrg/* move all values from ftable to ttable, and free ftable's buckets.
5110f8248bfSmrg * ttable is guaranteed empty to start with.
5121ab64890Smrg */
5131ab64890Smrgstatic void MoveValues(
5141ab64890Smrg    LTable ftable,
5151ab64890Smrg    register LTable ttable)
5161ab64890Smrg{
5171ab64890Smrg    register VEntry fentry, nfentry;
5181ab64890Smrg    register VEntry *prev;
5191ab64890Smrg    register VEntry *bucket;
5201ab64890Smrg    register VEntry tentry;
5211ab64890Smrg    register int i;
5221ab64890Smrg
5231ab64890Smrg    for (i = ftable->table.mask, bucket = ftable->buckets; i >= 0; i--) {
5241ab64890Smrg	for (fentry = *bucket++; fentry; fentry = nfentry) {
5251ab64890Smrg	    prev = &LeafHash(ttable, fentry->name);
5261ab64890Smrg	    tentry = *prev;
5271ab64890Smrg	    *prev = fentry;
5281ab64890Smrg	    /* chain on all with same name, to preserve invariant order */
5291ab64890Smrg	    while ((nfentry = fentry->next) && nfentry->name == fentry->name)
5301ab64890Smrg		fentry = nfentry;
5311ab64890Smrg	    fentry->next = tentry;
5321ab64890Smrg	}
5331ab64890Smrg    }
534818534a1Smrg    Xfree(ftable->buckets);
5351ab64890Smrg}
5361ab64890Smrg
5371ab64890Smrg/* move all tables from ftable to ttable, and free ftable.
5381ab64890Smrg * ttable is quaranteed empty to start with.
5391ab64890Smrg */
5401ab64890Smrgstatic void MoveTables(
5411ab64890Smrg    NTable ftable,
5421ab64890Smrg    register NTable ttable)
5431ab64890Smrg{
5441ab64890Smrg    register NTable fentry, nfentry;
5451ab64890Smrg    register NTable *prev;
5461ab64890Smrg    register NTable *bucket;
5471ab64890Smrg    register NTable tentry;
5481ab64890Smrg    register int i;
5491ab64890Smrg
5501ab64890Smrg    for (i = ftable->mask, bucket = NodeBuckets(ftable); i >= 0; i--) {
5511ab64890Smrg	for (fentry = *bucket++; fentry; fentry = nfentry) {
5521ab64890Smrg	    prev = &NodeHash(ttable, fentry->name);
5531ab64890Smrg	    tentry = *prev;
5541ab64890Smrg	    *prev = fentry;
5551ab64890Smrg	    /* chain on all with same name, to preserve invariant order */
5561ab64890Smrg	    while ((nfentry = fentry->next) && nfentry->name == fentry->name)
5571ab64890Smrg		fentry = nfentry;
5581ab64890Smrg	    fentry->next = tentry;
5591ab64890Smrg	}
5601ab64890Smrg    }
561818534a1Smrg    Xfree(ftable);
5621ab64890Smrg}
5631ab64890Smrg
5641ab64890Smrg/* grow the table, based on current number of entries */
5651ab64890Smrgstatic void GrowTable(
5661ab64890Smrg    NTable *prev)
5671ab64890Smrg{
5681ab64890Smrg    register NTable table;
5691ab64890Smrg    register int i;
5701ab64890Smrg
5711ab64890Smrg    table = *prev;
5721ab64890Smrg    i = table->mask;
5731ab64890Smrg    if (i == 255) /* biggest it gets */
5741ab64890Smrg	return;
5751ab64890Smrg    while (i < 255 && GrowthPred(table->entries, i))
5761ab64890Smrg	i = (i << 1) + 1;
5771ab64890Smrg    i++; /* i is now the new size */
5781ab64890Smrg    if (table->leaf) {
5791ab64890Smrg	register LTable ltable;
5801ab64890Smrg	LTableRec otable;
5811ab64890Smrg
5821ab64890Smrg	ltable = (LTable)table;
5831ab64890Smrg	/* cons up a copy to make MoveValues look symmetric */
5841ab64890Smrg	otable = *ltable;
5856cc2b21fSmrg	ltable->buckets = Xcalloc(i, sizeof(VEntry));
5861ab64890Smrg	if (!ltable->buckets) {
5871ab64890Smrg	    ltable->buckets = otable.buckets;
5881ab64890Smrg	    return;
5891ab64890Smrg	}
5901ab64890Smrg	ltable->table.mask = i - 1;
5911ab64890Smrg	MoveValues(&otable, ltable);
5921ab64890Smrg    } else {
5931ab64890Smrg	register NTable ntable;
5941ab64890Smrg
5956cc2b21fSmrg	ntable = Xcalloc(1, sizeof(NTableRec) + (i * sizeof(NTable)));
5961ab64890Smrg	if (!ntable)
5971ab64890Smrg	    return;
5981ab64890Smrg	*ntable = *table;
5991ab64890Smrg	ntable->mask = i - 1;
6001ab64890Smrg	*prev = ntable;
6011ab64890Smrg	MoveTables(table, ntable);
6021ab64890Smrg    }
6031ab64890Smrg}
6041ab64890Smrg
6051ab64890Smrg/* merge values from ftable into *pprev, destroy ftable in the process */
6061ab64890Smrgstatic void MergeValues(
6071ab64890Smrg    LTable ftable,
6081ab64890Smrg    NTable *pprev,
6091ab64890Smrg    Bool override)
6101ab64890Smrg{
6111ab64890Smrg    register VEntry fentry, tentry;
6121ab64890Smrg    register VEntry *prev;
6131ab64890Smrg    register LTable ttable;
6141ab64890Smrg    VEntry *bucket;
6151ab64890Smrg    int i;
6161ab64890Smrg    register XrmQuark q;
6171ab64890Smrg
6181ab64890Smrg    ttable = (LTable)*pprev;
6191ab64890Smrg    if (ftable->table.hasloose)
6201ab64890Smrg	ttable->table.hasloose = 1;
6211ab64890Smrg    for (i = ftable->table.mask, bucket = ftable->buckets;
6221ab64890Smrg	 i >= 0;
6231ab64890Smrg	 i--, bucket++) {
6241ab64890Smrg	for (fentry = *bucket; fentry; ) {
6251ab64890Smrg	    q = fentry->name;
6261ab64890Smrg	    prev = &LeafHash(ttable, q);
6271ab64890Smrg	    tentry = *prev;
6281ab64890Smrg	    while (tentry && tentry->name != q)
6291ab64890Smrg		tentry = *(prev = &tentry->next);
6301ab64890Smrg	    /* note: test intentionally uses fentry->name instead of q */
6311ab64890Smrg	    /* permits serendipitous inserts */
6321ab64890Smrg	    while (tentry && tentry->name == fentry->name) {
6331ab64890Smrg		/* if tentry is earlier, skip it */
6341ab64890Smrg		if (!fentry->tight && tentry->tight) {
6351ab64890Smrg		    tentry = *(prev = &tentry->next);
6361ab64890Smrg		    continue;
6371ab64890Smrg		}
6381ab64890Smrg		if (fentry->tight != tentry->tight) {
6391ab64890Smrg		    /* no match, chain in fentry */
6401ab64890Smrg		    *prev = fentry;
6411ab64890Smrg		    prev = &fentry->next;
6421ab64890Smrg		    fentry = *prev;
6431ab64890Smrg		    *prev = tentry;
6441ab64890Smrg		    ttable->table.entries++;
6451ab64890Smrg		} else if (override) {
6461ab64890Smrg		    /* match, chain in fentry, splice out and free tentry */
6471ab64890Smrg		    *prev = fentry;
6481ab64890Smrg		    prev = &fentry->next;
6491ab64890Smrg		    fentry = *prev;
6501ab64890Smrg		    *prev = tentry->next;
6511ab64890Smrg		    /* free the overridden entry */
652818534a1Smrg		    Xfree(tentry);
6531ab64890Smrg		    /* get next tentry */
6541ab64890Smrg		    tentry = *prev;
6551ab64890Smrg		} else {
6561ab64890Smrg		    /* match, discard fentry */
6571ab64890Smrg		    prev = &tentry->next;
6581ab64890Smrg		    tentry = fentry; /* use as a temp var */
6591ab64890Smrg		    fentry = fentry->next;
6601ab64890Smrg		    /* free the overpowered entry */
661818534a1Smrg		    Xfree(tentry);
6621ab64890Smrg		    /* get next tentry */
6631ab64890Smrg		    tentry = *prev;
6641ab64890Smrg		}
6651ab64890Smrg		if (!fentry)
6661ab64890Smrg		    break;
6671ab64890Smrg	    }
6681ab64890Smrg	    /* at this point, tentry cannot match any fentry named q */
6691ab64890Smrg	    /* chain in all bindings together, preserve invariant order */
6701ab64890Smrg	    while (fentry && fentry->name == q) {
6711ab64890Smrg		*prev = fentry;
6721ab64890Smrg		prev = &fentry->next;
6731ab64890Smrg		fentry = *prev;
6741ab64890Smrg		*prev = tentry;
6751ab64890Smrg		ttable->table.entries++;
6761ab64890Smrg	    }
6771ab64890Smrg	}
6781ab64890Smrg    }
679818534a1Smrg    Xfree(ftable->buckets);
680818534a1Smrg    Xfree(ftable);
6811ab64890Smrg    /* resize if necessary, now that we're all done */
6821ab64890Smrg    GROW(pprev);
6831ab64890Smrg}
6841ab64890Smrg
6851ab64890Smrg/* merge tables from ftable into *pprev, destroy ftable in the process */
6861ab64890Smrgstatic void MergeTables(
6871ab64890Smrg    NTable ftable,
6881ab64890Smrg    NTable *pprev,
6891ab64890Smrg    Bool override)
6901ab64890Smrg{
6911ab64890Smrg    register NTable fentry, tentry;
6921ab64890Smrg    NTable nfentry;
6931ab64890Smrg    register NTable *prev;
6941ab64890Smrg    register NTable ttable;
6951ab64890Smrg    NTable *bucket;
6961ab64890Smrg    int i;
6971ab64890Smrg    register XrmQuark q;
6981ab64890Smrg
6991ab64890Smrg    ttable = *pprev;
7001ab64890Smrg    if (ftable->hasloose)
7011ab64890Smrg	ttable->hasloose = 1;
7021ab64890Smrg    if (ftable->hasany)
7031ab64890Smrg	ttable->hasany = 1;
7041ab64890Smrg    for (i = ftable->mask, bucket = NodeBuckets(ftable);
7051ab64890Smrg	 i >= 0;
7061ab64890Smrg	 i--, bucket++) {
7071ab64890Smrg	for (fentry = *bucket; fentry; ) {
7081ab64890Smrg	    q = fentry->name;
7091ab64890Smrg	    prev = &NodeHash(ttable, q);
7101ab64890Smrg	    tentry = *prev;
7111ab64890Smrg	    while (tentry && tentry->name != q)
7121ab64890Smrg		tentry = *(prev = &tentry->next);
7131ab64890Smrg	    /* note: test intentionally uses fentry->name instead of q */
7141ab64890Smrg	    /* permits serendipitous inserts */
7151ab64890Smrg	    while (tentry && tentry->name == fentry->name) {
7161ab64890Smrg		/* if tentry is earlier, skip it */
7171ab64890Smrg		if ((fentry->leaf && !tentry->leaf) ||
7181ab64890Smrg		    (!fentry->tight && tentry->tight &&
7191ab64890Smrg		     (fentry->leaf || !tentry->leaf))) {
7201ab64890Smrg		    tentry = *(prev = &tentry->next);
7211ab64890Smrg		    continue;
7221ab64890Smrg		}
7231ab64890Smrg		nfentry = fentry->next;
7241ab64890Smrg		if (fentry->leaf != tentry->leaf ||
7251ab64890Smrg		    fentry->tight != tentry->tight) {
7261ab64890Smrg		    /* no match, just chain in */
7271ab64890Smrg		    *prev = fentry;
7281ab64890Smrg		    *(prev = &fentry->next) = tentry;
7291ab64890Smrg		    ttable->entries++;
7301ab64890Smrg		} else {
7311ab64890Smrg		    if (fentry->leaf)
7321ab64890Smrg			MergeValues((LTable)fentry, prev, override);
7331ab64890Smrg		    else
7341ab64890Smrg			MergeTables(fentry, prev, override);
7351ab64890Smrg		    /* bump to next tentry */
7361ab64890Smrg		    tentry = *(prev = &(*prev)->next);
7371ab64890Smrg		}
7381ab64890Smrg		/* bump to next fentry */
7391ab64890Smrg		fentry = nfentry;
7401ab64890Smrg		if (!fentry)
7411ab64890Smrg		    break;
7421ab64890Smrg	    }
7431ab64890Smrg	    /* at this point, tentry cannot match any fentry named q */
7441ab64890Smrg	    /* chain in all bindings together, preserve invariant order */
7451ab64890Smrg	    while (fentry && fentry->name == q) {
7461ab64890Smrg		*prev = fentry;
7471ab64890Smrg		prev = &fentry->next;
7481ab64890Smrg		fentry = *prev;
7491ab64890Smrg		*prev = tentry;
7501ab64890Smrg		ttable->entries++;
7511ab64890Smrg	    }
7521ab64890Smrg	}
7531ab64890Smrg    }
754818534a1Smrg    Xfree(ftable);
7551ab64890Smrg    /* resize if necessary, now that we're all done */
7561ab64890Smrg    GROW(pprev);
7571ab64890Smrg}
7581ab64890Smrg
7591ab64890Smrgvoid XrmCombineDatabase(
7601ab64890Smrg    XrmDatabase	from, XrmDatabase *into,
7611ab64890Smrg    Bool override)
7621ab64890Smrg{
7631ab64890Smrg    register NTable *prev;
7641ab64890Smrg    register NTable ftable, ttable, nftable;
7651ab64890Smrg
7661ab64890Smrg    if (!*into) {
7671ab64890Smrg	*into = from;
7681ab64890Smrg    } else if (from) {
7691ab64890Smrg	_XLockMutex(&from->linfo);
7701ab64890Smrg	_XLockMutex(&(*into)->linfo);
7711ab64890Smrg	if ((ftable = from->table)) {
7721ab64890Smrg	    prev = &(*into)->table;
7731ab64890Smrg	    ttable = *prev;
7741ab64890Smrg	    if (!ftable->leaf) {
7751ab64890Smrg		nftable = ftable->next;
7761ab64890Smrg		if (ttable && !ttable->leaf) {
7771ab64890Smrg		    /* both have node tables, merge them */
7781ab64890Smrg		    MergeTables(ftable, prev, override);
7791ab64890Smrg		    /* bump to into's leaf table, if any */
7801ab64890Smrg		    ttable = *(prev = &(*prev)->next);
7811ab64890Smrg		} else {
7821ab64890Smrg		    /* into has no node table, link from's in */
7831ab64890Smrg		    *prev = ftable;
7841ab64890Smrg		    *(prev = &ftable->next) = ttable;
7851ab64890Smrg		}
7861ab64890Smrg		/* bump to from's leaf table, if any */
7871ab64890Smrg		ftable = nftable;
7881ab64890Smrg	    } else {
7891ab64890Smrg		/* bump to into's leaf table, if any */
7901ab64890Smrg		if (ttable && !ttable->leaf)
7911ab64890Smrg		    ttable = *(prev = &ttable->next);
7921ab64890Smrg	    }
7931ab64890Smrg	    if (ftable) {
7941ab64890Smrg		/* if into has a leaf, merge, else insert */
7951ab64890Smrg		if (ttable)
7961ab64890Smrg		    MergeValues((LTable)ftable, prev, override);
7971ab64890Smrg		else
7981ab64890Smrg		    *prev = ftable;
7991ab64890Smrg	    }
8001ab64890Smrg	}
8011ab64890Smrg	(from->methods->destroy)(from->mbstate);
8021ab64890Smrg	_XUnlockMutex(&from->linfo);
8031ab64890Smrg	_XFreeMutex(&from->linfo);
804818534a1Smrg	Xfree(from);
8051ab64890Smrg	_XUnlockMutex(&(*into)->linfo);
8061ab64890Smrg    }
8071ab64890Smrg}
8081ab64890Smrg
8091ab64890Smrgvoid XrmMergeDatabases(
8101ab64890Smrg    XrmDatabase	from, XrmDatabase *into)
8111ab64890Smrg{
8121ab64890Smrg    XrmCombineDatabase(from, into, True);
8131ab64890Smrg}
8141ab64890Smrg
8151ab64890Smrg/* store a value in the database, overriding any existing entry */
8161ab64890Smrgstatic void PutEntry(
8171ab64890Smrg    XrmDatabase		db,
8181ab64890Smrg    XrmBindingList	bindings,
8191ab64890Smrg    XrmQuarkList	quarks,
8201ab64890Smrg    XrmRepresentation	type,
8211ab64890Smrg    XrmValuePtr		value)
8221ab64890Smrg{
8231ab64890Smrg    register NTable *pprev, *prev;
8241ab64890Smrg    register NTable table;
8251ab64890Smrg    register XrmQuark q;
8261ab64890Smrg    register VEntry *vprev;
8271ab64890Smrg    register VEntry entry;
8281ab64890Smrg    NTable *nprev, *firstpprev;
8291ab64890Smrg
8301ab64890Smrg#define NEWTABLE(q,i) \
831eb411b4bSmrg    table = Xmalloc(sizeof(LTableRec)); \
8321ab64890Smrg    if (!table) \
8331ab64890Smrg	return; \
8341ab64890Smrg    table->name = q; \
8351ab64890Smrg    table->hasloose = 0; \
8361ab64890Smrg    table->hasany = 0; \
8371ab64890Smrg    table->mask = 0; \
8381ab64890Smrg    table->entries = 0; \
8391ab64890Smrg    if (quarks[i]) { \
8401ab64890Smrg	table->leaf = 0; \
8411ab64890Smrg	nprev = NodeBuckets(table); \
8421ab64890Smrg    } else { \
8431ab64890Smrg	table->leaf = 1; \
844eb411b4bSmrg	if (!(nprev = Xmalloc(sizeof(VEntry *)))) {\
84557f47464Smrg	    Xfree(table); \
8461ab64890Smrg	    return; \
84757f47464Smrg        } \
8481ab64890Smrg	((LTable)table)->buckets = (VEntry *)nprev; \
8491ab64890Smrg    } \
8501ab64890Smrg    *nprev = (NTable)NULL; \
8511ab64890Smrg    table->next = *prev; \
8521ab64890Smrg    *prev = table
8531ab64890Smrg
8541ab64890Smrg    if (!db || !*quarks)
8551ab64890Smrg	return;
8561ab64890Smrg    table = *(prev = &db->table);
8571ab64890Smrg    /* if already at leaf, bump to the leaf table */
8581ab64890Smrg    if (!quarks[1] && table && !table->leaf)
8591ab64890Smrg	table = *(prev = &table->next);
8601ab64890Smrg    pprev = prev;
8611ab64890Smrg    if (!table || (quarks[1] && table->leaf)) {
8621ab64890Smrg	/* no top-level node table, create one and chain it in */
8631ab64890Smrg	NEWTABLE(NULLQUARK,1);
8641ab64890Smrg	table->tight = 1; /* arbitrary */
8651ab64890Smrg	prev = nprev;
8661ab64890Smrg    } else {
8671ab64890Smrg	/* search along until we need a value */
8681ab64890Smrg	while (quarks[1]) {
8691ab64890Smrg	    q = *quarks;
8701ab64890Smrg	    table = *(prev = &NodeHash(table, q));
8711ab64890Smrg	    while (table && table->name != q)
8721ab64890Smrg		table = *(prev = &table->next);
8731ab64890Smrg	    if (!table)
8741ab64890Smrg		break; /* not found */
8751ab64890Smrg	    if (quarks[2]) {
8761ab64890Smrg		if (table->leaf)
8771ab64890Smrg		    break; /* not found */
8781ab64890Smrg	    } else {
8791ab64890Smrg		if (!table->leaf) {
8801ab64890Smrg		    /* bump to leaf table, if any */
8811ab64890Smrg		    table = *(prev = &table->next);
8821ab64890Smrg		    if (!table || table->name != q)
8831ab64890Smrg			break; /* not found */
8841ab64890Smrg		    if (!table->leaf) {
8851ab64890Smrg			/* bump to leaf table, if any */
8861ab64890Smrg			table = *(prev = &table->next);
8871ab64890Smrg			if (!table || table->name != q)
8881ab64890Smrg			    break; /* not found */
8891ab64890Smrg		    }
8901ab64890Smrg		}
8911ab64890Smrg	    }
8921ab64890Smrg	    if (*bindings == XrmBindTightly) {
8931ab64890Smrg		if (!table->tight)
8941ab64890Smrg		    break; /* not found */
8951ab64890Smrg	    } else {
8961ab64890Smrg		if (table->tight) {
8971ab64890Smrg		    /* bump to loose table, if any */
8981ab64890Smrg		    table = *(prev = &table->next);
8991ab64890Smrg		    if (!table || table->name != q ||
9001ab64890Smrg			!quarks[2] != table->leaf)
9011ab64890Smrg			break; /* not found */
9021ab64890Smrg		}
9031ab64890Smrg	    }
9041ab64890Smrg	    /* found that one, bump to next quark */
9051ab64890Smrg	    pprev = prev;
9061ab64890Smrg	    quarks++;
9071ab64890Smrg	    bindings++;
9081ab64890Smrg	}
9091ab64890Smrg	if (!quarks[1]) {
9101ab64890Smrg	    /* found all the way to a leaf */
9111ab64890Smrg	    q = *quarks;
9121ab64890Smrg	    entry = *(vprev = &LeafHash((LTable)table, q));
9131ab64890Smrg	    while (entry && entry->name != q)
9141ab64890Smrg		entry = *(vprev = &entry->next);
9151ab64890Smrg	    /* if want loose and have tight, bump to next entry */
9161ab64890Smrg	    if (entry && *bindings == XrmBindLoosely && entry->tight)
9171ab64890Smrg		entry = *(vprev = &entry->next);
9181ab64890Smrg	    if (entry && entry->name == q &&
9191ab64890Smrg		(*bindings == XrmBindTightly) == entry->tight) {
9201ab64890Smrg		/* match, need to override */
9211ab64890Smrg		if ((type == XrmQString) == entry->string &&
9221ab64890Smrg		    entry->size == value->size) {
9231ab64890Smrg		    /* update type if not String, can be different */
9241ab64890Smrg		    if (!entry->string)
9251ab64890Smrg			RepType(entry) = type;
9261ab64890Smrg		    /* identical size, just overwrite value */
9271ab64890Smrg		    memcpy(RawValue(entry), (char *)value->addr, value->size);
9281ab64890Smrg		    return;
9291ab64890Smrg		}
9301ab64890Smrg		/* splice out and free old entry */
9311ab64890Smrg		*vprev = entry->next;
932818534a1Smrg		Xfree(entry);
9331ab64890Smrg		(*pprev)->entries--;
9341ab64890Smrg	    }
9351ab64890Smrg	    /* this is where to insert */
9361ab64890Smrg	    prev = (NTable *)vprev;
9371ab64890Smrg	}
9381ab64890Smrg    }
9391ab64890Smrg    /* keep the top table, because we may have to grow it */
9401ab64890Smrg    firstpprev = pprev;
9411ab64890Smrg    /* iterate until we get to the leaf */
9421ab64890Smrg    while (quarks[1]) {
9431ab64890Smrg	/* build a new table and chain it in */
9441ab64890Smrg	NEWTABLE(*quarks,2);
9451ab64890Smrg	if (*quarks++ == XrmQANY)
9461ab64890Smrg	    (*pprev)->hasany = 1;
9471ab64890Smrg	if (*bindings++ == XrmBindTightly) {
9481ab64890Smrg	    table->tight = 1;
9491ab64890Smrg	} else {
9501ab64890Smrg	    table->tight = 0;
9511ab64890Smrg	    (*pprev)->hasloose = 1;
9521ab64890Smrg	}
9531ab64890Smrg	(*pprev)->entries++;
9541ab64890Smrg	pprev = prev;
9551ab64890Smrg	prev = nprev;
9561ab64890Smrg    }
9571ab64890Smrg    /* now allocate the value entry */
958eb411b4bSmrg    entry = Xmalloc(((type == XrmQString) ?
959eb411b4bSmrg		     sizeof(VEntryRec) : sizeof(DEntryRec)) + value->size);
9601ab64890Smrg    if (!entry)
9611ab64890Smrg	return;
9621ab64890Smrg    entry->name = q = *quarks;
9631ab64890Smrg    if (*bindings == XrmBindTightly) {
9641ab64890Smrg	entry->tight = 1;
9651ab64890Smrg    } else {
9661ab64890Smrg	entry->tight = 0;
9671ab64890Smrg	(*pprev)->hasloose = 1;
9681ab64890Smrg    }
9691ab64890Smrg    /* chain it in, with a bit of type cast ugliness */
9701ab64890Smrg    entry->next = *((VEntry *)prev);
9711ab64890Smrg    *((VEntry *)prev) = entry;
9721ab64890Smrg    entry->size = value->size;
9731ab64890Smrg    if (type == XrmQString) {
9741ab64890Smrg	entry->string = 1;
9751ab64890Smrg    } else {
9761ab64890Smrg	entry->string = 0;
9771ab64890Smrg	RepType(entry) = type;
9781ab64890Smrg    }
9791ab64890Smrg    /* save a copy of the value */
9801ab64890Smrg    memcpy(RawValue(entry), (char *)value->addr, value->size);
9811ab64890Smrg    (*pprev)->entries++;
9821ab64890Smrg    /* this is a new leaf, need to remember it for search lists */
9831ab64890Smrg    if (q > maxResourceQuark) {
9841ab64890Smrg	unsigned oldsize = (maxResourceQuark + 1) >> 3;
9851ab64890Smrg	unsigned size = ((q | 0x7f) + 1) >> 3; /* reallocate in chunks */
9861ab64890Smrg	if (resourceQuarks) {
9871ab64890Smrg	    unsigned char *prevQuarks = resourceQuarks;
9881ab64890Smrg
989eb411b4bSmrg	    resourceQuarks = Xrealloc(resourceQuarks, size);
9901ab64890Smrg	    if (!resourceQuarks) {
9911ab64890Smrg		Xfree(prevQuarks);
9921ab64890Smrg	    }
9931ab64890Smrg	} else
994eb411b4bSmrg	    resourceQuarks = Xmalloc(size);
9951ab64890Smrg	if (resourceQuarks) {
9961ab64890Smrg	    bzero((char *)&resourceQuarks[oldsize], size - oldsize);
9971ab64890Smrg	    maxResourceQuark = (size << 3) - 1;
9981ab64890Smrg	} else {
9991ab64890Smrg	    maxResourceQuark = -1;
10001ab64890Smrg	}
10011ab64890Smrg    }
10021ab64890Smrg    if (q > 0 && resourceQuarks)
10031ab64890Smrg	resourceQuarks[q >> 3] |= 1 << (q & 0x7);
10041ab64890Smrg    GROW(firstpprev);
10051ab64890Smrg
10061ab64890Smrg#undef NEWTABLE
10071ab64890Smrg}
10081ab64890Smrg
10091ab64890Smrgvoid XrmQPutResource(
10101ab64890Smrg    XrmDatabase		*pdb,
10111ab64890Smrg    XrmBindingList      bindings,
10121ab64890Smrg    XrmQuarkList	quarks,
10131ab64890Smrg    XrmRepresentation	type,
10141ab64890Smrg    XrmValuePtr		value)
10151ab64890Smrg{
10161ab64890Smrg    if (!*pdb) *pdb = NewDatabase();
10171ab64890Smrg    _XLockMutex(&(*pdb)->linfo);
10181ab64890Smrg    PutEntry(*pdb, bindings, quarks, type, value);
10191ab64890Smrg    _XUnlockMutex(&(*pdb)->linfo);
10201ab64890Smrg}
10211ab64890Smrg
10221ab64890Smrgvoid
10231ab64890SmrgXrmPutResource(
10241ab64890Smrg    XrmDatabase     *pdb,
10251ab64890Smrg    _Xconst char    *specifier,
10261ab64890Smrg    _Xconst char    *type,
10271ab64890Smrg    XrmValuePtr	    value)
10281ab64890Smrg{
10291ab64890Smrg    XrmBinding	    bindings[MAXDBDEPTH+1];
10301ab64890Smrg    XrmQuark	    quarks[MAXDBDEPTH+1];
10311ab64890Smrg
10321ab64890Smrg    if (!*pdb) *pdb = NewDatabase();
10331ab64890Smrg    _XLockMutex(&(*pdb)->linfo);
10341ab64890Smrg    XrmStringToBindingQuarkList(specifier, bindings, quarks);
10351ab64890Smrg    PutEntry(*pdb, bindings, quarks, XrmStringToQuark(type), value);
10361ab64890Smrg    _XUnlockMutex(&(*pdb)->linfo);
10371ab64890Smrg}
10381ab64890Smrg
10391ab64890Smrgvoid
10401ab64890SmrgXrmQPutStringResource(
10411ab64890Smrg    XrmDatabase     *pdb,
10421ab64890Smrg    XrmBindingList  bindings,
10431ab64890Smrg    XrmQuarkList    quarks,
10441ab64890Smrg    _Xconst char    *str)
10451ab64890Smrg{
10461ab64890Smrg    XrmValue	value;
10471ab64890Smrg
10481ab64890Smrg    if (!*pdb) *pdb = NewDatabase();
10491ab64890Smrg    value.addr = (XPointer) str;
10509c019ec5Smaya    value.size = (unsigned) strlen(str) + 1;
10511ab64890Smrg    _XLockMutex(&(*pdb)->linfo);
10521ab64890Smrg    PutEntry(*pdb, bindings, quarks, XrmQString, &value);
10531ab64890Smrg    _XUnlockMutex(&(*pdb)->linfo);
10541ab64890Smrg}
10551ab64890Smrg
10561ab64890Smrg/*	Function Name: GetDatabase
10571ab64890Smrg *	Description: Parses a string and stores it as a database.
10581ab64890Smrg *	Arguments: db - the database.
10591ab64890Smrg *                 str - a pointer to the string containing the database.
10601ab64890Smrg *                 filename - source filename, if any.
10611ab64890Smrg *                 doall - whether to do all lines or just one
10621ab64890Smrg */
10631ab64890Smrg
10641ab64890Smrg/*
106561b2299dSmrg * This function is highly optimized to inline as much as possible.
106661b2299dSmrg * Be very careful with modifications, or simplifications, as they
10671ab64890Smrg * may adversely affect the performance.
10681ab64890Smrg *
10691ab64890Smrg * Chris Peterson, MIT X Consortium		5/17/90.
10701ab64890Smrg */
10711ab64890Smrg
107261b2299dSmrg/*
10731ab64890Smrg * Xlib spec says max 100 quarks in a lookup, will stop and return if
10741ab64890Smrg * return if any single production's lhs has more than 100 components.
10751ab64890Smrg */
10761ab64890Smrg#define QLIST_SIZE 100
10771ab64890Smrg
107861b2299dSmrg/*
10791ab64890Smrg * This should be big enough to handle things like the XKeysymDB or biggish
10801ab64890Smrg * ~/.Xdefaults or app-defaults files. Anything bigger will be allocated on
10811ab64890Smrg * the heap.
10821ab64890Smrg */
10831ab64890Smrg#define DEF_BUFF_SIZE 8192
10841ab64890Smrg
10851ab64890Smrgstatic void GetIncludeFile(
10861ab64890Smrg    XrmDatabase db,
10871ab64890Smrg    _Xconst char *base,
10881ab64890Smrg    _Xconst char *fname,
1089eb411b4bSmrg    int fnamelen,
1090eb411b4bSmrg    int depth);
10911ab64890Smrg
10921ab64890Smrgstatic void GetDatabase(
10931ab64890Smrg    XrmDatabase db,
1094eb411b4bSmrg    _Xconst char *str,
10951ab64890Smrg    _Xconst char *filename,
1096eb411b4bSmrg    Bool doall,
1097eb411b4bSmrg    int depth)
10981ab64890Smrg{
10991ab64890Smrg    char *rhs;
11001ab64890Smrg    char *lhs, lhs_s[DEF_BUFF_SIZE];
11011ab64890Smrg    XrmQuark quarks[QLIST_SIZE + 1];	/* allow for a terminal NullQuark */
11021ab64890Smrg    XrmBinding bindings[QLIST_SIZE + 1];
11031ab64890Smrg
11041ab64890Smrg    register char *ptr;
11051ab64890Smrg    register XrmBits bits = 0;
11061ab64890Smrg    register char c;
11071ab64890Smrg    register Signature sig;
11081ab64890Smrg    register char *ptr_max;
11091ab64890Smrg    register int num_quarks;
11101ab64890Smrg    register XrmBindingList t_bindings;
11111ab64890Smrg
11121ab64890Smrg    int len, alloc_chars;
11131ab64890Smrg    unsigned long str_len;
11141ab64890Smrg    XrmValue value;
11151ab64890Smrg    Bool only_pcs;
11161ab64890Smrg    Bool dolines;
11171ab64890Smrg
11181ab64890Smrg    if (!db)
11191ab64890Smrg	return;
11201ab64890Smrg
112161b2299dSmrg    /*
112261b2299dSmrg     * if strlen (str) < DEF_BUFF_SIZE allocate buffers on the stack for
11231ab64890Smrg     * speed otherwise malloc the buffer. From a buffer overflow standpoint
11241ab64890Smrg     * we can be sure that neither: a) a component on the lhs, or b) a
11251ab64890Smrg     * value on the rhs, will be longer than the overall length of str,
11261ab64890Smrg     * i.e. strlen(str).
11271ab64890Smrg     *
112861b2299dSmrg     * This should give good performance when parsing "*foo: bar" type
11291ab64890Smrg     * databases as might be passed with -xrm command line options; but
11301ab64890Smrg     * with larger databases, e.g. .Xdefaults, app-defaults, or KeysymDB
11311ab64890Smrg     * files, the size of the buffers will be overly large. One way
11321ab64890Smrg     * around this would be to double-parse each production with a resulting
11331ab64890Smrg     * performance hit. In any event we can be assured that a lhs component
11341ab64890Smrg     * name or a rhs value won't be longer than str itself.
11351ab64890Smrg     */
11361ab64890Smrg
11371ab64890Smrg    str_len = strlen (str);
11381ab64890Smrg    if (DEF_BUFF_SIZE > str_len) lhs = lhs_s;
1139eb411b4bSmrg    else if ((lhs = Xmalloc (str_len)) == NULL)
11401ab64890Smrg	return;
11411ab64890Smrg
11421ab64890Smrg    alloc_chars = DEF_BUFF_SIZE < str_len ? str_len : DEF_BUFF_SIZE;
1143eb411b4bSmrg    if ((rhs = Xmalloc (alloc_chars)) == NULL) {
11441ab64890Smrg	if (lhs != lhs_s) Xfree (lhs);
11451ab64890Smrg	return;
11461ab64890Smrg    }
11471ab64890Smrg
11481ab64890Smrg    (*db->methods->mbinit)(db->mbstate);
11491ab64890Smrg    str--;
11501ab64890Smrg    dolines = True;
11511ab64890Smrg    while (!is_EOF(bits) && dolines) {
11521ab64890Smrg	dolines = doall;
11531ab64890Smrg
11541ab64890Smrg	/*
115561b2299dSmrg	 * First: Remove extra whitespace.
11561ab64890Smrg	 */
11571ab64890Smrg
11581ab64890Smrg	do {
11591ab64890Smrg	    bits = next_char(c, str);
11601ab64890Smrg	} while is_space(bits);
11611ab64890Smrg
11621ab64890Smrg	/*
11631ab64890Smrg	 * Ignore empty lines.
11641ab64890Smrg	 */
11651ab64890Smrg
11661ab64890Smrg	if (is_EOL(bits))
11671ab64890Smrg	    continue;		/* start a new line. */
11681ab64890Smrg
11691ab64890Smrg	/*
11701ab64890Smrg	 * Second: check the first character in a line to see if it is
11711ab64890Smrg	 * "!" signifying a comment, or "#" signifying a directive.
11721ab64890Smrg	 */
11731ab64890Smrg
11741ab64890Smrg	if (c == '!') { /* Comment, spin to next newline */
11751ab64890Smrg	    while (is_simple(bits = next_char(c, str))) {}
11761ab64890Smrg	    if (is_EOL(bits))
11771ab64890Smrg		continue;
11781ab64890Smrg	    while (!is_EOL(bits = next_mbchar(c, len, str))) {}
11791ab64890Smrg	    str--;
11801ab64890Smrg	    continue;		/* start a new line. */
11811ab64890Smrg	}
11821ab64890Smrg
11831ab64890Smrg	if (c == '#') { /* Directive */
11841ab64890Smrg	    /* remove extra whitespace */
11851ab64890Smrg	    only_pcs = True;
11861ab64890Smrg	    while (is_space(bits = next_char(c, str))) {};
11871ab64890Smrg	    /* only "include" directive is currently defined */
11881ab64890Smrg	    if (!strncmp(str, "include", 7)) {
11891ab64890Smrg		str += (7-1);
11901ab64890Smrg		/* remove extra whitespace */
11911ab64890Smrg		while (is_space(bits = next_char(c, str))) {};
11921ab64890Smrg		/* must have a starting " */
11931ab64890Smrg		if (c == '"') {
11941ab64890Smrg		    _Xconst char *fname = str+1;
11951ab64890Smrg		    len = 0;
11961ab64890Smrg		    do {
11971ab64890Smrg			if (only_pcs) {
11981ab64890Smrg			    bits = next_char(c, str);
11991ab64890Smrg			    if (is_nonpcs(bits))
12001ab64890Smrg				only_pcs = False;
12011ab64890Smrg			}
12021ab64890Smrg			if (!only_pcs)
12031ab64890Smrg			    bits = next_mbchar(c, len, str);
12041ab64890Smrg		    } while (c != '"' && !is_EOL(bits));
12051ab64890Smrg		    /* must have an ending " */
12061ab64890Smrg		    if (c == '"')
1207eb411b4bSmrg			GetIncludeFile(db, filename, fname, str - len - fname,
1208eb411b4bSmrg			    depth);
12091ab64890Smrg		}
12101ab64890Smrg	    }
12111ab64890Smrg	    /* spin to next newline */
12121ab64890Smrg	    if (only_pcs) {
12131ab64890Smrg		while (is_simple(bits))
12141ab64890Smrg		    bits = next_char(c, str);
12151ab64890Smrg		if (is_EOL(bits))
12161ab64890Smrg		    continue;
12171ab64890Smrg	    }
12181ab64890Smrg	    while (!is_EOL(bits))
12191ab64890Smrg		bits = next_mbchar(c, len, str);
12201ab64890Smrg	    str--;
12211ab64890Smrg	    continue;		/* start a new line. */
12221ab64890Smrg	}
12231ab64890Smrg
12241ab64890Smrg	/*
12251ab64890Smrg	 * Third: loop through the LHS of the resource specification
12261ab64890Smrg	 * storing characters and converting this to a Quark.
12271ab64890Smrg	 */
122861b2299dSmrg
12291ab64890Smrg	num_quarks = 0;
12301ab64890Smrg	t_bindings = bindings;
12311ab64890Smrg
12321ab64890Smrg	sig = 0;
12331ab64890Smrg	ptr = lhs;
123461b2299dSmrg	*t_bindings = XrmBindTightly;
12351ab64890Smrg	for(;;) {
12361ab64890Smrg	    if (!is_binding(bits)) {
12371ab64890Smrg		while (!is_EOQ(bits)) {
12381ab64890Smrg		    *ptr++ = c;
12391ab64890Smrg		    sig = (sig << 1) + c; /* Compute the signature. */
12401ab64890Smrg		    bits = next_char(c, str);
12411ab64890Smrg		}
12421ab64890Smrg
124361b2299dSmrg		quarks[num_quarks++] =
12441ab64890Smrg			_XrmInternalStringToQuark(lhs, ptr - lhs, sig, False);
12451ab64890Smrg
12461ab64890Smrg		if (num_quarks > QLIST_SIZE) {
12471ab64890Smrg		    Xfree(rhs);
12481ab64890Smrg		    if (lhs != lhs_s) Xfree (lhs);
12491ab64890Smrg		    (*db->methods->mbfinish)(db->mbstate);
12501ab64890Smrg		    return;
12511ab64890Smrg		}
12521ab64890Smrg
12531ab64890Smrg		if (is_separator(bits))  {
12541ab64890Smrg		    if (!is_space(bits))
12551ab64890Smrg			break;
12561ab64890Smrg
12571ab64890Smrg		    /* Remove white space */
12581ab64890Smrg		    do {
12591ab64890Smrg			*ptr++ = c;
12601ab64890Smrg			sig = (sig << 1) + c; /* Compute the signature. */
12611ab64890Smrg		    } while (is_space(bits = next_char(c, str)));
12621ab64890Smrg
126361b2299dSmrg		    /*
12641ab64890Smrg		     * The spec doesn't permit it, but support spaces
126561b2299dSmrg		     * internal to resource name/class
12661ab64890Smrg		     */
12671ab64890Smrg
12681ab64890Smrg		    if (is_separator(bits))
12691ab64890Smrg			break;
12701ab64890Smrg		    num_quarks--;
12711ab64890Smrg		    continue;
12721ab64890Smrg		}
12731ab64890Smrg
12741ab64890Smrg		if (c == '.')
12751ab64890Smrg		    *(++t_bindings) = XrmBindTightly;
12761ab64890Smrg		else
12771ab64890Smrg		    *(++t_bindings) = XrmBindLoosely;
12781ab64890Smrg
12791ab64890Smrg		sig = 0;
12801ab64890Smrg		ptr = lhs;
12811ab64890Smrg	    }
12821ab64890Smrg	    else {
12831ab64890Smrg		/*
12841ab64890Smrg		 * Magic unspecified feature #254.
12851ab64890Smrg		 *
12861ab64890Smrg		 * If two separators appear with no Text between them then
12871ab64890Smrg		 * ignore them.
12881ab64890Smrg		 *
128961b2299dSmrg		 * If anyone of those separators is a '*' then the binding
12901ab64890Smrg		 * will be loose, otherwise it will be tight.
12911ab64890Smrg		 */
12921ab64890Smrg
12931ab64890Smrg		if (c == '*')
12941ab64890Smrg		    *t_bindings = XrmBindLoosely;
12951ab64890Smrg	    }
12961ab64890Smrg
12971ab64890Smrg	    bits = next_char(c, str);
12981ab64890Smrg	}
12991ab64890Smrg
13001ab64890Smrg	quarks[num_quarks] = NULLQUARK;
13011ab64890Smrg
13021ab64890Smrg	/*
13031ab64890Smrg	 * Make sure that there is a ':' in this line.
13041ab64890Smrg	 */
13051ab64890Smrg
13061ab64890Smrg	if (c != ':') {
13071ab64890Smrg	    char oldc;
13081ab64890Smrg
13091ab64890Smrg	    /*
13109c019ec5Smaya	     * A parsing error has occurred, toss everything on the line
13111ab64890Smrg	     * a new_line can still be escaped with a '\'.
13121ab64890Smrg	     */
13131ab64890Smrg
13141ab64890Smrg	    while (is_normal(bits))
13151ab64890Smrg		bits = next_char(c, str);
13161ab64890Smrg	    if (is_EOL(bits))
13171ab64890Smrg		continue;
13181ab64890Smrg	    bits = next_mbchar(c, len, str);
13191ab64890Smrg	    do {
13201ab64890Smrg		oldc = c;
13211ab64890Smrg		bits = next_mbchar(c, len, str);
13221ab64890Smrg	    } while (c && (c != '\n' || oldc == '\\'));
13231ab64890Smrg	    str--;
13241ab64890Smrg	    continue;
13251ab64890Smrg	}
13261ab64890Smrg
13271ab64890Smrg	/*
13281ab64890Smrg	 * I now have a quark and binding list for the entire left hand
13291ab64890Smrg	 * side.  "c" currently points to the ":" separating the left hand
13301ab64890Smrg	 * side for the right hand side.  It is time to begin processing
13311ab64890Smrg	 * the right hand side.
13321ab64890Smrg	 */
13331ab64890Smrg
133461b2299dSmrg	/*
13351ab64890Smrg	 * Fourth: Remove more whitespace
13361ab64890Smrg	 */
13371ab64890Smrg
13381ab64890Smrg	for(;;) {
13391ab64890Smrg	    if (is_space(bits = next_char(c, str)))
13401ab64890Smrg		continue;
13411ab64890Smrg	    if (c != '\\')
13421ab64890Smrg		break;
13431ab64890Smrg	    bits = next_char(c, str);
13441ab64890Smrg	    if (c == '\n')
13451ab64890Smrg		continue;
13461ab64890Smrg	    str--;
13471ab64890Smrg	    bits = BSLASH;
13481ab64890Smrg	    c = '\\';
13491ab64890Smrg	    break;
13501ab64890Smrg	}
13511ab64890Smrg
135261b2299dSmrg	/*
13531ab64890Smrg	 * Fifth: Process the right hand side.
13541ab64890Smrg	 */
13551ab64890Smrg
13561ab64890Smrg	ptr = rhs;
13571ab64890Smrg	ptr_max = ptr + alloc_chars - 4;
13581ab64890Smrg	only_pcs = True;
13591ab64890Smrg	len = 1;
13601ab64890Smrg
13611ab64890Smrg	for(;;) {
13621ab64890Smrg
13631ab64890Smrg	    /*
13641ab64890Smrg	     * Tight loop for the normal case:  Non backslash, non-end of value
13651ab64890Smrg	     * character that will fit into the allocated buffer.
13661ab64890Smrg	     */
13671ab64890Smrg
13681ab64890Smrg	    if (only_pcs) {
13691ab64890Smrg		while (is_normal(bits) && ptr < ptr_max) {
13701ab64890Smrg		    *ptr++ = c;
13711ab64890Smrg		    bits = next_char(c, str);
13721ab64890Smrg		}
13731ab64890Smrg		if (is_EOL(bits))
13741ab64890Smrg		    break;
13751ab64890Smrg		if (is_nonpcs(bits)) {
13761ab64890Smrg		    only_pcs = False;
13771ab64890Smrg		    bits = next_mbchar(c, len, str);
13781ab64890Smrg		}
13791ab64890Smrg	    }
13801ab64890Smrg	    while (!is_special(bits) && ptr + len <= ptr_max) {
13811ab64890Smrg		len = -len;
13821ab64890Smrg		while (len)
13831ab64890Smrg		    *ptr++ = str[len++];
13841ab64890Smrg		if (*str == '\0') {
13851ab64890Smrg		    bits = EOS;
13861ab64890Smrg		    break;
13871ab64890Smrg		}
13881ab64890Smrg		bits = next_mbchar(c, len, str);
13891ab64890Smrg	    }
13901ab64890Smrg
13911ab64890Smrg	    if (is_EOL(bits)) {
13921ab64890Smrg		str--;
13931ab64890Smrg		break;
13941ab64890Smrg	    }
13951ab64890Smrg
13961ab64890Smrg	    if (c == '\\') {
13971ab64890Smrg		/*
13981ab64890Smrg		 * We need to do some magic after a backslash.
13991ab64890Smrg		 */
14001ab64890Smrg		Bool read_next = True;
14011ab64890Smrg
14021ab64890Smrg		if (only_pcs) {
14031ab64890Smrg		    bits = next_char(c, str);
14041ab64890Smrg		    if (is_nonpcs(bits))
14051ab64890Smrg			only_pcs = False;
14061ab64890Smrg		}
14071ab64890Smrg		if (!only_pcs)
14081ab64890Smrg		    bits = next_mbchar(c, len, str);
14091ab64890Smrg
14101ab64890Smrg		if (is_EOL(bits)) {
14111ab64890Smrg		    if (is_EOF(bits))
14121ab64890Smrg			continue;
14131ab64890Smrg		} else if (c == 'n') {
14141ab64890Smrg		    /*
14151ab64890Smrg		     * "\n" means insert a newline.
14161ab64890Smrg		     */
14171ab64890Smrg		    *ptr++ = '\n';
14181ab64890Smrg		} else if (c == '\\') {
14191ab64890Smrg		    /*
14201ab64890Smrg		     * "\\" completes to just one backslash.
14211ab64890Smrg		     */
14221ab64890Smrg		    *ptr++ = '\\';
14231ab64890Smrg		} else {
14241ab64890Smrg		    /*
14251ab64890Smrg		     * pick up to three octal digits after the '\'.
14261ab64890Smrg		     */
14271ab64890Smrg		    char temp[3];
14281ab64890Smrg		    int count = 0;
14291ab64890Smrg		    while (is_odigit(bits) && count < 3) {
14301ab64890Smrg			temp[count++] = c;
14311ab64890Smrg			if (only_pcs) {
14321ab64890Smrg			    bits = next_char(c, str);
14331ab64890Smrg			    if (is_nonpcs(bits))
14341ab64890Smrg				only_pcs = False;
14351ab64890Smrg			}
14361ab64890Smrg			if (!only_pcs)
14371ab64890Smrg			    bits = next_mbchar(c, len, str);
14381ab64890Smrg		    }
14391ab64890Smrg
14401ab64890Smrg		    /*
14411ab64890Smrg		     * If we found three digits then insert that octal code
14421ab64890Smrg		     * into the value string as a character.
14431ab64890Smrg		     */
14441ab64890Smrg
14451ab64890Smrg		    if (count == 3) {
14461ab64890Smrg			*ptr++ = (unsigned char) ((temp[0] - '0') * 0100 +
14471ab64890Smrg						  (temp[1] - '0') * 010 +
14481ab64890Smrg						  (temp[2] - '0'));
14491ab64890Smrg		    }
14501ab64890Smrg		    else {
14511ab64890Smrg			int tcount;
14521ab64890Smrg
145361b2299dSmrg			/*
145461b2299dSmrg			 * Otherwise just insert those characters into the
14551ab64890Smrg			 * string, since no special processing is needed on
14561ab64890Smrg			 * numerics we can skip the special processing.
14571ab64890Smrg			 */
14581ab64890Smrg
14591ab64890Smrg			for (tcount = 0; tcount < count; tcount++) {
14601ab64890Smrg			    *ptr++ = temp[tcount]; /* print them in
14611ab64890Smrg						      the correct order */
14621ab64890Smrg			}
14631ab64890Smrg		    }
14641ab64890Smrg		    read_next = False;
14651ab64890Smrg		}
14661ab64890Smrg		if (read_next) {
14671ab64890Smrg		    if (only_pcs) {
14681ab64890Smrg			bits = next_char(c, str);
14691ab64890Smrg			if (is_nonpcs(bits))
14701ab64890Smrg			    only_pcs = False;
14711ab64890Smrg		    }
14721ab64890Smrg		    if (!only_pcs)
14731ab64890Smrg			bits = next_mbchar(c, len, str);
14741ab64890Smrg		}
14751ab64890Smrg	    }
14761ab64890Smrg
147761b2299dSmrg	    /*
14781ab64890Smrg	     * It is important to make sure that there is room for at least
14791ab64890Smrg	     * four more characters in the buffer, since I can add that
14809c019ec5Smaya	     * many characters into the buffer after a backslash has occurred.
14811ab64890Smrg	     */
14821ab64890Smrg
14831ab64890Smrg	    if (ptr + len > ptr_max) {
14841ab64890Smrg		char * temp_str;
14851ab64890Smrg
148661b2299dSmrg		alloc_chars += BUFSIZ/10;
14871ab64890Smrg		temp_str = Xrealloc(rhs, sizeof(char) * alloc_chars);
14881ab64890Smrg
14891ab64890Smrg		if (!temp_str) {
14901ab64890Smrg		    Xfree(rhs);
14911ab64890Smrg		    if (lhs != lhs_s) Xfree (lhs);
14921ab64890Smrg		    (*db->methods->mbfinish)(db->mbstate);
14931ab64890Smrg		    return;
14941ab64890Smrg		}
14951ab64890Smrg
14961ab64890Smrg		ptr = temp_str + (ptr - rhs); /* reset pointer. */
14971ab64890Smrg		rhs = temp_str;
14981ab64890Smrg		ptr_max = rhs + alloc_chars - 4;
14991ab64890Smrg	    }
15001ab64890Smrg	}
15011ab64890Smrg
15021ab64890Smrg	/*
150361b2299dSmrg	 * Lastly: Terminate the value string, and store this entry
15041ab64890Smrg	 * 	   into the database.
15051ab64890Smrg	 */
15061ab64890Smrg
15071ab64890Smrg	*ptr++ = '\0';
15081ab64890Smrg
15091ab64890Smrg	/* Store it in database */
15101ab64890Smrg	value.size = ptr - rhs;
15111ab64890Smrg	value.addr = (XPointer) rhs;
151261b2299dSmrg
15131ab64890Smrg	PutEntry(db, bindings, quarks, XrmQString, &value);
15141ab64890Smrg    }
15151ab64890Smrg
15161ab64890Smrg    if (lhs != lhs_s) Xfree (lhs);
15171ab64890Smrg    Xfree (rhs);
15181ab64890Smrg
15191ab64890Smrg    (*db->methods->mbfinish)(db->mbstate);
15201ab64890Smrg}
15211ab64890Smrg
15221ab64890Smrgvoid
15231ab64890SmrgXrmPutStringResource(
15241ab64890Smrg    XrmDatabase *pdb,
15251ab64890Smrg    _Xconst char*specifier,
15261ab64890Smrg    _Xconst char*str)
15271ab64890Smrg{
15281ab64890Smrg    XrmValue	value;
15291ab64890Smrg    XrmBinding	bindings[MAXDBDEPTH+1];
15301ab64890Smrg    XrmQuark	quarks[MAXDBDEPTH+1];
15311ab64890Smrg
15321ab64890Smrg    if (!*pdb) *pdb = NewDatabase();
15331ab64890Smrg    XrmStringToBindingQuarkList(specifier, bindings, quarks);
15341ab64890Smrg    value.addr = (XPointer) str;
15359c019ec5Smaya    value.size = (unsigned) strlen(str)+1;
15361ab64890Smrg    _XLockMutex(&(*pdb)->linfo);
15371ab64890Smrg    PutEntry(*pdb, bindings, quarks, XrmQString, &value);
15381ab64890Smrg    _XUnlockMutex(&(*pdb)->linfo);
15391ab64890Smrg}
15401ab64890Smrg
15411ab64890Smrg
15421ab64890Smrgvoid
15431ab64890SmrgXrmPutLineResource(
15441ab64890Smrg    XrmDatabase *pdb,
15451ab64890Smrg    _Xconst char*line)
15461ab64890Smrg{
15471ab64890Smrg    if (!*pdb) *pdb = NewDatabase();
15481ab64890Smrg    _XLockMutex(&(*pdb)->linfo);
1549eb411b4bSmrg    GetDatabase(*pdb, line, (char *)NULL, False, 0);
15501ab64890Smrg    _XUnlockMutex(&(*pdb)->linfo);
15511ab64890Smrg}
15521ab64890Smrg
15531ab64890SmrgXrmDatabase
15541ab64890SmrgXrmGetStringDatabase(
15551ab64890Smrg    _Xconst char    *data)
15561ab64890Smrg{
15571ab64890Smrg    XrmDatabase     db;
15581ab64890Smrg
15591ab64890Smrg    db = NewDatabase();
15601ab64890Smrg    _XLockMutex(&db->linfo);
1561eb411b4bSmrg    GetDatabase(db, data, (char *)NULL, True, 0);
15621ab64890Smrg    _XUnlockMutex(&db->linfo);
15631ab64890Smrg    return db;
15641ab64890Smrg}
15651ab64890Smrg
15661ab64890Smrg/*	Function Name: ReadInFile
15671ab64890Smrg *	Description: Reads the file into a buffer.
15681ab64890Smrg *	Arguments: filename - the name of the file.
15691ab64890Smrg *	Returns: An allocated string containing the contents of the file.
15701ab64890Smrg */
15711ab64890Smrg
15721ab64890Smrgstatic char *
15731ab64890SmrgReadInFile(_Xconst char *filename)
15741ab64890Smrg{
15751ab64890Smrg    register int fd, size;
15761ab64890Smrg    char * filebuf;
15771ab64890Smrg
15781ab64890Smrg
15791ab64890Smrg    /*
15801ab64890Smrg     * MS-Windows and OS/2 note: Default open mode includes O_TEXT
15811ab64890Smrg     */
15821ab64890Smrg    if ( (fd = _XOpenFile (filename, O_RDONLY)) == -1 )
15831ab64890Smrg	return (char *)NULL;
15841ab64890Smrg
15851ab64890Smrg    /*
15861ab64890Smrg     * MS-Windows and OS/2 note: depending on how the sources are
15871ab64890Smrg     * untarred, the newlines in resource files may or may not have
15881ab64890Smrg     * been expanded to CRLF. Either way the size returned by fstat
15891ab64890Smrg     * is sufficient to read the file into because in text-mode any
15901ab64890Smrg     * CRLFs in a file will be converted to newlines (LF) with the
15911ab64890Smrg     * result that the number of bytes actually read with be <=
15921ab64890Smrg     * to the size returned by fstat.
15931ab64890Smrg     */
159457f47464Smrg    {
159557f47464Smrg	struct stat status_buffer;
1596eb411b4bSmrg	if ( ((fstat(fd, &status_buffer)) == -1 ) ||
1597eb411b4bSmrg             (status_buffer.st_size >= INT_MAX) ) {
159857f47464Smrg	    close (fd);
159957f47464Smrg	    return (char *)NULL;
160057f47464Smrg	} else
1601eb411b4bSmrg	    size = (int) status_buffer.st_size;
160257f47464Smrg    }
16031ab64890Smrg
16041ab64890Smrg    if (!(filebuf = Xmalloc(size + 1))) { /* leave room for '\0' */
16051ab64890Smrg	close(fd);
16061ab64890Smrg	return (char *)NULL;
16071ab64890Smrg    }
16081ab64890Smrg    size = read (fd, filebuf, size);
16091ab64890Smrg    if (size < 0) {
16101ab64890Smrg	close (fd);
16111ab64890Smrg	Xfree(filebuf);
16121ab64890Smrg	return (char *)NULL;
16131ab64890Smrg    }
16141ab64890Smrg    close (fd);
16151ab64890Smrg
16161ab64890Smrg    filebuf[size] = '\0';	/* NULL terminate it. */
16171ab64890Smrg    return filebuf;
16181ab64890Smrg}
16191ab64890Smrg
16201ab64890Smrgstatic void
16211ab64890SmrgGetIncludeFile(
16221ab64890Smrg    XrmDatabase db,
16231ab64890Smrg    _Xconst char *base,
16241ab64890Smrg    _Xconst char *fname,
1625eb411b4bSmrg    int fnamelen,
1626eb411b4bSmrg    int depth)
16271ab64890Smrg{
16281ab64890Smrg    int len;
16291ab64890Smrg    char *str;
16301ab64890Smrg    char realfname[BUFSIZ];
16311ab64890Smrg
16321ab64890Smrg    if (fnamelen <= 0 || fnamelen >= BUFSIZ)
16331ab64890Smrg	return;
1634eb411b4bSmrg    if (depth >= MAXDBDEPTH)
1635eb411b4bSmrg	return;
16361ab64890Smrg    if (*fname != '/' && base && (str = strrchr(base, '/'))) {
16371ab64890Smrg	len = str - base + 1;
16381ab64890Smrg	if (len + fnamelen >= BUFSIZ)
16391ab64890Smrg	    return;
16409c019ec5Smaya	strncpy(realfname, base, (size_t) len);
16419c019ec5Smaya	strncpy(realfname + len, fname, (size_t) fnamelen);
16421ab64890Smrg	realfname[len + fnamelen] = '\0';
16431ab64890Smrg    } else {
16449c019ec5Smaya	strncpy(realfname, fname, (size_t) fnamelen);
16451ab64890Smrg	realfname[fnamelen] = '\0';
16461ab64890Smrg    }
16471ab64890Smrg    if (!(str = ReadInFile(realfname)))
16481ab64890Smrg	return;
1649eb411b4bSmrg    GetDatabase(db, str, realfname, True, depth + 1);
16501ab64890Smrg    Xfree(str);
16511ab64890Smrg}
16521ab64890Smrg
16531ab64890SmrgXrmDatabase
16541ab64890SmrgXrmGetFileDatabase(
16551ab64890Smrg    _Xconst char    *filename)
16561ab64890Smrg{
16571ab64890Smrg    XrmDatabase db;
16581ab64890Smrg    char *str;
16591ab64890Smrg
16601ab64890Smrg    if (!(str = ReadInFile(filename)))
16611ab64890Smrg	return (XrmDatabase)NULL;
16621ab64890Smrg
16631ab64890Smrg    db = NewDatabase();
16641ab64890Smrg    _XLockMutex(&db->linfo);
1665eb411b4bSmrg    GetDatabase(db, str, filename, True, 0);
16661ab64890Smrg    _XUnlockMutex(&db->linfo);
16671ab64890Smrg    Xfree(str);
16681ab64890Smrg    return db;
16691ab64890Smrg}
16701ab64890Smrg
16711ab64890SmrgStatus
16721ab64890SmrgXrmCombineFileDatabase(
16731ab64890Smrg    _Xconst char    *filename,
16741ab64890Smrg    XrmDatabase     *target,
16751ab64890Smrg    Bool             override)
16761ab64890Smrg{
16771ab64890Smrg    XrmDatabase db;
16781ab64890Smrg    char *str;
16791ab64890Smrg
16801ab64890Smrg    if (!(str = ReadInFile(filename)))
16811ab64890Smrg	return 0;
16821ab64890Smrg    if (override) {
16831ab64890Smrg	db = *target;
16841ab64890Smrg	if (!db)
16851ab64890Smrg	    *target = db = NewDatabase();
16861ab64890Smrg    } else
16871ab64890Smrg	db = NewDatabase();
16881ab64890Smrg    _XLockMutex(&db->linfo);
1689eb411b4bSmrg    GetDatabase(db, str, filename, True, 0);
16901ab64890Smrg    _XUnlockMutex(&db->linfo);
16911ab64890Smrg    Xfree(str);
16921ab64890Smrg    if (!override)
16931ab64890Smrg	XrmCombineDatabase(db, target, False);
16941ab64890Smrg    return 1;
16951ab64890Smrg}
16961ab64890Smrg
16971ab64890Smrg/* call the user proc for every value in the table, arbitrary order.
16981ab64890Smrg * stop if user proc returns True.  level is current depth in database.
16991ab64890Smrg */
17001ab64890Smrg/*ARGSUSED*/
17011ab64890Smrgstatic Bool EnumLTable(
17021ab64890Smrg    LTable		table,
17031ab64890Smrg    XrmNameList		names,
17041ab64890Smrg    XrmClassList 	classes,
17051ab64890Smrg    register int	level,
17061ab64890Smrg    register EClosure	closure)
17071ab64890Smrg{
17081ab64890Smrg    register VEntry *bucket;
17091ab64890Smrg    register int i;
17101ab64890Smrg    register VEntry entry;
17111ab64890Smrg    XrmValue value;
17121ab64890Smrg    XrmRepresentation type;
17131ab64890Smrg    Bool tightOk;
17141ab64890Smrg
17151ab64890Smrg    closure->bindings[level] = (table->table.tight ?
17161ab64890Smrg				XrmBindTightly : XrmBindLoosely);
17171ab64890Smrg    closure->quarks[level] = table->table.name;
17181ab64890Smrg    level++;
17191ab64890Smrg    tightOk = !*names;
17201ab64890Smrg    closure->quarks[level + 1] = NULLQUARK;
17211ab64890Smrg    for (i = table->table.mask, bucket = table->buckets;
17221ab64890Smrg	 i >= 0;
17231ab64890Smrg	 i--, bucket++) {
17241ab64890Smrg	for (entry = *bucket; entry; entry = entry->next) {
17251ab64890Smrg	    if (entry->tight && !tightOk)
17261ab64890Smrg		continue;
17271ab64890Smrg	    closure->bindings[level] = (entry->tight ?
17281ab64890Smrg					XrmBindTightly : XrmBindLoosely);
17291ab64890Smrg	    closure->quarks[level] = entry->name;
17301ab64890Smrg	    value.size = entry->size;
17311ab64890Smrg	    if (entry->string) {
17321ab64890Smrg		type = XrmQString;
17331ab64890Smrg		value.addr = StringValue(entry);
17341ab64890Smrg	    } else {
17351ab64890Smrg		type = RepType(entry);
17361ab64890Smrg		value.addr = DataValue(entry);
17371ab64890Smrg	    }
17381ab64890Smrg	    if ((*closure->proc)(&closure->db, closure->bindings+1,
17391ab64890Smrg				 closure->quarks+1, &type, &value,
17401ab64890Smrg				 closure->closure))
17411ab64890Smrg		return True;
17421ab64890Smrg	}
17431ab64890Smrg    }
17441ab64890Smrg    return False;
17451ab64890Smrg}
17461ab64890Smrg
17471ab64890Smrgstatic Bool EnumAllNTable(
17481ab64890Smrg    NTable		table,
17491ab64890Smrg    register int	level,
17501ab64890Smrg    register EClosure	closure)
17511ab64890Smrg{
17521ab64890Smrg    register NTable *bucket;
17531ab64890Smrg    register int i;
17541ab64890Smrg    register NTable entry;
17551ab64890Smrg    XrmQuark empty = NULLQUARK;
17561ab64890Smrg
17571ab64890Smrg    if (level >= MAXDBDEPTH)
17581ab64890Smrg	return False;
17591ab64890Smrg    for (i = table->mask, bucket = NodeBuckets(table);
17601ab64890Smrg	 i >= 0;
17611ab64890Smrg	 i--, bucket++) {
17621ab64890Smrg	for (entry = *bucket; entry; entry = entry->next) {
17631ab64890Smrg	    if (entry->leaf) {
17641ab64890Smrg		if (EnumLTable((LTable)entry, &empty, &empty, level, closure))
17651ab64890Smrg		    return True;
17661ab64890Smrg	    } else {
17671ab64890Smrg		closure->bindings[level] = (entry->tight ?
17681ab64890Smrg					    XrmBindTightly : XrmBindLoosely);
17691ab64890Smrg		closure->quarks[level] = entry->name;
17701ab64890Smrg		if (EnumAllNTable(entry, level+1, closure))
17711ab64890Smrg		    return True;
17721ab64890Smrg	    }
17731ab64890Smrg	}
17741ab64890Smrg    }
17751ab64890Smrg    return False;
17761ab64890Smrg}
17771ab64890Smrg
17781ab64890Smrg/* recurse on every table in the table, arbitrary order.
17791ab64890Smrg * stop if user proc returns True.  level is current depth in database.
17801ab64890Smrg */
17811ab64890Smrgstatic Bool EnumNTable(
17821ab64890Smrg    NTable		table,
17831ab64890Smrg    XrmNameList		names,
17841ab64890Smrg    XrmClassList 	classes,
17851ab64890Smrg    register int	level,
17861ab64890Smrg    register EClosure	closure)
17871ab64890Smrg{
17881ab64890Smrg    register NTable	entry;
17891ab64890Smrg    register XrmQuark	q;
17901ab64890Smrg    register unsigned int leaf;
17911ab64890Smrg    Bool (*get)(
17921ab64890Smrg            NTable		table,
17931ab64890Smrg            XrmNameList		names,
17941ab64890Smrg            XrmClassList 	classes,
17951ab64890Smrg            register int	level,
17961ab64890Smrg            EClosure		closure);
17971ab64890Smrg    Bool bilevel;
17981ab64890Smrg
17991ab64890Smrg/* find entries named ename, leafness leaf, tight or loose, and call get */
18001ab64890Smrg#define ITIGHTLOOSE(ename) \
18011ab64890Smrg    NFIND(ename); \
18021ab64890Smrg    if (entry) { \
18031ab64890Smrg	if (leaf == entry->leaf) { \
18041ab64890Smrg	    if (!leaf && !entry->tight && entry->next && \
18051ab64890Smrg		entry->next->name == q && entry->next->tight && \
18061ab64890Smrg		(bilevel || entry->next->hasloose) && \
18071ab64890Smrg		EnumLTable((LTable)entry->next, names+1, classes+1, \
18081ab64890Smrg			   level, closure)) \
18091ab64890Smrg		return True; \
18101ab64890Smrg	    if ((*get)(entry, names+1, classes+1, level, closure)) \
18111ab64890Smrg		return True; \
18121ab64890Smrg	    if (entry->tight && (entry = entry->next) && \
18131ab64890Smrg		entry->name == q && leaf == entry->leaf && \
18141ab64890Smrg		(*get)(entry, names+1, classes+1, level, closure)) \
18151ab64890Smrg		return True; \
18161ab64890Smrg	} else if (entry->leaf) { \
18171ab64890Smrg	    if ((bilevel || entry->hasloose) && \
18181ab64890Smrg		EnumLTable((LTable)entry, names+1, classes+1, level, closure))\
18191ab64890Smrg		return True; \
18201ab64890Smrg	    if (entry->tight && (entry = entry->next) && \
18211ab64890Smrg		entry->name == q && (bilevel || entry->hasloose) && \
18221ab64890Smrg		EnumLTable((LTable)entry, names+1, classes+1, level, closure))\
18231ab64890Smrg		return True; \
18241ab64890Smrg	} \
18251ab64890Smrg    }
18261ab64890Smrg
18271ab64890Smrg/* find entries named ename, leafness leaf, loose only, and call get */
18281ab64890Smrg#define ILOOSE(ename) \
18291ab64890Smrg    NFIND(ename); \
18301ab64890Smrg    if (entry && entry->tight && (entry = entry->next) && entry->name != q) \
18311ab64890Smrg	entry = (NTable)NULL; \
18321ab64890Smrg    if (entry) { \
18331ab64890Smrg	if (leaf == entry->leaf) { \
18341ab64890Smrg	    if ((*get)(entry, names+1, classes+1, level, closure)) \
18351ab64890Smrg		return True; \
18361ab64890Smrg	} else if (entry->leaf && (bilevel || entry->hasloose)) { \
18371ab64890Smrg	    if (EnumLTable((LTable)entry, names+1, classes+1, level, closure))\
18381ab64890Smrg		return True; \
18391ab64890Smrg	} \
18401ab64890Smrg    }
18411ab64890Smrg
18421ab64890Smrg    if (level >= MAXDBDEPTH)
18431ab64890Smrg	return False;
18441ab64890Smrg    closure->bindings[level] = (table->tight ?
18451ab64890Smrg				XrmBindTightly : XrmBindLoosely);
18461ab64890Smrg    closure->quarks[level] = table->name;
18471ab64890Smrg    level++;
18481ab64890Smrg    if (!*names) {
18491ab64890Smrg	if (EnumAllNTable(table, level, closure))
18501ab64890Smrg	    return True;
18511ab64890Smrg    } else {
18521ab64890Smrg	if (names[1] || closure->mode == XrmEnumAllLevels) {
18531ab64890Smrg	    get = EnumNTable; /* recurse */
18541ab64890Smrg	    leaf = 0;
18551ab64890Smrg	    bilevel = !names[1];
18561ab64890Smrg	} else {
18571ab64890Smrg	    get = (getNTableEProcp)EnumLTable; /* bottom of recursion */
18581ab64890Smrg	    leaf = 1;
18591ab64890Smrg	    bilevel = False;
18601ab64890Smrg	}
18611ab64890Smrg	if (table->hasloose && closure->mode == XrmEnumAllLevels) {
18621ab64890Smrg	    NTable *bucket;
18631ab64890Smrg	    int i;
18641ab64890Smrg	    XrmQuark empty = NULLQUARK;
18651ab64890Smrg
18661ab64890Smrg	    for (i = table->mask, bucket = NodeBuckets(table);
18671ab64890Smrg		 i >= 0;
18681ab64890Smrg		 i--, bucket++) {
18691ab64890Smrg		q = NULLQUARK;
18701ab64890Smrg		for (entry = *bucket; entry; entry = entry->next) {
18711ab64890Smrg		    if (!entry->tight && entry->name != q &&
18721ab64890Smrg			entry->name != *names && entry->name != *classes) {
18731ab64890Smrg			q = entry->name;
18741ab64890Smrg			if (entry->leaf) {
18751ab64890Smrg			    if (EnumLTable((LTable)entry, &empty, &empty,
18761ab64890Smrg					   level, closure))
18771ab64890Smrg				return True;
18781ab64890Smrg			} else {
18791ab64890Smrg			    if (EnumNTable(entry, &empty, &empty,
18801ab64890Smrg					   level, closure))
18811ab64890Smrg				return True;
18821ab64890Smrg			}
18831ab64890Smrg		    }
18841ab64890Smrg		}
18851ab64890Smrg	    }
18861ab64890Smrg	}
18871ab64890Smrg
18881ab64890Smrg	ITIGHTLOOSE(*names);   /* do name, tight and loose */
18891ab64890Smrg	ITIGHTLOOSE(*classes); /* do class, tight and loose */
18901ab64890Smrg	if (table->hasany) {
18911ab64890Smrg	    ITIGHTLOOSE(XrmQANY); /* do ANY, tight and loose */
18921ab64890Smrg	}
18931ab64890Smrg	if (table->hasloose) {
18941ab64890Smrg	    while (1) {
18951ab64890Smrg		names++;
18961ab64890Smrg		classes++;
18971ab64890Smrg		if (!*names)
18981ab64890Smrg		    break;
18991ab64890Smrg		if (!names[1] && closure->mode != XrmEnumAllLevels) {
19001ab64890Smrg		    get = (getNTableEProcp)EnumLTable; /* bottom of recursion */
19011ab64890Smrg		    leaf = 1;
19021ab64890Smrg		}
19031ab64890Smrg		ILOOSE(*names);   /* loose names */
19041ab64890Smrg		ILOOSE(*classes); /* loose classes */
19051ab64890Smrg		if (table->hasany) {
19061ab64890Smrg		    ILOOSE(XrmQANY); /* loose ANY */
19071ab64890Smrg		}
19081ab64890Smrg	    }
19091ab64890Smrg	    names--;
19101ab64890Smrg	    classes--;
19111ab64890Smrg	}
19121ab64890Smrg    }
19131ab64890Smrg    /* now look for matching leaf nodes */
19141ab64890Smrg    entry = table->next;
19151ab64890Smrg    if (!entry)
19161ab64890Smrg	return False;
19171ab64890Smrg    if (entry->leaf) {
19181ab64890Smrg	if (entry->tight && !table->tight)
19191ab64890Smrg	    entry = entry->next;
19201ab64890Smrg    } else {
19211ab64890Smrg	entry = entry->next;
19221ab64890Smrg	if (!entry || !entry->tight)
19231ab64890Smrg	    return False;
19241ab64890Smrg    }
19251ab64890Smrg    if (!entry || entry->name != table->name)
19261ab64890Smrg	return False;
19271ab64890Smrg    /* found one */
19281ab64890Smrg    level--;
19291ab64890Smrg    if ((!*names || entry->hasloose) &&
19301ab64890Smrg	EnumLTable((LTable)entry, names, classes, level, closure))
19311ab64890Smrg	return True;
19321ab64890Smrg    if (entry->tight && entry == table->next && (entry = entry->next) &&
19331ab64890Smrg	entry->name == table->name && (!*names || entry->hasloose))
19341ab64890Smrg	return EnumLTable((LTable)entry, names, classes, level, closure);
19351ab64890Smrg    return False;
19361ab64890Smrg
19371ab64890Smrg#undef ITIGHTLOOSE
19381ab64890Smrg#undef ILOOSE
19391ab64890Smrg}
19401ab64890Smrg
19411ab64890Smrg/* call the proc for every value in the database, arbitrary order.
19421ab64890Smrg * stop if the proc returns True.
19431ab64890Smrg */
19441ab64890SmrgBool XrmEnumerateDatabase(
19451ab64890Smrg    XrmDatabase		db,
19461ab64890Smrg    XrmNameList		names,
19471ab64890Smrg    XrmClassList	classes,
19481ab64890Smrg    int			mode,
19491ab64890Smrg    DBEnumProc		proc,
19501ab64890Smrg    XPointer		closure)
19511ab64890Smrg{
19521ab64890Smrg    XrmBinding  bindings[MAXDBDEPTH+2];
19531ab64890Smrg    XrmQuark	quarks[MAXDBDEPTH+2];
19541ab64890Smrg    register NTable table;
19551ab64890Smrg    EClosureRec	eclosure;
19561ab64890Smrg    Bool retval = False;
19571ab64890Smrg
19581ab64890Smrg    if (!db)
19591ab64890Smrg	return False;
19601ab64890Smrg    _XLockMutex(&db->linfo);
19611ab64890Smrg    eclosure.db = db;
19621ab64890Smrg    eclosure.proc = proc;
19631ab64890Smrg    eclosure.closure = closure;
19641ab64890Smrg    eclosure.bindings = bindings;
19651ab64890Smrg    eclosure.quarks = quarks;
19661ab64890Smrg    eclosure.mode = mode;
19671ab64890Smrg    table = db->table;
19681ab64890Smrg    if (table && !table->leaf && !*names && mode == XrmEnumOneLevel)
19691ab64890Smrg	table = table->next;
19701ab64890Smrg    if (table) {
19711ab64890Smrg	if (!table->leaf)
19721ab64890Smrg	    retval = EnumNTable(table, names, classes, 0, &eclosure);
19731ab64890Smrg	else
19741ab64890Smrg	    retval = EnumLTable((LTable)table, names, classes, 0, &eclosure);
19751ab64890Smrg    }
19761ab64890Smrg    _XUnlockMutex(&db->linfo);
19771ab64890Smrg    return retval;
19781ab64890Smrg}
19791ab64890Smrg
19801ab64890Smrgstatic void PrintBindingQuarkList(
19811ab64890Smrg    XrmBindingList      bindings,
19821ab64890Smrg    XrmQuarkList	quarks,
19831ab64890Smrg    FILE		*stream)
19841ab64890Smrg{
19851ab64890Smrg    Bool	firstNameSeen;
19861ab64890Smrg
19871ab64890Smrg    for (firstNameSeen = False; *quarks; bindings++, quarks++) {
19881ab64890Smrg	if (*bindings == XrmBindLoosely) {
19891ab64890Smrg	    (void) fprintf(stream, "*");
19901ab64890Smrg	} else if (firstNameSeen) {
19911ab64890Smrg	    (void) fprintf(stream, ".");
19921ab64890Smrg	}
19931ab64890Smrg	firstNameSeen = True;
19941ab64890Smrg	(void) fputs(XrmQuarkToString(*quarks), stream);
19951ab64890Smrg    }
19961ab64890Smrg}
19971ab64890Smrg
19981ab64890Smrg/* output out the entry in correct file syntax */
19991ab64890Smrg/*ARGSUSED*/
20001ab64890Smrgstatic Bool DumpEntry(
20011ab64890Smrg    XrmDatabase		*db,
20021ab64890Smrg    XrmBindingList      bindings,
20031ab64890Smrg    XrmQuarkList	quarks,
20041ab64890Smrg    XrmRepresentation   *type,
20051ab64890Smrg    XrmValuePtr		value,
20061ab64890Smrg    XPointer		data)
20071ab64890Smrg{
20081ab64890Smrg    FILE			*stream = (FILE *)data;
20091ab64890Smrg    register unsigned int	i;
20101ab64890Smrg    register char		*s;
20111ab64890Smrg    register char		c;
20121ab64890Smrg
20131ab64890Smrg    if (*type != XrmQString)
20141ab64890Smrg	(void) putc('!', stream);
20151ab64890Smrg    PrintBindingQuarkList(bindings, quarks, stream);
20161ab64890Smrg    s = value->addr;
20171ab64890Smrg    i = value->size;
20181ab64890Smrg    if (*type == XrmQString) {
20191ab64890Smrg	(void) fputs(":\t", stream);
20201ab64890Smrg	if (i)
20211ab64890Smrg	    i--;
20221ab64890Smrg    }
20231ab64890Smrg    else
20241ab64890Smrg	(void) fprintf(stream, "=%s:\t", XrmRepresentationToString(*type));
20251ab64890Smrg    if (i && (*s == ' ' || *s == '\t'))
20261ab64890Smrg	(void) putc('\\', stream); /* preserve leading whitespace */
20271ab64890Smrg    while (i--) {
20281ab64890Smrg	c = *s++;
20291ab64890Smrg	if (c == '\n') {
20301ab64890Smrg	    if (i)
20311ab64890Smrg		(void) fputs("\\n\\\n", stream);
20321ab64890Smrg	    else
20331ab64890Smrg		(void) fputs("\\n", stream);
20341ab64890Smrg	} else if (c == '\\')
20351ab64890Smrg	    (void) fputs("\\\\", stream);
20361ab64890Smrg	else if ((c < ' ' && c != '\t') ||
20371ab64890Smrg		 ((unsigned char)c >= 0x7f && (unsigned char)c < 0xa0))
20381ab64890Smrg	    (void) fprintf(stream, "\\%03o", (unsigned char)c);
20391ab64890Smrg	else
20401ab64890Smrg	    (void) putc(c, stream);
20411ab64890Smrg    }
20421ab64890Smrg    (void) putc('\n', stream);
20431ab64890Smrg    return ferror(stream) != 0;
20441ab64890Smrg}
20451ab64890Smrg
20461ab64890Smrg#ifdef DEBUG
20471ab64890Smrg
20481ab64890Smrgvoid PrintTable(
20491ab64890Smrg    NTable table,
20501ab64890Smrg    FILE *file)
20511ab64890Smrg{
20521ab64890Smrg    XrmBinding  bindings[MAXDBDEPTH+1];
20531ab64890Smrg    XrmQuark	quarks[MAXDBDEPTH+1];
20541ab64890Smrg    EClosureRec closure;
20551ab64890Smrg    XrmQuark	empty = NULLQUARK;
20561ab64890Smrg
20571ab64890Smrg    closure.db = (XrmDatabase)NULL;
20581ab64890Smrg    closure.proc = DumpEntry;
20591ab64890Smrg    closure.closure = (XPointer)file;
20601ab64890Smrg    closure.bindings = bindings;
20611ab64890Smrg    closure.quarks = quarks;
20621ab64890Smrg    closure.mode = XrmEnumAllLevels;
20631ab64890Smrg    if (table->leaf)
20641ab64890Smrg	EnumLTable((LTable)table, &empty, &empty, 0, &closure);
20651ab64890Smrg    else
20661ab64890Smrg	EnumNTable(table, &empty, &empty, 0, &closure);
20671ab64890Smrg}
20681ab64890Smrg
20691ab64890Smrg#endif /* DEBUG */
20701ab64890Smrg
20711ab64890Smrgvoid
20721ab64890SmrgXrmPutFileDatabase(
20731ab64890Smrg    XrmDatabase db,
20741ab64890Smrg    _Xconst char *fileName)
20751ab64890Smrg{
20761ab64890Smrg    FILE	*file;
20771ab64890Smrg    XrmQuark empty = NULLQUARK;
20781ab64890Smrg
20791ab64890Smrg    if (!db) return;
20801ab64890Smrg    if (!(file = fopen(fileName, "w"))) return;
20811ab64890Smrg    if (XrmEnumerateDatabase(db, &empty, &empty, XrmEnumAllLevels,
20821ab64890Smrg			     DumpEntry, (XPointer) file))
20831ab64890Smrg	unlink((char *)fileName);
20841ab64890Smrg    fclose(file);
20851ab64890Smrg}
20861ab64890Smrg
20871ab64890Smrg/* macros used in get/search functions */
20881ab64890Smrg
20891ab64890Smrg/* find entries named ename, leafness leaf, tight or loose, and call get */
20901ab64890Smrg#define GTIGHTLOOSE(ename,looseleaf) \
20911ab64890Smrg    NFIND(ename); \
20921ab64890Smrg    if (entry) { \
20931ab64890Smrg	if (leaf == entry->leaf) { \
20941ab64890Smrg	    if (!leaf && !entry->tight && entry->next && \
20951ab64890Smrg		entry->next->name == q && entry->next->tight && \
20961ab64890Smrg		entry->next->hasloose && \
20971ab64890Smrg		looseleaf((LTable)entry->next, names+1, classes+1, closure)) \
20981ab64890Smrg		return True; \
20991ab64890Smrg	    if ((*get)(entry, names+1, classes+1, closure)) \
21001ab64890Smrg		return True; \
21011ab64890Smrg	    if (entry->tight && (entry = entry->next) && \
21021ab64890Smrg		entry->name == q && leaf == entry->leaf && \
21031ab64890Smrg		(*get)(entry, names+1, classes+1, closure)) \
21041ab64890Smrg		return True; \
21051ab64890Smrg	} else if (entry->leaf) { \
21061ab64890Smrg	    if (entry->hasloose && \
21071ab64890Smrg		looseleaf((LTable)entry, names+1, classes+1, closure)) \
21081ab64890Smrg		return True; \
21091ab64890Smrg	    if (entry->tight && (entry = entry->next) && \
21101ab64890Smrg		entry->name == q && entry->hasloose && \
21111ab64890Smrg		looseleaf((LTable)entry, names+1, classes+1, closure)) \
21121ab64890Smrg		return True; \
21131ab64890Smrg	} \
21141ab64890Smrg    }
21151ab64890Smrg
21161ab64890Smrg/* find entries named ename, leafness leaf, loose only, and call get */
21171ab64890Smrg#define GLOOSE(ename,looseleaf) \
21181ab64890Smrg    NFIND(ename); \
21191ab64890Smrg    if (entry && entry->tight && (entry = entry->next) && entry->name != q) \
21201ab64890Smrg	entry = (NTable)NULL; \
21211ab64890Smrg    if (entry) { \
21221ab64890Smrg	if (leaf == entry->leaf) { \
21231ab64890Smrg	    if ((*get)(entry, names+1, classes+1, closure)) \
21241ab64890Smrg		return True; \
21251ab64890Smrg	} else if (entry->leaf && entry->hasloose) { \
21261ab64890Smrg	    if (looseleaf((LTable)entry, names+1, classes+1, closure)) \
21271ab64890Smrg		return True; \
21281ab64890Smrg	} \
21291ab64890Smrg    }
21301ab64890Smrg
21311ab64890Smrg/* add tight/loose entry to the search list, return True if list is full */
21321ab64890Smrg/*ARGSUSED*/
21331ab64890Smrgstatic Bool AppendLEntry(
21341ab64890Smrg    LTable		table,
21351ab64890Smrg    XrmNameList		names,
21361ab64890Smrg    XrmClassList 	classes,
21371ab64890Smrg    register SClosure	closure)
21381ab64890Smrg{
21391ab64890Smrg    /* check for duplicate */
21401ab64890Smrg    if (closure->idx >= 0 && closure->list[closure->idx] == table)
21411ab64890Smrg	return False;
21421ab64890Smrg    if (closure->idx == closure->limit)
21431ab64890Smrg	return True;
21441ab64890Smrg    /* append it */
21451ab64890Smrg    closure->idx++;
21461ab64890Smrg    closure->list[closure->idx] = table;
21471ab64890Smrg    return False;
21481ab64890Smrg}
21491ab64890Smrg
21501ab64890Smrg/* add loose entry to the search list, return True if list is full */
21511ab64890Smrg/*ARGSUSED*/
21521ab64890Smrgstatic Bool AppendLooseLEntry(
21531ab64890Smrg    LTable		table,
21541ab64890Smrg    XrmNameList		names,
21551ab64890Smrg    XrmClassList 	classes,
21561ab64890Smrg    register SClosure	closure)
21571ab64890Smrg{
21581ab64890Smrg    /* check for duplicate */
21591ab64890Smrg    if (closure->idx >= 0 && closure->list[closure->idx] == table)
21601ab64890Smrg	return False;
21611ab64890Smrg    if (closure->idx >= closure->limit - 1)
21621ab64890Smrg	return True;
21631ab64890Smrg    /* append it */
21641ab64890Smrg    closure->idx++;
21651ab64890Smrg    closure->list[closure->idx] = LOOSESEARCH;
21661ab64890Smrg    closure->idx++;
21671ab64890Smrg    closure->list[closure->idx] = table;
21681ab64890Smrg    return False;
21691ab64890Smrg}
21701ab64890Smrg
21711ab64890Smrg/* search for a leaf table */
21721ab64890Smrgstatic Bool SearchNEntry(
21731ab64890Smrg    NTable		table,
21741ab64890Smrg    XrmNameList		names,
21751ab64890Smrg    XrmClassList 	classes,
21761ab64890Smrg    SClosure		closure)
21771ab64890Smrg{
21781ab64890Smrg    register NTable	entry;
21791ab64890Smrg    register XrmQuark	q;
21801ab64890Smrg    register unsigned int leaf;
21811ab64890Smrg    Bool		(*get)(
21821ab64890Smrg            NTable		table,
21831ab64890Smrg            XrmNameList		names,
21841ab64890Smrg            XrmClassList 	classes,
21851ab64890Smrg            SClosure		closure);
21861ab64890Smrg
21871ab64890Smrg    if (names[1]) {
21881ab64890Smrg	get = SearchNEntry; /* recurse */
21891ab64890Smrg	leaf = 0;
21901ab64890Smrg    } else {
21911ab64890Smrg	get = (getNTableSProcp)AppendLEntry; /* bottom of recursion */
21921ab64890Smrg	leaf = 1;
21931ab64890Smrg    }
21941ab64890Smrg    GTIGHTLOOSE(*names, AppendLooseLEntry);   /* do name, tight and loose */
21951ab64890Smrg    GTIGHTLOOSE(*classes, AppendLooseLEntry); /* do class, tight and loose */
21961ab64890Smrg    if (table->hasany) {
21971ab64890Smrg	GTIGHTLOOSE(XrmQANY, AppendLooseLEntry); /* do ANY, tight and loose */
21981ab64890Smrg    }
21991ab64890Smrg    if (table->hasloose) {
22001ab64890Smrg	while (1) {
22011ab64890Smrg	    names++;
22021ab64890Smrg	    classes++;
22031ab64890Smrg	    if (!*names)
22041ab64890Smrg		break;
22051ab64890Smrg	    if (!names[1]) {
22061ab64890Smrg		get = (getNTableSProcp)AppendLEntry; /* bottom of recursion */
22071ab64890Smrg		leaf = 1;
22081ab64890Smrg	    }
22091ab64890Smrg	    GLOOSE(*names, AppendLooseLEntry);   /* loose names */
22101ab64890Smrg	    GLOOSE(*classes, AppendLooseLEntry); /* loose classes */
22111ab64890Smrg	    if (table->hasany) {
22121ab64890Smrg		GLOOSE(XrmQANY, AppendLooseLEntry); /* loose ANY */
22131ab64890Smrg	    }
22141ab64890Smrg	}
22151ab64890Smrg    }
22161ab64890Smrg    /* now look for matching leaf nodes */
22171ab64890Smrg    entry = table->next;
22181ab64890Smrg    if (!entry)
22191ab64890Smrg	return False;
22201ab64890Smrg    if (entry->leaf) {
22211ab64890Smrg	if (entry->tight && !table->tight)
22221ab64890Smrg	    entry = entry->next;
22231ab64890Smrg    } else {
22241ab64890Smrg	entry = entry->next;
22251ab64890Smrg	if (!entry || !entry->tight)
22261ab64890Smrg	    return False;
22271ab64890Smrg    }
22281ab64890Smrg    if (!entry || entry->name != table->name)
22291ab64890Smrg	return False;
22301ab64890Smrg    /* found one */
22311ab64890Smrg    if (entry->hasloose &&
22321ab64890Smrg	AppendLooseLEntry((LTable)entry, names, classes, closure))
22331ab64890Smrg	return True;
22341ab64890Smrg    if (entry->tight && entry == table->next && (entry = entry->next) &&
22351ab64890Smrg	entry->name == table->name && entry->hasloose)
22361ab64890Smrg	return AppendLooseLEntry((LTable)entry, names, classes, closure);
22371ab64890Smrg    return False;
22381ab64890Smrg}
22391ab64890Smrg
22401ab64890SmrgBool XrmQGetSearchList(
22411ab64890Smrg    XrmDatabase     db,
22421ab64890Smrg    XrmNameList	    names,
22431ab64890Smrg    XrmClassList    classes,
22441ab64890Smrg    XrmSearchList   searchList,	/* RETURN */
22451ab64890Smrg    int		    listLength)
22461ab64890Smrg{
22471ab64890Smrg    register NTable	table;
22481ab64890Smrg    SClosureRec		closure;
22491ab64890Smrg
22501ab64890Smrg    if (listLength <= 0)
22511ab64890Smrg	return False;
22521ab64890Smrg    closure.list = (LTable *)searchList;
22531ab64890Smrg    closure.idx = -1;
22541ab64890Smrg    closure.limit = listLength - 2;
22551ab64890Smrg    if (db) {
22561ab64890Smrg	_XLockMutex(&db->linfo);
22571ab64890Smrg	table = db->table;
22581ab64890Smrg	if (*names) {
22591ab64890Smrg	    if (table && !table->leaf) {
22601ab64890Smrg		if (SearchNEntry(table, names, classes, &closure)) {
22611ab64890Smrg		    _XUnlockMutex(&db->linfo);
22621ab64890Smrg		    return False;
22631ab64890Smrg		}
22641ab64890Smrg	    } else if (table && table->hasloose &&
22651ab64890Smrg		       AppendLooseLEntry((LTable)table, names, classes,
22661ab64890Smrg					 &closure)) {
22671ab64890Smrg		_XUnlockMutex(&db->linfo);
22681ab64890Smrg		return False;
22691ab64890Smrg	    }
22701ab64890Smrg	} else {
22711ab64890Smrg	    if (table && !table->leaf)
22721ab64890Smrg		table = table->next;
227361b2299dSmrg	    if (table &&
22741ab64890Smrg		AppendLEntry((LTable)table, names, classes, &closure)) {
22751ab64890Smrg		_XUnlockMutex(&db->linfo);
22761ab64890Smrg		return False;
22771ab64890Smrg	    }
22781ab64890Smrg	}
22791ab64890Smrg	_XUnlockMutex(&db->linfo);
22801ab64890Smrg    }
22811ab64890Smrg    closure.list[closure.idx + 1] = (LTable)NULL;
22821ab64890Smrg    return True;
22831ab64890Smrg}
22841ab64890Smrg
22851ab64890SmrgBool XrmQGetSearchResource(
22861ab64890Smrg	     XrmSearchList	searchList,
22871ab64890Smrg    register XrmName		name,
22881ab64890Smrg    register XrmClass		class,
22891ab64890Smrg    	     XrmRepresentation	*pType,  /* RETURN */
22901ab64890Smrg    	     XrmValue		*pValue) /* RETURN */
22911ab64890Smrg{
22921ab64890Smrg    register LTable *list;
22931ab64890Smrg    register LTable table;
22941ab64890Smrg    register VEntry entry = NULL;
22951ab64890Smrg    int flags;
22961ab64890Smrg
22971ab64890Smrg/* find tight or loose entry */
22981ab64890Smrg#define VTIGHTLOOSE(q) \
22991ab64890Smrg    entry = LeafHash(table, q); \
23001ab64890Smrg    while (entry && entry->name != q) \
23011ab64890Smrg	entry = entry->next; \
23021ab64890Smrg    if (entry) \
23031ab64890Smrg	break
23041ab64890Smrg
23051ab64890Smrg/* find loose entry */
23061ab64890Smrg#define VLOOSE(q) \
23071ab64890Smrg    entry = LeafHash(table, q); \
23081ab64890Smrg    while (entry && entry->name != q) \
23091ab64890Smrg	entry = entry->next; \
23101ab64890Smrg    if (entry) { \
23111ab64890Smrg	if (!entry->tight) \
23121ab64890Smrg	    break; \
23131ab64890Smrg	if ((entry = entry->next) && entry->name == q) \
23141ab64890Smrg	    break; \
23151ab64890Smrg    }
23161ab64890Smrg
23171ab64890Smrg    list = (LTable *)searchList;
23181ab64890Smrg    /* figure out which combination of name and class we need to search for */
23191ab64890Smrg    flags = 0;
23201ab64890Smrg    if (IsResourceQuark(name))
23211ab64890Smrg	flags = 2;
23221ab64890Smrg    if (IsResourceQuark(class))
23231ab64890Smrg	flags |= 1;
23241ab64890Smrg    if (!flags) {
23251ab64890Smrg	/* neither name nor class has ever been used to name a resource */
23261ab64890Smrg	table = (LTable)NULL;
23271ab64890Smrg    } else if (flags == 3) {
23281ab64890Smrg	/* both name and class */
23291ab64890Smrg	while ((table = *list++)) {
23301ab64890Smrg	    if (table != LOOSESEARCH) {
23311ab64890Smrg		VTIGHTLOOSE(name);  /* do name, tight and loose */
23321ab64890Smrg		VTIGHTLOOSE(class); /* do class, tight and loose */
23331ab64890Smrg	    } else {
23341ab64890Smrg		table = *list++;
23351ab64890Smrg		VLOOSE(name);  /* do name, loose only */
23361ab64890Smrg		VLOOSE(class); /* do class, loose only */
23371ab64890Smrg	    }
23381ab64890Smrg	}
23391ab64890Smrg    } else {
23401ab64890Smrg	/* just one of name or class */
23411ab64890Smrg	if (flags == 1)
23421ab64890Smrg	    name = class;
23431ab64890Smrg	while ((table = *list++)) {
23441ab64890Smrg	    if (table != LOOSESEARCH) {
23451ab64890Smrg		VTIGHTLOOSE(name); /* tight and loose */
23461ab64890Smrg	    } else {
23471ab64890Smrg		table = *list++;
23481ab64890Smrg		VLOOSE(name); /* loose only */
23491ab64890Smrg	    }
23501ab64890Smrg	}
23511ab64890Smrg    }
23521ab64890Smrg    if (table) {
23531ab64890Smrg	/* found a match */
23541ab64890Smrg	if (entry->string) {
23551ab64890Smrg	    *pType = XrmQString;
23561ab64890Smrg	    pValue->addr = StringValue(entry);
23571ab64890Smrg	} else {
23581ab64890Smrg	    *pType = RepType(entry);
23591ab64890Smrg	    pValue->addr = DataValue(entry);
23601ab64890Smrg	}
23611ab64890Smrg	pValue->size = entry->size;
23621ab64890Smrg	return True;
23631ab64890Smrg    }
23641ab64890Smrg    *pType = NULLQUARK;
23651ab64890Smrg    pValue->addr = (XPointer)NULL;
23661ab64890Smrg    pValue->size = 0;
23671ab64890Smrg    return False;
23681ab64890Smrg
23691ab64890Smrg#undef VTIGHTLOOSE
23701ab64890Smrg#undef VLOOSE
23711ab64890Smrg}
23721ab64890Smrg
23731ab64890Smrg/* look for a tight/loose value */
23741ab64890Smrgstatic Bool GetVEntry(
23751ab64890Smrg    LTable		table,
23761ab64890Smrg    XrmNameList		names,
23771ab64890Smrg    XrmClassList 	classes,
23781ab64890Smrg    VClosure		closure)
23791ab64890Smrg{
23801ab64890Smrg    register VEntry entry;
23811ab64890Smrg    register XrmQuark q;
23821ab64890Smrg
23831ab64890Smrg    /* try name first */
23841ab64890Smrg    q = *names;
23851ab64890Smrg    entry = LeafHash(table, q);
23861ab64890Smrg    while (entry && entry->name != q)
23871ab64890Smrg	entry = entry->next;
23881ab64890Smrg    if (!entry) {
23891ab64890Smrg	/* not found, try class */
23901ab64890Smrg	q = *classes;
23911ab64890Smrg	entry = LeafHash(table, q);
23921ab64890Smrg	while (entry && entry->name != q)
23931ab64890Smrg	    entry = entry->next;
23941ab64890Smrg	if (!entry)
23951ab64890Smrg	    return False;
23961ab64890Smrg    }
23971ab64890Smrg    if (entry->string) {
23981ab64890Smrg	*closure->type = XrmQString;
23991ab64890Smrg	closure->value->addr = StringValue(entry);
24001ab64890Smrg    } else {
24011ab64890Smrg	*closure->type = RepType(entry);
24021ab64890Smrg	closure->value->addr = DataValue(entry);
24031ab64890Smrg    }
24041ab64890Smrg    closure->value->size = entry->size;
24051ab64890Smrg    return True;
24061ab64890Smrg}
24071ab64890Smrg
24081ab64890Smrg/* look for a loose value */
24091ab64890Smrgstatic Bool GetLooseVEntry(
24101ab64890Smrg    LTable		table,
24111ab64890Smrg    XrmNameList		names,
24121ab64890Smrg    XrmClassList 	classes,
24131ab64890Smrg    VClosure		closure)
24141ab64890Smrg{
24151ab64890Smrg    register VEntry	entry;
24161ab64890Smrg    register XrmQuark	q;
24171ab64890Smrg
24181ab64890Smrg#define VLOOSE(ename) \
24191ab64890Smrg    q = ename; \
24201ab64890Smrg    entry = LeafHash(table, q); \
24211ab64890Smrg    while (entry && entry->name != q) \
24221ab64890Smrg	entry = entry->next; \
24231ab64890Smrg    if (entry && entry->tight && (entry = entry->next) && entry->name != q) \
24241ab64890Smrg	entry = (VEntry)NULL;
24251ab64890Smrg
24261ab64890Smrg    /* bump to last component */
24271ab64890Smrg    while (names[1]) {
24281ab64890Smrg	names++;
24291ab64890Smrg	classes++;
24301ab64890Smrg    }
24311ab64890Smrg    VLOOSE(*names);  /* do name, loose only */
24321ab64890Smrg    if (!entry) {
24331ab64890Smrg	VLOOSE(*classes); /* do class, loose only */
24341ab64890Smrg	if (!entry)
24351ab64890Smrg	    return False;
24361ab64890Smrg    }
24371ab64890Smrg    if (entry->string) {
24381ab64890Smrg	*closure->type = XrmQString;
24391ab64890Smrg	closure->value->addr = StringValue(entry);
24401ab64890Smrg    } else {
24411ab64890Smrg	*closure->type = RepType(entry);
24421ab64890Smrg	closure->value->addr = DataValue(entry);
24431ab64890Smrg    }
24441ab64890Smrg    closure->value->size = entry->size;
24451ab64890Smrg    return True;
24461ab64890Smrg
24471ab64890Smrg#undef VLOOSE
24481ab64890Smrg}
24491ab64890Smrg
24501ab64890Smrg/* recursive search for a value */
24511ab64890Smrgstatic Bool GetNEntry(
24521ab64890Smrg    NTable		table,
24531ab64890Smrg    XrmNameList		names,
24541ab64890Smrg    XrmClassList 	classes,
24551ab64890Smrg    VClosure		closure)
24561ab64890Smrg{
24571ab64890Smrg    register NTable	entry;
24581ab64890Smrg    register XrmQuark	q;
24591ab64890Smrg    register unsigned int leaf;
24601ab64890Smrg    Bool		(*get)(
24611ab64890Smrg            NTable              table,
24621ab64890Smrg            XrmNameList         names,
24631ab64890Smrg            XrmClassList        classes,
24641ab64890Smrg            VClosure            closure);
24651ab64890Smrg    NTable		otable;
24661ab64890Smrg
24671ab64890Smrg    if (names[2]) {
24681ab64890Smrg	get = GetNEntry; /* recurse */
24691ab64890Smrg	leaf = 0;
24701ab64890Smrg    } else {
24711ab64890Smrg	get = (getNTableVProcp)GetVEntry; /* bottom of recursion */
24721ab64890Smrg	leaf = 1;
24731ab64890Smrg    }
24741ab64890Smrg    GTIGHTLOOSE(*names, GetLooseVEntry);   /* do name, tight and loose */
24751ab64890Smrg    GTIGHTLOOSE(*classes, GetLooseVEntry); /* do class, tight and loose */
24761ab64890Smrg    if (table->hasany) {
24771ab64890Smrg	GTIGHTLOOSE(XrmQANY, GetLooseVEntry); /* do ANY, tight and loose */
24781ab64890Smrg    }
24791ab64890Smrg    if (table->hasloose) {
24801ab64890Smrg	while (1) {
24811ab64890Smrg	    names++;
24821ab64890Smrg	    classes++;
24831ab64890Smrg	    if (!names[1])
24841ab64890Smrg		break;
24851ab64890Smrg	    if (!names[2]) {
24861ab64890Smrg		get = (getNTableVProcp)GetVEntry; /* bottom of recursion */
24871ab64890Smrg		leaf = 1;
24881ab64890Smrg	    }
24891ab64890Smrg	    GLOOSE(*names, GetLooseVEntry);   /* do name, loose only */
24901ab64890Smrg	    GLOOSE(*classes, GetLooseVEntry); /* do class, loose only */
24911ab64890Smrg	    if (table->hasany) {
24921ab64890Smrg		GLOOSE(XrmQANY, GetLooseVEntry); /* do ANY, loose only */
24931ab64890Smrg	    }
24941ab64890Smrg	}
24951ab64890Smrg    }
24961ab64890Smrg    /* look for matching leaf tables */
24971ab64890Smrg    otable = table;
24981ab64890Smrg    table = table->next;
24991ab64890Smrg    if (!table)
25001ab64890Smrg	return False;
25011ab64890Smrg    if (table->leaf) {
25021ab64890Smrg	if (table->tight && !otable->tight)
25031ab64890Smrg	    table = table->next;
25041ab64890Smrg    } else {
25051ab64890Smrg	table = table->next;
25061ab64890Smrg	if (!table || !table->tight)
25071ab64890Smrg	    return False;
25081ab64890Smrg    }
25091ab64890Smrg    if (!table || table->name != otable->name)
25101ab64890Smrg	return False;
25111ab64890Smrg    /* found one */
25121ab64890Smrg    if (table->hasloose &&
25131ab64890Smrg	GetLooseVEntry((LTable)table, names, classes, closure))
25141ab64890Smrg	return True;
25151ab64890Smrg    if (table->tight && table == otable->next) {
25161ab64890Smrg	table = table->next;
25171ab64890Smrg	if (table && table->name == otable->name && table->hasloose)
25181ab64890Smrg	    return GetLooseVEntry((LTable)table, names, classes, closure);
25191ab64890Smrg    }
25201ab64890Smrg    return False;
25211ab64890Smrg}
25221ab64890Smrg
25231ab64890SmrgBool XrmQGetResource(
25241ab64890Smrg    XrmDatabase         db,
25251ab64890Smrg    XrmNameList		names,
25261ab64890Smrg    XrmClassList 	classes,
25271ab64890Smrg    XrmRepresentation	*pType,  /* RETURN */
25281ab64890Smrg    XrmValuePtr		pValue)  /* RETURN */
25291ab64890Smrg{
25301ab64890Smrg    register NTable table;
25311ab64890Smrg    VClosureRec closure;
25321ab64890Smrg
25331ab64890Smrg    if (db && *names) {
25341ab64890Smrg	_XLockMutex(&db->linfo);
25351ab64890Smrg	closure.type = pType;
25361ab64890Smrg	closure.value = pValue;
25371ab64890Smrg	table = db->table;
25381ab64890Smrg	if (names[1]) {
25391ab64890Smrg	    if (table && !table->leaf) {
25401ab64890Smrg		if (GetNEntry(table, names, classes, &closure)) {
25411ab64890Smrg		    _XUnlockMutex(&db->linfo);
25421ab64890Smrg		    return True;
25431ab64890Smrg		}
25441ab64890Smrg	    } else if (table && table->hasloose &&
25451ab64890Smrg		    GetLooseVEntry((LTable)table, names, classes, &closure)) {
25461ab64890Smrg		_XUnlockMutex (&db->linfo);
25471ab64890Smrg		return True;
25481ab64890Smrg	    }
25491ab64890Smrg	} else {
25501ab64890Smrg	    if (table && !table->leaf)
25511ab64890Smrg		table = table->next;
25521ab64890Smrg	    if (table && GetVEntry((LTable)table, names, classes, &closure)) {
25531ab64890Smrg		_XUnlockMutex(&db->linfo);
25541ab64890Smrg		return True;
25551ab64890Smrg	    }
25561ab64890Smrg	}
25571ab64890Smrg	_XUnlockMutex(&db->linfo);
25581ab64890Smrg    }
25591ab64890Smrg    *pType = NULLQUARK;
25601ab64890Smrg    pValue->addr = (XPointer)NULL;
25611ab64890Smrg    pValue->size = 0;
25621ab64890Smrg    return False;
25631ab64890Smrg}
25641ab64890Smrg
25651ab64890SmrgBool
256661b2299dSmrgXrmGetResource(XrmDatabase db, _Xconst char *name_str, _Xconst char *class_str,
256761b2299dSmrg	       XrmString *pType_str, XrmValuePtr pValue)
25681ab64890Smrg{
25691ab64890Smrg    XrmName		names[MAXDBDEPTH+1];
25701ab64890Smrg    XrmClass		classes[MAXDBDEPTH+1];
25711ab64890Smrg    XrmRepresentation   fromType;
25721ab64890Smrg    Bool		result;
25731ab64890Smrg
25741ab64890Smrg    XrmStringToNameList(name_str, names);
25751ab64890Smrg    XrmStringToClassList(class_str, classes);
25761ab64890Smrg    result = XrmQGetResource(db, names, classes, &fromType, pValue);
25771ab64890Smrg    (*pType_str) = XrmQuarkToString(fromType);
25781ab64890Smrg    return result;
25791ab64890Smrg}
25801ab64890Smrg
25811ab64890Smrg/* destroy all values, plus table itself */
25821ab64890Smrgstatic void DestroyLTable(
25831ab64890Smrg    LTable table)
25841ab64890Smrg{
25851ab64890Smrg    register int i;
25861ab64890Smrg    register VEntry *buckets;
25871ab64890Smrg    register VEntry entry, next;
25881ab64890Smrg
25891ab64890Smrg    buckets = table->buckets;
25901ab64890Smrg    for (i = table->table.mask; i >= 0; i--, buckets++) {
25911ab64890Smrg	for (next = *buckets; (entry = next); ) {
25921ab64890Smrg	    next = entry->next;
2593818534a1Smrg	    Xfree(entry);
25941ab64890Smrg	}
25951ab64890Smrg    }
2596818534a1Smrg    Xfree(table->buckets);
2597818534a1Smrg    Xfree(table);
25981ab64890Smrg}
25991ab64890Smrg
26001ab64890Smrg/* destroy all contained tables, plus table itself */
26011ab64890Smrgstatic void DestroyNTable(
26021ab64890Smrg    NTable table)
26031ab64890Smrg{
26041ab64890Smrg    register int i;
26051ab64890Smrg    register NTable *buckets;
26061ab64890Smrg    register NTable entry, next;
26071ab64890Smrg
26081ab64890Smrg    buckets = NodeBuckets(table);
26091ab64890Smrg    for (i = table->mask; i >= 0; i--, buckets++) {
26101ab64890Smrg	for (next = *buckets; (entry = next); ) {
26111ab64890Smrg	    next = entry->next;
26121ab64890Smrg	    if (entry->leaf)
26131ab64890Smrg		DestroyLTable((LTable)entry);
26141ab64890Smrg	    else
26151ab64890Smrg		DestroyNTable(entry);
26161ab64890Smrg	}
26171ab64890Smrg    }
2618818534a1Smrg    Xfree(table);
26191ab64890Smrg}
26201ab64890Smrg
26211ab64890Smrgconst char *
26221ab64890SmrgXrmLocaleOfDatabase(
26231ab64890Smrg    XrmDatabase db)
26241ab64890Smrg{
26251ab64890Smrg    const char* retval;
26261ab64890Smrg    _XLockMutex(&db->linfo);
26271ab64890Smrg    retval = (*db->methods->lcname)(db->mbstate);
26281ab64890Smrg    _XUnlockMutex(&db->linfo);
26291ab64890Smrg    return retval;
26301ab64890Smrg}
26311ab64890Smrg
26321ab64890Smrgvoid XrmDestroyDatabase(
26331ab64890Smrg    XrmDatabase   db)
26341ab64890Smrg{
26351ab64890Smrg    register NTable table, next;
26361ab64890Smrg
26371ab64890Smrg    if (db) {
26381ab64890Smrg	_XLockMutex(&db->linfo);
26391ab64890Smrg	for (next = db->table; (table = next); ) {
26401ab64890Smrg	    next = table->next;
26411ab64890Smrg	    if (table->leaf)
26421ab64890Smrg		DestroyLTable((LTable)table);
26431ab64890Smrg	    else
26441ab64890Smrg		DestroyNTable(table);
26451ab64890Smrg	}
26461ab64890Smrg	_XUnlockMutex(&db->linfo);
26471ab64890Smrg	_XFreeMutex(&db->linfo);
26481ab64890Smrg	(*db->methods->destroy)(db->mbstate);
2649818534a1Smrg	Xfree(db);
26501ab64890Smrg    }
26511ab64890Smrg}
2652