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