[25] | 1 | /* |
---|
| 2 | * tclLink.c -- |
---|
| 3 | * |
---|
| 4 | * This file implements linked variables (a C variable that is tied to a |
---|
| 5 | * Tcl variable). The idea of linked variables was first suggested by |
---|
| 6 | * Andreas Stolcke and this implementation is based heavily on a |
---|
| 7 | * prototype implementation provided by him. |
---|
| 8 | * |
---|
| 9 | * Copyright (c) 1993 The Regents of the University of California. |
---|
| 10 | * Copyright (c) 1994-1997 Sun Microsystems, Inc. |
---|
| 11 | * |
---|
| 12 | * See the file "license.terms" for information on usage and redistribution of |
---|
| 13 | * this file, and for a DISCLAIMER OF ALL WARRANTIES. |
---|
| 14 | * |
---|
| 15 | * RCS: @(#) $Id: tclLink.c,v 1.24 2007/12/13 15:23:18 dgp Exp $ |
---|
| 16 | */ |
---|
| 17 | |
---|
| 18 | #include "tclInt.h" |
---|
| 19 | |
---|
| 20 | /* |
---|
| 21 | * For each linked variable there is a data structure of the following type, |
---|
| 22 | * which describes the link and is the clientData for the trace set on the Tcl |
---|
| 23 | * variable. |
---|
| 24 | */ |
---|
| 25 | |
---|
| 26 | typedef struct Link { |
---|
| 27 | Tcl_Interp *interp; /* Interpreter containing Tcl variable. */ |
---|
| 28 | Tcl_Obj *varName; /* Name of variable (must be global). This is |
---|
| 29 | * needed during trace callbacks, since the |
---|
| 30 | * actual variable may be aliased at that time |
---|
| 31 | * via upvar. */ |
---|
| 32 | char *addr; /* Location of C variable. */ |
---|
| 33 | int type; /* Type of link (TCL_LINK_INT, etc.). */ |
---|
| 34 | union { |
---|
| 35 | char c; |
---|
| 36 | unsigned char uc; |
---|
| 37 | int i; |
---|
| 38 | unsigned int ui; |
---|
| 39 | short s; |
---|
| 40 | unsigned short us; |
---|
| 41 | long l; |
---|
| 42 | unsigned long ul; |
---|
| 43 | Tcl_WideInt w; |
---|
| 44 | Tcl_WideUInt uw; |
---|
| 45 | float f; |
---|
| 46 | double d; |
---|
| 47 | } lastValue; /* Last known value of C variable; used to |
---|
| 48 | * avoid string conversions. */ |
---|
| 49 | int flags; /* Miscellaneous one-bit values; see below for |
---|
| 50 | * definitions. */ |
---|
| 51 | } Link; |
---|
| 52 | |
---|
| 53 | /* |
---|
| 54 | * Definitions for flag bits: |
---|
| 55 | * LINK_READ_ONLY - 1 means errors should be generated if Tcl |
---|
| 56 | * script attempts to write variable. |
---|
| 57 | * LINK_BEING_UPDATED - 1 means that a call to Tcl_UpdateLinkedVar is |
---|
| 58 | * in progress for this variable, so trace |
---|
| 59 | * callbacks on the variable should be ignored. |
---|
| 60 | */ |
---|
| 61 | |
---|
| 62 | #define LINK_READ_ONLY 1 |
---|
| 63 | #define LINK_BEING_UPDATED 2 |
---|
| 64 | |
---|
| 65 | /* |
---|
| 66 | * Forward references to functions defined later in this file: |
---|
| 67 | */ |
---|
| 68 | |
---|
| 69 | static char * LinkTraceProc(ClientData clientData,Tcl_Interp *interp, |
---|
| 70 | CONST char *name1, CONST char *name2, int flags); |
---|
| 71 | static Tcl_Obj * ObjValue(Link *linkPtr); |
---|
| 72 | |
---|
| 73 | /* |
---|
| 74 | * Convenience macro for accessing the value of the C variable pointed to by a |
---|
| 75 | * link. Note that this macro produces something that may be regarded as an |
---|
| 76 | * lvalue or rvalue; it may be assigned to as well as read. Also note that |
---|
| 77 | * this macro assumes the name of the variable being accessed (linkPtr); this |
---|
| 78 | * is not strictly a good thing, but it keeps the code much shorter and |
---|
| 79 | * cleaner. |
---|
| 80 | */ |
---|
| 81 | |
---|
| 82 | #define LinkedVar(type) (*(type *) linkPtr->addr) |
---|
| 83 | |
---|
| 84 | /* |
---|
| 85 | *---------------------------------------------------------------------- |
---|
| 86 | * |
---|
| 87 | * Tcl_LinkVar -- |
---|
| 88 | * |
---|
| 89 | * Link a C variable to a Tcl variable so that changes to either one |
---|
| 90 | * causes the other to change. |
---|
| 91 | * |
---|
| 92 | * Results: |
---|
| 93 | * The return value is TCL_OK if everything went well or TCL_ERROR if an |
---|
| 94 | * error occurred (the interp's result is also set after errors). |
---|
| 95 | * |
---|
| 96 | * Side effects: |
---|
| 97 | * The value at *addr is linked to the Tcl variable "varName", using |
---|
| 98 | * "type" to convert between string values for Tcl and binary values for |
---|
| 99 | * *addr. |
---|
| 100 | * |
---|
| 101 | *---------------------------------------------------------------------- |
---|
| 102 | */ |
---|
| 103 | |
---|
| 104 | int |
---|
| 105 | Tcl_LinkVar( |
---|
| 106 | Tcl_Interp *interp, /* Interpreter in which varName exists. */ |
---|
| 107 | CONST char *varName, /* Name of a global variable in interp. */ |
---|
| 108 | char *addr, /* Address of a C variable to be linked to |
---|
| 109 | * varName. */ |
---|
| 110 | int type) /* Type of C variable: TCL_LINK_INT, etc. Also |
---|
| 111 | * may have TCL_LINK_READ_ONLY OR'ed in. */ |
---|
| 112 | { |
---|
| 113 | Tcl_Obj *objPtr; |
---|
| 114 | Link *linkPtr; |
---|
| 115 | int code; |
---|
| 116 | |
---|
| 117 | linkPtr = (Link *) ckalloc(sizeof(Link)); |
---|
| 118 | linkPtr->interp = interp; |
---|
| 119 | linkPtr->varName = Tcl_NewStringObj(varName, -1); |
---|
| 120 | Tcl_IncrRefCount(linkPtr->varName); |
---|
| 121 | linkPtr->addr = addr; |
---|
| 122 | linkPtr->type = type & ~TCL_LINK_READ_ONLY; |
---|
| 123 | if (type & TCL_LINK_READ_ONLY) { |
---|
| 124 | linkPtr->flags = LINK_READ_ONLY; |
---|
| 125 | } else { |
---|
| 126 | linkPtr->flags = 0; |
---|
| 127 | } |
---|
| 128 | objPtr = ObjValue(linkPtr); |
---|
| 129 | if (Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, objPtr, |
---|
| 130 | TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) { |
---|
| 131 | Tcl_DecrRefCount(linkPtr->varName); |
---|
| 132 | ckfree((char *) linkPtr); |
---|
| 133 | return TCL_ERROR; |
---|
| 134 | } |
---|
| 135 | code = Tcl_TraceVar(interp, varName, TCL_GLOBAL_ONLY|TCL_TRACE_READS |
---|
| 136 | |TCL_TRACE_WRITES|TCL_TRACE_UNSETS, LinkTraceProc, |
---|
| 137 | (ClientData) linkPtr); |
---|
| 138 | if (code != TCL_OK) { |
---|
| 139 | Tcl_DecrRefCount(linkPtr->varName); |
---|
| 140 | ckfree((char *) linkPtr); |
---|
| 141 | } |
---|
| 142 | return code; |
---|
| 143 | } |
---|
| 144 | |
---|
| 145 | /* |
---|
| 146 | *---------------------------------------------------------------------- |
---|
| 147 | * |
---|
| 148 | * Tcl_UnlinkVar -- |
---|
| 149 | * |
---|
| 150 | * Destroy the link between a Tcl variable and a C variable. |
---|
| 151 | * |
---|
| 152 | * Results: |
---|
| 153 | * None. |
---|
| 154 | * |
---|
| 155 | * Side effects: |
---|
| 156 | * If "varName" was previously linked to a C variable, the link is broken |
---|
| 157 | * to make the variable independent. If there was no previous link for |
---|
| 158 | * "varName" then nothing happens. |
---|
| 159 | * |
---|
| 160 | *---------------------------------------------------------------------- |
---|
| 161 | */ |
---|
| 162 | |
---|
| 163 | void |
---|
| 164 | Tcl_UnlinkVar( |
---|
| 165 | Tcl_Interp *interp, /* Interpreter containing variable to unlink */ |
---|
| 166 | CONST char *varName) /* Global variable in interp to unlink. */ |
---|
| 167 | { |
---|
| 168 | Link *linkPtr; |
---|
| 169 | |
---|
| 170 | linkPtr = (Link *) Tcl_VarTraceInfo(interp, varName, TCL_GLOBAL_ONLY, |
---|
| 171 | LinkTraceProc, (ClientData) NULL); |
---|
| 172 | if (linkPtr == NULL) { |
---|
| 173 | return; |
---|
| 174 | } |
---|
| 175 | Tcl_UntraceVar(interp, varName, |
---|
| 176 | TCL_GLOBAL_ONLY|TCL_TRACE_READS|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, |
---|
| 177 | LinkTraceProc, (ClientData) linkPtr); |
---|
| 178 | Tcl_DecrRefCount(linkPtr->varName); |
---|
| 179 | ckfree((char *) linkPtr); |
---|
| 180 | } |
---|
| 181 | |
---|
| 182 | /* |
---|
| 183 | *---------------------------------------------------------------------- |
---|
| 184 | * |
---|
| 185 | * Tcl_UpdateLinkedVar -- |
---|
| 186 | * |
---|
| 187 | * This function is invoked after a linked variable has been changed by C |
---|
| 188 | * code. It updates the Tcl variable so that traces on the variable will |
---|
| 189 | * trigger. |
---|
| 190 | * |
---|
| 191 | * Results: |
---|
| 192 | * None. |
---|
| 193 | * |
---|
| 194 | * Side effects: |
---|
| 195 | * The Tcl variable "varName" is updated from its C value, causing traces |
---|
| 196 | * on the variable to trigger. |
---|
| 197 | * |
---|
| 198 | *---------------------------------------------------------------------- |
---|
| 199 | */ |
---|
| 200 | |
---|
| 201 | void |
---|
| 202 | Tcl_UpdateLinkedVar( |
---|
| 203 | Tcl_Interp *interp, /* Interpreter containing variable. */ |
---|
| 204 | CONST char *varName) /* Name of global variable that is linked. */ |
---|
| 205 | { |
---|
| 206 | Link *linkPtr; |
---|
| 207 | int savedFlag; |
---|
| 208 | |
---|
| 209 | linkPtr = (Link *) Tcl_VarTraceInfo(interp, varName, TCL_GLOBAL_ONLY, |
---|
| 210 | LinkTraceProc, (ClientData) NULL); |
---|
| 211 | if (linkPtr == NULL) { |
---|
| 212 | return; |
---|
| 213 | } |
---|
| 214 | savedFlag = linkPtr->flags & LINK_BEING_UPDATED; |
---|
| 215 | linkPtr->flags |= LINK_BEING_UPDATED; |
---|
| 216 | Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr), |
---|
| 217 | TCL_GLOBAL_ONLY); |
---|
| 218 | /* |
---|
| 219 | * Callback may have unlinked the variable. [Bug 1740631] |
---|
| 220 | */ |
---|
| 221 | linkPtr = (Link *) Tcl_VarTraceInfo(interp, varName, TCL_GLOBAL_ONLY, |
---|
| 222 | LinkTraceProc, (ClientData) NULL); |
---|
| 223 | if (linkPtr != NULL) { |
---|
| 224 | linkPtr->flags = (linkPtr->flags & ~LINK_BEING_UPDATED) | savedFlag; |
---|
| 225 | } |
---|
| 226 | } |
---|
| 227 | |
---|
| 228 | /* |
---|
| 229 | *---------------------------------------------------------------------- |
---|
| 230 | * |
---|
| 231 | * LinkTraceProc -- |
---|
| 232 | * |
---|
| 233 | * This function is invoked when a linked Tcl variable is read, written, |
---|
| 234 | * or unset from Tcl. It's responsible for keeping the C variable in sync |
---|
| 235 | * with the Tcl variable. |
---|
| 236 | * |
---|
| 237 | * Results: |
---|
| 238 | * If all goes well, NULL is returned; otherwise an error message is |
---|
| 239 | * returned. |
---|
| 240 | * |
---|
| 241 | * Side effects: |
---|
| 242 | * The C variable may be updated to make it consistent with the Tcl |
---|
| 243 | * variable, or the Tcl variable may be overwritten to reject a |
---|
| 244 | * modification. |
---|
| 245 | * |
---|
| 246 | *---------------------------------------------------------------------- |
---|
| 247 | */ |
---|
| 248 | |
---|
| 249 | static char * |
---|
| 250 | LinkTraceProc( |
---|
| 251 | ClientData clientData, /* Contains information about the link. */ |
---|
| 252 | Tcl_Interp *interp, /* Interpreter containing Tcl variable. */ |
---|
| 253 | CONST char *name1, /* First part of variable name. */ |
---|
| 254 | CONST char *name2, /* Second part of variable name. */ |
---|
| 255 | int flags) /* Miscellaneous additional information. */ |
---|
| 256 | { |
---|
| 257 | Link *linkPtr = (Link *) clientData; |
---|
| 258 | int changed, valueLength; |
---|
| 259 | CONST char *value; |
---|
| 260 | char **pp; |
---|
| 261 | Tcl_Obj *valueObj; |
---|
| 262 | int valueInt; |
---|
| 263 | Tcl_WideInt valueWide; |
---|
| 264 | double valueDouble; |
---|
| 265 | |
---|
| 266 | /* |
---|
| 267 | * If the variable is being unset, then just re-create it (with a trace) |
---|
| 268 | * unless the whole interpreter is going away. |
---|
| 269 | */ |
---|
| 270 | |
---|
| 271 | if (flags & TCL_TRACE_UNSETS) { |
---|
| 272 | if (Tcl_InterpDeleted(interp)) { |
---|
| 273 | Tcl_DecrRefCount(linkPtr->varName); |
---|
| 274 | ckfree((char *) linkPtr); |
---|
| 275 | } else if (flags & TCL_TRACE_DESTROYED) { |
---|
| 276 | Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr), |
---|
| 277 | TCL_GLOBAL_ONLY); |
---|
| 278 | Tcl_TraceVar(interp, Tcl_GetString(linkPtr->varName), |
---|
| 279 | TCL_GLOBAL_ONLY|TCL_TRACE_READS|TCL_TRACE_WRITES |
---|
| 280 | |TCL_TRACE_UNSETS, LinkTraceProc, (ClientData) linkPtr); |
---|
| 281 | } |
---|
| 282 | return NULL; |
---|
| 283 | } |
---|
| 284 | |
---|
| 285 | /* |
---|
| 286 | * If we were invoked because of a call to Tcl_UpdateLinkedVar, then don't |
---|
| 287 | * do anything at all. In particular, we don't want to get upset that the |
---|
| 288 | * variable is being modified, even if it is supposed to be read-only. |
---|
| 289 | */ |
---|
| 290 | |
---|
| 291 | if (linkPtr->flags & LINK_BEING_UPDATED) { |
---|
| 292 | return NULL; |
---|
| 293 | } |
---|
| 294 | |
---|
| 295 | /* |
---|
| 296 | * For read accesses, update the Tcl variable if the C variable has |
---|
| 297 | * changed since the last time we updated the Tcl variable. |
---|
| 298 | */ |
---|
| 299 | |
---|
| 300 | if (flags & TCL_TRACE_READS) { |
---|
| 301 | switch (linkPtr->type) { |
---|
| 302 | case TCL_LINK_INT: |
---|
| 303 | case TCL_LINK_BOOLEAN: |
---|
| 304 | changed = (LinkedVar(int) != linkPtr->lastValue.i); |
---|
| 305 | break; |
---|
| 306 | case TCL_LINK_DOUBLE: |
---|
| 307 | changed = (LinkedVar(double) != linkPtr->lastValue.d); |
---|
| 308 | break; |
---|
| 309 | case TCL_LINK_WIDE_INT: |
---|
| 310 | changed = (LinkedVar(Tcl_WideInt) != linkPtr->lastValue.w); |
---|
| 311 | break; |
---|
| 312 | case TCL_LINK_WIDE_UINT: |
---|
| 313 | changed = (LinkedVar(Tcl_WideUInt) != linkPtr->lastValue.uw); |
---|
| 314 | break; |
---|
| 315 | case TCL_LINK_CHAR: |
---|
| 316 | changed = (LinkedVar(char) != linkPtr->lastValue.c); |
---|
| 317 | break; |
---|
| 318 | case TCL_LINK_UCHAR: |
---|
| 319 | changed = (LinkedVar(unsigned char) != linkPtr->lastValue.uc); |
---|
| 320 | break; |
---|
| 321 | case TCL_LINK_SHORT: |
---|
| 322 | changed = (LinkedVar(short) != linkPtr->lastValue.s); |
---|
| 323 | break; |
---|
| 324 | case TCL_LINK_USHORT: |
---|
| 325 | changed = (LinkedVar(unsigned short) != linkPtr->lastValue.us); |
---|
| 326 | break; |
---|
| 327 | case TCL_LINK_UINT: |
---|
| 328 | changed = (LinkedVar(unsigned int) != linkPtr->lastValue.ui); |
---|
| 329 | break; |
---|
| 330 | case TCL_LINK_LONG: |
---|
| 331 | changed = (LinkedVar(long) != linkPtr->lastValue.l); |
---|
| 332 | break; |
---|
| 333 | case TCL_LINK_ULONG: |
---|
| 334 | changed = (LinkedVar(unsigned long) != linkPtr->lastValue.ul); |
---|
| 335 | break; |
---|
| 336 | case TCL_LINK_FLOAT: |
---|
| 337 | changed = (LinkedVar(float) != linkPtr->lastValue.f); |
---|
| 338 | break; |
---|
| 339 | case TCL_LINK_STRING: |
---|
| 340 | changed = 1; |
---|
| 341 | break; |
---|
| 342 | default: |
---|
| 343 | return "internal error: bad linked variable type"; |
---|
| 344 | } |
---|
| 345 | if (changed) { |
---|
| 346 | Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr), |
---|
| 347 | TCL_GLOBAL_ONLY); |
---|
| 348 | } |
---|
| 349 | return NULL; |
---|
| 350 | } |
---|
| 351 | |
---|
| 352 | /* |
---|
| 353 | * For writes, first make sure that the variable is writable. Then convert |
---|
| 354 | * the Tcl value to C if possible. If the variable isn't writable or can't |
---|
| 355 | * be converted, then restore the varaible's old value and return an |
---|
| 356 | * error. Another tricky thing: we have to save and restore the interp's |
---|
| 357 | * result, since the variable access could occur when the result has been |
---|
| 358 | * partially set. |
---|
| 359 | */ |
---|
| 360 | |
---|
| 361 | if (linkPtr->flags & LINK_READ_ONLY) { |
---|
| 362 | Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr), |
---|
| 363 | TCL_GLOBAL_ONLY); |
---|
| 364 | return "linked variable is read-only"; |
---|
| 365 | } |
---|
| 366 | valueObj = Tcl_ObjGetVar2(interp, linkPtr->varName,NULL, TCL_GLOBAL_ONLY); |
---|
| 367 | if (valueObj == NULL) { |
---|
| 368 | /* |
---|
| 369 | * This shouldn't ever happen. |
---|
| 370 | */ |
---|
| 371 | |
---|
| 372 | return "internal error: linked variable couldn't be read"; |
---|
| 373 | } |
---|
| 374 | |
---|
| 375 | switch (linkPtr->type) { |
---|
| 376 | case TCL_LINK_INT: |
---|
| 377 | if (Tcl_GetIntFromObj(NULL, valueObj, &linkPtr->lastValue.i) |
---|
| 378 | != TCL_OK) { |
---|
| 379 | Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr), |
---|
| 380 | TCL_GLOBAL_ONLY); |
---|
| 381 | return "variable must have integer value"; |
---|
| 382 | } |
---|
| 383 | LinkedVar(int) = linkPtr->lastValue.i; |
---|
| 384 | break; |
---|
| 385 | |
---|
| 386 | case TCL_LINK_WIDE_INT: |
---|
| 387 | if (Tcl_GetWideIntFromObj(NULL, valueObj, &linkPtr->lastValue.w) |
---|
| 388 | != TCL_OK) { |
---|
| 389 | Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr), |
---|
| 390 | TCL_GLOBAL_ONLY); |
---|
| 391 | return "variable must have integer value"; |
---|
| 392 | } |
---|
| 393 | LinkedVar(Tcl_WideInt) = linkPtr->lastValue.w; |
---|
| 394 | break; |
---|
| 395 | |
---|
| 396 | case TCL_LINK_DOUBLE: |
---|
| 397 | if (Tcl_GetDoubleFromObj(NULL, valueObj, &linkPtr->lastValue.d) |
---|
| 398 | != TCL_OK) { |
---|
| 399 | #ifdef ACCEPT_NAN |
---|
| 400 | if (valueObj->typePtr != &tclDoubleType) { |
---|
| 401 | #endif |
---|
| 402 | Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, |
---|
| 403 | ObjValue(linkPtr), TCL_GLOBAL_ONLY); |
---|
| 404 | return "variable must have real value"; |
---|
| 405 | #ifdef ACCEPT_NAN |
---|
| 406 | } |
---|
| 407 | linkPtr->lastValue.d = valueObj->internalRep.doubleValue; |
---|
| 408 | #endif |
---|
| 409 | } |
---|
| 410 | LinkedVar(double) = linkPtr->lastValue.d; |
---|
| 411 | break; |
---|
| 412 | |
---|
| 413 | case TCL_LINK_BOOLEAN: |
---|
| 414 | if (Tcl_GetBooleanFromObj(NULL, valueObj, &linkPtr->lastValue.i) |
---|
| 415 | != TCL_OK) { |
---|
| 416 | Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr), |
---|
| 417 | TCL_GLOBAL_ONLY); |
---|
| 418 | return "variable must have boolean value"; |
---|
| 419 | } |
---|
| 420 | LinkedVar(int) = linkPtr->lastValue.i; |
---|
| 421 | break; |
---|
| 422 | |
---|
| 423 | case TCL_LINK_CHAR: |
---|
| 424 | if (Tcl_GetIntFromObj(interp, valueObj, &valueInt) != TCL_OK |
---|
| 425 | || valueInt < SCHAR_MIN || valueInt > SCHAR_MAX) { |
---|
| 426 | Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr), |
---|
| 427 | TCL_GLOBAL_ONLY); |
---|
| 428 | return "variable must have char value"; |
---|
| 429 | } |
---|
| 430 | linkPtr->lastValue.c = (char)valueInt; |
---|
| 431 | LinkedVar(char) = linkPtr->lastValue.c; |
---|
| 432 | break; |
---|
| 433 | |
---|
| 434 | case TCL_LINK_UCHAR: |
---|
| 435 | if (Tcl_GetIntFromObj(interp, valueObj, &valueInt) != TCL_OK |
---|
| 436 | || valueInt < 0 || valueInt > UCHAR_MAX) { |
---|
| 437 | Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr), |
---|
| 438 | TCL_GLOBAL_ONLY); |
---|
| 439 | return "variable must have unsigned char value"; |
---|
| 440 | } |
---|
| 441 | linkPtr->lastValue.uc = (unsigned char) valueInt; |
---|
| 442 | LinkedVar(unsigned char) = linkPtr->lastValue.uc; |
---|
| 443 | break; |
---|
| 444 | |
---|
| 445 | case TCL_LINK_SHORT: |
---|
| 446 | if (Tcl_GetIntFromObj(interp, valueObj, &valueInt) != TCL_OK |
---|
| 447 | || valueInt < SHRT_MIN || valueInt > SHRT_MAX) { |
---|
| 448 | Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr), |
---|
| 449 | TCL_GLOBAL_ONLY); |
---|
| 450 | return "variable must have short value"; |
---|
| 451 | } |
---|
| 452 | linkPtr->lastValue.s = (short)valueInt; |
---|
| 453 | LinkedVar(short) = linkPtr->lastValue.s; |
---|
| 454 | break; |
---|
| 455 | |
---|
| 456 | case TCL_LINK_USHORT: |
---|
| 457 | if (Tcl_GetIntFromObj(interp, valueObj, &valueInt) != TCL_OK |
---|
| 458 | || valueInt < 0 || valueInt > USHRT_MAX) { |
---|
| 459 | Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr), |
---|
| 460 | TCL_GLOBAL_ONLY); |
---|
| 461 | return "variable must have unsigned short value"; |
---|
| 462 | } |
---|
| 463 | linkPtr->lastValue.us = (unsigned short)valueInt; |
---|
| 464 | LinkedVar(unsigned short) = linkPtr->lastValue.us; |
---|
| 465 | break; |
---|
| 466 | |
---|
| 467 | case TCL_LINK_UINT: |
---|
| 468 | if (Tcl_GetWideIntFromObj(interp, valueObj, &valueWide) != TCL_OK |
---|
| 469 | || valueWide < 0 || valueWide > UINT_MAX) { |
---|
| 470 | Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr), |
---|
| 471 | TCL_GLOBAL_ONLY); |
---|
| 472 | return "variable must have unsigned int value"; |
---|
| 473 | } |
---|
| 474 | linkPtr->lastValue.ui = (unsigned int)valueWide; |
---|
| 475 | LinkedVar(unsigned int) = linkPtr->lastValue.ui; |
---|
| 476 | break; |
---|
| 477 | |
---|
| 478 | case TCL_LINK_LONG: |
---|
| 479 | if (Tcl_GetWideIntFromObj(interp, valueObj, &valueWide) != TCL_OK |
---|
| 480 | || valueWide < LONG_MIN || valueWide > LONG_MAX) { |
---|
| 481 | Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr), |
---|
| 482 | TCL_GLOBAL_ONLY); |
---|
| 483 | return "variable must have long value"; |
---|
| 484 | } |
---|
| 485 | linkPtr->lastValue.l = (long)valueWide; |
---|
| 486 | LinkedVar(long) = linkPtr->lastValue.l; |
---|
| 487 | break; |
---|
| 488 | |
---|
| 489 | case TCL_LINK_ULONG: |
---|
| 490 | if (Tcl_GetWideIntFromObj(interp, valueObj, &valueWide) != TCL_OK |
---|
| 491 | || valueWide < 0 || (Tcl_WideUInt) valueWide > ULONG_MAX) { |
---|
| 492 | Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr), |
---|
| 493 | TCL_GLOBAL_ONLY); |
---|
| 494 | return "variable must have unsigned long value"; |
---|
| 495 | } |
---|
| 496 | linkPtr->lastValue.ul = (unsigned long)valueWide; |
---|
| 497 | LinkedVar(unsigned long) = linkPtr->lastValue.ul; |
---|
| 498 | break; |
---|
| 499 | |
---|
| 500 | case TCL_LINK_WIDE_UINT: |
---|
| 501 | /* |
---|
| 502 | * FIXME: represent as a bignum. |
---|
| 503 | */ |
---|
| 504 | if (Tcl_GetWideIntFromObj(interp, valueObj, &valueWide) != TCL_OK) { |
---|
| 505 | Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr), |
---|
| 506 | TCL_GLOBAL_ONLY); |
---|
| 507 | return "variable must have unsigned wide int value"; |
---|
| 508 | } |
---|
| 509 | linkPtr->lastValue.uw = (Tcl_WideUInt)valueWide; |
---|
| 510 | LinkedVar(Tcl_WideUInt) = linkPtr->lastValue.uw; |
---|
| 511 | break; |
---|
| 512 | |
---|
| 513 | case TCL_LINK_FLOAT: |
---|
| 514 | if (Tcl_GetDoubleFromObj(interp, valueObj, &valueDouble) != TCL_OK |
---|
| 515 | || valueDouble < -FLT_MAX || valueDouble > FLT_MAX) { |
---|
| 516 | Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr), |
---|
| 517 | TCL_GLOBAL_ONLY); |
---|
| 518 | return "variable must have float value"; |
---|
| 519 | } |
---|
| 520 | linkPtr->lastValue.f = (float)valueDouble; |
---|
| 521 | LinkedVar(float) = linkPtr->lastValue.f; |
---|
| 522 | break; |
---|
| 523 | |
---|
| 524 | case TCL_LINK_STRING: |
---|
| 525 | value = Tcl_GetStringFromObj(valueObj, &valueLength); |
---|
| 526 | valueLength++; |
---|
| 527 | pp = (char **) linkPtr->addr; |
---|
| 528 | |
---|
| 529 | *pp = ckrealloc(*pp, valueLength); |
---|
| 530 | memcpy(*pp, value, (unsigned) valueLength); |
---|
| 531 | break; |
---|
| 532 | |
---|
| 533 | default: |
---|
| 534 | return "internal error: bad linked variable type"; |
---|
| 535 | } |
---|
| 536 | return NULL; |
---|
| 537 | } |
---|
| 538 | |
---|
| 539 | /* |
---|
| 540 | *---------------------------------------------------------------------- |
---|
| 541 | * |
---|
| 542 | * ObjValue -- |
---|
| 543 | * |
---|
| 544 | * Converts the value of a C variable to a Tcl_Obj* for use in a Tcl |
---|
| 545 | * variable to which it is linked. |
---|
| 546 | * |
---|
| 547 | * Results: |
---|
| 548 | * The return value is a pointer to a Tcl_Obj that represents the value |
---|
| 549 | * of the C variable given by linkPtr. |
---|
| 550 | * |
---|
| 551 | * Side effects: |
---|
| 552 | * None. |
---|
| 553 | * |
---|
| 554 | *---------------------------------------------------------------------- |
---|
| 555 | */ |
---|
| 556 | |
---|
| 557 | static Tcl_Obj * |
---|
| 558 | ObjValue( |
---|
| 559 | Link *linkPtr) /* Structure describing linked variable. */ |
---|
| 560 | { |
---|
| 561 | char *p; |
---|
| 562 | Tcl_Obj *resultObj; |
---|
| 563 | |
---|
| 564 | switch (linkPtr->type) { |
---|
| 565 | case TCL_LINK_INT: |
---|
| 566 | linkPtr->lastValue.i = LinkedVar(int); |
---|
| 567 | return Tcl_NewIntObj(linkPtr->lastValue.i); |
---|
| 568 | case TCL_LINK_WIDE_INT: |
---|
| 569 | linkPtr->lastValue.w = LinkedVar(Tcl_WideInt); |
---|
| 570 | return Tcl_NewWideIntObj(linkPtr->lastValue.w); |
---|
| 571 | case TCL_LINK_DOUBLE: |
---|
| 572 | linkPtr->lastValue.d = LinkedVar(double); |
---|
| 573 | return Tcl_NewDoubleObj(linkPtr->lastValue.d); |
---|
| 574 | case TCL_LINK_BOOLEAN: |
---|
| 575 | linkPtr->lastValue.i = LinkedVar(int); |
---|
| 576 | return Tcl_NewBooleanObj(linkPtr->lastValue.i != 0); |
---|
| 577 | case TCL_LINK_CHAR: |
---|
| 578 | linkPtr->lastValue.c = LinkedVar(char); |
---|
| 579 | return Tcl_NewIntObj(linkPtr->lastValue.c); |
---|
| 580 | case TCL_LINK_UCHAR: |
---|
| 581 | linkPtr->lastValue.uc = LinkedVar(unsigned char); |
---|
| 582 | return Tcl_NewIntObj(linkPtr->lastValue.uc); |
---|
| 583 | case TCL_LINK_SHORT: |
---|
| 584 | linkPtr->lastValue.s = LinkedVar(short); |
---|
| 585 | return Tcl_NewIntObj(linkPtr->lastValue.s); |
---|
| 586 | case TCL_LINK_USHORT: |
---|
| 587 | linkPtr->lastValue.us = LinkedVar(unsigned short); |
---|
| 588 | return Tcl_NewIntObj(linkPtr->lastValue.us); |
---|
| 589 | case TCL_LINK_UINT: |
---|
| 590 | linkPtr->lastValue.ui = LinkedVar(unsigned int); |
---|
| 591 | return Tcl_NewWideIntObj((Tcl_WideInt) linkPtr->lastValue.ui); |
---|
| 592 | case TCL_LINK_LONG: |
---|
| 593 | linkPtr->lastValue.l = LinkedVar(long); |
---|
| 594 | return Tcl_NewWideIntObj((Tcl_WideInt) linkPtr->lastValue.l); |
---|
| 595 | case TCL_LINK_ULONG: |
---|
| 596 | linkPtr->lastValue.ul = LinkedVar(unsigned long); |
---|
| 597 | return Tcl_NewWideIntObj((Tcl_WideInt) linkPtr->lastValue.ul); |
---|
| 598 | case TCL_LINK_FLOAT: |
---|
| 599 | linkPtr->lastValue.f = LinkedVar(float); |
---|
| 600 | return Tcl_NewDoubleObj(linkPtr->lastValue.f); |
---|
| 601 | case TCL_LINK_WIDE_UINT: |
---|
| 602 | linkPtr->lastValue.uw = LinkedVar(Tcl_WideUInt); |
---|
| 603 | /* |
---|
| 604 | * FIXME: represent as a bignum. |
---|
| 605 | */ |
---|
| 606 | return Tcl_NewWideIntObj((Tcl_WideInt) linkPtr->lastValue.uw); |
---|
| 607 | case TCL_LINK_STRING: |
---|
| 608 | p = LinkedVar(char *); |
---|
| 609 | if (p == NULL) { |
---|
| 610 | TclNewLiteralStringObj(resultObj, "NULL"); |
---|
| 611 | return resultObj; |
---|
| 612 | } |
---|
| 613 | return Tcl_NewStringObj(p, -1); |
---|
| 614 | |
---|
| 615 | /* |
---|
| 616 | * This code only gets executed if the link type is unknown (shouldn't |
---|
| 617 | * ever happen). |
---|
| 618 | */ |
---|
| 619 | |
---|
| 620 | default: |
---|
| 621 | TclNewLiteralStringObj(resultObj, "??"); |
---|
| 622 | return resultObj; |
---|
| 623 | } |
---|
| 624 | } |
---|
| 625 | |
---|
| 626 | /* |
---|
| 627 | * Local Variables: |
---|
| 628 | * mode: c |
---|
| 629 | * c-basic-offset: 4 |
---|
| 630 | * fill-column: 78 |
---|
| 631 | * End: |
---|
| 632 | */ |
---|