tclLink.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:14k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tclLink.c --
  3.  *
  4.  * This file implements linked variables (a C variable that is
  5.  * tied to a Tcl variable).  The idea of linked variables was
  6.  * first suggested by Andreas Stolcke and this implementation is
  7.  * based heavily on a prototype implementation provided by
  8.  * him.
  9.  *
  10.  * Copyright (c) 1993 The Regents of the University of California.
  11.  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
  12.  *
  13.  * See the file "license.terms" for information on usage and redistribution
  14.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  15.  *
  16.  * RCS: @(#) $Id: tclLink.c,v 1.8.2.3 2007/09/10 23:06:12 hobbs Exp $
  17.  */
  18. #include "tclInt.h"
  19. /*
  20.  * For each linked variable there is a data structure of the following
  21.  * type, which describes the link and is the clientData for the trace
  22.  * set on the Tcl variable.
  23.  */
  24. typedef struct Link {
  25.     Tcl_Interp *interp; /* Interpreter containing Tcl variable. */
  26.     Tcl_Obj *varName; /* Name of variable (must be global).  This
  27.  * is needed during trace callbacks, since
  28.  * the actual variable may be aliased at
  29.  * that time via upvar. */
  30.     char *addr; /* Location of C variable. */
  31.     int type; /* Type of link (TCL_LINK_INT, etc.). */
  32.     union {
  33. int i;
  34. double d;
  35. Tcl_WideInt w;
  36.     } lastValue; /* Last known value of C variable;  used to
  37.  * avoid string conversions. */
  38.     int flags; /* Miscellaneous one-bit values;  see below
  39.  * for definitions. */
  40. } Link;
  41. /*
  42.  * Definitions for flag bits:
  43.  * LINK_READ_ONLY - 1 means errors should be generated if Tcl
  44.  * script attempts to write variable.
  45.  * LINK_BEING_UPDATED - 1 means that a call to Tcl_UpdateLinkedVar
  46.  * is in progress for this variable, so
  47.  * trace callbacks on the variable should
  48.  * be ignored.
  49.  */
  50. #define LINK_READ_ONLY 1
  51. #define LINK_BEING_UPDATED 2
  52. /*
  53.  * Forward references to procedures defined later in this file:
  54.  */
  55. static char * LinkTraceProc _ANSI_ARGS_((ClientData clientData,
  56.     Tcl_Interp *interp, CONST char *name1, 
  57.                             CONST char *name2, int flags));
  58. static Tcl_Obj * ObjValue _ANSI_ARGS_((Link *linkPtr));
  59. /*
  60.  *----------------------------------------------------------------------
  61.  *
  62.  * Tcl_LinkVar --
  63.  *
  64.  * Link a C variable to a Tcl variable so that changes to either
  65.  * one causes the other to change.
  66.  *
  67.  * Results:
  68.  * The return value is TCL_OK if everything went well or TCL_ERROR
  69.  * if an error occurred (the interp's result is also set after
  70.  * errors).
  71.  *
  72.  * Side effects:
  73.  * The value at *addr is linked to the Tcl variable "varName",
  74.  * using "type" to convert between string values for Tcl and
  75.  * binary values for *addr.
  76.  *
  77.  *----------------------------------------------------------------------
  78.  */
  79. int
  80. Tcl_LinkVar(interp, varName, addr, type)
  81.     Tcl_Interp *interp; /* Interpreter in which varName exists. */
  82.     CONST char *varName; /* Name of a global variable in interp. */
  83.     char *addr; /* Address of a C variable to be linked
  84.  * to varName. */
  85.     int type; /* Type of C variable: TCL_LINK_INT, etc. 
  86.  * Also may have TCL_LINK_READ_ONLY
  87.  * OR'ed in. */
  88. {
  89.     Tcl_Obj *objPtr, *resPtr;
  90.     Link *linkPtr;
  91.     int code;
  92.     linkPtr = (Link *) ckalloc(sizeof(Link));
  93.     linkPtr->interp = interp;
  94.     linkPtr->varName = Tcl_NewStringObj(varName, -1);
  95.     Tcl_IncrRefCount(linkPtr->varName);
  96.     linkPtr->addr = addr;
  97.     linkPtr->type = type & ~TCL_LINK_READ_ONLY;
  98.     if (type & TCL_LINK_READ_ONLY) {
  99. linkPtr->flags = LINK_READ_ONLY;
  100.     } else {
  101. linkPtr->flags = 0;
  102.     }
  103.     objPtr = ObjValue(linkPtr);
  104.     Tcl_IncrRefCount(objPtr);
  105.     resPtr = Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, objPtr,
  106.     TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG);
  107.     Tcl_DecrRefCount(objPtr);
  108.     if (resPtr == NULL) {
  109. Tcl_DecrRefCount(linkPtr->varName);
  110. ckfree((char *) linkPtr);
  111. return TCL_ERROR;
  112.     }
  113.     code = Tcl_TraceVar(interp, varName, TCL_GLOBAL_ONLY|TCL_TRACE_READS
  114.     |TCL_TRACE_WRITES|TCL_TRACE_UNSETS, LinkTraceProc,
  115.     (ClientData) linkPtr);
  116.     if (code != TCL_OK) {
  117. Tcl_DecrRefCount(linkPtr->varName);
  118. ckfree((char *) linkPtr);
  119.     }
  120.     return code;
  121. }
  122. /*
  123.  *----------------------------------------------------------------------
  124.  *
  125.  * Tcl_UnlinkVar --
  126.  *
  127.  * Destroy the link between a Tcl variable and a C variable.
  128.  *
  129.  * Results:
  130.  * None.
  131.  *
  132.  * Side effects:
  133.  * If "varName" was previously linked to a C variable, the link
  134.  * is broken to make the variable independent.  If there was no
  135.  * previous link for "varName" then nothing happens.
  136.  *
  137.  *----------------------------------------------------------------------
  138.  */
  139. void
  140. Tcl_UnlinkVar(interp, varName)
  141.     Tcl_Interp *interp; /* Interpreter containing variable to unlink. */
  142.     CONST char *varName; /* Global variable in interp to unlink. */
  143. {
  144.     Link *linkPtr;
  145.     linkPtr = (Link *) Tcl_VarTraceInfo(interp, varName, TCL_GLOBAL_ONLY,
  146.     LinkTraceProc, (ClientData) NULL);
  147.     if (linkPtr == NULL) {
  148. return;
  149.     }
  150.     Tcl_UntraceVar(interp, varName,
  151.     TCL_GLOBAL_ONLY|TCL_TRACE_READS|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  152.     LinkTraceProc, (ClientData) linkPtr);
  153.     Tcl_DecrRefCount(linkPtr->varName);
  154.     ckfree((char *) linkPtr);
  155. }
  156. /*
  157.  *----------------------------------------------------------------------
  158.  *
  159.  * Tcl_UpdateLinkedVar --
  160.  *
  161.  * This procedure is invoked after a linked variable has been
  162.  * changed by C code.  It updates the Tcl variable so that
  163.  * traces on the variable will trigger.
  164.  *
  165.  * Results:
  166.  * None.
  167.  *
  168.  * Side effects:
  169.  * The Tcl variable "varName" is updated from its C value,
  170.  * causing traces on the variable to trigger.
  171.  *
  172.  *----------------------------------------------------------------------
  173.  */
  174. void
  175. Tcl_UpdateLinkedVar(interp, varName)
  176.     Tcl_Interp *interp; /* Interpreter containing variable. */
  177.     CONST char *varName; /* Name of global variable that is linked. */
  178. {
  179.     Link *linkPtr;
  180.     int savedFlag;
  181.     Tcl_Obj *objPtr;
  182.     linkPtr = (Link *) Tcl_VarTraceInfo(interp, varName, TCL_GLOBAL_ONLY,
  183.     LinkTraceProc, (ClientData) NULL);
  184.     if (linkPtr == NULL) {
  185. return;
  186.     }
  187.     savedFlag = linkPtr->flags & LINK_BEING_UPDATED;
  188.     linkPtr->flags |= LINK_BEING_UPDATED;
  189.     objPtr = ObjValue(linkPtr);
  190.     Tcl_IncrRefCount(objPtr);
  191.     Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, objPtr, TCL_GLOBAL_ONLY);
  192.     Tcl_DecrRefCount(objPtr);
  193.     /*
  194.      * Callback may have unlinked the variable. [Bug 1740631]
  195.      */
  196.     linkPtr = (Link *) Tcl_VarTraceInfo(interp, varName, TCL_GLOBAL_ONLY,
  197.     LinkTraceProc, (ClientData) NULL);
  198.     if (linkPtr != NULL) {
  199. linkPtr->flags = (linkPtr->flags & ~LINK_BEING_UPDATED) | savedFlag;
  200.     }
  201. }
  202. /*
  203.  *----------------------------------------------------------------------
  204.  *
  205.  * LinkTraceProc --
  206.  *
  207.  * This procedure is invoked when a linked Tcl variable is read,
  208.  * written, or unset from Tcl.  It's responsible for keeping the
  209.  * C variable in sync with the Tcl variable.
  210.  *
  211.  * Results:
  212.  * If all goes well, NULL is returned; otherwise an error message
  213.  * is returned.
  214.  *
  215.  * Side effects:
  216.  * The C variable may be updated to make it consistent with the
  217.  * Tcl variable, or the Tcl variable may be overwritten to reject
  218.  * a modification.
  219.  *
  220.  *----------------------------------------------------------------------
  221.  */
  222. static char *
  223. LinkTraceProc(clientData, interp, name1, name2, flags)
  224.     ClientData clientData; /* Contains information about the link. */
  225.     Tcl_Interp *interp; /* Interpreter containing Tcl variable. */
  226.     CONST char *name1; /* First part of variable name. */
  227.     CONST char *name2; /* Second part of variable name. */
  228.     int flags; /* Miscellaneous additional information. */
  229. {
  230.     Link *linkPtr = (Link *) clientData;
  231.     int changed, valueLength;
  232.     CONST char *value;
  233.     char **pp, *result;
  234.     Tcl_Obj *objPtr, *valueObj, *tmpPtr;
  235.     /*
  236.      * If the variable is being unset, then just re-create it (with a
  237.      * trace) unless the whole interpreter is going away.
  238.      */
  239.     if (flags & TCL_TRACE_UNSETS) {
  240. if (Tcl_InterpDeleted(interp)) {
  241.     Tcl_DecrRefCount(linkPtr->varName);
  242.     ckfree((char *) linkPtr);
  243. } else if (flags & TCL_TRACE_DESTROYED) {
  244.     tmpPtr = ObjValue(linkPtr);
  245.     Tcl_IncrRefCount(tmpPtr);
  246.     Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, tmpPtr,
  247.     TCL_GLOBAL_ONLY);
  248.     Tcl_DecrRefCount(tmpPtr);
  249.     Tcl_TraceVar(interp, Tcl_GetString(linkPtr->varName),
  250.     TCL_GLOBAL_ONLY|TCL_TRACE_READS|TCL_TRACE_WRITES
  251.     |TCL_TRACE_UNSETS, LinkTraceProc, (ClientData) linkPtr);
  252. }
  253. return NULL;
  254.     }
  255.     /*
  256.      * If we were invoked because of a call to Tcl_UpdateLinkedVar, then
  257.      * don't do anything at all.  In particular, we don't want to get
  258.      * upset that the variable is being modified, even if it is
  259.      * supposed to be read-only.
  260.      */
  261.     if (linkPtr->flags & LINK_BEING_UPDATED) {
  262. return NULL;
  263.     }
  264.     /*
  265.      * For read accesses, update the Tcl variable if the C variable
  266.      * has changed since the last time we updated the Tcl variable.
  267.      */
  268.     if (flags & TCL_TRACE_READS) {
  269. switch (linkPtr->type) {
  270. case TCL_LINK_INT:
  271. case TCL_LINK_BOOLEAN:
  272.     changed = *(int *)(linkPtr->addr) != linkPtr->lastValue.i;
  273.     break;
  274. case TCL_LINK_DOUBLE:
  275.     changed = *(double *)(linkPtr->addr) != linkPtr->lastValue.d;
  276.     break;
  277. case TCL_LINK_WIDE_INT:
  278.     changed = *(Tcl_WideInt *)(linkPtr->addr) != linkPtr->lastValue.w;
  279.     break;
  280. case TCL_LINK_STRING:
  281.     changed = 1;
  282.     break;
  283. default:
  284.     return "internal error: bad linked variable type";
  285. }
  286. if (changed) {
  287.     tmpPtr = ObjValue(linkPtr);
  288.     Tcl_IncrRefCount(tmpPtr);
  289.     Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, tmpPtr,
  290.     TCL_GLOBAL_ONLY);
  291.     Tcl_DecrRefCount(tmpPtr);
  292. }
  293. return NULL;
  294.     }
  295.     /*
  296.      * For writes, first make sure that the variable is writable.  Then
  297.      * convert the Tcl value to C if possible.  If the variable isn't
  298.      * writable or can't be converted, then restore the varaible's old
  299.      * value and return an error.  Another tricky thing: we have to save
  300.      * and restore the interpreter's result, since the variable access
  301.      * could occur when the result has been partially set.
  302.      */
  303.     if (linkPtr->flags & LINK_READ_ONLY) {
  304. tmpPtr = ObjValue(linkPtr);
  305. Tcl_IncrRefCount(tmpPtr);
  306. Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, tmpPtr,
  307. TCL_GLOBAL_ONLY);
  308. Tcl_DecrRefCount(tmpPtr);
  309. return "linked variable is read-only";
  310.     }
  311.     valueObj = Tcl_ObjGetVar2(interp, linkPtr->varName,NULL, TCL_GLOBAL_ONLY);
  312.     if (valueObj == NULL) {
  313. /*
  314.  * This shouldn't ever happen.
  315.  */
  316. return "internal error: linked variable couldn't be read";
  317.     }
  318.     objPtr = Tcl_GetObjResult(interp);
  319.     Tcl_IncrRefCount(objPtr);
  320.     Tcl_ResetResult(interp);
  321.     result = NULL;
  322.     switch (linkPtr->type) {
  323.     case TCL_LINK_INT:
  324. if (Tcl_GetIntFromObj(interp, valueObj, &linkPtr->lastValue.i)
  325. != TCL_OK) {
  326.     Tcl_SetObjResult(interp, objPtr);
  327.     tmpPtr = ObjValue(linkPtr);
  328.     Tcl_IncrRefCount(tmpPtr);
  329.     Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, tmpPtr,
  330.     TCL_GLOBAL_ONLY);
  331.     Tcl_DecrRefCount(tmpPtr);
  332.     result = "variable must have integer value";
  333.     goto end;
  334. }
  335. *(int *)(linkPtr->addr) = linkPtr->lastValue.i;
  336. break;
  337.     case TCL_LINK_WIDE_INT:
  338. if (Tcl_GetWideIntFromObj(interp, valueObj, &linkPtr->lastValue.w)
  339. != TCL_OK) {
  340.     Tcl_SetObjResult(interp, objPtr);
  341.     tmpPtr = ObjValue(linkPtr);
  342.     Tcl_IncrRefCount(tmpPtr);
  343.     Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, tmpPtr,
  344.     TCL_GLOBAL_ONLY);
  345.     Tcl_DecrRefCount(tmpPtr);
  346.     result = "variable must have integer value";
  347.     goto end;
  348. }
  349. *(Tcl_WideInt *)(linkPtr->addr) = linkPtr->lastValue.w;
  350. break;
  351.     case TCL_LINK_DOUBLE:
  352. if (Tcl_GetDoubleFromObj(interp, valueObj, &linkPtr->lastValue.d)
  353. != TCL_OK) {
  354.     Tcl_SetObjResult(interp, objPtr);
  355.     tmpPtr = ObjValue(linkPtr);
  356.     Tcl_IncrRefCount(tmpPtr);
  357.     Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, tmpPtr,
  358.     TCL_GLOBAL_ONLY);
  359.     Tcl_DecrRefCount(tmpPtr);
  360.     result = "variable must have real value";
  361.     goto end;
  362. }
  363. *(double *)(linkPtr->addr) = linkPtr->lastValue.d;
  364. break;
  365.     case TCL_LINK_BOOLEAN:
  366. if (Tcl_GetBooleanFromObj(interp, valueObj, &linkPtr->lastValue.i)
  367.     != TCL_OK) {
  368.     Tcl_SetObjResult(interp, objPtr);
  369.     tmpPtr = ObjValue(linkPtr);
  370.     Tcl_IncrRefCount(tmpPtr);
  371.     Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, tmpPtr,
  372.     TCL_GLOBAL_ONLY);
  373.     Tcl_DecrRefCount(tmpPtr);
  374.     result = "variable must have boolean value";
  375.     goto end;
  376. }
  377. *(int *)(linkPtr->addr) = linkPtr->lastValue.i;
  378. break;
  379.     case TCL_LINK_STRING:
  380. value = Tcl_GetStringFromObj(valueObj, &valueLength);
  381. valueLength++;
  382. pp = (char **)(linkPtr->addr);
  383. if (*pp != NULL) {
  384.     ckfree(*pp);
  385. }
  386. *pp = (char *) ckalloc((unsigned) valueLength);
  387. memcpy(*pp, value, (unsigned) valueLength);
  388. break;
  389.     default:
  390. return "internal error: bad linked variable type";
  391.     }
  392.     end:
  393.     Tcl_DecrRefCount(objPtr);
  394.     return result;
  395. }
  396. /*
  397.  *----------------------------------------------------------------------
  398.  *
  399.  * ObjValue --
  400.  *
  401.  * Converts the value of a C variable to a Tcl_Obj* for use in a
  402.  * Tcl variable to which it is linked.
  403.  *
  404.  * Results:
  405.  * The return value is a pointer to a Tcl_Obj that represents
  406.  * the value of the C variable given by linkPtr.
  407.  *
  408.  * Side effects:
  409.  * None.
  410.  *
  411.  *----------------------------------------------------------------------
  412.  */
  413. static Tcl_Obj *
  414. ObjValue(linkPtr)
  415.     Link *linkPtr; /* Structure describing linked variable. */
  416. {
  417.     char *p;
  418.     switch (linkPtr->type) {
  419.     case TCL_LINK_INT:
  420. linkPtr->lastValue.i = *(int *)(linkPtr->addr);
  421. return Tcl_NewIntObj(linkPtr->lastValue.i);
  422.     case TCL_LINK_WIDE_INT:
  423. linkPtr->lastValue.w = *(Tcl_WideInt *)(linkPtr->addr);
  424. return Tcl_NewWideIntObj(linkPtr->lastValue.w);
  425.     case TCL_LINK_DOUBLE:
  426. linkPtr->lastValue.d = *(double *)(linkPtr->addr);
  427. return Tcl_NewDoubleObj(linkPtr->lastValue.d);
  428.     case TCL_LINK_BOOLEAN:
  429. linkPtr->lastValue.i = *(int *)(linkPtr->addr);
  430. return Tcl_NewBooleanObj(linkPtr->lastValue.i != 0);
  431.     case TCL_LINK_STRING:
  432. p = *(char **)(linkPtr->addr);
  433. if (p == NULL) {
  434.     return Tcl_NewStringObj("NULL", 4);
  435. }
  436. return Tcl_NewStringObj(p, -1);
  437.     /*
  438.      * This code only gets executed if the link type is unknown
  439.      * (shouldn't ever happen).
  440.      */
  441.     default:
  442. return Tcl_NewStringObj("??", 2);
  443.     }
  444. }