home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tkisrc04.zip / tk / os2 / tkOption.c < prev    next >
C/C++ Source or Header  |  1998-08-07  |  40KB  |  1,391 lines

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