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