Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/tcl8.5.2/generic/tclIORChan.c @ 25

Last change on this file since 25 was 25, checked in by landauf, 16 years ago

added tcl to libs

File size: 71.0 KB
Line 
1/*
2 * tclIORChan.c --
3 *
4 *      This file contains the implementation of Tcl's generic channel
5 *      reflection code, which allows the implementation of Tcl channels in
6 *      Tcl code.
7 *
8 *      Parts of this file are based on code contributed by Jean-Claude
9 *      Wippler.
10 *
11 *      See TIP #219 for the specification of this functionality.
12 *
13 * Copyright (c) 2004-2005 ActiveState, a divison of Sophos
14 *
15 * See the file "license.terms" for information on usage and redistribution of
16 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
17 *
18 * RCS: @(#) $Id: tclIORChan.c,v 1.28 2008/02/26 21:50:52 jenglish Exp $
19 */
20
21#include <tclInt.h>
22#include <tclIO.h>
23#include <assert.h>
24
25#ifndef EINVAL
26#define EINVAL  9
27#endif
28#ifndef EOK
29#define EOK     0
30#endif
31
32/*
33 * Signatures of all functions used in the C layer of the reflection.
34 */
35
36static int              ReflectClose(ClientData clientData,
37                            Tcl_Interp *interp);
38static int              ReflectInput(ClientData clientData, char *buf,
39                            int toRead, int *errorCodePtr);
40static int              ReflectOutput(ClientData clientData, const char *buf,
41                            int toWrite, int *errorCodePtr);
42static void             ReflectWatch(ClientData clientData, int mask);
43static int              ReflectBlock(ClientData clientData, int mode);
44static Tcl_WideInt      ReflectSeekWide(ClientData clientData,
45                            Tcl_WideInt offset, int mode, int *errorCodePtr);
46static int              ReflectSeek(ClientData clientData, long offset,
47                            int mode, int *errorCodePtr);
48static int              ReflectGetOption(ClientData clientData,
49                            Tcl_Interp *interp, const char *optionName,
50                            Tcl_DString *dsPtr);
51static int              ReflectSetOption(ClientData clientData,
52                            Tcl_Interp *interp, const char *optionName,
53                            const char *newValue);
54
55/*
56 * The C layer channel type/driver definition used by the reflection. This is
57 * a version 3 structure.
58 */
59
60static Tcl_ChannelType tclRChannelType = {
61    "tclrchannel",         /* Type name.                                  */
62    TCL_CHANNEL_VERSION_5, /* v5 channel */
63    ReflectClose,          /* Close channel, clean instance data          */
64    ReflectInput,          /* Handle read request                         */
65    ReflectOutput,         /* Handle write request                        */
66    ReflectSeek,           /* Move location of access point.   NULL'able  */
67    ReflectSetOption,      /* Set options.                     NULL'able  */
68    ReflectGetOption,      /* Get options.                     NULL'able  */
69    ReflectWatch,          /* Initialize notifier                         */
70    NULL,                  /* Get OS handle from the channel.  NULL'able  */
71    NULL,                  /* No close2 support.               NULL'able  */
72    ReflectBlock,          /* Set blocking/nonblocking.        NULL'able  */
73    NULL,                  /* Flush channel. Not used by core. NULL'able  */
74    NULL,                  /* Handle events.                   NULL'able  */
75    ReflectSeekWide,       /* Move access point (64 bit).      NULL'able  */
76    NULL,                  /* thread action */
77    NULL,                  /* truncate */
78};
79
80/*
81 * Instance data for a reflected channel. ===========================
82 */
83
84typedef struct {
85    Tcl_Channel chan;           /* Back reference to generic channel
86                                 * structure. */
87    Tcl_Interp *interp;         /* Reference to the interpreter containing the
88                                 * Tcl level part of the channel. */
89#ifdef TCL_THREADS
90    Tcl_ThreadId thread;        /* Thread the 'interp' belongs to. */
91#endif
92
93    /* See [==] as well.
94     * Storage for the command prefix and the additional words required for
95     * the invocation of methods in the command handler.
96     *
97     * argv [0] ... [.] | [argc-2] [argc-1] | [argc]  [argc+2]
98     *      cmd ... pfx | method   chan     | detail1 detail2
99     *      ~~~~ CT ~~~            ~~ CT ~~
100     *
101     * CT = Belongs to the 'Command handler Thread'.
102     */
103
104    int argc;                   /* Number of preallocated words - 2 */
105    Tcl_Obj **argv;             /* Preallocated array for calling the handler.
106                                 * args[0] is placeholder for cmd word.
107                                 * Followed by the arguments in the prefix,
108                                 * plus 4 placeholders for method, channel,
109                                 * and at most two varying (method specific)
110                                 * words. */
111    int methods;                /* Bitmask of supported methods */
112
113    /*
114     * NOTE (9): Should we have predefined shared literals for the method
115     * names?
116     */
117
118    int mode;                   /* Mask of R/W mode */
119    int interest;               /* Mask of events the channel is interested
120                                 * in. */
121
122    /*
123     * Note regarding the usage of timers.
124     *
125     * Most channel implementations need a timer in the C level to ensure that
126     * data in buffers is flushed out through the generation of fake file
127     * events.
128     *
129     * See 'rechan', 'memchan', etc.
130     *
131     * Here this is _not_ required. Interest in events is posted to the Tcl
132     * level via 'watch'. And posting of events is possible from the Tcl level
133     * as well, via 'chan postevent'. This means that the generation of all
134     * events, fake or not, timer based or not, is completely in the hands of
135     * the Tcl level. Therefore no timer here.
136     */
137} ReflectedChannel;
138
139/*
140 * Structure of the table maping from channel handles to reflected
141 * channels. Each interpreter which has the handler command for one or more
142 * reflected channels records them in such a table, so that 'chan postevent'
143 * is able to find them even if the actual channel was moved to a different
144 * interpreter and/or thread.
145 *
146 * The table is reachable via the standard interpreter AssocData, the key is
147 * defined below.
148 */
149
150typedef struct {
151    Tcl_HashTable map;
152} ReflectedChannelMap;
153
154#define RCMKEY "ReflectedChannelMap"
155
156/*
157 * Event literals. ==================================================
158 */
159
160static const char *eventOptions[] = {
161    "read", "write", NULL
162};
163typedef enum {
164    EVENT_READ, EVENT_WRITE
165} EventOption;
166
167/*
168 * Method literals. ==================================================
169 */
170
171static const char *methodNames[] = {
172    "blocking",         /* OPT */
173    "cget",             /* OPT \/ Together or none */
174    "cgetall",          /* OPT /\ of these two     */
175    "configure",        /* OPT */
176    "finalize",         /*     */
177    "initialize",       /*     */
178    "read",             /* OPT */
179    "seek",             /* OPT */
180    "watch",            /*     */
181    "write",            /* OPT */
182    NULL
183};
184typedef enum {
185    METH_BLOCKING,
186    METH_CGET,
187    METH_CGETALL,
188    METH_CONFIGURE,
189    METH_FINAL,
190    METH_INIT,
191    METH_READ,
192    METH_SEEK,
193    METH_WATCH,
194    METH_WRITE
195} MethodName;
196
197#define FLAG(m) (1 << (m))
198#define REQUIRED_METHODS \
199        (FLAG(METH_INIT) | FLAG(METH_FINAL) | FLAG(METH_WATCH))
200#define NULLABLE_METHODS \
201        (FLAG(METH_BLOCKING) | FLAG(METH_SEEK) | \
202        FLAG(METH_CONFIGURE) | FLAG(METH_CGET) | FLAG(METH_CGETALL))
203
204#define RANDW \
205        (TCL_READABLE | TCL_WRITABLE)
206
207#define IMPLIES(a,b)    ((!(a)) || (b))
208#define NEGIMPL(a,b)
209#define HAS(x,f)        (x & FLAG(f))
210
211#ifdef TCL_THREADS
212/*
213 * Thread specific types and structures.
214 *
215 * We are here essentially creating a very specific implementation of 'thread
216 * send'.
217 */
218
219/*
220 * Enumeration of all operations which can be forwarded.
221 */
222
223typedef enum {
224    ForwardedClose,
225    ForwardedInput,
226    ForwardedOutput,
227    ForwardedSeek,
228    ForwardedWatch,
229    ForwardedBlock,
230    ForwardedSetOpt,
231    ForwardedGetOpt,
232    ForwardedGetOptAll
233} ForwardedOperation;
234
235/*
236 * Event used to forward driver invocations to the thread actually managing
237 * the channel. We cannot construct the command to execute and forward that.
238 * Because then it will contain a mixture of Tcl_Obj's belonging to both the
239 * command handler thread (CT), and the thread managing the channel (MT),
240 * executed in CT. Tcl_Obj's are not allowed to cross thread boundaries. So we
241 * forward an operation code, the argument details, and reference to results.
242 * The command is assembled in the CT and belongs fully to that thread. No
243 * sharing problems.
244 */
245
246typedef struct ForwardParamBase {
247    int code;                   /* O: Ok/Fail of the cmd handler */
248    char *msgStr;               /* O: Error message for handler failure */
249    int mustFree;               /* O: True if msgStr is allocated, false if
250                                 * otherwise (static). */
251} ForwardParamBase;
252
253/*
254 * Operation specific parameter/result structures. (These are "subtypes" of
255 * ForwardParamBase. Where an operation does not need any special types, it
256 * has no "subtype" and just uses ForwardParamBase, as listed above.)
257 */
258
259struct ForwardParamInput {
260    ForwardParamBase base;      /* "Supertype". MUST COME FIRST. */
261    char *buf;                  /* O: Where to store the read bytes */
262    int toRead;                 /* I: #bytes to read,
263                                 * O: #bytes actually read */
264};
265struct ForwardParamOutput {
266    ForwardParamBase base;      /* "Supertype". MUST COME FIRST. */
267    const char *buf;            /* I: Where the bytes to write come from */
268    int toWrite;                /* I: #bytes to write,
269                                 * O: #bytes actually written */
270};
271struct ForwardParamSeek {
272    ForwardParamBase base;      /* "Supertype". MUST COME FIRST. */
273    int seekMode;               /* I: How to seek */
274    Tcl_WideInt offset;         /* I: Where to seek,
275                                 * O: New location */
276};
277struct ForwardParamWatch {
278    ForwardParamBase base;      /* "Supertype". MUST COME FIRST. */
279    int mask;                   /* I: What events to watch for */
280};
281struct ForwardParamBlock {
282    ForwardParamBase base;      /* "Supertype". MUST COME FIRST. */
283    int nonblocking;            /* I: What mode to activate */
284};
285struct ForwardParamSetOpt {
286    ForwardParamBase base;      /* "Supertype". MUST COME FIRST. */
287    const char *name;           /* Name of option to set */
288    const char *value;          /* Value to set */
289};
290struct ForwardParamGetOpt {
291    ForwardParamBase base;      /* "Supertype". MUST COME FIRST. */
292    const char *name;           /* Name of option to get, maybe NULL */
293    Tcl_DString *value;         /* Result */
294};
295
296/*
297 * Now join all these together in a single union for convenience.
298 */
299
300typedef union ForwardParam {
301    ForwardParamBase base;
302    struct ForwardParamInput input;
303    struct ForwardParamOutput output;
304    struct ForwardParamSeek seek;
305    struct ForwardParamWatch watch;
306    struct ForwardParamBlock block;
307    struct ForwardParamSetOpt setOpt;
308    struct ForwardParamGetOpt getOpt;
309} ForwardParam;
310
311/*
312 * Forward declaration.
313 */
314
315typedef struct ForwardingResult ForwardingResult;
316
317/*
318 * General event structure, with reference to operation specific data.
319 */
320
321typedef struct ForwardingEvent {
322    Tcl_Event event;            /* Basic event data, has to be first item */
323    ForwardingResult *resultPtr;
324    ForwardedOperation op;      /* Forwarded driver operation */
325    ReflectedChannel *rcPtr;    /* Channel instance */
326    ForwardParam *param;        /* Packaged arguments and return values, a
327                                 * ForwardParam pointer. */
328} ForwardingEvent;
329
330/*
331 * Structure to manage the result of the forwarding. This is not the result of
332 * the operation itself, but about the success of the forward event itself.
333 * The event can be successful, even if the operation which was forwarded
334 * failed. It is also there to manage the synchronization between the involved
335 * threads.
336 */
337
338struct ForwardingResult {
339    Tcl_ThreadId src;           /* Originating thread. */
340    Tcl_ThreadId dst;           /* Thread the op was forwarded to. */
341    Tcl_Condition done;         /* Condition variable the forwarder blocks
342                                 * on. */
343    int result;                 /* TCL_OK or TCL_ERROR */
344    ForwardingEvent *evPtr;     /* Event the result belongs to. */
345    ForwardingResult *prevPtr, *nextPtr;
346                                /* Links into the list of pending forwarded
347                                 * results. */
348};
349
350/*
351 * List of forwarded operations which have not completed yet, plus the mutex
352 * to protect the access to this process global list.
353 */
354
355static ForwardingResult *forwardList = NULL;
356TCL_DECLARE_MUTEX(rcForwardMutex)
357
358/*
359 * Function containing the generic code executing a forward, and wrapper
360 * macros for the actual operations we wish to forward. Uses ForwardProc as
361 * the event function executed by the thread receiving a forwarding event
362 * (which executes the appropriate function and collects the result, if any).
363 *
364 * The two ExitProcs are handlers so that things do not deadlock when either
365 * thread involved in the forwarding exits. They also clean things up so that
366 * we don't leak resources when threads go away.
367 */
368
369static void             ForwardOpToOwnerThread(ReflectedChannel *rcPtr,
370                            ForwardedOperation op, const VOID *param);
371static int              ForwardProc(Tcl_Event *evPtr, int mask);
372static void             SrcExitProc(ClientData clientData);
373static void             DstExitProc(ClientData clientData);
374
375#define FreeReceivedError(p) \
376        if ((p)->base.mustFree) { \
377            ckfree((p)->base.msgStr); \
378        }
379#define PassReceivedErrorInterp(i,p) \
380        if ((i) != NULL) { \
381            Tcl_SetChannelErrorInterp((i), \
382                    Tcl_NewStringObj((p)->base.msgStr, -1)); \
383        } \
384        FreeReceivedError(p)
385#define PassReceivedError(c,p) \
386        Tcl_SetChannelError((c), Tcl_NewStringObj((p)->base.msgStr, -1)); \
387        FreeReceivedError(p)
388#define ForwardSetStaticError(p,emsg) \
389        (p)->base.code = TCL_ERROR; \
390        (p)->base.mustFree = 0; \
391        (p)->base.msgStr = (char *) (emsg)
392#define ForwardSetDynamicError(p,emsg) \
393        (p)->base.code = TCL_ERROR; \
394        (p)->base.mustFree = 1; \
395        (p)->base.msgStr = (char *) (emsg)
396
397static void             ForwardSetObjError(ForwardParam *p, Tcl_Obj *objPtr);
398#endif /* TCL_THREADS */
399
400#define SetChannelErrorStr(c,msgStr) \
401        Tcl_SetChannelError((c), Tcl_NewStringObj((msgStr), -1))
402
403static Tcl_Obj *        MarshallError(Tcl_Interp *interp);
404static void             UnmarshallErrorResult(Tcl_Interp *interp,
405                            Tcl_Obj *msgObj);
406
407/*
408 * Static functions for this file:
409 */
410
411static int              EncodeEventMask(Tcl_Interp *interp,
412                            const char *objName, Tcl_Obj *obj, int *mask);
413static Tcl_Obj *        DecodeEventMask(int mask);
414static ReflectedChannel * NewReflectedChannel(Tcl_Interp *interp,
415                            Tcl_Obj *cmdpfxObj, int mode, Tcl_Obj *handleObj);
416static Tcl_Obj *        NextHandle(void);
417static void             FreeReflectedChannel(ReflectedChannel *rcPtr);
418static int              InvokeTclMethod(ReflectedChannel *rcPtr,
419                            const char *method, Tcl_Obj *argOneObj,
420                            Tcl_Obj *argTwoObj, Tcl_Obj **resultObjPtr);
421
422static ReflectedChannelMap *    GetReflectedChannelMap(Tcl_Interp *interp);
423static void             DeleteReflectedChannelMap(ClientData clientData,
424                            Tcl_Interp *interp);
425
426/*
427 * Global constant strings (messages). ==================
428 * These string are used directly as bypass errors, thus they have to be valid
429 * Tcl lists where the last element is the message itself. Hence the
430 * list-quoting to keep the words of the message together. See also [x].
431 */
432
433static const char *msg_read_unsup = "{read not supported by Tcl driver}";
434static const char *msg_read_toomuch = "{read delivered more than requested}";
435static const char *msg_write_unsup = "{write not supported by Tcl driver}";
436static const char *msg_write_toomuch = "{write wrote more than requested}";
437static const char *msg_seek_beforestart = "{Tried to seek before origin}";
438#ifdef TCL_THREADS
439static const char *msg_send_originlost = "{Origin thread lost}";
440static const char *msg_send_dstlost = "{Destination thread lost}";
441#endif /* TCL_THREADS */
442
443/*
444 * Main methods to plug into the 'chan' ensemble'. ==================
445 */
446
447/*
448 *----------------------------------------------------------------------
449 *
450 * TclChanCreateObjCmd --
451 *
452 *      This function is invoked to process the "chan create" Tcl command.
453 *      See the user documentation for details on what it does.
454 *
455 * Results:
456 *      A standard Tcl result. The handle of the new channel is placed in the
457 *      interp result.
458 *
459 * Side effects:
460 *      Creates a new channel.
461 *
462 *----------------------------------------------------------------------
463 */
464
465int
466TclChanCreateObjCmd(
467    ClientData clientData,
468    Tcl_Interp *interp,
469    int objc,
470    Tcl_Obj *const *objv)
471{
472    ReflectedChannel *rcPtr;    /* Instance data of the new channel */
473    Tcl_Obj *rcId;              /* Handle of the new channel */
474    int mode;                   /* R/W mode of new channel. Has to match
475                                 * abilities of handler commands */
476    Tcl_Obj *cmdObj;            /* Command prefix, list of words */
477    Tcl_Obj *cmdNameObj;        /* Command name */
478    Tcl_Channel chan;           /* Token for the new channel */
479    Tcl_Obj *modeObj;           /* mode in obj form for method call */
480    int listc;                  /* Result of 'initialize', and of */
481    Tcl_Obj **listv;            /* its sublist in the 2nd element */
482    int methIndex;              /* Encoded method name */
483    int result;                 /* Result code for 'initialize' */
484    Tcl_Obj *resObj;            /* Result data for 'initialize' */
485    int methods;                /* Bitmask for supported methods. */
486    Channel *chanPtr;           /* 'chan' resolved to internal struct. */
487    Tcl_Obj *err;               /* Error message */
488    ReflectedChannelMap* rcmPtr; /* Map of reflected channels with handlers in this interp */
489    Tcl_HashEntry* hPtr;         /* Entry in the above map */
490    int isNew;                   /* Placeholder. */
491
492    /*
493     * Syntax:   chan create MODE CMDPREFIX
494     *           [0]  [1]    [2]  [3]
495     *
496     * Actually: rCreate MODE CMDPREFIX
497     *           [0]     [1]  [2]
498     */
499
500#define MODE    (1)
501#define CMD     (2)
502
503    /*
504     * Number of arguments...
505     */
506
507    if (objc != 3) {
508        Tcl_WrongNumArgs(interp, 1, objv, "mode cmdprefix");
509        return TCL_ERROR;
510    }
511
512    /*
513     * First argument is a list of modes. Allowed entries are "read", "write".
514     * Expect at least one list element. Abbreviations are ok.
515     */
516
517    modeObj = objv[MODE];
518    if (EncodeEventMask(interp, "mode", objv[MODE], &mode) != TCL_OK) {
519        return TCL_ERROR;
520    }
521
522    /*
523     * Second argument is command prefix, i.e. list of words, first word is
524     * name of handler command, other words are fixed arguments. Run the
525     * 'initialize' method to get the list of supported methods. Validate
526     * this.
527     */
528
529    cmdObj = objv[CMD];
530
531    /*
532     * Basic check that the command prefix truly is a list.
533     */
534
535    if (Tcl_ListObjIndex(interp, cmdObj, 0, &cmdNameObj) != TCL_OK) {
536        return TCL_ERROR;
537    }
538
539    /*
540     * Now create the channel.
541     */
542
543    rcId = NextHandle();
544    rcPtr = NewReflectedChannel(interp, cmdObj, mode, rcId);
545    chan = Tcl_CreateChannel(&tclRChannelType, TclGetString(rcId), rcPtr,
546            mode);
547    rcPtr->chan = chan;
548    chanPtr = (Channel *) chan;
549
550    /*
551     * Invoke 'initialize' and validate that the handler is present and ok.
552     * Squash the channel if not.
553     *
554     * Note: The conversion of 'mode' back into a Tcl_Obj ensures that
555     * 'initialize' is invoked with canonical mode names, and no
556     * abbreviations. Using modeObj directly could feed abbreviations into the
557     * handler, and the handler is not specified to handle such.
558     */
559
560    modeObj = DecodeEventMask(mode);
561    result = InvokeTclMethod(rcPtr, "initialize", modeObj, NULL, &resObj);
562    Tcl_DecrRefCount(modeObj);
563    if (result != TCL_OK) {
564        UnmarshallErrorResult(interp, resObj);
565        Tcl_DecrRefCount(resObj);       /* Remove reference held from invoke */
566        goto error;
567    }
568
569    /*
570     * Verify the result.
571     * - List, of method names. Convert to mask.
572     *   Check for non-optionals through the mask.
573     *   Compare open mode against optional r/w.
574     */
575
576    if (Tcl_ListObjGetElements(NULL, resObj, &listc, &listv) != TCL_OK) {
577        TclNewLiteralStringObj(err, "chan handler \"");
578        Tcl_AppendObjToObj(err, cmdObj);
579        Tcl_AppendToObj(err, " initialize\" returned non-list: ", -1);
580        Tcl_AppendObjToObj(err, resObj);
581        Tcl_SetObjResult(interp, err);
582        Tcl_DecrRefCount(resObj);
583        goto error;
584    }
585
586    methods = 0;
587    while (listc > 0) {
588        if (Tcl_GetIndexFromObj(interp, listv[listc-1], methodNames,
589                "method", TCL_EXACT, &methIndex) != TCL_OK) {
590            TclNewLiteralStringObj(err, "chan handler \"");
591            Tcl_AppendObjToObj(err, cmdObj);
592            Tcl_AppendToObj(err, " initialize\" returned ", -1);
593            Tcl_AppendObjToObj(err, Tcl_GetObjResult(interp));
594            Tcl_SetObjResult(interp, err);
595            Tcl_DecrRefCount(resObj);
596            goto error;
597        }
598
599        methods |= FLAG(methIndex);
600        listc--;
601    }
602    Tcl_DecrRefCount(resObj);
603
604    if ((REQUIRED_METHODS & methods) != REQUIRED_METHODS) {
605        TclNewLiteralStringObj(err, "chan handler \"");
606        Tcl_AppendObjToObj(err, cmdObj);
607        Tcl_AppendToObj(err, "\" does not support all required methods", -1);
608        Tcl_SetObjResult(interp, err);
609        goto error;
610    }
611
612    if ((mode & TCL_READABLE) && !HAS(methods, METH_READ)) {
613        TclNewLiteralStringObj(err, "chan handler \"");
614        Tcl_AppendObjToObj(err, cmdObj);
615        Tcl_AppendToObj(err, "\" lacks a \"read\" method", -1);
616        Tcl_SetObjResult(interp, err);
617        goto error;
618    }
619
620    if ((mode & TCL_WRITABLE) && !HAS(methods, METH_WRITE)) {
621        TclNewLiteralStringObj(err, "chan handler \"");
622        Tcl_AppendObjToObj(err, cmdObj);
623        Tcl_AppendToObj(err, "\" lacks a \"write\" method", -1);
624        Tcl_SetObjResult(interp, err);
625        goto error;
626    }
627
628    if (!IMPLIES(HAS(methods, METH_CGET), HAS(methods, METH_CGETALL))) {
629        TclNewLiteralStringObj(err, "chan handler \"");
630        Tcl_AppendObjToObj(err, cmdObj);
631        Tcl_AppendToObj(err, "\" supports \"cget\" but not \"cgetall\"", -1);
632        Tcl_SetObjResult(interp, err);
633        goto error;
634    }
635
636    if (!IMPLIES(HAS(methods, METH_CGETALL), HAS(methods, METH_CGET))) {
637        TclNewLiteralStringObj(err, "chan handler \"");
638        Tcl_AppendObjToObj(err, cmdObj);
639        Tcl_AppendToObj(err, "\" supports \"cgetall\" but not \"cget\"", -1);
640        Tcl_SetObjResult(interp, err);
641        goto error;
642    }
643
644    Tcl_ResetResult(interp);
645
646    /*
647     * Everything is fine now.
648     */
649
650    rcPtr->methods = methods;
651
652    if ((methods & NULLABLE_METHODS) != NULLABLE_METHODS) {
653        /*
654         * Some of the nullable methods are not supported. We clone the
655         * channel type, null the associated C functions, and use the result
656         * as the actual channel type.
657         */
658
659        Tcl_ChannelType *clonePtr = (Tcl_ChannelType *)
660                ckalloc(sizeof(Tcl_ChannelType));
661
662        memcpy(clonePtr, &tclRChannelType, sizeof(Tcl_ChannelType));
663
664        if (!(methods & FLAG(METH_CONFIGURE))) {
665            clonePtr->setOptionProc = NULL;
666        }
667
668        if (!(methods & FLAG(METH_CGET)) && !(methods & FLAG(METH_CGETALL))) {
669            clonePtr->getOptionProc = NULL;
670        }
671        if (!(methods & FLAG(METH_BLOCKING))) {
672            clonePtr->blockModeProc = NULL;
673        }
674        if (!(methods & FLAG(METH_SEEK))) {
675            clonePtr->seekProc = NULL;
676            clonePtr->wideSeekProc = NULL;
677        }
678
679        chanPtr->typePtr = clonePtr;
680    }
681
682    /*
683     * Register the channel in the I/O system, and in our our map for 'chan
684     * postevent'.
685     */
686
687    Tcl_RegisterChannel(interp, chan);
688
689    rcmPtr = GetReflectedChannelMap (interp);
690    hPtr   = Tcl_CreateHashEntry(&rcmPtr->map,
691                                 chanPtr->state->channelName, &isNew);
692    if (!isNew) {
693        if (chanPtr != Tcl_GetHashValue(hPtr)) {
694            Tcl_Panic("TclChanCreateObjCmd: duplicate channel names");
695        }
696    }
697    Tcl_SetHashValue(hPtr, chan);
698
699    /*
700     * Return handle as result of command.
701     */
702
703    Tcl_SetObjResult(interp, rcId);
704    return TCL_OK;
705
706 error:
707    /*
708     * Signal to ReflectClose to not call 'finalize'.
709     */
710
711    rcPtr->methods = 0;
712    Tcl_Close(interp, chan);
713    return TCL_ERROR;
714
715#undef MODE
716#undef CMD
717}
718
719/*
720 *----------------------------------------------------------------------
721 *
722 * TclChanPostEventObjCmd --
723 *
724 *      This function is invoked to process the "chan postevent" Tcl command.
725 *      See the user documentation for details on what it does.
726 *
727 * Results:
728 *      A standard Tcl result.
729 *
730 * Side effects:
731 *      Posts events to a reflected channel, invokes event handlers. The
732 *      latter implies that arbitrary side effects are possible.
733 *
734 *----------------------------------------------------------------------
735 */
736
737int
738TclChanPostEventObjCmd(
739    ClientData clientData,
740    Tcl_Interp *interp,
741    int objc,
742    Tcl_Obj *const *objv)
743{
744    /*
745     * Syntax:   chan postevent CHANNEL EVENTSPEC
746     *           [0]  [1]       [2]     [3]
747     *
748     * Actually: rPostevent CHANNEL EVENTSPEC
749     *           [0]        [1]     [2]
750     *
751     * where EVENTSPEC = {read write ...} (Abbreviations allowed as well).
752     */
753
754#define CHAN    (1)
755#define EVENT   (2)
756
757    const char *chanId;         /* Tcl level channel handle */
758    Tcl_Channel chan;           /* Channel associated to the handle */
759    const Tcl_ChannelType *chanTypePtr;
760                                /* Its associated driver structure */
761    ReflectedChannel *rcPtr;    /* Associated instance data */
762    int events;                 /* Mask of events to post */
763    ReflectedChannelMap* rcmPtr; /* Map of reflected channels with handlers in this interp */
764    Tcl_HashEntry* hPtr;         /* Entry in the above map */
765
766    /*
767     * Number of arguments...
768     */
769
770    if (objc != 3) {
771        Tcl_WrongNumArgs(interp, 1, objv, "channel eventspec");
772        return TCL_ERROR;
773    }
774
775    /*
776     * First argument is a channel, a reflected channel, and the call of this
777     * command is done from the interp defining the channel handler cmd.
778     */
779
780    chanId = TclGetString(objv[CHAN]);
781
782    rcmPtr = GetReflectedChannelMap (interp);
783    hPtr = Tcl_FindHashEntry (&rcmPtr->map, chanId);
784
785    if (hPtr == NULL) {
786        Tcl_AppendResult(interp, "can not find reflected channel named \"", chanId,
787                "\"", NULL);
788        Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "CHANNEL", chanId, NULL);
789        return TCL_ERROR;
790    }
791
792    /*
793     * Note that the search above subsumes several of the older checks, namely:
794     *
795     * (1) Does the channel handle refer to a reflected channel ?
796     * (2) Is the post event issued from the interpreter holding the handler
797     *     of the reflected channel ?
798     *
799     * A successful search answers yes to both. Because the map holds only
800     * handles of reflected channels, and only of such whose handler is
801     * defined in this interpreter.
802     *
803     * We keep the old checks for both, for paranioa, but abort now instead of
804     * throwing errors, as failure now means that our internal datastructures
805     * have gone seriously haywire.
806     */
807
808    chan        = Tcl_GetHashValue(hPtr);
809    chanTypePtr = Tcl_GetChannelType(chan);
810
811    /*
812     * We use a function referenced by the channel type as our cookie to
813     * detect calls to non-reflecting channels. The channel type itself is not
814     * suitable, as it might not be the static definition in this file, but a
815     * clone thereof. And while we have reserved the name of the type nothing
816     * in the core checks against violation, so someone else might have
817     * created a channel type using our name, clashing with ourselves.
818     */
819
820    if (chanTypePtr->watchProc != &ReflectWatch) {
821        Tcl_Panic ("TclChanPostEventObjCmd: channel is not a reflected channel");
822    }
823
824    rcPtr = (ReflectedChannel *) Tcl_GetChannelInstanceData(chan);
825
826    if (rcPtr->interp != interp) {
827        Tcl_Panic ("TclChanPostEventObjCmd: postevent accepted for call from outside interpreter");
828    }
829
830    /*
831     * Second argument is a list of events. Allowed entries are "read",
832     * "write". Expect at least one list element. Abbreviations are ok.
833     */
834
835    if (EncodeEventMask(interp, "event", objv[EVENT], &events) != TCL_OK) {
836        return TCL_ERROR;
837    }
838
839    /*
840     * Check that the channel is actually interested in the provided events.
841     */
842
843    if (events & ~rcPtr->interest) {
844        Tcl_AppendResult(interp, "tried to post events channel \"", chanId,
845                "\" is not interested in", NULL);
846        return TCL_ERROR;
847    }
848
849    /*
850     * We have the channel and the events to post.
851     */
852
853    Tcl_NotifyChannel(chan, events);
854
855    /*
856     * Squash interp results left by the event script.
857     */
858
859    Tcl_ResetResult(interp);
860    return TCL_OK;
861
862#undef CHAN
863#undef EVENT
864}
865
866/*
867 * Channel error message marshalling utilities.
868 */
869
870static Tcl_Obj*
871MarshallError(
872    Tcl_Interp *interp)
873{
874    /*
875     * Capture the result status of the interpreter into a string. => List of
876     * options and values, followed by the error message. The result has
877     * refCount 0.
878     */
879
880    Tcl_Obj *returnOpt = Tcl_GetReturnOptions(interp, TCL_ERROR);
881
882    /*
883     * => returnOpt.refCount == 0. We can append directly.
884     */
885
886    Tcl_ListObjAppendElement(NULL, returnOpt, Tcl_GetObjResult(interp));
887    return returnOpt;
888}
889
890static void
891UnmarshallErrorResult(
892    Tcl_Interp *interp,
893    Tcl_Obj *msgObj)
894{
895    int lc;
896    Tcl_Obj **lv;
897    int explicitResult;
898    int numOptions;
899
900    /*
901     * Process the caught message.
902     *
903     * Syntax = (option value)... ?message?
904     *
905     * Bad syntax causes a panic. This is OK because the other side uses
906     * Tcl_GetReturnOptions and list construction functions to marshall the
907     * information; if we panic here, something has gone badly wrong already.
908     */
909
910    if (Tcl_ListObjGetElements(interp, msgObj, &lc, &lv) != TCL_OK) {
911        Tcl_Panic("TclChanCaughtErrorBypass: Bad syntax of caught result");
912    }
913    if (interp == NULL) {
914        return;
915    }
916
917    explicitResult = lc & 1;            /* Odd number of values? */
918    numOptions = lc - explicitResult;
919
920    if (explicitResult) {
921        Tcl_SetObjResult(interp, lv[lc-1]);
922    }
923
924    (void) Tcl_SetReturnOptions(interp, Tcl_NewListObj(numOptions, lv));
925    ((Interp *)interp)->flags &= ~ERR_ALREADY_LOGGED;
926}
927
928int
929TclChanCaughtErrorBypass(
930    Tcl_Interp *interp,
931    Tcl_Channel chan)
932{
933    Tcl_Obj *chanMsgObj = NULL;
934    Tcl_Obj *interpMsgObj = NULL;
935    Tcl_Obj *msgObj = NULL;
936
937    /*
938     * Get a bypassed error message from channel and/or interpreter, save the
939     * reference, then kill the returned objects, if there were any. If there
940     * are messages in both the channel has preference.
941     */
942
943    if ((chan == NULL) && (interp == NULL)) {
944        return 0;
945    }
946
947    if (chan != NULL) {
948        Tcl_GetChannelError(chan, &chanMsgObj);
949    }
950    if (interp != NULL) {
951        Tcl_GetChannelErrorInterp(interp, &interpMsgObj);
952    }
953
954    if (chanMsgObj != NULL) {
955        msgObj = chanMsgObj;
956    } else if (interpMsgObj != NULL) {
957        msgObj = interpMsgObj;
958    }
959    if (msgObj != NULL) {
960        Tcl_IncrRefCount(msgObj);
961    }
962
963    if (chanMsgObj != NULL) {
964        Tcl_DecrRefCount(chanMsgObj);
965    }
966    if (interpMsgObj != NULL) {
967        Tcl_DecrRefCount(interpMsgObj);
968    }
969
970    /*
971     * No message returned, nothing caught.
972     */
973
974    if (msgObj == NULL) {
975        return 0;
976    }
977
978    UnmarshallErrorResult(interp, msgObj);
979
980    Tcl_DecrRefCount(msgObj);
981    return 1;
982}
983
984/*
985 * Driver functions. ================================================
986 */
987
988/*
989 *----------------------------------------------------------------------
990 *
991 * ReflectClose --
992 *
993 *      This function is invoked when the channel is closed, to delete the
994 *      driver specific instance data.
995 *
996 * Results:
997 *      A posix error.
998 *
999 * Side effects:
1000 *      Releases memory. Arbitrary, as it calls upon a script.
1001 *
1002 *----------------------------------------------------------------------
1003 */
1004
1005static int
1006ReflectClose(
1007    ClientData clientData,
1008    Tcl_Interp *interp)
1009{
1010    ReflectedChannel *rcPtr = (ReflectedChannel *) clientData;
1011    int result;                 /* Result code for 'close' */
1012    Tcl_Obj *resObj;            /* Result data for 'close' */
1013
1014    if (interp == NULL) {
1015        /*
1016         * This call comes from TclFinalizeIOSystem. There are no
1017         * interpreters, and therefore we cannot call upon the handler command
1018         * anymore. Threading is irrelevant as well. We simply clean up all
1019         * our C level data structures and leave the Tcl level to the other
1020         * finalization functions.
1021         */
1022
1023        /*
1024         * THREADED => Forward this to the origin thread
1025         *
1026         * Note: Have a thread delete handler for the origin thread. Use this
1027         * to clean up the structure!
1028         */
1029
1030#ifdef TCL_THREADS
1031        if (rcPtr->thread != Tcl_GetCurrentThread()) {
1032            ForwardParam p;
1033
1034            ForwardOpToOwnerThread(rcPtr, ForwardedClose, &p);
1035            result = p.base.code;
1036
1037            /*
1038             * FreeReflectedChannel is done in the forwarded operation!, in
1039             * the other thread. rcPtr here is gone!
1040             */
1041
1042            if (result != TCL_OK) {
1043                FreeReceivedError(&p);
1044            }
1045            return EOK;
1046        }
1047#endif
1048
1049        FreeReflectedChannel(rcPtr);
1050        return EOK;
1051    }
1052
1053    /*
1054     * -- No -- ASSERT rcPtr->methods & FLAG(METH_FINAL)
1055     *
1056     * A cleaned method mask here implies that the channel creation was
1057     * aborted, and "finalize" must not be called.
1058     */
1059
1060    if (rcPtr->methods == 0) {
1061        FreeReflectedChannel(rcPtr);
1062        return EOK;
1063    }
1064
1065    /*
1066     * Are we in the correct thread?
1067     */
1068
1069#ifdef TCL_THREADS
1070    if (rcPtr->thread != Tcl_GetCurrentThread()) {
1071        ForwardParam p;
1072
1073        ForwardOpToOwnerThread(rcPtr, ForwardedClose, &p);
1074        result = p.base.code;
1075
1076        /*
1077         * FreeReflectedChannel is done in the forwarded operation!, in the
1078         * other thread. rcPtr here is gone!
1079         */
1080
1081        if (result != TCL_OK) {
1082            PassReceivedErrorInterp(interp, &p);
1083        }
1084    } else {
1085#endif
1086        result = InvokeTclMethod(rcPtr, "finalize", NULL, NULL, &resObj);
1087        if ((result != TCL_OK) && (interp != NULL)) {
1088            Tcl_SetChannelErrorInterp(interp, resObj);
1089        }
1090
1091        Tcl_DecrRefCount(resObj);       /* Remove reference we held from the
1092                                         * invoke */
1093        FreeReflectedChannel(rcPtr);
1094#ifdef TCL_THREADS
1095    }
1096#endif
1097    return (result == TCL_OK) ? EOK : EINVAL;
1098}
1099
1100/*
1101 *----------------------------------------------------------------------
1102 *
1103 * ReflectInput --
1104 *
1105 *      This function is invoked when more data is requested from the channel.
1106 *
1107 * Results:
1108 *      The number of bytes read.
1109 *
1110 * Side effects:
1111 *      Allocates memory. Arbitrary, as it calls upon a script.
1112 *
1113 *----------------------------------------------------------------------
1114 */
1115
1116static int
1117ReflectInput(
1118    ClientData clientData,
1119    char *buf,
1120    int toRead,
1121    int *errorCodePtr)
1122{
1123    ReflectedChannel *rcPtr = (ReflectedChannel *) clientData;
1124    Tcl_Obj *toReadObj;
1125    int bytec;                  /* Number of returned bytes */
1126    unsigned char *bytev;       /* Array of returned bytes */
1127    Tcl_Obj *resObj;            /* Result data for 'read' */
1128
1129    /*
1130     * The following check can be done before thread redirection, because we
1131     * are reading from an item which is readonly, i.e. will never change
1132     * during the lifetime of the channel.
1133     */
1134
1135    if (!(rcPtr->methods & FLAG(METH_READ))) {
1136        SetChannelErrorStr(rcPtr->chan, msg_read_unsup);
1137        *errorCodePtr = EINVAL;
1138        return -1;
1139    }
1140
1141    /*
1142     * Are we in the correct thread?
1143     */
1144
1145#ifdef TCL_THREADS
1146    if (rcPtr->thread != Tcl_GetCurrentThread()) {
1147        ForwardParam p;
1148
1149        p.input.buf = buf;
1150        p.input.toRead = toRead;
1151
1152        ForwardOpToOwnerThread(rcPtr, ForwardedInput, &p);
1153
1154        if (p.base.code != TCL_OK) {
1155            PassReceivedError(rcPtr->chan, &p);
1156            *errorCodePtr = EINVAL;
1157        } else {
1158            *errorCodePtr = EOK;
1159        }
1160
1161        return p.input.toRead;
1162    }
1163#endif
1164
1165    /* ASSERT: rcPtr->method & FLAG(METH_READ) */
1166    /* ASSERT: rcPtr->mode & TCL_READABLE */
1167
1168    toReadObj = Tcl_NewIntObj(toRead);
1169    if (InvokeTclMethod(rcPtr, "read", toReadObj, NULL, &resObj)!=TCL_OK) {
1170        Tcl_SetChannelError(rcPtr->chan, resObj);
1171        Tcl_DecrRefCount(resObj);       /* Remove reference held from invoke */
1172        *errorCodePtr = EINVAL;
1173        return -1;
1174    }
1175
1176    bytev = Tcl_GetByteArrayFromObj(resObj, &bytec);
1177
1178    if (toRead < bytec) {
1179        Tcl_DecrRefCount(resObj);       /* Remove reference held from invoke */
1180        SetChannelErrorStr(rcPtr->chan, msg_read_toomuch);
1181        *errorCodePtr = EINVAL;
1182        return -1;
1183    }
1184
1185    *errorCodePtr = EOK;
1186
1187    if (bytec > 0) {
1188        memcpy(buf, bytev, (size_t)bytec);
1189    }
1190
1191    Tcl_DecrRefCount(resObj);           /* Remove reference held from invoke */
1192    return bytec;
1193}
1194
1195/*
1196 *----------------------------------------------------------------------
1197 *
1198 * ReflectOutput --
1199 *
1200 *      This function is invoked when data is writen to the channel.
1201 *
1202 * Results:
1203 *      The number of bytes actually written.
1204 *
1205 * Side effects:
1206 *      Allocates memory. Arbitrary, as it calls upon a script.
1207 *
1208 *----------------------------------------------------------------------
1209 */
1210
1211static int
1212ReflectOutput(
1213    ClientData clientData,
1214    const char *buf,
1215    int toWrite,
1216    int *errorCodePtr)
1217{
1218    ReflectedChannel *rcPtr = (ReflectedChannel *) clientData;
1219    Tcl_Obj *bufObj;
1220    Tcl_Obj *resObj;            /* Result data for 'write' */
1221    int written;
1222
1223    /*
1224     * The following check can be done before thread redirection, because we
1225     * are reading from an item which is readonly, i.e. will never change
1226     * during the lifetime of the channel.
1227     */
1228
1229    if (!(rcPtr->methods & FLAG(METH_WRITE))) {
1230        SetChannelErrorStr(rcPtr->chan, msg_write_unsup);
1231        *errorCodePtr = EINVAL;
1232        return -1;
1233    }
1234
1235    /*
1236     * Are we in the correct thread?
1237     */
1238
1239#ifdef TCL_THREADS
1240    if (rcPtr->thread != Tcl_GetCurrentThread()) {
1241        ForwardParam p;
1242
1243        p.output.buf = buf;
1244        p.output.toWrite = toWrite;
1245
1246        ForwardOpToOwnerThread(rcPtr, ForwardedOutput, &p);
1247
1248        if (p.base.code != TCL_OK) {
1249            PassReceivedError(rcPtr->chan, &p);
1250            *errorCodePtr = EINVAL;
1251        } else {
1252            *errorCodePtr = EOK;
1253        }
1254
1255        return p.output.toWrite;
1256    }
1257#endif
1258
1259    /* ASSERT: rcPtr->method & FLAG(METH_WRITE) */
1260    /* ASSERT: rcPtr->mode & TCL_WRITABLE */
1261
1262    bufObj = Tcl_NewByteArrayObj((unsigned char *) buf, toWrite);
1263    if (InvokeTclMethod(rcPtr, "write", bufObj, NULL, &resObj) != TCL_OK) {
1264        Tcl_SetChannelError(rcPtr->chan, resObj);
1265        Tcl_DecrRefCount(resObj);       /* Remove reference held from invoke */
1266        *errorCodePtr = EINVAL;
1267        return -1;
1268    }
1269
1270    if (Tcl_GetIntFromObj(rcPtr->interp, resObj, &written) != TCL_OK) {
1271        Tcl_DecrRefCount(resObj);       /* Remove reference held from invoke */
1272        Tcl_SetChannelError(rcPtr->chan, MarshallError(rcPtr->interp));
1273        *errorCodePtr = EINVAL;
1274        return -1;
1275    }
1276
1277    Tcl_DecrRefCount(resObj);           /* Remove reference held from invoke */
1278
1279    if ((written == 0) || (toWrite < written)) {
1280        /*
1281         * The handler claims to have written more than it was given. That is
1282         * bad. Note that the I/O core would crash if we were to return this
1283         * information, trying to write -nnn bytes in the next iteration.
1284         */
1285
1286        SetChannelErrorStr(rcPtr->chan, msg_write_toomuch);
1287        *errorCodePtr = EINVAL;
1288        return -1;
1289    }
1290
1291    *errorCodePtr = EOK;
1292    return written;
1293}
1294
1295/*
1296 *----------------------------------------------------------------------
1297 *
1298 * ReflectSeekWide / ReflectSeek --
1299 *
1300 *      This function is invoked when the user wishes to seek on the channel.
1301 *
1302 * Results:
1303 *      The new location of the access point.
1304 *
1305 * Side effects:
1306 *      Allocates memory. Arbitrary, as it calls upon a script.
1307 *
1308 *----------------------------------------------------------------------
1309 */
1310
1311static Tcl_WideInt
1312ReflectSeekWide(
1313    ClientData clientData,
1314    Tcl_WideInt offset,
1315    int seekMode,
1316    int *errorCodePtr)
1317{
1318    ReflectedChannel *rcPtr = (ReflectedChannel *) clientData;
1319    Tcl_Obj *offObj, *baseObj;
1320    Tcl_Obj *resObj;            /* Result for 'seek' */
1321    Tcl_WideInt newLoc;
1322
1323    /*
1324     * Are we in the correct thread?
1325     */
1326
1327#ifdef TCL_THREADS
1328    if (rcPtr->thread != Tcl_GetCurrentThread()) {
1329        ForwardParam p;
1330
1331        p.seek.seekMode = seekMode;
1332        p.seek.offset = offset;
1333
1334        ForwardOpToOwnerThread(rcPtr, ForwardedSeek, &p);
1335
1336        if (p.base.code != TCL_OK) {
1337            PassReceivedError(rcPtr->chan, &p);
1338            *errorCodePtr = EINVAL;
1339        } else {
1340            *errorCodePtr = EOK;
1341        }
1342
1343        return p.seek.offset;
1344    }
1345#endif
1346
1347    /* ASSERT: rcPtr->method & FLAG(METH_SEEK) */
1348
1349    offObj = Tcl_NewWideIntObj(offset);
1350    baseObj = Tcl_NewStringObj((seekMode == SEEK_SET) ? "start" :
1351            ((seekMode == SEEK_CUR) ? "current" : "end"), -1);
1352    if (InvokeTclMethod(rcPtr, "seek", offObj, baseObj, &resObj)!=TCL_OK) {
1353        Tcl_SetChannelError(rcPtr->chan, resObj);
1354        Tcl_DecrRefCount(resObj);       /* Remove reference held from invoke */
1355        *errorCodePtr = EINVAL;
1356        return -1;
1357    }
1358
1359    if (Tcl_GetWideIntFromObj(rcPtr->interp, resObj, &newLoc) != TCL_OK) {
1360        Tcl_DecrRefCount(resObj);       /* Remove reference held from invoke */
1361        Tcl_SetChannelError(rcPtr->chan, MarshallError(rcPtr->interp));
1362        *errorCodePtr = EINVAL;
1363        return -1;
1364    }
1365
1366    Tcl_DecrRefCount(resObj);           /* Remove reference held from invoke */
1367
1368    if (newLoc < Tcl_LongAsWide(0)) {
1369        SetChannelErrorStr(rcPtr->chan, msg_seek_beforestart);
1370        *errorCodePtr = EINVAL;
1371        return -1;
1372    }
1373
1374    *errorCodePtr = EOK;
1375    return newLoc;
1376}
1377
1378static int
1379ReflectSeek(
1380    ClientData clientData,
1381    long offset,
1382    int seekMode,
1383    int *errorCodePtr)
1384{
1385    /*
1386     * This function can be invoked from a transformation which is based on
1387     * standard seeking, i.e. non-wide. Because of this we have to implement
1388     * it, a dummy is not enough. We simply delegate the call to the wide
1389     * routine.
1390     */
1391
1392    return (int) ReflectSeekWide(clientData, Tcl_LongAsWide(offset), seekMode,
1393            errorCodePtr);
1394}
1395
1396/*
1397 *----------------------------------------------------------------------
1398 *
1399 * ReflectWatch --
1400 *
1401 *      This function is invoked to tell the channel what events the I/O
1402 *      system is interested in.
1403 *
1404 * Results:
1405 *      None.
1406 *
1407 * Side effects:
1408 *      Allocates memory. Arbitrary, as it calls upon a script.
1409 *
1410 *----------------------------------------------------------------------
1411 */
1412
1413static void
1414ReflectWatch(
1415    ClientData clientData,
1416    int mask)
1417{
1418    ReflectedChannel *rcPtr = (ReflectedChannel *) clientData;
1419    Tcl_Obj *maskObj;
1420
1421    /* ASSERT rcPtr->methods & FLAG(METH_WATCH) */
1422
1423    /*
1424     * We restrict the interest to what the channel can support. IOW there
1425     * will never be write events for a channel which is not writable.
1426     * Analoguously for read events and non-readable channels.
1427     */
1428
1429    mask &= rcPtr->mode;
1430
1431    if (mask == rcPtr->interest) {
1432        /*
1433         * Same old, same old, why should we do something?
1434         */
1435
1436        return;
1437    }
1438
1439    rcPtr->interest = mask;
1440
1441    /*
1442     * Are we in the correct thread?
1443     */
1444
1445#ifdef TCL_THREADS
1446    if (rcPtr->thread != Tcl_GetCurrentThread()) {
1447        ForwardParam p;
1448
1449        p.watch.mask = mask;
1450        ForwardOpToOwnerThread(rcPtr, ForwardedWatch, &p);
1451
1452        /*
1453         * Any failure from the forward is ignored. We have no place to put
1454         * this.
1455         */
1456
1457        return;
1458    }
1459#endif
1460
1461    maskObj = DecodeEventMask(mask);
1462    (void) InvokeTclMethod(rcPtr, "watch", maskObj, NULL, NULL);
1463    Tcl_DecrRefCount(maskObj);
1464}
1465
1466/*
1467 *----------------------------------------------------------------------
1468 *
1469 * ReflectBlock --
1470 *
1471 *      This function is invoked to tell the channel which blocking behaviour
1472 *      is required of it.
1473 *
1474 * Results:
1475 *      A posix error number.
1476 *
1477 * Side effects:
1478 *      Allocates memory. Arbitrary, as it calls upon a script.
1479 *
1480 *----------------------------------------------------------------------
1481 */
1482
1483static int
1484ReflectBlock(
1485    ClientData clientData,
1486    int nonblocking)
1487{
1488    ReflectedChannel *rcPtr = (ReflectedChannel *) clientData;
1489    Tcl_Obj *blockObj;
1490    int errorNum;               /* EINVAL or EOK (success). */
1491    Tcl_Obj *resObj;            /* Result data for 'blocking' */
1492
1493    /*
1494     * Are we in the correct thread?
1495     */
1496
1497#ifdef TCL_THREADS
1498    if (rcPtr->thread != Tcl_GetCurrentThread()) {
1499        ForwardParam p;
1500
1501        p.block.nonblocking = nonblocking;
1502
1503        ForwardOpToOwnerThread(rcPtr, ForwardedBlock, &p);
1504
1505        if (p.base.code != TCL_OK) {
1506            PassReceivedError(rcPtr->chan, &p);
1507            return EINVAL;
1508        }
1509
1510        return EOK;
1511    }
1512#endif
1513
1514    blockObj = Tcl_NewBooleanObj(!nonblocking);
1515
1516    if (InvokeTclMethod(rcPtr, "blocking", blockObj, NULL, &resObj) != TCL_OK) {
1517        Tcl_SetChannelError(rcPtr->chan, resObj);
1518        errorNum = EINVAL;
1519    } else {
1520        errorNum = EOK;
1521    }
1522
1523    Tcl_DecrRefCount(resObj);           /* Remove reference held from invoke */
1524    return errorNum;
1525}
1526
1527/*
1528 *----------------------------------------------------------------------
1529 *
1530 * ReflectSetOption --
1531 *
1532 *      This function is invoked to configure a channel option.
1533 *
1534 * Results:
1535 *      A standard Tcl result code.
1536 *
1537 * Side effects:
1538 *      Arbitrary, as it calls upon a Tcl script.
1539 *
1540 *----------------------------------------------------------------------
1541 */
1542
1543static int
1544ReflectSetOption(
1545    ClientData clientData,      /* Channel to query */
1546    Tcl_Interp *interp,         /* Interpreter to leave error messages in */
1547    const char *optionName,     /* Name of requested option */
1548    const char *newValue)       /* The new value */
1549{
1550    ReflectedChannel *rcPtr = (ReflectedChannel *) clientData;
1551    Tcl_Obj *optionObj, *valueObj;
1552    int result;                 /* Result code for 'configure' */
1553    Tcl_Obj *resObj;            /* Result data for 'configure' */
1554
1555    /*
1556     * Are we in the correct thread?
1557     */
1558
1559#ifdef TCL_THREADS
1560    if (rcPtr->thread != Tcl_GetCurrentThread()) {
1561        ForwardParam p;
1562
1563        p.setOpt.name = optionName;
1564        p.setOpt.value = newValue;
1565
1566        ForwardOpToOwnerThread(rcPtr, ForwardedSetOpt, &p);
1567
1568        if (p.base.code != TCL_OK) {
1569            Tcl_Obj *err = Tcl_NewStringObj(p.base.msgStr, -1);
1570
1571            UnmarshallErrorResult(interp, err);
1572            Tcl_DecrRefCount(err);
1573            FreeReceivedError(&p);
1574        }
1575
1576        return p.base.code;
1577    }
1578#endif
1579
1580    optionObj = Tcl_NewStringObj(optionName, -1);
1581    valueObj = Tcl_NewStringObj(newValue, -1);
1582    result = InvokeTclMethod(rcPtr, "configure",optionObj,valueObj, &resObj);
1583    if (result != TCL_OK) {
1584        UnmarshallErrorResult(interp, resObj);
1585    }
1586
1587    Tcl_DecrRefCount(resObj);           /* Remove reference held from invoke */
1588    return result;
1589}
1590
1591/*
1592 *----------------------------------------------------------------------
1593 *
1594 * ReflectGetOption --
1595 *
1596 *      This function is invoked to retrieve all or a channel option.
1597 *
1598 * Results:
1599 *      A standard Tcl result code.
1600 *
1601 * Side effects:
1602 *      Arbitrary, as it calls upon a Tcl script.
1603 *
1604 *----------------------------------------------------------------------
1605 */
1606
1607static int
1608ReflectGetOption(
1609    ClientData clientData,      /* Channel to query */
1610    Tcl_Interp *interp,         /* Interpreter to leave error messages in */
1611    const char *optionName,     /* Name of reuqested option */
1612    Tcl_DString *dsPtr)         /* String to place the result into */
1613{
1614    /*
1615     * This code is special. It has regular passing of Tcl result, and errors.
1616     * The bypass functions are not required.
1617     */
1618
1619    ReflectedChannel *rcPtr = (ReflectedChannel*) clientData;
1620    Tcl_Obj *optionObj;
1621    Tcl_Obj *resObj;            /* Result data for 'configure' */
1622    int listc;
1623    Tcl_Obj **listv;
1624    const char *method;
1625
1626    /*
1627     * Are we in the correct thread?
1628     */
1629
1630#ifdef TCL_THREADS
1631    if (rcPtr->thread != Tcl_GetCurrentThread()) {
1632        int opcode;
1633        ForwardParam p;
1634
1635        p.getOpt.name = optionName;
1636        p.getOpt.value = dsPtr;
1637
1638        if (optionName == NULL) {
1639            opcode = ForwardedGetOptAll;
1640        } else {
1641            opcode = ForwardedGetOpt;
1642        }
1643
1644        ForwardOpToOwnerThread(rcPtr, opcode, &p);
1645
1646        if (p.base.code != TCL_OK) {
1647            Tcl_Obj *err = Tcl_NewStringObj(p.base.msgStr, -1);
1648
1649            UnmarshallErrorResult(interp, err);
1650            Tcl_DecrRefCount(err);
1651            FreeReceivedError(&p);
1652        }
1653
1654        return p.base.code;
1655    }
1656#endif
1657
1658    if (optionName == NULL) {
1659        /*
1660         * Retrieve all options.
1661         */
1662
1663        method = "cgetall";
1664        optionObj = NULL;
1665    } else {
1666        /*
1667         * Retrieve the value of one option.
1668         */
1669
1670        method = "cget";
1671        optionObj = Tcl_NewStringObj(optionName, -1);
1672    }
1673
1674    if (InvokeTclMethod(rcPtr, method, optionObj, NULL, &resObj)!=TCL_OK) {
1675        UnmarshallErrorResult(interp, resObj);
1676        Tcl_DecrRefCount(resObj);       /* Remove reference held from invoke */
1677        return TCL_ERROR;
1678    }
1679
1680    /*
1681     * The result has to go into the 'dsPtr' for propagation to the caller of
1682     * the driver.
1683     */
1684
1685    if (optionObj != NULL) {
1686        Tcl_DStringAppend(dsPtr, TclGetString(resObj), -1);
1687        Tcl_DecrRefCount(resObj);       /* Remove reference held from invoke */
1688        return TCL_OK;
1689    }
1690
1691    /*
1692     * Extract the list and append each item as element.
1693     */
1694
1695    /*
1696     * NOTE (4): If we extract the string rep we can assume a properly quoted
1697     * string. Together with a separating space this way of simply appending
1698     * the whole string rep might be faster. It also doesn't check if the
1699     * result is a valid list. Nor that the list has an even number elements.
1700     */
1701
1702    if (Tcl_ListObjGetElements(interp, resObj, &listc, &listv) != TCL_OK) {
1703        Tcl_DecrRefCount(resObj);       /* Remove reference held from invoke */
1704        return TCL_ERROR;
1705    }
1706
1707    if ((listc % 2) == 1) {
1708        /*
1709         * Odd number of elements is wrong.
1710         */
1711
1712        Tcl_ResetResult(interp);
1713        Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1714                "Expected list with even number of "
1715                "elements, got %d element%s instead", listc,
1716                (listc == 1 ? "" : "s")));
1717        Tcl_DecrRefCount(resObj);       /* Remove reference held from invoke */
1718        return TCL_ERROR;
1719    } else {
1720        int len;
1721        char *str = Tcl_GetStringFromObj(resObj, &len);
1722
1723        if (len) {
1724            Tcl_DStringAppend(dsPtr, " ", 1);
1725            Tcl_DStringAppend(dsPtr, str, len);
1726        }
1727        Tcl_DecrRefCount(resObj);       /* Remove reference held from invoke */
1728        return TCL_OK;
1729    }
1730}
1731
1732/*
1733 * Helpers. =========================================================
1734 */
1735
1736/*
1737 *----------------------------------------------------------------------
1738 *
1739 * EncodeEventMask --
1740 *
1741 *      This function takes a list of event items and constructs the
1742 *      equivalent internal bitmask. The list must contain at least one
1743 *      element. Elements are "read", "write", or any unique abbreviation of
1744 *      them. Note that the bitmask is not changed if problems are
1745 *      encountered.
1746 *
1747 * Results:
1748 *      A standard Tcl error code. A bitmask where TCL_READABLE and/or
1749 *      TCL_WRITABLE can be set.
1750 *
1751 * Side effects:
1752 *      May shimmer 'obj' to a list representation. May place an error message
1753 *      into the interp result.
1754 *
1755 *----------------------------------------------------------------------
1756 */
1757
1758static int
1759EncodeEventMask(
1760    Tcl_Interp *interp,
1761    const char *objName,
1762    Tcl_Obj *obj,
1763    int *mask)
1764{
1765    int events;                 /* Mask of events to post */
1766    int listc;                  /* #elements in eventspec list */
1767    Tcl_Obj **listv;            /* Elements of eventspec list */
1768    int evIndex;                /* Id of event for an element of the eventspec
1769                                 * list. */
1770
1771    if (Tcl_ListObjGetElements(interp, obj, &listc, &listv) != TCL_OK) {
1772        return TCL_ERROR;
1773    }
1774
1775    if (listc < 1) {
1776        Tcl_AppendResult(interp, "bad ", objName, " list: is empty", NULL);
1777        return TCL_ERROR;
1778    }
1779
1780    events = 0;
1781    while (listc > 0) {
1782        if (Tcl_GetIndexFromObj(interp, listv[listc-1], eventOptions,
1783                objName, 0, &evIndex) != TCL_OK) {
1784            return TCL_ERROR;
1785        }
1786        switch (evIndex) {
1787        case EVENT_READ:
1788            events |= TCL_READABLE;
1789            break;
1790        case EVENT_WRITE:
1791            events |= TCL_WRITABLE;
1792            break;
1793        }
1794        listc --;
1795    }
1796
1797    *mask = events;
1798    return TCL_OK;
1799}
1800
1801/*
1802 *----------------------------------------------------------------------
1803 *
1804 * DecodeEventMask --
1805 *
1806 *      This function takes an internal bitmask of events and constructs the
1807 *      equivalent list of event items.
1808 *
1809 * Results:
1810 *      A Tcl_Obj reference. The object will have a refCount of one. The user
1811 *      has to decrement it to release the object.
1812 *
1813 * Side effects:
1814 *      None.
1815 *
1816 *----------------------------------------------------------------------
1817 */
1818
1819static Tcl_Obj *
1820DecodeEventMask(
1821    int mask)
1822{
1823    register const char *eventStr;
1824    Tcl_Obj *evObj;
1825
1826    switch (mask & RANDW) {
1827    case RANDW:
1828        eventStr = "read write";
1829        break;
1830    case TCL_READABLE:
1831        eventStr = "read";
1832        break;
1833    case TCL_WRITABLE:
1834        eventStr = "write";
1835        break;
1836    default:
1837        eventStr = "";
1838        break;
1839    }
1840
1841    evObj = Tcl_NewStringObj(eventStr, -1);
1842    Tcl_IncrRefCount(evObj);
1843    return evObj;
1844}
1845
1846/*
1847 *----------------------------------------------------------------------
1848 *
1849 * NewReflectedChannel --
1850 *
1851 *      This function is invoked to allocate and initialize the instance data
1852 *      of a new reflected channel.
1853 *
1854 * Results:
1855 *      A heap-allocated channel instance.
1856 *
1857 * Side effects:
1858 *      Allocates memory.
1859 *
1860 *----------------------------------------------------------------------
1861 */
1862
1863static ReflectedChannel *
1864NewReflectedChannel(
1865    Tcl_Interp *interp,
1866    Tcl_Obj *cmdpfxObj,
1867    int mode,
1868    Tcl_Obj *handleObj)
1869{
1870    ReflectedChannel *rcPtr;
1871    int i, listc;
1872    Tcl_Obj **listv;
1873
1874    rcPtr = (ReflectedChannel *) ckalloc(sizeof(ReflectedChannel));
1875
1876    /* rcPtr->chan: Assigned by caller. Dummy data here. */
1877    /* rcPtr->methods: Assigned by caller. Dummy data here. */
1878
1879    rcPtr->chan = NULL;
1880    rcPtr->methods = 0;
1881    rcPtr->interp = interp;
1882#ifdef TCL_THREADS
1883    rcPtr->thread = Tcl_GetCurrentThread();
1884#endif
1885    rcPtr->mode = mode;
1886    rcPtr->interest = 0;                /* Initially no interest registered */
1887
1888    /*
1889     * Method placeholder.
1890     */
1891
1892    /* ASSERT: cmdpfxObj is a Tcl List */
1893
1894    Tcl_ListObjGetElements(interp, cmdpfxObj, &listc, &listv);
1895
1896    /*
1897     * See [==] as well.
1898     * Storage for the command prefix and the additional words required for
1899     * the invocation of methods in the command handler.
1900     *
1901     * listv [0] [listc-1] | [listc]  [listc+1] |
1902     * argv  [0]   ... [.] | [argc-2] [argc-1]  | [argc]  [argc+2]
1903     *       cmd   ... pfx | method   chan      | detail1 detail2
1904     */
1905
1906    rcPtr->argc = listc + 2;
1907    rcPtr->argv = (Tcl_Obj **) ckalloc(sizeof(Tcl_Obj *) * (listc+4));
1908
1909    /*
1910     * Duplicate object references.
1911     */
1912
1913    for (i=0; i<listc ; i++) {
1914        Tcl_Obj *word = rcPtr->argv[i] = listv[i];
1915
1916        Tcl_IncrRefCount(word);
1917    }
1918
1919    i++;                                /* Skip placeholder for method */
1920
1921    /*
1922     * [Bug 1667990]: See [x] in FreeReflectedChannel for release
1923     */
1924
1925    rcPtr->argv[i] = handleObj;
1926    Tcl_IncrRefCount(handleObj);
1927
1928    /*
1929     * The next two objects are kept empty, varying arguments.
1930     */
1931
1932    /*
1933     * Initialization complete.
1934     */
1935
1936    return rcPtr;
1937}
1938
1939/*
1940 *----------------------------------------------------------------------
1941 *
1942 * NextHandle --
1943 *
1944 *      This function is invoked to generate a channel handle for a new
1945 *      reflected channel.
1946 *
1947 * Results:
1948 *      A Tcl_Obj containing the string of the new channel handle. The
1949 *      refcount of the returned object is -- zero --.
1950 *
1951 * Side effects:
1952 *      May allocate memory. Mutex protected critical section locks out other
1953 *      threads for a short time.
1954 *
1955 *----------------------------------------------------------------------
1956 */
1957
1958static Tcl_Obj *
1959NextHandle(void)
1960{
1961    /*
1962     * Count number of generated reflected channels. Used for id generation.
1963     * Ids are never reclaimed and there is no dealing with wrap around. On
1964     * the other hand, "unsigned long" should be big enough except for
1965     * absolute longrunners (generate a 100 ids per second => overflow will
1966     * occur in 1 1/3 years).
1967     */
1968
1969    TCL_DECLARE_MUTEX(rcCounterMutex)
1970    static unsigned long rcCounter = 0;
1971    Tcl_Obj *resObj;
1972
1973    Tcl_MutexLock(&rcCounterMutex);
1974    resObj = Tcl_ObjPrintf("rc%lu", rcCounter);
1975    rcCounter++;
1976    Tcl_MutexUnlock(&rcCounterMutex);
1977
1978    return resObj;
1979}
1980
1981static void
1982FreeReflectedChannel(
1983    ReflectedChannel *rcPtr)
1984{
1985    Channel *chanPtr = (Channel *) rcPtr->chan;
1986    int i, n;
1987
1988    if (chanPtr->typePtr != &tclRChannelType) {
1989        /*
1990         * Delete a cloned ChannelType structure.
1991         */
1992
1993        ckfree((char*) chanPtr->typePtr);
1994    }
1995
1996    n = rcPtr->argc - 2;
1997    for (i=0; i<n; i++) {
1998        Tcl_DecrRefCount(rcPtr->argv[i]);
1999    }
2000
2001    /*
2002     * [Bug 1667990]: See [x] in NewReflectedChannel for lock. n+1 = argc-1.
2003     */
2004
2005    Tcl_DecrRefCount(rcPtr->argv[n+1]);
2006
2007    ckfree((char*) rcPtr->argv);
2008    ckfree((char*) rcPtr);
2009}
2010
2011/*
2012 *----------------------------------------------------------------------
2013 *
2014 * InvokeTclMethod --
2015 *
2016 *      This function is used to invoke the Tcl level of a reflected channel.
2017 *      It handles all the command assembly, invokation, and generic state and
2018 *      result mgmt. It does *not* handle thread redirection; that is the
2019 *      responsibility of clients of this function.
2020 *
2021 * Results:
2022 *      Result code and data as returned by the method.
2023 *
2024 * Side effects:
2025 *      Arbitrary, as it calls upon a Tcl script.
2026 *
2027 *----------------------------------------------------------------------
2028 */
2029
2030static int
2031InvokeTclMethod(
2032    ReflectedChannel *rcPtr,
2033    const char *method,
2034    Tcl_Obj *argOneObj,         /* NULL'able */
2035    Tcl_Obj *argTwoObj,         /* NULL'able */
2036    Tcl_Obj **resultObjPtr)     /* NULL'able */
2037{
2038    int cmdc;                   /* #words in constructed command */
2039    Tcl_Obj *methObj = NULL;    /* Method name in object form */
2040    Tcl_InterpState sr;         /* State of handler interp */
2041    int result;                 /* Result code of method invokation */
2042    Tcl_Obj *resObj = NULL;     /* Result of method invokation. */
2043
2044    /*
2045     * NOTE (5): Decide impl. issue: Cache objects with method names? Needs
2046     * TSD data as reflections can be created in many different threads.
2047     */
2048
2049    /*
2050     * Insert method into the pre-allocated area, after the command prefix,
2051     * before the channel id.
2052     */
2053
2054    methObj = Tcl_NewStringObj(method, -1);
2055    Tcl_IncrRefCount(methObj);
2056    rcPtr->argv[rcPtr->argc - 2] = methObj;
2057
2058    /*
2059     * Append the additional argument containing method specific details
2060     * behind the channel id. If specified.
2061     */
2062
2063    cmdc = rcPtr->argc;
2064    if (argOneObj) {
2065        Tcl_IncrRefCount(argOneObj);
2066        rcPtr->argv[cmdc] = argOneObj;
2067        cmdc++;
2068        if (argTwoObj) {
2069            Tcl_IncrRefCount(argTwoObj);
2070            rcPtr->argv[cmdc] = argTwoObj;
2071            cmdc++;
2072        }
2073    }
2074
2075    /*
2076     * And run the handler... This is done in auch a manner which leaves any
2077     * existing state intact.
2078     */
2079
2080    sr = Tcl_SaveInterpState(rcPtr->interp, 0 /* Dummy */);
2081    Tcl_Preserve(rcPtr->interp);
2082    result = Tcl_EvalObjv(rcPtr->interp, cmdc, rcPtr->argv, TCL_EVAL_GLOBAL);
2083
2084    /*
2085     * We do not try to extract the result information if the caller has no
2086     * interest in it. I.e. there is no need to put effort into creating
2087     * something which is discarded immediately after.
2088     */
2089
2090    if (resultObjPtr) {
2091        if (result == TCL_OK) {
2092            /*
2093             * Ok result taken as is, also if the caller requests that there
2094             * is no capture.
2095             */
2096
2097            resObj = Tcl_GetObjResult(rcPtr->interp);
2098        } else {
2099            /*
2100             * Non-ok result is always treated as an error. We have to capture
2101             * the full state of the result, including additional options.
2102             *
2103             * This is complex and ugly, and would be completely unnecessary
2104             * if we only added support for a TCL_FORBID_EXCEPTIONS flag.
2105             */
2106
2107            if (result != TCL_ERROR) {
2108                Tcl_Obj *cmd = Tcl_NewListObj(cmdc, rcPtr->argv);
2109                int cmdLen;
2110                const char *cmdString = Tcl_GetStringFromObj(cmd, &cmdLen);
2111
2112                Tcl_IncrRefCount(cmd);
2113                Tcl_ResetResult(rcPtr->interp);
2114                Tcl_SetObjResult(rcPtr->interp, Tcl_ObjPrintf(
2115                        "chan handler returned bad code: %d", result));
2116                Tcl_LogCommandInfo(rcPtr->interp, cmdString, cmdString,
2117                        cmdLen);
2118                Tcl_DecrRefCount(cmd);
2119                result = TCL_ERROR;
2120            }
2121            Tcl_AppendObjToErrorInfo(rcPtr->interp, Tcl_ObjPrintf(
2122                    "\n    (chan handler subcommand \"%s\")", method));
2123            resObj = MarshallError(rcPtr->interp);
2124        }
2125        Tcl_IncrRefCount(resObj);
2126    }
2127    Tcl_RestoreInterpState(rcPtr->interp, sr);
2128    Tcl_Release(rcPtr->interp);
2129
2130    /*
2131     * Cleanup of the dynamic parts of the command.
2132     */
2133
2134    Tcl_DecrRefCount(methObj);
2135    if (argOneObj) {
2136        Tcl_DecrRefCount(argOneObj);
2137        if (argTwoObj) {
2138            Tcl_DecrRefCount(argTwoObj);
2139        }
2140    }
2141
2142    /*
2143     * The resObj has a ref count of 1 at this location. This means that the
2144     * caller of InvokeTclMethod has to dispose of it (but only if it was
2145     * returned to it).
2146     */
2147
2148    if (resultObjPtr != NULL) {
2149        *resultObjPtr = resObj;
2150    }
2151
2152    /*
2153     * There no need to handle the case where nothing is returned, because for
2154     * that case resObj was not set anyway.
2155     */
2156
2157    return result;
2158}
2159
2160/*
2161 *----------------------------------------------------------------------
2162 *
2163 * GetReflectedChannelMap --
2164 *
2165 *      Gets and potentially initializes the reflected channel map for an
2166 *      interpreter.
2167 *
2168 * Results:
2169 *      A pointer to the map created, for use by the caller.
2170 *
2171 * Side effects:
2172 *      Initializes the reflected channel map for an interpreter.
2173 *
2174 *----------------------------------------------------------------------
2175 */
2176
2177static ReflectedChannelMap *
2178GetReflectedChannelMap(
2179    Tcl_Interp *interp)
2180{
2181    ReflectedChannelMap* rcmPtr = Tcl_GetAssocData(interp, RCMKEY, NULL);
2182
2183    if (rcmPtr == NULL) {
2184        rcmPtr = (ReflectedChannelMap *) ckalloc(sizeof(ReflectedChannelMap));
2185        Tcl_InitHashTable(&rcmPtr->map, TCL_STRING_KEYS);
2186        Tcl_SetAssocData(interp, RCMKEY,
2187                (Tcl_InterpDeleteProc *) DeleteReflectedChannelMap, rcmPtr);
2188    }
2189    return rcmPtr;
2190}
2191
2192/*
2193 *----------------------------------------------------------------------
2194 *
2195 * DeleteReflectedChannelMap --
2196 *
2197 *      Deletes the channel table for an interpreter, closing any open
2198 *      channels whose refcount reaches zero. This procedure is invoked when
2199 *      an interpreter is deleted, via the AssocData cleanup mechanism.
2200 *
2201 * Results:
2202 *      None.
2203 *
2204 * Side effects:
2205 *      Deletes the hash table of channels. May close channels. May flush
2206 *      output on closed channels. Removes any channeEvent handlers that were
2207 *      registered in this interpreter.
2208 *
2209 *----------------------------------------------------------------------
2210 */
2211
2212static void
2213DeleteReflectedChannelMap(
2214    ClientData clientData,      /* The per-interpreter data structure. */
2215    Tcl_Interp *interp)         /* The interpreter being deleted. */
2216{
2217    ReflectedChannelMap* rcmPtr; /* The map */
2218    Tcl_HashSearch hSearch;      /* Search variable. */
2219    Tcl_HashEntry *hPtr;         /* Search variable. */
2220
2221    /*
2222     * Delete all entries. The channels may have been closed alreay, or will
2223     * be closed later, by the standard IO finalization of an interpreter
2224     * under destruction.
2225     */
2226
2227    rcmPtr = clientData;
2228    for (hPtr = Tcl_FirstHashEntry(&rcmPtr->map, &hSearch);
2229         hPtr != NULL;
2230         hPtr = Tcl_FirstHashEntry(&rcmPtr->map, &hSearch)) {
2231
2232        Tcl_DeleteHashEntry(hPtr);
2233    }
2234    Tcl_DeleteHashTable(&rcmPtr->map);
2235    ckfree((char *) &rcmPtr->map);
2236}
2237
2238#ifdef TCL_THREADS
2239static void
2240ForwardOpToOwnerThread(
2241    ReflectedChannel *rcPtr,    /* Channel instance */
2242    ForwardedOperation op,      /* Forwarded driver operation */
2243    const VOID *param)          /* Arguments */
2244{
2245    Tcl_ThreadId dst = rcPtr->thread;
2246    ForwardingEvent *evPtr;
2247    ForwardingResult *resultPtr;
2248    int result;
2249
2250    /*
2251     * Create and initialize the event and data structures.
2252     */
2253
2254    evPtr = (ForwardingEvent *) ckalloc(sizeof(ForwardingEvent));
2255    resultPtr = (ForwardingResult *) ckalloc(sizeof(ForwardingResult));
2256
2257    evPtr->event.proc = ForwardProc;
2258    evPtr->resultPtr = resultPtr;
2259    evPtr->op = op;
2260    evPtr->rcPtr = rcPtr;
2261    evPtr->param = (ForwardParam *) param;
2262
2263    resultPtr->src = Tcl_GetCurrentThread();
2264    resultPtr->dst = dst;
2265    resultPtr->done = NULL;
2266    resultPtr->result = -1;
2267    resultPtr->evPtr = evPtr;
2268
2269    /*
2270     * Now execute the forward.
2271     */
2272
2273    Tcl_MutexLock(&rcForwardMutex);
2274    TclSpliceIn(resultPtr, forwardList);
2275
2276    /*
2277     * Ensure cleanup of the event if any of the two involved threads exits
2278     * while this event is pending or in progress.
2279     */
2280
2281    Tcl_CreateThreadExitHandler(SrcExitProc, (ClientData) evPtr);
2282    Tcl_CreateThreadExitHandler(DstExitProc, (ClientData) evPtr);
2283
2284    /*
2285     * Queue the event and poke the other thread's notifier.
2286     */
2287
2288    Tcl_ThreadQueueEvent(dst, (Tcl_Event *)evPtr, TCL_QUEUE_TAIL);
2289    Tcl_ThreadAlert(dst);
2290
2291    /*
2292     * (*) Block until the other thread has either processed the transfer or
2293     * rejected it.
2294     */
2295
2296    while (resultPtr->result < 0) {
2297        /*
2298         * NOTE (1): Is it possible that the current thread goes away while
2299         * waiting here? IOW Is it possible that "SrcExitProc" is called while
2300         * we are here? See complementary note (2) in "SrcExitProc"
2301         */
2302
2303        Tcl_ConditionWait(&resultPtr->done, &rcForwardMutex, NULL);
2304    }
2305
2306    /*
2307     * Unlink result from the forwarder list.
2308     */
2309
2310    TclSpliceOut(resultPtr, forwardList);
2311
2312    resultPtr->nextPtr = NULL;
2313    resultPtr->prevPtr = NULL;
2314
2315    Tcl_MutexUnlock(&rcForwardMutex);
2316    Tcl_ConditionFinalize(&resultPtr->done);
2317
2318    /*
2319     * Kill the cleanup handlers now, and the result structure as well, before
2320     * returning the success code.
2321     *
2322     * Note: The event structure has already been deleted.
2323     */
2324
2325    Tcl_DeleteThreadExitHandler(SrcExitProc, (ClientData) evPtr);
2326    Tcl_DeleteThreadExitHandler(DstExitProc, (ClientData) evPtr);
2327
2328    result = resultPtr->result;
2329    ckfree((char*) resultPtr);
2330}
2331
2332static int
2333ForwardProc(
2334    Tcl_Event *evGPtr,
2335    int mask)
2336{
2337    /*
2338     * Notes regarding access to the referenced data.
2339     *
2340     * In principle the data belongs to the originating thread (see
2341     * evPtr->src), however this thread is currently blocked at (*), i.e.
2342     * quiescent. Because of this we can treat the data as belonging to us,
2343     * without fear of race conditions. I.e. we can read and write as we like.
2344     *
2345     * The only thing we cannot be sure of is the resultPtr. This can be be
2346     * NULLed if the originating thread went away while the event is handled
2347     * here now.
2348     */
2349
2350    ForwardingEvent *evPtr = (ForwardingEvent *) evGPtr;
2351    ForwardingResult *resultPtr = evPtr->resultPtr;
2352    ReflectedChannel *rcPtr = evPtr->rcPtr;
2353    Tcl_Interp *interp = rcPtr->interp;
2354    ForwardParam *paramPtr = evPtr->param;
2355    Tcl_Obj *resObj = NULL;     /* Interp result of InvokeTclMethod */
2356
2357    /*
2358     * Ignore the event if no one is waiting for its result anymore.
2359     */
2360
2361    if (!resultPtr) {
2362        return 1;
2363    }
2364
2365    paramPtr->base.code = TCL_OK;
2366    paramPtr->base.msgStr = NULL;
2367    paramPtr->base.mustFree = 0;
2368
2369    switch (evPtr->op) {
2370        /*
2371         * The destination thread for the following operations is
2372         * rcPtr->thread, which contains rcPtr->interp, the interp we have to
2373         * call upon for the driver.
2374         */
2375
2376    case ForwardedClose:
2377        /*
2378         * No parameters/results.
2379         */
2380
2381        if (InvokeTclMethod(rcPtr, "finalize", NULL, NULL, &resObj)!=TCL_OK) {
2382            ForwardSetObjError(paramPtr, resObj);
2383        }
2384
2385        /*
2386         * Freeing is done here, in the origin thread, because the argv[]
2387         * objects belong to this thread. Deallocating them in a different
2388         * thread is not allowed
2389         */
2390
2391        FreeReflectedChannel(rcPtr);
2392        break;
2393
2394    case ForwardedInput: {
2395        Tcl_Obj *toReadObj = Tcl_NewIntObj(paramPtr->input.toRead);
2396
2397        if (InvokeTclMethod(rcPtr, "read", toReadObj, NULL, &resObj)!=TCL_OK){
2398            ForwardSetObjError(paramPtr, resObj);
2399            paramPtr->input.toRead = -1;
2400        } else {
2401            /*
2402             * Process a regular result.
2403             */
2404
2405            int bytec;                  /* Number of returned bytes */
2406            unsigned char *bytev;       /* Array of returned bytes */
2407
2408            bytev = Tcl_GetByteArrayFromObj(resObj, &bytec);
2409
2410            if (paramPtr->input.toRead < bytec) {
2411                ForwardSetStaticError(paramPtr, msg_read_toomuch);
2412                paramPtr->input.toRead = -1;
2413            } else {
2414                if (bytec > 0) {
2415                    memcpy(paramPtr->input.buf, bytev, (size_t)bytec);
2416                }
2417                paramPtr->input.toRead = bytec;
2418            }
2419        }
2420        break;
2421    }
2422
2423    case ForwardedOutput: {
2424        Tcl_Obj *bufObj = Tcl_NewByteArrayObj((unsigned char *)
2425                paramPtr->output.buf, paramPtr->output.toWrite);
2426
2427        if (InvokeTclMethod(rcPtr, "write", bufObj, NULL, &resObj) != TCL_OK) {
2428            ForwardSetObjError(paramPtr, resObj);
2429            paramPtr->output.toWrite = -1;
2430        } else {
2431            /*
2432             * Process a regular result.
2433             */
2434
2435            int written;
2436
2437            if (Tcl_GetIntFromObj(interp, resObj, &written) != TCL_OK) {
2438                ForwardSetObjError(paramPtr, MarshallError(interp));
2439                paramPtr->output.toWrite = -1;
2440            } else if (written==0 || paramPtr->output.toWrite<written) {
2441                ForwardSetStaticError(paramPtr, msg_write_toomuch);
2442                paramPtr->output.toWrite = -1;
2443            } else {
2444                paramPtr->output.toWrite = written;
2445            }
2446        }
2447        break;
2448    }
2449
2450    case ForwardedSeek: {
2451        Tcl_Obj *offObj = Tcl_NewWideIntObj(paramPtr->seek.offset);
2452        Tcl_Obj *baseObj = Tcl_NewStringObj(
2453                (paramPtr->seek.seekMode==SEEK_SET) ? "start" :
2454                (paramPtr->seek.seekMode==SEEK_CUR) ? "current" : "end", -1);
2455
2456        if (InvokeTclMethod(rcPtr, "seek", offObj, baseObj, &resObj)!=TCL_OK){
2457            ForwardSetObjError(paramPtr, resObj);
2458            paramPtr->seek.offset = -1;
2459        } else {
2460            /*
2461             * Process a regular result. If the type is wrong this may change
2462             * into an error.
2463             */
2464
2465            Tcl_WideInt newLoc;
2466
2467            if (Tcl_GetWideIntFromObj(interp, resObj, &newLoc) == TCL_OK) {
2468                if (newLoc < Tcl_LongAsWide(0)) {
2469                    ForwardSetStaticError(paramPtr, msg_seek_beforestart);
2470                    paramPtr->seek.offset = -1;
2471                } else {
2472                    paramPtr->seek.offset = newLoc;
2473                }
2474            } else {
2475                ForwardSetObjError(paramPtr, MarshallError(interp));
2476                paramPtr->seek.offset = -1;
2477            }
2478        }
2479        break;
2480    }
2481
2482    case ForwardedWatch: {
2483        Tcl_Obj *maskObj = DecodeEventMask(paramPtr->watch.mask);
2484
2485        (void) InvokeTclMethod(rcPtr, "watch", maskObj, NULL, NULL);
2486        Tcl_DecrRefCount(maskObj);
2487        break;
2488    }
2489
2490    case ForwardedBlock: {
2491        Tcl_Obj *blockObj = Tcl_NewBooleanObj(!paramPtr->block.nonblocking);
2492
2493        if (InvokeTclMethod(rcPtr, "blocking", blockObj, NULL,
2494                &resObj) != TCL_OK) {
2495            ForwardSetObjError(paramPtr, resObj);
2496        }
2497        break;
2498    }
2499
2500    case ForwardedSetOpt: {
2501        Tcl_Obj *optionObj = Tcl_NewStringObj(paramPtr->setOpt.name, -1);
2502        Tcl_Obj *valueObj = Tcl_NewStringObj(paramPtr->setOpt.value, -1);
2503
2504        if (InvokeTclMethod(rcPtr, "configure", optionObj, valueObj,
2505                &resObj) != TCL_OK) {
2506            ForwardSetObjError(paramPtr, resObj);
2507        }
2508        break;
2509    }
2510
2511    case ForwardedGetOpt: {
2512        /*
2513         * Retrieve the value of one option.
2514         */
2515
2516        Tcl_Obj *optionObj = Tcl_NewStringObj(paramPtr->getOpt.name, -1);
2517
2518        if (InvokeTclMethod(rcPtr, "cget", optionObj, NULL, &resObj)!=TCL_OK){
2519            ForwardSetObjError(paramPtr, resObj);
2520        } else {
2521            Tcl_DStringAppend(paramPtr->getOpt.value,
2522                    TclGetString(resObj), -1);
2523        }
2524        break;
2525    }
2526
2527    case ForwardedGetOptAll:
2528        /*
2529         * Retrieve all options.
2530         */
2531
2532        if (InvokeTclMethod(rcPtr, "cgetall", NULL, NULL, &resObj) != TCL_OK){
2533            ForwardSetObjError(paramPtr, resObj);
2534        } else {
2535            /*
2536             * Extract list, validate that it is a list, and #elements. See
2537             * NOTE (4) as well.
2538             */
2539
2540            int listc;
2541            Tcl_Obj **listv;
2542
2543            if (Tcl_ListObjGetElements(interp, resObj, &listc,
2544                    &listv) != TCL_OK) {
2545                ForwardSetObjError(paramPtr, MarshallError(interp));
2546            } else if ((listc % 2) == 1) {
2547                /*
2548                 * Odd number of elements is wrong. [x].
2549                 */
2550
2551                char *buf = ckalloc(200);
2552                sprintf(buf,
2553                        "{Expected list with even number of elements, got %d %s instead}",
2554                        listc, (listc == 1 ? "element" : "elements"));
2555
2556                ForwardSetDynamicError(paramPtr, buf);
2557            } else {
2558                int len;
2559                const char *str = Tcl_GetStringFromObj(resObj, &len);
2560
2561                if (len) {
2562                    Tcl_DStringAppend(paramPtr->getOpt.value, " ", 1);
2563                    Tcl_DStringAppend(paramPtr->getOpt.value, str, len);
2564                }
2565            }
2566        }
2567        break;
2568
2569    default:
2570        /*
2571         * Bad operation code.
2572         */
2573
2574        Tcl_Panic("Bad operation code in ForwardProc");
2575        break;
2576    }
2577
2578    /*
2579     * Remove the reference we held on the result of the invoke, if we had
2580     * such.
2581     */
2582
2583    if (resObj != NULL) {
2584        Tcl_DecrRefCount(resObj);
2585    }
2586
2587    if (resultPtr) {
2588        /*
2589         * Report the forwarding result synchronously to the waiting caller.
2590         * This unblocks (*) as well. This is wrapped into a conditional
2591         * because the caller may have exited in the mean time.
2592         */
2593
2594        Tcl_MutexLock(&rcForwardMutex);
2595        resultPtr->result = TCL_OK;
2596        Tcl_ConditionNotify(&resultPtr->done);
2597        Tcl_MutexUnlock(&rcForwardMutex);
2598    }
2599
2600    return 1;
2601}
2602
2603static void
2604SrcExitProc(
2605    ClientData clientData)
2606{
2607    ForwardingEvent *evPtr = (ForwardingEvent *) clientData;
2608    ForwardingResult *resultPtr;
2609    ForwardParam *paramPtr;
2610
2611    /*
2612     * NOTE (2): Can this handler be called with the originator blocked?
2613     */
2614
2615    /*
2616     * The originator for the event exited. It is not sure if this can happen,
2617     * as the originator should be blocked at (*) while the event is in
2618     * transit/pending.
2619     *
2620     * We make sure that the event cannot refer to the result anymore, remove
2621     * it from the list of pending results and free the structure. Locking the
2622     * access ensures that we cannot get in conflict with "ForwardProc",
2623     * should it already execute the event.
2624     */
2625
2626    Tcl_MutexLock(&rcForwardMutex);
2627
2628    resultPtr = evPtr->resultPtr;
2629    paramPtr = evPtr->param;
2630
2631    evPtr->resultPtr = NULL;
2632    resultPtr->evPtr = NULL;
2633    resultPtr->result = TCL_ERROR;
2634
2635    ForwardSetStaticError(paramPtr, msg_send_originlost);
2636
2637    /*
2638     * See below: TclSpliceOut(resultPtr, forwardList);
2639     */
2640
2641    Tcl_MutexUnlock(&rcForwardMutex);
2642
2643    /*
2644     * This unlocks (*). The structure will be spliced out and freed by
2645     * "ForwardProc". Maybe.
2646     */
2647
2648    Tcl_ConditionNotify(&resultPtr->done);
2649}
2650
2651static void
2652DstExitProc(
2653    ClientData clientData)
2654{
2655    ForwardingEvent *evPtr = (ForwardingEvent *) clientData;
2656    ForwardingResult *resultPtr = evPtr->resultPtr;
2657    ForwardParam *paramPtr = evPtr->param;
2658
2659    /*
2660     * NOTE (3): It is not clear if the event still exists when this handler
2661     * is called. We might have to use 'resultPtr' as our clientData instead.
2662     */
2663
2664    /*
2665     * The receiver for the event exited, before processing the event. We
2666     * detach the result now, wake the originator up and signal failure.
2667     */
2668
2669    evPtr->resultPtr = NULL;
2670    resultPtr->evPtr = NULL;
2671    resultPtr->result = TCL_ERROR;
2672
2673    ForwardSetStaticError(paramPtr, msg_send_dstlost);
2674
2675    Tcl_ConditionNotify(&resultPtr->done);
2676}
2677
2678static void
2679ForwardSetObjError(
2680    ForwardParam *paramPtr,
2681    Tcl_Obj *obj)
2682{
2683    int len;
2684    const char *msgStr = Tcl_GetStringFromObj(obj, &len);
2685
2686    len++;
2687    ForwardSetDynamicError(paramPtr, ckalloc((unsigned) len));
2688    memcpy(paramPtr->base.msgStr, msgStr, (unsigned) len);
2689}
2690#endif
2691
2692/*
2693 * Local Variables:
2694 * mode: c
2695 * c-basic-offset: 4
2696 * fill-column: 78
2697 * End:
2698 */
Note: See TracBrowser for help on using the repository browser.