|
|
1.1 root 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:
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:
369: /*
370: *--------------------------------------------------------------
371: *
372: * Tk_GetOption --
373: *
374: * Retrieve an option from the option database.
375: *
376: * Results:
377: * The return value is the value specified in the option
378: * database for the given name and class on the given
379: * window. If there is nothing specified in the database
380: * for that option, then NULL is returned.
381: *
382: * Side effects:
383: * The internal caches used to speed up option mapping
384: * may be modified, if this tkwin is different from the
385: * last tkwin used for option retrieval.
386: *
387: *--------------------------------------------------------------
388: */
389:
390: Tk_Uid
391: Tk_GetOption(tkwin, name, className)
392: Tk_Window tkwin; /* Token for window that option is
393: * associated with. */
394: char *name; /* Name of option. */
395: char *className; /* Class of option. NULL means there
396: * is no class for this option: just
397: * check for name. */
398: {
399: Tk_Uid nameId, classId;
400: register Element *elPtr, *bestPtr;
401: register int count;
402:
403: /*
404: * Note: no need to call OptionInit here: it will be done by
405: * the SetupStacks call below (squeeze out those nanoseconds).
406: */
407:
408: if (tkwin != (Tk_Window) cachedWindow) {
409: SetupStacks((TkWindow *) tkwin, 1);
410: }
411:
412: nameId = Tk_GetUid(name);
413: bestPtr = &defaultMatch;
414: for (elPtr = stacks[EXACT_LEAF_NAME]->els,
415: count = stacks[EXACT_LEAF_NAME]->numUsed; count > 0;
416: elPtr++, count--) {
417: if ((elPtr->nameUid == nameId)
418: && (elPtr->priority > bestPtr->priority)) {
419: bestPtr = elPtr;
420: }
421: }
422: for (elPtr = stacks[WILDCARD_LEAF_NAME]->els,
423: count = stacks[WILDCARD_LEAF_NAME]->numUsed; count > 0;
424: elPtr++, count--) {
425: if ((elPtr->nameUid == nameId)
426: && (elPtr->priority > bestPtr->priority)) {
427: bestPtr = elPtr;
428: }
429: }
430: if (className != NULL) {
431: classId = Tk_GetUid(className);
432: for (elPtr = stacks[EXACT_LEAF_CLASS]->els,
433: count = stacks[EXACT_LEAF_CLASS]->numUsed; count > 0;
434: elPtr++, count--) {
435: if ((elPtr->nameUid == classId)
436: && (elPtr->priority > bestPtr->priority)) {
437: bestPtr = elPtr;
438: }
439: }
440: for (elPtr = stacks[WILDCARD_LEAF_CLASS]->els,
441: count = stacks[WILDCARD_LEAF_CLASS]->numUsed; count > 0;
442: elPtr++, count--) {
443: if ((elPtr->nameUid == classId)
444: && (elPtr->priority > bestPtr->priority)) {
445: bestPtr = elPtr;
446: }
447: }
448: }
449: return bestPtr->child.valueUid;
450: }
451:
452: /*
453: *--------------------------------------------------------------
454: *
455: * Tk_OptionCmd --
456: *
457: * This procedure is invoked to process the "option" Tcl command.
458: * See the user documentation for details on what it does.
459: *
460: * Results:
461: * A standard Tcl result.
462: *
463: * Side effects:
464: * See the user documentation.
465: *
466: *--------------------------------------------------------------
467: */
468:
469: int
470: Tk_OptionCmd(clientData, interp, argc, argv)
471: ClientData clientData; /* Main window associated with
472: * interpreter. */
473: Tcl_Interp *interp; /* Current interpreter. */
474: int argc; /* Number of arguments. */
475: char **argv; /* Argument strings. */
476: {
477: Tk_Window tkwin = (Tk_Window) clientData;
478: 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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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 **) ®Prop);
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.