OSDN Git Service

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