tclIORChan.c

Go to the documentation of this file.
00001 /*
00002  * tclIORChan.c --
00003  *
00004  *      This file contains the implementation of Tcl's generic channel
00005  *      reflection code, which allows the implementation of Tcl channels in
00006  *      Tcl code.
00007  *
00008  *      Parts of this file are based on code contributed by Jean-Claude
00009  *      Wippler.
00010  *
00011  *      See TIP #219 for the specification of this functionality.
00012  *
00013  * Copyright (c) 2004-2005 ActiveState, a divison of Sophos
00014  *
00015  * See the file "license.terms" for information on usage and redistribution of
00016  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
00017  *
00018  * RCS: @(#) $Id: tclIORChan.c,v 1.27 2007/12/13 15:23:18 dgp Exp $
00019  */
00020 
00021 #include <tclInt.h>
00022 #include <tclIO.h>
00023 #include <assert.h>
00024 
00025 #ifndef EINVAL
00026 #define EINVAL  9
00027 #endif
00028 #ifndef EOK
00029 #define EOK     0
00030 #endif
00031 
00032 /*
00033  * Signatures of all functions used in the C layer of the reflection.
00034  */
00035 
00036 static int              ReflectClose(ClientData clientData,
00037                             Tcl_Interp *interp);
00038 static int              ReflectInput(ClientData clientData, char *buf,
00039                             int toRead, int *errorCodePtr);
00040 static int              ReflectOutput(ClientData clientData, const char *buf,
00041                             int toWrite, int *errorCodePtr);
00042 static void             ReflectWatch(ClientData clientData, int mask);
00043 static int              ReflectBlock(ClientData clientData, int mode);
00044 static Tcl_WideInt      ReflectSeekWide(ClientData clientData,
00045                             Tcl_WideInt offset, int mode, int *errorCodePtr);
00046 static int              ReflectSeek(ClientData clientData, long offset,
00047                             int mode, int *errorCodePtr);
00048 static int              ReflectGetOption(ClientData clientData,
00049                             Tcl_Interp *interp, const char *optionName,
00050                             Tcl_DString *dsPtr);
00051 static int              ReflectSetOption(ClientData clientData,
00052                             Tcl_Interp *interp, const char *optionName,
00053                             const char *newValue);
00054 
00055 /*
00056  * The C layer channel type/driver definition used by the reflection. This is
00057  * a version 3 structure.
00058  */
00059 
00060 static Tcl_ChannelType tclRChannelType = {
00061     "tclrchannel",         /* Type name.                                  */
00062     TCL_CHANNEL_VERSION_5, /* v5 channel */
00063     ReflectClose,          /* Close channel, clean instance data          */
00064     ReflectInput,          /* Handle read request                         */
00065     ReflectOutput,         /* Handle write request                        */
00066     ReflectSeek,           /* Move location of access point.   NULL'able  */
00067     ReflectSetOption,      /* Set options.                     NULL'able  */
00068     ReflectGetOption,      /* Get options.                     NULL'able  */
00069     ReflectWatch,          /* Initialize notifier                         */
00070     NULL,                  /* Get OS handle from the channel.  NULL'able  */
00071     NULL,                  /* No close2 support.               NULL'able  */
00072     ReflectBlock,          /* Set blocking/nonblocking.        NULL'able  */
00073     NULL,                  /* Flush channel. Not used by core. NULL'able  */
00074     NULL,                  /* Handle events.                   NULL'able  */
00075     ReflectSeekWide,       /* Move access point (64 bit).      NULL'able  */
00076     NULL,                  /* thread action */
00077     NULL,                  /* truncate */
00078 };
00079 
00080 /*
00081  * Instance data for a reflected channel. ===========================
00082  */
00083 
00084 typedef struct {
00085     Tcl_Channel chan;           /* Back reference to generic channel
00086                                  * structure. */
00087     Tcl_Interp *interp;         /* Reference to the interpreter containing the
00088                                  * Tcl level part of the channel. */
00089 #ifdef TCL_THREADS
00090     Tcl_ThreadId thread;        /* Thread the 'interp' belongs to. */
00091 #endif
00092 
00093     /* See [==] as well.
00094      * Storage for the command prefix and the additional words required for
00095      * the invocation of methods in the command handler.
00096      *
00097      * argv [0] ... [.] | [argc-2] [argc-1] | [argc]  [argc+2]
00098      *      cmd ... pfx | method   chan     | detail1 detail2
00099      *      ~~~~ CT ~~~            ~~ CT ~~
00100      *
00101      * CT = Belongs to the 'Command handler Thread'.
00102      */
00103 
00104     int argc;                   /* Number of preallocated words - 2 */
00105     Tcl_Obj **argv;             /* Preallocated array for calling the handler.
00106                                  * args[0] is placeholder for cmd word.
00107                                  * Followed by the arguments in the prefix,
00108                                  * plus 4 placeholders for method, channel,
00109                                  * and at most two varying (method specific)
00110                                  * words. */
00111     int methods;                /* Bitmask of supported methods */
00112 
00113     /*
00114      * NOTE (9): Should we have predefined shared literals for the method
00115      * names?
00116      */
00117 
00118     int mode;                   /* Mask of R/W mode */
00119     int interest;               /* Mask of events the channel is interested
00120                                  * in. */
00121 
00122     /*
00123      * Note regarding the usage of timers.
00124      *
00125      * Most channel implementations need a timer in the C level to ensure that
00126      * data in buffers is flushed out through the generation of fake file
00127      * events.
00128      *
00129      * See 'rechan', 'memchan', etc.
00130      *
00131      * Here this is _not_ required. Interest in events is posted to the Tcl
00132      * level via 'watch'. And posting of events is possible from the Tcl level
00133      * as well, via 'chan postevent'. This means that the generation of all
00134      * events, fake or not, timer based or not, is completely in the hands of
00135      * the Tcl level. Therefore no timer here.
00136      */
00137 } ReflectedChannel;
00138 
00139 /*
00140  * Structure of the table maping from channel handles to reflected
00141  * channels. Each interpreter which has the handler command for one or more
00142  * reflected channels records them in such a table, so that 'chan postevent'
00143  * is able to find them even if the actual channel was moved to a different
00144  * interpreter and/or thread.
00145  *
00146  * The table is reachable via the standard interpreter AssocData, the key is
00147  * defined below.
00148  */
00149 
00150 typedef struct {
00151     Tcl_HashTable map;
00152 } ReflectedChannelMap;
00153 
00154 #define RCMKEY "ReflectedChannelMap"
00155 
00156 /*
00157  * Event literals. ==================================================
00158  */
00159 
00160 static const char *eventOptions[] = {
00161     "read", "write", NULL
00162 };
00163 typedef enum {
00164     EVENT_READ, EVENT_WRITE
00165 } EventOption;
00166 
00167 /*
00168  * Method literals. ==================================================
00169  */
00170 
00171 static const char *methodNames[] = {
00172     "blocking",         /* OPT */
00173     "cget",             /* OPT \/ Together or none */
00174     "cgetall",          /* OPT /\ of these two     */
00175     "configure",        /* OPT */
00176     "finalize",         /*     */
00177     "initialize",       /*     */
00178     "read",             /* OPT */
00179     "seek",             /* OPT */
00180     "watch",            /*     */
00181     "write",            /* OPT */
00182     NULL
00183 };
00184 typedef enum {
00185     METH_BLOCKING,
00186     METH_CGET,
00187     METH_CGETALL,
00188     METH_CONFIGURE,
00189     METH_FINAL,
00190     METH_INIT,
00191     METH_READ,
00192     METH_SEEK,
00193     METH_WATCH,
00194     METH_WRITE,
00195 } MethodName;
00196 
00197 #define FLAG(m) (1 << (m))
00198 #define REQUIRED_METHODS \
00199         (FLAG(METH_INIT) | FLAG(METH_FINAL) | FLAG(METH_WATCH))
00200 #define NULLABLE_METHODS \
00201         (FLAG(METH_BLOCKING) | FLAG(METH_SEEK) | \
00202         FLAG(METH_CONFIGURE) | FLAG(METH_CGET) | FLAG(METH_CGETALL))
00203 
00204 #define RANDW \
00205         (TCL_READABLE | TCL_WRITABLE)
00206 
00207 #define IMPLIES(a,b)    ((!(a)) || (b))
00208 #define NEGIMPL(a,b)
00209 #define HAS(x,f)        (x & FLAG(f))
00210 
00211 #ifdef TCL_THREADS
00212 /*
00213  * Thread specific types and structures.
00214  *
00215  * We are here essentially creating a very specific implementation of 'thread
00216  * send'.
00217  */
00218 
00219 /*
00220  * Enumeration of all operations which can be forwarded.
00221  */
00222 
00223 typedef enum {
00224     ForwardedClose,
00225     ForwardedInput,
00226     ForwardedOutput,
00227     ForwardedSeek,
00228     ForwardedWatch,
00229     ForwardedBlock,
00230     ForwardedSetOpt,
00231     ForwardedGetOpt,
00232     ForwardedGetOptAll
00233 } ForwardedOperation;
00234 
00235 /*
00236  * Event used to forward driver invocations to the thread actually managing
00237  * the channel. We cannot construct the command to execute and forward that.
00238  * Because then it will contain a mixture of Tcl_Obj's belonging to both the
00239  * command handler thread (CT), and the thread managing the channel (MT),
00240  * executed in CT. Tcl_Obj's are not allowed to cross thread boundaries. So we
00241  * forward an operation code, the argument details, and reference to results.
00242  * The command is assembled in the CT and belongs fully to that thread. No
00243  * sharing problems.
00244  */
00245 
00246 typedef struct ForwardParamBase {
00247     int code;                   /* O: Ok/Fail of the cmd handler */
00248     char *msgStr;               /* O: Error message for handler failure */
00249     int mustFree;               /* O: True if msgStr is allocated, false if
00250                                  * otherwise (static). */
00251 } ForwardParamBase;
00252 
00253 /*
00254  * Operation specific parameter/result structures. (These are "subtypes" of
00255  * ForwardParamBase. Where an operation does not need any special types, it
00256  * has no "subtype" and just uses ForwardParamBase, as listed above.)
00257  */
00258 
00259 struct ForwardParamInput {
00260     ForwardParamBase base;      /* "Supertype". MUST COME FIRST. */
00261     char *buf;                  /* O: Where to store the read bytes */
00262     int toRead;                 /* I: #bytes to read,
00263                                  * O: #bytes actually read */
00264 };
00265 struct ForwardParamOutput {
00266     ForwardParamBase base;      /* "Supertype". MUST COME FIRST. */
00267     const char *buf;            /* I: Where the bytes to write come from */
00268     int toWrite;                /* I: #bytes to write,
00269                                  * O: #bytes actually written */
00270 };
00271 struct ForwardParamSeek {
00272     ForwardParamBase base;      /* "Supertype". MUST COME FIRST. */
00273     int seekMode;               /* I: How to seek */
00274     Tcl_WideInt offset;         /* I: Where to seek,
00275                                  * O: New location */
00276 };
00277 struct ForwardParamWatch {
00278     ForwardParamBase base;      /* "Supertype". MUST COME FIRST. */
00279     int mask;                   /* I: What events to watch for */
00280 };
00281 struct ForwardParamBlock {
00282     ForwardParamBase base;      /* "Supertype". MUST COME FIRST. */
00283     int nonblocking;            /* I: What mode to activate */
00284 };
00285 struct ForwardParamSetOpt {
00286     ForwardParamBase base;      /* "Supertype". MUST COME FIRST. */
00287     const char *name;           /* Name of option to set */
00288     const char *value;          /* Value to set */
00289 };
00290 struct ForwardParamGetOpt {
00291     ForwardParamBase base;      /* "Supertype". MUST COME FIRST. */
00292     const char *name;           /* Name of option to get, maybe NULL */
00293     Tcl_DString *value;         /* Result */
00294 };
00295 
00296 /*
00297  * Now join all these together in a single union for convenience.
00298  */
00299 
00300 typedef union ForwardParam {
00301     ForwardParamBase base;
00302     struct ForwardParamInput input;
00303     struct ForwardParamOutput output;
00304     struct ForwardParamSeek seek;
00305     struct ForwardParamWatch watch;
00306     struct ForwardParamBlock block;
00307     struct ForwardParamSetOpt setOpt;
00308     struct ForwardParamGetOpt getOpt;
00309 } ForwardParam;
00310 
00311 /*
00312  * Forward declaration.
00313  */
00314 
00315 typedef struct ForwardingResult ForwardingResult;
00316 
00317 /*
00318  * General event structure, with reference to operation specific data.
00319  */
00320 
00321 typedef struct ForwardingEvent {
00322     Tcl_Event event;            /* Basic event data, has to be first item */
00323     ForwardingResult *resultPtr;
00324     ForwardedOperation op;      /* Forwarded driver operation */
00325     ReflectedChannel *rcPtr;    /* Channel instance */
00326     ForwardParam *param;        /* Packaged arguments and return values, a
00327                                  * ForwardParam pointer. */
00328 } ForwardingEvent;
00329 
00330 /*
00331  * Structure to manage the result of the forwarding. This is not the result of
00332  * the operation itself, but about the success of the forward event itself.
00333  * The event can be successful, even if the operation which was forwarded
00334  * failed. It is also there to manage the synchronization between the involved
00335  * threads.
00336  */
00337 
00338 struct ForwardingResult {
00339     Tcl_ThreadId src;           /* Originating thread. */
00340     Tcl_ThreadId dst;           /* Thread the op was forwarded to. */
00341     Tcl_Condition done;         /* Condition variable the forwarder blocks
00342                                  * on. */
00343     int result;                 /* TCL_OK or TCL_ERROR */
00344     ForwardingEvent *evPtr;     /* Event the result belongs to. */
00345     ForwardingResult *prevPtr, *nextPtr;
00346                                 /* Links into the list of pending forwarded
00347                                  * results. */
00348 };
00349 
00350 /*
00351  * List of forwarded operations which have not completed yet, plus the mutex
00352  * to protect the access to this process global list.
00353  */
00354 
00355 static ForwardingResult *forwardList = NULL;
00356 TCL_DECLARE_MUTEX(rcForwardMutex)
00357 
00358 /*
00359  * Function containing the generic code executing a forward, and wrapper
00360  * macros for the actual operations we wish to forward. Uses ForwardProc as
00361  * the event function executed by the thread receiving a forwarding event
00362  * (which executes the appropriate function and collects the result, if any).
00363  *
00364  * The two ExitProcs are handlers so that things do not deadlock when either
00365  * thread involved in the forwarding exits. They also clean things up so that
00366  * we don't leak resources when threads go away.
00367  */
00368 
00369 static void             ForwardOpToOwnerThread(ReflectedChannel *rcPtr,
00370                             ForwardedOperation op, const VOID *param);
00371 static int              ForwardProc(Tcl_Event *evPtr, int mask);
00372 static void             SrcExitProc(ClientData clientData);
00373 static void             DstExitProc(ClientData clientData);
00374 
00375 #define FreeReceivedError(p) \
00376         if ((p)->base.mustFree) { \
00377             ckfree((p)->base.msgStr); \
00378         }
00379 #define PassReceivedErrorInterp(i,p) \
00380         if ((i) != NULL) { \
00381             Tcl_SetChannelErrorInterp((i), \
00382                     Tcl_NewStringObj((p)->base.msgStr, -1)); \
00383         } \
00384         FreeReceivedError(p)
00385 #define PassReceivedError(c,p) \
00386         Tcl_SetChannelError((c), Tcl_NewStringObj((p)->base.msgStr, -1)); \
00387         FreeReceivedError(p)
00388 #define ForwardSetStaticError(p,emsg) \
00389         (p)->base.code = TCL_ERROR; \
00390         (p)->base.mustFree = 0; \
00391         (p)->base.msgStr = (char *) (emsg)
00392 #define ForwardSetDynamicError(p,emsg) \
00393         (p)->base.code = TCL_ERROR; \
00394         (p)->base.mustFree = 1; \
00395         (p)->base.msgStr = (char *) (emsg)
00396 
00397 static void             ForwardSetObjError(ForwardParam *p, Tcl_Obj *objPtr);
00398 #endif /* TCL_THREADS */
00399 
00400 #define SetChannelErrorStr(c,msgStr) \
00401         Tcl_SetChannelError((c), Tcl_NewStringObj((msgStr), -1))
00402 
00403 static Tcl_Obj *        MarshallError(Tcl_Interp *interp);
00404 static void             UnmarshallErrorResult(Tcl_Interp *interp,
00405                             Tcl_Obj *msgObj);
00406 
00407 /*
00408  * Static functions for this file:
00409  */
00410 
00411 static int              EncodeEventMask(Tcl_Interp *interp,
00412                             const char *objName, Tcl_Obj *obj, int *mask);
00413 static Tcl_Obj *        DecodeEventMask(int mask);
00414 static ReflectedChannel * NewReflectedChannel(Tcl_Interp *interp,
00415                             Tcl_Obj *cmdpfxObj, int mode, Tcl_Obj *handleObj);
00416 static Tcl_Obj *        NextHandle(void);
00417 static void             FreeReflectedChannel(ReflectedChannel *rcPtr);
00418 static int              InvokeTclMethod(ReflectedChannel *rcPtr,
00419                             const char *method, Tcl_Obj *argOneObj,
00420                             Tcl_Obj *argTwoObj, Tcl_Obj **resultObjPtr);
00421 
00422 static ReflectedChannelMap *    GetReflectedChannelMap(Tcl_Interp *interp);
00423 static void             DeleteReflectedChannelMap(ClientData clientData,
00424                             Tcl_Interp *interp);
00425 
00426 /*
00427  * Global constant strings (messages). ==================
00428  * These string are used directly as bypass errors, thus they have to be valid
00429  * Tcl lists where the last element is the message itself. Hence the
00430  * list-quoting to keep the words of the message together. See also [x].
00431  */
00432 
00433 static const char *msg_read_unsup = "{read not supported by Tcl driver}";
00434 static const char *msg_read_toomuch = "{read delivered more than requested}";
00435 static const char *msg_write_unsup = "{write not supported by Tcl driver}";
00436 static const char *msg_write_toomuch = "{write wrote more than requested}";
00437 static const char *msg_seek_beforestart = "{Tried to seek before origin}";
00438 #ifdef TCL_THREADS
00439 static const char *msg_send_originlost = "{Origin thread lost}";
00440 static const char *msg_send_dstlost = "{Destination thread lost}";
00441 #endif /* TCL_THREADS */
00442 
00443 /*
00444  * Main methods to plug into the 'chan' ensemble'. ==================
00445  */
00446 
00447 /*
00448  *----------------------------------------------------------------------
00449  *
00450  * TclChanCreateObjCmd --
00451  *
00452  *      This function is invoked to process the "chan create" Tcl command.
00453  *      See the user documentation for details on what it does.
00454  *
00455  * Results:
00456  *      A standard Tcl result. The handle of the new channel is placed in the
00457  *      interp result.
00458  *
00459  * Side effects:
00460  *      Creates a new channel.
00461  *
00462  *----------------------------------------------------------------------
00463  */
00464 
00465 int
00466 TclChanCreateObjCmd(
00467     ClientData clientData,
00468     Tcl_Interp *interp,
00469     int objc,
00470     Tcl_Obj *const *objv)
00471 {
00472     ReflectedChannel *rcPtr;    /* Instance data of the new channel */
00473     Tcl_Obj *rcId;              /* Handle of the new channel */
00474     int mode;                   /* R/W mode of new channel. Has to match
00475                                  * abilities of handler commands */
00476     Tcl_Obj *cmdObj;            /* Command prefix, list of words */
00477     Tcl_Obj *cmdNameObj;        /* Command name */
00478     Tcl_Channel chan;           /* Token for the new channel */
00479     Tcl_Obj *modeObj;           /* mode in obj form for method call */
00480     int listc;                  /* Result of 'initialize', and of */
00481     Tcl_Obj **listv;            /* its sublist in the 2nd element */
00482     int methIndex;              /* Encoded method name */
00483     int result;                 /* Result code for 'initialize' */
00484     Tcl_Obj *resObj;            /* Result data for 'initialize' */
00485     int methods;                /* Bitmask for supported methods. */
00486     Channel *chanPtr;           /* 'chan' resolved to internal struct. */
00487     Tcl_Obj *err;               /* Error message */
00488     ReflectedChannelMap* rcmPtr; /* Map of reflected channels with handlers in this interp */
00489     Tcl_HashEntry* hPtr;         /* Entry in the above map */
00490     int isNew;                   /* Placeholder. */
00491 
00492     /*
00493      * Syntax:   chan create MODE CMDPREFIX
00494      *           [0]  [1]    [2]  [3]
00495      *
00496      * Actually: rCreate MODE CMDPREFIX
00497      *           [0]     [1]  [2]
00498      */
00499 
00500 #define MODE    (1)
00501 #define CMD     (2)
00502 
00503     /*
00504      * Number of arguments...
00505      */
00506 
00507     if (objc != 3) {
00508         Tcl_WrongNumArgs(interp, 1, objv, "mode cmdprefix");
00509         return TCL_ERROR;
00510     }
00511 
00512     /*
00513      * First argument is a list of modes. Allowed entries are "read", "write".
00514      * Expect at least one list element. Abbreviations are ok.
00515      */
00516 
00517     modeObj = objv[MODE];
00518     if (EncodeEventMask(interp, "mode", objv[MODE], &mode) != TCL_OK) {
00519         return TCL_ERROR;
00520     }
00521 
00522     /*
00523      * Second argument is command prefix, i.e. list of words, first word is
00524      * name of handler command, other words are fixed arguments. Run the
00525      * 'initialize' method to get the list of supported methods. Validate
00526      * this.
00527      */
00528 
00529     cmdObj = objv[CMD];
00530 
00531     /*
00532      * Basic check that the command prefix truly is a list.
00533      */
00534 
00535     if (Tcl_ListObjIndex(interp, cmdObj, 0, &cmdNameObj) != TCL_OK) {
00536         return TCL_ERROR;
00537     }
00538 
00539     /*
00540      * Now create the channel.
00541      */
00542 
00543     rcId = NextHandle();
00544     rcPtr = NewReflectedChannel(interp, cmdObj, mode, rcId);
00545     chan = Tcl_CreateChannel(&tclRChannelType, TclGetString(rcId), rcPtr,
00546             mode);
00547     rcPtr->chan = chan;
00548     chanPtr = (Channel *) chan;
00549 
00550     /*
00551      * Invoke 'initialize' and validate that the handler is present and ok.
00552      * Squash the channel if not.
00553      *
00554      * Note: The conversion of 'mode' back into a Tcl_Obj ensures that
00555      * 'initialize' is invoked with canonical mode names, and no
00556      * abbreviations. Using modeObj directly could feed abbreviations into the
00557      * handler, and the handler is not specified to handle such.
00558      */
00559 
00560     modeObj = DecodeEventMask(mode);
00561     result = InvokeTclMethod(rcPtr, "initialize", modeObj, NULL, &resObj);
00562     Tcl_DecrRefCount(modeObj);
00563     if (result != TCL_OK) {
00564         UnmarshallErrorResult(interp, resObj);
00565         Tcl_DecrRefCount(resObj);       /* Remove reference held from invoke */
00566         goto error;
00567     }
00568 
00569     /*
00570      * Verify the result.
00571      * - List, of method names. Convert to mask.
00572      *   Check for non-optionals through the mask.
00573      *   Compare open mode against optional r/w.
00574      */
00575 
00576     if (Tcl_ListObjGetElements(NULL, resObj, &listc, &listv) != TCL_OK) {
00577         TclNewLiteralStringObj(err, "chan handler \"");
00578         Tcl_AppendObjToObj(err, cmdObj);
00579         Tcl_AppendToObj(err, " initialize\" returned non-list: ", -1);
00580         Tcl_AppendObjToObj(err, resObj);
00581         Tcl_SetObjResult(interp, err);
00582         Tcl_DecrRefCount(resObj);
00583         goto error;
00584     }
00585 
00586     methods = 0;
00587     while (listc > 0) {
00588         if (Tcl_GetIndexFromObj(interp, listv[listc-1], methodNames,
00589                 "method", TCL_EXACT, &methIndex) != TCL_OK) {
00590             TclNewLiteralStringObj(err, "chan handler \"");
00591             Tcl_AppendObjToObj(err, cmdObj);
00592             Tcl_AppendToObj(err, " initialize\" returned ", -1);
00593             Tcl_AppendObjToObj(err, Tcl_GetObjResult(interp));
00594             Tcl_SetObjResult(interp, err);
00595             Tcl_DecrRefCount(resObj);
00596             goto error;
00597         }
00598 
00599         methods |= FLAG(methIndex);
00600         listc--;
00601     }
00602     Tcl_DecrRefCount(resObj);
00603 
00604     if ((REQUIRED_METHODS & methods) != REQUIRED_METHODS) {
00605         TclNewLiteralStringObj(err, "chan handler \"");
00606         Tcl_AppendObjToObj(err, cmdObj);
00607         Tcl_AppendToObj(err, "\" does not support all required methods", -1);
00608         Tcl_SetObjResult(interp, err);
00609         goto error;
00610     }
00611 
00612     if ((mode & TCL_READABLE) && !HAS(methods, METH_READ)) {
00613         TclNewLiteralStringObj(err, "chan handler \"");
00614         Tcl_AppendObjToObj(err, cmdObj);
00615         Tcl_AppendToObj(err, "\" lacks a \"read\" method", -1);
00616         Tcl_SetObjResult(interp, err);
00617         goto error;
00618     }
00619 
00620     if ((mode & TCL_WRITABLE) && !HAS(methods, METH_WRITE)) {
00621         TclNewLiteralStringObj(err, "chan handler \"");
00622         Tcl_AppendObjToObj(err, cmdObj);
00623         Tcl_AppendToObj(err, "\" lacks a \"write\" method", -1);
00624         Tcl_SetObjResult(interp, err);
00625         goto error;
00626     }
00627 
00628     if (!IMPLIES(HAS(methods, METH_CGET), HAS(methods, METH_CGETALL))) {
00629         TclNewLiteralStringObj(err, "chan handler \"");
00630         Tcl_AppendObjToObj(err, cmdObj);
00631         Tcl_AppendToObj(err, "\" supports \"cget\" but not \"cgetall\"", -1);
00632         Tcl_SetObjResult(interp, err);
00633         goto error;
00634     }
00635 
00636     if (!IMPLIES(HAS(methods, METH_CGETALL), HAS(methods, METH_CGET))) {
00637         TclNewLiteralStringObj(err, "chan handler \"");
00638         Tcl_AppendObjToObj(err, cmdObj);
00639         Tcl_AppendToObj(err, "\" supports \"cgetall\" but not \"cget\"", -1);
00640         Tcl_SetObjResult(interp, err);
00641         goto error;
00642     }
00643 
00644     Tcl_ResetResult(interp);
00645 
00646     /*
00647      * Everything is fine now.
00648      */
00649 
00650     rcPtr->methods = methods;
00651 
00652     if ((methods & NULLABLE_METHODS) != NULLABLE_METHODS) {
00653         /*
00654          * Some of the nullable methods are not supported. We clone the
00655          * channel type, null the associated C functions, and use the result
00656          * as the actual channel type.
00657          */
00658 
00659         Tcl_ChannelType *clonePtr = (Tcl_ChannelType *)
00660                 ckalloc(sizeof(Tcl_ChannelType));
00661 
00662         memcpy(clonePtr, &tclRChannelType, sizeof(Tcl_ChannelType));
00663 
00664         if (!(methods & FLAG(METH_CONFIGURE))) {
00665             clonePtr->setOptionProc = NULL;
00666         }
00667 
00668         if (!(methods & FLAG(METH_CGET)) && !(methods & FLAG(METH_CGETALL))) {
00669             clonePtr->getOptionProc = NULL;
00670         }
00671         if (!(methods & FLAG(METH_BLOCKING))) {
00672             clonePtr->blockModeProc = NULL;
00673         }
00674         if (!(methods & FLAG(METH_SEEK))) {
00675             clonePtr->seekProc = NULL;
00676             clonePtr->wideSeekProc = NULL;
00677         }
00678 
00679         chanPtr->typePtr = clonePtr;
00680     }
00681 
00682     /*
00683      * Register the channel in the I/O system, and in our our map for 'chan
00684      * postevent'.
00685      */
00686 
00687     Tcl_RegisterChannel(interp, chan);
00688 
00689     rcmPtr = GetReflectedChannelMap (interp);
00690     hPtr   = Tcl_CreateHashEntry(&rcmPtr->map,
00691                                  chanPtr->state->channelName, &isNew);
00692     if (!isNew) {
00693         if (chanPtr != Tcl_GetHashValue(hPtr)) {
00694             Tcl_Panic("TclChanCreateObjCmd: duplicate channel names");
00695         }
00696     }
00697     Tcl_SetHashValue(hPtr, chan);
00698 
00699     /*
00700      * Return handle as result of command.
00701      */
00702 
00703     Tcl_SetObjResult(interp, rcId);
00704     return TCL_OK;
00705 
00706  error:
00707     /*
00708      * Signal to ReflectClose to not call 'finalize'.
00709      */
00710 
00711     rcPtr->methods = 0;
00712     Tcl_Close(interp, chan);
00713     return TCL_ERROR;
00714 
00715 #undef MODE
00716 #undef CMD
00717 }
00718 
00719 /*
00720  *----------------------------------------------------------------------
00721  *
00722  * TclChanPostEventObjCmd --
00723  *
00724  *      This function is invoked to process the "chan postevent" Tcl command.
00725  *      See the user documentation for details on what it does.
00726  *
00727  * Results:
00728  *      A standard Tcl result.
00729  *
00730  * Side effects:
00731  *      Posts events to a reflected channel, invokes event handlers. The
00732  *      latter implies that arbitrary side effects are possible.
00733  *
00734  *----------------------------------------------------------------------
00735  */
00736 
00737 int
00738 TclChanPostEventObjCmd(
00739     ClientData clientData,
00740     Tcl_Interp *interp,
00741     int objc,
00742     Tcl_Obj *const *objv)
00743 {
00744     /*
00745      * Syntax:   chan postevent CHANNEL EVENTSPEC
00746      *           [0]  [1]       [2]     [3]
00747      *
00748      * Actually: rPostevent CHANNEL EVENTSPEC
00749      *           [0]        [1]     [2]
00750      *
00751      * where EVENTSPEC = {read write ...} (Abbreviations allowed as well).
00752      */
00753 
00754 #define CHAN    (1)
00755 #define EVENT   (2)
00756 
00757     const char *chanId;         /* Tcl level channel handle */
00758     Tcl_Channel chan;           /* Channel associated to the handle */
00759     const Tcl_ChannelType *chanTypePtr;
00760                                 /* Its associated driver structure */
00761     ReflectedChannel *rcPtr;    /* Associated instance data */
00762     int events;                 /* Mask of events to post */
00763     ReflectedChannelMap* rcmPtr; /* Map of reflected channels with handlers in this interp */
00764     Tcl_HashEntry* hPtr;         /* Entry in the above map */
00765 
00766     /*
00767      * Number of arguments...
00768      */
00769 
00770     if (objc != 3) {
00771         Tcl_WrongNumArgs(interp, 1, objv, "channel eventspec");
00772         return TCL_ERROR;
00773     }
00774 
00775     /*
00776      * First argument is a channel, a reflected channel, and the call of this
00777      * command is done from the interp defining the channel handler cmd.
00778      */
00779 
00780     chanId = TclGetString(objv[CHAN]);
00781 
00782     rcmPtr = GetReflectedChannelMap (interp);
00783     hPtr = Tcl_FindHashEntry (&rcmPtr->map, chanId);
00784 
00785     if (hPtr == NULL) {
00786         Tcl_AppendResult(interp, "can not find reflected channel named \"", chanId,
00787                 "\"", NULL);
00788         Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "CHANNEL", chanId, NULL);
00789         return TCL_ERROR;
00790     }
00791 
00792     /*
00793      * Note that the search above subsumes several of the older checks, namely:
00794      *
00795      * (1) Does the channel handle refer to a reflected channel ?
00796      * (2) Is the post event issued from the interpreter holding the handler
00797      *     of the reflected channel ?
00798      *
00799      * A successful search answers yes to both. Because the map holds only
00800      * handles of reflected channels, and only of such whose handler is
00801      * defined in this interpreter.
00802      *
00803      * We keep the old checks for both, for paranioa, but abort now instead of
00804      * throwing errors, as failure now means that our internal datastructures
00805      * have gone seriously haywire.
00806      */
00807 
00808     chan        = Tcl_GetHashValue(hPtr);
00809     chanTypePtr = Tcl_GetChannelType(chan);
00810 
00811     /*
00812      * We use a function referenced by the channel type as our cookie to
00813      * detect calls to non-reflecting channels. The channel type itself is not
00814      * suitable, as it might not be the static definition in this file, but a
00815      * clone thereof. And while we have reserved the name of the type nothing
00816      * in the core checks against violation, so someone else might have
00817      * created a channel type using our name, clashing with ourselves.
00818      */
00819 
00820     if (chanTypePtr->watchProc != &ReflectWatch) {
00821         Tcl_Panic ("TclChanPostEventObjCmd: channel is not a reflected channel");
00822     }
00823 
00824     rcPtr = (ReflectedChannel *) Tcl_GetChannelInstanceData(chan);
00825 
00826     if (rcPtr->interp != interp) {
00827         Tcl_Panic ("TclChanPostEventObjCmd: postevent accepted for call from outside interpreter");
00828     }
00829 
00830     /*
00831      * Second argument is a list of events. Allowed entries are "read",
00832      * "write". Expect at least one list element. Abbreviations are ok.
00833      */
00834 
00835     if (EncodeEventMask(interp, "event", objv[EVENT], &events) != TCL_OK) {
00836         return TCL_ERROR;
00837     }
00838 
00839     /*
00840      * Check that the channel is actually interested in the provided events.
00841      */
00842 
00843     if (events & ~rcPtr->interest) {
00844         Tcl_AppendResult(interp, "tried to post events channel \"", chanId,
00845                 "\" is not interested in", NULL);
00846         return TCL_ERROR;
00847     }
00848 
00849     /*
00850      * We have the channel and the events to post.
00851      */
00852 
00853     Tcl_NotifyChannel(chan, events);
00854 
00855     /*
00856      * Squash interp results left by the event script.
00857      */
00858 
00859     Tcl_ResetResult(interp);
00860     return TCL_OK;
00861 
00862 #undef CHAN
00863 #undef EVENT
00864 }
00865 
00866 /*
00867  * Channel error message marshalling utilities.
00868  */
00869 
00870 static Tcl_Obj*
00871 MarshallError(
00872     Tcl_Interp *interp)
00873 {
00874     /*
00875      * Capture the result status of the interpreter into a string. => List of
00876      * options and values, followed by the error message. The result has
00877      * refCount 0.
00878      */
00879 
00880     Tcl_Obj *returnOpt = Tcl_GetReturnOptions(interp, TCL_ERROR);
00881 
00882     /*
00883      * => returnOpt.refCount == 0. We can append directly.
00884      */
00885 
00886     Tcl_ListObjAppendElement(NULL, returnOpt, Tcl_GetObjResult(interp));
00887     return returnOpt;
00888 }
00889 
00890 static void
00891 UnmarshallErrorResult(
00892     Tcl_Interp *interp,
00893     Tcl_Obj *msgObj)
00894 {
00895     int lc;
00896     Tcl_Obj **lv;
00897     int explicitResult;
00898     int numOptions;
00899 
00900     /*
00901      * Process the caught message.
00902      *
00903      * Syntax = (option value)... ?message?
00904      *
00905      * Bad syntax causes a panic. This is OK because the other side uses
00906      * Tcl_GetReturnOptions and list construction functions to marshall the
00907      * information; if we panic here, something has gone badly wrong already.
00908      */
00909 
00910     if (Tcl_ListObjGetElements(interp, msgObj, &lc, &lv) != TCL_OK) {
00911         Tcl_Panic("TclChanCaughtErrorBypass: Bad syntax of caught result");
00912     }
00913     if (interp == NULL) {
00914         return;
00915     }
00916 
00917     explicitResult = lc & 1;            /* Odd number of values? */
00918     numOptions = lc - explicitResult;
00919 
00920     if (explicitResult) {
00921         Tcl_SetObjResult(interp, lv[lc-1]);
00922     }
00923 
00924     (void) Tcl_SetReturnOptions(interp, Tcl_NewListObj(numOptions, lv));
00925     ((Interp *)interp)->flags &= ~ERR_ALREADY_LOGGED;
00926 }
00927 
00928 int
00929 TclChanCaughtErrorBypass(
00930     Tcl_Interp *interp,
00931     Tcl_Channel chan)
00932 {
00933     Tcl_Obj *chanMsgObj = NULL;
00934     Tcl_Obj *interpMsgObj = NULL;
00935     Tcl_Obj *msgObj = NULL;
00936 
00937     /*
00938      * Get a bypassed error message from channel and/or interpreter, save the
00939      * reference, then kill the returned objects, if there were any. If there
00940      * are messages in both the channel has preference.
00941      */
00942 
00943     if ((chan == NULL) && (interp == NULL)) {
00944         return 0;
00945     }
00946 
00947     if (chan != NULL) {
00948         Tcl_GetChannelError(chan, &chanMsgObj);
00949     }
00950     if (interp != NULL) {
00951         Tcl_GetChannelErrorInterp(interp, &interpMsgObj);
00952     }
00953 
00954     if (chanMsgObj != NULL) {
00955         msgObj = chanMsgObj;
00956     } else if (interpMsgObj != NULL) {
00957         msgObj = interpMsgObj;
00958     }
00959     if (msgObj != NULL) {
00960         Tcl_IncrRefCount(msgObj);
00961     }
00962 
00963     if (chanMsgObj != NULL) {
00964         Tcl_DecrRefCount(chanMsgObj);
00965     }
00966     if (interpMsgObj != NULL) {
00967         Tcl_DecrRefCount(interpMsgObj);
00968     }
00969 
00970     /*
00971      * No message returned, nothing caught.
00972      */
00973 
00974     if (msgObj == NULL) {
00975         return 0;
00976     }
00977 
00978     UnmarshallErrorResult(interp, msgObj);
00979 
00980     Tcl_DecrRefCount(msgObj);
00981     return 1;
00982 }
00983 
00984 /*
00985  * Driver functions. ================================================
00986  */
00987 
00988 /*
00989  *----------------------------------------------------------------------
00990  *
00991  * ReflectClose --
00992  *
00993  *      This function is invoked when the channel is closed, to delete the
00994  *      driver specific instance data.
00995  *
00996  * Results:
00997  *      A posix error.
00998  *
00999  * Side effects:
01000  *      Releases memory. Arbitrary, as it calls upon a script.
01001  *
01002  *----------------------------------------------------------------------
01003  */
01004 
01005 static int
01006 ReflectClose(
01007     ClientData clientData,
01008     Tcl_Interp *interp)
01009 {
01010     ReflectedChannel *rcPtr = (ReflectedChannel *) clientData;
01011     int result;                 /* Result code for 'close' */
01012     Tcl_Obj *resObj;            /* Result data for 'close' */
01013 
01014     if (interp == NULL) {
01015         /*
01016          * This call comes from TclFinalizeIOSystem. There are no
01017          * interpreters, and therefore we cannot call upon the handler command
01018          * anymore. Threading is irrelevant as well. We simply clean up all
01019          * our C level data structures and leave the Tcl level to the other
01020          * finalization functions.
01021          */
01022 
01023         /*
01024          * THREADED => Forward this to the origin thread
01025          *
01026          * Note: Have a thread delete handler for the origin thread. Use this
01027          * to clean up the structure!
01028          */
01029 
01030 #ifdef TCL_THREADS
01031         if (rcPtr->thread != Tcl_GetCurrentThread()) {
01032             ForwardParam p;
01033 
01034             ForwardOpToOwnerThread(rcPtr, ForwardedClose, &p);
01035             result = p.base.code;
01036 
01037             /*
01038              * FreeReflectedChannel is done in the forwarded operation!, in
01039              * the other thread. rcPtr here is gone!
01040              */
01041 
01042             if (result != TCL_OK) {
01043                 FreeReceivedError(&p);
01044             }
01045             return EOK;
01046         }
01047 #endif
01048 
01049         FreeReflectedChannel(rcPtr);
01050         return EOK;
01051     }
01052 
01053     /*
01054      * -- No -- ASSERT rcPtr->methods & FLAG(METH_FINAL)
01055      *
01056      * A cleaned method mask here implies that the channel creation was
01057      * aborted, and "finalize" must not be called.
01058      */
01059 
01060     if (rcPtr->methods == 0) {
01061         FreeReflectedChannel(rcPtr);
01062         return EOK;
01063     }
01064 
01065     /*
01066      * Are we in the correct thread?
01067      */
01068 
01069 #ifdef TCL_THREADS
01070     if (rcPtr->thread != Tcl_GetCurrentThread()) {
01071         ForwardParam p;
01072 
01073         ForwardOpToOwnerThread(rcPtr, ForwardedClose, &p);
01074         result = p.base.code;
01075 
01076         /*
01077          * FreeReflectedChannel is done in the forwarded operation!, in the
01078          * other thread. rcPtr here is gone!
01079          */
01080 
01081         if (result != TCL_OK) {
01082             PassReceivedErrorInterp(interp, &p);
01083         }
01084     } else {
01085 #endif
01086         result = InvokeTclMethod(rcPtr, "finalize", NULL, NULL, &resObj);
01087         if ((result != TCL_OK) && (interp != NULL)) {
01088             Tcl_SetChannelErrorInterp(interp, resObj);
01089         }
01090 
01091         Tcl_DecrRefCount(resObj);       /* Remove reference we held from the
01092                                          * invoke */
01093         FreeReflectedChannel(rcPtr);
01094 #ifdef TCL_THREADS
01095     }
01096 #endif
01097     return (result == TCL_OK) ? EOK : EINVAL;
01098 }
01099 
01100 /*
01101  *----------------------------------------------------------------------
01102  *
01103  * ReflectInput --
01104  *
01105  *      This function is invoked when more data is requested from the channel.
01106  *
01107  * Results:
01108  *      The number of bytes read.
01109  *
01110  * Side effects:
01111  *      Allocates memory. Arbitrary, as it calls upon a script.
01112  *
01113  *----------------------------------------------------------------------
01114  */
01115 
01116 static int
01117 ReflectInput(
01118     ClientData clientData,
01119     char *buf,
01120     int toRead,
01121     int *errorCodePtr)
01122 {
01123     ReflectedChannel *rcPtr = (ReflectedChannel *) clientData;
01124     Tcl_Obj *toReadObj;
01125     int bytec;                  /* Number of returned bytes */
01126     unsigned char *bytev;       /* Array of returned bytes */
01127     Tcl_Obj *resObj;            /* Result data for 'read' */
01128 
01129     /*
01130      * The following check can be done before thread redirection, because we
01131      * are reading from an item which is readonly, i.e. will never change
01132      * during the lifetime of the channel.
01133      */
01134 
01135     if (!(rcPtr->methods & FLAG(METH_READ))) {
01136         SetChannelErrorStr(rcPtr->chan, msg_read_unsup);
01137         *errorCodePtr = EINVAL;
01138         return -1;
01139     }
01140 
01141     /*
01142      * Are we in the correct thread?
01143      */
01144 
01145 #ifdef TCL_THREADS
01146     if (rcPtr->thread != Tcl_GetCurrentThread()) {
01147         ForwardParam p;
01148 
01149         p.input.buf = buf;
01150         p.input.toRead = toRead;
01151 
01152         ForwardOpToOwnerThread(rcPtr, ForwardedInput, &p);
01153 
01154         if (p.base.code != TCL_OK) {
01155             PassReceivedError(rcPtr->chan, &p);
01156             *errorCodePtr = EINVAL;
01157         } else {
01158             *errorCodePtr = EOK;
01159         }
01160 
01161         return p.input.toRead;
01162     }
01163 #endif
01164 
01165     /* ASSERT: rcPtr->method & FLAG(METH_READ) */
01166     /* ASSERT: rcPtr->mode & TCL_READABLE */
01167 
01168     toReadObj = Tcl_NewIntObj(toRead);
01169     if (InvokeTclMethod(rcPtr, "read", toReadObj, NULL, &resObj)!=TCL_OK) {
01170         Tcl_SetChannelError(rcPtr->chan, resObj);
01171         Tcl_DecrRefCount(resObj);       /* Remove reference held from invoke */
01172         *errorCodePtr = EINVAL;
01173         return -1;
01174     }
01175 
01176     bytev = Tcl_GetByteArrayFromObj(resObj, &bytec);
01177 
01178     if (toRead < bytec) {
01179         Tcl_DecrRefCount(resObj);       /* Remove reference held from invoke */
01180         SetChannelErrorStr(rcPtr->chan, msg_read_toomuch);
01181         *errorCodePtr = EINVAL;
01182         return -1;
01183     }
01184 
01185     *errorCodePtr = EOK;
01186 
01187     if (bytec > 0) {
01188         memcpy(buf, bytev, (size_t)bytec);
01189     }
01190 
01191     Tcl_DecrRefCount(resObj);           /* Remove reference held from invoke */
01192     return bytec;
01193 }
01194 
01195 /*
01196  *----------------------------------------------------------------------
01197  *
01198  * ReflectOutput --
01199  *
01200  *      This function is invoked when data is writen to the channel.
01201  *
01202  * Results:
01203  *      The number of bytes actually written.
01204  *
01205  * Side effects:
01206  *      Allocates memory. Arbitrary, as it calls upon a script.
01207  *
01208  *----------------------------------------------------------------------
01209  */
01210 
01211 static int
01212 ReflectOutput(
01213     ClientData clientData,
01214     const char *buf,
01215     int toWrite,
01216     int *errorCodePtr)
01217 {
01218     ReflectedChannel *rcPtr = (ReflectedChannel *) clientData;
01219     Tcl_Obj *bufObj;
01220     Tcl_Obj *resObj;            /* Result data for 'write' */
01221     int written;
01222 
01223     /*
01224      * The following check can be done before thread redirection, because we
01225      * are reading from an item which is readonly, i.e. will never change
01226      * during the lifetime of the channel.
01227      */
01228 
01229     if (!(rcPtr->methods & FLAG(METH_WRITE))) {
01230         SetChannelErrorStr(rcPtr->chan, msg_write_unsup);
01231         *errorCodePtr = EINVAL;
01232         return -1;
01233     }
01234 
01235     /*
01236      * Are we in the correct thread?
01237      */
01238 
01239 #ifdef TCL_THREADS
01240     if (rcPtr->thread != Tcl_GetCurrentThread()) {
01241         ForwardParam p;
01242 
01243         p.output.buf = buf;
01244         p.output.toWrite = toWrite;
01245 
01246         ForwardOpToOwnerThread(rcPtr, ForwardedOutput, &p);
01247 
01248         if (p.base.code != TCL_OK) {
01249             PassReceivedError(rcPtr->chan, &p);
01250             *errorCodePtr = EINVAL;
01251         } else {
01252             *errorCodePtr = EOK;
01253         }
01254 
01255         return p.output.toWrite;
01256     }
01257 #endif
01258 
01259     /* ASSERT: rcPtr->method & FLAG(METH_WRITE) */
01260     /* ASSERT: rcPtr->mode & TCL_WRITABLE */
01261 
01262     bufObj = Tcl_NewByteArrayObj((unsigned char *) buf, toWrite);
01263     if (InvokeTclMethod(rcPtr, "write", bufObj, NULL, &resObj) != TCL_OK) {
01264         Tcl_SetChannelError(rcPtr->chan, resObj);
01265         Tcl_DecrRefCount(resObj);       /* Remove reference held from invoke */
01266         *errorCodePtr = EINVAL;
01267         return -1;
01268     }
01269 
01270     if (Tcl_GetIntFromObj(rcPtr->interp, resObj, &written) != TCL_OK) {
01271         Tcl_DecrRefCount(resObj);       /* Remove reference held from invoke */
01272         Tcl_SetChannelError(rcPtr->chan, MarshallError(rcPtr->interp));
01273         *errorCodePtr = EINVAL;
01274         return -1;
01275     }
01276 
01277     Tcl_DecrRefCount(resObj);           /* Remove reference held from invoke */
01278 
01279     if ((written == 0) || (toWrite < written)) {
01280         /*
01281          * The handler claims to have written more than it was given. That is
01282          * bad. Note that the I/O core would crash if we were to return this
01283          * information, trying to write -nnn bytes in the next iteration.
01284          */
01285 
01286         SetChannelErrorStr(rcPtr->chan, msg_write_toomuch);
01287         *errorCodePtr = EINVAL;
01288         return -1;
01289     }
01290 
01291     *errorCodePtr = EOK;
01292     return written;
01293 }
01294 
01295 /*
01296  *----------------------------------------------------------------------
01297  *
01298  * ReflectSeekWide / ReflectSeek --
01299  *
01300  *      This function is invoked when the user wishes to seek on the channel.
01301  *
01302  * Results:
01303  *      The new location of the access point.
01304  *
01305  * Side effects:
01306  *      Allocates memory. Arbitrary, as it calls upon a script.
01307  *
01308  *----------------------------------------------------------------------
01309  */
01310 
01311 static Tcl_WideInt
01312 ReflectSeekWide(
01313     ClientData clientData,
01314     Tcl_WideInt offset,
01315     int seekMode,
01316     int *errorCodePtr)
01317 {
01318     ReflectedChannel *rcPtr = (ReflectedChannel *) clientData;
01319     Tcl_Obj *offObj, *baseObj;
01320     Tcl_Obj *resObj;            /* Result for 'seek' */
01321     Tcl_WideInt newLoc;
01322 
01323     /*
01324      * Are we in the correct thread?
01325      */
01326 
01327 #ifdef TCL_THREADS
01328     if (rcPtr->thread != Tcl_GetCurrentThread()) {
01329         ForwardParam p;
01330 
01331         p.seek.seekMode = seekMode;
01332         p.seek.offset = offset;
01333 
01334         ForwardOpToOwnerThread(rcPtr, ForwardedSeek, &p);
01335 
01336         if (p.base.code != TCL_OK) {
01337             PassReceivedError(rcPtr->chan, &p);
01338             *errorCodePtr = EINVAL;
01339         } else {
01340             *errorCodePtr = EOK;
01341         }
01342 
01343         return p.seek.offset;
01344     }
01345 #endif
01346 
01347     /* ASSERT: rcPtr->method & FLAG(METH_SEEK) */
01348 
01349     offObj = Tcl_NewWideIntObj(offset);
01350     baseObj = Tcl_NewStringObj((seekMode == SEEK_SET) ? "start" :
01351             ((seekMode == SEEK_CUR) ? "current" : "end"), -1);
01352     if (InvokeTclMethod(rcPtr, "seek", offObj, baseObj, &resObj)!=TCL_OK) {
01353         Tcl_SetChannelError(rcPtr->chan, resObj);
01354         Tcl_DecrRefCount(resObj);       /* Remove reference held from invoke */
01355         *errorCodePtr = EINVAL;
01356         return -1;
01357     }
01358 
01359     if (Tcl_GetWideIntFromObj(rcPtr->interp, resObj, &newLoc) != TCL_OK) {
01360         Tcl_DecrRefCount(resObj);       /* Remove reference held from invoke */
01361         Tcl_SetChannelError(rcPtr->chan, MarshallError(rcPtr->interp));
01362         *errorCodePtr = EINVAL;
01363         return -1;
01364     }
01365 
01366     Tcl_DecrRefCount(resObj);           /* Remove reference held from invoke */
01367 
01368     if (newLoc < Tcl_LongAsWide(0)) {
01369         SetChannelErrorStr(rcPtr->chan, msg_seek_beforestart);
01370         *errorCodePtr = EINVAL;
01371         return -1;
01372     }
01373 
01374     *errorCodePtr = EOK;
01375     return newLoc;
01376 }
01377 
01378 static int
01379 ReflectSeek(
01380     ClientData clientData,
01381     long offset,
01382     int seekMode,
01383     int *errorCodePtr)
01384 {
01385     /*
01386      * This function can be invoked from a transformation which is based on
01387      * standard seeking, i.e. non-wide. Because of this we have to implement
01388      * it, a dummy is not enough. We simply delegate the call to the wide
01389      * routine.
01390      */
01391 
01392     return (int) ReflectSeekWide(clientData, Tcl_LongAsWide(offset), seekMode,
01393             errorCodePtr);
01394 }
01395 
01396 /*
01397  *----------------------------------------------------------------------
01398  *
01399  * ReflectWatch --
01400  *
01401  *      This function is invoked to tell the channel what events the I/O
01402  *      system is interested in.
01403  *
01404  * Results:
01405  *      None.
01406  *
01407  * Side effects:
01408  *      Allocates memory. Arbitrary, as it calls upon a script.
01409  *
01410  *----------------------------------------------------------------------
01411  */
01412 
01413 static void
01414 ReflectWatch(
01415     ClientData clientData,
01416     int mask)
01417 {
01418     ReflectedChannel *rcPtr = (ReflectedChannel *) clientData;
01419     Tcl_Obj *maskObj;
01420 
01421     /* ASSERT rcPtr->methods & FLAG(METH_WATCH) */
01422 
01423     /*
01424      * We restrict the interest to what the channel can support. IOW there
01425      * will never be write events for a channel which is not writable.
01426      * Analoguously for read events and non-readable channels.
01427      */
01428 
01429     mask &= rcPtr->mode;
01430 
01431     if (mask == rcPtr->interest) {
01432         /*
01433          * Same old, same old, why should we do something?
01434          */
01435 
01436         return;
01437     }
01438 
01439     rcPtr->interest = mask;
01440 
01441     /*
01442      * Are we in the correct thread?
01443      */
01444 
01445 #ifdef TCL_THREADS
01446     if (rcPtr->thread != Tcl_GetCurrentThread()) {
01447         ForwardParam p;
01448 
01449         p.watch.mask = mask;
01450         ForwardOpToOwnerThread(rcPtr, ForwardedWatch, &p);
01451 
01452         /*
01453          * Any failure from the forward is ignored. We have no place to put
01454          * this.
01455          */
01456 
01457         return;
01458     }
01459 #endif
01460 
01461     maskObj = DecodeEventMask(mask);
01462     (void) InvokeTclMethod(rcPtr, "watch", maskObj, NULL, NULL);
01463     Tcl_DecrRefCount(maskObj);
01464 }
01465 
01466 /*
01467  *----------------------------------------------------------------------
01468  *
01469  * ReflectBlock --
01470  *
01471  *      This function is invoked to tell the channel which blocking behaviour
01472  *      is required of it.
01473  *
01474  * Results:
01475  *      A posix error number.
01476  *
01477  * Side effects:
01478  *      Allocates memory. Arbitrary, as it calls upon a script.
01479  *
01480  *----------------------------------------------------------------------
01481  */
01482 
01483 static int
01484 ReflectBlock(
01485     ClientData clientData,
01486     int nonblocking)
01487 {
01488     ReflectedChannel *rcPtr = (ReflectedChannel *) clientData;
01489     Tcl_Obj *blockObj;
01490     int errorNum;               /* EINVAL or EOK (success). */
01491     Tcl_Obj *resObj;            /* Result data for 'blocking' */
01492 
01493     /*
01494      * Are we in the correct thread?
01495      */
01496 
01497 #ifdef TCL_THREADS
01498     if (rcPtr->thread != Tcl_GetCurrentThread()) {
01499         ForwardParam p;
01500 
01501         p.block.nonblocking = nonblocking;
01502 
01503         ForwardOpToOwnerThread(rcPtr, ForwardedBlock, &p);
01504 
01505         if (p.base.code != TCL_OK) {
01506             PassReceivedError(rcPtr->chan, &p);
01507             return EINVAL;
01508         }
01509 
01510         return EOK;
01511     }
01512 #endif
01513 
01514     blockObj = Tcl_NewBooleanObj(!nonblocking);
01515 
01516     if (InvokeTclMethod(rcPtr, "blocking", blockObj, NULL, &resObj) != TCL_OK) {
01517         Tcl_SetChannelError(rcPtr->chan, resObj);
01518         errorNum = EINVAL;
01519     } else {
01520         errorNum = EOK;
01521     }
01522 
01523     Tcl_DecrRefCount(resObj);           /* Remove reference held from invoke */
01524     return errorNum;
01525 }
01526 
01527 /*
01528  *----------------------------------------------------------------------
01529  *
01530  * ReflectSetOption --
01531  *
01532  *      This function is invoked to configure a channel option.
01533  *
01534  * Results:
01535  *      A standard Tcl result code.
01536  *
01537  * Side effects:
01538  *      Arbitrary, as it calls upon a Tcl script.
01539  *
01540  *----------------------------------------------------------------------
01541  */
01542 
01543 static int
01544 ReflectSetOption(
01545     ClientData clientData,      /* Channel to query */
01546     Tcl_Interp *interp,         /* Interpreter to leave error messages in */
01547     const char *optionName,     /* Name of requested option */
01548     const char *newValue)       /* The new value */
01549 {
01550     ReflectedChannel *rcPtr = (ReflectedChannel *) clientData;
01551     Tcl_Obj *optionObj, *valueObj;
01552     int result;                 /* Result code for 'configure' */
01553     Tcl_Obj *resObj;            /* Result data for 'configure' */
01554 
01555     /*
01556      * Are we in the correct thread?
01557      */
01558 
01559 #ifdef TCL_THREADS
01560     if (rcPtr->thread != Tcl_GetCurrentThread()) {
01561         ForwardParam p;
01562 
01563         p.setOpt.name = optionName;
01564         p.setOpt.value = newValue;
01565 
01566         ForwardOpToOwnerThread(rcPtr, ForwardedSetOpt, &p);
01567 
01568         if (p.base.code != TCL_OK) {
01569             Tcl_Obj *err = Tcl_NewStringObj(p.base.msgStr, -1);
01570 
01571             UnmarshallErrorResult(interp, err);
01572             Tcl_DecrRefCount(err);
01573             FreeReceivedError(&p);
01574         }
01575 
01576         return p.base.code;
01577     }
01578 #endif
01579 
01580     optionObj = Tcl_NewStringObj(optionName, -1);
01581     valueObj = Tcl_NewStringObj(newValue, -1);
01582     result = InvokeTclMethod(rcPtr, "configure",optionObj,valueObj, &resObj);
01583     if (result != TCL_OK) {
01584         UnmarshallErrorResult(interp, resObj);
01585     }
01586 
01587     Tcl_DecrRefCount(resObj);           /* Remove reference held from invoke */
01588     return result;
01589 }
01590 
01591 /*
01592  *----------------------------------------------------------------------
01593  *
01594  * ReflectGetOption --
01595  *
01596  *      This function is invoked to retrieve all or a channel option.
01597  *
01598  * Results:
01599  *      A standard Tcl result code.
01600  *
01601  * Side effects:
01602  *      Arbitrary, as it calls upon a Tcl script.
01603  *
01604  *----------------------------------------------------------------------
01605  */
01606 
01607 static int
01608 ReflectGetOption(
01609     ClientData clientData,      /* Channel to query */
01610     Tcl_Interp *interp,         /* Interpreter to leave error messages in */
01611     const char *optionName,     /* Name of reuqested option */
01612     Tcl_DString *dsPtr)         /* String to place the result into */
01613 {
01614     /*
01615      * This code is special. It has regular passing of Tcl result, and errors.
01616      * The bypass functions are not required.
01617      */
01618 
01619     ReflectedChannel *rcPtr = (ReflectedChannel*) clientData;
01620     Tcl_Obj *optionObj;
01621     Tcl_Obj *resObj;            /* Result data for 'configure' */
01622     int listc;
01623     Tcl_Obj **listv;
01624     const char *method;
01625 
01626     /*
01627      * Are we in the correct thread?
01628      */
01629 
01630 #ifdef TCL_THREADS
01631     if (rcPtr->thread != Tcl_GetCurrentThread()) {
01632         int opcode;
01633         ForwardParam p;
01634 
01635         p.getOpt.name = optionName;
01636         p.getOpt.value = dsPtr;
01637 
01638         if (optionName == NULL) {
01639             opcode = ForwardedGetOptAll;
01640         } else {
01641             opcode = ForwardedGetOpt;
01642         }
01643 
01644         ForwardOpToOwnerThread(rcPtr, opcode, &p);
01645 
01646         if (p.base.code != TCL_OK) {
01647             Tcl_Obj *err = Tcl_NewStringObj(p.base.msgStr, -1);
01648 
01649             UnmarshallErrorResult(interp, err);
01650             Tcl_DecrRefCount(err);
01651             FreeReceivedError(&p);
01652         }
01653 
01654         return p.base.code;
01655     }
01656 #endif
01657 
01658     if (optionName == NULL) {
01659         /*
01660          * Retrieve all options.
01661          */
01662 
01663         method = "cgetall";
01664         optionObj = NULL;
01665     } else {
01666         /*
01667          * Retrieve the value of one option.
01668          */
01669 
01670         method = "cget";
01671         optionObj = Tcl_NewStringObj(optionName, -1);
01672     }
01673 
01674     if (InvokeTclMethod(rcPtr, method, optionObj, NULL, &resObj)!=TCL_OK) {
01675         UnmarshallErrorResult(interp, resObj);
01676         Tcl_DecrRefCount(resObj);       /* Remove reference held from invoke */
01677         return TCL_ERROR;
01678     }
01679 
01680     /*
01681      * The result has to go into the 'dsPtr' for propagation to the caller of
01682      * the driver.
01683      */
01684 
01685     if (optionObj != NULL) {
01686         Tcl_DStringAppend(dsPtr, TclGetString(resObj), -1);
01687         Tcl_DecrRefCount(resObj);       /* Remove reference held from invoke */
01688         return TCL_OK;
01689     }
01690 
01691     /*
01692      * Extract the list and append each item as element.
01693      */
01694 
01695     /*
01696      * NOTE (4): If we extract the string rep we can assume a properly quoted
01697      * string. Together with a separating space this way of simply appending
01698      * the whole string rep might be faster. It also doesn't check if the
01699      * result is a valid list. Nor that the list has an even number elements.
01700      */
01701 
01702     if (Tcl_ListObjGetElements(interp, resObj, &listc, &listv) != TCL_OK) {
01703         Tcl_DecrRefCount(resObj);       /* Remove reference held from invoke */
01704         return TCL_ERROR;
01705     }
01706 
01707     if ((listc % 2) == 1) {
01708         /*
01709          * Odd number of elements is wrong.
01710          */
01711 
01712         Tcl_ResetResult(interp);
01713         Tcl_SetObjResult(interp, Tcl_ObjPrintf(
01714                 "Expected list with even number of "
01715                 "elements, got %d element%s instead", listc,
01716                 (listc == 1 ? "" : "s")));
01717         Tcl_DecrRefCount(resObj);       /* Remove reference held from invoke */
01718         return TCL_ERROR;
01719     } else {
01720         int len;
01721         char *str = Tcl_GetStringFromObj(resObj, &len);
01722 
01723         if (len) {
01724             Tcl_DStringAppend(dsPtr, " ", 1);
01725             Tcl_DStringAppend(dsPtr, str, len);
01726         }
01727         Tcl_DecrRefCount(resObj);       /* Remove reference held from invoke */
01728         return TCL_OK;
01729     }
01730 }
01731 
01732 /*
01733  * Helpers. =========================================================
01734  */
01735 
01736 /*
01737  *----------------------------------------------------------------------
01738  *
01739  * EncodeEventMask --
01740  *
01741  *      This function takes a list of event items and constructs the
01742  *      equivalent internal bitmask. The list must contain at least one
01743  *      element. Elements are "read", "write", or any unique abbreviation of
01744  *      them. Note that the bitmask is not changed if problems are
01745  *      encountered.
01746  *
01747  * Results:
01748  *      A standard Tcl error code. A bitmask where TCL_READABLE and/or
01749  *      TCL_WRITABLE can be set.
01750  *
01751  * Side effects:
01752  *      May shimmer 'obj' to a list representation. May place an error message
01753  *      into the interp result.
01754  *
01755  *----------------------------------------------------------------------
01756  */
01757 
01758 static int
01759 EncodeEventMask(
01760     Tcl_Interp *interp,
01761     const char *objName,
01762     Tcl_Obj *obj,
01763     int *mask)
01764 {
01765     int events;                 /* Mask of events to post */
01766     int listc;                  /* #elements in eventspec list */
01767     Tcl_Obj **listv;            /* Elements of eventspec list */
01768     int evIndex;                /* Id of event for an element of the eventspec
01769                                  * list. */
01770 
01771     if (Tcl_ListObjGetElements(interp, obj, &listc, &listv) != TCL_OK) {
01772         return TCL_ERROR;
01773     }
01774 
01775     if (listc < 1) {
01776         Tcl_AppendResult(interp, "bad ", objName, " list: is empty", NULL);
01777         return TCL_ERROR;
01778     }
01779 
01780     events = 0;
01781     while (listc > 0) {
01782         if (Tcl_GetIndexFromObj(interp, listv[listc-1], eventOptions,
01783                 objName, 0, &evIndex) != TCL_OK) {
01784             return TCL_ERROR;
01785         }
01786         switch (evIndex) {
01787         case EVENT_READ:
01788             events |= TCL_READABLE;
01789             break;
01790         case EVENT_WRITE:
01791             events |= TCL_WRITABLE;
01792             break;
01793         }
01794         listc --;
01795     }
01796 
01797     *mask = events;
01798     return TCL_OK;
01799 }
01800 
01801 /*
01802  *----------------------------------------------------------------------
01803  *
01804  * DecodeEventMask --
01805  *
01806  *      This function takes an internal bitmask of events and constructs the
01807  *      equivalent list of event items.
01808  *
01809  * Results:
01810  *      A Tcl_Obj reference. The object will have a refCount of one. The user
01811  *      has to decrement it to release the object.
01812  *
01813  * Side effects:
01814  *      None.
01815  *
01816  *----------------------------------------------------------------------
01817  */
01818 
01819 static Tcl_Obj *
01820 DecodeEventMask(
01821     int mask)
01822 {
01823     register const char *eventStr;
01824     Tcl_Obj *evObj;
01825 
01826     switch (mask & RANDW) {
01827     case RANDW:
01828         eventStr = "read write";
01829         break;
01830     case TCL_READABLE:
01831         eventStr = "read";
01832         break;
01833     case TCL_WRITABLE:
01834         eventStr = "write";
01835         break;
01836     default:
01837         eventStr = "";
01838         break;
01839     }
01840 
01841     evObj = Tcl_NewStringObj(eventStr, -1);
01842     Tcl_IncrRefCount(evObj);
01843     return evObj;
01844 }
01845 
01846 /*
01847  *----------------------------------------------------------------------
01848  *
01849  * NewReflectedChannel --
01850  *
01851  *      This function is invoked to allocate and initialize the instance data
01852  *      of a new reflected channel.
01853  *
01854  * Results:
01855  *      A heap-allocated channel instance.
01856  *
01857  * Side effects:
01858  *      Allocates memory.
01859  *
01860  *----------------------------------------------------------------------
01861  */
01862 
01863 static ReflectedChannel *
01864 NewReflectedChannel(
01865     Tcl_Interp *interp,
01866     Tcl_Obj *cmdpfxObj,
01867     int mode,
01868     Tcl_Obj *handleObj)
01869 {
01870     ReflectedChannel *rcPtr;
01871     int i, listc;
01872     Tcl_Obj **listv;
01873 
01874     rcPtr = (ReflectedChannel *) ckalloc(sizeof(ReflectedChannel));
01875 
01876     /* rcPtr->chan: Assigned by caller. Dummy data here. */
01877     /* rcPtr->methods: Assigned by caller. Dummy data here. */
01878 
01879     rcPtr->chan = NULL;
01880     rcPtr->methods = 0;
01881     rcPtr->interp = interp;
01882 #ifdef TCL_THREADS
01883     rcPtr->thread = Tcl_GetCurrentThread();
01884 #endif
01885     rcPtr->mode = mode;
01886     rcPtr->interest = 0;                /* Initially no interest registered */
01887 
01888     /*
01889      * Method placeholder.
01890      */
01891 
01892     /* ASSERT: cmdpfxObj is a Tcl List */
01893 
01894     Tcl_ListObjGetElements(interp, cmdpfxObj, &listc, &listv);
01895 
01896     /*
01897      * See [==] as well.
01898      * Storage for the command prefix and the additional words required for
01899      * the invocation of methods in the command handler.
01900      *
01901      * listv [0] [listc-1] | [listc]  [listc+1] |
01902      * argv  [0]   ... [.] | [argc-2] [argc-1]  | [argc]  [argc+2]
01903      *       cmd   ... pfx | method   chan      | detail1 detail2
01904      */
01905 
01906     rcPtr->argc = listc + 2;
01907     rcPtr->argv = (Tcl_Obj **) ckalloc(sizeof(Tcl_Obj *) * (listc+4));
01908 
01909     /*
01910      * Duplicate object references.
01911      */
01912 
01913     for (i=0; i<listc ; i++) {
01914         Tcl_Obj *word = rcPtr->argv[i] = listv[i];
01915 
01916         Tcl_IncrRefCount(word);
01917     }
01918 
01919     i++;                                /* Skip placeholder for method */
01920 
01921     /*
01922      * [Bug 1667990]: See [x] in FreeReflectedChannel for release
01923      */
01924 
01925     rcPtr->argv[i] = handleObj;
01926     Tcl_IncrRefCount(handleObj);
01927 
01928     /*
01929      * The next two objects are kept empty, varying arguments.
01930      */
01931 
01932     /*
01933      * Initialization complete.
01934      */
01935 
01936     return rcPtr;
01937 }
01938 
01939 /*
01940  *----------------------------------------------------------------------
01941  *
01942  * NextHandle --
01943  *
01944  *      This function is invoked to generate a channel handle for a new
01945  *      reflected channel.
01946  *
01947  * Results:
01948  *      A Tcl_Obj containing the string of the new channel handle. The
01949  *      refcount of the returned object is -- zero --.
01950  *
01951  * Side effects:
01952  *      May allocate memory. Mutex protected critical section locks out other
01953  *      threads for a short time.
01954  *
01955  *----------------------------------------------------------------------
01956  */
01957 
01958 static Tcl_Obj *
01959 NextHandle(void)
01960 {
01961     /*
01962      * Count number of generated reflected channels. Used for id generation.
01963      * Ids are never reclaimed and there is no dealing with wrap around. On
01964      * the other hand, "unsigned long" should be big enough except for
01965      * absolute longrunners (generate a 100 ids per second => overflow will
01966      * occur in 1 1/3 years).
01967      */
01968 
01969     TCL_DECLARE_MUTEX(rcCounterMutex)
01970     static unsigned long rcCounter = 0;
01971     Tcl_Obj *resObj;
01972 
01973     Tcl_MutexLock(&rcCounterMutex);
01974     resObj = Tcl_ObjPrintf("rc%lu", rcCounter);
01975     rcCounter++;
01976     Tcl_MutexUnlock(&rcCounterMutex);
01977 
01978     return resObj;
01979 }
01980 
01981 static void
01982 FreeReflectedChannel(
01983     ReflectedChannel *rcPtr)
01984 {
01985     Channel *chanPtr = (Channel *) rcPtr->chan;
01986     int i, n;
01987 
01988     if (chanPtr->typePtr != &tclRChannelType) {
01989         /*
01990          * Delete a cloned ChannelType structure.
01991          */
01992 
01993         ckfree((char*) chanPtr->typePtr);
01994     }
01995 
01996     n = rcPtr->argc - 2;
01997     for (i=0; i<n; i++) {
01998         Tcl_DecrRefCount(rcPtr->argv[i]);
01999     }
02000 
02001     /*
02002      * [Bug 1667990]: See [x] in NewReflectedChannel for lock. n+1 = argc-1.
02003      */
02004 
02005     Tcl_DecrRefCount(rcPtr->argv[n+1]);
02006 
02007     ckfree((char*) rcPtr->argv);
02008     ckfree((char*) rcPtr);
02009 }
02010 
02011 /*
02012  *----------------------------------------------------------------------
02013  *
02014  * InvokeTclMethod --
02015  *
02016  *      This function is used to invoke the Tcl level of a reflected channel.
02017  *      It handles all the command assembly, invokation, and generic state and
02018  *      result mgmt. It does *not* handle thread redirection; that is the
02019  *      responsibility of clients of this function.
02020  *
02021  * Results:
02022  *      Result code and data as returned by the method.
02023  *
02024  * Side effects:
02025  *      Arbitrary, as it calls upon a Tcl script.
02026  *
02027  *----------------------------------------------------------------------
02028  */
02029 
02030 static int
02031 InvokeTclMethod(
02032     ReflectedChannel *rcPtr,
02033     const char *method,
02034     Tcl_Obj *argOneObj,         /* NULL'able */
02035     Tcl_Obj *argTwoObj,         /* NULL'able */
02036     Tcl_Obj **resultObjPtr)     /* NULL'able */
02037 {
02038     int cmdc;                   /* #words in constructed command */
02039     Tcl_Obj *methObj = NULL;    /* Method name in object form */
02040     Tcl_InterpState sr;         /* State of handler interp */
02041     int result;                 /* Result code of method invokation */
02042     Tcl_Obj *resObj = NULL;     /* Result of method invokation. */
02043 
02044     /*
02045      * NOTE (5): Decide impl. issue: Cache objects with method names? Needs
02046      * TSD data as reflections can be created in many different threads.
02047      */
02048 
02049     /*
02050      * Insert method into the pre-allocated area, after the command prefix,
02051      * before the channel id.
02052      */
02053 
02054     methObj = Tcl_NewStringObj(method, -1);
02055     Tcl_IncrRefCount(methObj);
02056     rcPtr->argv[rcPtr->argc - 2] = methObj;
02057 
02058     /*
02059      * Append the additional argument containing method specific details
02060      * behind the channel id. If specified.
02061      */
02062 
02063     cmdc = rcPtr->argc;
02064     if (argOneObj) {
02065         Tcl_IncrRefCount(argOneObj);
02066         rcPtr->argv[cmdc] = argOneObj;
02067         cmdc++;
02068         if (argTwoObj) {
02069             Tcl_IncrRefCount(argTwoObj);
02070             rcPtr->argv[cmdc] = argTwoObj;
02071             cmdc++;
02072         }
02073     }
02074 
02075     /*
02076      * And run the handler... This is done in auch a manner which leaves any
02077      * existing state intact.
02078      */
02079 
02080     sr = Tcl_SaveInterpState(rcPtr->interp, 0 /* Dummy */);
02081     Tcl_Preserve(rcPtr->interp);
02082     result = Tcl_EvalObjv(rcPtr->interp, cmdc, rcPtr->argv, TCL_EVAL_GLOBAL);
02083 
02084     /*
02085      * We do not try to extract the result information if the caller has no
02086      * interest in it. I.e. there is no need to put effort into creating
02087      * something which is discarded immediately after.
02088      */
02089 
02090     if (resultObjPtr) {
02091         if (result == TCL_OK) {
02092             /*
02093              * Ok result taken as is, also if the caller requests that there
02094              * is no capture.
02095              */
02096 
02097             resObj = Tcl_GetObjResult(rcPtr->interp);
02098         } else {
02099             /*
02100              * Non-ok result is always treated as an error. We have to capture
02101              * the full state of the result, including additional options.
02102              *
02103              * This is complex and ugly, and would be completely unnecessary
02104              * if we only added support for a TCL_FORBID_EXCEPTIONS flag.
02105              */
02106 
02107             if (result != TCL_ERROR) {
02108                 Tcl_Obj *cmd = Tcl_NewListObj(cmdc, rcPtr->argv);
02109                 int cmdLen;
02110                 const char *cmdString = Tcl_GetStringFromObj(cmd, &cmdLen);
02111 
02112                 Tcl_IncrRefCount(cmd);
02113                 Tcl_ResetResult(rcPtr->interp);
02114                 Tcl_SetObjResult(rcPtr->interp, Tcl_ObjPrintf(
02115                         "chan handler returned bad code: %d", result));
02116                 Tcl_LogCommandInfo(rcPtr->interp, cmdString, cmdString,
02117                         cmdLen);
02118                 Tcl_DecrRefCount(cmd);
02119                 result = TCL_ERROR;
02120             }
02121             Tcl_AppendObjToErrorInfo(rcPtr->interp, Tcl_ObjPrintf(
02122                     "\n    (chan handler subcommand \"%s\")", method));
02123             resObj = MarshallError(rcPtr->interp);
02124         }
02125         Tcl_IncrRefCount(resObj);
02126     }
02127     Tcl_RestoreInterpState(rcPtr->interp, sr);
02128     Tcl_Release(rcPtr->interp);
02129 
02130     /*
02131      * Cleanup of the dynamic parts of the command.
02132      */
02133 
02134     Tcl_DecrRefCount(methObj);
02135     if (argOneObj) {
02136         Tcl_DecrRefCount(argOneObj);
02137         if (argTwoObj) {
02138             Tcl_DecrRefCount(argTwoObj);
02139         }
02140     }
02141 
02142     /*
02143      * The resObj has a ref count of 1 at this location. This means that the
02144      * caller of InvokeTclMethod has to dispose of it (but only if it was
02145      * returned to it).
02146      */
02147 
02148     if (resultObjPtr != NULL) {
02149         *resultObjPtr = resObj;
02150     }
02151 
02152     /*
02153      * There no need to handle the case where nothing is returned, because for
02154      * that case resObj was not set anyway.
02155      */
02156 
02157     return result;
02158 }
02159 
02160 /*
02161  *----------------------------------------------------------------------
02162  *
02163  * GetReflectedChannelMap --
02164  *
02165  *      Gets and potentially initializes the reflected channel map for an
02166  *      interpreter.
02167  *
02168  * Results:
02169  *      A pointer to the map created, for use by the caller.
02170  *
02171  * Side effects:
02172  *      Initializes the reflected channel map for an interpreter.
02173  *
02174  *----------------------------------------------------------------------
02175  */
02176 
02177 static ReflectedChannelMap *
02178 GetReflectedChannelMap(
02179     Tcl_Interp *interp)
02180 {
02181     ReflectedChannelMap* rcmPtr = Tcl_GetAssocData(interp, RCMKEY, NULL);
02182 
02183     if (rcmPtr == NULL) {
02184         rcmPtr = (ReflectedChannelMap *) ckalloc(sizeof(ReflectedChannelMap));
02185         Tcl_InitHashTable(&rcmPtr->map, TCL_STRING_KEYS);
02186         Tcl_SetAssocData(interp, RCMKEY,
02187                 (Tcl_InterpDeleteProc *) DeleteReflectedChannelMap, rcmPtr);
02188     }
02189     return rcmPtr;
02190 }
02191 
02192 /*
02193  *----------------------------------------------------------------------
02194  *
02195  * DeleteReflectedChannelMap --
02196  *
02197  *      Deletes the channel table for an interpreter, closing any open
02198  *      channels whose refcount reaches zero. This procedure is invoked when
02199  *      an interpreter is deleted, via the AssocData cleanup mechanism.
02200  *
02201  * Results:
02202  *      None.
02203  *
02204  * Side effects:
02205  *      Deletes the hash table of channels. May close channels. May flush
02206  *      output on closed channels. Removes any channeEvent handlers that were
02207  *      registered in this interpreter.
02208  *
02209  *----------------------------------------------------------------------
02210  */
02211 
02212 static void
02213 DeleteReflectedChannelMap(
02214     ClientData clientData,      /* The per-interpreter data structure. */
02215     Tcl_Interp *interp)         /* The interpreter being deleted. */
02216 {
02217     ReflectedChannelMap* rcmPtr; /* The map */
02218     Tcl_HashSearch hSearch;      /* Search variable. */
02219     Tcl_HashEntry *hPtr;         /* Search variable. */
02220 
02221     /*
02222      * Delete all entries. The channels may have been closed alreay, or will
02223      * be closed later, by the standard IO finalization of an interpreter
02224      * under destruction.
02225      */
02226 
02227     rcmPtr = clientData;
02228     for (hPtr = Tcl_FirstHashEntry(&rcmPtr->map, &hSearch);
02229          hPtr != NULL;
02230          hPtr = Tcl_FirstHashEntry(&rcmPtr->map, &hSearch)) {
02231 
02232         Tcl_DeleteHashEntry(hPtr);
02233     }
02234     Tcl_DeleteHashTable(&rcmPtr->map);
02235     ckfree((char *) &rcmPtr->map);
02236 }
02237 
02238 #ifdef TCL_THREADS
02239 static void
02240 ForwardOpToOwnerThread(
02241     ReflectedChannel *rcPtr,    /* Channel instance */
02242     ForwardedOperation op,      /* Forwarded driver operation */
02243     const VOID *param)          /* Arguments */
02244 {
02245     Tcl_ThreadId dst = rcPtr->thread;
02246     ForwardingEvent *evPtr;
02247     ForwardingResult *resultPtr;
02248     int result;
02249 
02250     /*
02251      * Create and initialize the event and data structures.
02252      */
02253 
02254     evPtr = (ForwardingEvent *) ckalloc(sizeof(ForwardingEvent));
02255     resultPtr = (ForwardingResult *) ckalloc(sizeof(ForwardingResult));
02256 
02257     evPtr->event.proc = ForwardProc;
02258     evPtr->resultPtr = resultPtr;
02259     evPtr->op = op;
02260     evPtr->rcPtr = rcPtr;
02261     evPtr->param = (ForwardParam *) param;
02262 
02263     resultPtr->src = Tcl_GetCurrentThread();
02264     resultPtr->dst = dst;
02265     resultPtr->done = NULL;
02266     resultPtr->result = -1;
02267     resultPtr->evPtr = evPtr;
02268 
02269     /*
02270      * Now execute the forward.
02271      */
02272 
02273     Tcl_MutexLock(&rcForwardMutex);
02274     TclSpliceIn(resultPtr, forwardList);
02275 
02276     /*
02277      * Ensure cleanup of the event if any of the two involved threads exits
02278      * while this event is pending or in progress.
02279      */
02280 
02281     Tcl_CreateThreadExitHandler(SrcExitProc, (ClientData) evPtr);
02282     Tcl_CreateThreadExitHandler(DstExitProc, (ClientData) evPtr);
02283 
02284     /*
02285      * Queue the event and poke the other thread's notifier.
02286      */
02287 
02288     Tcl_ThreadQueueEvent(dst, (Tcl_Event *)evPtr, TCL_QUEUE_TAIL);
02289     Tcl_ThreadAlert(dst);
02290 
02291     /*
02292      * (*) Block until the other thread has either processed the transfer or
02293      * rejected it.
02294      */
02295 
02296     while (resultPtr->result < 0) {
02297         /*
02298          * NOTE (1): Is it possible that the current thread goes away while
02299          * waiting here? IOW Is it possible that "SrcExitProc" is called while
02300          * we are here? See complementary note (2) in "SrcExitProc"
02301          */
02302 
02303         Tcl_ConditionWait(&resultPtr->done, &rcForwardMutex, NULL);
02304     }
02305 
02306     /*
02307      * Unlink result from the forwarder list.
02308      */
02309 
02310     TclSpliceOut(resultPtr, forwardList);
02311 
02312     resultPtr->nextPtr = NULL;
02313     resultPtr->prevPtr = NULL;
02314 
02315     Tcl_MutexUnlock(&rcForwardMutex);
02316     Tcl_ConditionFinalize(&resultPtr->done);
02317 
02318     /*
02319      * Kill the cleanup handlers now, and the result structure as well, before
02320      * returning the success code.
02321      *
02322      * Note: The event structure has already been deleted.
02323      */
02324 
02325     Tcl_DeleteThreadExitHandler(SrcExitProc, (ClientData) evPtr);
02326     Tcl_DeleteThreadExitHandler(DstExitProc, (ClientData) evPtr);
02327 
02328     result = resultPtr->result;
02329     ckfree((char*) resultPtr);
02330 }
02331 
02332 static int
02333 ForwardProc(
02334     Tcl_Event *evGPtr,
02335     int mask)
02336 {
02337     /*
02338      * Notes regarding access to the referenced data.
02339      *
02340      * In principle the data belongs to the originating thread (see
02341      * evPtr->src), however this thread is currently blocked at (*), i.e.
02342      * quiescent. Because of this we can treat the data as belonging to us,
02343      * without fear of race conditions. I.e. we can read and write as we like.
02344      *
02345      * The only thing we cannot be sure of is the resultPtr. This can be be
02346      * NULLed if the originating thread went away while the event is handled
02347      * here now.
02348      */
02349 
02350     ForwardingEvent *evPtr = (ForwardingEvent *) evGPtr;
02351     ForwardingResult *resultPtr = evPtr->resultPtr;
02352     ReflectedChannel *rcPtr = evPtr->rcPtr;
02353     Tcl_Interp *interp = rcPtr->interp;
02354     ForwardParam *paramPtr = evPtr->param;
02355     Tcl_Obj *resObj = NULL;     /* Interp result of InvokeTclMethod */
02356 
02357     /*
02358      * Ignore the event if no one is waiting for its result anymore.
02359      */
02360 
02361     if (!resultPtr) {
02362         return 1;
02363     }
02364 
02365     paramPtr->base.code = TCL_OK;
02366     paramPtr->base.msgStr = NULL;
02367     paramPtr->base.mustFree = 0;
02368 
02369     switch (evPtr->op) {
02370         /*
02371          * The destination thread for the following operations is
02372          * rcPtr->thread, which contains rcPtr->interp, the interp we have to
02373          * call upon for the driver.
02374          */
02375 
02376     case ForwardedClose:
02377         /*
02378          * No parameters/results.
02379          */
02380 
02381         if (InvokeTclMethod(rcPtr, "finalize", NULL, NULL, &resObj)!=TCL_OK) {
02382             ForwardSetObjError(paramPtr, resObj);
02383         }
02384 
02385         /*
02386          * Freeing is done here, in the origin thread, because the argv[]
02387          * objects belong to this thread. Deallocating them in a different
02388          * thread is not allowed
02389          */
02390 
02391         FreeReflectedChannel(rcPtr);
02392         break;
02393 
02394     case ForwardedInput: {
02395         Tcl_Obj *toReadObj = Tcl_NewIntObj(paramPtr->input.toRead);
02396 
02397         if (InvokeTclMethod(rcPtr, "read", toReadObj, NULL, &resObj)!=TCL_OK){
02398             ForwardSetObjError(paramPtr, resObj);
02399             paramPtr->input.toRead = -1;
02400         } else {
02401             /*
02402              * Process a regular result.
02403              */
02404 
02405             int bytec;                  /* Number of returned bytes */
02406             unsigned char *bytev;       /* Array of returned bytes */
02407 
02408             bytev = Tcl_GetByteArrayFromObj(resObj, &bytec);
02409 
02410             if (paramPtr->input.toRead < bytec) {
02411                 ForwardSetStaticError(paramPtr, msg_read_toomuch);
02412                 paramPtr->input.toRead = -1;
02413             } else {
02414                 if (bytec > 0) {
02415                     memcpy(paramPtr->input.buf, bytev, (size_t)bytec);
02416                 }
02417                 paramPtr->input.toRead = bytec;
02418             }
02419         }
02420         break;
02421     }
02422 
02423     case ForwardedOutput: {
02424         Tcl_Obj *bufObj = Tcl_NewByteArrayObj((unsigned char *)
02425                 paramPtr->output.buf, paramPtr->output.toWrite);
02426 
02427         if (InvokeTclMethod(rcPtr, "write", bufObj, NULL, &resObj) != TCL_OK) {
02428             ForwardSetObjError(paramPtr, resObj);
02429             paramPtr->output.toWrite = -1;
02430         } else {
02431             /*
02432              * Process a regular result.
02433              */
02434 
02435             int written;
02436 
02437             if (Tcl_GetIntFromObj(interp, resObj, &written) != TCL_OK) {
02438                 ForwardSetObjError(paramPtr, MarshallError(interp));
02439                 paramPtr->output.toWrite = -1;
02440             } else if (written==0 || paramPtr->output.toWrite<written) {
02441                 ForwardSetStaticError(paramPtr, msg_write_toomuch);
02442                 paramPtr->output.toWrite = -1;
02443             } else {
02444                 paramPtr->output.toWrite = written;
02445             }
02446         }
02447         break;
02448     }
02449 
02450     case ForwardedSeek: {
02451         Tcl_Obj *offObj = Tcl_NewWideIntObj(paramPtr->seek.offset);
02452         Tcl_Obj *baseObj = Tcl_NewStringObj(
02453                 (paramPtr->seek.seekMode==SEEK_SET) ? "start" :
02454                 (paramPtr->seek.seekMode==SEEK_CUR) ? "current" : "end", -1);
02455 
02456         if (InvokeTclMethod(rcPtr, "seek", offObj, baseObj, &resObj)!=TCL_OK){
02457             ForwardSetObjError(paramPtr, resObj);
02458             paramPtr->seek.offset = -1;
02459         } else {
02460             /*
02461              * Process a regular result. If the type is wrong this may change
02462              * into an error.
02463              */
02464 
02465             Tcl_WideInt newLoc;
02466 
02467             if (Tcl_GetWideIntFromObj(interp, resObj, &newLoc) == TCL_OK) {
02468                 if (newLoc < Tcl_LongAsWide(0)) {
02469                     ForwardSetStaticError(paramPtr, msg_seek_beforestart);
02470                     paramPtr->seek.offset = -1;
02471                 } else {
02472                     paramPtr->seek.offset = newLoc;
02473                 }
02474             } else {
02475                 ForwardSetObjError(paramPtr, MarshallError(interp));
02476                 paramPtr->seek.offset = -1;
02477             }
02478         }
02479         break;
02480     }
02481 
02482     case ForwardedWatch: {
02483         Tcl_Obj *maskObj = DecodeEventMask(paramPtr->watch.mask);
02484 
02485         (void) InvokeTclMethod(rcPtr, "watch", maskObj, NULL, NULL);
02486         Tcl_DecrRefCount(maskObj);
02487         break;
02488     }
02489 
02490     case ForwardedBlock: {
02491         Tcl_Obj *blockObj = Tcl_NewBooleanObj(!paramPtr->block.nonblocking);
02492 
02493         if (InvokeTclMethod(rcPtr, "blocking", blockObj, NULL,
02494                 &resObj) != TCL_OK) {
02495             ForwardSetObjError(paramPtr, resObj);
02496         }
02497         break;
02498     }
02499 
02500     case ForwardedSetOpt: {
02501         Tcl_Obj *optionObj = Tcl_NewStringObj(paramPtr->setOpt.name, -1);
02502         Tcl_Obj *valueObj = Tcl_NewStringObj(paramPtr->setOpt.value, -1);
02503 
02504         if (InvokeTclMethod(rcPtr, "configure", optionObj, valueObj,
02505                 &resObj) != TCL_OK) {
02506             ForwardSetObjError(paramPtr, resObj);
02507         }
02508         break;
02509     }
02510 
02511     case ForwardedGetOpt: {
02512         /*
02513          * Retrieve the value of one option.
02514          */
02515 
02516         Tcl_Obj *optionObj = Tcl_NewStringObj(paramPtr->getOpt.name, -1);
02517 
02518         if (InvokeTclMethod(rcPtr, "cget", optionObj, NULL, &resObj)!=TCL_OK){
02519             ForwardSetObjError(paramPtr, resObj);
02520         } else {
02521             Tcl_DStringAppend(paramPtr->getOpt.value,
02522                     TclGetString(resObj), -1);
02523         }
02524         break;
02525     }
02526 
02527     case ForwardedGetOptAll:
02528         /*
02529          * Retrieve all options.
02530          */
02531 
02532         if (InvokeTclMethod(rcPtr, "cgetall", NULL, NULL, &resObj) != TCL_OK){
02533             ForwardSetObjError(paramPtr, resObj);
02534         } else {
02535             /*
02536              * Extract list, validate that it is a list, and #elements. See
02537              * NOTE (4) as well.
02538              */
02539 
02540             int listc;
02541             Tcl_Obj **listv;
02542 
02543             if (Tcl_ListObjGetElements(interp, resObj, &listc,
02544                     &listv) != TCL_OK) {
02545                 ForwardSetObjError(paramPtr, MarshallError(interp));
02546             } else if ((listc % 2) == 1) {
02547                 /*
02548                  * Odd number of elements is wrong. [x].
02549                  */
02550 
02551                 char *buf = ckalloc(200);
02552                 sprintf(buf,
02553                         "{Expected list with even number of elements, got %d %s instead}",
02554                         listc, (listc == 1 ? "element" : "elements"));
02555 
02556                 ForwardSetDynamicError(paramPtr, buf);
02557             } else {
02558                 int len;
02559                 const char *str = Tcl_GetStringFromObj(resObj, &len);
02560 
02561                 if (len) {
02562                     Tcl_DStringAppend(paramPtr->getOpt.value, " ", 1);
02563                     Tcl_DStringAppend(paramPtr->getOpt.value, str, len);
02564                 }
02565             }
02566         }
02567         break;
02568 
02569     default:
02570         /*
02571          * Bad operation code.
02572          */
02573 
02574         Tcl_Panic("Bad operation code in ForwardProc");
02575         break;
02576     }
02577 
02578     /*
02579      * Remove the reference we held on the result of the invoke, if we had
02580      * such.
02581      */
02582 
02583     if (resObj != NULL) {
02584         Tcl_DecrRefCount(resObj);
02585     }
02586 
02587     if (resultPtr) {
02588         /*
02589          * Report the forwarding result synchronously to the waiting caller.
02590          * This unblocks (*) as well. This is wrapped into a conditional
02591          * because the caller may have exited in the mean time.
02592          */
02593 
02594         Tcl_MutexLock(&rcForwardMutex);
02595         resultPtr->result = TCL_OK;
02596         Tcl_ConditionNotify(&resultPtr->done);
02597         Tcl_MutexUnlock(&rcForwardMutex);
02598     }
02599 
02600     return 1;
02601 }
02602 
02603 static void
02604 SrcExitProc(
02605     ClientData clientData)
02606 {
02607     ForwardingEvent *evPtr = (ForwardingEvent *) clientData;
02608     ForwardingResult *resultPtr;
02609     ForwardParam *paramPtr;
02610 
02611     /*
02612      * NOTE (2): Can this handler be called with the originator blocked?
02613      */
02614 
02615     /*
02616      * The originator for the event exited. It is not sure if this can happen,
02617      * as the originator should be blocked at (*) while the event is in
02618      * transit/pending.
02619      *
02620      * We make sure that the event cannot refer to the result anymore, remove
02621      * it from the list of pending results and free the structure. Locking the
02622      * access ensures that we cannot get in conflict with "ForwardProc",
02623      * should it already execute the event.
02624      */
02625 
02626     Tcl_MutexLock(&rcForwardMutex);
02627 
02628     resultPtr = evPtr->resultPtr;
02629     paramPtr = evPtr->param;
02630 
02631     evPtr->resultPtr = NULL;
02632     resultPtr->evPtr = NULL;
02633     resultPtr->result = TCL_ERROR;
02634 
02635     ForwardSetStaticError(paramPtr, msg_send_originlost);
02636 
02637     /*
02638      * See below: TclSpliceOut(resultPtr, forwardList);
02639      */
02640 
02641     Tcl_MutexUnlock(&rcForwardMutex);
02642 
02643     /*
02644      * This unlocks (*). The structure will be spliced out and freed by
02645      * "ForwardProc". Maybe.
02646      */
02647 
02648     Tcl_ConditionNotify(&resultPtr->done);
02649 }
02650 
02651 static void
02652 DstExitProc(
02653     ClientData clientData)
02654 {
02655     ForwardingEvent *evPtr = (ForwardingEvent *) clientData;
02656     ForwardingResult *resultPtr = evPtr->resultPtr;
02657     ForwardParam *paramPtr = evPtr->param;
02658 
02659     /*
02660      * NOTE (3): It is not clear if the event still exists when this handler
02661      * is called. We might have to use 'resultPtr' as our clientData instead.
02662      */
02663 
02664     /*
02665      * The receiver for the event exited, before processing the event. We
02666      * detach the result now, wake the originator up and signal failure.
02667      */
02668 
02669     evPtr->resultPtr = NULL;
02670     resultPtr->evPtr = NULL;
02671     resultPtr->result = TCL_ERROR;
02672 
02673     ForwardSetStaticError(paramPtr, msg_send_dstlost);
02674 
02675     Tcl_ConditionNotify(&resultPtr->done);
02676 }
02677 
02678 static void
02679 ForwardSetObjError(
02680     ForwardParam *paramPtr,
02681     Tcl_Obj *obj)
02682 {
02683     int len;
02684     const char *msgStr = Tcl_GetStringFromObj(obj, &len);
02685 
02686     len++;
02687     ForwardSetDynamicError(paramPtr, ckalloc((unsigned) len));
02688     memcpy(paramPtr->base.msgStr, msgStr, (unsigned) len);
02689 }
02690 #endif
02691 
02692 /*
02693  * Local Variables:
02694  * mode: c
02695  * c-basic-offset: 4
02696  * fill-column: 78
02697  * End:
02698  */



Generated on Wed Mar 12 12:18:18 2008 by  doxygen 1.5.1