2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
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)
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
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
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 $ */
26 /***********************************************************
27 Copyright 1987, 1988, 1990 by Digital Equipment Corporation, Maynard
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.
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
47 ******************************************************************/
50 Copyright (c) 1987, 1988, 1990 X Consortium
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:
60 The above copyright notice and this permission notice shall be included
61 in all copies or substantial portions of the Software.
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.
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.
80 #include "_fallibint.h"
81 #include <X11/Xresource.h>
82 #include "_fallcint.h"
84 #include "_falloking.h"
94 #if defined(__STDC__) && !defined(NORCONST)
102 These Xrm routines allow very fast lookup of resources in the resource
103 database. Several usage patterns are exploited:
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.
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.
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.
126 The Xrm representation has been completely redesigned to substantially reduce
127 memory and hopefully improve performance.
129 The database is structured into two kinds of tables: LTables that contain
130 only values, and NTables that contain only other tables.
134 The next pointer of the top-level node table points to the top-level leaf
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
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.
148 typedef unsigned long Signature;
150 static XrmQuark XrmQString, XrmQANY;
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 */
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;
172 typedef struct _DEntry {
173 VEntryRec entry; /* entry */
174 XrmRepresentation type; /* representation type */
175 } DEntryRec, *DEntry;
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))
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;
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]
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.
206 typedef struct _LTable {
209 } LTableRec, *LTable;
211 #define LeafHash(le,q) (le)->buckets[(q) & (le)->table.mask]
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.
219 typedef struct _XrmHashBucketRec {
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;
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;
241 /* placed in XrmSearchList to indicate next table is loose only */
242 #define LOOSESEARCH ((LTable)1)
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;
254 /* predicate to determine when to resize a hash table */
255 #define GrowthPred(n,m) ((unsigned)(n) > (((m) + 1) << 2))
258 if (GrowthPred((*prev)->entries, (*prev)->mask)) \
261 /* pick a reasonable value for maximum depth of resource database */
262 #define MAXDBDEPTH 100
264 /* macro used in get/search functions */
266 /* find an entry named ename, with leafness given by leaf */
267 #define NFIND(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; \
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.
285 static unsigned char *resourceQuarks = (unsigned char *)NULL;
286 static XrmQuark maxResourceQuark = -1;
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)))
292 typedef unsigned char XrmBits;
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)
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)]
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))
322 static XrmBits Const xrmtypes[256] = {
324 0,SPACE,EOL,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. */
342 void falrmInitialize()
344 XrmQString = falrmPermStringToQuark("String");
345 XrmQANY = falrmPermStringToQuark("?");
348 XrmDatabase falrmGetDatabase(display)
352 LockDisplay(display);
353 retval = display->db;
354 UnlockDisplay(display);
358 void falrmSetDatabase(display, database)
360 XrmDatabase database;
362 LockDisplay(display);
363 display->db = database;
364 UnlockDisplay(display);
367 #if NeedFunctionPrototypes
368 void falrmStringToQuarkList(
369 register _Xconst char *name,
370 register XrmQuarkList quarks) /* RETURN */
372 void falrmStringToQuarkList(name, quarks)
374 register XrmQuarkList quarks; /* RETURN */
377 register XrmBits bits;
378 register Signature sig = 0;
379 register char ch, *tname;
382 if (tname = (char *)name) {
384 while (!is_EOF(bits = next_char(ch, tname))) {
385 if (is_binding (bits)) {
387 /* Found a complete name */
388 *quarks++ = _falrmInternalStringToQuark(name,tname - name,
396 sig = (sig << 1) + ch; /* Compute the signature. */
400 *quarks++ = _falrmInternalStringToQuark(name, tname - name, sig, False);
405 #if NeedFunctionPrototypes
406 void falrmStringToBindingQuarkList(
407 register _Xconst char *name,
408 register XrmBindingList bindings, /* RETURN */
409 register XrmQuarkList quarks) /* RETURN */
411 void falrmStringToBindingQuarkList(name, bindings, quarks)
413 register XrmBindingList bindings; /* RETURN */
414 register XrmQuarkList quarks; /* RETURN */
417 register XrmBits bits;
418 register Signature sig = 0;
419 register char ch, *tname;
420 register XrmBinding binding;
423 if (tname = (char *)name) {
425 binding = XrmBindTightly;
426 while (!is_EOF(bits = next_char(ch, tname))) {
427 if (is_binding (bits)) {
429 /* Found a complete name */
430 *bindings++ = binding;
431 *quarks++ = _falrmInternalStringToQuark(name, tname - name,
436 binding = XrmBindTightly;
441 binding = XrmBindLoosely;
444 sig = (sig << 1) + ch; /* Compute the signature. */
449 *quarks++ = _falrmInternalStringToQuark(name, tname - name, sig, False);
456 static void PrintQuarkList(quarks, stream)
462 for (firstNameSeen = False; *quarks; quarks++) {
464 (void) fprintf(stream, ".");
466 firstNameSeen = True;
467 (void) fputs(falrmQuarkToString(*quarks), stream);
469 } /* PrintQuarkList */
474 static void mbnoop(state)
480 static char mbchar(state, str, lenp)
490 static char *lcname(state)
496 static RConst XrmMethodsRec mb_methods = {
504 static XrmDatabase NewDatabase()
506 register XrmDatabase db;
508 db = (XrmDatabase) Xmalloc(sizeof(XrmHashBucketRec));
510 _XCreateMutex(&db->linfo);
511 db->table = (NTable)NULL;
512 db->methods = _falrmInitParseInfo(&db->mbstate);
514 db->methods = (XrmMethods)&mb_methods;
519 /* move all values from ftable to ttable, and free ftable's buckets.
520 * ttable is quaranteed empty to start with.
522 static void MoveValues(ftable, ttable)
524 register LTable ttable;
526 register VEntry fentry, nfentry;
527 register VEntry *prev;
528 register VEntry *bucket;
529 register VEntry tentry;
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);
537 /* chain on all with same name, to preserve invariant order */
538 while ((nfentry = fentry->next) && nfentry->name == fentry->name)
540 fentry->next = tentry;
543 Xfree((char *)ftable->buckets);
546 /* move all tables from ftable to ttable, and free ftable.
547 * ttable is quaranteed empty to start with.
549 static void MoveTables(ftable, ttable)
551 register NTable ttable;
553 register NTable fentry, nfentry;
554 register NTable *prev;
555 register NTable *bucket;
556 register NTable tentry;
559 for (i = ftable->mask, bucket = NodeBuckets(ftable); i >= 0; i--) {
560 for (fentry = *bucket++; fentry; fentry = nfentry) {
561 prev = &NodeHash(ttable, fentry->name);
564 /* chain on all with same name, to preserve invariant order */
565 while ((nfentry = fentry->next) && nfentry->name == fentry->name)
567 fentry->next = tentry;
570 Xfree((char *)ftable);
573 /* grow the table, based on current number of entries */
574 static void GrowTable(prev)
577 register NTable table;
582 if (i == 255) /* biggest it gets */
584 while (i < 255 && GrowthPred(table->entries, i))
586 i++; /* i is now the new size */
588 register LTable ltable;
591 ltable = (LTable)table;
592 /* cons up a copy to make MoveValues look symmetric */
594 ltable->buckets = (VEntry *)Xmalloc(i * sizeof(VEntry));
595 if (!ltable->buckets) {
596 ltable->buckets = otable.buckets;
599 ltable->table.mask = i - 1;
600 bzero((char *)ltable->buckets, i * sizeof(VEntry));
601 MoveValues(&otable, ltable);
603 register NTable ntable;
605 ntable = (NTable)Xmalloc(sizeof(NTableRec) + i * sizeof(NTable));
609 ntable->mask = i - 1;
610 bzero((char *)NodeBuckets(ntable), i * sizeof(NTable));
612 MoveTables(table, ntable);
616 /* merge values from ftable into *pprev, destroy ftable in the process */
617 static void MergeValues(ftable, pprev, override)
622 register VEntry fentry, tentry;
623 register VEntry *prev;
624 register LTable ttable;
629 ttable = (LTable)*pprev;
630 if (ftable->table.hasloose)
631 ttable->table.hasloose = 1;
632 for (i = ftable->table.mask, bucket = ftable->buckets;
635 for (fentry = *bucket; fentry; ) {
637 prev = &LeafHash(ttable, q);
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);
649 if (fentry->tight != tentry->tight) {
650 /* no match, chain in fentry */
652 prev = &fentry->next;
655 ttable->table.entries++;
656 } else if (override) {
657 /* match, chain in fentry, splice out and free tentry */
659 prev = &fentry->next;
661 *prev = tentry->next;
662 /* free the overridden entry */
663 Xfree((char *)tentry);
664 /* get next tentry */
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 */
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) {
683 prev = &fentry->next;
686 ttable->table.entries++;
690 Xfree((char *)ftable->buckets);
691 Xfree((char *)ftable);
692 /* resize if necessary, now that we're all done */
696 /* merge tables from ftable into *pprev, destroy ftable in the process */
697 static void MergeTables(ftable, pprev, override)
702 register NTable fentry, tentry;
704 register NTable *prev;
705 register NTable ttable;
711 if (ftable->hasloose)
712 ttable->hasloose = 1;
715 for (i = ftable->mask, bucket = NodeBuckets(ftable);
718 for (fentry = *bucket; fentry; ) {
720 prev = &NodeHash(ttable, q);
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);
734 nfentry = fentry->next;
735 if (fentry->leaf != tentry->leaf ||
736 fentry->tight != tentry->tight) {
737 /* no match, just chain in */
739 *(prev = &fentry->next) = tentry;
743 MergeValues((LTable)fentry, prev, override);
745 MergeTables(fentry, prev, override);
746 /* bump to next tentry */
747 tentry = *(prev = &(*prev)->next);
749 /* bump to next fentry */
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) {
758 prev = &fentry->next;
765 Xfree((char *)ftable);
766 /* resize if necessary, now that we're all done */
770 void falrmCombineDatabase(from, into, override)
771 XrmDatabase from, *into;
774 register NTable *prev;
775 register NTable ftable, ttable, nftable;
780 _XLockMutex(&from->linfo);
781 _XLockMutex(&(*into)->linfo);
782 if (ftable = from->table) {
783 prev = &(*into)->table;
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);
793 /* into has no node table, link from's in */
795 *(prev = &ftable->next) = ttable;
797 /* bump to from's leaf table, if any */
800 /* bump to into's leaf table, if any */
801 if (ttable && !ttable->leaf)
802 ttable = *(prev = &ttable->next);
805 /* if into has a leaf, merge, else insert */
807 MergeValues((LTable)ftable, prev, override);
812 (from->methods->destroy)(from->mbstate);
813 _XFreeMutex(&from->linfo);
815 _XUnlockMutex(&(*into)->linfo);
819 void falrmMergeDatabases(from, into)
820 XrmDatabase from, *into;
822 falrmCombineDatabase(from, into, True);
825 /* store a value in the database, overriding any existing entry */
826 static void PutEntry(db, bindings, quarks, type, value)
828 XrmBindingList bindings;
830 XrmRepresentation type;
833 register NTable *pprev, *prev;
834 register NTable table;
836 register VEntry *vprev;
837 register VEntry entry;
838 NTable *nprev, *firstpprev;
840 #define NEWTABLE(q,i) \
841 table = (NTable)Xmalloc(sizeof(LTableRec)); \
845 table->hasloose = 0; \
848 table->entries = 0; \
851 nprev = NodeBuckets(table); \
854 if (!(nprev = (NTable *)Xmalloc(sizeof(VEntry *)))) \
856 ((LTable)table)->buckets = (VEntry *)nprev; \
858 *nprev = (NTable)NULL; \
859 table->next = *prev; \
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);
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 */
875 /* search along until we need a value */
878 table = *(prev = &NodeHash(table, q));
879 while (table && table->name != q)
880 table = *(prev = &table->next);
882 break; /* not found */
885 break; /* not found */
888 /* bump to leaf table, if any */
889 table = *(prev = &table->next);
890 if (!table || table->name != q)
891 break; /* not found */
893 /* bump to leaf table, if any */
894 table = *(prev = &table->next);
895 if (!table || table->name != q)
896 break; /* not found */
900 if (*bindings == XrmBindTightly) {
902 break; /* not found */
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 */
912 /* found that one, bump to next quark */
918 /* found all the way to a leaf */
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 */
933 RepType(entry) = type;
934 /* identical size, just overwrite value */
935 memcpy(RawValue(entry), (char *)value->addr, value->size);
938 /* splice out and free old entry */
939 *vprev = entry->next;
940 Xfree((char *)entry);
943 /* this is where to insert */
944 prev = (NTable *)vprev;
947 /* keep the top table, because we may have to grow it */
949 /* iterate until we get to the leaf */
951 /* build a new table and chain it in */
953 if (*quarks++ == XrmQANY)
954 (*pprev)->hasany = 1;
955 if (*bindings++ == XrmBindTightly) {
959 (*pprev)->hasloose = 1;
965 /* now allocate the value entry */
966 entry = (VEntry)Xmalloc(((type == XrmQString) ?
967 sizeof(VEntryRec) : sizeof(DEntryRec)) +
971 entry->name = q = *quarks;
972 if (*bindings == XrmBindTightly) {
976 (*pprev)->hasloose = 1;
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) {
986 RepType(entry) = type;
988 /* save a copy of the value */
989 memcpy(RawValue(entry), (char *)value->addr, value->size);
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 */
996 resourceQuarks = (unsigned char *)Xrealloc((char *)resourceQuarks,
999 resourceQuarks = (unsigned char *)Xmalloc(size);
1000 if (resourceQuarks) {
1001 bzero((char *)&resourceQuarks[oldsize], size - oldsize);
1002 maxResourceQuark = (size << 3) - 1;
1004 maxResourceQuark = -1;
1007 if (q > 0 && resourceQuarks)
1008 resourceQuarks[q >> 3] |= 1 << (q & 0x7);
1014 void falrmQPutResource(pdb, bindings, quarks, type, value)
1016 XrmBindingList bindings;
1017 XrmQuarkList quarks;
1018 XrmRepresentation type;
1021 if (!*pdb) *pdb = NewDatabase();
1022 _XLockMutex(&(*pdb)->linfo);
1023 PutEntry(*pdb, bindings, quarks, type, value);
1024 _XUnlockMutex(&(*pdb)->linfo);
1027 #if NeedFunctionPrototypes
1028 void falrmPutResource(
1030 _Xconst char *specifier,
1034 void falrmPutResource(pdb, specifier, type, value)
1041 XrmBinding bindings[MAXDBDEPTH+1];
1042 XrmQuark quarks[MAXDBDEPTH+1];
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);
1051 #if NeedFunctionPrototypes
1052 void falrmQPutStringResource(
1054 XrmBindingList bindings,
1055 XrmQuarkList quarks,
1058 void falrmQPutStringResource(pdb, bindings, quarks, str)
1060 XrmBindingList bindings;
1061 XrmQuarkList quarks;
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);
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
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.
1088 * Chris Peterson, MIT X Consortium 5/17/90.
1091 #define LIST_SIZE 101
1092 #define BUFFER_SIZE 100
1094 static void GetIncludeFile();
1096 static void GetDatabase(db, str, filename, doall)
1103 register XrmBits bits = 0;
1106 register Signature sig;
1107 register char *ptr_max;
1108 register XrmQuarkList t_quarks;
1109 register XrmBindingList t_bindings;
1111 int alloc_chars = BUFSIZ;
1112 char buffer[BUFSIZ], *value_str;
1113 XrmQuark quarks[LIST_SIZE];
1114 XrmBinding bindings[LIST_SIZE];
1122 if (!(value_str = Xmalloc(sizeof(char) * alloc_chars)))
1125 (*db->methods->mbinit)(db->mbstate);
1128 while (!is_EOF(bits) && dolines) {
1132 * First: Remove extra whitespace.
1136 bits = next_char(c, str);
1137 } while is_space(bits);
1140 * Ignore empty lines.
1144 continue; /* start a new line. */
1147 * Second: check the first character in a line to see if it is
1148 * "!" signifying a comment, or "#" signifying a directive.
1151 if (c == '!') { /* Comment, spin to next newline */
1152 while (is_simple(bits = next_char(c, str))) {}
1155 while (!is_EOL(bits = next_mbchar(c, len, str))) {}
1157 continue; /* start a new line. */
1160 if (c == '#') { /* Directive */
1161 /* remove extra whitespace */
1163 while (is_space(bits = next_char(c, str))) {};
1164 /* only "include" directive is currently defined */
1165 if (!strncmp(str, "include", 7)) {
1167 /* remove extra whitespace */
1168 while (is_space(bits = next_char(c, str))) {};
1169 /* must have a starting " */
1171 char *fname = str+1;
1175 bits = next_char(c, str);
1176 if (is_nonpcs(bits))
1180 bits = next_mbchar(c, len, str);
1181 } while (c != '"' && !is_EOL(bits));
1182 /* must have an ending " */
1184 GetIncludeFile(db, filename, fname, str - len - fname);
1187 /* spin to next newline */
1189 while (is_simple(bits))
1190 bits = next_char(c, str);
1194 while (!is_EOL(bits))
1195 bits = next_mbchar(c, len, str);
1197 continue; /* start a new line. */
1201 * Third: loop through the LHS of the resource specification
1202 * storing characters and converting this to a Quark.
1204 * If the number of quarks is greater than LIST_SIZE - 1. This
1205 * function will trash your memory.
1207 * If the length of any quark is larger than BUFSIZ this function
1208 * will also trash memory.
1211 t_bindings = bindings;
1216 *t_bindings = XrmBindTightly;
1218 if (!is_binding(bits)) {
1219 while (!is_EOQ(bits)) {
1221 sig = (sig << 1) + c; /* Compute the signature. */
1222 bits = next_char(c, str);
1225 *t_quarks++ = _falrmInternalStringToQuark(buffer, ptr - buffer,
1228 if (is_separator(bits)) {
1229 if (!is_space(bits))
1232 /* Remove white space */
1235 sig = (sig << 1) + c; /* Compute the signature. */
1236 } while (is_space(bits = next_char(c, str)));
1239 * The spec doesn't permit it, but support spaces
1240 * internal to resource name/class
1243 if (is_separator(bits))
1250 *(++t_bindings) = XrmBindTightly;
1252 *(++t_bindings) = XrmBindLoosely;
1259 * Magic unspecified feature #254.
1261 * If two separators appear with no Text between them then
1264 * If anyone of those separators is a '*' then the binding
1265 * will be loose, otherwise it will be tight.
1269 *t_bindings = XrmBindLoosely;
1272 bits = next_char(c, str);
1275 *t_quarks = NULLQUARK;
1278 * Make sure that there is a ':' in this line.
1285 * A parsing error has occurred, toss everything on the line
1286 * a new_line can still be escaped with a '\'.
1289 while (is_normal(bits))
1290 bits = next_char(c, str);
1293 bits = next_mbchar(c, len, str);
1296 bits = next_mbchar(c, len, str);
1297 } while (c && (c != '\n' || oldc == '\\'));
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.
1310 * Fourth: Remove more whitespace
1314 if (is_space(bits = next_char(c, str)))
1318 bits = next_char(c, str);
1328 * Fifth: Process the right hand side.
1332 ptr_max = ptr + alloc_chars - 4;
1339 * Tight loop for the normal case: Non backslash, non-end of value
1340 * character that will fit into the allocated buffer.
1344 while (is_normal(bits) && ptr < ptr_max) {
1346 bits = next_char(c, str);
1350 if (is_nonpcs(bits)) {
1352 bits = next_mbchar(c, len, str);
1355 while (!is_special(bits) && ptr + len <= ptr_max) {
1358 *ptr++ = str[len++];
1359 bits = next_mbchar(c, len, str);
1369 * We need to do some magic after a backslash.
1371 Bool read_next = True;
1374 bits = next_char(c, str);
1375 if (is_nonpcs(bits))
1379 bits = next_mbchar(c, len, str);
1384 } else if (c == 'n') {
1386 * "\n" means insert a newline.
1389 } else if (c == '\\') {
1391 * "\\" completes to just one backslash.
1396 * pick up to three octal digits after the '\'.
1400 while (is_odigit(bits) && count < 3) {
1403 bits = next_char(c, str);
1404 if (is_nonpcs(bits))
1408 bits = next_mbchar(c, len, str);
1412 * If we found three digits then insert that octal code
1413 * into the value string as a character.
1417 *ptr++ = (unsigned char) ((temp[0] - '0') * 0100 +
1418 (temp[1] - '0') * 010 +
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.
1430 for (tcount = 0; tcount < count; tcount++) {
1431 *ptr++ = temp[tcount]; /* print them in
1432 the correct order */
1439 bits = next_char(c, str);
1440 if (is_nonpcs(bits))
1444 bits = next_mbchar(c, len, str);
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.
1454 if (ptr + len > ptr_max) {
1457 alloc_chars += BUFSIZ/10;
1458 temp_str = Xrealloc(value_str, sizeof(char) * alloc_chars);
1462 (*db->methods->mbfinish)(db->mbstate);
1466 ptr = temp_str + (ptr - value_str); /* reset pointer. */
1467 value_str = temp_str;
1468 ptr_max = value_str + alloc_chars - 4;
1473 * Lastly: Terminate the value string, and store this entry
1474 * into the database.
1479 /* Store it in database */
1480 value.size = ptr - value_str;
1481 value.addr = (XPointer) value_str;
1483 PutEntry(db, bindings, quarks, XrmQString, &value);
1487 (*db->methods->mbfinish)(db->mbstate);
1490 #if NeedFunctionPrototypes
1491 void falrmPutStringResource(
1493 _Xconst char*specifier,
1496 void falrmPutStringResource(pdb, specifier, str)
1503 XrmBinding bindings[MAXDBDEPTH+1];
1504 XrmQuark quarks[MAXDBDEPTH+1];
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);
1516 #if NeedFunctionPrototypes
1517 void falrmPutLineResource(
1521 void falrmPutLineResource(pdb, line)
1526 if (!*pdb) *pdb = NewDatabase();
1527 _XLockMutex(&(*pdb)->linfo);
1528 GetDatabase(*pdb, line, (char *)NULL, False);
1529 _XUnlockMutex(&(*pdb)->linfo);
1532 #if NeedFunctionPrototypes
1533 XrmDatabase falrmGetStringDatabase(
1536 XrmDatabase falrmGetStringDatabase(data)
1543 _XLockMutex(&db->linfo);
1544 GetDatabase(db, data, (char *)NULL, True);
1545 _XUnlockMutex(&db->linfo);
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.
1556 ReadInFile(filename)
1559 register int fd, size;
1562 if ( (fd = OpenFile(filename)) == -1 )
1563 return (char *)NULL;
1565 GetSizeOfFile(filename, size);
1567 if (!(filebuf = Xmalloc(size + 1))) { /* leave room for '\0' */
1569 return (char *)NULL;
1572 size = ReadFile(fd, filebuf, size);
1576 return (char *)NULL;
1580 filebuf[size] = '\0'; /* NULL terminate it. */
1585 GetIncludeFile(db, base, fname, fnamelen)
1593 char realfname[BUFSIZ];
1595 if (fnamelen <= 0 || fnamelen >= BUFSIZ)
1597 if (*fname != '/' && base && (str = strrchr(base, '/'))) {
1598 len = str - base + 1;
1599 if (len + fnamelen >= BUFSIZ)
1601 strncpy(realfname, base, len);
1602 strncpy(realfname + len, fname, fnamelen);
1603 realfname[len + fnamelen] = '\0';
1605 strncpy(realfname, fname, fnamelen);
1606 realfname[fnamelen] = '\0';
1608 if (!(str = ReadInFile(realfname)))
1610 GetDatabase(db, str, realfname, True);
1614 #if NeedFunctionPrototypes
1615 XrmDatabase falrmGetFileDatabase(
1616 _Xconst char *filename)
1618 XrmDatabase falrmGetFileDatabase(filename)
1625 if (!(str = ReadInFile(filename)))
1626 return (XrmDatabase)NULL;
1629 _XLockMutex(&db->linfo);
1630 GetDatabase(db, str, filename, True);
1631 _XUnlockMutex(&db->linfo);
1636 #if NeedFunctionPrototypes
1637 Status falrmCombineFileDatabase(
1638 _Xconst char *filename,
1639 XrmDatabase *target,
1642 Status falrmCombineFileDatabase(filename, target, override)
1644 XrmDatabase *target;
1651 if (!(str = ReadInFile(filename)))
1656 *target = db = NewDatabase();
1659 _XLockMutex(&db->linfo);
1660 GetDatabase(db, str, filename, True);
1661 _XUnlockMutex(&db->linfo);
1664 falrmCombineDatabase(db, target, False);
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.
1672 static Bool EnumLTable(table, names, classes, level, closure)
1675 XrmClassList classes;
1677 register EClosure closure;
1679 register VEntry *bucket;
1681 register VEntry entry;
1683 XrmRepresentation type;
1686 closure->bindings[level] = (table->table.tight ?
1687 XrmBindTightly : XrmBindLoosely);
1688 closure->quarks[level] = table->table.name;
1691 closure->quarks[level + 1] = NULLQUARK;
1692 for (i = table->table.mask, bucket = table->buckets;
1695 for (entry = *bucket; entry; entry = entry->next) {
1696 if (entry->tight && !tightOk)
1698 closure->bindings[level] = (entry->tight ?
1699 XrmBindTightly : XrmBindLoosely);
1700 closure->quarks[level] = entry->name;
1701 value.size = entry->size;
1702 if (entry->string) {
1704 value.addr = StringValue(entry);
1706 type = RepType(entry);
1707 value.addr = DataValue(entry);
1709 if ((*closure->proc)(&closure->db, closure->bindings+1,
1710 closure->quarks+1, &type, &value,
1718 static Bool EnumAllNTable(table, level, closure)
1721 register EClosure closure;
1723 register NTable *bucket;
1725 register NTable entry;
1726 XrmQuark empty = NULLQUARK;
1728 if (level >= MAXDBDEPTH)
1730 for (i = table->mask, bucket = NodeBuckets(table);
1733 for (entry = *bucket; entry; entry = entry->next) {
1735 if (EnumLTable((LTable)entry, &empty, &empty, level, closure))
1738 closure->bindings[level] = (entry->tight ?
1739 XrmBindTightly : XrmBindLoosely);
1740 closure->quarks[level] = entry->name;
1741 if (EnumAllNTable(entry, level+1, closure))
1749 /* recurse on every table in the table, arbitrary order.
1750 * stop if user proc returns True. level is current depth in database.
1752 static Bool EnumNTable(table, names, classes, level, closure)
1755 XrmClassList classes;
1757 register EClosure closure;
1759 register NTable entry;
1760 register XrmQuark q;
1761 register unsigned int leaf;
1765 /* find entries named ename, leafness leaf, tight or loose, and call get */
1766 #define ITIGHTLOOSE(ename) \
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, \
1776 if ((*get)(entry, names+1, classes+1, level, closure)) \
1778 if (entry->tight && (entry = entry->next) && \
1779 entry->name == q && leaf == entry->leaf && \
1780 (*get)(entry, names+1, classes+1, level, closure)) \
1782 } else if (entry->leaf) { \
1783 if ((bilevel || entry->hasloose) && \
1784 EnumLTable((LTable)entry, names+1, classes+1, level, closure))\
1786 if (entry->tight && (entry = entry->next) && \
1787 entry->name == q && (bilevel || entry->hasloose) && \
1788 EnumLTable((LTable)entry, names+1, classes+1, level, closure))\
1793 /* find entries named ename, leafness leaf, loose only, and call get */
1794 #define ILOOSE(ename) \
1796 if (entry && entry->tight && (entry = entry->next) && entry->name != q) \
1797 entry = (NTable)NULL; \
1799 if (leaf == entry->leaf) { \
1800 if ((*get)(entry, names+1, classes+1, level, closure)) \
1802 } else if (entry->leaf && (bilevel || entry->hasloose)) { \
1803 if (EnumLTable((LTable)entry, names+1, classes+1, level, closure))\
1808 if (level >= MAXDBDEPTH)
1810 closure->bindings[level] = (table->tight ?
1811 XrmBindTightly : XrmBindLoosely);
1812 closure->quarks[level] = table->name;
1815 if (EnumAllNTable(table, level, closure))
1818 if (names[1] || closure->mode == XrmEnumAllLevels) {
1819 get = EnumNTable; /* recurse */
1821 bilevel = !names[1];
1823 get = EnumLTable; /* bottom of recursion */
1827 if (table->hasloose && closure->mode == XrmEnumAllLevels) {
1830 XrmQuark empty = NULLQUARK;
1832 for (i = table->mask, bucket = NodeBuckets(table);
1836 for (entry = *bucket; entry; entry = entry->next) {
1837 if (!entry->tight && entry->name != q &&
1838 entry->name != *names && entry->name != *classes) {
1841 if (EnumLTable((LTable)entry, &empty, &empty,
1845 if (EnumNTable(entry, &empty, &empty,
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 */
1859 if (table->hasloose) {
1865 if (!names[1] && closure->mode != XrmEnumAllLevels) {
1866 get = EnumLTable; /* bottom of recursion */
1869 ILOOSE(*names); /* loose names */
1870 ILOOSE(*classes); /* loose classes */
1871 if (table->hasany) {
1872 ILOOSE(XrmQANY); /* loose ANY */
1879 /* now look for matching leaf nodes */
1880 entry = table->next;
1884 if (entry->tight && !table->tight)
1885 entry = entry->next;
1887 entry = entry->next;
1888 if (!entry || !entry->tight)
1891 if (!entry || entry->name != table->name)
1895 if ((!*names || entry->hasloose) &&
1896 EnumLTable((LTable)entry, names, classes, level, closure))
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);
1907 /* call the proc for every value in the database, arbitrary order.
1908 * stop if the proc returns True.
1910 Bool falrmEnumerateDatabase(db, names, classes, mode, proc, closure)
1913 XrmClassList classes;
1918 XrmBinding bindings[MAXDBDEPTH+2];
1919 XrmQuark quarks[MAXDBDEPTH+2];
1920 register NTable table;
1921 EClosureRec eclosure;
1922 Bool retval = False;
1926 _XLockMutex(&db->linfo);
1928 eclosure.proc = proc;
1929 eclosure.closure = closure;
1930 eclosure.bindings = bindings;
1931 eclosure.quarks = quarks;
1932 eclosure.mode = mode;
1934 if (table && !table->leaf && !*names && mode == XrmEnumOneLevel)
1935 table = table->next;
1938 retval = EnumNTable(table, names, classes, 0, &eclosure);
1940 retval = EnumLTable((LTable)table, names, classes, 0, &eclosure);
1942 _XUnlockMutex(&db->linfo);
1946 static void PrintBindingQuarkList(bindings, quarks, stream)
1947 XrmBindingList bindings;
1948 XrmQuarkList quarks;
1953 for (firstNameSeen = False; *quarks; bindings++, quarks++) {
1954 if (*bindings == XrmBindLoosely) {
1955 (void) fprintf(stream, "*");
1956 } else if (firstNameSeen) {
1957 (void) fprintf(stream, ".");
1959 firstNameSeen = True;
1960 (void) fputs(falrmQuarkToString(*quarks), stream);
1964 /* output out the entry in correct file syntax */
1966 static Bool DumpEntry(db, bindings, quarks, type, value, data)
1968 XrmBindingList bindings;
1969 XrmQuarkList quarks;
1970 XrmRepresentation *type;
1974 FILE *stream = (FILE *)data;
1975 register unsigned int i;
1979 if (*type != XrmQString)
1980 (void) putc('!', stream);
1981 PrintBindingQuarkList(bindings, quarks, stream);
1984 if (*type == XrmQString) {
1985 (void) fputs(":\t", stream);
1990 (void) fprintf(stream, "=%s:\t", XrmRepresentationToString(*type));
1991 if (i && (*s == ' ' || *s == '\t'))
1992 (void) putc('\\', stream); /* preserve leading whitespace */
1997 (void) fputs("\\n\\\n", stream);
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);
2006 (void) putc(c, stream);
2008 (void) putc('\n', stream);
2009 return ferror(stream) != 0;
2014 void falPrintTable(table, file)
2018 XrmBinding bindings[MAXDBDEPTH+1];
2019 XrmQuark quarks[MAXDBDEPTH+1];
2020 EClosureRec closure;
2021 XrmQuark empty = NULLQUARK;
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;
2030 EnumLTable((LTable)table, &empty, &empty, 0, &closure);
2032 EnumNTable(table, &empty, &empty, 0, &closure);
2037 #if NeedFunctionPrototypes
2038 void falrmPutFileDatabase(
2040 _Xconst char *fileName)
2042 void falrmPutFileDatabase(db, fileName)
2048 XrmQuark empty = NULLQUARK;
2051 if (!(file = fopen(fileName, "w"))) return;
2052 if (falrmEnumerateDatabase(db, &empty, &empty, XrmEnumAllLevels,
2053 DumpEntry, (XPointer) file))
2054 unlink((char *)fileName);
2058 /* macros used in get/search functions */
2060 /* find entries named ename, leafness leaf, tight or loose, and call get */
2061 #define GTIGHTLOOSE(ename,looseleaf) \
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)) \
2070 if ((*get)(entry, names+1, classes+1, closure)) \
2072 if (entry->tight && (entry = entry->next) && \
2073 entry->name == q && leaf == entry->leaf && \
2074 (*get)(entry, names+1, classes+1, closure)) \
2076 } else if (entry->leaf) { \
2077 if (entry->hasloose && \
2078 looseleaf((LTable)entry, names+1, classes+1, closure)) \
2080 if (entry->tight && (entry = entry->next) && \
2081 entry->name == q && entry->hasloose && \
2082 looseleaf((LTable)entry, names+1, classes+1, closure)) \
2087 /* find entries named ename, leafness leaf, loose only, and call get */
2088 #define GLOOSE(ename,looseleaf) \
2090 if (entry && entry->tight && (entry = entry->next) && entry->name != q) \
2091 entry = (NTable)NULL; \
2093 if (leaf == entry->leaf) { \
2094 if ((*get)(entry, names+1, classes+1, closure)) \
2096 } else if (entry->leaf && entry->hasloose) { \
2097 if (looseleaf((LTable)entry, names+1, classes+1, closure)) \
2102 /* add tight/loose entry to the search list, return True if list is full */
2104 static Bool AppendLEntry(table, names, classes, closure)
2107 XrmClassList classes;
2108 register SClosure closure;
2110 /* check for duplicate */
2111 if (closure->idx >= 0 && closure->list[closure->idx] == table)
2113 if (closure->idx == closure->limit)
2117 closure->list[closure->idx] = table;
2121 /* add loose entry to the search list, return True if list is full */
2123 static Bool AppendLooseLEntry(table, names, classes, closure)
2126 XrmClassList classes;
2127 register SClosure closure;
2129 /* check for duplicate */
2130 if (closure->idx >= 0 && closure->list[closure->idx] == table)
2132 if (closure->idx >= closure->limit - 1)
2136 closure->list[closure->idx] = LOOSESEARCH;
2138 closure->list[closure->idx] = table;
2142 /* search for a leaf table */
2143 static Bool SearchNEntry(table, names, classes, closure)
2146 XrmClassList classes;
2149 register NTable entry;
2150 register XrmQuark q;
2151 register unsigned int leaf;
2155 get = SearchNEntry; /* recurse */
2158 get = AppendLEntry; /* bottom of recursion */
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 */
2166 if (table->hasloose) {
2173 get = AppendLEntry; /* bottom of recursion */
2176 GLOOSE(*names, AppendLooseLEntry); /* loose names */
2177 GLOOSE(*classes, AppendLooseLEntry); /* loose classes */
2178 if (table->hasany) {
2179 GLOOSE(XrmQANY, AppendLooseLEntry); /* loose ANY */
2183 /* now look for matching leaf nodes */
2184 entry = table->next;
2188 if (entry->tight && !table->tight)
2189 entry = entry->next;
2191 entry = entry->next;
2192 if (!entry || !entry->tight)
2195 if (!entry || entry->name != table->name)
2198 if (entry->hasloose &&
2199 AppendLooseLEntry((LTable)entry, names, classes, closure))
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);
2207 Bool falrmQGetSearchList(db, names, classes, searchList, listLength)
2210 XrmClassList classes;
2211 XrmSearchList searchList; /* RETURN */
2214 register NTable table;
2215 SClosureRec closure;
2217 if (listLength <= 0)
2219 closure.list = (LTable *)searchList;
2221 closure.limit = listLength - 2;
2223 _XLockMutex(&db->linfo);
2226 if (table && !table->leaf) {
2227 if (SearchNEntry(table, names, classes, &closure)) {
2228 _XUnlockMutex(&db->linfo);
2231 } else if (table && table->hasloose &&
2232 AppendLooseLEntry((LTable)table, names, classes,
2234 _XUnlockMutex(&db->linfo);
2238 if (table && !table->leaf)
2239 table = table->next;
2241 AppendLEntry((LTable)table, names, classes, &closure)) {
2242 _XUnlockMutex(&db->linfo);
2246 _XUnlockMutex(&db->linfo);
2248 closure.list[closure.idx + 1] = (LTable)NULL;
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 */
2259 register LTable *list;
2260 register LTable table;
2261 register VEntry entry;
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; \
2272 /* find loose entry */
2274 entry = LeafHash(table, q); \
2275 while (entry && entry->name != q) \
2276 entry = entry->next; \
2278 if (!entry->tight) \
2280 if ((entry = entry->next) && entry->name == q) \
2284 list = (LTable *)searchList;
2285 /* figure out which combination of name and class we need to search for */
2287 if (IsResourceQuark(name))
2289 if (IsResourceQuark(class))
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 */
2302 VLOOSE(name); /* do name, loose only */
2303 VLOOSE(class); /* do class, loose only */
2307 /* just one of name or class */
2310 while (table = *list++) {
2311 if (table != LOOSESEARCH) {
2312 VTIGHTLOOSE(name); /* tight and loose */
2315 VLOOSE(name); /* loose only */
2321 if (entry->string) {
2322 *pType = XrmQString;
2323 pValue->addr = StringValue(entry);
2325 *pType = RepType(entry);
2326 pValue->addr = DataValue(entry);
2328 pValue->size = entry->size;
2332 pValue->addr = (XPointer)NULL;
2340 /* look for a tight/loose value */
2341 static Bool GetVEntry(table, names, classes, closure)
2344 XrmClassList classes;
2347 register VEntry entry;
2348 register XrmQuark q;
2350 /* try name first */
2352 entry = LeafHash(table, q);
2353 while (entry && entry->name != q)
2354 entry = entry->next;
2356 /* not found, try class */
2358 entry = LeafHash(table, q);
2359 while (entry && entry->name != q)
2360 entry = entry->next;
2364 if (entry->string) {
2365 *closure->type = XrmQString;
2366 closure->value->addr = StringValue(entry);
2368 *closure->type = RepType(entry);
2369 closure->value->addr = DataValue(entry);
2371 closure->value->size = entry->size;
2375 /* look for a loose value */
2376 static Bool GetLooseVEntry(table, names, classes, closure)
2379 XrmClassList classes;
2382 register VEntry entry;
2383 register XrmQuark q;
2385 #define VLOOSE(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;
2393 /* bump to last component */
2398 VLOOSE(*names); /* do name, loose only */
2400 VLOOSE(*classes); /* do class, loose only */
2404 if (entry->string) {
2405 *closure->type = XrmQString;
2406 closure->value->addr = StringValue(entry);
2408 *closure->type = RepType(entry);
2409 closure->value->addr = DataValue(entry);
2411 closure->value->size = entry->size;
2417 /* recursive search for a value */
2418 static Bool GetNEntry(table, names, classes, closure)
2421 XrmClassList classes;
2424 register NTable entry;
2425 register XrmQuark q;
2426 register unsigned int leaf;
2431 get = GetNEntry; /* recurse */
2434 get = GetVEntry; /* bottom of recursion */
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 */
2442 if (table->hasloose) {
2449 get = GetVEntry; /* bottom of recursion */
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 */
2459 /* look for matching leaf tables */
2461 table = table->next;
2465 if (table->tight && !otable->tight)
2466 table = table->next;
2468 table = table->next;
2469 if (!table || !table->tight)
2472 if (!table || table->name != otable->name)
2475 if (table->hasloose &&
2476 GetLooseVEntry((LTable)table, names, classes, closure))
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);
2486 Bool falrmQGetResource(db, names, classes, pType, pValue)
2489 XrmClassList classes;
2490 XrmRepresentation *pType; /* RETURN */
2491 XrmValuePtr pValue; /* RETURN */
2493 register NTable table;
2494 VClosureRec closure;
2497 _XLockMutex(&db->linfo);
2498 closure.type = pType;
2499 closure.value = pValue;
2502 if (table && !table->leaf) {
2503 if (GetNEntry(table, names, classes, &closure)) {
2504 _XUnlockMutex(&db->linfo);
2507 } else if (table && table->hasloose &&
2508 GetLooseVEntry((LTable)table, names, classes, &closure)) {
2509 _XUnlockMutex (&db->linfo);
2513 if (table && !table->leaf)
2514 table = table->next;
2515 if (table && GetVEntry((LTable)table, names, classes, &closure)) {
2516 _XUnlockMutex(&db->linfo);
2520 _XUnlockMutex(&db->linfo);
2523 pValue->addr = (XPointer)NULL;
2528 #if NeedFunctionPrototypes
2529 Bool falrmGetResource(db, name_str, class_str, pType_str, pValue)
2531 _Xconst char *name_str;
2532 _Xconst char *class_str;
2533 XrmString *pType_str; /* RETURN */
2534 XrmValuePtr pValue; /* RETURN */
2536 Bool falrmGetResource(db, name_str, class_str, pType_str, pValue)
2539 XrmString class_str;
2540 XrmString *pType_str; /* RETURN */
2541 XrmValuePtr pValue; /* RETURN */
2544 XrmName names[MAXDBDEPTH+1];
2545 XrmClass classes[MAXDBDEPTH+1];
2546 XrmRepresentation fromType;
2549 XrmStringToNameList(name_str, names);
2550 XrmStringToClassList(class_str, classes);
2551 result = falrmQGetResource(db, names, classes, &fromType, pValue);
2552 (*pType_str) = falrmQuarkToString(fromType);
2556 /* destroy all values, plus table itself */
2557 static void DestroyLTable(table)
2561 register VEntry *buckets;
2562 register VEntry entry, next;
2564 buckets = table->buckets;
2565 for (i = table->table.mask; i >= 0; i--, buckets++) {
2566 for (next = *buckets; entry = next; ) {
2568 Xfree((char *)entry);
2571 Xfree((char *)table->buckets);
2572 Xfree((char *)table);
2575 /* destroy all contained tables, plus table itself */
2576 static void DestroyNTable(table)
2580 register NTable *buckets;
2581 register NTable entry, next;
2583 buckets = NodeBuckets(table);
2584 for (i = table->mask; i >= 0; i--, buckets++) {
2585 for (next = *buckets; entry = next; ) {
2588 DestroyLTable((LTable)entry);
2590 DestroyNTable(entry);
2593 Xfree((char *)table);
2596 char *falrmLocaleOfDatabase(db)
2600 _XLockMutex(&db->linfo);
2601 retval = (*db->methods->lcname)(db->mbstate);
2602 _XUnlockMutex(&db->linfo);
2606 void falrmDestroyDatabase(db)
2609 register NTable table, next;
2612 _XLockMutex(&db->linfo);
2613 for (next = db->table; table = next; ) {
2616 DestroyLTable((LTable)table);
2618 DestroyNTable(table);
2620 _XFreeMutex(&db->linfo);
2621 (*db->methods->destroy)(db->mbstate);