OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [tcl/] [generic/] [tclLink.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.