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.
9 * Copyright (c) 1993 The Regents of the University of California.
10 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
12 * See the file "license.terms" for information on usage and redistribution of
13 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
19 * For each linked variable there is a data structure of the following type,
20 * which describes the link and is the clientData for the trace set on the Tcl
25 Tcl_Interp *interp; /* Interpreter containing Tcl variable. */
26 Namespace *nsPtr; /* Namespace containing Tcl variable */
27 Tcl_Obj *varName; /* Name of variable (must be global). This is
28 * needed during trace callbacks, since the
29 * actual variable may be aliased at that time
31 char *addr; /* Location of C variable. */
32 int type; /* Type of link (TCL_LINK_INT, etc.). */
46 } lastValue; /* Last known value of C variable; used to
47 * avoid string conversions. */
48 int flags; /* Miscellaneous one-bit values; see below for
53 * Definitions for flag bits:
54 * LINK_READ_ONLY - 1 means errors should be generated if Tcl
55 * script attempts to write variable.
56 * LINK_BEING_UPDATED - 1 means that a call to Tcl_UpdateLinkedVar is
57 * in progress for this variable, so trace
58 * callbacks on the variable should be ignored.
61 #define LINK_READ_ONLY 1
62 #define LINK_BEING_UPDATED 2
65 * Forward references to functions defined later in this file:
68 static char * LinkTraceProc(ClientData clientData,Tcl_Interp *interp,
69 const char *name1, const char *name2, int flags);
70 static Tcl_Obj * ObjValue(Link *linkPtr);
71 static int GetInvalidIntFromObj(Tcl_Obj *objPtr, int *intPtr);
72 static int GetInvalidWideFromObj(Tcl_Obj *objPtr, Tcl_WideInt *widePtr);
73 static int GetInvalidDoubleFromObj(Tcl_Obj *objPtr, double *doublePtr);
76 * Convenience macro for accessing the value of the C variable pointed to by a
77 * link. Note that this macro produces something that may be regarded as an
78 * lvalue or rvalue; it may be assigned to as well as read. Also note that
79 * this macro assumes the name of the variable being accessed (linkPtr); this
80 * is not strictly a good thing, but it keeps the code much shorter and
84 #define LinkedVar(type) (*(type *) linkPtr->addr)
87 *----------------------------------------------------------------------
91 * Link a C variable to a Tcl variable so that changes to either one
92 * causes the other to change.
95 * The return value is TCL_OK if everything went well or TCL_ERROR if an
96 * error occurred (the interp's result is also set after errors).
99 * The value at *addr is linked to the Tcl variable "varName", using
100 * "type" to convert between string values for Tcl and binary values for
103 *----------------------------------------------------------------------
108 Tcl_Interp *interp, /* Interpreter in which varName exists. */
109 const char *varName, /* Name of a global variable in interp. */
110 char *addr, /* Address of a C variable to be linked to
112 int type) /* Type of C variable: TCL_LINK_INT, etc. Also
113 * may have TCL_LINK_READ_ONLY OR'ed in. */
121 linkPtr = (Link *) Tcl_VarTraceInfo2(interp, varName, NULL,
122 TCL_GLOBAL_ONLY, LinkTraceProc, (ClientData) NULL);
123 if (linkPtr != NULL) {
124 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
125 "variable '%s' is already linked", varName));
129 linkPtr = ckalloc(sizeof(Link));
130 linkPtr->interp = interp;
131 linkPtr->nsPtr = NULL;
132 linkPtr->varName = Tcl_NewStringObj(varName, -1);
133 Tcl_IncrRefCount(linkPtr->varName);
134 linkPtr->addr = addr;
135 linkPtr->type = type & ~TCL_LINK_READ_ONLY;
136 if (type & TCL_LINK_READ_ONLY) {
137 linkPtr->flags = LINK_READ_ONLY;
141 objPtr = ObjValue(linkPtr);
142 if (Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, objPtr,
143 TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
144 Tcl_DecrRefCount(linkPtr->varName);
149 TclGetNamespaceForQualName(interp, varName, NULL, TCL_GLOBAL_ONLY,
150 &(linkPtr->nsPtr), &dummy, &dummy, &name);
151 linkPtr->nsPtr->refCount++;
153 code = Tcl_TraceVar2(interp, varName, NULL,
154 TCL_GLOBAL_ONLY|TCL_TRACE_READS|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
155 LinkTraceProc, linkPtr);
156 if (code != TCL_OK) {
157 Tcl_DecrRefCount(linkPtr->varName);
158 TclNsDecrRefCount(linkPtr->nsPtr);
165 *----------------------------------------------------------------------
169 * Destroy the link between a Tcl variable and a C variable.
175 * If "varName" was previously linked to a C variable, the link is broken
176 * to make the variable independent. If there was no previous link for
177 * "varName" then nothing happens.
179 *----------------------------------------------------------------------
184 Tcl_Interp *interp, /* Interpreter containing variable to unlink */
185 const char *varName) /* Global variable in interp to unlink. */
187 Link *linkPtr = (Link *) Tcl_VarTraceInfo2(interp, varName, NULL,
188 TCL_GLOBAL_ONLY, LinkTraceProc, NULL);
190 if (linkPtr == NULL) {
193 Tcl_UntraceVar2(interp, varName, NULL,
194 TCL_GLOBAL_ONLY|TCL_TRACE_READS|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
195 LinkTraceProc, linkPtr);
196 Tcl_DecrRefCount(linkPtr->varName);
197 if (linkPtr->nsPtr) {
198 TclNsDecrRefCount(linkPtr->nsPtr);
204 *----------------------------------------------------------------------
206 * Tcl_UpdateLinkedVar --
208 * This function is invoked after a linked variable has been changed by C
209 * code. It updates the Tcl variable so that traces on the variable will
216 * The Tcl variable "varName" is updated from its C value, causing traces
217 * on the variable to trigger.
219 *----------------------------------------------------------------------
224 Tcl_Interp *interp, /* Interpreter containing variable. */
225 const char *varName) /* Name of global variable that is linked. */
227 Link *linkPtr = (Link *) Tcl_VarTraceInfo2(interp, varName, NULL,
228 TCL_GLOBAL_ONLY, LinkTraceProc, NULL);
231 if (linkPtr == NULL) {
234 savedFlag = linkPtr->flags & LINK_BEING_UPDATED;
235 linkPtr->flags |= LINK_BEING_UPDATED;
236 Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
239 * Callback may have unlinked the variable. [Bug 1740631]
241 linkPtr = (Link *) Tcl_VarTraceInfo2(interp, varName, NULL,
242 TCL_GLOBAL_ONLY, LinkTraceProc, NULL);
243 if (linkPtr != NULL) {
244 linkPtr->flags = (linkPtr->flags & ~LINK_BEING_UPDATED) | savedFlag;
249 *----------------------------------------------------------------------
253 * This function is invoked when a linked Tcl variable is read, written,
254 * or unset from Tcl. It's responsible for keeping the C variable in sync
255 * with the Tcl variable.
258 * If all goes well, NULL is returned; otherwise an error message is
262 * The C variable may be updated to make it consistent with the Tcl
263 * variable, or the Tcl variable may be overwritten to reject a
266 *----------------------------------------------------------------------
271 ClientData clientData, /* Contains information about the link. */
272 Tcl_Interp *interp, /* Interpreter containing Tcl variable. */
273 const char *name1, /* First part of variable name. */
274 const char *name2, /* Second part of variable name. */
275 int flags) /* Miscellaneous additional information. */
277 Link *linkPtr = clientData;
284 Tcl_WideInt valueWide;
288 * If the variable is being unset, then just re-create it (with a trace)
289 * unless the whole interpreter is going away.
292 if (flags & TCL_TRACE_UNSETS) {
293 if (Tcl_InterpDeleted(interp) || TclNamespaceDeleted(linkPtr->nsPtr)) {
294 Tcl_DecrRefCount(linkPtr->varName);
295 if (linkPtr->nsPtr) {
296 TclNsDecrRefCount(linkPtr->nsPtr);
299 } else if (flags & TCL_TRACE_DESTROYED) {
300 Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
302 Tcl_TraceVar2(interp, Tcl_GetString(linkPtr->varName), NULL,
303 TCL_GLOBAL_ONLY|TCL_TRACE_READS|TCL_TRACE_WRITES
304 |TCL_TRACE_UNSETS, LinkTraceProc, linkPtr);
310 * If we were invoked because of a call to Tcl_UpdateLinkedVar, then don't
311 * do anything at all. In particular, we don't want to get upset that the
312 * variable is being modified, even if it is supposed to be read-only.
315 if (linkPtr->flags & LINK_BEING_UPDATED) {
320 * For read accesses, update the Tcl variable if the C variable has
321 * changed since the last time we updated the Tcl variable.
324 if (flags & TCL_TRACE_READS) {
325 switch (linkPtr->type) {
327 case TCL_LINK_BOOLEAN:
328 changed = (LinkedVar(int) != linkPtr->lastValue.i);
330 case TCL_LINK_DOUBLE:
331 changed = (LinkedVar(double) != linkPtr->lastValue.d);
333 case TCL_LINK_WIDE_INT:
334 changed = (LinkedVar(Tcl_WideInt) != linkPtr->lastValue.w);
336 case TCL_LINK_WIDE_UINT:
337 changed = (LinkedVar(Tcl_WideUInt) != linkPtr->lastValue.uw);
340 changed = (LinkedVar(char) != linkPtr->lastValue.c);
343 changed = (LinkedVar(unsigned char) != linkPtr->lastValue.uc);
346 changed = (LinkedVar(short) != linkPtr->lastValue.s);
348 case TCL_LINK_USHORT:
349 changed = (LinkedVar(unsigned short) != linkPtr->lastValue.us);
352 changed = (LinkedVar(unsigned int) != linkPtr->lastValue.ui);
355 changed = (LinkedVar(long) != linkPtr->lastValue.l);
358 changed = (LinkedVar(unsigned long) != linkPtr->lastValue.ul);
361 changed = (LinkedVar(float) != linkPtr->lastValue.f);
363 case TCL_LINK_STRING:
367 return (char *) "internal error: bad linked variable type";
370 Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
377 * For writes, first make sure that the variable is writable. Then convert
378 * the Tcl value to C if possible. If the variable isn't writable or can't
379 * be converted, then restore the varaible's old value and return an
380 * error. Another tricky thing: we have to save and restore the interp's
381 * result, since the variable access could occur when the result has been
385 if (linkPtr->flags & LINK_READ_ONLY) {
386 Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
388 return (char *) "linked variable is read-only";
390 valueObj = Tcl_ObjGetVar2(interp, linkPtr->varName,NULL, TCL_GLOBAL_ONLY);
391 if (valueObj == NULL) {
393 * This shouldn't ever happen.
396 return (char *) "internal error: linked variable couldn't be read";
399 switch (linkPtr->type) {
401 if (Tcl_GetIntFromObj(NULL, valueObj, &linkPtr->lastValue.i) != TCL_OK
402 && GetInvalidIntFromObj(valueObj, &linkPtr->lastValue.i) != TCL_OK) {
403 Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
405 return (char *) "variable must have integer value";
407 LinkedVar(int) = linkPtr->lastValue.i;
410 case TCL_LINK_WIDE_INT:
411 if (Tcl_GetWideIntFromObj(NULL, valueObj, &linkPtr->lastValue.w) != TCL_OK
412 && GetInvalidWideFromObj(valueObj, &linkPtr->lastValue.w) != TCL_OK) {
413 Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
415 return (char *) "variable must have integer value";
417 LinkedVar(Tcl_WideInt) = linkPtr->lastValue.w;
420 case TCL_LINK_DOUBLE:
421 if (Tcl_GetDoubleFromObj(NULL, valueObj, &linkPtr->lastValue.d) != TCL_OK) {
423 if (valueObj->typePtr != &tclDoubleType) {
425 if (GetInvalidDoubleFromObj(valueObj, &linkPtr->lastValue.d) != TCL_OK) {
426 Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
428 return (char *) "variable must have real value";
432 linkPtr->lastValue.d = valueObj->internalRep.doubleValue;
435 LinkedVar(double) = linkPtr->lastValue.d;
438 case TCL_LINK_BOOLEAN:
439 if (Tcl_GetBooleanFromObj(NULL, valueObj, &linkPtr->lastValue.i) != TCL_OK) {
440 Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
442 return (char *) "variable must have boolean value";
444 LinkedVar(int) = linkPtr->lastValue.i;
448 if ((Tcl_GetIntFromObj(NULL, valueObj, &valueInt) != TCL_OK
449 && GetInvalidIntFromObj(valueObj, &valueInt) != TCL_OK)
450 || valueInt < SCHAR_MIN || valueInt > SCHAR_MAX) {
451 Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
453 return (char *) "variable must have char value";
455 LinkedVar(char) = linkPtr->lastValue.c = (char)valueInt;
459 if ((Tcl_GetIntFromObj(NULL, valueObj, &valueInt) != TCL_OK
460 && GetInvalidIntFromObj(valueObj, &valueInt) != TCL_OK)
461 || valueInt < 0 || valueInt > UCHAR_MAX) {
462 Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
464 return (char *) "variable must have unsigned char value";
466 LinkedVar(unsigned char) = linkPtr->lastValue.uc = (unsigned char) valueInt;
470 if ((Tcl_GetIntFromObj(NULL, valueObj, &valueInt) != TCL_OK
471 && GetInvalidIntFromObj(valueObj, &valueInt) != TCL_OK)
472 || valueInt < SHRT_MIN || valueInt > SHRT_MAX) {
473 Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
475 return (char *) "variable must have short value";
477 LinkedVar(short) = linkPtr->lastValue.s = (short)valueInt;
480 case TCL_LINK_USHORT:
481 if ((Tcl_GetIntFromObj(NULL, valueObj, &valueInt) != TCL_OK
482 && GetInvalidIntFromObj(valueObj, &valueInt) != TCL_OK)
483 || valueInt < 0 || valueInt > USHRT_MAX) {
484 Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
486 return (char *) "variable must have unsigned short value";
488 LinkedVar(unsigned short) = linkPtr->lastValue.us = (unsigned short)valueInt;
492 if ((Tcl_GetWideIntFromObj(NULL, valueObj, &valueWide) != TCL_OK
493 && GetInvalidWideFromObj(valueObj, &valueWide) != TCL_OK)
494 || valueWide < 0 || valueWide > UINT_MAX) {
495 Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
497 return (char *) "variable must have unsigned int value";
499 LinkedVar(unsigned int) = linkPtr->lastValue.ui = (unsigned int)valueWide;
503 if ((Tcl_GetWideIntFromObj(NULL, valueObj, &valueWide) != TCL_OK
504 && GetInvalidWideFromObj(valueObj, &valueWide) != TCL_OK)
505 || valueWide < LONG_MIN || valueWide > LONG_MAX) {
506 Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
508 return (char *) "variable must have long value";
510 LinkedVar(long) = linkPtr->lastValue.l = (long)valueWide;
514 if ((Tcl_GetWideIntFromObj(NULL, valueObj, &valueWide) != TCL_OK
515 && GetInvalidWideFromObj(valueObj, &valueWide) != TCL_OK)
516 || valueWide < 0 || (Tcl_WideUInt) valueWide > ULONG_MAX) {
517 Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
519 return (char *) "variable must have unsigned long value";
521 LinkedVar(unsigned long) = linkPtr->lastValue.ul = (unsigned long)valueWide;
524 case TCL_LINK_WIDE_UINT:
526 * FIXME: represent as a bignum.
528 if (Tcl_GetWideIntFromObj(NULL, valueObj, &valueWide) != TCL_OK
529 && GetInvalidWideFromObj(valueObj, &valueWide) != TCL_OK) {
530 Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
532 return (char *) "variable must have unsigned wide int value";
534 LinkedVar(Tcl_WideUInt) = linkPtr->lastValue.uw = (Tcl_WideUInt)valueWide;
538 if ((Tcl_GetDoubleFromObj(NULL, valueObj, &valueDouble) != TCL_OK
539 && GetInvalidDoubleFromObj(valueObj, &valueDouble) != TCL_OK)
540 || valueDouble < -FLT_MAX || valueDouble > FLT_MAX) {
541 Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
543 return (char *) "variable must have float value";
545 LinkedVar(float) = linkPtr->lastValue.f = (float)valueDouble;
548 case TCL_LINK_STRING:
549 value = TclGetString(valueObj);
550 valueLength = valueObj->length + 1;
551 pp = (char **) linkPtr->addr;
553 *pp = ckrealloc(*pp, valueLength);
554 memcpy(*pp, value, valueLength);
558 return (char *) "internal error: bad linked variable type";
564 *----------------------------------------------------------------------
568 * Converts the value of a C variable to a Tcl_Obj* for use in a Tcl
569 * variable to which it is linked.
572 * The return value is a pointer to a Tcl_Obj that represents the value
573 * of the C variable given by linkPtr.
578 *----------------------------------------------------------------------
583 Link *linkPtr) /* Structure describing linked variable. */
588 switch (linkPtr->type) {
590 linkPtr->lastValue.i = LinkedVar(int);
591 return Tcl_NewIntObj(linkPtr->lastValue.i);
592 case TCL_LINK_WIDE_INT:
593 linkPtr->lastValue.w = LinkedVar(Tcl_WideInt);
594 return Tcl_NewWideIntObj(linkPtr->lastValue.w);
595 case TCL_LINK_DOUBLE:
596 linkPtr->lastValue.d = LinkedVar(double);
597 return Tcl_NewDoubleObj(linkPtr->lastValue.d);
598 case TCL_LINK_BOOLEAN:
599 linkPtr->lastValue.i = LinkedVar(int);
600 return Tcl_NewBooleanObj(linkPtr->lastValue.i != 0);
602 linkPtr->lastValue.c = LinkedVar(char);
603 return Tcl_NewIntObj(linkPtr->lastValue.c);
605 linkPtr->lastValue.uc = LinkedVar(unsigned char);
606 return Tcl_NewIntObj(linkPtr->lastValue.uc);
608 linkPtr->lastValue.s = LinkedVar(short);
609 return Tcl_NewIntObj(linkPtr->lastValue.s);
610 case TCL_LINK_USHORT:
611 linkPtr->lastValue.us = LinkedVar(unsigned short);
612 return Tcl_NewIntObj(linkPtr->lastValue.us);
614 linkPtr->lastValue.ui = LinkedVar(unsigned int);
615 return Tcl_NewWideIntObj((Tcl_WideInt) linkPtr->lastValue.ui);
617 linkPtr->lastValue.l = LinkedVar(long);
618 return Tcl_NewWideIntObj((Tcl_WideInt) linkPtr->lastValue.l);
620 linkPtr->lastValue.ul = LinkedVar(unsigned long);
621 return Tcl_NewWideIntObj((Tcl_WideInt) linkPtr->lastValue.ul);
623 linkPtr->lastValue.f = LinkedVar(float);
624 return Tcl_NewDoubleObj(linkPtr->lastValue.f);
625 case TCL_LINK_WIDE_UINT:
626 linkPtr->lastValue.uw = LinkedVar(Tcl_WideUInt);
628 * FIXME: represent as a bignum.
630 return Tcl_NewWideIntObj((Tcl_WideInt) linkPtr->lastValue.uw);
631 case TCL_LINK_STRING:
632 p = LinkedVar(char *);
634 TclNewLiteralStringObj(resultObj, "NULL");
637 return Tcl_NewStringObj(p, -1);
640 * This code only gets executed if the link type is unknown (shouldn't
645 TclNewLiteralStringObj(resultObj, "??");
650 static int SetInvalidRealFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr);
652 static Tcl_ObjType invalidRealType = {
653 "invalidReal", /* name */
654 NULL, /* freeIntRepProc */
655 NULL, /* dupIntRepProc */
656 NULL, /* updateStringProc */
657 NULL /* setFromAnyProc */
661 SetInvalidRealFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr) {
666 str = TclGetStringFromObj(objPtr, &length);
667 if ((length == 1) && (str[0] == '.')){
668 objPtr->typePtr = &invalidRealType;
669 objPtr->internalRep.doubleValue = 0.0;
672 if (TclParseNumber(NULL, objPtr, NULL, str, length, &endPtr,
673 TCL_PARSE_DECIMAL_ONLY) == TCL_OK) {
674 /* If number is followed by [eE][+-]?, then it is an invalid
675 * double, but it could be the start of a valid double. */
676 if (*endPtr == 'e' || *endPtr == 'E') {
678 if (*endPtr == '+' || *endPtr == '-') ++endPtr;
680 double doubleValue = 0.0;
681 Tcl_GetDoubleFromObj(NULL, objPtr, &doubleValue);
682 if (objPtr->typePtr->freeIntRepProc) objPtr->typePtr->freeIntRepProc(objPtr);
683 objPtr->typePtr = &invalidRealType;
684 objPtr->internalRep.doubleValue = doubleValue;
694 * This function checks for integer representations, which are valid
695 * when linking with C variables, but which are invalid in other
696 * contexts in Tcl. Handled are "+", "-", "", "0x", "0b" and "0o"
697 * (upperand lowercase). See bug [39f6304c2e].
700 GetInvalidIntFromObj(Tcl_Obj *objPtr,
703 const char *str = TclGetString(objPtr);
705 if ((objPtr->length == 0) ||
706 ((objPtr->length == 2) && (str[0] == '0') && strchr("xXbBoO", str[1]))) {
709 } else if ((objPtr->length == 1) && strchr("+-", str[0])) {
710 *intPtr = (str[0] == '+');
717 GetInvalidWideFromObj(Tcl_Obj *objPtr, Tcl_WideInt *widePtr)
721 if (GetInvalidIntFromObj(objPtr, &intValue) != TCL_OK) {
729 * This function checks for double representations, which are valid
730 * when linking with C variables, but which are invalid in other
731 * contexts in Tcl. Handled are "+", "-", "", ".", "0x", "0b" and "0o"
732 * (upper- and lowercase) and sequences like "1e-". See bug [39f6304c2e].
735 GetInvalidDoubleFromObj(Tcl_Obj *objPtr,
740 if (objPtr->typePtr == &invalidRealType) {
743 if (GetInvalidIntFromObj(objPtr, &intValue) == TCL_OK) {
744 *doublePtr = (double) intValue;
747 if (SetInvalidRealFromAny(NULL, objPtr) == TCL_OK) {
749 *doublePtr = objPtr->internalRep.doubleValue;