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