Xrm.c revision 57f47464
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 "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 Xfree(table); \ 847 return; \ 848 } \ 849 ((LTable)table)->buckets = (VEntry *)nprev; \ 850 } \ 851 *nprev = (NTable)NULL; \ 852 table->next = *prev; \ 853 *prev = table 854 855 if (!db || !*quarks) 856 return; 857 table = *(prev = &db->table); 858 /* if already at leaf, bump to the leaf table */ 859 if (!quarks[1] && table && !table->leaf) 860 table = *(prev = &table->next); 861 pprev = prev; 862 if (!table || (quarks[1] && table->leaf)) { 863 /* no top-level node table, create one and chain it in */ 864 NEWTABLE(NULLQUARK,1); 865 table->tight = 1; /* arbitrary */ 866 prev = nprev; 867 } else { 868 /* search along until we need a value */ 869 while (quarks[1]) { 870 q = *quarks; 871 table = *(prev = &NodeHash(table, q)); 872 while (table && table->name != q) 873 table = *(prev = &table->next); 874 if (!table) 875 break; /* not found */ 876 if (quarks[2]) { 877 if (table->leaf) 878 break; /* not found */ 879 } else { 880 if (!table->leaf) { 881 /* bump to leaf table, if any */ 882 table = *(prev = &table->next); 883 if (!table || table->name != q) 884 break; /* not found */ 885 if (!table->leaf) { 886 /* bump to leaf table, if any */ 887 table = *(prev = &table->next); 888 if (!table || table->name != q) 889 break; /* not found */ 890 } 891 } 892 } 893 if (*bindings == XrmBindTightly) { 894 if (!table->tight) 895 break; /* not found */ 896 } else { 897 if (table->tight) { 898 /* bump to loose table, if any */ 899 table = *(prev = &table->next); 900 if (!table || table->name != q || 901 !quarks[2] != table->leaf) 902 break; /* not found */ 903 } 904 } 905 /* found that one, bump to next quark */ 906 pprev = prev; 907 quarks++; 908 bindings++; 909 } 910 if (!quarks[1]) { 911 /* found all the way to a leaf */ 912 q = *quarks; 913 entry = *(vprev = &LeafHash((LTable)table, q)); 914 while (entry && entry->name != q) 915 entry = *(vprev = &entry->next); 916 /* if want loose and have tight, bump to next entry */ 917 if (entry && *bindings == XrmBindLoosely && entry->tight) 918 entry = *(vprev = &entry->next); 919 if (entry && entry->name == q && 920 (*bindings == XrmBindTightly) == entry->tight) { 921 /* match, need to override */ 922 if ((type == XrmQString) == entry->string && 923 entry->size == value->size) { 924 /* update type if not String, can be different */ 925 if (!entry->string) 926 RepType(entry) = type; 927 /* identical size, just overwrite value */ 928 memcpy(RawValue(entry), (char *)value->addr, value->size); 929 return; 930 } 931 /* splice out and free old entry */ 932 *vprev = entry->next; 933 Xfree((char *)entry); 934 (*pprev)->entries--; 935 } 936 /* this is where to insert */ 937 prev = (NTable *)vprev; 938 } 939 } 940 /* keep the top table, because we may have to grow it */ 941 firstpprev = pprev; 942 /* iterate until we get to the leaf */ 943 while (quarks[1]) { 944 /* build a new table and chain it in */ 945 NEWTABLE(*quarks,2); 946 if (*quarks++ == XrmQANY) 947 (*pprev)->hasany = 1; 948 if (*bindings++ == XrmBindTightly) { 949 table->tight = 1; 950 } else { 951 table->tight = 0; 952 (*pprev)->hasloose = 1; 953 } 954 (*pprev)->entries++; 955 pprev = prev; 956 prev = nprev; 957 } 958 /* now allocate the value entry */ 959 entry = (VEntry)Xmalloc(((type == XrmQString) ? 960 sizeof(VEntryRec) : sizeof(DEntryRec)) + 961 value->size); 962 if (!entry) 963 return; 964 entry->name = q = *quarks; 965 if (*bindings == XrmBindTightly) { 966 entry->tight = 1; 967 } else { 968 entry->tight = 0; 969 (*pprev)->hasloose = 1; 970 } 971 /* chain it in, with a bit of type cast ugliness */ 972 entry->next = *((VEntry *)prev); 973 *((VEntry *)prev) = entry; 974 entry->size = value->size; 975 if (type == XrmQString) { 976 entry->string = 1; 977 } else { 978 entry->string = 0; 979 RepType(entry) = type; 980 } 981 /* save a copy of the value */ 982 memcpy(RawValue(entry), (char *)value->addr, value->size); 983 (*pprev)->entries++; 984 /* this is a new leaf, need to remember it for search lists */ 985 if (q > maxResourceQuark) { 986 unsigned oldsize = (maxResourceQuark + 1) >> 3; 987 unsigned size = ((q | 0x7f) + 1) >> 3; /* reallocate in chunks */ 988 if (resourceQuarks) { 989 unsigned char *prevQuarks = resourceQuarks; 990 991 resourceQuarks = (unsigned char *)Xrealloc((char *)resourceQuarks, 992 size); 993 if (!resourceQuarks) { 994 Xfree(prevQuarks); 995 } 996 } else 997 resourceQuarks = (unsigned char *)Xmalloc(size); 998 if (resourceQuarks) { 999 bzero((char *)&resourceQuarks[oldsize], size - oldsize); 1000 maxResourceQuark = (size << 3) - 1; 1001 } else { 1002 maxResourceQuark = -1; 1003 } 1004 } 1005 if (q > 0 && resourceQuarks) 1006 resourceQuarks[q >> 3] |= 1 << (q & 0x7); 1007 GROW(firstpprev); 1008 1009#undef NEWTABLE 1010} 1011 1012void XrmQPutResource( 1013 XrmDatabase *pdb, 1014 XrmBindingList bindings, 1015 XrmQuarkList quarks, 1016 XrmRepresentation type, 1017 XrmValuePtr value) 1018{ 1019 if (!*pdb) *pdb = NewDatabase(); 1020 _XLockMutex(&(*pdb)->linfo); 1021 PutEntry(*pdb, bindings, quarks, type, value); 1022 _XUnlockMutex(&(*pdb)->linfo); 1023} 1024 1025void 1026XrmPutResource( 1027 XrmDatabase *pdb, 1028 _Xconst char *specifier, 1029 _Xconst char *type, 1030 XrmValuePtr value) 1031{ 1032 XrmBinding bindings[MAXDBDEPTH+1]; 1033 XrmQuark quarks[MAXDBDEPTH+1]; 1034 1035 if (!*pdb) *pdb = NewDatabase(); 1036 _XLockMutex(&(*pdb)->linfo); 1037 XrmStringToBindingQuarkList(specifier, bindings, quarks); 1038 PutEntry(*pdb, bindings, quarks, XrmStringToQuark(type), value); 1039 _XUnlockMutex(&(*pdb)->linfo); 1040} 1041 1042void 1043XrmQPutStringResource( 1044 XrmDatabase *pdb, 1045 XrmBindingList bindings, 1046 XrmQuarkList quarks, 1047 _Xconst char *str) 1048{ 1049 XrmValue value; 1050 1051 if (!*pdb) *pdb = NewDatabase(); 1052 value.addr = (XPointer) str; 1053 value.size = strlen(str)+1; 1054 _XLockMutex(&(*pdb)->linfo); 1055 PutEntry(*pdb, bindings, quarks, XrmQString, &value); 1056 _XUnlockMutex(&(*pdb)->linfo); 1057} 1058 1059/* Function Name: GetDatabase 1060 * Description: Parses a string and stores it as a database. 1061 * Arguments: db - the database. 1062 * str - a pointer to the string containing the database. 1063 * filename - source filename, if any. 1064 * doall - whether to do all lines or just one 1065 */ 1066 1067/* 1068 * This function is highly optimized to inline as much as possible. 1069 * Be very careful with modifications, or simplifications, as they 1070 * may adversely affect the performance. 1071 * 1072 * Chris Peterson, MIT X Consortium 5/17/90. 1073 */ 1074 1075/* 1076 * Xlib spec says max 100 quarks in a lookup, will stop and return if 1077 * return if any single production's lhs has more than 100 components. 1078 */ 1079#define QLIST_SIZE 100 1080 1081/* 1082 * This should be big enough to handle things like the XKeysymDB or biggish 1083 * ~/.Xdefaults or app-defaults files. Anything bigger will be allocated on 1084 * the heap. 1085 */ 1086#define DEF_BUFF_SIZE 8192 1087 1088static void GetIncludeFile( 1089 XrmDatabase db, 1090 _Xconst char *base, 1091 _Xconst char *fname, 1092 int fnamelen); 1093 1094static void GetDatabase( 1095 XrmDatabase db, 1096 _Xconst register char *str, 1097 _Xconst char *filename, 1098 Bool doall) 1099{ 1100 char *rhs; 1101 char *lhs, lhs_s[DEF_BUFF_SIZE]; 1102 XrmQuark quarks[QLIST_SIZE + 1]; /* allow for a terminal NullQuark */ 1103 XrmBinding bindings[QLIST_SIZE + 1]; 1104 1105 register char *ptr; 1106 register XrmBits bits = 0; 1107 register char c; 1108 register Signature sig; 1109 register char *ptr_max; 1110 register int num_quarks; 1111 register XrmBindingList t_bindings; 1112 1113 int len, alloc_chars; 1114 unsigned long str_len; 1115 XrmValue value; 1116 Bool only_pcs; 1117 Bool dolines; 1118 1119 if (!db) 1120 return; 1121 1122 /* 1123 * if strlen (str) < DEF_BUFF_SIZE allocate buffers on the stack for 1124 * speed otherwise malloc the buffer. From a buffer overflow standpoint 1125 * we can be sure that neither: a) a component on the lhs, or b) a 1126 * value on the rhs, will be longer than the overall length of str, 1127 * i.e. strlen(str). 1128 * 1129 * This should give good performance when parsing "*foo: bar" type 1130 * databases as might be passed with -xrm command line options; but 1131 * with larger databases, e.g. .Xdefaults, app-defaults, or KeysymDB 1132 * files, the size of the buffers will be overly large. One way 1133 * around this would be to double-parse each production with a resulting 1134 * performance hit. In any event we can be assured that a lhs component 1135 * name or a rhs value won't be longer than str itself. 1136 */ 1137 1138 str_len = strlen (str); 1139 if (DEF_BUFF_SIZE > str_len) lhs = lhs_s; 1140 else if ((lhs = (char*) Xmalloc (str_len)) == NULL) 1141 return; 1142 1143 alloc_chars = DEF_BUFF_SIZE < str_len ? str_len : DEF_BUFF_SIZE; 1144 if ((rhs = (char*) Xmalloc (alloc_chars)) == NULL) { 1145 if (lhs != lhs_s) Xfree (lhs); 1146 return; 1147 } 1148 1149 (*db->methods->mbinit)(db->mbstate); 1150 str--; 1151 dolines = True; 1152 while (!is_EOF(bits) && dolines) { 1153 dolines = doall; 1154 1155 /* 1156 * First: Remove extra whitespace. 1157 */ 1158 1159 do { 1160 bits = next_char(c, str); 1161 } while is_space(bits); 1162 1163 /* 1164 * Ignore empty lines. 1165 */ 1166 1167 if (is_EOL(bits)) 1168 continue; /* start a new line. */ 1169 1170 /* 1171 * Second: check the first character in a line to see if it is 1172 * "!" signifying a comment, or "#" signifying a directive. 1173 */ 1174 1175 if (c == '!') { /* Comment, spin to next newline */ 1176 while (is_simple(bits = next_char(c, str))) {} 1177 if (is_EOL(bits)) 1178 continue; 1179 while (!is_EOL(bits = next_mbchar(c, len, str))) {} 1180 str--; 1181 continue; /* start a new line. */ 1182 } 1183 1184 if (c == '#') { /* Directive */ 1185 /* remove extra whitespace */ 1186 only_pcs = True; 1187 while (is_space(bits = next_char(c, str))) {}; 1188 /* only "include" directive is currently defined */ 1189 if (!strncmp(str, "include", 7)) { 1190 str += (7-1); 1191 /* remove extra whitespace */ 1192 while (is_space(bits = next_char(c, str))) {}; 1193 /* must have a starting " */ 1194 if (c == '"') { 1195 _Xconst char *fname = str+1; 1196 len = 0; 1197 do { 1198 if (only_pcs) { 1199 bits = next_char(c, str); 1200 if (is_nonpcs(bits)) 1201 only_pcs = False; 1202 } 1203 if (!only_pcs) 1204 bits = next_mbchar(c, len, str); 1205 } while (c != '"' && !is_EOL(bits)); 1206 /* must have an ending " */ 1207 if (c == '"') 1208 GetIncludeFile(db, filename, fname, str - len - fname); 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 occured, 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 occured. 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 = 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); 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); 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#ifdef __UNIXOS2__ 1579 filename = __XOS2RedirRoot(filename); 1580#endif 1581 1582 /* 1583 * MS-Windows and OS/2 note: Default open mode includes O_TEXT 1584 */ 1585 if ( (fd = _XOpenFile (filename, O_RDONLY)) == -1 ) 1586 return (char *)NULL; 1587 1588 /* 1589 * MS-Windows and OS/2 note: depending on how the sources are 1590 * untarred, the newlines in resource files may or may not have 1591 * been expanded to CRLF. Either way the size returned by fstat 1592 * is sufficient to read the file into because in text-mode any 1593 * CRLFs in a file will be converted to newlines (LF) with the 1594 * result that the number of bytes actually read with be <= 1595 * to the size returned by fstat. 1596 */ 1597 { 1598 struct stat status_buffer; 1599 if ( (fstat(fd, &status_buffer)) == -1 ) { 1600 close (fd); 1601 return (char *)NULL; 1602 } else 1603 size = status_buffer.st_size; 1604 } 1605 1606 if (!(filebuf = Xmalloc(size + 1))) { /* leave room for '\0' */ 1607 close(fd); 1608 return (char *)NULL; 1609 } 1610 size = read (fd, filebuf, size); 1611 1612#ifdef __UNIXOS2__ 1613 { /* kill CRLF */ 1614 int i,k; 1615 for (i=k=0; i<size; i++) 1616 if (filebuf[i] != 0x0d) { 1617 filebuf[k++] = filebuf[i]; 1618 } 1619 filebuf[k] = 0; 1620 } 1621#endif 1622 1623 if (size < 0) { 1624 close (fd); 1625 Xfree(filebuf); 1626 return (char *)NULL; 1627 } 1628 close (fd); 1629 1630 filebuf[size] = '\0'; /* NULL terminate it. */ 1631 return filebuf; 1632} 1633 1634static void 1635GetIncludeFile( 1636 XrmDatabase db, 1637 _Xconst char *base, 1638 _Xconst char *fname, 1639 int fnamelen) 1640{ 1641 int len; 1642 char *str; 1643 char realfname[BUFSIZ]; 1644 1645 if (fnamelen <= 0 || fnamelen >= BUFSIZ) 1646 return; 1647 if (*fname != '/' && base && (str = strrchr(base, '/'))) { 1648 len = str - base + 1; 1649 if (len + fnamelen >= BUFSIZ) 1650 return; 1651 strncpy(realfname, base, len); 1652 strncpy(realfname + len, fname, fnamelen); 1653 realfname[len + fnamelen] = '\0'; 1654 } else { 1655 strncpy(realfname, fname, fnamelen); 1656 realfname[fnamelen] = '\0'; 1657 } 1658 if (!(str = ReadInFile(realfname))) 1659 return; 1660 GetDatabase(db, str, realfname, True); 1661 Xfree(str); 1662} 1663 1664XrmDatabase 1665XrmGetFileDatabase( 1666 _Xconst char *filename) 1667{ 1668 XrmDatabase db; 1669 char *str; 1670 1671 if (!(str = ReadInFile(filename))) 1672 return (XrmDatabase)NULL; 1673 1674 db = NewDatabase(); 1675 _XLockMutex(&db->linfo); 1676 GetDatabase(db, str, filename, True); 1677 _XUnlockMutex(&db->linfo); 1678 Xfree(str); 1679 return db; 1680} 1681 1682Status 1683XrmCombineFileDatabase( 1684 _Xconst char *filename, 1685 XrmDatabase *target, 1686 Bool override) 1687{ 1688 XrmDatabase db; 1689 char *str; 1690 1691 if (!(str = ReadInFile(filename))) 1692 return 0; 1693 if (override) { 1694 db = *target; 1695 if (!db) 1696 *target = db = NewDatabase(); 1697 } else 1698 db = NewDatabase(); 1699 _XLockMutex(&db->linfo); 1700 GetDatabase(db, str, filename, True); 1701 _XUnlockMutex(&db->linfo); 1702 Xfree(str); 1703 if (!override) 1704 XrmCombineDatabase(db, target, False); 1705 return 1; 1706} 1707 1708/* call the user proc for every value in the table, arbitrary order. 1709 * stop if user proc returns True. level is current depth in database. 1710 */ 1711/*ARGSUSED*/ 1712static Bool EnumLTable( 1713 LTable table, 1714 XrmNameList names, 1715 XrmClassList classes, 1716 register int level, 1717 register EClosure closure) 1718{ 1719 register VEntry *bucket; 1720 register int i; 1721 register VEntry entry; 1722 XrmValue value; 1723 XrmRepresentation type; 1724 Bool tightOk; 1725 1726 closure->bindings[level] = (table->table.tight ? 1727 XrmBindTightly : XrmBindLoosely); 1728 closure->quarks[level] = table->table.name; 1729 level++; 1730 tightOk = !*names; 1731 closure->quarks[level + 1] = NULLQUARK; 1732 for (i = table->table.mask, bucket = table->buckets; 1733 i >= 0; 1734 i--, bucket++) { 1735 for (entry = *bucket; entry; entry = entry->next) { 1736 if (entry->tight && !tightOk) 1737 continue; 1738 closure->bindings[level] = (entry->tight ? 1739 XrmBindTightly : XrmBindLoosely); 1740 closure->quarks[level] = entry->name; 1741 value.size = entry->size; 1742 if (entry->string) { 1743 type = XrmQString; 1744 value.addr = StringValue(entry); 1745 } else { 1746 type = RepType(entry); 1747 value.addr = DataValue(entry); 1748 } 1749 if ((*closure->proc)(&closure->db, closure->bindings+1, 1750 closure->quarks+1, &type, &value, 1751 closure->closure)) 1752 return True; 1753 } 1754 } 1755 return False; 1756} 1757 1758static Bool EnumAllNTable( 1759 NTable table, 1760 register int level, 1761 register EClosure closure) 1762{ 1763 register NTable *bucket; 1764 register int i; 1765 register NTable entry; 1766 XrmQuark empty = NULLQUARK; 1767 1768 if (level >= MAXDBDEPTH) 1769 return False; 1770 for (i = table->mask, bucket = NodeBuckets(table); 1771 i >= 0; 1772 i--, bucket++) { 1773 for (entry = *bucket; entry; entry = entry->next) { 1774 if (entry->leaf) { 1775 if (EnumLTable((LTable)entry, &empty, &empty, level, closure)) 1776 return True; 1777 } else { 1778 closure->bindings[level] = (entry->tight ? 1779 XrmBindTightly : XrmBindLoosely); 1780 closure->quarks[level] = entry->name; 1781 if (EnumAllNTable(entry, level+1, closure)) 1782 return True; 1783 } 1784 } 1785 } 1786 return False; 1787} 1788 1789/* recurse on every table in the table, arbitrary order. 1790 * stop if user proc returns True. level is current depth in database. 1791 */ 1792static Bool EnumNTable( 1793 NTable table, 1794 XrmNameList names, 1795 XrmClassList classes, 1796 register int level, 1797 register EClosure closure) 1798{ 1799 register NTable entry; 1800 register XrmQuark q; 1801 register unsigned int leaf; 1802 Bool (*get)( 1803 NTable table, 1804 XrmNameList names, 1805 XrmClassList classes, 1806 register int level, 1807 EClosure closure); 1808 Bool bilevel; 1809 1810/* find entries named ename, leafness leaf, tight or loose, and call get */ 1811#define ITIGHTLOOSE(ename) \ 1812 NFIND(ename); \ 1813 if (entry) { \ 1814 if (leaf == entry->leaf) { \ 1815 if (!leaf && !entry->tight && entry->next && \ 1816 entry->next->name == q && entry->next->tight && \ 1817 (bilevel || entry->next->hasloose) && \ 1818 EnumLTable((LTable)entry->next, names+1, classes+1, \ 1819 level, closure)) \ 1820 return True; \ 1821 if ((*get)(entry, names+1, classes+1, level, closure)) \ 1822 return True; \ 1823 if (entry->tight && (entry = entry->next) && \ 1824 entry->name == q && leaf == entry->leaf && \ 1825 (*get)(entry, names+1, classes+1, level, closure)) \ 1826 return True; \ 1827 } else if (entry->leaf) { \ 1828 if ((bilevel || entry->hasloose) && \ 1829 EnumLTable((LTable)entry, names+1, classes+1, level, closure))\ 1830 return True; \ 1831 if (entry->tight && (entry = entry->next) && \ 1832 entry->name == q && (bilevel || entry->hasloose) && \ 1833 EnumLTable((LTable)entry, names+1, classes+1, level, closure))\ 1834 return True; \ 1835 } \ 1836 } 1837 1838/* find entries named ename, leafness leaf, loose only, and call get */ 1839#define ILOOSE(ename) \ 1840 NFIND(ename); \ 1841 if (entry && entry->tight && (entry = entry->next) && entry->name != q) \ 1842 entry = (NTable)NULL; \ 1843 if (entry) { \ 1844 if (leaf == entry->leaf) { \ 1845 if ((*get)(entry, names+1, classes+1, level, closure)) \ 1846 return True; \ 1847 } else if (entry->leaf && (bilevel || entry->hasloose)) { \ 1848 if (EnumLTable((LTable)entry, names+1, classes+1, level, closure))\ 1849 return True; \ 1850 } \ 1851 } 1852 1853 if (level >= MAXDBDEPTH) 1854 return False; 1855 closure->bindings[level] = (table->tight ? 1856 XrmBindTightly : XrmBindLoosely); 1857 closure->quarks[level] = table->name; 1858 level++; 1859 if (!*names) { 1860 if (EnumAllNTable(table, level, closure)) 1861 return True; 1862 } else { 1863 if (names[1] || closure->mode == XrmEnumAllLevels) { 1864 get = EnumNTable; /* recurse */ 1865 leaf = 0; 1866 bilevel = !names[1]; 1867 } else { 1868 get = (getNTableEProcp)EnumLTable; /* bottom of recursion */ 1869 leaf = 1; 1870 bilevel = False; 1871 } 1872 if (table->hasloose && closure->mode == XrmEnumAllLevels) { 1873 NTable *bucket; 1874 int i; 1875 XrmQuark empty = NULLQUARK; 1876 1877 for (i = table->mask, bucket = NodeBuckets(table); 1878 i >= 0; 1879 i--, bucket++) { 1880 q = NULLQUARK; 1881 for (entry = *bucket; entry; entry = entry->next) { 1882 if (!entry->tight && entry->name != q && 1883 entry->name != *names && entry->name != *classes) { 1884 q = entry->name; 1885 if (entry->leaf) { 1886 if (EnumLTable((LTable)entry, &empty, &empty, 1887 level, closure)) 1888 return True; 1889 } else { 1890 if (EnumNTable(entry, &empty, &empty, 1891 level, closure)) 1892 return True; 1893 } 1894 } 1895 } 1896 } 1897 } 1898 1899 ITIGHTLOOSE(*names); /* do name, tight and loose */ 1900 ITIGHTLOOSE(*classes); /* do class, tight and loose */ 1901 if (table->hasany) { 1902 ITIGHTLOOSE(XrmQANY); /* do ANY, tight and loose */ 1903 } 1904 if (table->hasloose) { 1905 while (1) { 1906 names++; 1907 classes++; 1908 if (!*names) 1909 break; 1910 if (!names[1] && closure->mode != XrmEnumAllLevels) { 1911 get = (getNTableEProcp)EnumLTable; /* bottom of recursion */ 1912 leaf = 1; 1913 } 1914 ILOOSE(*names); /* loose names */ 1915 ILOOSE(*classes); /* loose classes */ 1916 if (table->hasany) { 1917 ILOOSE(XrmQANY); /* loose ANY */ 1918 } 1919 } 1920 names--; 1921 classes--; 1922 } 1923 } 1924 /* now look for matching leaf nodes */ 1925 entry = table->next; 1926 if (!entry) 1927 return False; 1928 if (entry->leaf) { 1929 if (entry->tight && !table->tight) 1930 entry = entry->next; 1931 } else { 1932 entry = entry->next; 1933 if (!entry || !entry->tight) 1934 return False; 1935 } 1936 if (!entry || entry->name != table->name) 1937 return False; 1938 /* found one */ 1939 level--; 1940 if ((!*names || entry->hasloose) && 1941 EnumLTable((LTable)entry, names, classes, level, closure)) 1942 return True; 1943 if (entry->tight && entry == table->next && (entry = entry->next) && 1944 entry->name == table->name && (!*names || entry->hasloose)) 1945 return EnumLTable((LTable)entry, names, classes, level, closure); 1946 return False; 1947 1948#undef ITIGHTLOOSE 1949#undef ILOOSE 1950} 1951 1952/* call the proc for every value in the database, arbitrary order. 1953 * stop if the proc returns True. 1954 */ 1955Bool XrmEnumerateDatabase( 1956 XrmDatabase db, 1957 XrmNameList names, 1958 XrmClassList classes, 1959 int mode, 1960 DBEnumProc proc, 1961 XPointer closure) 1962{ 1963 XrmBinding bindings[MAXDBDEPTH+2]; 1964 XrmQuark quarks[MAXDBDEPTH+2]; 1965 register NTable table; 1966 EClosureRec eclosure; 1967 Bool retval = False; 1968 1969 if (!db) 1970 return False; 1971 _XLockMutex(&db->linfo); 1972 eclosure.db = db; 1973 eclosure.proc = proc; 1974 eclosure.closure = closure; 1975 eclosure.bindings = bindings; 1976 eclosure.quarks = quarks; 1977 eclosure.mode = mode; 1978 table = db->table; 1979 if (table && !table->leaf && !*names && mode == XrmEnumOneLevel) 1980 table = table->next; 1981 if (table) { 1982 if (!table->leaf) 1983 retval = EnumNTable(table, names, classes, 0, &eclosure); 1984 else 1985 retval = EnumLTable((LTable)table, names, classes, 0, &eclosure); 1986 } 1987 _XUnlockMutex(&db->linfo); 1988 return retval; 1989} 1990 1991static void PrintBindingQuarkList( 1992 XrmBindingList bindings, 1993 XrmQuarkList quarks, 1994 FILE *stream) 1995{ 1996 Bool firstNameSeen; 1997 1998 for (firstNameSeen = False; *quarks; bindings++, quarks++) { 1999 if (*bindings == XrmBindLoosely) { 2000 (void) fprintf(stream, "*"); 2001 } else if (firstNameSeen) { 2002 (void) fprintf(stream, "."); 2003 } 2004 firstNameSeen = True; 2005 (void) fputs(XrmQuarkToString(*quarks), stream); 2006 } 2007} 2008 2009/* output out the entry in correct file syntax */ 2010/*ARGSUSED*/ 2011static Bool DumpEntry( 2012 XrmDatabase *db, 2013 XrmBindingList bindings, 2014 XrmQuarkList quarks, 2015 XrmRepresentation *type, 2016 XrmValuePtr value, 2017 XPointer data) 2018{ 2019 FILE *stream = (FILE *)data; 2020 register unsigned int i; 2021 register char *s; 2022 register char c; 2023 2024 if (*type != XrmQString) 2025 (void) putc('!', stream); 2026 PrintBindingQuarkList(bindings, quarks, stream); 2027 s = value->addr; 2028 i = value->size; 2029 if (*type == XrmQString) { 2030 (void) fputs(":\t", stream); 2031 if (i) 2032 i--; 2033 } 2034 else 2035 (void) fprintf(stream, "=%s:\t", XrmRepresentationToString(*type)); 2036 if (i && (*s == ' ' || *s == '\t')) 2037 (void) putc('\\', stream); /* preserve leading whitespace */ 2038 while (i--) { 2039 c = *s++; 2040 if (c == '\n') { 2041 if (i) 2042 (void) fputs("\\n\\\n", stream); 2043 else 2044 (void) fputs("\\n", stream); 2045 } else if (c == '\\') 2046 (void) fputs("\\\\", stream); 2047 else if ((c < ' ' && c != '\t') || 2048 ((unsigned char)c >= 0x7f && (unsigned char)c < 0xa0)) 2049 (void) fprintf(stream, "\\%03o", (unsigned char)c); 2050 else 2051 (void) putc(c, stream); 2052 } 2053 (void) putc('\n', stream); 2054 return ferror(stream) != 0; 2055} 2056 2057#ifdef DEBUG 2058 2059void PrintTable( 2060 NTable table, 2061 FILE *file) 2062{ 2063 XrmBinding bindings[MAXDBDEPTH+1]; 2064 XrmQuark quarks[MAXDBDEPTH+1]; 2065 EClosureRec closure; 2066 XrmQuark empty = NULLQUARK; 2067 2068 closure.db = (XrmDatabase)NULL; 2069 closure.proc = DumpEntry; 2070 closure.closure = (XPointer)file; 2071 closure.bindings = bindings; 2072 closure.quarks = quarks; 2073 closure.mode = XrmEnumAllLevels; 2074 if (table->leaf) 2075 EnumLTable((LTable)table, &empty, &empty, 0, &closure); 2076 else 2077 EnumNTable(table, &empty, &empty, 0, &closure); 2078} 2079 2080#endif /* DEBUG */ 2081 2082void 2083XrmPutFileDatabase( 2084 XrmDatabase db, 2085 _Xconst char *fileName) 2086{ 2087 FILE *file; 2088 XrmQuark empty = NULLQUARK; 2089 2090 if (!db) return; 2091 if (!(file = fopen(fileName, "w"))) return; 2092 if (XrmEnumerateDatabase(db, &empty, &empty, XrmEnumAllLevels, 2093 DumpEntry, (XPointer) file)) 2094 unlink((char *)fileName); 2095 fclose(file); 2096} 2097 2098/* macros used in get/search functions */ 2099 2100/* find entries named ename, leafness leaf, tight or loose, and call get */ 2101#define GTIGHTLOOSE(ename,looseleaf) \ 2102 NFIND(ename); \ 2103 if (entry) { \ 2104 if (leaf == entry->leaf) { \ 2105 if (!leaf && !entry->tight && entry->next && \ 2106 entry->next->name == q && entry->next->tight && \ 2107 entry->next->hasloose && \ 2108 looseleaf((LTable)entry->next, names+1, classes+1, closure)) \ 2109 return True; \ 2110 if ((*get)(entry, names+1, classes+1, closure)) \ 2111 return True; \ 2112 if (entry->tight && (entry = entry->next) && \ 2113 entry->name == q && leaf == entry->leaf && \ 2114 (*get)(entry, names+1, classes+1, closure)) \ 2115 return True; \ 2116 } else if (entry->leaf) { \ 2117 if (entry->hasloose && \ 2118 looseleaf((LTable)entry, names+1, classes+1, closure)) \ 2119 return True; \ 2120 if (entry->tight && (entry = entry->next) && \ 2121 entry->name == q && entry->hasloose && \ 2122 looseleaf((LTable)entry, names+1, classes+1, closure)) \ 2123 return True; \ 2124 } \ 2125 } 2126 2127/* find entries named ename, leafness leaf, loose only, and call get */ 2128#define GLOOSE(ename,looseleaf) \ 2129 NFIND(ename); \ 2130 if (entry && entry->tight && (entry = entry->next) && entry->name != q) \ 2131 entry = (NTable)NULL; \ 2132 if (entry) { \ 2133 if (leaf == entry->leaf) { \ 2134 if ((*get)(entry, names+1, classes+1, closure)) \ 2135 return True; \ 2136 } else if (entry->leaf && entry->hasloose) { \ 2137 if (looseleaf((LTable)entry, names+1, classes+1, closure)) \ 2138 return True; \ 2139 } \ 2140 } 2141 2142/* add tight/loose entry to the search list, return True if list is full */ 2143/*ARGSUSED*/ 2144static Bool AppendLEntry( 2145 LTable table, 2146 XrmNameList names, 2147 XrmClassList classes, 2148 register SClosure closure) 2149{ 2150 /* check for duplicate */ 2151 if (closure->idx >= 0 && closure->list[closure->idx] == table) 2152 return False; 2153 if (closure->idx == closure->limit) 2154 return True; 2155 /* append it */ 2156 closure->idx++; 2157 closure->list[closure->idx] = table; 2158 return False; 2159} 2160 2161/* add loose entry to the search list, return True if list is full */ 2162/*ARGSUSED*/ 2163static Bool AppendLooseLEntry( 2164 LTable table, 2165 XrmNameList names, 2166 XrmClassList classes, 2167 register SClosure closure) 2168{ 2169 /* check for duplicate */ 2170 if (closure->idx >= 0 && closure->list[closure->idx] == table) 2171 return False; 2172 if (closure->idx >= closure->limit - 1) 2173 return True; 2174 /* append it */ 2175 closure->idx++; 2176 closure->list[closure->idx] = LOOSESEARCH; 2177 closure->idx++; 2178 closure->list[closure->idx] = table; 2179 return False; 2180} 2181 2182/* search for a leaf table */ 2183static Bool SearchNEntry( 2184 NTable table, 2185 XrmNameList names, 2186 XrmClassList classes, 2187 SClosure closure) 2188{ 2189 register NTable entry; 2190 register XrmQuark q; 2191 register unsigned int leaf; 2192 Bool (*get)( 2193 NTable table, 2194 XrmNameList names, 2195 XrmClassList classes, 2196 SClosure closure); 2197 2198 if (names[1]) { 2199 get = SearchNEntry; /* recurse */ 2200 leaf = 0; 2201 } else { 2202 get = (getNTableSProcp)AppendLEntry; /* bottom of recursion */ 2203 leaf = 1; 2204 } 2205 GTIGHTLOOSE(*names, AppendLooseLEntry); /* do name, tight and loose */ 2206 GTIGHTLOOSE(*classes, AppendLooseLEntry); /* do class, tight and loose */ 2207 if (table->hasany) { 2208 GTIGHTLOOSE(XrmQANY, AppendLooseLEntry); /* do ANY, tight and loose */ 2209 } 2210 if (table->hasloose) { 2211 while (1) { 2212 names++; 2213 classes++; 2214 if (!*names) 2215 break; 2216 if (!names[1]) { 2217 get = (getNTableSProcp)AppendLEntry; /* bottom of recursion */ 2218 leaf = 1; 2219 } 2220 GLOOSE(*names, AppendLooseLEntry); /* loose names */ 2221 GLOOSE(*classes, AppendLooseLEntry); /* loose classes */ 2222 if (table->hasany) { 2223 GLOOSE(XrmQANY, AppendLooseLEntry); /* loose ANY */ 2224 } 2225 } 2226 } 2227 /* now look for matching leaf nodes */ 2228 entry = table->next; 2229 if (!entry) 2230 return False; 2231 if (entry->leaf) { 2232 if (entry->tight && !table->tight) 2233 entry = entry->next; 2234 } else { 2235 entry = entry->next; 2236 if (!entry || !entry->tight) 2237 return False; 2238 } 2239 if (!entry || entry->name != table->name) 2240 return False; 2241 /* found one */ 2242 if (entry->hasloose && 2243 AppendLooseLEntry((LTable)entry, names, classes, closure)) 2244 return True; 2245 if (entry->tight && entry == table->next && (entry = entry->next) && 2246 entry->name == table->name && entry->hasloose) 2247 return AppendLooseLEntry((LTable)entry, names, classes, closure); 2248 return False; 2249} 2250 2251Bool XrmQGetSearchList( 2252 XrmDatabase db, 2253 XrmNameList names, 2254 XrmClassList classes, 2255 XrmSearchList searchList, /* RETURN */ 2256 int listLength) 2257{ 2258 register NTable table; 2259 SClosureRec closure; 2260 2261 if (listLength <= 0) 2262 return False; 2263 closure.list = (LTable *)searchList; 2264 closure.idx = -1; 2265 closure.limit = listLength - 2; 2266 if (db) { 2267 _XLockMutex(&db->linfo); 2268 table = db->table; 2269 if (*names) { 2270 if (table && !table->leaf) { 2271 if (SearchNEntry(table, names, classes, &closure)) { 2272 _XUnlockMutex(&db->linfo); 2273 return False; 2274 } 2275 } else if (table && table->hasloose && 2276 AppendLooseLEntry((LTable)table, names, classes, 2277 &closure)) { 2278 _XUnlockMutex(&db->linfo); 2279 return False; 2280 } 2281 } else { 2282 if (table && !table->leaf) 2283 table = table->next; 2284 if (table && 2285 AppendLEntry((LTable)table, names, classes, &closure)) { 2286 _XUnlockMutex(&db->linfo); 2287 return False; 2288 } 2289 } 2290 _XUnlockMutex(&db->linfo); 2291 } 2292 closure.list[closure.idx + 1] = (LTable)NULL; 2293 return True; 2294} 2295 2296Bool XrmQGetSearchResource( 2297 XrmSearchList searchList, 2298 register XrmName name, 2299 register XrmClass class, 2300 XrmRepresentation *pType, /* RETURN */ 2301 XrmValue *pValue) /* RETURN */ 2302{ 2303 register LTable *list; 2304 register LTable table; 2305 register VEntry entry = NULL; 2306 int flags; 2307 2308/* find tight or loose entry */ 2309#define VTIGHTLOOSE(q) \ 2310 entry = LeafHash(table, q); \ 2311 while (entry && entry->name != q) \ 2312 entry = entry->next; \ 2313 if (entry) \ 2314 break 2315 2316/* find loose entry */ 2317#define VLOOSE(q) \ 2318 entry = LeafHash(table, q); \ 2319 while (entry && entry->name != q) \ 2320 entry = entry->next; \ 2321 if (entry) { \ 2322 if (!entry->tight) \ 2323 break; \ 2324 if ((entry = entry->next) && entry->name == q) \ 2325 break; \ 2326 } 2327 2328 list = (LTable *)searchList; 2329 /* figure out which combination of name and class we need to search for */ 2330 flags = 0; 2331 if (IsResourceQuark(name)) 2332 flags = 2; 2333 if (IsResourceQuark(class)) 2334 flags |= 1; 2335 if (!flags) { 2336 /* neither name nor class has ever been used to name a resource */ 2337 table = (LTable)NULL; 2338 } else if (flags == 3) { 2339 /* both name and class */ 2340 while ((table = *list++)) { 2341 if (table != LOOSESEARCH) { 2342 VTIGHTLOOSE(name); /* do name, tight and loose */ 2343 VTIGHTLOOSE(class); /* do class, tight and loose */ 2344 } else { 2345 table = *list++; 2346 VLOOSE(name); /* do name, loose only */ 2347 VLOOSE(class); /* do class, loose only */ 2348 } 2349 } 2350 } else { 2351 /* just one of name or class */ 2352 if (flags == 1) 2353 name = class; 2354 while ((table = *list++)) { 2355 if (table != LOOSESEARCH) { 2356 VTIGHTLOOSE(name); /* tight and loose */ 2357 } else { 2358 table = *list++; 2359 VLOOSE(name); /* loose only */ 2360 } 2361 } 2362 } 2363 if (table) { 2364 /* found a match */ 2365 if (entry->string) { 2366 *pType = XrmQString; 2367 pValue->addr = StringValue(entry); 2368 } else { 2369 *pType = RepType(entry); 2370 pValue->addr = DataValue(entry); 2371 } 2372 pValue->size = entry->size; 2373 return True; 2374 } 2375 *pType = NULLQUARK; 2376 pValue->addr = (XPointer)NULL; 2377 pValue->size = 0; 2378 return False; 2379 2380#undef VTIGHTLOOSE 2381#undef VLOOSE 2382} 2383 2384/* look for a tight/loose value */ 2385static Bool GetVEntry( 2386 LTable table, 2387 XrmNameList names, 2388 XrmClassList classes, 2389 VClosure closure) 2390{ 2391 register VEntry entry; 2392 register XrmQuark q; 2393 2394 /* try name first */ 2395 q = *names; 2396 entry = LeafHash(table, q); 2397 while (entry && entry->name != q) 2398 entry = entry->next; 2399 if (!entry) { 2400 /* not found, try class */ 2401 q = *classes; 2402 entry = LeafHash(table, q); 2403 while (entry && entry->name != q) 2404 entry = entry->next; 2405 if (!entry) 2406 return False; 2407 } 2408 if (entry->string) { 2409 *closure->type = XrmQString; 2410 closure->value->addr = StringValue(entry); 2411 } else { 2412 *closure->type = RepType(entry); 2413 closure->value->addr = DataValue(entry); 2414 } 2415 closure->value->size = entry->size; 2416 return True; 2417} 2418 2419/* look for a loose value */ 2420static Bool GetLooseVEntry( 2421 LTable table, 2422 XrmNameList names, 2423 XrmClassList classes, 2424 VClosure closure) 2425{ 2426 register VEntry entry; 2427 register XrmQuark q; 2428 2429#define VLOOSE(ename) \ 2430 q = ename; \ 2431 entry = LeafHash(table, q); \ 2432 while (entry && entry->name != q) \ 2433 entry = entry->next; \ 2434 if (entry && entry->tight && (entry = entry->next) && entry->name != q) \ 2435 entry = (VEntry)NULL; 2436 2437 /* bump to last component */ 2438 while (names[1]) { 2439 names++; 2440 classes++; 2441 } 2442 VLOOSE(*names); /* do name, loose only */ 2443 if (!entry) { 2444 VLOOSE(*classes); /* do class, loose only */ 2445 if (!entry) 2446 return False; 2447 } 2448 if (entry->string) { 2449 *closure->type = XrmQString; 2450 closure->value->addr = StringValue(entry); 2451 } else { 2452 *closure->type = RepType(entry); 2453 closure->value->addr = DataValue(entry); 2454 } 2455 closure->value->size = entry->size; 2456 return True; 2457 2458#undef VLOOSE 2459} 2460 2461/* recursive search for a value */ 2462static Bool GetNEntry( 2463 NTable table, 2464 XrmNameList names, 2465 XrmClassList classes, 2466 VClosure closure) 2467{ 2468 register NTable entry; 2469 register XrmQuark q; 2470 register unsigned int leaf; 2471 Bool (*get)( 2472 NTable table, 2473 XrmNameList names, 2474 XrmClassList classes, 2475 VClosure closure); 2476 NTable otable; 2477 2478 if (names[2]) { 2479 get = GetNEntry; /* recurse */ 2480 leaf = 0; 2481 } else { 2482 get = (getNTableVProcp)GetVEntry; /* bottom of recursion */ 2483 leaf = 1; 2484 } 2485 GTIGHTLOOSE(*names, GetLooseVEntry); /* do name, tight and loose */ 2486 GTIGHTLOOSE(*classes, GetLooseVEntry); /* do class, tight and loose */ 2487 if (table->hasany) { 2488 GTIGHTLOOSE(XrmQANY, GetLooseVEntry); /* do ANY, tight and loose */ 2489 } 2490 if (table->hasloose) { 2491 while (1) { 2492 names++; 2493 classes++; 2494 if (!names[1]) 2495 break; 2496 if (!names[2]) { 2497 get = (getNTableVProcp)GetVEntry; /* bottom of recursion */ 2498 leaf = 1; 2499 } 2500 GLOOSE(*names, GetLooseVEntry); /* do name, loose only */ 2501 GLOOSE(*classes, GetLooseVEntry); /* do class, loose only */ 2502 if (table->hasany) { 2503 GLOOSE(XrmQANY, GetLooseVEntry); /* do ANY, loose only */ 2504 } 2505 } 2506 } 2507 /* look for matching leaf tables */ 2508 otable = table; 2509 table = table->next; 2510 if (!table) 2511 return False; 2512 if (table->leaf) { 2513 if (table->tight && !otable->tight) 2514 table = table->next; 2515 } else { 2516 table = table->next; 2517 if (!table || !table->tight) 2518 return False; 2519 } 2520 if (!table || table->name != otable->name) 2521 return False; 2522 /* found one */ 2523 if (table->hasloose && 2524 GetLooseVEntry((LTable)table, names, classes, closure)) 2525 return True; 2526 if (table->tight && table == otable->next) { 2527 table = table->next; 2528 if (table && table->name == otable->name && table->hasloose) 2529 return GetLooseVEntry((LTable)table, names, classes, closure); 2530 } 2531 return False; 2532} 2533 2534Bool XrmQGetResource( 2535 XrmDatabase db, 2536 XrmNameList names, 2537 XrmClassList classes, 2538 XrmRepresentation *pType, /* RETURN */ 2539 XrmValuePtr pValue) /* RETURN */ 2540{ 2541 register NTable table; 2542 VClosureRec closure; 2543 2544 if (db && *names) { 2545 _XLockMutex(&db->linfo); 2546 closure.type = pType; 2547 closure.value = pValue; 2548 table = db->table; 2549 if (names[1]) { 2550 if (table && !table->leaf) { 2551 if (GetNEntry(table, names, classes, &closure)) { 2552 _XUnlockMutex(&db->linfo); 2553 return True; 2554 } 2555 } else if (table && table->hasloose && 2556 GetLooseVEntry((LTable)table, names, classes, &closure)) { 2557 _XUnlockMutex (&db->linfo); 2558 return True; 2559 } 2560 } else { 2561 if (table && !table->leaf) 2562 table = table->next; 2563 if (table && GetVEntry((LTable)table, names, classes, &closure)) { 2564 _XUnlockMutex(&db->linfo); 2565 return True; 2566 } 2567 } 2568 _XUnlockMutex(&db->linfo); 2569 } 2570 *pType = NULLQUARK; 2571 pValue->addr = (XPointer)NULL; 2572 pValue->size = 0; 2573 return False; 2574} 2575 2576Bool 2577XrmGetResource(XrmDatabase db, _Xconst char *name_str, _Xconst char *class_str, 2578 XrmString *pType_str, XrmValuePtr pValue) 2579{ 2580 XrmName names[MAXDBDEPTH+1]; 2581 XrmClass classes[MAXDBDEPTH+1]; 2582 XrmRepresentation fromType; 2583 Bool result; 2584 2585 XrmStringToNameList(name_str, names); 2586 XrmStringToClassList(class_str, classes); 2587 result = XrmQGetResource(db, names, classes, &fromType, pValue); 2588 (*pType_str) = XrmQuarkToString(fromType); 2589 return result; 2590} 2591 2592/* destroy all values, plus table itself */ 2593static void DestroyLTable( 2594 LTable table) 2595{ 2596 register int i; 2597 register VEntry *buckets; 2598 register VEntry entry, next; 2599 2600 buckets = table->buckets; 2601 for (i = table->table.mask; i >= 0; i--, buckets++) { 2602 for (next = *buckets; (entry = next); ) { 2603 next = entry->next; 2604 Xfree((char *)entry); 2605 } 2606 } 2607 Xfree((char *)table->buckets); 2608 Xfree((char *)table); 2609} 2610 2611/* destroy all contained tables, plus table itself */ 2612static void DestroyNTable( 2613 NTable table) 2614{ 2615 register int i; 2616 register NTable *buckets; 2617 register NTable entry, next; 2618 2619 buckets = NodeBuckets(table); 2620 for (i = table->mask; i >= 0; i--, buckets++) { 2621 for (next = *buckets; (entry = next); ) { 2622 next = entry->next; 2623 if (entry->leaf) 2624 DestroyLTable((LTable)entry); 2625 else 2626 DestroyNTable(entry); 2627 } 2628 } 2629 Xfree((char *)table); 2630} 2631 2632const char * 2633XrmLocaleOfDatabase( 2634 XrmDatabase db) 2635{ 2636 const char* retval; 2637 _XLockMutex(&db->linfo); 2638 retval = (*db->methods->lcname)(db->mbstate); 2639 _XUnlockMutex(&db->linfo); 2640 return retval; 2641} 2642 2643void XrmDestroyDatabase( 2644 XrmDatabase db) 2645{ 2646 register NTable table, next; 2647 2648 if (db) { 2649 _XLockMutex(&db->linfo); 2650 for (next = db->table; (table = next); ) { 2651 next = table->next; 2652 if (table->leaf) 2653 DestroyLTable((LTable)table); 2654 else 2655 DestroyNTable(table); 2656 } 2657 _XUnlockMutex(&db->linfo); 2658 _XFreeMutex(&db->linfo); 2659 (*db->methods->destroy)(db->mbstate); 2660 Xfree((char *)db); 2661 } 2662} 2663