tkOption.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:46k
源码类别:

通讯编程

开发平台:

Visual C++

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