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