1 /*
2  * bltSwitch.c --
3  *
4  *	This module implements command/argument switch parsing
5  *	procedures for the BLT toolkit.
6  *
7  * Copyright 1991-1998 Lucent Technologies, Inc.
8  *
9  * Permission to use, copy, modify, and distribute this software and
10  * its documentation for any purpose and without fee is hereby
11  * granted, provided that the above copyright notice appear in all
12  * copies and that both that the copyright notice and warranty
13  * disclaimer appear in supporting documentation, and that the names
14  * of Lucent Technologies any of their entities not be used in
15  * advertising or publicity pertaining to distribution of the software
16  * without specific, written prior permission.
17  *
18  * Lucent Technologies disclaims all warranties with regard to this
19  * software, including all implied warranties of merchantability and
20  * fitness.  In no event shall Lucent Technologies be liable for any
21  * special, indirect or consequential damages or any damages
22  * whatsoever resulting from loss of use, data or profits, whether in
23  * an action of contract, negligence or other tortuous action, arising
24  * out of or in connection with the use or performance of this
25  * software.
26  */
27 
28 #include "bltInt.h"
29 #if defined(__STDC__)
30 #include <stdarg.h>
31 #else
32 #include <varargs.h>
33 #endif
34 
35 #include "bltSwitch.h"
36 
37 static Blt_SwitchSpec *	GetCachedSwitchSpecs _ANSI_ARGS_((Tcl_Interp *interp,
38     const Blt_SwitchSpec *staticSpecs));
39     static void		DeleteSpecCacheTable _ANSI_ARGS_((
40     ClientData clientData, Tcl_Interp *interp));
41 
42 /*
43  *--------------------------------------------------------------
44  *
45  * FindSwitchSpec --
46  *
47  *	Search through a table of configuration specs, looking for
48  *	one that matches a given argvName.
49  *
50  * Results:
51  *	The return value is a pointer to the matching entry, or NULL
52  *	if nothing matched.  In that case an error message is left
53  *	in the interp's result.
54  *
55  * Side effects:
56  *	None.
57  *
58  *--------------------------------------------------------------
59  */
60 static Blt_SwitchSpec *
FindSwitchSpec(interp,specs,name,needFlags,hateFlags,flags)61 FindSwitchSpec(interp, specs, name, needFlags, hateFlags, flags)
62     Tcl_Interp *interp;		/* Used for reporting errors. */
63     Blt_SwitchSpec *specs;	/* Pointer to table of configuration
64 				 * specifications for a widget. */
65     char *name;			/* Name (suitable for use in a "switch"
66 				 * command) identifying particular option. */
67     int needFlags;		/* Flags that must be present in matching
68 				 * entry. */
69     int hateFlags;		/* Flags that must NOT be present in
70 				 * matching entry. */
71     int flags;
72 {
73     register Blt_SwitchSpec *specPtr;
74     register char c;		/* First character of current argument. */
75     Blt_SwitchSpec *matchPtr;	/* Matching spec, or NULL. */
76     size_t length;
77 
78     c = name[1];
79     length = strlen(name);
80     matchPtr = NULL;
81     specs = Blt_GetCachedSwitchSpecs(interp, specs);
82     for (specPtr = specs; specPtr->type != BLT_SWITCH_END; specPtr++) {
83 	if (specPtr->switchName == NULL) {
84 	    continue;
85 	}
86 	if ((specPtr->switchName[1] != c)
87 	    || (strncmp(specPtr->switchName, name, length) != 0)) {
88 	    continue;
89 	}
90 	if ((flags&BLT_SWITCH_EXACT) && specPtr->switchName[length] != 0) {
91 	    continue;
92         }
93 	if (((specPtr->flags & needFlags) != needFlags)
94 	    || (specPtr->flags & hateFlags)) {
95 	    continue;
96 	}
97 	if (specPtr->switchName[length] == 0) {
98 	    return specPtr;	/* Stop on a perfect match. */
99 	}
100 	if (matchPtr != NULL) {
101 	    Tcl_AppendResult(interp, "ambiguous option \"", name, "\"",
102 		(char *) NULL);
103 	    return (Blt_SwitchSpec *) NULL;
104 	}
105 	matchPtr = specPtr;
106     }
107 
108     if (matchPtr == NULL) {
109 	Tcl_AppendResult(interp, "unknown option \"", name, "\" not one of: ", (char *)NULL);
110         for (specPtr = specs; specPtr->type != BLT_SWITCH_END; specPtr++) {
111             if (specPtr->switchName == NULL) {
112                 continue;
113             }
114             if (name[1] != '?' || specPtr->type == 0 || specPtr->type >= BLT_SWITCH_END) {
115                 Tcl_AppendResult(interp, specPtr->switchName, " ", 0);
116             } else {
117                 static char *typenames[BLT_SWITCH_END+10] = {
118                     "bool", "int", "posint", "nonneg", "double", "string",
119                     "list", "flag", "value", "custom", "END"
120                 };
121                 if (typenames[BLT_SWITCH_END] == 0 ||
122                 strcmp(typenames[BLT_SWITCH_END],"END")) {
123                     fprintf(stderr, "Blt_SwitchTypes changed\n");
124                     continue;
125                 }
126                 Tcl_AppendResult(interp, "?", specPtr->switchName, 0);
127                 if (specPtr->type != BLT_SWITCH_FLAG) {
128                     Tcl_AppendResult(interp, " ", typenames[specPtr->type], 0);
129                 }
130                 Tcl_AppendResult(interp, "? ", 0);
131             }
132         }
133         return (Blt_SwitchSpec *) NULL;
134     }
135     return matchPtr;
136 }
137 
138 /*
139  *--------------------------------------------------------------
140  *
141  * DoSwitch --
142  *
143  *	This procedure applies a single configuration option
144  *	to a widget record.
145  *
146  * Results:
147  *	A standard Tcl return value.
148  *
149  * Side effects:
150  *	WidgRec is modified as indicated by specPtr and value.
151  *	The old value is recycled, if that is appropriate for
152  *	the value type.
153  *
154  *--------------------------------------------------------------
155  */
156 static int
DoSwitch(interp,specPtr,string,record,obj)157 DoSwitch(interp, specPtr, string, record, obj)
158     Tcl_Interp *interp;		/* Interpreter for error reporting. */
159     Blt_SwitchSpec *specPtr;	/* Specifier to apply. */
160     char *string;		/* Value to use to fill in widgRec. */
161     ClientData record;		/* Record whose fields are to be
162 				 * modified.  Values must be properly
163 				 * initialized. */
164     Tcl_Obj *obj;
165 {
166     char *ptr;
167     int isNull;
168     int count;
169 
170     isNull = ((*string == '\0') && (specPtr->flags & BLT_SWITCH_NULL_OK));
171     do {
172 	ptr = (char *)record + specPtr->offset;
173 	switch (specPtr->type) {
174 	case BLT_SWITCH_BOOLEAN:
175 	    if (Tcl_GetBoolean(interp, string, (int *)ptr) != TCL_OK) {
176 		return TCL_ERROR;
177 	    }
178 	    break;
179 
180 	case BLT_SWITCH_INT:
181 	    if (Tcl_GetInt(interp, string, (int *)ptr) != TCL_OK) {
182 		return TCL_ERROR;
183 	    }
184 	    break;
185 
186 	case BLT_SWITCH_INT_NONNEGATIVE:
187 	    if (Tcl_GetInt(interp, string, &count) != TCL_OK) {
188 		return TCL_ERROR;
189 	    }
190 	    if (count < 0) {
191 		Tcl_AppendResult(interp, "bad value \"", string, "\": ",
192 				 "can't be negative", (char *)NULL);
193 		return TCL_ERROR;
194 	    }
195 	    *((int *)ptr) = count;
196 	    break;
197 
198 	case BLT_SWITCH_INT_POSITIVE:
199 	    if (Tcl_GetInt(interp, string, &count) != TCL_OK) {
200 		return TCL_ERROR;
201 	    }
202 	    if (count <= 0) {
203 		Tcl_AppendResult(interp, "bad value \"", string, "\": ",
204 			"must be positive", (char *)NULL);
205 		return TCL_ERROR;
206 	    }
207 	    *((int *)ptr) = count;
208 	    break;
209 
210 	case BLT_SWITCH_DOUBLE:
211 	    if (Tcl_GetDouble(interp, string, (double *)ptr) != TCL_OK) {
212 		return TCL_ERROR;
213 	    }
214 	    break;
215 	case BLT_SWITCH_OBJ:
216 	    (*((Tcl_Obj **)ptr)) = obj;
217 	    break;
218 
219 	case BLT_SWITCH_STRING:
220 	    {
221 		char *old, *new, **strPtr;
222 
223 		strPtr = (char **)ptr;
224 		if (isNull) {
225 		    new = NULL;
226 		} else {
227 		    new = Blt_Strdup(string);
228 		}
229 		old = *strPtr;
230 		if (old != NULL) {
231 		    Blt_Free(old);
232 		}
233 		*strPtr = new;
234 	    }
235 	    break;
236 
237 	case BLT_SWITCH_LIST:
238 	    if (Tcl_SplitList(interp, string, &count, (char ***)ptr)
239 		!= TCL_OK) {
240 		return TCL_ERROR;
241 	    }
242 	    break;
243 
244 	case BLT_SWITCH_CUSTOM:
245 	    if ((*specPtr->customPtr->parseProc) \
246 		(specPtr->customPtr->clientData, interp, specPtr->switchName,
247 			string, record, specPtr->offset) != TCL_OK) {
248 		return TCL_ERROR;
249 	    }
250 	    break;
251 
252 	default:
253 	    Tcl_AppendResult(interp, "bad switch table: unknown type \"",
254 		 Blt_Itoa(specPtr->type), "\"", (char *)NULL);
255 	    return TCL_ERROR;
256 	}
257 	specPtr++;
258     } while ((specPtr->switchName == NULL) &&
259 	     (specPtr->type != BLT_SWITCH_END));
260     return TCL_OK;
261 }
262 
263 /*
264  *--------------------------------------------------------------
265  *
266  * Blt_ProcessSwitches --
267  *
268  *	Process command-line options and database options to
269  *	fill in fields of a widget record with resources and
270  *	other parameters.
271  *
272  * Results:
273  *	Returns the number of arguments comsumed by parsing the
274  *	command line.  If an error occurred, -1 will be returned
275  *	and an error messages can be found as the interpreter
276  *	result.
277  *
278  * Side effects:
279  *	The fields of widgRec get filled in with information
280  *	from argc/argv and the option database.  Old information
281  *	in widgRec's fields gets recycled.
282  *
283  *--------------------------------------------------------------
284  */
285 int
Blt_ProcessSwitches(interp,specs,argc,argv,record,flags)286 Blt_ProcessSwitches(interp, specs, argc, argv, record, flags)
287     Tcl_Interp *interp;		/* Interpreter for error reporting. */
288     Blt_SwitchSpec *specs;	/* Describes legal options. */
289     int argc;			/* Number of elements in argv. */
290     char **argv;		/* Command-line options. */
291     char *record;		/* Record whose fields are to be
292 				 * modified.  Values must be properly
293 				 * initialized. */
294     int flags;			/* Used to specify additional flags
295 				 * that must be present in switch specs
296 				 * for them to be considered.  Also,
297 				 * may have BLT_SWITCH_ARGV_ONLY set. */
298 {
299     register int count;
300     char *arg;
301     register Blt_SwitchSpec *specPtr;
302     int needFlags;		/* Specs must contain this set of flags
303 				 * or else they are not considered. */
304     int hateFlags;		/* If a spec contains any bits here, it's
305 				 * not considered. */
306 
307     needFlags = flags & ~(BLT_SWITCH_USER_BIT - 1);
308     hateFlags = 0;
309 
310     /*
311      * Pass 1:  Clear the change flags on all the specs so that we
312      *          can check it later.
313      */
314     specs = Blt_GetCachedSwitchSpecs(interp, specs);
315     for (specPtr = specs; specPtr->type != BLT_SWITCH_END; specPtr++) {
316 	specPtr->flags &= ~BLT_SWITCH_SPECIFIED;
317     }
318     /*
319      * Pass 2:  Process the arguments that match entries in the specs.
320      *		It's an error if the argument doesn't match anything.
321      */
322     for (count = 0; count < argc; count++) {
323 	arg = argv[count];
324 	if (flags & BLT_SWITCH_OBJV_PARTIAL) {
325 	    if ((arg[0] != '-') || ((arg[1] == '-') && (argv[2] == '\0'))) {
326 		/*
327 		 * If the argument doesn't start with a '-' (not a switch)
328 		 * or is '--', stop processing and return the number of
329 		 * arguments comsumed.
330 		 */
331 		return count;
332 	    }
333 	}
334 	specPtr = FindSwitchSpec(interp, specs, arg, needFlags, hateFlags, flags);
335 	if (specPtr == NULL) {
336 	    return -1;
337 	}
338 	if (specPtr->type == BLT_SWITCH_FLAG) {
339 	    char *ptr;
340 
341 	    ptr = record + specPtr->offset;
342 	    *((int *)ptr) |= specPtr->value;
343 	} else if (specPtr->type == BLT_SWITCH_VALUE) {
344 	    char *ptr;
345 
346 	    ptr = record + specPtr->offset;
347 	    *((int *)ptr) = specPtr->value;
348 	} else {
349 	    if ((count + 1) == argc) {
350 		Tcl_AppendResult(interp, "value for \"", arg, "\" missing",
351 			(char *) NULL);
352 		return -1;
353 	    }
354 	    count++;
355 	    if (DoSwitch(interp, specPtr, argv[count], record, NULL) != TCL_OK) {
356 		char msg[100];
357 
358 		sprintf(msg, "\n    (processing \"%.40s\" option)",
359 			specPtr->switchName);
360 		Tcl_AddErrorInfo(interp, msg);
361 		return -1;
362 	    }
363 	}
364 	specPtr->flags |= BLT_SWITCH_SPECIFIED;
365     }
366     return count;
367 }
368 
369 #if (TCL_VERSION_NUMBER >= _VERSION(8,0,0))
370 
371 /*
372  *--------------------------------------------------------------
373  *
374  * Blt_ProcessObjSwitches --
375  *
376  *	Process command-line options and database options to
377  *	fill in fields of a widget record with resources and
378  *	other parameters.
379  *
380  * Results:
381  *	Returns the number of arguments comsumed by parsing the
382  *	command line.  If an error occurred, -1 will be returned
383  *	and an error messages can be found as the interpreter
384  *	result.
385  *
386  * Side effects:
387  *	The fields of widgRec get filled in with information
388  *	from argc/argv and the option database.  Old information
389  *	in widgRec's fields gets recycled.
390  *
391  *--------------------------------------------------------------
392  */
393 int
Blt_ProcessObjSwitches(interp,specs,objc,objv,record,flags)394 Blt_ProcessObjSwitches(interp, specs, objc, objv, record, flags)
395     Tcl_Interp *interp;		/* Interpreter for error reporting. */
396     Blt_SwitchSpec *specs;	/* Describes legal options. */
397     int objc;			/* Number of elements in argv. */
398     Tcl_Obj *CONST *objv;	/* Command-line options. */
399     char *record;		/* Record whose fields are to be
400 				 * modified.  Values must be properly
401 				 * initialized. */
402     int flags;			/* Used to specify additional flags
403 				 * that must be present in switch specs
404 				 * for them to be considered.  Also,
405 				 * may have BLT_SWITCH_ARGV_ONLY set. */
406 {
407     register Blt_SwitchSpec *specPtr;
408     register int count;
409     int needFlags;		/* Specs must contain this set of flags
410 				 * or else they are not considered. */
411     int hateFlags;		/* If a spec contains any bits here, it's
412 				 * not considered. */
413 
414     needFlags = flags & ~(BLT_SWITCH_USER_BIT - 1);
415     hateFlags = 0;
416 
417     /*
418      * Pass 1:  Clear the change flags on all the specs so that we
419      *          can check it later.
420      */
421     specs = Blt_GetCachedSwitchSpecs(interp, specs);
422     for (specPtr = specs; specPtr->type != BLT_SWITCH_END; specPtr++) {
423 	specPtr->flags &= ~BLT_SWITCH_SPECIFIED;
424     }
425     /*
426      * Pass 2:  Process the arguments that match entries in the specs.
427      *		It's an error if the argument doesn't match anything.
428      */
429     for (count = 0; count < objc; count++) {
430 	char *arg;
431 
432 	arg = Tcl_GetString(objv[count]);
433 	if (flags & BLT_SWITCH_OBJV_PARTIAL) {
434 	    if ((arg[0] != '-') || ((arg[1] == '-') && (arg[2] == '\0'))) {
435 		/*
436 		 * If the argument doesn't start with a '-' (not a switch)
437 		 * or is '--', stop processing and return the number of
438 		 * arguments comsumed.
439 		 */
440 		return count;
441 	    }
442 	}
443 	specPtr = FindSwitchSpec(interp, specs, arg, needFlags, hateFlags, flags);
444 	if (specPtr == NULL) {
445 	    return -1;
446 	}
447 	if (specPtr->type == BLT_SWITCH_FLAG) {
448 	    char *ptr;
449 
450 	    ptr = record + specPtr->offset;
451 	    *((int *)ptr) |= specPtr->value;
452 	} else if (specPtr->type == BLT_SWITCH_VALUE) {
453 	    char *ptr;
454 
455 	    ptr = record + specPtr->offset;
456 	    *((int *)ptr) = specPtr->value;
457 	} else {
458 	    count++;
459 	    if (count == objc) {
460 		Tcl_AppendResult(interp, "value for \"", arg, "\" missing",
461 				 (char *) NULL);
462 		return -1;
463 	    }
464 	    arg = Tcl_GetString(objv[count]);
465 	    if (DoSwitch(interp, specPtr, arg, record, objv[count]) != TCL_OK) {
466 		char msg[100];
467 
468 		sprintf(msg, "\n    (processing \"%.40s\" option)",
469 			specPtr->switchName);
470 		Tcl_AddErrorInfo(interp, msg);
471 		return -1;
472 	    }
473 	}
474 	specPtr->flags |= BLT_SWITCH_SPECIFIED;
475     }
476     return count;
477 }
478 #endif
479 
480 /*
481  *----------------------------------------------------------------------
482  *
483  * Blt_FreeSwitches --
484  *
485  *	Free up all resources associated with switch options.
486  *
487  * Results:
488  *	None.
489  *
490  *----------------------------------------------------------------------
491  */
492 
493 /* ARGSUSED */
494 void
Blt_FreeSwitches(interp,specs,record,needFlags)495 Blt_FreeSwitches(interp, specs, record, needFlags)
496     Tcl_Interp *interp;
497     Blt_SwitchSpec *specs;	/* Describes legal options. */
498     char *record;		/* Record whose fields contain current
499 				 * values for options. */
500     int needFlags;		/* Used to specify additional flags
501 				 * that must be present in config specs
502 				 * for them to be considered. */
503 {
504     register Blt_SwitchSpec *specPtr;
505 
506     specs = Blt_GetCachedSwitchSpecs(interp, specs);
507     for (specPtr = specs; specPtr->type != BLT_SWITCH_END; specPtr++) {
508 	if ((specPtr->flags & needFlags) == needFlags) {
509 	    char *ptr;
510 
511 	    ptr = record + specPtr->offset;
512 	    switch (specPtr->type) {
513 	    case BLT_SWITCH_STRING:
514 	    case BLT_SWITCH_LIST:
515 		if (*((char **) ptr) != NULL) {
516 		    Blt_Free(*((char **) ptr));
517 		    *((char **) ptr) = NULL;
518 		}
519 		break;
520 
521 	    case BLT_SWITCH_CUSTOM:
522 		if ((*(char **)ptr != NULL) &&
523 		    (specPtr->customPtr->freeProc != NULL)) {
524 		    (*specPtr->customPtr->freeProc)(*(char **)ptr);
525 		    *((char **) ptr) = NULL;
526 		}
527 		break;
528 
529 	    default:
530 		break;
531 	    }
532 	}
533     }
534 }
535 
536 
537 /*
538  *----------------------------------------------------------------------
539  *
540  * Blt_SwitchModified --
541  *
542  *      Given the configuration specifications and one or more option
543  *	patterns (terminated by a NULL), indicate if any of the matching
544  *	configuration options has been reset.
545  *
546  * Results:
547  *      Returns 1 if one of the options has changed, 0 otherwise.
548  *
549  *----------------------------------------------------------------------
550  */
551 int Blt_SwitchChanged
TCL_VARARGS_DEF(Blt_SwitchSpec *,arg1)552 TCL_VARARGS_DEF(Blt_SwitchSpec *, arg1)
553 {
554     va_list argList;
555     Blt_SwitchSpec *specs;
556     register Blt_SwitchSpec *specPtr;
557     register char *switchName;
558     Tcl_Interp *interp;
559 
560     specs = TCL_VARARGS_START(Blt_SwitchSpec *, arg1, argList);
561     interp = va_arg(argList, Tcl_Interp *);
562     specs = Blt_GetCachedSwitchSpecs(interp, specs);
563     while ((switchName = va_arg(argList, char *)) != NULL) {
564 	for (specPtr = specs; specPtr->type != BLT_SWITCH_END; specPtr++) {
565 	    if ((Tcl_StringMatch(specPtr->switchName, switchName)) &&
566 		(specPtr->flags & BLT_SWITCH_SPECIFIED)) {
567 		va_end(argList);
568 		return 1;
569 	    }
570 	}
571     }
572     va_end(argList);
573     return 0;
574 }
575 
576 /*
577 *--------------------------------------------------------------
578 *
579 * DeleteSpecCacheTable --
580 *
581 *	Delete the per-interpreter copy of all the Blt_SwitchSpec tables which
582 *	were stored in the interpreter's assoc-data store.
583 *
584 * Results:
585 *	None
586 *
587 * Side effects:
588 *	None
589 *
590 *--------------------------------------------------------------
591 */
592 
593 static void
DeleteSpecCacheTable(clientData,interp)594 DeleteSpecCacheTable(clientData, interp)
595 ClientData clientData;
596 Tcl_Interp *interp;
597 {
598     Tcl_HashTable *tablePtr = (Tcl_HashTable *) clientData;
599     Tcl_HashEntry *entryPtr;
600     Tcl_HashSearch search;
601 
602     for (entryPtr = Tcl_FirstHashEntry(tablePtr,&search); entryPtr != NULL;
603     entryPtr = Tcl_NextHashEntry(&search)) {
604         /*
605         * Someone else deallocates the Tk_Uids themselves.
606         */
607 
608         ckfree((char *) Tcl_GetHashValue(entryPtr));
609     }
610     Tcl_DeleteHashTable(tablePtr);
611     ckfree((char *) tablePtr);
612 }
613 
614 
615 Blt_SwitchSpec *
Blt_GetCachedSwitchSpecs(interp,staticSpecs)616 Blt_GetCachedSwitchSpecs(interp, staticSpecs)
617 Tcl_Interp *interp;
618 const Blt_SwitchSpec *staticSpecs;
619 {
620     return GetCachedSwitchSpecs(interp, staticSpecs);
621 }
622 
623 static Blt_SwitchSpec *
GetCachedSwitchSpecs(interp,staticSpecs)624 GetCachedSwitchSpecs(interp, staticSpecs)
625 Tcl_Interp *interp;		/* Interpreter in which to store the cache. */
626 const Blt_SwitchSpec *staticSpecs;
627 /* Value to cache a copy of; it is also used
628 * as a key into the cache. */
629 {
630     Blt_SwitchSpec *cachedSpecs;
631     Tcl_HashTable *specCacheTablePtr;
632     Tcl_HashEntry *entryPtr;
633     int isNew;
634 
635     /*
636     * Get (or allocate if it doesn't exist) the hash table that the writable
637     * copies of the widget specs are stored in. In effect, this is
638     * self-initializing code.
639     */
640 
641     specCacheTablePtr = (Tcl_HashTable *)
642     Tcl_GetAssocData(interp, "bltSwitchSpec.threadTable", NULL);
643     if (specCacheTablePtr == NULL) {
644         specCacheTablePtr = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
645         Tcl_InitHashTable(specCacheTablePtr, TCL_ONE_WORD_KEYS);
646         Tcl_SetAssocData(interp, "bltSwitchSpec.threadTable",
647             DeleteSpecCacheTable, (ClientData) specCacheTablePtr);
648     }
649 
650     /*
651     * Look up or create the hash entry that the constant specs are mapped to,
652         * which will have the writable specs as its associated value.
653         */
654 
655         entryPtr = Tcl_CreateHashEntry(specCacheTablePtr, (char *) staticSpecs,
656         &isNew);
657         if (isNew) {
658         unsigned int entrySpace = sizeof(Blt_SwitchSpec);
659         const Blt_SwitchSpec *staticSpecPtr;
660 
661         /*
662         * OK, no working copy in this interpreter so copy. Need to work out
663         * how much space to allocate first.
664         */
665 
666         for (staticSpecPtr=staticSpecs; staticSpecPtr->type!=BLT_SWITCH_END;
667         staticSpecPtr++) {
668             entrySpace += sizeof(Blt_SwitchSpec);
669         }
670 
671         /*
672         * Now allocate our working copy's space and copy over the contents
673         * from the master copy.
674         */
675 
676         cachedSpecs = (Blt_SwitchSpec *) ckalloc(entrySpace);
677         memcpy((void *) cachedSpecs, (void *) staticSpecs, entrySpace);
678         Tcl_SetHashValue(entryPtr, (ClientData) cachedSpecs);
679 
680     } else {
681         cachedSpecs = (Blt_SwitchSpec *) Tcl_GetHashValue(entryPtr);
682     }
683 
684     return cachedSpecs;
685 }
686