1/* 2 * tkOption.c -- 3 * 4 * This module contains functions to manage the option database, which 5 * allows various strings to be associated with windows either by name or 6 * by class or both. 7 * 8 * Copyright (c) 1990-1994 The Regents of the University of California. 9 * Copyright (c) 1994-1997 Sun Microsystems, Inc. 10 * 11 * See the file "license.terms" for information on usage and redistribution of 12 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 13 * 14 * RCS: @(#) $Id$ 15 */ 16 17#include "tkInt.h" 18 19/* 20 * The option database is stored as one tree for each main window. Each name 21 * or class field in an option is associated with a node or leaf of the tree. 22 * For example, the options "x.y.z" and "x.y*a" each correspond to three nodes 23 * in the tree; they share the nodes "x" and "x.y", but have different leaf 24 * nodes. One of the following structures exists for each node or leaf in the 25 * option tree. It is actually stored as part of the parent node, and 26 * describes a particular child of the parent. 27 * 28 * The structure of the option db tree is a little confusing. There are four 29 * different kinds of nodes in the tree: 30 * interior class nodes 31 * interior name nodes 32 * leaf class nodes 33 * leaf name nodes 34 * 35 * All interior nodes refer to _window_ classes and names; all leaf nodes 36 * refer to _option_ classes and names. When looking for a particular option, 37 * therefore, you must compare interior node values to corresponding window 38 * values, and compare leaf node values to corresponding option values. 39 * 40 * The tree is actually stored in a collection of arrays; there is one each 41 * combination of WILDCARD/EXACT and CLASS/NAME and NODE/LEAF. The NODE arrays 42 * contain the interior nodes of the tree; each element has a pointer to an 43 * array of elements which are the leaves of the tree. The LEAF arrays, rather 44 * than holding the leaves of the tree, hold a cached subset of the option 45 * database, consisting of the values of all defined options for a single 46 * window, and some additional information about each ancestor of the window 47 * (since some options may be inherited from a parent), all the way back to 48 * the root window. 49 * 50 * Each time a call is made to Tk_GetOption, Tk will attempt to use the cached 51 * information to satisfy the lookup. If the call is for a window other than 52 * that for which options are currently cached, the portion of the cache that 53 * contains information for common ancestors of the two windows is retained 54 * and the remainder is discarded and rebuilt with new information for the new 55 * window. 56 */ 57 58typedef struct Element { 59 Tk_Uid nameUid; /* Name or class from one element of an option 60 * spec. */ 61 union { 62 struct ElArray *arrayPtr; 63 /* If this is an intermediate node, a pointer 64 * to a structure describing the remaining 65 * elements of all options whose prefixes are 66 * the same up through this element. */ 67 Tk_Uid valueUid; /* For leaf nodes, this is the string value of 68 * the option. */ 69 } child; 70 int priority; /* Used to select among matching options. 71 * Includes both the priority level and a 72 * serial #. Greater value means higher 73 * priority. Irrelevant except in leaf 74 * nodes. */ 75 int flags; /* OR-ed combination of bits. See below for 76 * values. */ 77} Element; 78 79/* 80 * Flags in Element structures: 81 * 82 * CLASS - Non-zero means this element refers to a class, zero 83 * means this element refers to a name. 84 * NODE - Zero means this is a leaf element (the child field is 85 * a value, not a pointer to another node). One means 86 * this is a node element. 87 * WILDCARD - Non-zero means this there was a star in the original 88 * specification just before this element. Zero means 89 * there was a dot. 90 */ 91 92#define TYPE_MASK 0x7 93 94#define CLASS 0x1 95#define NODE 0x2 96#define WILDCARD 0x4 97 98#define EXACT_LEAF_NAME 0x0 99#define EXACT_LEAF_CLASS 0x1 100#define EXACT_NODE_NAME 0x2 101#define EXACT_NODE_CLASS 0x3 102#define WILDCARD_LEAF_NAME 0x4 103#define WILDCARD_LEAF_CLASS 0x5 104#define WILDCARD_NODE_NAME 0x6 105#define WILDCARD_NODE_CLASS 0x7 106 107/* 108 * The following structure is used to manage a dynamic array of Elements. 109 * These structures are used for two purposes: to store the contents of a node 110 * in the option tree, and for the option stacks described below. 111 */ 112 113typedef struct ElArray { 114 int arraySize; /* Number of elements actually allocated in 115 * the "els" array. */ 116 int numUsed; /* Number of elements currently in use out of 117 * els. */ 118 Element *nextToUse; /* Pointer to &els[numUsed]. */ 119 Element els[1]; /* Array of structures describing children of 120 * this node. The array will actually contain 121 * enough elements for all of the children 122 * (and even a few extras, perhaps). This must 123 * be the last field in the structure. */ 124} ElArray; 125 126#define EL_ARRAY_SIZE(numEls) ((unsigned) (sizeof(ElArray) \ 127 + ((numEls)-1)*sizeof(Element))) 128#define INITIAL_SIZE 5 129 130/* 131 * In addition to the option tree, which is a relatively static structure, 132 * there are eight additional structures called "stacks", which are used to 133 * speed up queries into the option database. The stack structures are 134 * designed for the situation where an individual widget makes repeated 135 * requests for its particular options. The requests differ only in their last 136 * name/class, so during the first request we extract all the options 137 * pertaining to the particular widget and save them in a stack-like cache; 138 * subsequent requests for the same widget can search the cache relatively 139 * quickly. In fact, the cache is a hierarchical one, storing a list of 140 * relevant options for this widget and all of its ancestors up to the 141 * application root; hence the name "stack". 142 * 143 * Each of the eight stacks consists of an array of Elements, ordered in terms 144 * of levels in the window hierarchy. All the elements relevant for the 145 * top-level widget appear first in the array, followed by all those from the 146 * next-level widget on the path to the current widget, etc. down to those for 147 * the current widget. 148 * 149 * Cached information is divided into eight stacks according to the CLASS, 150 * NODE, and WILDCARD flags. Leaf and non-leaf information is kept separate to 151 * speed up individual probes (non-leaf information is only relevant when 152 * building the stacks, but isn't relevant when making probes; similarly, only 153 * non-leaf information is relevant when the stacks are being extended to the 154 * next widget down in the widget hierarchy). Wildcard elements are handled 155 * separately from "exact" elements because once they appear at a particular 156 * level in the stack they remain active for all deeper levels; exact elements 157 * are only relevant at a particular level. For example, when searching for 158 * options relevant in a particular window, the entire wildcard stacks get 159 * checked, but only the portions of the exact stacks that pertain to the 160 * window's parent. Lastly, name and class stacks are kept separate because 161 * different search keys are used when searching them; keeping them separate 162 * speeds up the searches. 163 */ 164 165#define NUM_STACKS 8 166 167/* 168 * One of the following structures is used to keep track of each level in the 169 * stacks. 170 */ 171 172typedef struct StackLevel { 173 TkWindow *winPtr; /* Window corresponding to this stack 174 * level. */ 175 int bases[NUM_STACKS]; /* For each stack, index of first element on 176 * stack corresponding to this level (used to 177 * restore "numUsed" fields when popping out 178 * of a level. */ 179} StackLevel; 180 181typedef struct ThreadSpecificData { 182 int initialized; /* 0 means the ThreadSpecific Data structure 183 * for the current thread needs to be 184 * initialized. */ 185 ElArray *stacks[NUM_STACKS]; 186 TkWindow *cachedWindow; /* Lowest-level window currently loaded in 187 * stacks at present. NULL means stacks have 188 * never been used, or have been invalidated 189 * because of a change to the database. */ 190 /* 191 * Information about all of the stack levels that are currently active. 192 * This array grows dynamically to become as large as needed. 193 */ 194 195 StackLevel *levels; /* Array describing current stack. */ 196 int numLevels; /* Total space allocated. */ 197 int curLevel; /* Highest level currently in use. Note: 198 * curLevel is never 0! (I don't remember why 199 * anymore...) */ 200 int serial; /* A serial number for all options entered 201 * into the database so far. It increments on 202 * each addition to the option database. It is 203 * used in computing option priorities, so 204 * that the most recent entry wins when 205 * choosing between options at the same 206 * priority level. */ 207 Element defaultMatch; /* Special "no match" Element to use as 208 * default for searches.*/ 209} ThreadSpecificData; 210static Tcl_ThreadDataKey dataKey; 211 212/* 213 * Forward declarations for functions defined in this file: 214 */ 215 216static int AddFromString(Tcl_Interp *interp, Tk_Window tkwin, 217 char *string, int priority); 218static void ClearOptionTree(ElArray *arrayPtr); 219static ElArray * ExtendArray(ElArray *arrayPtr, Element *elPtr); 220static void ExtendStacks(ElArray *arrayPtr, int leaf); 221static int GetDefaultOptions(Tcl_Interp *interp, 222 TkWindow *winPtr); 223static ElArray * NewArray(int numEls); 224static void OptionThreadExitProc(ClientData clientData); 225static void OptionInit(TkMainInfo *mainPtr); 226static int ParsePriority(Tcl_Interp *interp, char *string); 227static int ReadOptionFile(Tcl_Interp *interp, Tk_Window tkwin, 228 char *fileName, int priority); 229static void SetupStacks(TkWindow *winPtr, int leaf); 230 231/* 232 *-------------------------------------------------------------- 233 * 234 * Tk_AddOption -- 235 * 236 * Add a new option to the option database. 237 * 238 * Results: 239 * None. 240 * 241 * Side effects: 242 * Information is added to the option database. 243 * 244 *-------------------------------------------------------------- 245 */ 246 247void 248Tk_AddOption( 249 Tk_Window tkwin, /* Window token; option will be associated 250 * with main window for this window. */ 251 CONST char *name, /* Multi-element name of option. */ 252 CONST char *value, /* String value for option. */ 253 int priority) /* Overall priority level to use for this 254 * option, such as TK_USER_DEFAULT_PRIO or 255 * TK_INTERACTIVE_PRIO. Must be between 0 and 256 * TK_MAX_PRIO. */ 257{ 258 TkWindow *winPtr = ((TkWindow *) tkwin)->mainPtr->winPtr; 259 register ElArray **arrayPtrPtr; 260 register Element *elPtr; 261 Element newEl; 262 register CONST char *p; 263 CONST char *field; 264 int count, firstField; 265 ptrdiff_t length; 266#define TMP_SIZE 100 267 char tmp[TMP_SIZE+1]; 268 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 269 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 270 271 if (winPtr->mainPtr->optionRootPtr == NULL) { 272 OptionInit(winPtr->mainPtr); 273 } 274 tsdPtr->cachedWindow = NULL;/* Invalidate the cache. */ 275 276 /* 277 * Compute the priority for the new element, including both the overall 278 * level and the serial number (to disambiguate with the level). 279 */ 280 281 if (priority < 0) { 282 priority = 0; 283 } else if (priority > TK_MAX_PRIO) { 284 priority = TK_MAX_PRIO; 285 } 286 newEl.priority = (priority << 24) + tsdPtr->serial; 287 tsdPtr->serial++; 288 289 /* 290 * Parse the option one field at a time. 291 */ 292 293 arrayPtrPtr = &(((TkWindow *) tkwin)->mainPtr->optionRootPtr); 294 p = name; 295 for (firstField = 1; ; firstField = 0) { 296 /* 297 * Scan the next field from the name and convert it to a Tk_Uid. Must 298 * copy the field before calling Tk_Uid, so that a terminating NULL 299 * may be added without modifying the source string. 300 */ 301 302 if (*p == '*') { 303 newEl.flags = WILDCARD; 304 p++; 305 } else { 306 newEl.flags = 0; 307 } 308 field = p; 309 while ((*p != 0) && (*p != '.') && (*p != '*')) { 310 p++; 311 } 312 length = p - field; 313 if (length > TMP_SIZE) { 314 length = TMP_SIZE; 315 } 316 strncpy(tmp, field, (size_t) length); 317 tmp[length] = 0; 318 newEl.nameUid = Tk_GetUid(tmp); 319 if (isupper(UCHAR(*field))) { 320 newEl.flags |= CLASS; 321 } 322 323 if (*p != 0) { 324 /* 325 * New element will be a node. If this option can't possibly apply 326 * to this main window, then just skip it. Otherwise, add it to 327 * the parent, if it isn't already there, and descend into it. 328 */ 329 330 newEl.flags |= NODE; 331 if (firstField && !(newEl.flags & WILDCARD) 332 && (newEl.nameUid != winPtr->nameUid) 333 && (newEl.nameUid != winPtr->classUid)) { 334 return; 335 } 336 for (elPtr = (*arrayPtrPtr)->els, count = (*arrayPtrPtr)->numUsed; 337 ; elPtr++, count--) { 338 if (count == 0) { 339 newEl.child.arrayPtr = NewArray(5); 340 *arrayPtrPtr = ExtendArray(*arrayPtrPtr, &newEl); 341 arrayPtrPtr = &((*arrayPtrPtr) 342 ->nextToUse[-1].child.arrayPtr); 343 break; 344 } 345 if ((elPtr->nameUid == newEl.nameUid) 346 && (elPtr->flags == newEl.flags)) { 347 arrayPtrPtr = &(elPtr->child.arrayPtr); 348 break; 349 } 350 } 351 if (*p == '.') { 352 p++; 353 } 354 } else { 355 /* 356 * New element is a leaf. Add it to the parent, if it isn't 357 * already there. If it exists already, keep whichever value has 358 * highest priority. 359 */ 360 361 newEl.child.valueUid = Tk_GetUid(value); 362 for (elPtr = (*arrayPtrPtr)->els, count = (*arrayPtrPtr)->numUsed; 363 ; elPtr++, count--) { 364 if (count == 0) { 365 *arrayPtrPtr = ExtendArray(*arrayPtrPtr, &newEl); 366 return; 367 } 368 if ((elPtr->nameUid == newEl.nameUid) 369 && (elPtr->flags == newEl.flags)) { 370 if (elPtr->priority < newEl.priority) { 371 elPtr->priority = newEl.priority; 372 elPtr->child.valueUid = newEl.child.valueUid; 373 } 374 return; 375 } 376 } 377 } 378 } 379} 380 381/* 382 *-------------------------------------------------------------- 383 * 384 * Tk_GetOption -- 385 * 386 * Retrieve an option from the option database. 387 * 388 * Results: 389 * The return value is the value specified in the option database for the 390 * given name and class on the given window. If there is nothing 391 * specified in the database for that option, then NULL is returned. 392 * 393 * Side effects: 394 * The internal caches used to speed up option mapping may be modified, 395 * if this tkwin is different from the last tkwin used for option 396 * retrieval. 397 * 398 *-------------------------------------------------------------- 399 */ 400 401Tk_Uid 402Tk_GetOption( 403 Tk_Window tkwin, /* Token for window that option is associated 404 * with. */ 405 CONST char *name, /* Name of option. */ 406 CONST char *className) /* Class of option. NULL means there is no 407 * class for this option: just check for 408 * name. */ 409{ 410 Tk_Uid nameId, classId = NULL; 411 char *masqName; 412 register Element *elPtr, *bestPtr; 413 register int count; 414 StackLevel *levelPtr; 415 int stackDepth[NUM_STACKS]; 416 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 417 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 418 419 /* 420 * Note: no need to call OptionInit here: it will be done by the 421 * SetupStacks call below (squeeze out those nanoseconds). 422 */ 423 424 if (tkwin != (Tk_Window) tsdPtr->cachedWindow) { 425 SetupStacks((TkWindow *) tkwin, 1); 426 } 427 428 /* 429 * Get a default "best" match. 430 */ 431 432 bestPtr = &tsdPtr->defaultMatch; 433 434 /* 435 * For megawidget support, we want to have some widget options masquerade 436 * as options for other widgets. For example, a combobox has a button in 437 * it; this button ought to pick up the *Button.background, etc., options. 438 * But because the class of the widget is Combobox, our normal search 439 * won't get that option. 440 * 441 * To work around this, the option name field syntax was extended to allow 442 * for a "." in the name; if this character occurs in the name, then it 443 * indicates that this name contains a new window class and an option 444 * name, ie, "Button.foreground". If we see this form in the name field, 445 * we query the option database directly (since the option stacks will not 446 * have the information we need). 447 */ 448 449 masqName = strchr(name, (int)'.'); 450 if (masqName != NULL) { 451 /* 452 * This option is masquerading with a different window class. Search 453 * the stack to the depth it was before the current window's 454 * information was pushed (the value for which is stored in the bases 455 * field). 456 */ 457 458 levelPtr = &tsdPtr->levels[tsdPtr->curLevel]; 459 nameId = Tk_GetUid(masqName+1); 460 for (count = 0; count < NUM_STACKS; count++) { 461 stackDepth[count] = levelPtr->bases[count]; 462 } 463 } else { 464 /* 465 * No option masquerading here. Just use the current level to get the 466 * stack depths. 467 */ 468 469 nameId = Tk_GetUid(name); 470 for (count = 0; count < NUM_STACKS; count++) { 471 stackDepth[count] = tsdPtr->stacks[count]->numUsed; 472 } 473 } 474 475 /* 476 * Probe the stacks for matches. 477 */ 478 479 for (elPtr = tsdPtr->stacks[EXACT_LEAF_NAME]->els, 480 count = stackDepth[EXACT_LEAF_NAME]; count > 0; 481 elPtr++, count--) { 482 if ((elPtr->nameUid == nameId) 483 && (elPtr->priority > bestPtr->priority)) { 484 bestPtr = elPtr; 485 } 486 } 487 for (elPtr = tsdPtr->stacks[WILDCARD_LEAF_NAME]->els, 488 count = stackDepth[WILDCARD_LEAF_NAME]; count > 0; 489 elPtr++, count--) { 490 if ((elPtr->nameUid == nameId) 491 && (elPtr->priority > bestPtr->priority)) { 492 bestPtr = elPtr; 493 } 494 } 495 496 if (className != NULL) { 497 classId = Tk_GetUid(className); 498 for (elPtr = tsdPtr->stacks[EXACT_LEAF_CLASS]->els, 499 count = stackDepth[EXACT_LEAF_CLASS]; count > 0; 500 elPtr++, count--) { 501 if ((elPtr->nameUid == classId) 502 && (elPtr->priority > bestPtr->priority)) { 503 bestPtr = elPtr; 504 } 505 } 506 for (elPtr = tsdPtr->stacks[WILDCARD_LEAF_CLASS]->els, 507 count = stackDepth[WILDCARD_LEAF_CLASS]; count > 0; 508 elPtr++, count--) { 509 if ((elPtr->nameUid == classId) 510 && (elPtr->priority > bestPtr->priority)) { 511 bestPtr = elPtr; 512 } 513 } 514 } 515 516 /* 517 * If this option was masquerading with a different window class, probe 518 * the option database now. Note that this will be inefficient if the 519 * option database is densely populated, or if the widget has many 520 * masquerading options. 521 */ 522 523 if (masqName != NULL) { 524 char *masqClass; 525 Tk_Uid nodeId, winClassId, winNameId; 526 unsigned int classNameLength; 527 register Element *nodePtr, *leafPtr; 528 static int searchOrder[] = { 529 EXACT_NODE_NAME, WILDCARD_NODE_NAME, EXACT_NODE_CLASS, 530 WILDCARD_NODE_CLASS, -1 531 }; 532 int *currentPtr, currentStack, leafCount; 533 534 /* 535 * Extract the masquerade class name from the name field. 536 */ 537 538 classNameLength = (unsigned int)(masqName - name); 539 masqClass = (char *) ckalloc(classNameLength + 1); 540 strncpy(masqClass, name, classNameLength); 541 masqClass[classNameLength] = '\0'; 542 543 winClassId = Tk_GetUid(masqClass); 544 ckfree(masqClass); 545 winNameId = ((TkWindow *)tkwin)->nameUid; 546 547 levelPtr = &tsdPtr->levels[tsdPtr->curLevel]; 548 549 for (currentPtr = searchOrder; *currentPtr != -1; currentPtr++) { 550 currentStack = *currentPtr; 551 nodePtr = tsdPtr->stacks[currentStack]->els; 552 count = levelPtr->bases[currentStack]; 553 554 /* 555 * For wildcard stacks, check all entries; for non-wildcard 556 * stacks, only check things that matched in the parent. 557 */ 558 559 if (!(currentStack & WILDCARD)) { 560 nodePtr += levelPtr[-1].bases[currentStack]; 561 count -= levelPtr[-1].bases[currentStack]; 562 } 563 564 if (currentStack && CLASS) { 565 nodeId = winClassId; 566 } else { 567 nodeId = winNameId; 568 } 569 570 for ( ; count > 0; nodePtr++, count--) { 571 if (nodePtr->nameUid == nodeId) { 572 leafPtr = nodePtr->child.arrayPtr->els; 573 leafCount = nodePtr->child.arrayPtr->numUsed; 574 for ( ; leafCount > 0; leafPtr++, leafCount--) { 575 if (leafPtr->flags & CLASS && className != NULL) { 576 if (leafPtr->nameUid == classId && 577 leafPtr->priority > bestPtr->priority) { 578 bestPtr = leafPtr; 579 } 580 } else { 581 if (leafPtr->nameUid == nameId && 582 leafPtr->priority > bestPtr->priority) { 583 bestPtr = leafPtr; 584 } 585 } 586 } 587 } 588 } 589 } 590 } 591 592 return bestPtr->child.valueUid; 593} 594 595/* 596 *-------------------------------------------------------------- 597 * 598 * Tk_OptionObjCmd -- 599 * 600 * This function is invoked to process the "option" Tcl command. See the 601 * user documentation for details on what it does. 602 * 603 * Results: 604 * A standard Tcl result. 605 * 606 * Side effects: 607 * See the user documentation. 608 * 609 *-------------------------------------------------------------- 610 */ 611 612int 613Tk_OptionObjCmd( 614 ClientData clientData, /* Main window associated with interpreter. */ 615 Tcl_Interp *interp, /* Current interpreter. */ 616 int objc, /* Number of Tcl_Obj arguments. */ 617 Tcl_Obj *CONST objv[]) /* Tcl_Obj arguments. */ 618{ 619 Tk_Window tkwin = (Tk_Window) clientData; 620 int index, result; 621 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 622 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 623 624 static CONST char *optionCmds[] = { 625 "add", "clear", "get", "readfile", NULL 626 }; 627 628 enum optionVals { 629 OPTION_ADD, OPTION_CLEAR, OPTION_GET, OPTION_READFILE 630 }; 631 632 if (objc < 2) { 633 Tcl_WrongNumArgs(interp, 1, objv, "cmd arg ?arg ...?"); 634 return TCL_ERROR; 635 } 636 637 result = Tcl_GetIndexFromObj(interp, objv[1], optionCmds, "option", 0, 638 &index); 639 if (result != TCL_OK) { 640 return result; 641 } 642 643 result = TCL_OK; 644 switch ((enum optionVals) index) { 645 case OPTION_ADD: { 646 int priority; 647 if ((objc != 4) && (objc != 5)) { 648 Tcl_WrongNumArgs(interp, 2, objv, "pattern value ?priority?"); 649 return TCL_ERROR; 650 } 651 652 if (objc == 4) { 653 priority = TK_INTERACTIVE_PRIO; 654 } else { 655 priority = ParsePriority(interp, Tcl_GetString(objv[4])); 656 if (priority < 0) { 657 return TCL_ERROR; 658 } 659 } 660 Tk_AddOption(tkwin, Tcl_GetString(objv[2]), Tcl_GetString(objv[3]), 661 priority); 662 break; 663 } 664 665 case OPTION_CLEAR: { 666 TkMainInfo *mainPtr; 667 668 if (objc != 2) { 669 Tcl_WrongNumArgs(interp, 2, objv, ""); 670 return TCL_ERROR; 671 } 672 mainPtr = ((TkWindow *) tkwin)->mainPtr; 673 if (mainPtr->optionRootPtr != NULL) { 674 ClearOptionTree(mainPtr->optionRootPtr); 675 mainPtr->optionRootPtr = NULL; 676 } 677 tsdPtr->cachedWindow = NULL; 678 break; 679 } 680 681 case OPTION_GET: { 682 Tk_Window window; 683 Tk_Uid value; 684 685 if (objc != 5) { 686 Tcl_WrongNumArgs(interp, 2, objv, "window name class"); 687 return TCL_ERROR; 688 } 689 window = Tk_NameToWindow(interp, Tcl_GetString(objv[2]), tkwin); 690 if (window == NULL) { 691 return TCL_ERROR; 692 } 693 value = Tk_GetOption(window, Tcl_GetString(objv[3]), 694 Tcl_GetString(objv[4])); 695 if (value != NULL) { 696 Tcl_SetResult(interp, (char *)value, TCL_STATIC); 697 } 698 break; 699 } 700 701 case OPTION_READFILE: { 702 int priority; 703 704 if ((objc != 3) && (objc != 4)) { 705 Tcl_WrongNumArgs(interp, 2, objv, "fileName ?priority?"); 706 return TCL_ERROR; 707 } 708 709 if (objc == 4) { 710 priority = ParsePriority(interp, Tcl_GetString(objv[3])); 711 if (priority < 0) { 712 return TCL_ERROR; 713 } 714 } else { 715 priority = TK_INTERACTIVE_PRIO; 716 } 717 result = ReadOptionFile(interp, tkwin, Tcl_GetString(objv[2]), 718 priority); 719 break; 720 } 721 } 722 return result; 723} 724 725/* 726 *-------------------------------------------------------------- 727 * 728 * TkOptionDeadWindow -- 729 * 730 * This function is called whenever a window is deleted. It cleans up any 731 * option-related stuff associated with the window. 732 * 733 * Results: 734 * None. 735 * 736 * Side effects: 737 * Option-related resources are freed. See code below for details. 738 * 739 *-------------------------------------------------------------- 740 */ 741 742void 743TkOptionDeadWindow( 744 register TkWindow *winPtr) /* Window to be cleaned up. */ 745{ 746 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 747 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 748 749 /* 750 * If this window is in the option stacks, then clear the stacks. 751 * 752 * XXX: OptionThreadExitProc will be invoked before DeleteWindowsExitProc 753 * XXX: if it is thread-specific (which it should be), invalidating the 754 * XXX: tsd. Tk shutdown needs to be verified to handle this correctly. 755 */ 756 757 if (tsdPtr->initialized && (winPtr->optionLevel != -1)) { 758 int i; 759 760 for (i = 1; i <= tsdPtr->curLevel; i++) { 761 tsdPtr->levels[i].winPtr->optionLevel = -1; 762 } 763 tsdPtr->curLevel = -1; 764 tsdPtr->cachedWindow = NULL; 765 } 766 767 /* 768 * If this window was a main window, then delete its option database. 769 */ 770 771 if ((winPtr->mainPtr != NULL) && (winPtr->mainPtr->winPtr == winPtr) 772 && (winPtr->mainPtr->optionRootPtr != NULL)) { 773 ClearOptionTree(winPtr->mainPtr->optionRootPtr); 774 winPtr->mainPtr->optionRootPtr = NULL; 775 } 776} 777 778/* 779 *---------------------------------------------------------------------- 780 * 781 * TkOptionClassChanged -- 782 * 783 * This function is invoked when a window's class changes. If the window 784 * is on the option cache, this function flushes any information for the 785 * window, since the new class could change what is relevant. 786 * 787 * Results: 788 * None. 789 * 790 * Side effects: 791 * The option cache may be flushed in part or in whole. 792 * 793 *---------------------------------------------------------------------- 794 */ 795 796void 797TkOptionClassChanged( 798 TkWindow *winPtr) /* Window whose class changed. */ 799{ 800 int i, j, *basePtr; 801 ElArray *arrayPtr; 802 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 803 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 804 805 if (winPtr->optionLevel == -1) { 806 return; 807 } 808 809 /* 810 * Find the lowest stack level that refers to this window, then flush all 811 * of the levels above the matching one. 812 */ 813 814 for (i = 1; i <= tsdPtr->curLevel; i++) { 815 if (tsdPtr->levels[i].winPtr == winPtr) { 816 for (j = i; j <= tsdPtr->curLevel; j++) { 817 tsdPtr->levels[j].winPtr->optionLevel = -1; 818 } 819 tsdPtr->curLevel = i-1; 820 basePtr = tsdPtr->levels[i].bases; 821 for (j = 0; j < NUM_STACKS; j++) { 822 arrayPtr = tsdPtr->stacks[j]; 823 arrayPtr->numUsed = basePtr[j]; 824 arrayPtr->nextToUse = &arrayPtr->els[arrayPtr->numUsed]; 825 } 826 if (tsdPtr->curLevel <= 0) { 827 tsdPtr->cachedWindow = NULL; 828 } else { 829 tsdPtr->cachedWindow = tsdPtr->levels[tsdPtr->curLevel].winPtr; 830 } 831 break; 832 } 833 } 834} 835 836/* 837 *---------------------------------------------------------------------- 838 * 839 * ParsePriority -- 840 * 841 * Parse a string priority value. 842 * 843 * Results: 844 * The return value is the integer priority level corresponding to 845 * string, or -1 if string doesn't point to a valid priority level. In 846 * this case, an error message is left in the interp's result. 847 * 848 * Side effects: 849 * None. 850 * 851 *---------------------------------------------------------------------- 852 */ 853 854static int 855ParsePriority( 856 Tcl_Interp *interp, /* Interpreter to use for error reporting. */ 857 char *string) /* Describes a priority level, either 858 * symbolically or numerically. */ 859{ 860 int priority, c; 861 size_t length; 862 863 c = string[0]; 864 length = strlen(string); 865 if ((c == 'w') 866 && (strncmp(string, "widgetDefault", length) == 0)) { 867 return TK_WIDGET_DEFAULT_PRIO; 868 } else if ((c == 's') 869 && (strncmp(string, "startupFile", length) == 0)) { 870 return TK_STARTUP_FILE_PRIO; 871 } else if ((c == 'u') 872 && (strncmp(string, "userDefault", length) == 0)) { 873 return TK_USER_DEFAULT_PRIO; 874 } else if ((c == 'i') 875 && (strncmp(string, "interactive", length) == 0)) { 876 return TK_INTERACTIVE_PRIO; 877 } else { 878 char *end; 879 880 priority = strtoul(string, &end, 0); 881 if ((end == string) || (*end != 0) || (priority < 0) 882 || (priority > 100)) { 883 Tcl_AppendResult(interp, "bad priority level \"", string, 884 "\": must be widgetDefault, startupFile, userDefault, ", 885 "interactive, or a number between 0 and 100", NULL); 886 return -1; 887 } 888 } 889 return priority; 890} 891 892/* 893 *---------------------------------------------------------------------- 894 * 895 * AddFromString -- 896 * 897 * Given a string containing lines in the standard format for X resources 898 * (see other documentation for details on what this is), parse the 899 * resource specifications and enter them as options for tkwin's main 900 * window. 901 * 902 * Results: 903 * The return value is a standard Tcl return code. In the case of an 904 * error in parsing string, TCL_ERROR will be returned and an error 905 * message will be left in the interp's result. The memory at string is 906 * totally trashed by this function. If you care about its contents, make 907 * a copy before calling here. 908 * 909 * Side effects: 910 * None. 911 * 912 *---------------------------------------------------------------------- 913 */ 914 915static int 916AddFromString( 917 Tcl_Interp *interp, /* Interpreter to use for reporting results. */ 918 Tk_Window tkwin, /* Token for window: options are entered for 919 * this window's main window. */ 920 char *string, /* String containing option specifiers. */ 921 int priority) /* Priority level to use for options in this 922 * string, such as TK_USER_DEFAULT_PRIO or 923 * TK_INTERACTIVE_PRIO. Must be between 0 and 924 * TK_MAX_PRIO. */ 925{ 926 register char *src, *dst; 927 char *name, *value; 928 int lineNum; 929 930 src = string; 931 lineNum = 1; 932 while (1) { 933 934 /* 935 * Skip leading white space and empty lines and comment lines, and 936 * check for the end of the spec. 937 */ 938 939 while ((*src == ' ') || (*src == '\t')) { 940 src++; 941 } 942 if ((*src == '#') || (*src == '!')) { 943 do { 944 src++; 945 if ((src[0] == '\\') && (src[1] == '\n')) { 946 src += 2; 947 lineNum++; 948 } 949 } while ((*src != '\n') && (*src != 0)); 950 } 951 if (*src == '\n') { 952 src++; 953 lineNum++; 954 continue; 955 } 956 if (*src == '\0') { 957 break; 958 } 959 960 /* 961 * Parse off the option name, collapsing out backslash-newline 962 * sequences of course. 963 */ 964 965 dst = name = src; 966 while (*src != ':') { 967 if ((*src == '\0') || (*src == '\n')) { 968 char buf[32 + TCL_INTEGER_SPACE]; 969 970 sprintf(buf, "missing colon on line %d", lineNum); 971 Tcl_SetResult(interp, buf, TCL_VOLATILE); 972 return TCL_ERROR; 973 } 974 if ((src[0] == '\\') && (src[1] == '\n')) { 975 src += 2; 976 lineNum++; 977 } else { 978 *dst = *src; 979 dst++; 980 src++; 981 } 982 } 983 984 /* 985 * Eliminate trailing white space on the name, and null-terminate 986 * it. 987 */ 988 989 while ((dst != name) && ((dst[-1] == ' ') || (dst[-1] == '\t'))) { 990 dst--; 991 } 992 *dst = '\0'; 993 994 /* 995 * Skip white space between the name and the value. 996 */ 997 998 src++; 999 while ((*src == ' ') || (*src == '\t')) { 1000 src++; 1001 } 1002 if (*src == '\0') { 1003 char buf[32 + TCL_INTEGER_SPACE]; 1004 1005 sprintf(buf, "missing value on line %d", lineNum); 1006 Tcl_SetResult(interp, buf, TCL_VOLATILE); 1007 return TCL_ERROR; 1008 } 1009 1010 /* 1011 * Parse off the value, squeezing out backslash-newline sequences 1012 * along the way. 1013 */ 1014 1015 dst = value = src; 1016 while (*src != '\n') { 1017 if (*src == '\0') { 1018 char buf[32 + TCL_INTEGER_SPACE]; 1019 1020 sprintf(buf, "missing newline on line %d", lineNum); 1021 Tcl_SetResult(interp, buf, TCL_VOLATILE); 1022 return TCL_ERROR; 1023 } 1024 if ((src[0] == '\\') && (src[1] == '\n')) { 1025 src += 2; 1026 lineNum++; 1027 } else { 1028 *dst = *src; 1029 dst++; 1030 src++; 1031 } 1032 } 1033 *dst = 0; 1034 1035 /* 1036 * Enter the option into the database. 1037 */ 1038 1039 Tk_AddOption(tkwin, name, value, priority); 1040 src++; 1041 lineNum++; 1042 } 1043 return TCL_OK; 1044} 1045 1046/* 1047 *---------------------------------------------------------------------- 1048 * 1049 * ReadOptionFile -- 1050 * 1051 * Read a file of options ("resources" in the old X terminology) and load 1052 * them into the option database. 1053 * 1054 * Results: 1055 * The return value is a standard Tcl return code. In the case of an 1056 * error in parsing string, TCL_ERROR will be returned and an error 1057 * message will be left in the interp's result. 1058 * 1059 * Side effects: 1060 * None. 1061 * 1062 *---------------------------------------------------------------------- 1063 */ 1064 1065static int 1066ReadOptionFile( 1067 Tcl_Interp *interp, /* Interpreter to use for reporting results. */ 1068 Tk_Window tkwin, /* Token for window: options are entered for 1069 * this window's main window. */ 1070 char *fileName, /* Name of file containing options. */ 1071 int priority) /* Priority level to use for options in this 1072 * file, such as TK_USER_DEFAULT_PRIO or 1073 * TK_INTERACTIVE_PRIO. Must be between 0 and 1074 * TK_MAX_PRIO. */ 1075{ 1076 CONST char *realName; 1077 char *buffer; 1078 int result, bufferSize; 1079 Tcl_Channel chan; 1080 Tcl_DString newName; 1081 1082 /* 1083 * Prevent file system access in a safe interpreter. 1084 */ 1085 1086 if (Tcl_IsSafe(interp)) { 1087 Tcl_AppendResult(interp, "can't read options from a file in a", 1088 " safe interpreter", NULL); 1089 return TCL_ERROR; 1090 } 1091 1092 realName = Tcl_TranslateFileName(interp, fileName, &newName); 1093 if (realName == NULL) { 1094 return TCL_ERROR; 1095 } 1096 chan = Tcl_OpenFileChannel(interp, realName, "r", 0); 1097 Tcl_DStringFree(&newName); 1098 if (chan == NULL) { 1099 Tcl_ResetResult(interp); 1100 Tcl_AppendResult(interp, "couldn't open \"", fileName, 1101 "\": ", Tcl_PosixError(interp), NULL); 1102 return TCL_ERROR; 1103 } 1104 1105 /* 1106 * Compute size of file by seeking to the end of the file. This will 1107 * overallocate if we are performing CRLF translation. 1108 */ 1109 1110 bufferSize = (int) Tcl_Seek(chan, (Tcl_WideInt) 0, SEEK_END); 1111 (void) Tcl_Seek(chan, (Tcl_WideInt) 0, SEEK_SET); 1112 1113 if (bufferSize < 0) { 1114 Tcl_AppendResult(interp, "error seeking to end of file \"", 1115 fileName, "\":", Tcl_PosixError(interp), NULL); 1116 Tcl_Close(NULL, chan); 1117 return TCL_ERROR; 1118 1119 } 1120 buffer = (char *) ckalloc((unsigned) bufferSize+1); 1121 bufferSize = Tcl_Read(chan, buffer, bufferSize); 1122 if (bufferSize < 0) { 1123 Tcl_AppendResult(interp, "error reading file \"", fileName, "\":", 1124 Tcl_PosixError(interp), NULL); 1125 Tcl_Close(NULL, chan); 1126 return TCL_ERROR; 1127 } 1128 Tcl_Close(NULL, chan); 1129 buffer[bufferSize] = 0; 1130 result = AddFromString(interp, tkwin, buffer, priority); 1131 ckfree(buffer); 1132 return result; 1133} 1134 1135/* 1136 *-------------------------------------------------------------- 1137 * 1138 * NewArray -- 1139 * 1140 * Create a new ElArray structure of a given size. 1141 * 1142 * Results: 1143 * The return value is a pointer to a properly initialized element array 1144 * with "numEls" space. The array is marked as having no active elements. 1145 * 1146 * Side effects: 1147 * Memory is allocated. 1148 * 1149 *-------------------------------------------------------------- 1150 */ 1151 1152static ElArray * 1153NewArray( 1154 int numEls) /* How many elements of space to allocate. */ 1155{ 1156 register ElArray *arrayPtr; 1157 1158 arrayPtr = (ElArray *) ckalloc(EL_ARRAY_SIZE(numEls)); 1159 arrayPtr->arraySize = numEls; 1160 arrayPtr->numUsed = 0; 1161 arrayPtr->nextToUse = arrayPtr->els; 1162 return arrayPtr; 1163} 1164 1165/* 1166 *-------------------------------------------------------------- 1167 * 1168 * ExtendArray -- 1169 * 1170 * Add a new element to an array, extending the array if necessary. 1171 * 1172 * Results: 1173 * The return value is a pointer to the new array, which will be 1174 * different from arrayPtr if the array got expanded. 1175 * 1176 * Side effects: 1177 * Memory may be allocated or freed. 1178 * 1179 *-------------------------------------------------------------- 1180 */ 1181 1182static ElArray * 1183ExtendArray( 1184 register ElArray *arrayPtr, /* Array to be extended. */ 1185 register Element *elPtr) /* Element to be copied into array. */ 1186{ 1187 /* 1188 * If the current array has filled up, make it bigger. 1189 */ 1190 1191 if (arrayPtr->numUsed >= arrayPtr->arraySize) { 1192 register ElArray *newPtr; 1193 1194 newPtr = (ElArray *) ckalloc(EL_ARRAY_SIZE(2*arrayPtr->arraySize)); 1195 newPtr->arraySize = 2*arrayPtr->arraySize; 1196 newPtr->numUsed = arrayPtr->numUsed; 1197 newPtr->nextToUse = &newPtr->els[newPtr->numUsed]; 1198 memcpy(newPtr->els, arrayPtr->els, 1199 arrayPtr->arraySize * sizeof(Element)); 1200 ckfree((char *) arrayPtr); 1201 arrayPtr = newPtr; 1202 } 1203 1204 *arrayPtr->nextToUse = *elPtr; 1205 arrayPtr->nextToUse++; 1206 arrayPtr->numUsed++; 1207 return arrayPtr; 1208} 1209 1210/* 1211 *-------------------------------------------------------------- 1212 * 1213 * SetupStacks -- 1214 * 1215 * Arrange the stacks so that they cache all the option information for a 1216 * particular window. 1217 * 1218 * Results: 1219 * None. 1220 * 1221 * Side effects: 1222 * The stacks are modified to hold information for tkwin and all its 1223 * ancestors in the window hierarchy. 1224 * 1225 *-------------------------------------------------------------- 1226 */ 1227 1228static void 1229SetupStacks( 1230 TkWindow *winPtr, /* Window for which information is to be 1231 * cached. */ 1232 int leaf) /* Non-zero means this is the leaf window 1233 * being probed. Zero means this is an 1234 * ancestor of the desired leaf. */ 1235{ 1236 int level, i, *iPtr; 1237 register StackLevel *levelPtr; 1238 register ElArray *arrayPtr; 1239 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 1240 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 1241 1242 /* 1243 * The following array defines the order in which the current stacks are 1244 * searched to find matching entries to add to the stacks. Given the 1245 * current priority-based scheme, the order below is no longer relevant; 1246 * all that matters is that an element is on the list *somewhere*. The 1247 * ordering is a relic of the old days when priorities were determined 1248 * differently. 1249 */ 1250 1251 static int searchOrder[] = {WILDCARD_NODE_CLASS, WILDCARD_NODE_NAME, 1252 EXACT_NODE_CLASS, EXACT_NODE_NAME, -1}; 1253 1254 if (winPtr->mainPtr->optionRootPtr == NULL) { 1255 OptionInit(winPtr->mainPtr); 1256 } 1257 1258 /* 1259 * Step 1: make sure that options are cached for this window's parent. 1260 */ 1261 1262 if (winPtr->parentPtr != NULL) { 1263 level = winPtr->parentPtr->optionLevel; 1264 if ((level == -1) || (tsdPtr->cachedWindow == NULL)) { 1265 SetupStacks(winPtr->parentPtr, 0); 1266 level = winPtr->parentPtr->optionLevel; 1267 } 1268 level++; 1269 } else { 1270 level = 1; 1271 } 1272 1273 /* 1274 * Step 2: pop extra unneeded information off the stacks and mark those 1275 * windows as no longer having cached information. 1276 */ 1277 1278 if (tsdPtr->curLevel >= level) { 1279 while (tsdPtr->curLevel >= level) { 1280 tsdPtr->levels[tsdPtr->curLevel].winPtr->optionLevel = -1; 1281 tsdPtr->curLevel--; 1282 } 1283 levelPtr = &tsdPtr->levels[level]; 1284 for (i = 0; i < NUM_STACKS; i++) { 1285 arrayPtr = tsdPtr->stacks[i]; 1286 arrayPtr->numUsed = levelPtr->bases[i]; 1287 arrayPtr->nextToUse = &arrayPtr->els[arrayPtr->numUsed]; 1288 } 1289 } 1290 tsdPtr->curLevel = winPtr->optionLevel = level; 1291 1292 /* 1293 * Step 3: if the root database information isn't loaded or isn't valid, 1294 * initialize level 0 of the stack from the database root (this only 1295 * happens if winPtr is a main window). 1296 */ 1297 1298 if ((tsdPtr->curLevel == 1) 1299 && ((tsdPtr->cachedWindow == NULL) 1300 || (tsdPtr->cachedWindow->mainPtr != winPtr->mainPtr))) { 1301 for (i = 0; i < NUM_STACKS; i++) { 1302 arrayPtr = tsdPtr->stacks[i]; 1303 arrayPtr->numUsed = 0; 1304 arrayPtr->nextToUse = arrayPtr->els; 1305 } 1306 ExtendStacks(winPtr->mainPtr->optionRootPtr, 0); 1307 } 1308 1309 /* 1310 * Step 4: create a new stack level; grow the level array if we've run out 1311 * of levels. Clear the stacks for EXACT_LEAF_NAME and EXACT_LEAF_CLASS 1312 * (anything that was there is of no use any more). 1313 */ 1314 1315 if (tsdPtr->curLevel >= tsdPtr->numLevels) { 1316 StackLevel *newLevels; 1317 1318 newLevels = (StackLevel *) ckalloc((unsigned) 1319 (tsdPtr->numLevels * 2 * sizeof(StackLevel))); 1320 memcpy(newLevels, tsdPtr->levels, 1321 tsdPtr->numLevels * sizeof(StackLevel)); 1322 ckfree((char *) tsdPtr->levels); 1323 tsdPtr->numLevels *= 2; 1324 tsdPtr->levels = newLevels; 1325 } 1326 levelPtr = &tsdPtr->levels[tsdPtr->curLevel]; 1327 levelPtr->winPtr = winPtr; 1328 arrayPtr = tsdPtr->stacks[EXACT_LEAF_NAME]; 1329 arrayPtr->numUsed = 0; 1330 arrayPtr->nextToUse = arrayPtr->els; 1331 arrayPtr = tsdPtr->stacks[EXACT_LEAF_CLASS]; 1332 arrayPtr->numUsed = 0; 1333 arrayPtr->nextToUse = arrayPtr->els; 1334 for (i = 0; i < NUM_STACKS; i++) { 1335 levelPtr->bases[i] = tsdPtr->stacks[i]->numUsed; 1336 } 1337 1338 /* 1339 * Step 5: scan the current stack level looking for matches to this 1340 * window's name or class; where found, add new information to the stacks. 1341 */ 1342 1343 for (iPtr = searchOrder; *iPtr != -1; iPtr++) { 1344 register Element *elPtr; 1345 int count; 1346 Tk_Uid id; 1347 1348 i = *iPtr; 1349 if (i & CLASS) { 1350 id = winPtr->classUid; 1351 } else { 1352 id = winPtr->nameUid; 1353 } 1354 elPtr = tsdPtr->stacks[i]->els; 1355 count = levelPtr->bases[i]; 1356 1357 /* 1358 * For wildcard stacks, check all entries; for non-wildcard stacks, 1359 * only check things that matched in the parent. 1360 */ 1361 1362 if (!(i & WILDCARD)) { 1363 elPtr += levelPtr[-1].bases[i]; 1364 count -= levelPtr[-1].bases[i]; 1365 } 1366 for ( ; count > 0; elPtr++, count--) { 1367 if (elPtr->nameUid != id) { 1368 continue; 1369 } 1370 ExtendStacks(elPtr->child.arrayPtr, leaf); 1371 } 1372 } 1373 tsdPtr->cachedWindow = winPtr; 1374} 1375 1376/* 1377 *-------------------------------------------------------------- 1378 * 1379 * ExtendStacks -- 1380 * 1381 * Given an element array, copy all the elements from the array onto the 1382 * system stacks (except for irrelevant leaf elements). 1383 * 1384 * Results: 1385 * None. 1386 * 1387 * Side effects: 1388 * The option stacks are extended. 1389 * 1390 *-------------------------------------------------------------- 1391 */ 1392 1393static void 1394ExtendStacks( 1395 ElArray *arrayPtr, /* Array of elements to copy onto stacks. */ 1396 int leaf) /* If zero, then don't copy exact leaf 1397 * elements. */ 1398{ 1399 register int count; 1400 register Element *elPtr; 1401 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 1402 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 1403 1404 for (elPtr = arrayPtr->els, count = arrayPtr->numUsed; 1405 count > 0; elPtr++, count--) { 1406 if (!(elPtr->flags & (NODE|WILDCARD)) && !leaf) { 1407 continue; 1408 } 1409 tsdPtr->stacks[elPtr->flags] = 1410 ExtendArray(tsdPtr->stacks[elPtr->flags], elPtr); 1411 } 1412} 1413 1414/* 1415 *-------------------------------------------------------------- 1416 * 1417 * OptionThreadExitProc -- 1418 * 1419 * Free data structures for option handling. 1420 * 1421 * Results: 1422 * None. 1423 * 1424 * Side effects: 1425 * Option-related data structures get freed. 1426 * 1427 *-------------------------------------------------------------- 1428 */ 1429 1430static void 1431OptionThreadExitProc( 1432 ClientData clientData) /* not used */ 1433{ 1434 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 1435 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 1436 1437 if (tsdPtr->initialized) { 1438 int i; 1439 1440 for (i = 0; i < NUM_STACKS; i++) { 1441 ckfree((char *) tsdPtr->stacks[i]); 1442 } 1443 ckfree((char *) tsdPtr->levels); 1444 tsdPtr->initialized = 0; 1445 } 1446} 1447 1448/* 1449 *-------------------------------------------------------------- 1450 * 1451 * OptionInit -- 1452 * 1453 * Initialize data structures for option handling. 1454 * 1455 * Results: 1456 * None. 1457 * 1458 * Side effects: 1459 * Option-related data structures get initialized. 1460 * 1461 *-------------------------------------------------------------- 1462 */ 1463 1464static void 1465OptionInit( 1466 register TkMainInfo *mainPtr) 1467 /* Top-level information about window that 1468 * isn't initialized yet. */ 1469{ 1470 int i; 1471 Tcl_Interp *interp; 1472 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 1473 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 1474 Element *defaultMatchPtr = &tsdPtr->defaultMatch; 1475 1476 /* 1477 * First, once-only initialization. 1478 */ 1479 1480 if (tsdPtr->initialized == 0) { 1481 tsdPtr->initialized = 1; 1482 tsdPtr->cachedWindow = NULL; 1483 tsdPtr->numLevels = 5; 1484 tsdPtr->curLevel = -1; 1485 tsdPtr->serial = 0; 1486 1487 tsdPtr->levels = (StackLevel *) 1488 ckalloc((unsigned) (5*sizeof(StackLevel))); 1489 for (i = 0; i < NUM_STACKS; i++) { 1490 tsdPtr->stacks[i] = NewArray(10); 1491 tsdPtr->levels[0].bases[i] = 0; 1492 } 1493 1494 defaultMatchPtr->nameUid = NULL; 1495 defaultMatchPtr->child.valueUid = NULL; 1496 defaultMatchPtr->priority = -1; 1497 defaultMatchPtr->flags = 0; 1498 Tcl_CreateThreadExitHandler(OptionThreadExitProc, NULL); 1499 } 1500 1501 /* 1502 * Then, per-main-window initialization. Create and delete dummy 1503 * interpreter for message logging. 1504 */ 1505 1506 mainPtr->optionRootPtr = NewArray(20); 1507 interp = Tcl_CreateInterp(); 1508 (void) GetDefaultOptions(interp, mainPtr->winPtr); 1509 Tcl_DeleteInterp(interp); 1510} 1511 1512/* 1513 *-------------------------------------------------------------- 1514 * 1515 * ClearOptionTree -- 1516 * 1517 * This function is called to erase everything in a hierarchical option 1518 * database. 1519 * 1520 * Results: 1521 * None. 1522 * 1523 * Side effects: 1524 * All the options associated with arrayPtr are deleted, along with all 1525 * option subtrees. The space pointed to by arrayPtr is freed. 1526 * 1527 *-------------------------------------------------------------- 1528 */ 1529 1530static void 1531ClearOptionTree( 1532 ElArray *arrayPtr) /* Array of options; delete everything 1533 * referred to recursively by this. */ 1534{ 1535 register Element *elPtr; 1536 int count; 1537 1538 for (count = arrayPtr->numUsed, elPtr = arrayPtr->els; count > 0; 1539 count--, elPtr++) { 1540 if (elPtr->flags & NODE) { 1541 ClearOptionTree(elPtr->child.arrayPtr); 1542 } 1543 } 1544 ckfree((char *) arrayPtr); 1545} 1546 1547/* 1548 *-------------------------------------------------------------- 1549 * 1550 * GetDefaultOptions -- 1551 * 1552 * This function is invoked to load the default set of options for a 1553 * window. 1554 * 1555 * Results: 1556 * None. 1557 * 1558 * Side effects: 1559 * Options are added to those for winPtr's main window. If there exists a 1560 * RESOURCE_MANAGER proprety for winPtr's display, that is used. 1561 * Otherwise, the .Xdefaults file in the user's home directory is used. 1562 * 1563 *-------------------------------------------------------------- 1564 */ 1565 1566static int 1567GetDefaultOptions( 1568 Tcl_Interp *interp, /* Interpreter to use for error reporting. */ 1569 TkWindow *winPtr) /* Fetch option defaults for main window 1570 * associated with this. */ 1571{ 1572 char *regProp, **regPropPtr = ®Prop; 1573 int result, actualFormat; 1574 unsigned long numItems, bytesAfter; 1575 Atom actualType; 1576 1577 /* 1578 * Try the RESOURCE_MANAGER property on the root window first. 1579 */ 1580 1581 regProp = NULL; 1582 result = XGetWindowProperty(winPtr->display, 1583 RootWindow(winPtr->display, 0), XA_RESOURCE_MANAGER, 0, 100000, 1584 False, XA_STRING, &actualType, &actualFormat, &numItems, 1585 &bytesAfter, (unsigned char **) regPropPtr); 1586 1587 if ((result == Success) && (actualType == XA_STRING) 1588 && (actualFormat == 8)) { 1589 result = AddFromString(interp, (Tk_Window) winPtr, regProp, 1590 TK_USER_DEFAULT_PRIO); 1591 XFree(regProp); 1592 return result; 1593 } 1594 1595 /* 1596 * No luck there. Try a .Xdefaults file in the user's home directory. 1597 */ 1598 1599 if (regProp != NULL) { 1600 XFree(regProp); 1601 } 1602 result = ReadOptionFile(interp, (Tk_Window) winPtr, "~/.Xdefaults", 1603 TK_USER_DEFAULT_PRIO); 1604 return result; 1605} 1606 1607/* 1608 * Local Variables: 1609 * mode: c 1610 * c-basic-offset: 4 1611 * fill-column: 78 1612 * End: 1613 */ 1614