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 | */ |
---|