Overview
Artifact ID: | d2a4556d7f2e8158c2742299da4253c6b9f218d6 |
---|---|
Ticket: | 507083ffffffffffffffffffffffffffffffffff |
Date: | 2002-01-25 00:33:32 |
User: | andreas_kupries |
Artifact Attached: | 059483312fc0190a9ec7e1b7fca9dd926ff04377 |
Filename: | tclConfig.c |
Description: | tclConfig.c, generic embedding support, Version 2 |
Content Appended
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287/* * tclConfig.c -- * * This file provides the facilities which allow Tcl and other packages * to embed configuration information into their binary libraries. * * Copyright (c) 2002 Andreas Kupries <[email protected]> * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * RCS: @(#) $Id: tclIO.c,v 1.47 2002/01/17 04:37:33 dgp Exp $ */ #include "tclInt.h" /* * Internal structure to hold additional information about the * embedded configuration. Allocated on the heap during construction * of the configuration command. Allocated big enough to hold * references to as many strings as there are entries in the * configuration itself. These references will refer to the * configuration values, converted into UTF 8. This conversion is done * on demand. */ typedef struct Tcl_ConfigMeta { Tcl_Config* configuration; /* Reference to the embedded * configuration. */ Tcl_Encoding valEncoding; /* Token for the encoding used to * represent the values in the * configuration. */ Tcl_Obj* keylist; /* List of the registered keys */ int entries; /* Number of entries in * configuration. */ Tcl_Obj* value [1]; /* Array of the values converted to * UTF-8 */ } Tcl_ConfigMeta; /* * Static functions in this file: */ static int QueryConfigObjCmd _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, int objc, struct Tcl_Obj * CONST * objv)); static void QueryConfigDelete _ANSI_ARGS_((ClientData clientData)); /* *--------------------------------------------------------------------------- * * Tcl_RegisterConfig -- * * See TIP#59 for details on what this procedure does. * * Results: * None. * * Side effects: * Creates namespace and cfg query command in it as per TIP #59. * *--------------------------------------------------------------------------- */ void Tcl_RegisterConfig (interp, pkgName, configuration, valEncoding) Tcl_Interp* interp; /* interp 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 */ { /* Actions: * - Count the entries in the configuration, * - Allocate a big enough wrapper (meta) and initialize it * - Create the configuration query command and use the wrapper * as its client data. */ int n, i; Tcl_Config* cfg; Tcl_ConfigMeta* wrap; Tcl_DString cmdName; for (n = 0, cfg = configuration; cfg->key != (CONST char*) NULL; n++, cfg++) /* empty loop */ ; wrap = (Tcl_ConfigMeta*) Tcl_Alloc (sizeof (Tcl_ConfigMeta) + (sizeof (char*) * n)); wrap->configuration = configuration; wrap->entries = n; wrap->valEncoding = Tcl_GetEncoding (NULL, valEncoding); wrap->keylist = (Tcl_Obj*) NULL; for (i = 0; i < n; i++) { wrap->value [i] = (Tcl_Obj*) NULL; } Tcl_DStringInit (&cmdName); Tcl_DStringAppend (&cmdName, "::", -1); Tcl_DStringAppend (&cmdName, pkgName, -1); /* The incomplete command name is the name of the namespace to * place it in */ if ((Tcl_Namespace*) NULL == Tcl_CreateNamespace (interp, Tcl_DStringValue (&cmdName), (ClientData) NULL, (Tcl_NamespaceDeleteProc *) NULL)) { Tcl_Panic ("Unable to create namespace for package configuration"); } Tcl_DStringAppend (&cmdName, "::pkgconfig", -1); if ((Tcl_Command) NULL == Tcl_CreateObjCommand (interp, Tcl_DStringValue (&cmdName), QueryConfigObjCmd, (ClientData) wrap, QueryConfigDelete)) { Tcl_Panic ("Unable to create query command for package configuration"); } Tcl_DStringFree (&cmdName); } /* *------------------------------------------------------------------------- * * QueryConfigObjCmd -- * * Implementation of "::<package>::pkgconfig", the command to * query configuration information embedded into a binary library. * * Results: * A standard tcl result. * * Side effects: * See the manual for what this command does. * *------------------------------------------------------------------------- */ static int QueryConfigObjCmd (clientData, interp, objc, objv) ClientData clientData; Tcl_Interp *interp; int objc; struct Tcl_Obj * CONST * objv; { Tcl_ConfigMeta* wrap = (Tcl_ConfigMeta*) clientData; int index, i; static CONST char *subcmdStrings[] = { "get", "list", NULL }; enum subcmds { CFG_GET, CFG_LIST }; if ((objc < 2) || (objc > 3)) { Tcl_WrongNumArgs (interp, objc-1, objv+1, "list | get key"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[1], subcmdStrings, "subcommand", 0, &index) != TCL_OK) { return TCL_ERROR; } switch ((enum subcmds) index) { case CFG_GET: if (objc != 3) { Tcl_WrongNumArgs (interp, objc-1, objv+1, "get key"); return TCL_ERROR; } for (i=0; i < wrap->entries; i++) { /* We can use 'strcmp' as we know that the keys are in * ASCII/UTF-8 */ if (strcmp (wrap->configuration [i].key, Tcl_GetString (objv [2])) == 0) { if (wrap->value [i] == (Tcl_Obj*) NULL) { /* Convert the value associated with a key to * UTF 8 on demand, i.e. only if requested at * all and cache the result as it will never * change. */ Tcl_DString conv; Tcl_Obj* s = Tcl_NewStringObj (Tcl_ExternalToUtfDString (wrap->valEncoding, wrap->configuration [i].value, -1, &conv), -1 ); Tcl_DStringFree (&conv); if (s == (Tcl_Obj*) NULL) { Tcl_SetObjResult (interp, Tcl_NewStringObj ("unable to convert value to utf-8", -1)); return TCL_ERROR; } Tcl_IncrRefCount (s); wrap->value [i] = s; } Tcl_SetObjResult (interp, wrap->value [i]); return TCL_OK; } } Tcl_SetObjResult (interp, Tcl_NewStringObj ("key not known", -1)); return TCL_ERROR; case CFG_LIST: if (objc != 2) { Tcl_WrongNumArgs (interp, objc-1, objv+1, "list"); return TCL_ERROR; } if (wrap->keylist == (Tcl_Obj*) NULL) { /* Generate the list of know keys on demand and cache * it as it will never change. */ int i; Tcl_Obj* l = Tcl_NewListObj (0, NULL); if (l == (Tcl_Obj*) NULL) { return TCL_ERROR; } for (i=0; i < wrap->entries; i++) { Tcl_Obj* s = Tcl_NewStringObj (wrap->configuration [i].key, -1); if (s == (Tcl_Obj*) NULL) { Tcl_DecrRefCount (l); return TCL_ERROR; } if (TCL_OK != Tcl_ListObjAppendElement (interp, l, s)) { Tcl_DecrRefCount (l); return TCL_ERROR; } } Tcl_IncrRefCount (l); wrap->keylist = l; } Tcl_SetObjResult (interp, wrap->keylist); return TCL_OK; default: Tcl_Panic ("This can't happen"); break; } return TCL_ERROR; } /* *------------------------------------------------------------------------- * * QueryConfigDelete -- * * Command delete procedure. Cleans up after the configuration query * command when it is deleted by the user or during finalization. * * Results: * None. * * Side effects: * Deallocates all non-transient memory allocated by Tcl_RegisterConfig. * *------------------------------------------------------------------------- */ static void QueryConfigDelete (clientData) ClientData clientData; { Tcl_ConfigMeta* wrap = (Tcl_ConfigMeta*) clientData; int i; for (i = 0; i < wrap->entries; i++) { if (wrap->value[i] != (Tcl_Obj*) NULL) { Tcl_DecrRefCount (wrap->value [i]); } } if (wrap->keylist != (Tcl_Obj*) NULL) { Tcl_DecrRefCount (wrap->keylist); } Tcl_FreeEncoding (wrap->valEncoding); Tcl_Free ((char*) wrap); }