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