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