summaryrefslogtreecommitdiffstats
path: root/generic/tclConfig.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclConfig.c')
-rw-r--r--generic/tclConfig.c372
1 files changed, 212 insertions, 160 deletions
diff --git a/generic/tclConfig.c b/generic/tclConfig.c
index 3cd5813..2fb3e92 100644
--- a/generic/tclConfig.c
+++ b/generic/tclConfig.c
@@ -1,4 +1,4 @@
-/*
+/*
* tclConfig.c --
*
* This file provides the facilities which allow Tcl and other packages
@@ -6,54 +6,55 @@
*
* Copyright (c) 2002 Andreas Kupries <andreas_kupries@users.sourceforge.net>
*
- * See the file "license.terms" for information on usage and redistribution
- * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
- *
- * RCS: @(#) $Id: tclConfig.c,v 1.5 2003/12/24 04:18:19 davygrvy Exp $
+ * See the file "license.terms" for information on usage and redistribution of
+ * this file, and for a DISCLAIMER OF ALL WARRANTIES.
*/
#include "tclInt.h"
-
-
/*
* Internal structure to hold embedded configuration information.
*
- * Our structure is a two-level dictionary associated with the
- * 'interp'. The first level is keyed with the package name and maps
- * to the dictionary for that package. The package dictionary is keyed
- * with metadata keys and maps to the metadata value for that
- * key. This is package specific. The metadata values are in UTF8,
- * converted from the external representation given to us by the
- * caller.
+ * Our structure is a two-level dictionary associated with the 'interp'. The
+ * first level is keyed with the package name and maps to the dictionary for
+ * that package. The package dictionary is keyed with metadata keys and maps
+ * to the metadata value for that key. This is package specific. The metadata
+ * values are in UTF-8, converted from the external representation given to us
+ * by the caller.
*/
-#define ASSOC_KEY "tclPackageAboutDict"
+#define ASSOC_KEY "tclPackageAboutDict"
/*
- * Static functions in this file:
+ * A ClientData struct for the QueryConfig command. Store the three bits
+ * of data we need; the package name for which we store a config dict,
+ * the (Tcl_Interp *) in which it is stored, and the encoding.
*/
-static int
-QueryConfigObjCmd _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, int objc, struct Tcl_Obj * CONST * objv));
-
-static void
-QueryConfigDelete _ANSI_ARGS_((ClientData clientData));
+typedef struct QCCD {
+ Tcl_Obj *pkg;
+ Tcl_Interp *interp;
+ char *encoding;
+} QCCD;
-static Tcl_Obj*
-GetConfigDict _ANSI_ARGS_((Tcl_Interp* interp));
+/*
+ * Static functions in this file:
+ */
-static void
-ConfigDictDeleteProc _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp));
+static int QueryConfigObjCmd(ClientData clientData,
+ Tcl_Interp *interp, int objc,
+ struct Tcl_Obj *const *objv);
+static void QueryConfigDelete(ClientData clientData);
+static Tcl_Obj * GetConfigDict(Tcl_Interp *interp);
+static void ConfigDictDeleteProc(ClientData clientData,
+ Tcl_Interp *interp);
/*
*----------------------------------------------------------------------
*
* Tcl_RegisterConfig --
*
- * See TIP#59 for details on what this procedure does.
+ * See TIP#59 for details on what this function does.
*
* Results:
* None.
@@ -65,110 +66,113 @@ ConfigDictDeleteProc _ANSI_ARGS_((ClientData clientData,
*/
void
-Tcl_RegisterConfig (interp, pkgName, configuration, valEncoding)
- Tcl_Interp* interp; /* Interpreter the configuration
- * command is registered in. */
- CONST char* pkgName; /* Name of the package registering
- * the embedded configuration. ASCII,
- * thus in UTF-8 too. */
- Tcl_Config* configuration; /* Embedded configuration */
- CONST char* valEncoding; /* Name of the encoding used to
- * store the configuration values,
- * ASCII, thus UTF-8 */
+Tcl_RegisterConfig(
+ Tcl_Interp *interp, /* Interpreter the configuration command is
+ * registered in. */
+ const char *pkgName, /* Name of the package registering the
+ * embedded configuration. ASCII, thus in
+ * UTF-8 too. */
+ const Tcl_Config *configuration, /* Embedded configuration. */
+ const char *valEncoding) /* Name of the encoding used to store the
+ * configuration values, ASCII, thus UTF-8. */
{
- Tcl_Encoding venc = Tcl_GetEncoding (NULL, valEncoding);
- Tcl_Obj* pDB = GetConfigDict (interp);
- Tcl_Obj* pkg = Tcl_NewStringObj (pkgName, -1);
- Tcl_Obj* pkgDict;
- Tcl_DString cmdName;
- Tcl_Config* cfg;
- int res;
+ Tcl_Obj *pDB, *pkgDict;
+ Tcl_DString cmdName;
+ const Tcl_Config *cfg;
+ QCCD *cdPtr = ckalloc(sizeof(QCCD));
+
+ cdPtr->interp = interp;
+ if (valEncoding) {
+ cdPtr->encoding = ckalloc(strlen(valEncoding)+1);
+ strcpy(cdPtr->encoding, valEncoding);
+ } else {
+ cdPtr->encoding = NULL;
+ }
+ cdPtr->pkg = Tcl_NewStringObj(pkgName, -1);
/*
- * Phase I: Adding the provided information to the internal
- * database of package meta data.
+ * Phase I: Adding the provided information to the internal database of
+ * package meta data.
+ *
+ * Phase II: Create a command for querying this database, specific to the
+ * package registering its configuration. This is the approved interface
+ * in TIP 59. In the future a more general interface should be done, as
+ * follow-up to TIP 59. Simply because our database is now general across
+ * packages, and not a structure tied to one package.
*
- * Phase II: Create a command for querying this database, specific
- * to the package registerting its configuration. This is the
- * approved interface in TIP 59. In the future a more general
- * interface should be done, as followup to TIP 59. Simply because
- * our database is now general across packages, and not a
- * structure tied to one package.
+ * Note, the created command will have a reference through its clientdata.
*/
- /* Note, the created command will have a reference through its clientdata */
- Tcl_IncrRefCount (pkg);
-
- /* Retrieve package specific configuration ... */
+ Tcl_IncrRefCount(cdPtr->pkg);
- res = Tcl_DictObjGet (interp, pDB, pkg, &pkgDict);
- if ((TCL_OK != res) || (pkgDict == NULL)) {
- pkgDict = Tcl_NewDictObj ();
- } else if (Tcl_IsShared (pkgDict)) {
- pkgDict = Tcl_DuplicateObj (pkgDict);
- }
+ /*
+ * For venc == NULL aka bogus encoding we skip the step setting up the
+ * dictionaries visible at Tcl level. I.e. they are not filled
+ */
- /* Extend the package configuration ... */
+ pDB = GetConfigDict(interp);
- for (cfg = configuration;
- (cfg->key != (CONST char*) NULL) && (cfg->key [0] != '\0') ;
- cfg++) {
+ /*
+ * Retrieve package specific configuration...
+ */
- Tcl_DString conv;
- CONST char* convValue = Tcl_ExternalToUtfDString (venc, cfg->value, -1, &conv);
+ if (Tcl_DictObjGet(interp, pDB, cdPtr->pkg, &pkgDict) != TCL_OK
+ || (pkgDict == NULL)) {
+ pkgDict = Tcl_NewDictObj();
+ } else if (Tcl_IsShared(pkgDict)) {
+ pkgDict = Tcl_DuplicateObj(pkgDict);
+ }
- /*
- * We know that the keys are in ASCII/UTF-8, so for them is no
- * conversion required.
- */
+ /*
+ * Extend the package configuration...
+ * We cannot assume that the encodings are initialized, therefore
+ * store the value as-is in a byte array. See Bug [9b2e636361].
+ */
- Tcl_DictObjPut (interp, pkgDict,
- Tcl_NewStringObj (cfg->key, -1),
- Tcl_NewStringObj (convValue, -1));
- Tcl_DStringFree (&conv);
+ for (cfg=configuration ; cfg->key!=NULL && cfg->key[0]!='\0' ; cfg++) {
+ Tcl_DictObjPut(interp, pkgDict, Tcl_NewStringObj(cfg->key, -1),
+ Tcl_NewByteArrayObj((unsigned char *)cfg->value, strlen(cfg->value)));
}
- /* Write the changes back into the overall database */
+ /*
+ * Write the changes back into the overall database.
+ */
- Tcl_DictObjPut (interp, pDB, pkg, pkgDict);
+ Tcl_DictObjPut(interp, pDB, cdPtr->pkg, pkgDict);
/*
* Now create the interface command for retrieval of the package
* information.
*/
- Tcl_DStringInit (&cmdName);
- Tcl_DStringAppend (&cmdName, "::", -1);
- Tcl_DStringAppend (&cmdName, pkgName, -1);
+ Tcl_DStringInit(&cmdName);
+ TclDStringAppendLiteral(&cmdName, "::");
+ Tcl_DStringAppend(&cmdName, pkgName, -1);
- /* The incomplete command name is the name of the namespace to
- * place it in.
+ /*
+ * The incomplete command name is the name of the namespace to place it
+ * in.
*/
- if ((Tcl_Namespace*) NULL == Tcl_FindNamespace(interp,
- Tcl_DStringValue (&cmdName), NULL, TCL_GLOBAL_ONLY)) {
-
- if ((Tcl_Namespace*) NULL == Tcl_CreateNamespace (interp,
- Tcl_DStringValue (&cmdName), (ClientData) NULL,
- (Tcl_NamespaceDeleteProc *) NULL)) {
-
- Tcl_Panic ("%s.\n%s %s", Tcl_GetStringResult(interp),
- "Tcl_RegisterConfig: Unable to create namespace for",
- "package configuration.");
+ if (Tcl_FindNamespace(interp, Tcl_DStringValue(&cmdName), NULL,
+ TCL_GLOBAL_ONLY) == NULL) {
+ if (Tcl_CreateNamespace(interp, Tcl_DStringValue(&cmdName),
+ NULL, NULL) == NULL) {
+ Tcl_Panic("%s.\n%s: %s",
+ Tcl_GetStringResult(interp), "Tcl_RegisterConfig",
+ "Unable to create namespace for package configuration.");
}
}
- Tcl_DStringAppend (&cmdName, "::pkgconfig", -1);
-
- if ((Tcl_Command) NULL == Tcl_CreateObjCommand (interp,
- Tcl_DStringValue (&cmdName), QueryConfigObjCmd,
- (ClientData) pkg, QueryConfigDelete)) {
+ TclDStringAppendLiteral(&cmdName, "::pkgconfig");
- Tcl_Panic ("%s %s", "Tcl_RegisterConfig: Unable to create query",
- "command for package configuration");
+ if (Tcl_CreateObjCommand(interp, Tcl_DStringValue(&cmdName),
+ QueryConfigObjCmd, cdPtr, QueryConfigDelete) == NULL) {
+ Tcl_Panic("%s: %s", "Tcl_RegisterConfig",
+ "Unable to create query command for package configuration");
}
- Tcl_DStringFree (&cmdName);
+ Tcl_DStringFree(&cmdName);
}
/*
@@ -176,8 +180,8 @@ Tcl_RegisterConfig (interp, pkgName, configuration, valEncoding)
*
* QueryConfigObjCmd --
*
- * Implementation of "::<package>::pkgconfig", the command to
- * query configuration information embedded into a binary library.
+ * Implementation of "::<package>::pkgconfig", the command to query
+ * configuration information embedded into a binary library.
*
* Results:
* A standard tcl result.
@@ -189,78 +193,109 @@ Tcl_RegisterConfig (interp, pkgName, configuration, valEncoding)
*/
static int
-QueryConfigObjCmd(clientData, interp, objc, objv)
- ClientData clientData;
- Tcl_Interp *interp;
- int objc;
- struct Tcl_Obj * CONST *objv;
+QueryConfigObjCmd(
+ ClientData clientData,
+ Tcl_Interp *interp,
+ int objc,
+ struct Tcl_Obj *const *objv)
{
- Tcl_Obj *pkgName = (Tcl_Obj*) clientData;
- Tcl_Obj *pDB, *pkgDict, *val;
- Tcl_DictSearch s;
- int n, i, res, done, index;
- Tcl_Obj *key, **vals;
-
- static CONST char *subcmdStrings[] = {
+ QCCD *cdPtr = clientData;
+ Tcl_Obj *pkgName = cdPtr->pkg;
+ Tcl_Obj *pDB, *pkgDict, *val, *listPtr;
+ int n, index;
+ static const char *const subcmdStrings[] = {
"get", "list", NULL
};
enum subcmds {
CFG_GET, CFG_LIST
};
+ Tcl_DString conv;
+ Tcl_Encoding venc = NULL;
+ const char *value;
if ((objc < 2) || (objc > 3)) {
- Tcl_WrongNumArgs (interp, 0, NULL, "list | get key");
+ Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?arg?");
return TCL_ERROR;
}
- if (Tcl_GetIndexFromObj(interp, objv[1], subcmdStrings,
- "subcommand", 0, &index) != TCL_OK) {
+ if (Tcl_GetIndexFromObj(interp, objv[1], subcmdStrings, "subcommand", 0,
+ &index) != TCL_OK) {
return TCL_ERROR;
}
pDB = GetConfigDict(interp);
- res = Tcl_DictObjGet(interp, pDB, pkgName, &pkgDict);
- if (res!=TCL_OK || pkgDict==NULL) {
- /* Maybe a Tcl_Panic is better, because the package data has to be present */
- Tcl_SetObjResult(interp, Tcl_NewStringObj("package not known", -1));
+ if (Tcl_DictObjGet(interp, pDB, pkgName, &pkgDict) != TCL_OK
+ || pkgDict == NULL) {
+ /*
+ * Maybe a Tcl_Panic is better, because the package data has to be
+ * present.
+ */
+
+ Tcl_SetObjResult(interp, Tcl_NewStringObj("package not known", -1));
+ Tcl_SetErrorCode(interp, "TCL", "FATAL", "PKGCFG_BASE",
+ Tcl_GetString(pkgName), NULL);
return TCL_ERROR;
}
switch ((enum subcmds) index) {
case CFG_GET:
if (objc != 3) {
- Tcl_WrongNumArgs(interp, 0, NULL, "get key");
+ Tcl_WrongNumArgs(interp, 2, objv, "key");
return TCL_ERROR;
}
- res = Tcl_DictObjGet(interp, pkgDict, objv [2], &val);
- if (res!=TCL_OK || val==NULL) {
+ if (Tcl_DictObjGet(interp, pkgDict, objv[2], &val) != TCL_OK
+ || val == NULL) {
Tcl_SetObjResult(interp, Tcl_NewStringObj("key not known", -1));
+ Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "CONFIG",
+ Tcl_GetString(objv[2]), NULL);
return TCL_ERROR;
}
- Tcl_SetObjResult(interp, val);
+ if (cdPtr->encoding) {
+ venc = Tcl_GetEncoding(interp, cdPtr->encoding);
+ if (!venc) {
+ return TCL_ERROR;
+ }
+ }
+ /*
+ * Value is stored as-is in a byte array, see Bug [9b2e636361],
+ * so we have to decode it first.
+ */
+ value = (const char *) Tcl_GetByteArrayFromObj(val, &n);
+ value = Tcl_ExternalToUtfDString(venc, value, n, &conv);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(value,
+ Tcl_DStringLength(&conv)));
+ Tcl_DStringFree(&conv);
return TCL_OK;
case CFG_LIST:
if (objc != 2) {
- Tcl_WrongNumArgs(interp, 0, NULL, "list");
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
return TCL_ERROR;
}
Tcl_DictObjSize(interp, pkgDict, &n);
- if (n == 0) {
- Tcl_SetObjResult(interp, Tcl_NewListObj(0, NULL));
- return TCL_OK;
+ listPtr = Tcl_NewListObj(n, NULL);
+
+ if (!listPtr) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "insufficient memory to create list", -1));
+ Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL);
+ return TCL_ERROR;
}
- vals = (Tcl_Obj**) ckalloc(n * sizeof(Tcl_Obj*));
+ if (n) {
+ Tcl_DictSearch s;
+ Tcl_Obj *key;
+ int done;
- for (i=0, Tcl_DictObjFirst(interp, pkgDict, &s, &key, NULL, &done);
- !done; Tcl_DictObjNext(&s, &key, NULL, &done), i++) {
- vals[i] = key;
+ for (Tcl_DictObjFirst(interp, pkgDict, &s, &key, NULL, &done);
+ !done; Tcl_DictObjNext(&s, &key, NULL, &done)) {
+ Tcl_ListObjAppendElement(NULL, listPtr, key);
+ }
}
- Tcl_SetObjResult(interp, TclNewListObjDirect(n, vals));
+ Tcl_SetObjResult(interp, listPtr);
return TCL_OK;
default:
@@ -275,7 +310,7 @@ QueryConfigObjCmd(clientData, interp, objc, objv)
*
* QueryConfigDelete --
*
- * Command delete procedure. Cleans up after the configuration query
+ * Command delete function. Cleans up after the configuration query
* command when it is deleted by the user or during finalization.
*
* Results:
@@ -288,11 +323,19 @@ QueryConfigObjCmd(clientData, interp, objc, objv)
*/
static void
-QueryConfigDelete (clientData)
- ClientData clientData;
+QueryConfigDelete(
+ ClientData clientData)
{
- Tcl_Obj* pkgName = (Tcl_Obj*) clientData;
- Tcl_DecrRefCount (pkgName);
+ QCCD *cdPtr = clientData;
+ Tcl_Obj *pkgName = cdPtr->pkg;
+ Tcl_Obj *pDB = GetConfigDict(cdPtr->interp);
+
+ Tcl_DictObjRemove(NULL, pDB, pkgName);
+ Tcl_DecrRefCount(pkgName);
+ if (cdPtr->encoding) {
+ ckfree((char *)cdPtr->encoding);
+ }
+ ckfree((char *)cdPtr);
}
/*
@@ -312,19 +355,19 @@ QueryConfigDelete (clientData)
*-------------------------------------------------------------------------
*/
-static Tcl_Obj*
-GetConfigDict (interp)
- Tcl_Interp* interp;
+static Tcl_Obj *
+GetConfigDict(
+ Tcl_Interp *interp)
{
- Tcl_Obj* pDB = Tcl_GetAssocData (interp, ASSOC_KEY, NULL);
+ Tcl_Obj *pDB = Tcl_GetAssocData(interp, ASSOC_KEY, NULL);
- if (pDB == (Tcl_Obj*) NULL) {
- pDB = Tcl_NewDictObj ();
- Tcl_IncrRefCount (pDB);
- Tcl_SetAssocData (interp, ASSOC_KEY, ConfigDictDeleteProc, pDB);
- }
+ if (pDB == NULL) {
+ pDB = Tcl_NewDictObj();
+ Tcl_IncrRefCount(pDB);
+ Tcl_SetAssocData(interp, ASSOC_KEY, ConfigDictDeleteProc, pDB);
+ }
- return pDB;
+ return pDB;
}
/*
@@ -332,10 +375,10 @@ GetConfigDict (interp)
*
* ConfigDictDeleteProc --
*
- * This procedure is associated with the "Package About dict" assoc data
- * for an interpreter; it is invoked when the interpreter is
- * deleted in order to free the information assoicated with any
- * pending error reports.
+ * This function is associated with the "Package About dict" assoc data
+ * for an interpreter; it is invoked when the interpreter is deleted in
+ * order to free the information associated with any pending error
+ * reports.
*
* Results:
* None.
@@ -347,10 +390,19 @@ GetConfigDict (interp)
*/
static void
-ConfigDictDeleteProc(clientData, interp)
- ClientData clientData; /* Pointer to Tcl_Obj. */
- Tcl_Interp *interp; /* Interpreter being deleted. */
+ConfigDictDeleteProc(
+ ClientData clientData, /* Pointer to Tcl_Obj. */
+ Tcl_Interp *interp) /* Interpreter being deleted. */
{
- Tcl_Obj* pDB = (Tcl_Obj*) clientData;
- Tcl_DecrRefCount (pDB);
+ Tcl_Obj *pDB = clientData;
+
+ Tcl_DecrRefCount(pDB);
}
+
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */