]> www.wagner.pp.ru Git - oss/ck.git/blob - ckConfig.c
Ck console graphics toolkit
[oss/ck.git] / ckConfig.c
1 /* 
2  * ckConfig.c --
3  *
4  *      This file contains the Ck_ConfigureWidget procedure.
5  *
6  * Copyright (c) 1990-1994 The Regents of the University of California.
7  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
8  * Copyright (c) 1995 Christian Werner
9  *
10  * See the file "license.terms" for information on usage and redistribution
11  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12  */
13
14 #include "ckPort.h"
15 #include "ck.h"
16
17 /*
18  * Values for "flags" field of Ck_ConfigSpec structures.  Be sure
19  * to coordinate these values with those defined in ck.h
20  * (CK_CONFIG_*).  There must not be overlap!
21  *
22  * INIT -               Non-zero means (char *) things have been
23  *                      converted to Ck_Uid's.
24  */
25
26 #define INIT            0x20
27
28 /*
29  * Forward declarations for procedures defined later in this file:
30  */
31
32 static int              DoConfig _ANSI_ARGS_((Tcl_Interp *interp,
33                             CkWindow *winPtr, Ck_ConfigSpec *specPtr,
34                             Ck_Uid value, int valueIsUid, char *widgRec));
35 static Ck_ConfigSpec *  FindConfigSpec _ANSI_ARGS_ ((Tcl_Interp *interp,
36                             Ck_ConfigSpec *specs, char *argvName,
37                             int needFlags, int hateFlags));
38 static char *           FormatConfigInfo _ANSI_ARGS_ ((Tcl_Interp *interp,
39                             CkWindow *winPtr, Ck_ConfigSpec *specPtr,
40                             char *widgRec));
41 static char *           FormatConfigValue _ANSI_ARGS_((Tcl_Interp *interp,
42                             CkWindow *tkwin, Ck_ConfigSpec *specPtr,
43                             char *widgRec, char *buffer,
44                             Tcl_FreeProc **freeProcPtr));
45 \f
46 /*
47  *--------------------------------------------------------------
48  *
49  * Ck_ConfigureWidget --
50  *
51  *      Process command-line options to fill in fields of a
52  *      widget record with resources and other parameters.
53  *
54  * Results:
55  *      A standard Tcl return value.  In case of an error,
56  *      interp->result will hold an error message.
57  *
58  * Side effects:
59  *      The fields of widgRec get filled in with information
60  *      from argc/argv.  Old information in widgRec's fields
61  *      gets recycled.
62  *
63  *--------------------------------------------------------------
64  */
65
66 int
67 Ck_ConfigureWidget(interp, winPtr, specs, argc, argv, widgRec, flags)
68     Tcl_Interp *interp;         /* Interpreter for error reporting. */
69     CkWindow *winPtr;           /* Window containing widget. */
70     Ck_ConfigSpec *specs;       /* Describes legal options. */
71     int argc;                   /* Number of elements in argv. */
72     char **argv;                /* Command-line options. */
73     char *widgRec;              /* Record whose fields are to be
74                                  * modified.  Values must be properly
75                                  * initialized. */
76     int flags;                  /* Used to specify additional flags
77                                  * that must be present in config specs
78                                  * for them to be considered.  Also,
79                                  * may have CK_CONFIG_ARGV_ONLY set. */
80 {
81     Ck_ConfigSpec *specPtr;
82     Ck_Uid value;               /* Value of option from database. */
83     int needFlags;              /* Specs must contain this set of flags
84                                  * or else they are not considered. */
85     int hateFlags;              /* If a spec contains any bits here, it's
86                                  * not considered. */
87
88     needFlags = flags & ~(CK_CONFIG_USER_BIT - 1);
89     if (!(winPtr->mainPtr->flags & CK_HAS_COLOR)) {
90         hateFlags = CK_CONFIG_COLOR_ONLY;
91     } else {
92         hateFlags = CK_CONFIG_MONO_ONLY;
93     }
94
95     /*
96      * Pass one:  scan through all the option specs, replacing strings
97      * with Ck_Uids (if this hasn't been done already) and clearing
98      * the CK_CONFIG_OPTION_SPECIFIED flags.
99      */
100
101     for (specPtr = specs; specPtr->type != CK_CONFIG_END; specPtr++) {
102         if (!(specPtr->specFlags & INIT) && (specPtr->argvName != NULL)) {
103             if (specPtr->dbName != NULL) {
104                 specPtr->dbName = Ck_GetUid(specPtr->dbName);
105             }
106             if (specPtr->dbClass != NULL) {
107                 specPtr->dbClass = Ck_GetUid(specPtr->dbClass);
108             }
109             if (specPtr->defValue != NULL) {
110                 specPtr->defValue = Ck_GetUid(specPtr->defValue);
111             }
112         }
113         specPtr->specFlags = (specPtr->specFlags & ~CK_CONFIG_OPTION_SPECIFIED)
114                 | INIT;
115     }
116
117     /*
118      * Pass two:  scan through all of the arguments, processing those
119      * that match entries in the specs.
120      */
121
122     for ( ; argc > 0; argc -= 2, argv += 2) {
123         specPtr = FindConfigSpec(interp, specs, *argv, needFlags, hateFlags);
124         if (specPtr == NULL) {
125             return TCL_ERROR;
126         }
127
128         /*
129          * Process the entry.
130          */
131
132         if (argc < 2) {
133             Tcl_AppendResult(interp, "value for \"", *argv,
134                     "\" missing", (char *) NULL);
135             return TCL_ERROR;
136         }
137         if (DoConfig(interp, winPtr, specPtr, argv[1], 0, widgRec) != TCL_OK) {
138             char msg[100];
139
140             sprintf(msg, "\n    (processing \"%.40s\" option)",
141                     specPtr->argvName);
142             Tcl_AddErrorInfo(interp, msg);
143             return TCL_ERROR;
144         }
145         specPtr->specFlags |= CK_CONFIG_OPTION_SPECIFIED;
146     }
147
148     /*
149      * Pass three:  scan through all of the specs again;  if no
150      * command-line argument matched a spec, then check for info
151      * in the option database.  If there was nothing in the
152      * database, then use the default.
153      */
154
155     if (!(flags & CK_CONFIG_ARGV_ONLY)) {
156         for (specPtr = specs; specPtr->type != CK_CONFIG_END; specPtr++) {
157             if ((specPtr->specFlags & CK_CONFIG_OPTION_SPECIFIED)
158                     || (specPtr->argvName == NULL)
159                     || (specPtr->type == CK_CONFIG_SYNONYM)) {
160                 continue;
161             }
162             if (((specPtr->specFlags & needFlags) != needFlags)
163                     || (specPtr->specFlags & hateFlags)) {
164                 continue;
165             }
166             value = NULL;
167             if (specPtr->dbName != NULL) {
168                 value = Ck_GetOption(winPtr, specPtr->dbName,
169                     specPtr->dbClass);
170             }
171             if (value != NULL) {
172                 if (DoConfig(interp, winPtr, specPtr, value, 1, widgRec) !=
173                         TCL_OK) {
174                     char msg[200];
175     
176                     sprintf(msg, "\n    (%s \"%.50s\" in widget \"%.50s\")",
177                             "database entry for",
178                             specPtr->dbName, winPtr->pathName);
179                     Tcl_AddErrorInfo(interp, msg);
180                     return TCL_ERROR;
181                 }
182             } else {
183                 value = specPtr->defValue;
184                 if ((value != NULL) && !(specPtr->specFlags
185                         & CK_CONFIG_DONT_SET_DEFAULT)) {
186                     if (DoConfig(interp, winPtr, specPtr, value, 1, widgRec) !=
187                             TCL_OK) {
188                         char msg[200];
189         
190                         sprintf(msg,
191                                 "\n    (%s \"%.50s\" in widget \"%.50s\")",
192                                 "default value for",
193                                 specPtr->dbName, winPtr->pathName);
194                         Tcl_AddErrorInfo(interp, msg);
195                         return TCL_ERROR;
196                     }
197                 }
198             }
199         }
200     }
201     return TCL_OK;
202 }
203 \f
204 /*
205  *--------------------------------------------------------------
206  *
207  * FindConfigSpec --
208  *
209  *      Search through a table of configuration specs, looking for
210  *      one that matches a given argvName.
211  *
212  * Results:
213  *      The return value is a pointer to the matching entry, or NULL
214  *      if nothing matched.  In that case an error message is left
215  *      in interp->result.
216  *
217  * Side effects:
218  *      None.
219  *
220  *--------------------------------------------------------------
221  */
222
223 static Ck_ConfigSpec *
224 FindConfigSpec(interp, specs, argvName, needFlags, hateFlags)
225     Tcl_Interp *interp;         /* Used for reporting errors. */
226     Ck_ConfigSpec *specs;       /* Pointer to table of configuration
227                                  * specifications for a widget. */
228     char *argvName;             /* Name (suitable for use in a "config"
229                                  * command) identifying particular option. */
230     int needFlags;              /* Flags that must be present in matching
231                                  * entry. */
232     int hateFlags;              /* Flags that must NOT be present in
233                                  * matching entry. */
234 {
235     Ck_ConfigSpec *specPtr;
236     char c;                     /* First character of current argument. */
237     Ck_ConfigSpec *matchPtr;    /* Matching spec, or NULL. */
238     int length;
239
240     c = argvName[1];
241     length = strlen(argvName);
242     matchPtr = NULL;
243     for (specPtr = specs; specPtr->type != CK_CONFIG_END; specPtr++) {
244         if (specPtr->argvName == NULL) {
245             continue;
246         }
247         if ((specPtr->argvName[1] != c)
248                 || (strncmp(specPtr->argvName, argvName, length) != 0)) {
249             continue;
250         }
251         if (((specPtr->specFlags & needFlags) != needFlags)
252                 || (specPtr->specFlags & hateFlags)) {
253             continue;
254         }
255         if (specPtr->argvName[length] == 0) {
256             matchPtr = specPtr;
257             goto gotMatch;
258         }
259         if (matchPtr != NULL) {
260             Tcl_AppendResult(interp, "ambiguous option \"", argvName,
261                     "\"", (char *) NULL);
262             return (Ck_ConfigSpec *) NULL;
263         }
264         matchPtr = specPtr;
265     }
266
267     if (matchPtr == NULL) {
268         Tcl_AppendResult(interp, "unknown option \"", argvName,
269                 "\"", (char *) NULL);
270         return (Ck_ConfigSpec *) NULL;
271     }
272
273     /*
274      * Found a matching entry.  If it's a synonym, then find the
275      * entry that it's a synonym for.
276      */
277
278     gotMatch:
279     specPtr = matchPtr;
280     if (specPtr->type == CK_CONFIG_SYNONYM) {
281         for (specPtr = specs; ; specPtr++) {
282             if (specPtr->type == CK_CONFIG_END) {
283                 Tcl_AppendResult(interp,
284                         "couldn't find synonym for option \"",
285                         argvName, "\"", (char *) NULL);
286                 return (Ck_ConfigSpec *) NULL;
287             }
288             if ((specPtr->dbName == matchPtr->dbName) 
289                     && (specPtr->type != CK_CONFIG_SYNONYM)
290                     && ((specPtr->specFlags & needFlags) == needFlags)
291                     && !(specPtr->specFlags & hateFlags)) {
292                 break;
293             }
294         }
295     }
296     return specPtr;
297 }
298 \f
299 /*
300  *--------------------------------------------------------------
301  *
302  * DoConfig --
303  *
304  *      This procedure applies a single configuration option
305  *      to a widget record.
306  *
307  * Results:
308  *      A standard Tcl return value.
309  *
310  * Side effects:
311  *      WidgRec is modified as indicated by specPtr and value.
312  *      The old value is recycled, if that is appropriate for
313  *      the value type.
314  *
315  *--------------------------------------------------------------
316  */
317
318 static int
319 DoConfig(interp, winPtr, specPtr, value, valueIsUid, widgRec)
320     Tcl_Interp *interp;         /* Interpreter for error reporting. */
321     CkWindow *winPtr;           /* Window containing widget. */
322     Ck_ConfigSpec *specPtr;     /* Specifier to apply. */
323     char *value;                /* Value to use to fill in widgRec. */
324     int valueIsUid;             /* Non-zero means value is a Tk_Uid;
325                                  * zero means it's an ordinary string. */
326     char *widgRec;              /* Record whose fields are to be
327                                  * modified.  Values must be properly
328                                  * initialized. */
329 {
330     char *ptr;
331     Ck_Uid uid;
332     int nullValue;
333
334     nullValue = 0;
335     if ((*value == 0) && (specPtr->specFlags & CK_CONFIG_NULL_OK)) {
336         nullValue = 1;
337     }
338
339     do {
340         ptr = widgRec + specPtr->offset;
341         switch (specPtr->type) {
342             case CK_CONFIG_BOOLEAN:
343                 if (Tcl_GetBoolean(interp, value, (int *) ptr) != TCL_OK) {
344                     return TCL_ERROR;
345                 }
346                 break;
347             case CK_CONFIG_INT:
348                 if (Tcl_GetInt(interp, value, (int *) ptr) != TCL_OK) {
349                     return TCL_ERROR;
350                 }
351                 break;
352             case CK_CONFIG_DOUBLE:
353                 if (Tcl_GetDouble(interp, value, (double *) ptr) != TCL_OK) {
354                     return TCL_ERROR;
355                 }
356                 break;
357             case CK_CONFIG_STRING: {
358                 char *old, *new;
359
360                 if (nullValue) {
361                     new = NULL;
362                 } else {
363                     new = (char *) ckalloc((unsigned) (strlen(value) + 1));
364                     strcpy(new, value);
365                 }
366                 old = *((char **) ptr);
367                 if (old != NULL) {
368                     ckfree(old);
369                 }
370                 *((char **) ptr) = new;
371                 break;
372             }
373             case CK_CONFIG_UID:
374                 if (nullValue) {
375                     *((Ck_Uid *) ptr) = NULL;
376                 } else {
377                     uid = valueIsUid ? (Ck_Uid) value : Ck_GetUid(value);
378                     *((Ck_Uid *) ptr) = uid;
379                 }
380                 break;
381             case CK_CONFIG_COLOR: {
382                 int color;
383
384                 uid = valueIsUid ? (Ck_Uid) value : Ck_GetUid(value);
385                 if (Ck_GetColor(interp, (char *) value, &color) != TCL_OK)
386                     return TCL_ERROR;
387                 *((int *) ptr) = color;
388                 break;
389             }
390             case CK_CONFIG_BORDER: {
391                 CkBorder *new, *old;
392
393                 if (nullValue) {
394                     new = NULL;
395                 } else {
396                     uid = valueIsUid ? (Ck_Uid) value : Ck_GetUid(value);
397                     new = Ck_GetBorder(interp, uid);
398                     if (new == NULL) {
399                         return TCL_ERROR;
400                     }
401                 }
402                 old = *((CkBorder **) ptr);
403                 if (old != NULL) {
404                     Ck_FreeBorder(old);
405                 }
406                 *((CkBorder **) ptr) = new;
407                 break;
408             }
409             case CK_CONFIG_JUSTIFY:
410                 uid = valueIsUid ? (Ck_Uid) value : Ck_GetUid(value);
411                 if (Ck_GetJustify(interp, uid, (Ck_Justify *) ptr) != TCL_OK) {
412                     return TCL_ERROR;
413                 }
414                 break;
415             case CK_CONFIG_ANCHOR:
416                 uid = valueIsUid ? (Ck_Uid) value : Ck_GetUid(value);
417                 if (Ck_GetAnchor(interp, uid, (Ck_Anchor *) ptr) != TCL_OK) {
418                     return TCL_ERROR;
419                 }
420                 break;
421             case CK_CONFIG_COORD:
422                 if (Ck_GetCoord(interp, winPtr, value, (int *) ptr) != TCL_OK)
423                     return TCL_ERROR;
424                 break;
425             case CK_CONFIG_ATTR:
426                 if (Ck_GetAttr(interp, value, (int *) ptr) != TCL_OK)
427                     return TCL_ERROR;
428                 break;
429             case CK_CONFIG_WINDOW: {
430                 CkWindow *winPtr2;
431
432                 if (nullValue) {
433                     winPtr2 = NULL;
434                 } else {
435                     winPtr2 = Ck_NameToWindow(interp, value, winPtr);
436                     if (winPtr2 == NULL) {
437                         return TCL_ERROR;
438                     }
439                 }
440                 *((CkWindow **) ptr) = winPtr2;
441                 break;
442             }
443             case CK_CONFIG_CUSTOM:
444                 if ((*specPtr->customPtr->parseProc)(
445                         specPtr->customPtr->clientData, interp, winPtr,
446                         value, widgRec, specPtr->offset) != TCL_OK) {
447                     return TCL_ERROR;
448                 }
449                 break;
450             default: {
451                 sprintf(interp->result, "bad config table: unknown type %d",
452                         specPtr->type);
453                 return TCL_ERROR;
454             }
455         }
456         specPtr++;
457     } while ((specPtr->argvName == NULL) && (specPtr->type != CK_CONFIG_END));
458     return TCL_OK;
459 }
460 \f
461 /*
462  *--------------------------------------------------------------
463  *
464  * Ck_ConfigureInfo --
465  *
466  *      Return information about the configuration options
467  *      for a window, and their current values.
468  *
469  * Results:
470  *      Always returns TCL_OK.  Interp->result will be modified
471  *      hold a description of either a single configuration option
472  *      available for "widgRec" via "specs", or all the configuration
473  *      options available.  In the "all" case, the result will
474  *      available for "widgRec" via "specs".  The result will
475  *      be a list, each of whose entries describes one option.
476  *      Each entry will itself be a list containing the option's
477  *      name for use on command lines, database name, database
478  *      class, default value, and current value (empty string
479  *      if none).  For options that are synonyms, the list will
480  *      contain only two values:  name and synonym name.  If the
481  *      "name" argument is non-NULL, then the only information
482  *      returned is that for the named argument (i.e. the corresponding
483  *      entry in the overall list is returned).
484  *
485  * Side effects:
486  *      None.
487  *
488  *--------------------------------------------------------------
489  */
490
491 int
492 Ck_ConfigureInfo(interp, winPtr, specs, widgRec, argvName, flags)
493     Tcl_Interp *interp;         /* Interpreter for error reporting. */
494     CkWindow *winPtr;           /* Window corresponding to widgRec. */
495     Ck_ConfigSpec *specs;       /* Describes legal options. */
496     char *widgRec;              /* Record whose fields contain current
497                                  * values for options. */
498     char *argvName;             /* If non-NULL, indicates a single option
499                                  * whose info is to be returned.  Otherwise
500                                  * info is returned for all options. */
501     int flags;                  /* Used to specify additional flags
502                                  * that must be present in config specs
503                                  * for them to be considered. */
504 {
505     Ck_ConfigSpec *specPtr;
506     int needFlags, hateFlags;
507     char *list;
508     char *leader = "{";
509
510     needFlags = flags & ~(CK_CONFIG_USER_BIT - 1);
511     if (!(winPtr->mainPtr->flags & CK_HAS_COLOR)) {
512         hateFlags = CK_CONFIG_COLOR_ONLY;
513     } else {
514         hateFlags = CK_CONFIG_MONO_ONLY;
515     }
516
517     /*
518      * If information is only wanted for a single configuration
519      * spec, then handle that one spec specially.
520      */
521
522     Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
523     if (argvName != NULL) {
524         specPtr = FindConfigSpec(interp, specs, argvName, needFlags,
525                 hateFlags);
526         if (specPtr == NULL) {
527             return TCL_ERROR;
528         }
529         interp->result = FormatConfigInfo(interp, winPtr, specPtr, widgRec);
530         interp->freeProc = (Tcl_FreeProc *) free;
531         return TCL_OK;
532     }
533
534     /*
535      * Loop through all the specs, creating a big list with all
536      * their information.
537      */
538
539     for (specPtr = specs; specPtr->type != CK_CONFIG_END; specPtr++) {
540         if ((argvName != NULL) && (specPtr->argvName != argvName)) {
541             continue;
542         }
543         if (((specPtr->specFlags & needFlags) != needFlags)
544                 || (specPtr->specFlags & hateFlags)) {
545             continue;
546         }
547         if (specPtr->argvName == NULL) {
548             continue;
549         }
550         list = FormatConfigInfo(interp, winPtr, specPtr, widgRec);
551         Tcl_AppendResult(interp, leader, list, "}", (char *) NULL);
552         ckfree(list);
553         leader = " {";
554     }
555     return TCL_OK;
556 }
557 /*
558  *----------------------------------------------------------------------
559  *
560  * Ck_ConfigureValue --
561  *
562  *      This procedure returns the current value of a configuration
563  *      option for a widget.
564  *
565  * Results:
566  *      The return value is a standard Tcl completion code (TCL_OK or
567  *      TCL_ERROR).  Interp->result will be set to hold either the value
568  *      of the option given by argvName (if TCL_OK is returned) or
569  *      an error message (if TCL_ERROR is returned).
570  *
571  * Side effects:
572  *      None.
573  *
574  *----------------------------------------------------------------------
575  */
576
577 int
578 Ck_ConfigureValue(interp, winPtr, specs, widgRec, argvName, flags)
579     Tcl_Interp *interp;         /* Interpreter for error reporting. */
580     CkWindow *winPtr;           /* Window corresponding to widgRec. */
581     Ck_ConfigSpec *specs;       /* Describes legal options. */
582     char *widgRec;              /* Record whose fields contain current
583                                  * values for options. */
584     char *argvName;             /* Gives the command-line name for the
585                                  * option whose value is to be returned. */
586     int flags;                  /* Used to specify additional flags
587                                  * that must be present in config specs
588                                  * for them to be considered. */
589 {
590     Ck_ConfigSpec *specPtr;
591     int needFlags, hateFlags;
592
593     needFlags = flags & ~(CK_CONFIG_USER_BIT - 1);
594     if (winPtr->mainPtr->flags & CK_HAS_COLOR)
595         hateFlags = CK_CONFIG_MONO_ONLY;
596     else
597         hateFlags = CK_CONFIG_COLOR_ONLY;
598     specPtr = FindConfigSpec(interp, specs, argvName, needFlags, hateFlags);
599     if (specPtr == NULL) {
600         return TCL_ERROR;
601     }
602     interp->result = FormatConfigValue(interp, winPtr, specPtr, widgRec,
603             interp->result, &interp->freeProc);
604     return TCL_OK;
605 }
606 \f
607 /*
608  *--------------------------------------------------------------
609  *
610  * FormatConfigInfo --
611  *
612  *      Create a valid Tcl list holding the configuration information
613  *      for a single configuration option.
614  *
615  * Results:
616  *      A Tcl list, dynamically allocated.  The caller is expected to
617  *      arrange for this list to be freed eventually.
618  *
619  * Side effects:
620  *      Memory is allocated.
621  *
622  *--------------------------------------------------------------
623  */
624
625 static char *
626 FormatConfigInfo(interp, winPtr, specPtr, widgRec)
627     Tcl_Interp *interp;                 /* Interpreter to use for things
628                                          * like floating-point precision. */
629     CkWindow *winPtr;                   /* Window corresponding to widget. */
630     Ck_ConfigSpec *specPtr;             /* Pointer to information describing
631                                          * option. */
632     char *widgRec;                      /* Pointer to record holding current
633                                          * values of info for widget. */
634 {
635     char *argv[6], *result;
636     char buffer[200];
637     Tcl_FreeProc *freeProc = (Tcl_FreeProc *) NULL;
638
639     argv[0] = specPtr->argvName;
640     argv[1] = specPtr->dbName;
641     argv[2] = specPtr->dbClass;
642     argv[3] = specPtr->defValue;
643     if (specPtr->type == CK_CONFIG_SYNONYM) {
644         return Tcl_Merge(2, argv);
645     }
646     argv[4] = FormatConfigValue(interp, winPtr, specPtr, widgRec, buffer,
647             &freeProc);
648     if (argv[1] == NULL) {
649         argv[1] = "";
650     }
651     if (argv[2] == NULL) {
652         argv[2] = "";
653     }
654     if (argv[3] == NULL) {
655         argv[3] = "";
656     }
657     if (argv[4] == NULL) {
658         argv[4] = "";
659     }
660     result = Tcl_Merge(5, argv);
661     if (freeProc != NULL) {
662         if (freeProc == (Tcl_FreeProc *) free) {
663             ckfree(argv[4]);
664         } else {
665             (*freeProc)(argv[4]);
666         }
667     }
668     return result;
669 }
670 \f
671 /*
672  *----------------------------------------------------------------------
673  *
674  * FormatConfigValue --
675  *
676  *      This procedure formats the current value of a configuration
677  *      option.
678  *
679  * Results:
680  *      The return value is the formatted value of the option given
681  *      by specPtr and widgRec.  If the value is static, so that it
682  *      need not be freed, *freeProcPtr will be set to NULL;  otherwise
683  *      *freeProcPtr will be set to the address of a procedure to
684  *      free the result, and the caller must invoke this procedure
685  *      when it is finished with the result.
686  *
687  * Side effects:
688  *      None.
689  *
690  *----------------------------------------------------------------------
691  */
692
693 static char *
694 FormatConfigValue(interp, winPtr, specPtr, widgRec, buffer, freeProcPtr)
695     Tcl_Interp *interp;         /* Interpreter for use in real conversions. */
696     CkWindow *winPtr;           /* Window corresponding to widget. */
697     Ck_ConfigSpec *specPtr;     /* Pointer to information describing option.
698                                  * Must not point to a synonym option. */
699     char *widgRec;              /* Pointer to record holding current
700                                  * values of info for widget. */
701     char *buffer;               /* Static buffer to use for small values.
702                                  * Must have at least 200 bytes of storage. */
703     Tcl_FreeProc **freeProcPtr; /* Pointer to word to fill in with address
704                                  * of procedure to free the result, or NULL
705                                  * if result is static. */
706 {
707     char *ptr, *result;
708
709     *freeProcPtr = NULL;
710     ptr = widgRec + specPtr->offset;
711     result = "";
712     switch (specPtr->type) {
713         case CK_CONFIG_BOOLEAN:
714             if (*((int *) ptr) == 0) {
715                 result = "0";
716             } else {
717                 result = "1";
718             }
719             break;
720         case CK_CONFIG_INT:
721         case CK_CONFIG_COORD:
722             sprintf(buffer, "%d", *((int *) ptr));
723             result = buffer;
724             break;
725         case CK_CONFIG_DOUBLE:
726             Tcl_PrintDouble(interp, *((double *) ptr), buffer);
727             result = buffer;
728             break;
729         case CK_CONFIG_STRING:
730             result = (*(char **) ptr);
731             if (result == NULL)
732                 result = "";
733             break;
734         case CK_CONFIG_UID: {
735             Ck_Uid uid = *((Ck_Uid *) ptr);
736             if (uid != NULL) {
737                 result = uid;
738             }
739             break;
740         }
741         case CK_CONFIG_COLOR: {
742             result = Ck_NameOfColor(*((int *) ptr));
743             break;
744         }
745         case CK_CONFIG_BORDER: {
746             CkBorder *borderPtr = *((CkBorder **) ptr);
747             if (borderPtr != NULL) {
748                 result = Ck_NameOfBorder(borderPtr);
749             }
750             break;
751         }
752         case CK_CONFIG_JUSTIFY:
753             result = Ck_NameOfJustify(*((Ck_Justify *) ptr));
754             break;
755         case CK_CONFIG_ANCHOR:
756             result = Ck_NameOfAnchor(*((Ck_Anchor *) ptr));
757             break;
758         case CK_CONFIG_ATTR:
759             result = Ck_NameOfAttr(*(int *) ptr);
760             *freeProcPtr = (Tcl_FreeProc *) free;
761             break;
762         case CK_CONFIG_WINDOW: {
763             CkWindow *winPtr2;
764
765             winPtr2 = *((CkWindow **) ptr);
766             if (winPtr2 != NULL) {
767                 result = winPtr2->pathName;
768             }
769             break;
770         }
771         case CK_CONFIG_CUSTOM:
772             result = (*specPtr->customPtr->printProc)(
773                     specPtr->customPtr->clientData, winPtr, widgRec,
774                     specPtr->offset, freeProcPtr);
775             break;
776         default: 
777             result = "?? unknown type ??";
778     }
779     return result;
780 }
781 \f
782 /*
783  *----------------------------------------------------------------------
784  *
785  * Ck_FreeOptions --
786  *
787  *      Free up all resources associated with configuration options.
788  *
789  * Results:
790  *      None.
791  *
792  * Side effects:
793  *      Any resource in widgRec that is controlled by a configuration
794  *      option is freed in the appropriate fashion.
795  *
796  *----------------------------------------------------------------------
797  */
798
799 void
800 Ck_FreeOptions(specs, widgRec, needFlags)
801     Ck_ConfigSpec *specs;       /* Describes legal options. */
802     char *widgRec;              /* Record whose fields contain current
803                                  * values for options. */
804     int needFlags;              /* Used to specify additional flags
805                                  * that must be present in config specs
806                                  * for them to be considered. */
807 {
808     Ck_ConfigSpec *specPtr;
809     char *ptr;
810
811     for (specPtr = specs; specPtr->type != CK_CONFIG_END; specPtr++) {
812         if ((specPtr->specFlags & needFlags) != needFlags) {
813             continue;
814         }
815         ptr = widgRec + specPtr->offset;
816         switch (specPtr->type) {
817             case CK_CONFIG_STRING:
818                 if (*((char **) ptr) != NULL) {
819                     ckfree(*((char **) ptr));
820                     *((char **) ptr) = NULL;
821                 }
822                 break;
823             case CK_CONFIG_BORDER:
824                 if (*((CkBorder **) ptr) != NULL) {
825                     Ck_FreeBorder(*((CkBorder **) ptr));
826                     *((CkBorder **) ptr) = NULL;
827                 }
828                 break;
829         }
830     }
831 }