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