void Tk_AddOption( Tk_Window tkwin, /* Window token; option will be associated * with main window for this window. */ CONST char *name, /* Multi-element name of option. */ CONST char *value, /* String value for option. */ int priority) /* Overall priority level to use for this * option, such as TK_USER_DEFAULT_PRIO or * TK_INTERACTIVE_PRIO. Must be between 0 and * TK_MAX_PRIO. */ { TkWindow *winPtr = ((TkWindow *) tkwin)->mainPtr->winPtr; register ElArray **arrayPtrPtr; register Element *elPtr; Element newEl; register CONST char *p; CONST char *field; int count, firstField; ptrdiff_t length; #define TMP_SIZE 100 char tmp[TMP_SIZE+1]; ThreadSpecificData *tsdPtr = (ThreadSpecificData *) Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); if (winPtr->mainPtr->optionRootPtr == NULL) { OptionInit(winPtr->mainPtr); } tsdPtr->cachedWindow = NULL;/* Invalidate the cache. */ /* * Compute the priority for the new element, including both the overall * level and the serial number (to disambiguate with the level). */ if (priority < 0) { priority = 0; } else if (priority > TK_MAX_PRIO) { priority = TK_MAX_PRIO; } newEl.priority = (priority << 24) + tsdPtr->serial; tsdPtr->serial++; /* * Parse the option one field at a time. */ arrayPtrPtr = &(((TkWindow *) tkwin)->mainPtr->optionRootPtr); p = name; for (firstField = 1; ; firstField = 0) { /* * Scan the next field from the name and convert it to a Tk_Uid. Must * copy the field before calling Tk_Uid, so that a terminating NULL * may be added without modifying the source string. */ if (*p == '*') { newEl.flags = WILDCARD; p++; } else { newEl.flags = 0; } field = p; while ((*p != 0) && (*p != '.') && (*p != '*')) { p++; } length = p - field; if (length > TMP_SIZE) { length = TMP_SIZE; } strncpy(tmp, field, (size_t) length); tmp[length] = 0; newEl.nameUid = Tk_GetUid(tmp); if (isupper(UCHAR(*field))) { newEl.flags |= CLASS; } if (*p != 0) { /* * New element will be a node. If this option can't possibly apply * to this main window, then just skip it. Otherwise, add it to * the parent, if it isn't already there, and descend into it. */ newEl.flags |= NODE; if (firstField && !(newEl.flags & WILDCARD) && (newEl.nameUid != winPtr->nameUid) && (newEl.nameUid != winPtr->classUid)) { return; } for (elPtr = (*arrayPtrPtr)->els, count = (*arrayPtrPtr)->numUsed; ; elPtr++, count--) { if (count == 0) { newEl.child.arrayPtr = NewArray(5); *arrayPtrPtr = ExtendArray(*arrayPtrPtr, &newEl); arrayPtrPtr = &((*arrayPtrPtr) ->nextToUse[-1].child.arrayPtr); break; } if ((elPtr->nameUid == newEl.nameUid) && (elPtr->flags == newEl.flags)) { arrayPtrPtr = &(elPtr->child.arrayPtr); break; } } if (*p == '.') { p++; } } else { /* * New element is a leaf. Add it to the parent, if it isn't * already there. If it exists already, keep whichever value has * highest priority. */ newEl.child.valueUid = Tk_GetUid(value); for (elPtr = (*arrayPtrPtr)->els, count = (*arrayPtrPtr)->numUsed; ; elPtr++, count--) { if (count == 0) { *arrayPtrPtr = ExtendArray(*arrayPtrPtr, &newEl); return; } if ((elPtr->nameUid == newEl.nameUid) && (elPtr->flags == newEl.flags)) { if (elPtr->priority < newEl.priority) { elPtr->priority = newEl.priority; elPtr->child.valueUid = newEl.child.valueUid; } return; } } } } }
static void SetupStacks( TkWindow *winPtr, /* Window for which information is to be * cached. */ int leaf) /* Non-zero means this is the leaf window * being probed. Zero means this is an * ancestor of the desired leaf. */ { int level, i; const int *iPtr; register StackLevel *levelPtr; register ElArray *arrayPtr; ThreadSpecificData *tsdPtr = Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); /* * The following array defines the order in which the current stacks are * searched to find matching entries to add to the stacks. Given the * current priority-based scheme, the order below is no longer relevant; * all that matters is that an element is on the list *somewhere*. The * ordering is a relic of the old days when priorities were determined * differently. */ static const int searchOrder[] = {WILDCARD_NODE_CLASS, WILDCARD_NODE_NAME, EXACT_NODE_CLASS, EXACT_NODE_NAME, -1}; if (winPtr->mainPtr->optionRootPtr == NULL) { OptionInit(winPtr->mainPtr); } /* * Step 1: make sure that options are cached for this window's parent. */ if (winPtr->parentPtr != NULL) { level = winPtr->parentPtr->optionLevel; if ((level == -1) || (tsdPtr->cachedWindow == NULL)) { SetupStacks(winPtr->parentPtr, 0); level = winPtr->parentPtr->optionLevel; } level++; } else { level = 1; } /* * Step 2: pop extra unneeded information off the stacks and mark those * windows as no longer having cached information. */ if (tsdPtr->curLevel >= level) { while (tsdPtr->curLevel >= level) { tsdPtr->levels[tsdPtr->curLevel].winPtr->optionLevel = -1; tsdPtr->curLevel--; } levelPtr = &tsdPtr->levels[level]; for (i = 0; i < NUM_STACKS; i++) { arrayPtr = tsdPtr->stacks[i]; arrayPtr->numUsed = levelPtr->bases[i]; arrayPtr->nextToUse = &arrayPtr->els[arrayPtr->numUsed]; } } tsdPtr->curLevel = winPtr->optionLevel = level; /* * Step 3: if the root database information isn't loaded or isn't valid, * initialize level 0 of the stack from the database root (this only * happens if winPtr is a main window). */ if ((tsdPtr->curLevel == 1) && ((tsdPtr->cachedWindow == NULL) || (tsdPtr->cachedWindow->mainPtr != winPtr->mainPtr))) { for (i = 0; i < NUM_STACKS; i++) { arrayPtr = tsdPtr->stacks[i]; arrayPtr->numUsed = 0; arrayPtr->nextToUse = arrayPtr->els; } ExtendStacks(winPtr->mainPtr->optionRootPtr, 0); } /* * Step 4: create a new stack level; grow the level array if we've run out * of levels. Clear the stacks for EXACT_LEAF_NAME and EXACT_LEAF_CLASS * (anything that was there is of no use any more). */ if (tsdPtr->curLevel >= tsdPtr->numLevels) { StackLevel *newLevels = (StackLevel *) ckalloc((unsigned) (tsdPtr->numLevels*2*sizeof(StackLevel))); memcpy(newLevels, tsdPtr->levels, tsdPtr->numLevels * sizeof(StackLevel)); ckfree((char *) tsdPtr->levels); tsdPtr->numLevels *= 2; tsdPtr->levels = newLevels; } levelPtr = &tsdPtr->levels[tsdPtr->curLevel]; levelPtr->winPtr = winPtr; arrayPtr = tsdPtr->stacks[EXACT_LEAF_NAME]; arrayPtr->numUsed = 0; arrayPtr->nextToUse = arrayPtr->els; arrayPtr = tsdPtr->stacks[EXACT_LEAF_CLASS]; arrayPtr->numUsed = 0; arrayPtr->nextToUse = arrayPtr->els; for (i = 0; i < NUM_STACKS; i++) { levelPtr->bases[i] = tsdPtr->stacks[i]->numUsed; } /* * Step 5: scan the current stack level looking for matches to this * window's name or class; where found, add new information to the stacks. */ for (iPtr = searchOrder; *iPtr != -1; iPtr++) { register Element *elPtr; int count; Tk_Uid id; i = *iPtr; if (i & CLASS) { id = winPtr->classUid; } else { id = winPtr->nameUid; } elPtr = tsdPtr->stacks[i]->els; count = levelPtr->bases[i]; /* * For wildcard stacks, check all entries; for non-wildcard stacks, * only check things that matched in the parent. */ if (!(i & WILDCARD)) { elPtr += levelPtr[-1].bases[i]; count -= levelPtr[-1].bases[i]; } for ( ; count > 0; elPtr++, count--) { if (elPtr->nameUid != id) { continue; } ExtendStacks(elPtr->child.arrayPtr, leaf); } } tsdPtr->cachedWindow = winPtr; }