aboutsummaryrefslogtreecommitdiff
path: root/nx-X11/lib/src/Xrm.c
diff options
context:
space:
mode:
Diffstat (limited to 'nx-X11/lib/src/Xrm.c')
-rw-r--r--nx-X11/lib/src/Xrm.c2651
1 files changed, 2651 insertions, 0 deletions
diff --git a/nx-X11/lib/src/Xrm.c b/nx-X11/lib/src/Xrm.c
new file mode 100644
index 000000000..e3587d021
--- /dev/null
+++ b/nx-X11/lib/src/Xrm.c
@@ -0,0 +1,2651 @@
+
+/***********************************************************
+Copyright 1987, 1988, 1990 by Digital Equipment Corporation, Maynard
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+/*
+
+Copyright 1987, 1988, 1990, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from The Open Group.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <ctype.h>
+#include "Xlibint.h"
+#include <nx-X11/Xresource.h>
+#include "Xlcint.h"
+#ifdef XTHREADS
+#include "locking.h"
+#endif
+#include <nx-X11/Xos.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include "Xresinternal.h"
+#include "Xresource.h"
+
+/*
+
+These Xrm routines allow very fast lookup of resources in the resource
+database. Several usage patterns are exploited:
+
+(1) Widgets get a lot of resources at one time. Rather than look up each from
+scratch, we can precompute the prioritized list of database levels once, then
+search for each resource starting at the beginning of the list.
+
+(2) Many database levels don't contain any leaf resource nodes. There is no
+point in looking for resources on a level that doesn't contain any. This
+information is kept on a per-level basis.
+
+(3) Sometimes the widget instance tree is structured such that you get the same
+class name repeated on the fully qualified widget name. This can result in the
+same database level occuring multiple times on the search list. The code below
+only checks to see if you get two identical search lists in a row, rather than
+look back through all database levels, but in practice this removes all
+duplicates I've ever observed.
+
+Joel McCormack
+
+*/
+
+/*
+
+The Xrm representation has been completely redesigned to substantially reduce
+memory and hopefully improve performance.
+
+The database is structured into two kinds of tables: LTables that contain
+only values, and NTables that contain only other tables.
+
+Some invariants:
+
+The next pointer of the top-level node table points to the top-level leaf
+table, if any.
+
+Within an LTable, for a given name, the tight value always precedes the
+loose value, and if both are present the loose value is always right after
+the tight value.
+
+Within an NTable, all of the entries for a given name are contiguous,
+in the order tight NTable, loose NTable, tight LTable, loose LTable.
+
+Bob Scheifler
+
+*/
+
+static XrmQuark XrmQString, XrmQANY;
+
+typedef Bool (*DBEnumProc)(
+ XrmDatabase* /* db */,
+ XrmBindingList /* bindings */,
+ XrmQuarkList /* quarks */,
+ XrmRepresentation* /* type */,
+ XrmValue* /* value */,
+ XPointer /* closure */
+);
+
+typedef struct _VEntry {
+ struct _VEntry *next; /* next in chain */
+ XrmQuark name; /* name of this entry */
+ unsigned int tight:1; /* 1 if it is a tight binding */
+ unsigned int string:1; /* 1 if type is String */
+ unsigned int size:30; /* size of value */
+} VEntryRec, *VEntry;
+
+
+typedef struct _DEntry {
+ VEntryRec entry; /* entry */
+ XrmRepresentation type; /* representation type */
+} DEntryRec, *DEntry;
+
+/* the value is right after the structure */
+#define StringValue(ve) (XPointer)((ve) + 1)
+#define RepType(ve) ((DEntry)(ve))->type
+/* the value is right after the structure */
+#define DataValue(ve) (XPointer)(((DEntry)(ve)) + 1)
+#define RawValue(ve) (char *)((ve)->string ? StringValue(ve) : DataValue(ve))
+
+typedef struct _NTable {
+ struct _NTable *next; /* next in chain */
+ XrmQuark name; /* name of this entry */
+ unsigned int tight:1; /* 1 if it is a tight binding */
+ unsigned int leaf:1; /* 1 if children are values */
+ unsigned int hasloose:1; /* 1 if has loose children */
+ unsigned int hasany:1; /* 1 if has ANY entry */
+ unsigned int pad:4; /* unused */
+ unsigned int mask:8; /* hash size - 1 */
+ unsigned int entries:16; /* number of children */
+} NTableRec, *NTable;
+
+/* the buckets are right after the structure */
+#define NodeBuckets(ne) ((NTable *)((ne) + 1))
+#define NodeHash(ne,q) NodeBuckets(ne)[(q) & (ne)->mask]
+
+/* leaf tables have an extra level of indirection for the buckets,
+ * so that resizing can be done without invalidating a search list.
+ * This is completely ugly, and wastes some memory, but the Xlib
+ * spec doesn't really specify whether invalidation is OK, and the
+ * old implementation did not invalidate.
+ */
+typedef struct _LTable {
+ NTableRec table;
+ VEntry *buckets;
+} LTableRec, *LTable;
+
+#define LeafHash(le,q) (le)->buckets[(q) & (le)->table.mask]
+
+/* An XrmDatabase just holds a pointer to the first top-level table.
+ * The type name is no longer descriptive, but better to not change
+ * the Xresource.h header file. This type also gets used to define
+ * XrmSearchList, which is a complete crock, but we'll just leave it
+ * and caste types as required.
+ */
+typedef struct _XrmHashBucketRec {
+ NTable table;
+ XPointer mbstate;
+ XrmMethods methods;
+#ifdef XTHREADS
+ LockInfoRec linfo;
+#endif
+} XrmHashBucketRec;
+
+/* closure used in get/put resource */
+typedef struct _VClosure {
+ XrmRepresentation *type; /* type of value */
+ XrmValuePtr value; /* value itself */
+} VClosureRec, *VClosure;
+
+/* closure used in get search list */
+typedef struct _SClosure {
+ LTable *list; /* search list */
+ int idx; /* index of last filled element */
+ int limit; /* maximum index */
+} SClosureRec, *SClosure;
+
+/* placed in XrmSearchList to indicate next table is loose only */
+#define LOOSESEARCH ((LTable)1)
+
+/* closure used in enumerate database */
+typedef struct _EClosure {
+ XrmDatabase db; /* the database */
+ DBEnumProc proc; /* the user proc */
+ XPointer closure; /* the user closure */
+ XrmBindingList bindings; /* binding list */
+ XrmQuarkList quarks; /* quark list */
+ int mode; /* XrmEnum<kind> */
+} EClosureRec, *EClosure;
+
+/* types for typecasting ETable based functions to NTable based functions */
+typedef Bool (*getNTableSProcp)(
+ NTable table,
+ XrmNameList names,
+ XrmClassList classes,
+ SClosure closure);
+typedef Bool (*getNTableVProcp)(
+ NTable table,
+ XrmNameList names,
+ XrmClassList classes,
+ VClosure closure);
+typedef Bool (*getNTableEProcp)(
+ NTable table,
+ XrmNameList names,
+ XrmClassList classes,
+ register int level,
+ EClosure closure);
+
+/* predicate to determine when to resize a hash table */
+#define GrowthPred(n,m) ((unsigned)(n) > (((m) + 1) << 2))
+
+#define GROW(prev) \
+ if (GrowthPred((*prev)->entries, (*prev)->mask)) \
+ GrowTable(prev)
+
+/* pick a reasonable value for maximum depth of resource database */
+#define MAXDBDEPTH 100
+
+/* macro used in get/search functions */
+
+/* find an entry named ename, with leafness given by leaf */
+#define NFIND(ename) \
+ q = ename; \
+ entry = NodeHash(table, q); \
+ while (entry && entry->name != q) \
+ entry = entry->next; \
+ if (leaf && entry && !entry->leaf) { \
+ entry = entry->next; \
+ if (entry && !entry->leaf) \
+ entry = entry->next; \
+ if (entry && entry->name != q) \
+ entry = (NTable)NULL; \
+ }
+
+/* resourceQuarks keeps track of what quarks have been associated with values
+ * in all LTables. If a quark has never been used in an LTable, we don't need
+ * to bother looking for it.
+ */
+
+static unsigned char *resourceQuarks = (unsigned char *)NULL;
+static XrmQuark maxResourceQuark = -1;
+
+/* determines if a quark has been used for a value in any database */
+#define IsResourceQuark(q) ((q) > 0 && (q) <= maxResourceQuark && \
+ resourceQuarks[(q) >> 3] & (1 << ((q) & 7)))
+
+typedef unsigned char XrmBits;
+
+#define BSLASH ((XrmBits) (1 << 5))
+#define NORMAL ((XrmBits) (1 << 4))
+#define EOQ ((XrmBits) (1 << 3))
+#define SEP ((XrmBits) (1 << 2))
+#define ENDOF ((XrmBits) (1 << 1))
+#define SPACE (NORMAL|EOQ|SEP|(XrmBits)0)
+#define RSEP (NORMAL|EOQ|SEP|(XrmBits)1)
+#define EOS (EOQ|SEP|ENDOF|(XrmBits)0)
+#define EOL (EOQ|SEP|ENDOF|(XrmBits)1)
+#define BINDING (NORMAL|EOQ)
+#define ODIGIT (NORMAL|(XrmBits)1)
+
+#define next_char(ch,str) xrmtypes[(unsigned char)((ch) = *(++(str)))]
+#define next_mbchar(ch,len,str) xrmtypes[(unsigned char)(ch = (*db->methods->mbchar)(db->mbstate, str, &len), str += len, ch)]
+
+#define is_space(bits) ((bits) == SPACE)
+#define is_EOQ(bits) ((bits) & EOQ)
+#define is_EOF(bits) ((bits) == EOS)
+#define is_EOL(bits) ((bits) & ENDOF)
+#define is_binding(bits) ((bits) == BINDING)
+#define is_odigit(bits) ((bits) == ODIGIT)
+#define is_separator(bits) ((bits) & SEP)
+#define is_nonpcs(bits) (!(bits))
+#define is_normal(bits) ((bits) & NORMAL)
+#define is_simple(bits) ((bits) & (NORMAL|BSLASH))
+#define is_special(bits) ((bits) & (ENDOF|BSLASH))
+
+/* parsing types */
+static XrmBits const xrmtypes[256] = {
+ EOS,0,0,0,0,0,0,0,
+ 0,SPACE,EOL,0,0,
+#if defined(WIN32)
+ EOL, /* treat CR the same as LF, just in case */
+#else
+ 0,
+#endif
+ 0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ SPACE,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,
+ NORMAL,NORMAL,BINDING,NORMAL,NORMAL,NORMAL,BINDING,NORMAL,
+ ODIGIT,ODIGIT,ODIGIT,ODIGIT,ODIGIT,ODIGIT,ODIGIT,ODIGIT,
+ NORMAL,NORMAL,RSEP,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,
+ NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,
+ NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,
+ NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,
+ NORMAL,NORMAL,NORMAL,NORMAL,BSLASH,NORMAL,NORMAL,NORMAL,
+ NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,
+ NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,
+ NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,
+ NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,0
+ /* The rest will be automatically initialized to zero. */
+};
+
+void XrmInitialize(void)
+{
+ XrmQString = XrmPermStringToQuark("String");
+ XrmQANY = XrmPermStringToQuark("?");
+}
+
+XrmDatabase XrmGetDatabase(
+ Display *display)
+{
+ XrmDatabase retval;
+ LockDisplay(display);
+ retval = display->db;
+ UnlockDisplay(display);
+ return retval;
+}
+
+void XrmSetDatabase(
+ Display *display,
+ XrmDatabase database)
+{
+ LockDisplay(display);
+ /* destroy database if set up implicitly by XGetDefault() */
+ if (display->db && (display->flags & XlibDisplayDfltRMDB)) {
+ XrmDestroyDatabase(display->db);
+ display->flags &= ~XlibDisplayDfltRMDB;
+ }
+ display->db = database;
+ UnlockDisplay(display);
+}
+
+void
+XrmStringToQuarkList(
+ register _Xconst char *name,
+ register XrmQuarkList quarks) /* RETURN */
+{
+ register XrmBits bits;
+ register Signature sig = 0;
+ register char ch, *tname;
+ register int i = 0;
+
+ if ((tname = (char *)name)) {
+ tname--;
+ while (!is_EOF(bits = next_char(ch, tname))) {
+ if (is_binding (bits)) {
+ if (i) {
+ /* Found a complete name */
+ *quarks++ = _XrmInternalStringToQuark(name,tname - name,
+ sig, False);
+ i = 0;
+ sig = 0;
+ }
+ name = tname+1;
+ }
+ else {
+ sig = (sig << 1) + ch; /* Compute the signature. */
+ i++;
+ }
+ }
+ *quarks++ = _XrmInternalStringToQuark(name, tname - name, sig, False);
+ }
+ *quarks = NULLQUARK;
+}
+
+void
+XrmStringToBindingQuarkList(
+ register _Xconst char *name,
+ register XrmBindingList bindings, /* RETURN */
+ register XrmQuarkList quarks) /* RETURN */
+{
+ register XrmBits bits;
+ register Signature sig = 0;
+ register char ch, *tname;
+ register XrmBinding binding;
+ register int i = 0;
+
+ if ((tname = (char *)name)) {
+ tname--;
+ binding = XrmBindTightly;
+ while (!is_EOF(bits = next_char(ch, tname))) {
+ if (is_binding (bits)) {
+ if (i) {
+ /* Found a complete name */
+ *bindings++ = binding;
+ *quarks++ = _XrmInternalStringToQuark(name, tname - name,
+ sig, False);
+
+ i = 0;
+ sig = 0;
+ binding = XrmBindTightly;
+ }
+ name = tname+1;
+
+ if (ch == '*')
+ binding = XrmBindLoosely;
+ }
+ else {
+ sig = (sig << 1) + ch; /* Compute the signature. */
+ i++;
+ }
+ }
+ *bindings = binding;
+ *quarks++ = _XrmInternalStringToQuark(name, tname - name, sig, False);
+ }
+ *quarks = NULLQUARK;
+}
+
+#ifdef DEBUG
+
+static void PrintQuarkList(
+ XrmQuarkList quarks,
+ FILE *stream)
+{
+ Bool firstNameSeen;
+
+ for (firstNameSeen = False; *quarks; quarks++) {
+ if (firstNameSeen) {
+ (void) fprintf(stream, ".");
+ }
+ firstNameSeen = True;
+ (void) fputs(XrmQuarkToString(*quarks), stream);
+ }
+} /* PrintQuarkList */
+
+#endif /* DEBUG */
+
+
+/*
+ * Fallback methods for Xrm parsing.
+ * Simulate a C locale. No state needed here.
+ */
+
+static void
+c_mbnoop(
+ XPointer state)
+{
+}
+
+static char
+c_mbchar(
+ XPointer state,
+ const char *str,
+ int *lenp)
+{
+ *lenp = 1;
+ return *str;
+}
+
+static const char *
+c_lcname(
+ XPointer state)
+{
+ return "C";
+}
+
+static const XrmMethodsRec mb_methods = {
+ c_mbnoop, /* mbinit */
+ c_mbchar, /* mbchar */
+ c_mbnoop, /* mbfinish */
+ c_lcname, /* lcname */
+ c_mbnoop /* destroy */
+};
+
+
+static XrmDatabase NewDatabase(void)
+{
+ register XrmDatabase db;
+
+ db = Xmalloc(sizeof(XrmHashBucketRec));
+ if (db) {
+ _XCreateMutex(&db->linfo);
+ db->table = (NTable)NULL;
+ db->mbstate = (XPointer)NULL;
+ db->methods = _XrmInitParseInfo(&db->mbstate);
+ if (!db->methods)
+ db->methods = &mb_methods;
+ }
+ return db;
+}
+
+/* move all values from ftable to ttable, and free ftable's buckets.
+ * ttable is guaranteed empty to start with.
+ */
+static void MoveValues(
+ LTable ftable,
+ register LTable ttable)
+{
+ register VEntry fentry, nfentry;
+ register VEntry *prev;
+ register VEntry *bucket;
+ register VEntry tentry;
+ register int i;
+
+ for (i = ftable->table.mask, bucket = ftable->buckets; i >= 0; i--) {
+ for (fentry = *bucket++; fentry; fentry = nfentry) {
+ prev = &LeafHash(ttable, fentry->name);
+ tentry = *prev;
+ *prev = fentry;
+ /* chain on all with same name, to preserve invariant order */
+ while ((nfentry = fentry->next) && nfentry->name == fentry->name)
+ fentry = nfentry;
+ fentry->next = tentry;
+ }
+ }
+ Xfree(ftable->buckets);
+}
+
+/* move all tables from ftable to ttable, and free ftable.
+ * ttable is quaranteed empty to start with.
+ */
+static void MoveTables(
+ NTable ftable,
+ register NTable ttable)
+{
+ register NTable fentry, nfentry;
+ register NTable *prev;
+ register NTable *bucket;
+ register NTable tentry;
+ register int i;
+
+ for (i = ftable->mask, bucket = NodeBuckets(ftable); i >= 0; i--) {
+ for (fentry = *bucket++; fentry; fentry = nfentry) {
+ prev = &NodeHash(ttable, fentry->name);
+ tentry = *prev;
+ *prev = fentry;
+ /* chain on all with same name, to preserve invariant order */
+ while ((nfentry = fentry->next) && nfentry->name == fentry->name)
+ fentry = nfentry;
+ fentry->next = tentry;
+ }
+ }
+ Xfree(ftable);
+}
+
+/* grow the table, based on current number of entries */
+static void GrowTable(
+ NTable *prev)
+{
+ register NTable table;
+ register int i;
+
+ table = *prev;
+ i = table->mask;
+ if (i == 255) /* biggest it gets */
+ return;
+ while (i < 255 && GrowthPred(table->entries, i))
+ i = (i << 1) + 1;
+ i++; /* i is now the new size */
+ if (table->leaf) {
+ register LTable ltable;
+ LTableRec otable;
+
+ ltable = (LTable)table;
+ /* cons up a copy to make MoveValues look symmetric */
+ otable = *ltable;
+ ltable->buckets = Xcalloc(i, sizeof(VEntry));
+ if (!ltable->buckets) {
+ ltable->buckets = otable.buckets;
+ return;
+ }
+ ltable->table.mask = i - 1;
+ MoveValues(&otable, ltable);
+ } else {
+ register NTable ntable;
+
+ ntable = Xcalloc(1, sizeof(NTableRec) + (i * sizeof(NTable)));
+ if (!ntable)
+ return;
+ *ntable = *table;
+ ntable->mask = i - 1;
+ *prev = ntable;
+ MoveTables(table, ntable);
+ }
+}
+
+/* merge values from ftable into *pprev, destroy ftable in the process */
+static void MergeValues(
+ LTable ftable,
+ NTable *pprev,
+ Bool override)
+{
+ register VEntry fentry, tentry;
+ register VEntry *prev;
+ register LTable ttable;
+ VEntry *bucket;
+ int i;
+ register XrmQuark q;
+
+ ttable = (LTable)*pprev;
+ if (ftable->table.hasloose)
+ ttable->table.hasloose = 1;
+ for (i = ftable->table.mask, bucket = ftable->buckets;
+ i >= 0;
+ i--, bucket++) {
+ for (fentry = *bucket; fentry; ) {
+ q = fentry->name;
+ prev = &LeafHash(ttable, q);
+ tentry = *prev;
+ while (tentry && tentry->name != q)
+ tentry = *(prev = &tentry->next);
+ /* note: test intentionally uses fentry->name instead of q */
+ /* permits serendipitous inserts */
+ while (tentry && tentry->name == fentry->name) {
+ /* if tentry is earlier, skip it */
+ if (!fentry->tight && tentry->tight) {
+ tentry = *(prev = &tentry->next);
+ continue;
+ }
+ if (fentry->tight != tentry->tight) {
+ /* no match, chain in fentry */
+ *prev = fentry;
+ prev = &fentry->next;
+ fentry = *prev;
+ *prev = tentry;
+ ttable->table.entries++;
+ } else if (override) {
+ /* match, chain in fentry, splice out and free tentry */
+ *prev = fentry;
+ prev = &fentry->next;
+ fentry = *prev;
+ *prev = tentry->next;
+ /* free the overridden entry */
+ Xfree(tentry);
+ /* get next tentry */
+ tentry = *prev;
+ } else {
+ /* match, discard fentry */
+ prev = &tentry->next;
+ tentry = fentry; /* use as a temp var */
+ fentry = fentry->next;
+ /* free the overpowered entry */
+ Xfree(tentry);
+ /* get next tentry */
+ tentry = *prev;
+ }
+ if (!fentry)
+ break;
+ }
+ /* at this point, tentry cannot match any fentry named q */
+ /* chain in all bindings together, preserve invariant order */
+ while (fentry && fentry->name == q) {
+ *prev = fentry;
+ prev = &fentry->next;
+ fentry = *prev;
+ *prev = tentry;
+ ttable->table.entries++;
+ }
+ }
+ }
+ Xfree(ftable->buckets);
+ Xfree(ftable);
+ /* resize if necessary, now that we're all done */
+ GROW(pprev);
+}
+
+/* merge tables from ftable into *pprev, destroy ftable in the process */
+static void MergeTables(
+ NTable ftable,
+ NTable *pprev,
+ Bool override)
+{
+ register NTable fentry, tentry;
+ NTable nfentry;
+ register NTable *prev;
+ register NTable ttable;
+ NTable *bucket;
+ int i;
+ register XrmQuark q;
+
+ ttable = *pprev;
+ if (ftable->hasloose)
+ ttable->hasloose = 1;
+ if (ftable->hasany)
+ ttable->hasany = 1;
+ for (i = ftable->mask, bucket = NodeBuckets(ftable);
+ i >= 0;
+ i--, bucket++) {
+ for (fentry = *bucket; fentry; ) {
+ q = fentry->name;
+ prev = &NodeHash(ttable, q);
+ tentry = *prev;
+ while (tentry && tentry->name != q)
+ tentry = *(prev = &tentry->next);
+ /* note: test intentionally uses fentry->name instead of q */
+ /* permits serendipitous inserts */
+ while (tentry && tentry->name == fentry->name) {
+ /* if tentry is earlier, skip it */
+ if ((fentry->leaf && !tentry->leaf) ||
+ (!fentry->tight && tentry->tight &&
+ (fentry->leaf || !tentry->leaf))) {
+ tentry = *(prev = &tentry->next);
+ continue;
+ }
+ nfentry = fentry->next;
+ if (fentry->leaf != tentry->leaf ||
+ fentry->tight != tentry->tight) {
+ /* no match, just chain in */
+ *prev = fentry;
+ *(prev = &fentry->next) = tentry;
+ ttable->entries++;
+ } else {
+ if (fentry->leaf)
+ MergeValues((LTable)fentry, prev, override);
+ else
+ MergeTables(fentry, prev, override);
+ /* bump to next tentry */
+ tentry = *(prev = &(*prev)->next);
+ }
+ /* bump to next fentry */
+ fentry = nfentry;
+ if (!fentry)
+ break;
+ }
+ /* at this point, tentry cannot match any fentry named q */
+ /* chain in all bindings together, preserve invariant order */
+ while (fentry && fentry->name == q) {
+ *prev = fentry;
+ prev = &fentry->next;
+ fentry = *prev;
+ *prev = tentry;
+ ttable->entries++;
+ }
+ }
+ }
+ Xfree(ftable);
+ /* resize if necessary, now that we're all done */
+ GROW(pprev);
+}
+
+void XrmCombineDatabase(
+ XrmDatabase from, XrmDatabase *into,
+ Bool override)
+{
+ register NTable *prev;
+ register NTable ftable, ttable, nftable;
+
+ if (!*into) {
+ *into = from;
+ } else if (from) {
+ _XLockMutex(&from->linfo);
+ _XLockMutex(&(*into)->linfo);
+ if ((ftable = from->table)) {
+ prev = &(*into)->table;
+ ttable = *prev;
+ if (!ftable->leaf) {
+ nftable = ftable->next;
+ if (ttable && !ttable->leaf) {
+ /* both have node tables, merge them */
+ MergeTables(ftable, prev, override);
+ /* bump to into's leaf table, if any */
+ ttable = *(prev = &(*prev)->next);
+ } else {
+ /* into has no node table, link from's in */
+ *prev = ftable;
+ *(prev = &ftable->next) = ttable;
+ }
+ /* bump to from's leaf table, if any */
+ ftable = nftable;
+ } else {
+ /* bump to into's leaf table, if any */
+ if (ttable && !ttable->leaf)
+ ttable = *(prev = &ttable->next);
+ }
+ if (ftable) {
+ /* if into has a leaf, merge, else insert */
+ if (ttable)
+ MergeValues((LTable)ftable, prev, override);
+ else
+ *prev = ftable;
+ }
+ }
+ (from->methods->destroy)(from->mbstate);
+ _XUnlockMutex(&from->linfo);
+ _XFreeMutex(&from->linfo);
+ Xfree(from);
+ _XUnlockMutex(&(*into)->linfo);
+ }
+}
+
+void XrmMergeDatabases(
+ XrmDatabase from, XrmDatabase *into)
+{
+ XrmCombineDatabase(from, into, True);
+}
+
+/* store a value in the database, overriding any existing entry */
+static void PutEntry(
+ XrmDatabase db,
+ XrmBindingList bindings,
+ XrmQuarkList quarks,
+ XrmRepresentation type,
+ XrmValuePtr value)
+{
+ register NTable *pprev, *prev;
+ register NTable table;
+ register XrmQuark q;
+ register VEntry *vprev;
+ register VEntry entry;
+ NTable *nprev, *firstpprev;
+
+#define NEWTABLE(q,i) \
+ table = Xmalloc(sizeof(LTableRec)); \
+ if (!table) \
+ return; \
+ table->name = q; \
+ table->hasloose = 0; \
+ table->hasany = 0; \
+ table->mask = 0; \
+ table->entries = 0; \
+ if (quarks[i]) { \
+ table->leaf = 0; \
+ nprev = NodeBuckets(table); \
+ } else { \
+ table->leaf = 1; \
+ if (!(nprev = Xmalloc(sizeof(VEntry *)))) {\
+ Xfree(table); \
+ return; \
+ } \
+ ((LTable)table)->buckets = (VEntry *)nprev; \
+ } \
+ *nprev = (NTable)NULL; \
+ table->next = *prev; \
+ *prev = table
+
+ if (!db || !*quarks)
+ return;
+ table = *(prev = &db->table);
+ /* if already at leaf, bump to the leaf table */
+ if (!quarks[1] && table && !table->leaf)
+ table = *(prev = &table->next);
+ pprev = prev;
+ if (!table || (quarks[1] && table->leaf)) {
+ /* no top-level node table, create one and chain it in */
+ NEWTABLE(NULLQUARK,1);
+ table->tight = 1; /* arbitrary */
+ prev = nprev;
+ } else {
+ /* search along until we need a value */
+ while (quarks[1]) {
+ q = *quarks;
+ table = *(prev = &NodeHash(table, q));
+ while (table && table->name != q)
+ table = *(prev = &table->next);
+ if (!table)
+ break; /* not found */
+ if (quarks[2]) {
+ if (table->leaf)
+ break; /* not found */
+ } else {
+ if (!table->leaf) {
+ /* bump to leaf table, if any */
+ table = *(prev = &table->next);
+ if (!table || table->name != q)
+ break; /* not found */
+ if (!table->leaf) {
+ /* bump to leaf table, if any */
+ table = *(prev = &table->next);
+ if (!table || table->name != q)
+ break; /* not found */
+ }
+ }
+ }
+ if (*bindings == XrmBindTightly) {
+ if (!table->tight)
+ break; /* not found */
+ } else {
+ if (table->tight) {
+ /* bump to loose table, if any */
+ table = *(prev = &table->next);
+ if (!table || table->name != q ||
+ !quarks[2] != table->leaf)
+ break; /* not found */
+ }
+ }
+ /* found that one, bump to next quark */
+ pprev = prev;
+ quarks++;
+ bindings++;
+ }
+ if (!quarks[1]) {
+ /* found all the way to a leaf */
+ q = *quarks;
+ entry = *(vprev = &LeafHash((LTable)table, q));
+ while (entry && entry->name != q)
+ entry = *(vprev = &entry->next);
+ /* if want loose and have tight, bump to next entry */
+ if (entry && *bindings == XrmBindLoosely && entry->tight)
+ entry = *(vprev = &entry->next);
+ if (entry && entry->name == q &&
+ (*bindings == XrmBindTightly) == entry->tight) {
+ /* match, need to override */
+ if ((type == XrmQString) == entry->string &&
+ entry->size == value->size) {
+ /* update type if not String, can be different */
+ if (!entry->string)
+ RepType(entry) = type;
+ /* identical size, just overwrite value */
+ memcpy(RawValue(entry), (char *)value->addr, value->size);
+ return;
+ }
+ /* splice out and free old entry */
+ *vprev = entry->next;
+ Xfree(entry);
+ (*pprev)->entries--;
+ }
+ /* this is where to insert */
+ prev = (NTable *)vprev;
+ }
+ }
+ /* keep the top table, because we may have to grow it */
+ firstpprev = pprev;
+ /* iterate until we get to the leaf */
+ while (quarks[1]) {
+ /* build a new table and chain it in */
+ NEWTABLE(*quarks,2);
+ if (*quarks++ == XrmQANY)
+ (*pprev)->hasany = 1;
+ if (*bindings++ == XrmBindTightly) {
+ table->tight = 1;
+ } else {
+ table->tight = 0;
+ (*pprev)->hasloose = 1;
+ }
+ (*pprev)->entries++;
+ pprev = prev;
+ prev = nprev;
+ }
+ /* now allocate the value entry */
+ entry = Xmalloc(((type == XrmQString) ?
+ sizeof(VEntryRec) : sizeof(DEntryRec)) + value->size);
+ if (!entry)
+ return;
+ entry->name = q = *quarks;
+ if (*bindings == XrmBindTightly) {
+ entry->tight = 1;
+ } else {
+ entry->tight = 0;
+ (*pprev)->hasloose = 1;
+ }
+ /* chain it in, with a bit of type cast ugliness */
+ entry->next = *((VEntry *)prev);
+ *((VEntry *)prev) = entry;
+ entry->size = value->size;
+ if (type == XrmQString) {
+ entry->string = 1;
+ } else {
+ entry->string = 0;
+ RepType(entry) = type;
+ }
+ /* save a copy of the value */
+ memcpy(RawValue(entry), (char *)value->addr, value->size);
+ (*pprev)->entries++;
+ /* this is a new leaf, need to remember it for search lists */
+ if (q > maxResourceQuark) {
+ unsigned oldsize = (maxResourceQuark + 1) >> 3;
+ unsigned size = ((q | 0x7f) + 1) >> 3; /* reallocate in chunks */
+ if (resourceQuarks) {
+ unsigned char *prevQuarks = resourceQuarks;
+
+ resourceQuarks = Xrealloc(resourceQuarks, size);
+ if (!resourceQuarks) {
+ Xfree(prevQuarks);
+ }
+ } else
+ resourceQuarks = Xmalloc(size);
+ if (resourceQuarks) {
+ bzero((char *)&resourceQuarks[oldsize], size - oldsize);
+ maxResourceQuark = (size << 3) - 1;
+ } else {
+ maxResourceQuark = -1;
+ }
+ }
+ if (q > 0 && resourceQuarks)
+ resourceQuarks[q >> 3] |= 1 << (q & 0x7);
+ GROW(firstpprev);
+
+#undef NEWTABLE
+}
+
+void XrmQPutResource(
+ XrmDatabase *pdb,
+ XrmBindingList bindings,
+ XrmQuarkList quarks,
+ XrmRepresentation type,
+ XrmValuePtr value)
+{
+ if (!*pdb) *pdb = NewDatabase();
+ _XLockMutex(&(*pdb)->linfo);
+ PutEntry(*pdb, bindings, quarks, type, value);
+ _XUnlockMutex(&(*pdb)->linfo);
+}
+
+void
+XrmPutResource(
+ XrmDatabase *pdb,
+ _Xconst char *specifier,
+ _Xconst char *type,
+ XrmValuePtr value)
+{
+ XrmBinding bindings[MAXDBDEPTH+1];
+ XrmQuark quarks[MAXDBDEPTH+1];
+
+ if (!*pdb) *pdb = NewDatabase();
+ _XLockMutex(&(*pdb)->linfo);
+ XrmStringToBindingQuarkList(specifier, bindings, quarks);
+ PutEntry(*pdb, bindings, quarks, XrmStringToQuark(type), value);
+ _XUnlockMutex(&(*pdb)->linfo);
+}
+
+void
+XrmQPutStringResource(
+ XrmDatabase *pdb,
+ XrmBindingList bindings,
+ XrmQuarkList quarks,
+ _Xconst char *str)
+{
+ XrmValue value;
+
+ if (!*pdb) *pdb = NewDatabase();
+ value.addr = (XPointer) str;
+ value.size = strlen(str)+1;
+ _XLockMutex(&(*pdb)->linfo);
+ PutEntry(*pdb, bindings, quarks, XrmQString, &value);
+ _XUnlockMutex(&(*pdb)->linfo);
+}
+
+/* Function Name: GetDatabase
+ * Description: Parses a string and stores it as a database.
+ * Arguments: db - the database.
+ * str - a pointer to the string containing the database.
+ * filename - source filename, if any.
+ * doall - whether to do all lines or just one
+ */
+
+/*
+ * This function is highly optimized to inline as much as possible.
+ * Be very careful with modifications, or simplifications, as they
+ * may adversely affect the performance.
+ *
+ * Chris Peterson, MIT X Consortium 5/17/90.
+ */
+
+/*
+ * Xlib spec says max 100 quarks in a lookup, will stop and return if
+ * return if any single production's lhs has more than 100 components.
+ */
+#define QLIST_SIZE 100
+
+/*
+ * This should be big enough to handle things like the XKeysymDB or biggish
+ * ~/.Xdefaults or app-defaults files. Anything bigger will be allocated on
+ * the heap.
+ */
+#define DEF_BUFF_SIZE 8192
+
+static void GetIncludeFile(
+ XrmDatabase db,
+ _Xconst char *base,
+ _Xconst char *fname,
+ int fnamelen,
+ int depth);
+
+static void GetDatabase(
+ XrmDatabase db,
+ _Xconst char *str,
+ _Xconst char *filename,
+ Bool doall,
+ int depth)
+{
+ char *rhs;
+ char *lhs, lhs_s[DEF_BUFF_SIZE];
+ XrmQuark quarks[QLIST_SIZE + 1]; /* allow for a terminal NullQuark */
+ XrmBinding bindings[QLIST_SIZE + 1];
+
+ register char *ptr;
+ register XrmBits bits = 0;
+ register char c;
+ register Signature sig;
+ register char *ptr_max;
+ register int num_quarks;
+ register XrmBindingList t_bindings;
+
+ int len, alloc_chars;
+ unsigned long str_len;
+ XrmValue value;
+ Bool only_pcs;
+ Bool dolines;
+
+ if (!db)
+ return;
+
+ /*
+ * if strlen (str) < DEF_BUFF_SIZE allocate buffers on the stack for
+ * speed otherwise malloc the buffer. From a buffer overflow standpoint
+ * we can be sure that neither: a) a component on the lhs, or b) a
+ * value on the rhs, will be longer than the overall length of str,
+ * i.e. strlen(str).
+ *
+ * This should give good performance when parsing "*foo: bar" type
+ * databases as might be passed with -xrm command line options; but
+ * with larger databases, e.g. .Xdefaults, app-defaults, or KeysymDB
+ * files, the size of the buffers will be overly large. One way
+ * around this would be to double-parse each production with a resulting
+ * performance hit. In any event we can be assured that a lhs component
+ * name or a rhs value won't be longer than str itself.
+ */
+
+ str_len = strlen (str);
+ if (DEF_BUFF_SIZE > str_len) lhs = lhs_s;
+ else if ((lhs = Xmalloc (str_len)) == NULL)
+ return;
+
+ alloc_chars = DEF_BUFF_SIZE < str_len ? str_len : DEF_BUFF_SIZE;
+ if ((rhs = Xmalloc (alloc_chars)) == NULL) {
+ if (lhs != lhs_s) Xfree (lhs);
+ return;
+ }
+
+ (*db->methods->mbinit)(db->mbstate);
+ str--;
+ dolines = True;
+ while (!is_EOF(bits) && dolines) {
+ dolines = doall;
+
+ /*
+ * First: Remove extra whitespace.
+ */
+
+ do {
+ bits = next_char(c, str);
+ } while is_space(bits);
+
+ /*
+ * Ignore empty lines.
+ */
+
+ if (is_EOL(bits))
+ continue; /* start a new line. */
+
+ /*
+ * Second: check the first character in a line to see if it is
+ * "!" signifying a comment, or "#" signifying a directive.
+ */
+
+ if (c == '!') { /* Comment, spin to next newline */
+ while (is_simple(bits = next_char(c, str))) {}
+ if (is_EOL(bits))
+ continue;
+ while (!is_EOL(bits = next_mbchar(c, len, str))) {}
+ str--;
+ continue; /* start a new line. */
+ }
+
+ if (c == '#') { /* Directive */
+ /* remove extra whitespace */
+ only_pcs = True;
+ while (is_space(bits = next_char(c, str))) {};
+ /* only "include" directive is currently defined */
+ if (!strncmp(str, "include", 7)) {
+ str += (7-1);
+ /* remove extra whitespace */
+ while (is_space(bits = next_char(c, str))) {};
+ /* must have a starting " */
+ if (c == '"') {
+ _Xconst char *fname = str+1;
+ len = 0;
+ do {
+ if (only_pcs) {
+ bits = next_char(c, str);
+ if (is_nonpcs(bits))
+ only_pcs = False;
+ }
+ if (!only_pcs)
+ bits = next_mbchar(c, len, str);
+ } while (c != '"' && !is_EOL(bits));
+ /* must have an ending " */
+ if (c == '"')
+ GetIncludeFile(db, filename, fname, str - len - fname,
+ depth);
+ }
+ }
+ /* spin to next newline */
+ if (only_pcs) {
+ while (is_simple(bits))
+ bits = next_char(c, str);
+ if (is_EOL(bits))
+ continue;
+ }
+ while (!is_EOL(bits))
+ bits = next_mbchar(c, len, str);
+ str--;
+ continue; /* start a new line. */
+ }
+
+ /*
+ * Third: loop through the LHS of the resource specification
+ * storing characters and converting this to a Quark.
+ */
+
+ num_quarks = 0;
+ t_bindings = bindings;
+
+ sig = 0;
+ ptr = lhs;
+ *t_bindings = XrmBindTightly;
+ for(;;) {
+ if (!is_binding(bits)) {
+ while (!is_EOQ(bits)) {
+ *ptr++ = c;
+ sig = (sig << 1) + c; /* Compute the signature. */
+ bits = next_char(c, str);
+ }
+
+ quarks[num_quarks++] =
+ _XrmInternalStringToQuark(lhs, ptr - lhs, sig, False);
+
+ if (num_quarks > QLIST_SIZE) {
+ Xfree(rhs);
+ if (lhs != lhs_s) Xfree (lhs);
+ (*db->methods->mbfinish)(db->mbstate);
+ return;
+ }
+
+ if (is_separator(bits)) {
+ if (!is_space(bits))
+ break;
+
+ /* Remove white space */
+ do {
+ *ptr++ = c;
+ sig = (sig << 1) + c; /* Compute the signature. */
+ } while (is_space(bits = next_char(c, str)));
+
+ /*
+ * The spec doesn't permit it, but support spaces
+ * internal to resource name/class
+ */
+
+ if (is_separator(bits))
+ break;
+ num_quarks--;
+ continue;
+ }
+
+ if (c == '.')
+ *(++t_bindings) = XrmBindTightly;
+ else
+ *(++t_bindings) = XrmBindLoosely;
+
+ sig = 0;
+ ptr = lhs;
+ }
+ else {
+ /*
+ * Magic unspecified feature #254.
+ *
+ * If two separators appear with no Text between them then
+ * ignore them.
+ *
+ * If anyone of those separators is a '*' then the binding
+ * will be loose, otherwise it will be tight.
+ */
+
+ if (c == '*')
+ *t_bindings = XrmBindLoosely;
+ }
+
+ bits = next_char(c, str);
+ }
+
+ quarks[num_quarks] = NULLQUARK;
+
+ /*
+ * Make sure that there is a ':' in this line.
+ */
+
+ if (c != ':') {
+ char oldc;
+
+ /*
+ * A parsing error has occured, toss everything on the line
+ * a new_line can still be escaped with a '\'.
+ */
+
+ while (is_normal(bits))
+ bits = next_char(c, str);
+ if (is_EOL(bits))
+ continue;
+ bits = next_mbchar(c, len, str);
+ do {
+ oldc = c;
+ bits = next_mbchar(c, len, str);
+ } while (c && (c != '\n' || oldc == '\\'));
+ str--;
+ continue;
+ }
+
+ /*
+ * I now have a quark and binding list for the entire left hand
+ * side. "c" currently points to the ":" separating the left hand
+ * side for the right hand side. It is time to begin processing
+ * the right hand side.
+ */
+
+ /*
+ * Fourth: Remove more whitespace
+ */
+
+ for(;;) {
+ if (is_space(bits = next_char(c, str)))
+ continue;
+ if (c != '\\')
+ break;
+ bits = next_char(c, str);
+ if (c == '\n')
+ continue;
+ str--;
+ bits = BSLASH;
+ c = '\\';
+ break;
+ }
+
+ /*
+ * Fifth: Process the right hand side.
+ */
+
+ ptr = rhs;
+ ptr_max = ptr + alloc_chars - 4;
+ only_pcs = True;
+ len = 1;
+
+ for(;;) {
+
+ /*
+ * Tight loop for the normal case: Non backslash, non-end of value
+ * character that will fit into the allocated buffer.
+ */
+
+ if (only_pcs) {
+ while (is_normal(bits) && ptr < ptr_max) {
+ *ptr++ = c;
+ bits = next_char(c, str);
+ }
+ if (is_EOL(bits))
+ break;
+ if (is_nonpcs(bits)) {
+ only_pcs = False;
+ bits = next_mbchar(c, len, str);
+ }
+ }
+ while (!is_special(bits) && ptr + len <= ptr_max) {
+ len = -len;
+ while (len)
+ *ptr++ = str[len++];
+ if (*str == '\0') {
+ bits = EOS;
+ break;
+ }
+ bits = next_mbchar(c, len, str);
+ }
+
+ if (is_EOL(bits)) {
+ str--;
+ break;
+ }
+
+ if (c == '\\') {
+ /*
+ * We need to do some magic after a backslash.
+ */
+ Bool read_next = True;
+
+ if (only_pcs) {
+ bits = next_char(c, str);
+ if (is_nonpcs(bits))
+ only_pcs = False;
+ }
+ if (!only_pcs)
+ bits = next_mbchar(c, len, str);
+
+ if (is_EOL(bits)) {
+ if (is_EOF(bits))
+ continue;
+ } else if (c == 'n') {
+ /*
+ * "\n" means insert a newline.
+ */
+ *ptr++ = '\n';
+ } else if (c == '\\') {
+ /*
+ * "\\" completes to just one backslash.
+ */
+ *ptr++ = '\\';
+ } else {
+ /*
+ * pick up to three octal digits after the '\'.
+ */
+ char temp[3];
+ int count = 0;
+ while (is_odigit(bits) && count < 3) {
+ temp[count++] = c;
+ if (only_pcs) {
+ bits = next_char(c, str);
+ if (is_nonpcs(bits))
+ only_pcs = False;
+ }
+ if (!only_pcs)
+ bits = next_mbchar(c, len, str);
+ }
+
+ /*
+ * If we found three digits then insert that octal code
+ * into the value string as a character.
+ */
+
+ if (count == 3) {
+ *ptr++ = (unsigned char) ((temp[0] - '0') * 0100 +
+ (temp[1] - '0') * 010 +
+ (temp[2] - '0'));
+ }
+ else {
+ int tcount;
+
+ /*
+ * Otherwise just insert those characters into the
+ * string, since no special processing is needed on
+ * numerics we can skip the special processing.
+ */
+
+ for (tcount = 0; tcount < count; tcount++) {
+ *ptr++ = temp[tcount]; /* print them in
+ the correct order */
+ }
+ }
+ read_next = False;
+ }
+ if (read_next) {
+ if (only_pcs) {
+ bits = next_char(c, str);
+ if (is_nonpcs(bits))
+ only_pcs = False;
+ }
+ if (!only_pcs)
+ bits = next_mbchar(c, len, str);
+ }
+ }
+
+ /*
+ * It is important to make sure that there is room for at least
+ * four more characters in the buffer, since I can add that
+ * many characters into the buffer after a backslash has occured.
+ */
+
+ if (ptr + len > ptr_max) {
+ char * temp_str;
+
+ alloc_chars += BUFSIZ/10;
+ temp_str = Xrealloc(rhs, sizeof(char) * alloc_chars);
+
+ if (!temp_str) {
+ Xfree(rhs);
+ if (lhs != lhs_s) Xfree (lhs);
+ (*db->methods->mbfinish)(db->mbstate);
+ return;
+ }
+
+ ptr = temp_str + (ptr - rhs); /* reset pointer. */
+ rhs = temp_str;
+ ptr_max = rhs + alloc_chars - 4;
+ }
+ }
+
+ /*
+ * Lastly: Terminate the value string, and store this entry
+ * into the database.
+ */
+
+ *ptr++ = '\0';
+
+ /* Store it in database */
+ value.size = ptr - rhs;
+ value.addr = (XPointer) rhs;
+
+ PutEntry(db, bindings, quarks, XrmQString, &value);
+ }
+
+ if (lhs != lhs_s) Xfree (lhs);
+ Xfree (rhs);
+
+ (*db->methods->mbfinish)(db->mbstate);
+}
+
+void
+XrmPutStringResource(
+ XrmDatabase *pdb,
+ _Xconst char*specifier,
+ _Xconst char*str)
+{
+ XrmValue value;
+ XrmBinding bindings[MAXDBDEPTH+1];
+ XrmQuark quarks[MAXDBDEPTH+1];
+
+ if (!*pdb) *pdb = NewDatabase();
+ XrmStringToBindingQuarkList(specifier, bindings, quarks);
+ value.addr = (XPointer) str;
+ value.size = strlen(str)+1;
+ _XLockMutex(&(*pdb)->linfo);
+ PutEntry(*pdb, bindings, quarks, XrmQString, &value);
+ _XUnlockMutex(&(*pdb)->linfo);
+}
+
+
+void
+XrmPutLineResource(
+ XrmDatabase *pdb,
+ _Xconst char*line)
+{
+ if (!*pdb) *pdb = NewDatabase();
+ _XLockMutex(&(*pdb)->linfo);
+ GetDatabase(*pdb, line, (char *)NULL, False, 0);
+ _XUnlockMutex(&(*pdb)->linfo);
+}
+
+XrmDatabase
+XrmGetStringDatabase(
+ _Xconst char *data)
+{
+ XrmDatabase db;
+
+ db = NewDatabase();
+ _XLockMutex(&db->linfo);
+ GetDatabase(db, data, (char *)NULL, True, 0);
+ _XUnlockMutex(&db->linfo);
+ return db;
+}
+
+/* Function Name: ReadInFile
+ * Description: Reads the file into a buffer.
+ * Arguments: filename - the name of the file.
+ * Returns: An allocated string containing the contents of the file.
+ */
+
+static char *
+ReadInFile(_Xconst char *filename)
+{
+ register int fd, size;
+ char * filebuf;
+
+ /*
+ * MS-Windows and OS/2 note: Default open mode includes O_TEXT
+ */
+ if ( (fd = _XOpenFile (filename, O_RDONLY)) == -1 )
+ return (char *)NULL;
+
+ /*
+ * MS-Windows and OS/2 note: depending on how the sources are
+ * untarred, the newlines in resource files may or may not have
+ * been expanded to CRLF. Either way the size returned by fstat
+ * is sufficient to read the file into because in text-mode any
+ * CRLFs in a file will be converted to newlines (LF) with the
+ * result that the number of bytes actually read with be <=
+ * to the size returned by fstat.
+ */
+ {
+ struct stat status_buffer;
+ if ( ((fstat(fd, &status_buffer)) == -1 ) ||
+ (status_buffer.st_size >= INT_MAX) ) {
+ close (fd);
+ return (char *)NULL;
+ } else
+ size = (int) status_buffer.st_size;
+ }
+
+ if (!(filebuf = Xmalloc(size + 1))) { /* leave room for '\0' */
+ close(fd);
+ return (char *)NULL;
+ }
+ size = read (fd, filebuf, size);
+
+ if (size < 0) {
+ close (fd);
+ Xfree(filebuf);
+ return (char *)NULL;
+ }
+ close (fd);
+
+ filebuf[size] = '\0'; /* NULL terminate it. */
+ return filebuf;
+}
+
+static void
+GetIncludeFile(
+ XrmDatabase db,
+ _Xconst char *base,
+ _Xconst char *fname,
+ int fnamelen,
+ int depth)
+{
+ int len;
+ char *str;
+ char realfname[BUFSIZ];
+
+ if (fnamelen <= 0 || fnamelen >= BUFSIZ)
+ return;
+ if (depth >= MAXDBDEPTH)
+ return;
+ if (*fname != '/' && base && (str = strrchr(base, '/'))) {
+ len = str - base + 1;
+ if (len + fnamelen >= BUFSIZ)
+ return;
+ strncpy(realfname, base, len);
+ strncpy(realfname + len, fname, fnamelen);
+ realfname[len + fnamelen] = '\0';
+ } else {
+ strncpy(realfname, fname, fnamelen);
+ realfname[fnamelen] = '\0';
+ }
+ if (!(str = ReadInFile(realfname)))
+ return;
+ GetDatabase(db, str, realfname, True, depth + 1);
+ Xfree(str);
+}
+
+XrmDatabase
+XrmGetFileDatabase(
+ _Xconst char *filename)
+{
+ XrmDatabase db;
+ char *str;
+
+ if (!(str = ReadInFile(filename)))
+ return (XrmDatabase)NULL;
+
+ db = NewDatabase();
+ _XLockMutex(&db->linfo);
+ GetDatabase(db, str, filename, True, 0);
+ _XUnlockMutex(&db->linfo);
+ Xfree(str);
+ return db;
+}
+
+Status
+XrmCombineFileDatabase(
+ _Xconst char *filename,
+ XrmDatabase *target,
+ Bool override)
+{
+ XrmDatabase db;
+ char *str;
+
+ if (!(str = ReadInFile(filename)))
+ return 0;
+ if (override) {
+ db = *target;
+ if (!db)
+ *target = db = NewDatabase();
+ } else
+ db = NewDatabase();
+ _XLockMutex(&db->linfo);
+ GetDatabase(db, str, filename, True, 0);
+ _XUnlockMutex(&db->linfo);
+ Xfree(str);
+ if (!override)
+ XrmCombineDatabase(db, target, False);
+ return 1;
+}
+
+/* call the user proc for every value in the table, arbitrary order.
+ * stop if user proc returns True. level is current depth in database.
+ */
+/*ARGSUSED*/
+static Bool EnumLTable(
+ LTable table,
+ XrmNameList names,
+ XrmClassList classes,
+ register int level,
+ register EClosure closure)
+{
+ register VEntry *bucket;
+ register int i;
+ register VEntry entry;
+ XrmValue value;
+ XrmRepresentation type;
+ Bool tightOk;
+
+ closure->bindings[level] = (table->table.tight ?
+ XrmBindTightly : XrmBindLoosely);
+ closure->quarks[level] = table->table.name;
+ level++;
+ tightOk = !*names;
+ closure->quarks[level + 1] = NULLQUARK;
+ for (i = table->table.mask, bucket = table->buckets;
+ i >= 0;
+ i--, bucket++) {
+ for (entry = *bucket; entry; entry = entry->next) {
+ if (entry->tight && !tightOk)
+ continue;
+ closure->bindings[level] = (entry->tight ?
+ XrmBindTightly : XrmBindLoosely);
+ closure->quarks[level] = entry->name;
+ value.size = entry->size;
+ if (entry->string) {
+ type = XrmQString;
+ value.addr = StringValue(entry);
+ } else {
+ type = RepType(entry);
+ value.addr = DataValue(entry);
+ }
+ if ((*closure->proc)(&closure->db, closure->bindings+1,
+ closure->quarks+1, &type, &value,
+ closure->closure))
+ return True;
+ }
+ }
+ return False;
+}
+
+static Bool EnumAllNTable(
+ NTable table,
+ register int level,
+ register EClosure closure)
+{
+ register NTable *bucket;
+ register int i;
+ register NTable entry;
+ XrmQuark empty = NULLQUARK;
+
+ if (level >= MAXDBDEPTH)
+ return False;
+ for (i = table->mask, bucket = NodeBuckets(table);
+ i >= 0;
+ i--, bucket++) {
+ for (entry = *bucket; entry; entry = entry->next) {
+ if (entry->leaf) {
+ if (EnumLTable((LTable)entry, &empty, &empty, level, closure))
+ return True;
+ } else {
+ closure->bindings[level] = (entry->tight ?
+ XrmBindTightly : XrmBindLoosely);
+ closure->quarks[level] = entry->name;
+ if (EnumAllNTable(entry, level+1, closure))
+ return True;
+ }
+ }
+ }
+ return False;
+}
+
+/* recurse on every table in the table, arbitrary order.
+ * stop if user proc returns True. level is current depth in database.
+ */
+static Bool EnumNTable(
+ NTable table,
+ XrmNameList names,
+ XrmClassList classes,
+ register int level,
+ register EClosure closure)
+{
+ register NTable entry;
+ register XrmQuark q;
+ register unsigned int leaf;
+ Bool (*get)(
+ NTable table,
+ XrmNameList names,
+ XrmClassList classes,
+ register int level,
+ EClosure closure);
+ Bool bilevel;
+
+/* find entries named ename, leafness leaf, tight or loose, and call get */
+#define ITIGHTLOOSE(ename) \
+ NFIND(ename); \
+ if (entry) { \
+ if (leaf == entry->leaf) { \
+ if (!leaf && !entry->tight && entry->next && \
+ entry->next->name == q && entry->next->tight && \
+ (bilevel || entry->next->hasloose) && \
+ EnumLTable((LTable)entry->next, names+1, classes+1, \
+ level, closure)) \
+ return True; \
+ if ((*get)(entry, names+1, classes+1, level, closure)) \
+ return True; \
+ if (entry->tight && (entry = entry->next) && \
+ entry->name == q && leaf == entry->leaf && \
+ (*get)(entry, names+1, classes+1, level, closure)) \
+ return True; \
+ } else if (entry->leaf) { \
+ if ((bilevel || entry->hasloose) && \
+ EnumLTable((LTable)entry, names+1, classes+1, level, closure))\
+ return True; \
+ if (entry->tight && (entry = entry->next) && \
+ entry->name == q && (bilevel || entry->hasloose) && \
+ EnumLTable((LTable)entry, names+1, classes+1, level, closure))\
+ return True; \
+ } \
+ }
+
+/* find entries named ename, leafness leaf, loose only, and call get */
+#define ILOOSE(ename) \
+ NFIND(ename); \
+ if (entry && entry->tight && (entry = entry->next) && entry->name != q) \
+ entry = (NTable)NULL; \
+ if (entry) { \
+ if (leaf == entry->leaf) { \
+ if ((*get)(entry, names+1, classes+1, level, closure)) \
+ return True; \
+ } else if (entry->leaf && (bilevel || entry->hasloose)) { \
+ if (EnumLTable((LTable)entry, names+1, classes+1, level, closure))\
+ return True; \
+ } \
+ }
+
+ if (level >= MAXDBDEPTH)
+ return False;
+ closure->bindings[level] = (table->tight ?
+ XrmBindTightly : XrmBindLoosely);
+ closure->quarks[level] = table->name;
+ level++;
+ if (!*names) {
+ if (EnumAllNTable(table, level, closure))
+ return True;
+ } else {
+ if (names[1] || closure->mode == XrmEnumAllLevels) {
+ get = EnumNTable; /* recurse */
+ leaf = 0;
+ bilevel = !names[1];
+ } else {
+ get = (getNTableEProcp)EnumLTable; /* bottom of recursion */
+ leaf = 1;
+ bilevel = False;
+ }
+ if (table->hasloose && closure->mode == XrmEnumAllLevels) {
+ NTable *bucket;
+ int i;
+ XrmQuark empty = NULLQUARK;
+
+ for (i = table->mask, bucket = NodeBuckets(table);
+ i >= 0;
+ i--, bucket++) {
+ q = NULLQUARK;
+ for (entry = *bucket; entry; entry = entry->next) {
+ if (!entry->tight && entry->name != q &&
+ entry->name != *names && entry->name != *classes) {
+ q = entry->name;
+ if (entry->leaf) {
+ if (EnumLTable((LTable)entry, &empty, &empty,
+ level, closure))
+ return True;
+ } else {
+ if (EnumNTable(entry, &empty, &empty,
+ level, closure))
+ return True;
+ }
+ }
+ }
+ }
+ }
+
+ ITIGHTLOOSE(*names); /* do name, tight and loose */
+ ITIGHTLOOSE(*classes); /* do class, tight and loose */
+ if (table->hasany) {
+ ITIGHTLOOSE(XrmQANY); /* do ANY, tight and loose */
+ }
+ if (table->hasloose) {
+ while (1) {
+ names++;
+ classes++;
+ if (!*names)
+ break;
+ if (!names[1] && closure->mode != XrmEnumAllLevels) {
+ get = (getNTableEProcp)EnumLTable; /* bottom of recursion */
+ leaf = 1;
+ }
+ ILOOSE(*names); /* loose names */
+ ILOOSE(*classes); /* loose classes */
+ if (table->hasany) {
+ ILOOSE(XrmQANY); /* loose ANY */
+ }
+ }
+ names--;
+ classes--;
+ }
+ }
+ /* now look for matching leaf nodes */
+ entry = table->next;
+ if (!entry)
+ return False;
+ if (entry->leaf) {
+ if (entry->tight && !table->tight)
+ entry = entry->next;
+ } else {
+ entry = entry->next;
+ if (!entry || !entry->tight)
+ return False;
+ }
+ if (!entry || entry->name != table->name)
+ return False;
+ /* found one */
+ level--;
+ if ((!*names || entry->hasloose) &&
+ EnumLTable((LTable)entry, names, classes, level, closure))
+ return True;
+ if (entry->tight && entry == table->next && (entry = entry->next) &&
+ entry->name == table->name && (!*names || entry->hasloose))
+ return EnumLTable((LTable)entry, names, classes, level, closure);
+ return False;
+
+#undef ITIGHTLOOSE
+#undef ILOOSE
+}
+
+/* call the proc for every value in the database, arbitrary order.
+ * stop if the proc returns True.
+ */
+Bool XrmEnumerateDatabase(
+ XrmDatabase db,
+ XrmNameList names,
+ XrmClassList classes,
+ int mode,
+ DBEnumProc proc,
+ XPointer closure)
+{
+ XrmBinding bindings[MAXDBDEPTH+2];
+ XrmQuark quarks[MAXDBDEPTH+2];
+ register NTable table;
+ EClosureRec eclosure;
+ Bool retval = False;
+
+ if (!db)
+ return False;
+ _XLockMutex(&db->linfo);
+ eclosure.db = db;
+ eclosure.proc = proc;
+ eclosure.closure = closure;
+ eclosure.bindings = bindings;
+ eclosure.quarks = quarks;
+ eclosure.mode = mode;
+ table = db->table;
+ if (table && !table->leaf && !*names && mode == XrmEnumOneLevel)
+ table = table->next;
+ if (table) {
+ if (!table->leaf)
+ retval = EnumNTable(table, names, classes, 0, &eclosure);
+ else
+ retval = EnumLTable((LTable)table, names, classes, 0, &eclosure);
+ }
+ _XUnlockMutex(&db->linfo);
+ return retval;
+}
+
+static void PrintBindingQuarkList(
+ XrmBindingList bindings,
+ XrmQuarkList quarks,
+ FILE *stream)
+{
+ Bool firstNameSeen;
+
+ for (firstNameSeen = False; *quarks; bindings++, quarks++) {
+ if (*bindings == XrmBindLoosely) {
+ (void) fprintf(stream, "*");
+ } else if (firstNameSeen) {
+ (void) fprintf(stream, ".");
+ }
+ firstNameSeen = True;
+ (void) fputs(XrmQuarkToString(*quarks), stream);
+ }
+}
+
+/* output out the entry in correct file syntax */
+/*ARGSUSED*/
+static Bool DumpEntry(
+ XrmDatabase *db,
+ XrmBindingList bindings,
+ XrmQuarkList quarks,
+ XrmRepresentation *type,
+ XrmValuePtr value,
+ XPointer data)
+{
+ FILE *stream = (FILE *)data;
+ register unsigned int i;
+ register char *s;
+ register char c;
+
+ if (*type != XrmQString)
+ (void) putc('!', stream);
+ PrintBindingQuarkList(bindings, quarks, stream);
+ s = value->addr;
+ i = value->size;
+ if (*type == XrmQString) {
+ (void) fputs(":\t", stream);
+ if (i)
+ i--;
+ }
+ else
+ (void) fprintf(stream, "=%s:\t", XrmRepresentationToString(*type));
+ if (i && (*s == ' ' || *s == '\t'))
+ (void) putc('\\', stream); /* preserve leading whitespace */
+ while (i--) {
+ c = *s++;
+ if (c == '\n') {
+ if (i)
+ (void) fputs("\\n\\\n", stream);
+ else
+ (void) fputs("\\n", stream);
+ } else if (c == '\\')
+ (void) fputs("\\\\", stream);
+ else if ((c < ' ' && c != '\t') ||
+ ((unsigned char)c >= 0x7f && (unsigned char)c < 0xa0))
+ (void) fprintf(stream, "\\%03o", (unsigned char)c);
+ else
+ (void) putc(c, stream);
+ }
+ (void) putc('\n', stream);
+ return ferror(stream) != 0;
+}
+
+#ifdef DEBUG
+
+void PrintTable(
+ NTable table,
+ FILE *file)
+{
+ XrmBinding bindings[MAXDBDEPTH+1];
+ XrmQuark quarks[MAXDBDEPTH+1];
+ EClosureRec closure;
+ XrmQuark empty = NULLQUARK;
+
+ closure.db = (XrmDatabase)NULL;
+ closure.proc = DumpEntry;
+ closure.closure = (XPointer)file;
+ closure.bindings = bindings;
+ closure.quarks = quarks;
+ closure.mode = XrmEnumAllLevels;
+ if (table->leaf)
+ EnumLTable((LTable)table, &empty, &empty, 0, &closure);
+ else
+ EnumNTable(table, &empty, &empty, 0, &closure);
+}
+
+#endif /* DEBUG */
+
+void
+XrmPutFileDatabase(
+ XrmDatabase db,
+ _Xconst char *fileName)
+{
+ FILE *file;
+ XrmQuark empty = NULLQUARK;
+
+ if (!db) return;
+ if (!(file = fopen(fileName, "w"))) return;
+ if (XrmEnumerateDatabase(db, &empty, &empty, XrmEnumAllLevels,
+ DumpEntry, (XPointer) file))
+ unlink((char *)fileName);
+ fclose(file);
+}
+
+/* macros used in get/search functions */
+
+/* find entries named ename, leafness leaf, tight or loose, and call get */
+#define GTIGHTLOOSE(ename,looseleaf) \
+ NFIND(ename); \
+ if (entry) { \
+ if (leaf == entry->leaf) { \
+ if (!leaf && !entry->tight && entry->next && \
+ entry->next->name == q && entry->next->tight && \
+ entry->next->hasloose && \
+ looseleaf((LTable)entry->next, names+1, classes+1, closure)) \
+ return True; \
+ if ((*get)(entry, names+1, classes+1, closure)) \
+ return True; \
+ if (entry->tight && (entry = entry->next) && \
+ entry->name == q && leaf == entry->leaf && \
+ (*get)(entry, names+1, classes+1, closure)) \
+ return True; \
+ } else if (entry->leaf) { \
+ if (entry->hasloose && \
+ looseleaf((LTable)entry, names+1, classes+1, closure)) \
+ return True; \
+ if (entry->tight && (entry = entry->next) && \
+ entry->name == q && entry->hasloose && \
+ looseleaf((LTable)entry, names+1, classes+1, closure)) \
+ return True; \
+ } \
+ }
+
+/* find entries named ename, leafness leaf, loose only, and call get */
+#define GLOOSE(ename,looseleaf) \
+ NFIND(ename); \
+ if (entry && entry->tight && (entry = entry->next) && entry->name != q) \
+ entry = (NTable)NULL; \
+ if (entry) { \
+ if (leaf == entry->leaf) { \
+ if ((*get)(entry, names+1, classes+1, closure)) \
+ return True; \
+ } else if (entry->leaf && entry->hasloose) { \
+ if (looseleaf((LTable)entry, names+1, classes+1, closure)) \
+ return True; \
+ } \
+ }
+
+/* add tight/loose entry to the search list, return True if list is full */
+/*ARGSUSED*/
+static Bool AppendLEntry(
+ LTable table,
+ XrmNameList names,
+ XrmClassList classes,
+ register SClosure closure)
+{
+ /* check for duplicate */
+ if (closure->idx >= 0 && closure->list[closure->idx] == table)
+ return False;
+ if (closure->idx == closure->limit)
+ return True;
+ /* append it */
+ closure->idx++;
+ closure->list[closure->idx] = table;
+ return False;
+}
+
+/* add loose entry to the search list, return True if list is full */
+/*ARGSUSED*/
+static Bool AppendLooseLEntry(
+ LTable table,
+ XrmNameList names,
+ XrmClassList classes,
+ register SClosure closure)
+{
+ /* check for duplicate */
+ if (closure->idx >= 0 && closure->list[closure->idx] == table)
+ return False;
+ if (closure->idx >= closure->limit - 1)
+ return True;
+ /* append it */
+ closure->idx++;
+ closure->list[closure->idx] = LOOSESEARCH;
+ closure->idx++;
+ closure->list[closure->idx] = table;
+ return False;
+}
+
+/* search for a leaf table */
+static Bool SearchNEntry(
+ NTable table,
+ XrmNameList names,
+ XrmClassList classes,
+ SClosure closure)
+{
+ register NTable entry;
+ register XrmQuark q;
+ register unsigned int leaf;
+ Bool (*get)(
+ NTable table,
+ XrmNameList names,
+ XrmClassList classes,
+ SClosure closure);
+
+ if (names[1]) {
+ get = SearchNEntry; /* recurse */
+ leaf = 0;
+ } else {
+ get = (getNTableSProcp)AppendLEntry; /* bottom of recursion */
+ leaf = 1;
+ }
+ GTIGHTLOOSE(*names, AppendLooseLEntry); /* do name, tight and loose */
+ GTIGHTLOOSE(*classes, AppendLooseLEntry); /* do class, tight and loose */
+ if (table->hasany) {
+ GTIGHTLOOSE(XrmQANY, AppendLooseLEntry); /* do ANY, tight and loose */
+ }
+ if (table->hasloose) {
+ while (1) {
+ names++;
+ classes++;
+ if (!*names)
+ break;
+ if (!names[1]) {
+ get = (getNTableSProcp)AppendLEntry; /* bottom of recursion */
+ leaf = 1;
+ }
+ GLOOSE(*names, AppendLooseLEntry); /* loose names */
+ GLOOSE(*classes, AppendLooseLEntry); /* loose classes */
+ if (table->hasany) {
+ GLOOSE(XrmQANY, AppendLooseLEntry); /* loose ANY */
+ }
+ }
+ }
+ /* now look for matching leaf nodes */
+ entry = table->next;
+ if (!entry)
+ return False;
+ if (entry->leaf) {
+ if (entry->tight && !table->tight)
+ entry = entry->next;
+ } else {
+ entry = entry->next;
+ if (!entry || !entry->tight)
+ return False;
+ }
+ if (!entry || entry->name != table->name)
+ return False;
+ /* found one */
+ if (entry->hasloose &&
+ AppendLooseLEntry((LTable)entry, names, classes, closure))
+ return True;
+ if (entry->tight && entry == table->next && (entry = entry->next) &&
+ entry->name == table->name && entry->hasloose)
+ return AppendLooseLEntry((LTable)entry, names, classes, closure);
+ return False;
+}
+
+Bool XrmQGetSearchList(
+ XrmDatabase db,
+ XrmNameList names,
+ XrmClassList classes,
+ XrmSearchList searchList, /* RETURN */
+ int listLength)
+{
+ register NTable table;
+ SClosureRec closure;
+
+ if (listLength <= 0)
+ return False;
+ closure.list = (LTable *)searchList;
+ closure.idx = -1;
+ closure.limit = listLength - 2;
+ if (db) {
+ _XLockMutex(&db->linfo);
+ table = db->table;
+ if (*names) {
+ if (table && !table->leaf) {
+ if (SearchNEntry(table, names, classes, &closure)) {
+ _XUnlockMutex(&db->linfo);
+ return False;
+ }
+ } else if (table && table->hasloose &&
+ AppendLooseLEntry((LTable)table, names, classes,
+ &closure)) {
+ _XUnlockMutex(&db->linfo);
+ return False;
+ }
+ } else {
+ if (table && !table->leaf)
+ table = table->next;
+ if (table &&
+ AppendLEntry((LTable)table, names, classes, &closure)) {
+ _XUnlockMutex(&db->linfo);
+ return False;
+ }
+ }
+ _XUnlockMutex(&db->linfo);
+ }
+ closure.list[closure.idx + 1] = (LTable)NULL;
+ return True;
+}
+
+Bool XrmQGetSearchResource(
+ XrmSearchList searchList,
+ register XrmName name,
+ register XrmClass class,
+ XrmRepresentation *pType, /* RETURN */
+ XrmValue *pValue) /* RETURN */
+{
+ register LTable *list;
+ register LTable table;
+ register VEntry entry = NULL;
+ int flags;
+
+/* find tight or loose entry */
+#define VTIGHTLOOSE(q) \
+ entry = LeafHash(table, q); \
+ while (entry && entry->name != q) \
+ entry = entry->next; \
+ if (entry) \
+ break
+
+/* find loose entry */
+#define VLOOSE(q) \
+ entry = LeafHash(table, q); \
+ while (entry && entry->name != q) \
+ entry = entry->next; \
+ if (entry) { \
+ if (!entry->tight) \
+ break; \
+ if ((entry = entry->next) && entry->name == q) \
+ break; \
+ }
+
+ list = (LTable *)searchList;
+ /* figure out which combination of name and class we need to search for */
+ flags = 0;
+ if (IsResourceQuark(name))
+ flags = 2;
+ if (IsResourceQuark(class))
+ flags |= 1;
+ if (!flags) {
+ /* neither name nor class has ever been used to name a resource */
+ table = (LTable)NULL;
+ } else if (flags == 3) {
+ /* both name and class */
+ while ((table = *list++)) {
+ if (table != LOOSESEARCH) {
+ VTIGHTLOOSE(name); /* do name, tight and loose */
+ VTIGHTLOOSE(class); /* do class, tight and loose */
+ } else {
+ table = *list++;
+ VLOOSE(name); /* do name, loose only */
+ VLOOSE(class); /* do class, loose only */
+ }
+ }
+ } else {
+ /* just one of name or class */
+ if (flags == 1)
+ name = class;
+ while ((table = *list++)) {
+ if (table != LOOSESEARCH) {
+ VTIGHTLOOSE(name); /* tight and loose */
+ } else {
+ table = *list++;
+ VLOOSE(name); /* loose only */
+ }
+ }
+ }
+ if (table) {
+ /* found a match */
+ if (entry->string) {
+ *pType = XrmQString;
+ pValue->addr = StringValue(entry);
+ } else {
+ *pType = RepType(entry);
+ pValue->addr = DataValue(entry);
+ }
+ pValue->size = entry->size;
+ return True;
+ }
+ *pType = NULLQUARK;
+ pValue->addr = (XPointer)NULL;
+ pValue->size = 0;
+ return False;
+
+#undef VTIGHTLOOSE
+#undef VLOOSE
+}
+
+/* look for a tight/loose value */
+static Bool GetVEntry(
+ LTable table,
+ XrmNameList names,
+ XrmClassList classes,
+ VClosure closure)
+{
+ register VEntry entry;
+ register XrmQuark q;
+
+ /* try name first */
+ q = *names;
+ entry = LeafHash(table, q);
+ while (entry && entry->name != q)
+ entry = entry->next;
+ if (!entry) {
+ /* not found, try class */
+ q = *classes;
+ entry = LeafHash(table, q);
+ while (entry && entry->name != q)
+ entry = entry->next;
+ if (!entry)
+ return False;
+ }
+ if (entry->string) {
+ *closure->type = XrmQString;
+ closure->value->addr = StringValue(entry);
+ } else {
+ *closure->type = RepType(entry);
+ closure->value->addr = DataValue(entry);
+ }
+ closure->value->size = entry->size;
+ return True;
+}
+
+/* look for a loose value */
+static Bool GetLooseVEntry(
+ LTable table,
+ XrmNameList names,
+ XrmClassList classes,
+ VClosure closure)
+{
+ register VEntry entry;
+ register XrmQuark q;
+
+#define VLOOSE(ename) \
+ q = ename; \
+ entry = LeafHash(table, q); \
+ while (entry && entry->name != q) \
+ entry = entry->next; \
+ if (entry && entry->tight && (entry = entry->next) && entry->name != q) \
+ entry = (VEntry)NULL;
+
+ /* bump to last component */
+ while (names[1]) {
+ names++;
+ classes++;
+ }
+ VLOOSE(*names); /* do name, loose only */
+ if (!entry) {
+ VLOOSE(*classes); /* do class, loose only */
+ if (!entry)
+ return False;
+ }
+ if (entry->string) {
+ *closure->type = XrmQString;
+ closure->value->addr = StringValue(entry);
+ } else {
+ *closure->type = RepType(entry);
+ closure->value->addr = DataValue(entry);
+ }
+ closure->value->size = entry->size;
+ return True;
+
+#undef VLOOSE
+}
+
+/* recursive search for a value */
+static Bool GetNEntry(
+ NTable table,
+ XrmNameList names,
+ XrmClassList classes,
+ VClosure closure)
+{
+ register NTable entry;
+ register XrmQuark q;
+ register unsigned int leaf;
+ Bool (*get)(
+ NTable table,
+ XrmNameList names,
+ XrmClassList classes,
+ VClosure closure);
+ NTable otable;
+
+ if (names[2]) {
+ get = GetNEntry; /* recurse */
+ leaf = 0;
+ } else {
+ get = (getNTableVProcp)GetVEntry; /* bottom of recursion */
+ leaf = 1;
+ }
+ GTIGHTLOOSE(*names, GetLooseVEntry); /* do name, tight and loose */
+ GTIGHTLOOSE(*classes, GetLooseVEntry); /* do class, tight and loose */
+ if (table->hasany) {
+ GTIGHTLOOSE(XrmQANY, GetLooseVEntry); /* do ANY, tight and loose */
+ }
+ if (table->hasloose) {
+ while (1) {
+ names++;
+ classes++;
+ if (!names[1])
+ break;
+ if (!names[2]) {
+ get = (getNTableVProcp)GetVEntry; /* bottom of recursion */
+ leaf = 1;
+ }
+ GLOOSE(*names, GetLooseVEntry); /* do name, loose only */
+ GLOOSE(*classes, GetLooseVEntry); /* do class, loose only */
+ if (table->hasany) {
+ GLOOSE(XrmQANY, GetLooseVEntry); /* do ANY, loose only */
+ }
+ }
+ }
+ /* look for matching leaf tables */
+ otable = table;
+ table = table->next;
+ if (!table)
+ return False;
+ if (table->leaf) {
+ if (table->tight && !otable->tight)
+ table = table->next;
+ } else {
+ table = table->next;
+ if (!table || !table->tight)
+ return False;
+ }
+ if (!table || table->name != otable->name)
+ return False;
+ /* found one */
+ if (table->hasloose &&
+ GetLooseVEntry((LTable)table, names, classes, closure))
+ return True;
+ if (table->tight && table == otable->next) {
+ table = table->next;
+ if (table && table->name == otable->name && table->hasloose)
+ return GetLooseVEntry((LTable)table, names, classes, closure);
+ }
+ return False;
+}
+
+Bool XrmQGetResource(
+ XrmDatabase db,
+ XrmNameList names,
+ XrmClassList classes,
+ XrmRepresentation *pType, /* RETURN */
+ XrmValuePtr pValue) /* RETURN */
+{
+ register NTable table;
+ VClosureRec closure;
+
+ if (db && *names) {
+ _XLockMutex(&db->linfo);
+ closure.type = pType;
+ closure.value = pValue;
+ table = db->table;
+ if (names[1]) {
+ if (table && !table->leaf) {
+ if (GetNEntry(table, names, classes, &closure)) {
+ _XUnlockMutex(&db->linfo);
+ return True;
+ }
+ } else if (table && table->hasloose &&
+ GetLooseVEntry((LTable)table, names, classes, &closure)) {
+ _XUnlockMutex (&db->linfo);
+ return True;
+ }
+ } else {
+ if (table && !table->leaf)
+ table = table->next;
+ if (table && GetVEntry((LTable)table, names, classes, &closure)) {
+ _XUnlockMutex(&db->linfo);
+ return True;
+ }
+ }
+ _XUnlockMutex(&db->linfo);
+ }
+ *pType = NULLQUARK;
+ pValue->addr = (XPointer)NULL;
+ pValue->size = 0;
+ return False;
+}
+
+Bool
+XrmGetResource(XrmDatabase db, _Xconst char *name_str, _Xconst char *class_str,
+ XrmString *pType_str, XrmValuePtr pValue)
+{
+ XrmName names[MAXDBDEPTH+1];
+ XrmClass classes[MAXDBDEPTH+1];
+ XrmRepresentation fromType;
+ Bool result;
+
+ XrmStringToNameList(name_str, names);
+ XrmStringToClassList(class_str, classes);
+ result = XrmQGetResource(db, names, classes, &fromType, pValue);
+ (*pType_str) = XrmQuarkToString(fromType);
+ return result;
+}
+
+/* destroy all values, plus table itself */
+static void DestroyLTable(
+ LTable table)
+{
+ register int i;
+ register VEntry *buckets;
+ register VEntry entry, next;
+
+ buckets = table->buckets;
+ for (i = table->table.mask; i >= 0; i--, buckets++) {
+ for (next = *buckets; (entry = next); ) {
+ next = entry->next;
+ Xfree(entry);
+ }
+ }
+ Xfree(table->buckets);
+ Xfree(table);
+}
+
+/* destroy all contained tables, plus table itself */
+static void DestroyNTable(
+ NTable table)
+{
+ register int i;
+ register NTable *buckets;
+ register NTable entry, next;
+
+ buckets = NodeBuckets(table);
+ for (i = table->mask; i >= 0; i--, buckets++) {
+ for (next = *buckets; (entry = next); ) {
+ next = entry->next;
+ if (entry->leaf)
+ DestroyLTable((LTable)entry);
+ else
+ DestroyNTable(entry);
+ }
+ }
+ Xfree(table);
+}
+
+const char *
+XrmLocaleOfDatabase(
+ XrmDatabase db)
+{
+ const char* retval;
+ _XLockMutex(&db->linfo);
+ retval = (*db->methods->lcname)(db->mbstate);
+ _XUnlockMutex(&db->linfo);
+ return retval;
+}
+
+void XrmDestroyDatabase(
+ XrmDatabase db)
+{
+ register NTable table, next;
+
+ if (db) {
+ _XLockMutex(&db->linfo);
+ for (next = db->table; (table = next); ) {
+ next = table->next;
+ if (table->leaf)
+ DestroyLTable((LTable)table);
+ else
+ DestroyNTable(table);
+ }
+ _XUnlockMutex(&db->linfo);
+ _XFreeMutex(&db->linfo);
+ (*db->methods->destroy)(db->mbstate);
+ Xfree(db);
+ }
+}