4 * This file provides the facilities which allow Tcl and other packages
5 * to embed configuration information into their binary libraries.
7 * Copyright (c) 2002 Andreas Kupries <andreas_kupries@users.sourceforge.net>
9 * See the file "license.terms" for information on usage and redistribution of
10 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
16 * Internal structure to hold embedded configuration information.
18 * Our structure is a two-level dictionary associated with the 'interp'. The
19 * first level is keyed with the package name and maps to the dictionary for
20 * that package. The package dictionary is keyed with metadata keys and maps
21 * to the metadata value for that key. This is package specific. The metadata
22 * values are in UTF-8, converted from the external representation given to us
26 #define ASSOC_KEY "tclPackageAboutDict"
29 * A ClientData struct for the QueryConfig command. Store the three bits
30 * of data we need; the package name for which we store a config dict,
31 * the (Tcl_Interp *) in which it is stored, and the encoding.
41 * Static functions in this file:
44 static int QueryConfigObjCmd(ClientData clientData,
45 Tcl_Interp *interp, int objc,
46 struct Tcl_Obj *const *objv);
47 static void QueryConfigDelete(ClientData clientData);
48 static Tcl_Obj * GetConfigDict(Tcl_Interp *interp);
49 static void ConfigDictDeleteProc(ClientData clientData,
53 *----------------------------------------------------------------------
55 * Tcl_RegisterConfig --
57 * See TIP#59 for details on what this function does.
63 * Creates namespace and cfg query command in it as per TIP #59.
65 *----------------------------------------------------------------------
70 Tcl_Interp *interp, /* Interpreter the configuration command is
72 const char *pkgName, /* Name of the package registering the
73 * embedded configuration. ASCII, thus in
75 const Tcl_Config *configuration, /* Embedded configuration. */
76 const char *valEncoding) /* Name of the encoding used to store the
77 * configuration values, ASCII, thus UTF-8. */
79 Tcl_Obj *pDB, *pkgDict;
81 const Tcl_Config *cfg;
82 QCCD *cdPtr = ckalloc(sizeof(QCCD));
84 cdPtr->interp = interp;
86 cdPtr->encoding = ckalloc(strlen(valEncoding)+1);
87 strcpy(cdPtr->encoding, valEncoding);
89 cdPtr->encoding = NULL;
91 cdPtr->pkg = Tcl_NewStringObj(pkgName, -1);
94 * Phase I: Adding the provided information to the internal database of
97 * Phase II: Create a command for querying this database, specific to the
98 * package registering its configuration. This is the approved interface
99 * in TIP 59. In the future a more general interface should be done, as
100 * follow-up to TIP 59. Simply because our database is now general across
101 * packages, and not a structure tied to one package.
103 * Note, the created command will have a reference through its clientdata.
106 Tcl_IncrRefCount(cdPtr->pkg);
109 * For venc == NULL aka bogus encoding we skip the step setting up the
110 * dictionaries visible at Tcl level. I.e. they are not filled
113 pDB = GetConfigDict(interp);
116 * Retrieve package specific configuration...
119 if (Tcl_DictObjGet(interp, pDB, cdPtr->pkg, &pkgDict) != TCL_OK
120 || (pkgDict == NULL)) {
121 pkgDict = Tcl_NewDictObj();
122 } else if (Tcl_IsShared(pkgDict)) {
123 pkgDict = Tcl_DuplicateObj(pkgDict);
127 * Extend the package configuration...
128 * We cannot assume that the encodings are initialized, therefore
129 * store the value as-is in a byte array. See Bug [9b2e636361].
132 for (cfg=configuration ; cfg->key!=NULL && cfg->key[0]!='\0' ; cfg++) {
133 Tcl_DictObjPut(interp, pkgDict, Tcl_NewStringObj(cfg->key, -1),
134 Tcl_NewByteArrayObj((unsigned char *)cfg->value, strlen(cfg->value)));
138 * Write the changes back into the overall database.
141 Tcl_DictObjPut(interp, pDB, cdPtr->pkg, pkgDict);
144 * Now create the interface command for retrieval of the package
148 Tcl_DStringInit(&cmdName);
149 TclDStringAppendLiteral(&cmdName, "::");
150 Tcl_DStringAppend(&cmdName, pkgName, -1);
153 * The incomplete command name is the name of the namespace to place it
157 if (Tcl_FindNamespace(interp, Tcl_DStringValue(&cmdName), NULL,
158 TCL_GLOBAL_ONLY) == NULL) {
159 if (Tcl_CreateNamespace(interp, Tcl_DStringValue(&cmdName),
160 NULL, NULL) == NULL) {
161 Tcl_Panic("%s.\n%s: %s",
162 Tcl_GetStringResult(interp), "Tcl_RegisterConfig",
163 "Unable to create namespace for package configuration.");
167 TclDStringAppendLiteral(&cmdName, "::pkgconfig");
169 if (Tcl_CreateObjCommand(interp, Tcl_DStringValue(&cmdName),
170 QueryConfigObjCmd, cdPtr, QueryConfigDelete) == NULL) {
171 Tcl_Panic("%s: %s", "Tcl_RegisterConfig",
172 "Unable to create query command for package configuration");
175 Tcl_DStringFree(&cmdName);
179 *----------------------------------------------------------------------
181 * QueryConfigObjCmd --
183 * Implementation of "::<package>::pkgconfig", the command to query
184 * configuration information embedded into a binary library.
187 * A standard tcl result.
190 * See the manual for what this command does.
192 *----------------------------------------------------------------------
197 ClientData clientData,
200 struct Tcl_Obj *const *objv)
202 QCCD *cdPtr = clientData;
203 Tcl_Obj *pkgName = cdPtr->pkg;
204 Tcl_Obj *pDB, *pkgDict, *val, *listPtr;
206 static const char *const subcmdStrings[] = {
213 Tcl_Encoding venc = NULL;
216 if ((objc < 2) || (objc > 3)) {
217 Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?arg?");
220 if (Tcl_GetIndexFromObj(interp, objv[1], subcmdStrings, "subcommand", 0,
225 pDB = GetConfigDict(interp);
226 if (Tcl_DictObjGet(interp, pDB, pkgName, &pkgDict) != TCL_OK
227 || pkgDict == NULL) {
229 * Maybe a Tcl_Panic is better, because the package data has to be
233 Tcl_SetObjResult(interp, Tcl_NewStringObj("package not known", -1));
234 Tcl_SetErrorCode(interp, "TCL", "FATAL", "PKGCFG_BASE",
235 Tcl_GetString(pkgName), NULL);
239 switch ((enum subcmds) index) {
242 Tcl_WrongNumArgs(interp, 2, objv, "key");
246 if (Tcl_DictObjGet(interp, pkgDict, objv[2], &val) != TCL_OK
248 Tcl_SetObjResult(interp, Tcl_NewStringObj("key not known", -1));
249 Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "CONFIG",
250 Tcl_GetString(objv[2]), NULL);
254 if (cdPtr->encoding) {
255 venc = Tcl_GetEncoding(interp, cdPtr->encoding);
261 * Value is stored as-is in a byte array, see Bug [9b2e636361],
262 * so we have to decode it first.
264 value = (const char *) Tcl_GetByteArrayFromObj(val, &n);
265 value = Tcl_ExternalToUtfDString(venc, value, n, &conv);
266 Tcl_SetObjResult(interp, Tcl_NewStringObj(value,
267 Tcl_DStringLength(&conv)));
268 Tcl_DStringFree(&conv);
273 Tcl_WrongNumArgs(interp, 2, objv, NULL);
277 Tcl_DictObjSize(interp, pkgDict, &n);
278 listPtr = Tcl_NewListObj(n, NULL);
281 Tcl_SetObjResult(interp, Tcl_NewStringObj(
282 "insufficient memory to create list", -1));
283 Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL);
292 for (Tcl_DictObjFirst(interp, pkgDict, &s, &key, NULL, &done);
293 !done; Tcl_DictObjNext(&s, &key, NULL, &done)) {
294 Tcl_ListObjAppendElement(NULL, listPtr, key);
298 Tcl_SetObjResult(interp, listPtr);
302 Tcl_Panic("QueryConfigObjCmd: Unknown subcommand to 'pkgconfig'. This can't happen");
309 *-------------------------------------------------------------------------
311 * QueryConfigDelete --
313 * Command delete function. Cleans up after the configuration query
314 * command when it is deleted by the user or during finalization.
320 * Deallocates all non-transient memory allocated by Tcl_RegisterConfig.
322 *-------------------------------------------------------------------------
327 ClientData clientData)
329 QCCD *cdPtr = clientData;
330 Tcl_Obj *pkgName = cdPtr->pkg;
331 Tcl_Obj *pDB = GetConfigDict(cdPtr->interp);
333 Tcl_DictObjRemove(NULL, pDB, pkgName);
334 Tcl_DecrRefCount(pkgName);
335 if (cdPtr->encoding) {
336 ckfree((char *)cdPtr->encoding);
338 ckfree((char *)cdPtr);
342 *-------------------------------------------------------------------------
346 * Retrieve the package metadata database from the interpreter.
347 * Initializes it, if not present yet.
350 * A Tcl_Obj reference
353 * May allocate a Tcl_Obj.
355 *-------------------------------------------------------------------------
362 Tcl_Obj *pDB = Tcl_GetAssocData(interp, ASSOC_KEY, NULL);
365 pDB = Tcl_NewDictObj();
366 Tcl_IncrRefCount(pDB);
367 Tcl_SetAssocData(interp, ASSOC_KEY, ConfigDictDeleteProc, pDB);
374 *----------------------------------------------------------------------
376 * ConfigDictDeleteProc --
378 * This function is associated with the "Package About dict" assoc data
379 * for an interpreter; it is invoked when the interpreter is deleted in
380 * order to free the information associated with any pending error
387 * The package metadata database is freed.
389 *----------------------------------------------------------------------
393 ConfigDictDeleteProc(
394 ClientData clientData, /* Pointer to Tcl_Obj. */
395 Tcl_Interp *interp) /* Interpreter being deleted. */
397 Tcl_Obj *pDB = clientData;
399 Tcl_DecrRefCount(pDB);