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