tclConfig.cGo to the documentation of this file.00001 /* 00002 * tclConfig.c -- 00003 * 00004 * This file provides the facilities which allow Tcl and other packages 00005 * to embed configuration information into their binary libraries. 00006 * 00007 * Copyright (c) 2002 Andreas Kupries <andreas_kupries@users.sourceforge.net> 00008 * 00009 * See the file "license.terms" for information on usage and redistribution of 00010 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 00011 * 00012 * RCS: @(#) $Id: tclConfig.c,v 1.19 2007/12/13 15:23:16 dgp Exp $ 00013 */ 00014 00015 #include "tclInt.h" 00016 00017 /* 00018 * Internal structure to hold embedded configuration information. 00019 * 00020 * Our structure is a two-level dictionary associated with the 'interp'. The 00021 * first level is keyed with the package name and maps to the dictionary for 00022 * that package. The package dictionary is keyed with metadata keys and maps 00023 * to the metadata value for that key. This is package specific. The metadata 00024 * values are in UTF-8, converted from the external representation given to us 00025 * by the caller. 00026 */ 00027 00028 #define ASSOC_KEY "tclPackageAboutDict" 00029 00030 /* 00031 * A ClientData struct for the QueryConfig command. Store the two bits 00032 * of data we need; the package name for which we store a config dict, 00033 * and the (Tcl_Interp *) in which it is stored. 00034 */ 00035 00036 typedef struct QCCD { 00037 Tcl_Obj *pkg; 00038 Tcl_Interp *interp; 00039 } QCCD; 00040 00041 /* 00042 * Static functions in this file: 00043 */ 00044 00045 static int QueryConfigObjCmd(ClientData clientData, 00046 Tcl_Interp *interp, int objc, 00047 struct Tcl_Obj *CONST *objv); 00048 static void QueryConfigDelete(ClientData clientData); 00049 static Tcl_Obj * GetConfigDict(Tcl_Interp *interp); 00050 static void ConfigDictDeleteProc(ClientData clientData, 00051 Tcl_Interp *interp); 00052 00053 /* 00054 *---------------------------------------------------------------------- 00055 * 00056 * Tcl_RegisterConfig -- 00057 * 00058 * See TIP#59 for details on what this function does. 00059 * 00060 * Results: 00061 * None. 00062 * 00063 * Side effects: 00064 * Creates namespace and cfg query command in it as per TIP #59. 00065 * 00066 *---------------------------------------------------------------------- 00067 */ 00068 00069 void 00070 Tcl_RegisterConfig( 00071 Tcl_Interp *interp, /* Interpreter the configuration command is 00072 * registered in. */ 00073 CONST char *pkgName, /* Name of the package registering the 00074 * embedded configuration. ASCII, thus in 00075 * UTF-8 too. */ 00076 Tcl_Config *configuration, /* Embedded configuration. */ 00077 CONST char *valEncoding) /* Name of the encoding used to store the 00078 * configuration values, ASCII, thus UTF-8. */ 00079 { 00080 Tcl_Obj *pDB, *pkgDict; 00081 Tcl_DString cmdName; 00082 Tcl_Config *cfg; 00083 Tcl_Encoding venc = Tcl_GetEncoding(NULL, valEncoding); 00084 QCCD *cdPtr = (QCCD *)ckalloc(sizeof(QCCD)); 00085 00086 cdPtr->interp = interp; 00087 cdPtr->pkg = Tcl_NewStringObj(pkgName, -1); 00088 00089 /* 00090 * Phase I: Adding the provided information to the internal database of 00091 * package meta data. Only if we have an ok encoding. 00092 * 00093 * Phase II: Create a command for querying this database, specific to the 00094 * package registerting its configuration. This is the approved interface 00095 * in TIP 59. In the future a more general interface should be done, as 00096 * followup to TIP 59. Simply because our database is now general across 00097 * packages, and not a structure tied to one package. 00098 * 00099 * Note, the created command will have a reference through its clientdata. 00100 */ 00101 00102 Tcl_IncrRefCount(cdPtr->pkg); 00103 00104 /* 00105 * For venc == NULL aka bogus encoding we skip the step setting up the 00106 * dictionaries visible at Tcl level. I.e. they are not filled 00107 */ 00108 00109 if (venc != NULL) { 00110 /* 00111 * Retrieve package specific configuration... 00112 */ 00113 00114 pDB = GetConfigDict(interp); 00115 00116 if (Tcl_DictObjGet(interp, pDB, cdPtr->pkg, &pkgDict) != TCL_OK 00117 || (pkgDict == NULL)) { 00118 pkgDict = Tcl_NewDictObj(); 00119 } else if (Tcl_IsShared(pkgDict)) { 00120 pkgDict = Tcl_DuplicateObj(pkgDict); 00121 } 00122 00123 /* 00124 * Extend the package configuration... 00125 */ 00126 00127 for (cfg=configuration ; cfg->key!=NULL && cfg->key[0]!='\0' ; cfg++) { 00128 Tcl_DString conv; 00129 CONST char *convValue = 00130 Tcl_ExternalToUtfDString(venc, cfg->value, -1, &conv); 00131 00132 /* 00133 * We know that the keys are in ASCII/UTF-8, so for them is no 00134 * conversion required. 00135 */ 00136 00137 Tcl_DictObjPut(interp, pkgDict, Tcl_NewStringObj(cfg->key, -1), 00138 Tcl_NewStringObj(convValue, -1)); 00139 Tcl_DStringFree(&conv); 00140 } 00141 00142 /* 00143 * We're now done with the encoding, so drop it. 00144 */ 00145 00146 Tcl_FreeEncoding(venc); 00147 00148 /* 00149 * Write the changes back into the overall database. 00150 */ 00151 00152 Tcl_DictObjPut(interp, pDB, cdPtr->pkg, pkgDict); 00153 } 00154 00155 /* 00156 * Now create the interface command for retrieval of the package 00157 * information. 00158 */ 00159 00160 Tcl_DStringInit(&cmdName); 00161 Tcl_DStringAppend(&cmdName, "::", -1); 00162 Tcl_DStringAppend(&cmdName, pkgName, -1); 00163 00164 /* 00165 * The incomplete command name is the name of the namespace to place it 00166 * in. 00167 */ 00168 00169 if (Tcl_FindNamespace(interp, Tcl_DStringValue(&cmdName), NULL, 00170 TCL_GLOBAL_ONLY) == NULL) { 00171 if (Tcl_CreateNamespace(interp, Tcl_DStringValue(&cmdName), 00172 NULL, NULL) == NULL) { 00173 Tcl_Panic("%s.\n%s: %s", 00174 Tcl_GetStringResult(interp), "Tcl_RegisterConfig", 00175 "Unable to create namespace for package configuration."); 00176 } 00177 } 00178 00179 Tcl_DStringAppend(&cmdName, "::pkgconfig", -1); 00180 00181 if (Tcl_CreateObjCommand(interp, Tcl_DStringValue(&cmdName), 00182 QueryConfigObjCmd, (ClientData) cdPtr, QueryConfigDelete) == NULL) { 00183 Tcl_Panic("%s: %s", "Tcl_RegisterConfig", 00184 "Unable to create query command for package configuration"); 00185 } 00186 00187 Tcl_DStringFree(&cmdName); 00188 } 00189 00190 /* 00191 *---------------------------------------------------------------------- 00192 * 00193 * QueryConfigObjCmd -- 00194 * 00195 * Implementation of "::<package>::pkgconfig", the command to query 00196 * configuration information embedded into a binary library. 00197 * 00198 * Results: 00199 * A standard tcl result. 00200 * 00201 * Side effects: 00202 * See the manual for what this command does. 00203 * 00204 *---------------------------------------------------------------------- 00205 */ 00206 00207 static int 00208 QueryConfigObjCmd( 00209 ClientData clientData, 00210 Tcl_Interp *interp, 00211 int objc, 00212 struct Tcl_Obj *CONST *objv) 00213 { 00214 QCCD *cdPtr = (QCCD *) clientData; 00215 Tcl_Obj *pkgName = cdPtr->pkg; 00216 Tcl_Obj *pDB, *pkgDict, *val, *listPtr; 00217 int n, index; 00218 static CONST char *subcmdStrings[] = { 00219 "get", "list", NULL 00220 }; 00221 enum subcmds { 00222 CFG_GET, CFG_LIST 00223 }; 00224 00225 if ((objc < 2) || (objc > 3)) { 00226 Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?argument?"); 00227 return TCL_ERROR; 00228 } 00229 if (Tcl_GetIndexFromObj(interp, objv[1], subcmdStrings, "subcommand", 0, 00230 &index) != TCL_OK) { 00231 return TCL_ERROR; 00232 } 00233 00234 pDB = GetConfigDict(interp); 00235 if (Tcl_DictObjGet(interp, pDB, pkgName, &pkgDict) != TCL_OK 00236 || pkgDict == NULL) { 00237 /* 00238 * Maybe a Tcl_Panic is better, because the package data has to be 00239 * present. 00240 */ 00241 00242 Tcl_SetResult(interp, "package not known", TCL_STATIC); 00243 return TCL_ERROR; 00244 } 00245 00246 switch ((enum subcmds) index) { 00247 case CFG_GET: 00248 if (objc != 3) { 00249 Tcl_WrongNumArgs(interp, 2, objv, "key"); 00250 return TCL_ERROR; 00251 } 00252 00253 if (Tcl_DictObjGet(interp, pkgDict, objv [2], &val) != TCL_OK 00254 || val == NULL) { 00255 Tcl_SetResult(interp, "key not known", TCL_STATIC); 00256 return TCL_ERROR; 00257 } 00258 00259 Tcl_SetObjResult(interp, val); 00260 return TCL_OK; 00261 00262 case CFG_LIST: 00263 if (objc != 2) { 00264 Tcl_WrongNumArgs(interp, 2, objv, NULL); 00265 return TCL_ERROR; 00266 } 00267 00268 Tcl_DictObjSize(interp, pkgDict, &n); 00269 listPtr = Tcl_NewListObj(n, NULL); 00270 00271 if (!listPtr) { 00272 Tcl_SetResult(interp, "insufficient memory to create list", 00273 TCL_STATIC); 00274 return TCL_ERROR; 00275 } 00276 00277 if (n) { 00278 List *listRepPtr = (List *) 00279 listPtr->internalRep.twoPtrValue.ptr1; 00280 Tcl_DictSearch s; 00281 Tcl_Obj *key, **vals; 00282 int done, i = 0; 00283 00284 listRepPtr->elemCount = n; 00285 vals = &listRepPtr->elements; 00286 00287 for (Tcl_DictObjFirst(interp, pkgDict, &s, &key, NULL, &done); 00288 !done; Tcl_DictObjNext(&s, &key, NULL, &done)) { 00289 vals[i++] = key; 00290 Tcl_IncrRefCount(key); 00291 } 00292 } 00293 00294 Tcl_SetObjResult(interp, listPtr); 00295 return TCL_OK; 00296 00297 default: 00298 Tcl_Panic("QueryConfigObjCmd: Unknown subcommand to 'pkgconfig'. This can't happen"); 00299 break; 00300 } 00301 return TCL_ERROR; 00302 } 00303 00304 /* 00305 *------------------------------------------------------------------------- 00306 * 00307 * QueryConfigDelete -- 00308 * 00309 * Command delete function. Cleans up after the configuration query 00310 * command when it is deleted by the user or during finalization. 00311 * 00312 * Results: 00313 * None. 00314 * 00315 * Side effects: 00316 * Deallocates all non-transient memory allocated by Tcl_RegisterConfig. 00317 * 00318 *------------------------------------------------------------------------- 00319 */ 00320 00321 static void 00322 QueryConfigDelete( 00323 ClientData clientData) 00324 { 00325 QCCD *cdPtr = (QCCD *) clientData; 00326 Tcl_Obj *pkgName = cdPtr->pkg; 00327 Tcl_Obj *pDB = GetConfigDict(cdPtr->interp); 00328 Tcl_DictObjRemove(NULL, pDB, pkgName); 00329 Tcl_DecrRefCount(pkgName); 00330 ckfree((char *)cdPtr); 00331 } 00332 00333 /* 00334 *------------------------------------------------------------------------- 00335 * 00336 * GetConfigDict -- 00337 * 00338 * Retrieve the package metadata database from the interpreter. 00339 * Initializes it, if not present yet. 00340 * 00341 * Results: 00342 * A Tcl_Obj reference 00343 * 00344 * Side effects: 00345 * May allocate a Tcl_Obj. 00346 * 00347 *------------------------------------------------------------------------- 00348 */ 00349 00350 static Tcl_Obj * 00351 GetConfigDict( 00352 Tcl_Interp *interp) 00353 { 00354 Tcl_Obj *pDB = Tcl_GetAssocData(interp, ASSOC_KEY, NULL); 00355 00356 if (pDB == NULL) { 00357 pDB = Tcl_NewDictObj(); 00358 Tcl_IncrRefCount(pDB); 00359 Tcl_SetAssocData(interp, ASSOC_KEY, ConfigDictDeleteProc, pDB); 00360 } 00361 00362 return pDB; 00363 } 00364 00365 /* 00366 *---------------------------------------------------------------------- 00367 * 00368 * ConfigDictDeleteProc -- 00369 * 00370 * This function is associated with the "Package About dict" assoc data 00371 * for an interpreter; it is invoked when the interpreter is deleted in 00372 * order to free the information assoicated with any pending error 00373 * reports. 00374 * 00375 * Results: 00376 * None. 00377 * 00378 * Side effects: 00379 * The package metadata database is freed. 00380 * 00381 *---------------------------------------------------------------------- 00382 */ 00383 00384 static void 00385 ConfigDictDeleteProc( 00386 ClientData clientData, /* Pointer to Tcl_Obj. */ 00387 Tcl_Interp *interp) /* Interpreter being deleted. */ 00388 { 00389 Tcl_Obj *pDB = (Tcl_Obj *) clientData; 00390 00391 Tcl_DecrRefCount(pDB); 00392 } 00393 00394 /* 00395 * Local Variables: 00396 * mode: c 00397 * c-basic-offset: 4 00398 * fill-column: 78 00399 * End: 00400 */
Generated on Wed Mar 12 12:18:14 2008 by 1.5.1 |