Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/tcl8.5.2/generic/tclConfig.c @ 35

Last change on this file since 35 was 25, checked in by landauf, 17 years ago

added tcl to libs

File size: 10.1 KB
Line 
1/*
2 * tclConfig.c --
3 *
4 *      This file provides the facilities which allow Tcl and other packages
5 *      to embed configuration information into their binary libraries.
6 *
7 * Copyright (c) 2002 Andreas Kupries <andreas_kupries@users.sourceforge.net>
8 *
9 * See the file "license.terms" for information on usage and redistribution of
10 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 *
12 * RCS: @(#) $Id: tclConfig.c,v 1.19 2007/12/13 15:23:16 dgp Exp $
13 */
14
15#include "tclInt.h"
16
17/*
18 * Internal structure to hold embedded configuration information.
19 *
20 * Our structure is a two-level dictionary associated with the 'interp'. The
21 * first level is keyed with the package name and maps to the dictionary for
22 * that package. The package dictionary is keyed with metadata keys and maps
23 * to the metadata value for that key. This is package specific. The metadata
24 * values are in UTF-8, converted from the external representation given to us
25 * by the caller.
26 */
27
28#define ASSOC_KEY       "tclPackageAboutDict"
29
30/*
31 * A ClientData struct for the QueryConfig command.  Store the two bits
32 * of data we need; the package name for which we store a config dict,
33 * and the (Tcl_Interp *) in which it is stored.
34 */
35
36typedef struct QCCD {
37    Tcl_Obj *pkg;
38    Tcl_Interp *interp;
39} QCCD;
40
41/*
42 * Static functions in this file:
43 */
44
45static int              QueryConfigObjCmd(ClientData clientData,
46                            Tcl_Interp *interp, int objc,
47                            struct Tcl_Obj *CONST *objv);
48static void             QueryConfigDelete(ClientData clientData);
49static Tcl_Obj *        GetConfigDict(Tcl_Interp *interp);
50static void             ConfigDictDeleteProc(ClientData clientData,
51                            Tcl_Interp *interp);
52
53/*
54 *----------------------------------------------------------------------
55 *
56 * Tcl_RegisterConfig --
57 *
58 *      See TIP#59 for details on what this function does.
59 *
60 * Results:
61 *      None.
62 *
63 * Side effects:
64 *      Creates namespace and cfg query command in it as per TIP #59.
65 *
66 *----------------------------------------------------------------------
67 */
68
69void
70Tcl_RegisterConfig(
71    Tcl_Interp *interp,         /* Interpreter the configuration command is
72                                 * registered in. */
73    CONST char *pkgName,        /* Name of the package registering the
74                                 * embedded configuration. ASCII, thus in
75                                 * UTF-8 too. */
76    Tcl_Config *configuration,  /* Embedded configuration. */
77    CONST char *valEncoding)    /* Name of the encoding used to store the
78                                 * configuration values, ASCII, thus UTF-8. */
79{
80    Tcl_Obj *pDB, *pkgDict;
81    Tcl_DString cmdName;
82    Tcl_Config *cfg;
83    Tcl_Encoding venc = Tcl_GetEncoding(NULL, valEncoding);
84    QCCD *cdPtr = (QCCD *)ckalloc(sizeof(QCCD));
85
86    cdPtr->interp = interp;
87    cdPtr->pkg = Tcl_NewStringObj(pkgName, -1);
88
89    /*
90     * Phase I: Adding the provided information to the internal database of
91     * package meta data. Only if we have an ok encoding.
92     *
93     * Phase II: Create a command for querying this database, specific to the
94     * package registerting its configuration. This is the approved interface
95     * in TIP 59. In the future a more general interface should be done, as
96     * followup to TIP 59. Simply because our database is now general across
97     * packages, and not a structure tied to one package.
98     *
99     * Note, the created command will have a reference through its clientdata.
100     */
101
102    Tcl_IncrRefCount(cdPtr->pkg);
103
104    /*
105     * For venc == NULL aka bogus encoding we skip the step setting up the
106     * dictionaries visible at Tcl level. I.e. they are not filled
107     */
108
109    if (venc != NULL) {
110        /*
111         * Retrieve package specific configuration...
112         */
113
114        pDB = GetConfigDict(interp);
115
116        if (Tcl_DictObjGet(interp, pDB, cdPtr->pkg, &pkgDict) != TCL_OK
117            || (pkgDict == NULL)) {
118            pkgDict = Tcl_NewDictObj();
119        } else if (Tcl_IsShared(pkgDict)) {
120            pkgDict = Tcl_DuplicateObj(pkgDict);
121        }
122
123        /*
124         * Extend the package configuration...
125         */
126
127        for (cfg=configuration ; cfg->key!=NULL && cfg->key[0]!='\0' ; cfg++) {
128            Tcl_DString conv;
129            CONST char *convValue =
130                Tcl_ExternalToUtfDString(venc, cfg->value, -1, &conv);
131
132            /*
133             * We know that the keys are in ASCII/UTF-8, so for them is no
134             * conversion required.
135             */
136
137            Tcl_DictObjPut(interp, pkgDict, Tcl_NewStringObj(cfg->key, -1),
138                           Tcl_NewStringObj(convValue, -1));
139            Tcl_DStringFree(&conv);
140        }
141
142        /*
143         * We're now done with the encoding, so drop it.
144         */
145
146        Tcl_FreeEncoding(venc);
147
148        /*
149         * Write the changes back into the overall database.
150         */
151
152        Tcl_DictObjPut(interp, pDB, cdPtr->pkg, pkgDict);
153    }
154
155    /*
156     * Now create the interface command for retrieval of the package
157     * information.
158     */
159
160    Tcl_DStringInit(&cmdName);
161    Tcl_DStringAppend(&cmdName, "::", -1);
162    Tcl_DStringAppend(&cmdName, pkgName, -1);
163
164    /*
165     * The incomplete command name is the name of the namespace to place it
166     * in.
167     */
168
169    if (Tcl_FindNamespace(interp, Tcl_DStringValue(&cmdName), NULL,
170            TCL_GLOBAL_ONLY) == NULL) {
171        if (Tcl_CreateNamespace(interp, Tcl_DStringValue(&cmdName),
172                NULL, NULL) == NULL) {
173            Tcl_Panic("%s.\n%s: %s",
174                    Tcl_GetStringResult(interp), "Tcl_RegisterConfig",
175                    "Unable to create namespace for package configuration.");
176        }
177    }
178
179    Tcl_DStringAppend(&cmdName, "::pkgconfig", -1);
180
181    if (Tcl_CreateObjCommand(interp, Tcl_DStringValue(&cmdName),
182            QueryConfigObjCmd, (ClientData) cdPtr, QueryConfigDelete) == NULL) {
183        Tcl_Panic("%s: %s", "Tcl_RegisterConfig",
184                "Unable to create query command for package configuration");
185    }
186
187    Tcl_DStringFree(&cmdName);
188}
189
190/*
191 *----------------------------------------------------------------------
192 *
193 * QueryConfigObjCmd --
194 *
195 *      Implementation of "::<package>::pkgconfig", the command to query
196 *      configuration information embedded into a binary library.
197 *
198 * Results:
199 *      A standard tcl result.
200 *
201 * Side effects:
202 *      See the manual for what this command does.
203 *
204 *----------------------------------------------------------------------
205 */
206
207static int
208QueryConfigObjCmd(
209    ClientData clientData,
210    Tcl_Interp *interp,
211    int objc,
212    struct Tcl_Obj *CONST *objv)
213{
214    QCCD *cdPtr = (QCCD *) clientData;
215    Tcl_Obj *pkgName = cdPtr->pkg;
216    Tcl_Obj *pDB, *pkgDict, *val, *listPtr;
217    int n, index;
218    static CONST char *subcmdStrings[] = {
219        "get", "list", NULL
220    };
221    enum subcmds {
222        CFG_GET, CFG_LIST
223    };
224
225    if ((objc < 2) || (objc > 3)) {
226        Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?argument?");
227        return TCL_ERROR;
228    }
229    if (Tcl_GetIndexFromObj(interp, objv[1], subcmdStrings, "subcommand", 0,
230            &index) != TCL_OK) {
231        return TCL_ERROR;
232    }
233
234    pDB = GetConfigDict(interp);
235    if (Tcl_DictObjGet(interp, pDB, pkgName, &pkgDict) != TCL_OK
236            || pkgDict == NULL) {
237        /*
238         * Maybe a Tcl_Panic is better, because the package data has to be
239         * present.
240         */
241
242        Tcl_SetResult(interp, "package not known", TCL_STATIC);
243        return TCL_ERROR;
244    }
245
246    switch ((enum subcmds) index) {
247    case CFG_GET:
248        if (objc != 3) {
249            Tcl_WrongNumArgs(interp, 2, objv, "key");
250            return TCL_ERROR;
251        }
252
253        if (Tcl_DictObjGet(interp, pkgDict, objv [2], &val) != TCL_OK
254                || val == NULL) {
255            Tcl_SetResult(interp, "key not known", TCL_STATIC);
256            return TCL_ERROR;
257        }
258
259        Tcl_SetObjResult(interp, val);
260        return TCL_OK;
261
262    case CFG_LIST:
263        if (objc != 2) {
264            Tcl_WrongNumArgs(interp, 2, objv, NULL);
265            return TCL_ERROR;
266        }
267
268        Tcl_DictObjSize(interp, pkgDict, &n);
269        listPtr = Tcl_NewListObj(n, NULL);
270
271        if (!listPtr) {
272            Tcl_SetResult(interp, "insufficient memory to create list",
273                    TCL_STATIC);
274            return TCL_ERROR;
275        }
276
277        if (n) {
278            List *listRepPtr = (List *)
279                    listPtr->internalRep.twoPtrValue.ptr1;
280            Tcl_DictSearch s;
281            Tcl_Obj *key, **vals;
282            int done, i = 0;
283
284            listRepPtr->elemCount = n;
285            vals = &listRepPtr->elements;
286
287            for (Tcl_DictObjFirst(interp, pkgDict, &s, &key, NULL, &done);
288                    !done; Tcl_DictObjNext(&s, &key, NULL, &done)) {
289                vals[i++] = key;
290                Tcl_IncrRefCount(key);
291            }
292        }
293
294        Tcl_SetObjResult(interp, listPtr);
295        return TCL_OK;
296
297    default:
298        Tcl_Panic("QueryConfigObjCmd: Unknown subcommand to 'pkgconfig'. This can't happen");
299        break;
300    }
301    return TCL_ERROR;
302}
303
304/*
305 *-------------------------------------------------------------------------
306 *
307 * QueryConfigDelete --
308 *
309 *      Command delete function. Cleans up after the configuration query
310 *      command when it is deleted by the user or during finalization.
311 *
312 * Results:
313 *      None.
314 *
315 * Side effects:
316 *      Deallocates all non-transient memory allocated by Tcl_RegisterConfig.
317 *
318 *-------------------------------------------------------------------------
319 */
320
321static void
322QueryConfigDelete(
323    ClientData clientData)
324{
325    QCCD *cdPtr = (QCCD *) clientData;
326    Tcl_Obj *pkgName = cdPtr->pkg;
327    Tcl_Obj *pDB = GetConfigDict(cdPtr->interp);
328    Tcl_DictObjRemove(NULL, pDB, pkgName);
329    Tcl_DecrRefCount(pkgName);
330    ckfree((char *)cdPtr);
331}
332
333/*
334 *-------------------------------------------------------------------------
335 *
336 * GetConfigDict --
337 *
338 *      Retrieve the package metadata database from the interpreter.
339 *      Initializes it, if not present yet.
340 *
341 * Results:
342 *      A Tcl_Obj reference
343 *
344 * Side effects:
345 *      May allocate a Tcl_Obj.
346 *
347 *-------------------------------------------------------------------------
348 */
349
350static Tcl_Obj *
351GetConfigDict(
352    Tcl_Interp *interp)
353{
354    Tcl_Obj *pDB = Tcl_GetAssocData(interp, ASSOC_KEY, NULL);
355
356    if (pDB == NULL) {
357        pDB = Tcl_NewDictObj();
358        Tcl_IncrRefCount(pDB);
359        Tcl_SetAssocData(interp, ASSOC_KEY, ConfigDictDeleteProc, pDB);
360    }
361
362    return pDB;
363}
364
365/*
366 *----------------------------------------------------------------------
367 *
368 * ConfigDictDeleteProc --
369 *
370 *      This function is associated with the "Package About dict" assoc data
371 *      for an interpreter; it is invoked when the interpreter is deleted in
372 *      order to free the information assoicated with any pending error
373 *      reports.
374 *
375 * Results:
376 *      None.
377 *
378 * Side effects:
379 *      The package metadata database is freed.
380 *
381 *----------------------------------------------------------------------
382 */
383
384static void
385ConfigDictDeleteProc(
386    ClientData clientData,      /* Pointer to Tcl_Obj. */
387    Tcl_Interp *interp)         /* Interpreter being deleted. */
388{
389    Tcl_Obj *pDB = (Tcl_Obj *) clientData;
390
391    Tcl_DecrRefCount(pDB);
392}
393
394/*
395 * Local Variables:
396 * mode: c
397 * c-basic-offset: 4
398 * fill-column: 78
399 * End:
400 */
Note: See TracBrowser for help on using the repository browser.