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