tclIOGT.c

Go to the documentation of this file.
00001 /*
00002  * tclIOGT.c --
00003  *
00004  *      Implements a generic transformation exposing the underlying API at the
00005  *      script level. Contributed by Andreas Kupries.
00006  *
00007  * Copyright (c) 2000 Ajuba Solutions
00008  * Copyright (c) 1999-2000 Andreas Kupries (a.kupries@westend.com)
00009  *
00010  * See the file "license.terms" for information on usage and redistribution of
00011  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
00012  *
00013  * CVS: $Id: tclIOGT.c,v 1.20 2007/12/13 15:23:18 dgp Exp $
00014  */
00015 
00016 #include "tclInt.h"
00017 #include "tclIO.h"
00018 
00019 /*
00020  * Forward declarations of internal procedures. First the driver procedures of
00021  * the transformation.
00022  */
00023 
00024 static int              TransformBlockModeProc(ClientData instanceData,
00025                             int mode);
00026 static int              TransformCloseProc(ClientData instanceData,
00027                             Tcl_Interp *interp);
00028 static int              TransformInputProc(ClientData instanceData, char *buf,
00029                             int toRead, int *errorCodePtr);
00030 static int              TransformOutputProc(ClientData instanceData,
00031                             const char *buf, int toWrite, int *errorCodePtr);
00032 static int              TransformSeekProc(ClientData instanceData, long offset,
00033                             int mode, int *errorCodePtr);
00034 static int              TransformSetOptionProc(ClientData instanceData,
00035                             Tcl_Interp *interp, const char *optionName,
00036                             const char *value);
00037 static int              TransformGetOptionProc(ClientData instanceData,
00038                             Tcl_Interp *interp, const char *optionName,
00039                             Tcl_DString *dsPtr);
00040 static void             TransformWatchProc(ClientData instanceData, int mask);
00041 static int              TransformGetFileHandleProc(ClientData instanceData,
00042                             int direction, ClientData *handlePtr);
00043 static int              TransformNotifyProc(ClientData instanceData, int mask);
00044 static Tcl_WideInt      TransformWideSeekProc(ClientData instanceData,
00045                             Tcl_WideInt offset, int mode, int *errorCodePtr);
00046 
00047 /*
00048  * Forward declarations of internal procedures. Secondly the procedures for
00049  * handling and generating fileeevents.
00050  */
00051 
00052 static void             TransformChannelHandlerTimer(ClientData clientData);
00053 
00054 /*
00055  * Forward declarations of internal procedures. Third, helper procedures
00056  * encapsulating essential tasks.
00057  */
00058 
00059 typedef struct TransformChannelData TransformChannelData;
00060 
00061 static int              ExecuteCallback(TransformChannelData *ctrl,
00062                             Tcl_Interp *interp, unsigned char *op,
00063                             unsigned char *buf, int bufLen, int transmit,
00064                             int preserve);
00065 
00066 /*
00067  * Action codes to give to 'ExecuteCallback' (argument 'transmit'), telling
00068  * the procedure what to do with the result of the script it calls.
00069  */
00070 
00071 #define TRANSMIT_DONT   0       /* No transfer to do. */
00072 #define TRANSMIT_DOWN   1       /* Transfer to the underlying channel. */
00073 #define TRANSMIT_SELF   2       /* Transfer into our channel. */
00074 #define TRANSMIT_IBUF   3       /* Transfer to internal input buffer. */
00075 #define TRANSMIT_NUM    4       /* Transfer number to 'maxRead'. */
00076 
00077 /*
00078  * Codes for 'preserve' of 'ExecuteCallback'.
00079  */
00080 
00081 #define P_PRESERVE      1
00082 #define P_NO_PRESERVE   0
00083 
00084 /*
00085  * Strings for the action codes delivered to the script implementing a
00086  * transformation. Argument 'op' of 'ExecuteCallback'.
00087  */
00088 
00089 #define A_CREATE_WRITE  (UCHARP("create/write"))
00090 #define A_DELETE_WRITE  (UCHARP("delete/write"))
00091 #define A_FLUSH_WRITE   (UCHARP("flush/write"))
00092 #define A_WRITE         (UCHARP("write"))
00093 
00094 #define A_CREATE_READ   (UCHARP("create/read"))
00095 #define A_DELETE_READ   (UCHARP("delete/read"))
00096 #define A_FLUSH_READ    (UCHARP("flush/read"))
00097 #define A_READ          (UCHARP("read"))
00098 
00099 #define A_QUERY_MAXREAD (UCHARP("query/maxRead"))
00100 #define A_CLEAR_READ    (UCHARP("clear/read"))
00101 
00102 /*
00103  * Management of a simple buffer.
00104  */
00105 
00106 typedef struct ResultBuffer ResultBuffer;
00107 
00108 static inline void      ResultClear(ResultBuffer *r);
00109 static inline void      ResultInit(ResultBuffer *r);
00110 static inline int       ResultEmpty(ResultBuffer *r);
00111 static inline int       ResultCopy(ResultBuffer *r, unsigned char *buf,
00112                             size_t toRead);
00113 static inline void      ResultAdd(ResultBuffer *r, unsigned char *buf,
00114                             size_t toWrite);
00115 
00116 /*
00117  * This structure describes the channel type structure for Tcl-based
00118  * transformations.
00119  */
00120 
00121 static Tcl_ChannelType transformChannelType = {
00122     "transform",                /* Type name. */
00123     TCL_CHANNEL_VERSION_5,      /* v5 channel */
00124     TransformCloseProc,         /* Close proc. */
00125     TransformInputProc,         /* Input proc. */
00126     TransformOutputProc,        /* Output proc. */
00127     TransformSeekProc,          /* Seek proc. */
00128     TransformSetOptionProc,     /* Set option proc. */
00129     TransformGetOptionProc,     /* Get option proc. */
00130     TransformWatchProc,         /* Initialize notifier. */
00131     TransformGetFileHandleProc, /* Get OS handles out of channel. */
00132     NULL,                       /* close2proc */
00133     TransformBlockModeProc,     /* Set blocking/nonblocking mode.*/
00134     NULL,                       /* Flush proc. */
00135     TransformNotifyProc,        /* Handling of events bubbling up. */
00136     TransformWideSeekProc,      /* Wide seek proc. */
00137     NULL,                       /* Thread action. */
00138     NULL,                       /* Truncate. */
00139 };
00140 
00141 /*
00142  * Possible values for 'flags' field in control structure, see below.
00143  */
00144 
00145 #define CHANNEL_ASYNC (1<<0)    /* Non-blocking mode. */
00146 
00147 /*
00148  * Definition of the structure containing the information about the internal
00149  * input buffer.
00150  */
00151 
00152 struct ResultBuffer {
00153     unsigned char *buf;         /* Reference to the buffer area. */
00154     size_t allocated;           /* Allocated size of the buffer area. */
00155     size_t used;                /* Number of bytes in the buffer, no more than
00156                                  * number allocated. */
00157 };
00158 
00159 /*
00160  * Additional bytes to allocate during buffer expansion.
00161  */
00162 
00163 #define INCREMENT       512
00164 
00165 /*
00166  * Number of milliseconds to wait before firing an event to flush out
00167  * information waiting in buffers (fileevent support).
00168  */
00169 
00170 #define FLUSH_DELAY     5
00171 
00172 /*
00173  * Convenience macro to make some casts easier to use.
00174  */
00175 
00176 #define UCHARP(x)       ((unsigned char *) (x))
00177 
00178 /*
00179  * Definition of a structure used by all transformations generated here to
00180  * maintain their local state.
00181  */
00182 
00183 struct TransformChannelData {
00184     /*
00185      * General section. Data to integrate the transformation into the channel
00186      * system.
00187      */
00188 
00189     Tcl_Channel self;           /* Our own Channel handle. */
00190     int readIsFlushed;          /* Flag to note whether in.flushProc was
00191                                  * called or not. */
00192     int flags;                  /* Currently CHANNEL_ASYNC or zero. */
00193     int watchMask;              /* Current watch/event/interest mask. */
00194     int mode;                   /* Mode of parent channel, OR'ed combination
00195                                  * of TCL_READABLE, TCL_WRITABLE. */
00196     Tcl_TimerToken timer;       /* Timer for automatic flushing of information
00197                                  * sitting in an internal buffer. Required for
00198                                  * full fileevent support. */
00199 
00200     /*
00201      * Transformation specific data.
00202      */
00203 
00204     int maxRead;                /* Maximum allowed number of bytes to read, as
00205                                  * given to us by the Tcl script implementing
00206                                  * the transformation. */
00207     Tcl_Interp *interp;         /* Reference to the interpreter which created
00208                                  * the transformation. Used to execute the
00209                                  * code below. */
00210     Tcl_Obj *command;           /* Tcl code to execute for a buffer */
00211     ResultBuffer result;        /* Internal buffer used to store the result of
00212                                  * a transformation of incoming data. Also
00213                                  * serves as buffer of all data not yet
00214                                  * consumed by the reader. */
00215 };
00216 
00217 /*
00218  *----------------------------------------------------------------------
00219  *
00220  * TclChannelTransform --
00221  *
00222  *      Implements the Tcl "testchannel transform" debugging command. This is
00223  *      part of the testing environment. This sets up a tcl script (cmdObjPtr)
00224  *      to be used as a transform on the channel.
00225  *
00226  * Results:
00227  *      A standard Tcl result.
00228  *
00229  * Side effects:
00230  *      None.
00231  *
00232  *----------------------------------------------------------------------
00233  */
00234 
00235         /* ARGSUSED */
00236 int
00237 TclChannelTransform(
00238     Tcl_Interp *interp,         /* Interpreter for result. */
00239     Tcl_Channel chan,           /* Channel to transform. */
00240     Tcl_Obj *cmdObjPtr)         /* Script to use for transform. */
00241 {
00242     Channel *chanPtr;           /* The actual channel. */
00243     ChannelState *statePtr;     /* State info for channel. */
00244     int mode;                   /* Read/write mode of the channel. */
00245     TransformChannelData *dataPtr;
00246     Tcl_DString ds;
00247 
00248     if (chan == NULL) {
00249         return TCL_ERROR;
00250     }
00251 
00252     chanPtr = (Channel *) chan;
00253     statePtr = chanPtr->state;
00254     chanPtr = statePtr->topChanPtr;
00255     chan = (Tcl_Channel) chanPtr;
00256     mode = (statePtr->flags & (TCL_READABLE|TCL_WRITABLE));
00257 
00258     /*
00259      * Now initialize the transformation state and stack it upon the specified
00260      * channel. One of the necessary things to do is to retrieve the blocking
00261      * regime of the underlying channel and to use the same for us too.
00262      */
00263 
00264     dataPtr = (TransformChannelData *) ckalloc(sizeof(TransformChannelData));
00265 
00266     Tcl_DStringInit(&ds);
00267     Tcl_GetChannelOption(interp, chan, "-blocking", &ds);
00268     dataPtr->readIsFlushed = 0;
00269     dataPtr->flags = 0;
00270     if (ds.string[0] == '0') {
00271         dataPtr->flags |= CHANNEL_ASYNC;
00272     }
00273     Tcl_DStringFree(&ds);
00274 
00275     dataPtr->self = chan;
00276     dataPtr->watchMask = 0;
00277     dataPtr->mode = mode;
00278     dataPtr->timer = NULL;
00279     dataPtr->maxRead = 4096;    /* Initial value not relevant. */
00280     dataPtr->interp = interp;
00281     dataPtr->command = cmdObjPtr;
00282     Tcl_IncrRefCount(dataPtr->command);
00283 
00284     ResultInit(&dataPtr->result);
00285 
00286     dataPtr->self = Tcl_StackChannel(interp, &transformChannelType, dataPtr,
00287             mode, chan);
00288     if (dataPtr->self == NULL) {
00289         Tcl_AppendResult(interp, "\nfailed to stack channel \"",
00290                 Tcl_GetChannelName(chan), "\"", NULL);
00291         Tcl_DecrRefCount(dataPtr->command);
00292         ResultClear(&dataPtr->result);
00293         ckfree((char *) dataPtr);
00294         return TCL_ERROR;
00295     }
00296 
00297     /*
00298      * At last initialize the transformation at the script level.
00299      */
00300 
00301     if ((dataPtr->mode & TCL_WRITABLE) && ExecuteCallback(dataPtr, NULL,
00302             A_CREATE_WRITE, NULL, 0, TRANSMIT_DONT, P_NO_PRESERVE) != TCL_OK){
00303         Tcl_UnstackChannel(interp, chan);
00304         return TCL_ERROR;
00305     }
00306 
00307     if ((dataPtr->mode & TCL_READABLE) && ExecuteCallback(dataPtr, NULL,
00308             A_CREATE_READ, NULL, 0, TRANSMIT_DONT, P_NO_PRESERVE) != TCL_OK) {
00309         ExecuteCallback(dataPtr, NULL, A_DELETE_WRITE, NULL, 0, TRANSMIT_DONT,
00310                 P_NO_PRESERVE);
00311         Tcl_UnstackChannel(interp, chan);
00312         return TCL_ERROR;
00313     }
00314 
00315     return TCL_OK;
00316 }
00317 
00318 /*
00319  *----------------------------------------------------------------------
00320  *
00321  * ExecuteCallback --
00322  *
00323  *      Executes the defined callback for buffer and operation.
00324  *
00325  * Side effects:
00326  *      As of the executed tcl script.
00327  *
00328  * Result:
00329  *      A standard TCL error code. In case of an error a message is left in
00330  *      the result area of the specified interpreter.
00331  *
00332  *----------------------------------------------------------------------
00333  */
00334 
00335 static int
00336 ExecuteCallback(
00337     TransformChannelData *dataPtr,
00338                                 /* Transformation with the callback. */
00339     Tcl_Interp *interp,         /* Current interpreter, possibly NULL. */
00340     unsigned char *op,          /* Operation invoking the callback. */
00341     unsigned char *buf,         /* Buffer to give to the script. */
00342     int bufLen,                 /* And its length. */
00343     int transmit,               /* Flag, determines whether the result of the
00344                                  * callback is sent to the underlying channel
00345                                  * or not. */
00346     int preserve)               /* Flag. If true the procedure will preserve
00347                                  * the result state of all accessed
00348                                  * interpreters. */
00349 {
00350     Tcl_Obj *resObj;            /* See below, switch (transmit). */
00351     int resLen;
00352     unsigned char *resBuf;
00353     Tcl_InterpState state = NULL;
00354     int res = TCL_OK;
00355     Tcl_Obj *command = Tcl_DuplicateObj(dataPtr->command);
00356 
00357     /*
00358      * Step 1, create the complete command to execute. Do this by appending
00359      * operation and buffer to operate upon to a copy of the callback
00360      * definition. We *cannot* create a list containing 3 objects and then use
00361      * 'Tcl_EvalObjv', because the command may contain additional prefixed
00362      * arguments. Feather's curried commands would come in handy here.
00363      */
00364 
00365     if (preserve == P_PRESERVE) {
00366         state = Tcl_SaveInterpState(dataPtr->interp, res);
00367     }
00368 
00369     Tcl_IncrRefCount(command);
00370     res = Tcl_ListObjAppendElement(dataPtr->interp, command,
00371             Tcl_NewStringObj((char *) op, -1));
00372     if (res != TCL_OK) {
00373         goto cleanup;
00374     }
00375 
00376     /*
00377      * Use a byte-array to prevent the misinterpretation of binary data coming
00378      * through as UTF while at the tcl level.
00379      */
00380 
00381     res = Tcl_ListObjAppendElement(dataPtr->interp, command,
00382             Tcl_NewByteArrayObj(buf, bufLen));
00383     if (res != TCL_OK) {
00384         goto cleanup;
00385     }
00386 
00387     /*
00388      * Step 2, execute the command at the global level of the interpreter used
00389      * to create the transformation. Destroy the command afterward. If an
00390      * error occured and the current interpreter is defined and not equal to
00391      * the interpreter for the callback, then copy the error message into
00392      * current interpreter. Don't copy if in preservation mode.
00393      */
00394 
00395     res = Tcl_EvalObjEx(dataPtr->interp, command, TCL_EVAL_GLOBAL);
00396     Tcl_DecrRefCount(command);
00397     command = NULL;
00398 
00399     if ((res != TCL_OK) && (interp != NULL) && (dataPtr->interp != interp)
00400             && (preserve == P_NO_PRESERVE)) {
00401         Tcl_SetObjResult(interp, Tcl_GetObjResult(dataPtr->interp));
00402         return res;
00403     }
00404 
00405     /*
00406      * Step 3, transmit a possible conversion result to the underlying
00407      * channel, or ourselves.
00408      */
00409 
00410     switch (transmit) {
00411     case TRANSMIT_DONT:
00412         /* nothing to do */
00413         break;
00414 
00415     case TRANSMIT_DOWN:
00416         resObj = Tcl_GetObjResult(dataPtr->interp);
00417         resBuf = Tcl_GetByteArrayFromObj(resObj, &resLen);
00418         Tcl_WriteRaw(Tcl_GetStackedChannel(dataPtr->self), (char *) resBuf,
00419                 resLen);
00420         break;
00421 
00422     case TRANSMIT_SELF:
00423         resObj = Tcl_GetObjResult(dataPtr->interp);
00424         resBuf = Tcl_GetByteArrayFromObj(resObj, &resLen);
00425         Tcl_WriteRaw(dataPtr->self, (char *) resBuf, resLen);
00426         break;
00427 
00428     case TRANSMIT_IBUF:
00429         resObj = Tcl_GetObjResult(dataPtr->interp);
00430         resBuf = Tcl_GetByteArrayFromObj(resObj, &resLen);
00431         ResultAdd(&dataPtr->result, resBuf, resLen);
00432         break;
00433 
00434     case TRANSMIT_NUM:
00435         /*
00436          * Interpret result as integer number.
00437          */
00438 
00439         resObj = Tcl_GetObjResult(dataPtr->interp);
00440         TclGetIntFromObj(dataPtr->interp, resObj, &dataPtr->maxRead);
00441         break;
00442     }
00443 
00444     Tcl_ResetResult(dataPtr->interp);
00445     if (preserve == P_PRESERVE) {
00446         (void) Tcl_RestoreInterpState(dataPtr->interp, state);
00447     }
00448     return res;
00449 
00450   cleanup:
00451     if (preserve == P_PRESERVE) {
00452         (void) Tcl_RestoreInterpState(dataPtr->interp, state);
00453     }
00454     if (command != NULL) {
00455         Tcl_DecrRefCount(command);
00456     }
00457     return res;
00458 }
00459 
00460 /*
00461  *----------------------------------------------------------------------
00462  *
00463  * TransformBlockModeProc --
00464  *
00465  *      Trap handler. Called by the generic IO system during option processing
00466  *      to change the blocking mode of the channel.
00467  *
00468  * Side effects:
00469  *      Forwards the request to the underlying channel.
00470  *
00471  * Result:
00472  *      0 if successful, errno when failed.
00473  *
00474  *----------------------------------------------------------------------
00475  */
00476 
00477 static int
00478 TransformBlockModeProc(
00479     ClientData instanceData,    /* State of transformation. */
00480     int mode)                   /* New blocking mode. */
00481 {
00482     TransformChannelData *dataPtr = instanceData;
00483 
00484     if (mode == TCL_MODE_NONBLOCKING) {
00485         dataPtr->flags |= CHANNEL_ASYNC;
00486     } else {
00487         dataPtr->flags &= ~CHANNEL_ASYNC;
00488     }
00489     return 0;
00490 }
00491 
00492 /*
00493  *----------------------------------------------------------------------
00494  *
00495  * TransformCloseProc --
00496  *
00497  *      Trap handler. Called by the generic IO system during destruction of
00498  *      the transformation channel.
00499  *
00500  * Side effects:
00501  *      Releases the memory allocated in 'Tcl_TransformObjCmd'.
00502  *
00503  * Result:
00504  *      None.
00505  *
00506  *----------------------------------------------------------------------
00507  */
00508 
00509 static int
00510 TransformCloseProc(
00511     ClientData instanceData,
00512     Tcl_Interp *interp)
00513 {
00514     TransformChannelData *dataPtr = instanceData;
00515 
00516     /*
00517      * Important: In this procedure 'dataPtr->self' already points to the
00518      * underlying channel.
00519      *
00520      * There is no need to cancel an existing channel handler, this is already
00521      * done. Either by 'Tcl_UnstackChannel' or by the general cleanup in
00522      * 'Tcl_Close'.
00523      *
00524      * But we have to cancel an active timer to prevent it from firing on the
00525      * removed channel.
00526      */
00527 
00528     if (dataPtr->timer != NULL) {
00529         Tcl_DeleteTimerHandler(dataPtr->timer);
00530         dataPtr->timer = NULL;
00531     }
00532 
00533     /*
00534      * Now flush data waiting in internal buffers to output and input. The
00535      * input must be done despite the fact that there is no real receiver for
00536      * it anymore. But the scripts might have sideeffects other parts of the
00537      * system rely on (f.e. signaling the close to interested parties).
00538      */
00539 
00540     if (dataPtr->mode & TCL_WRITABLE) {
00541         ExecuteCallback(dataPtr, interp, A_FLUSH_WRITE, NULL, 0,
00542                 TRANSMIT_DOWN, P_PRESERVE);
00543     }
00544 
00545     if ((dataPtr->mode & TCL_READABLE) && !dataPtr->readIsFlushed) {
00546         dataPtr->readIsFlushed = 1;
00547         ExecuteCallback(dataPtr, interp, A_FLUSH_READ, NULL, 0, TRANSMIT_IBUF,
00548                 P_PRESERVE);
00549     }
00550 
00551     if (dataPtr->mode & TCL_WRITABLE) {
00552         ExecuteCallback(dataPtr, interp, A_DELETE_WRITE, NULL, 0,
00553                 TRANSMIT_DONT, P_PRESERVE);
00554     }
00555     if (dataPtr->mode & TCL_READABLE) {
00556         ExecuteCallback(dataPtr, interp, A_DELETE_READ, NULL, 0,
00557                 TRANSMIT_DONT, P_PRESERVE);
00558     }
00559 
00560     /*
00561      * General cleanup.
00562      */
00563 
00564     ResultClear(&dataPtr->result);
00565     Tcl_DecrRefCount(dataPtr->command);
00566     ckfree((char *) dataPtr);
00567     return TCL_OK;
00568 }
00569 
00570 /*
00571  *----------------------------------------------------------------------
00572  *
00573  * TransformInputProc --
00574  *
00575  *      Called by the generic IO system to convert read data.
00576  *
00577  * Side effects:
00578  *      As defined by the conversion.
00579  *
00580  * Result:
00581  *      A transformed buffer.
00582  *
00583  *----------------------------------------------------------------------
00584  */
00585 
00586 static int
00587 TransformInputProc(
00588     ClientData instanceData,
00589     char *buf,
00590     int toRead,
00591     int *errorCodePtr)
00592 {
00593     TransformChannelData *dataPtr = instanceData;
00594     int gotBytes, read, copied;
00595     Tcl_Channel downChan;
00596 
00597     /*
00598      * Should assert(dataPtr->mode & TCL_READABLE);
00599      */
00600 
00601     if (toRead == 0) {
00602         /*
00603          * Catch a no-op.
00604          */
00605         return 0;
00606     }
00607 
00608     gotBytes = 0;
00609     downChan = Tcl_GetStackedChannel(dataPtr->self);
00610 
00611     while (toRead > 0) {
00612         /*
00613          * Loop until the request is satisfied (or no data is available from
00614          * below, possibly EOF).
00615          */
00616 
00617         copied = ResultCopy(&dataPtr->result, UCHARP(buf), toRead);
00618         toRead -= copied;
00619         buf += copied;
00620         gotBytes += copied;
00621 
00622         if (toRead == 0) {
00623             /*
00624              * The request was completely satisfied from our buffers. We can
00625              * break out of the loop and return to the caller.
00626              */
00627 
00628             return gotBytes;
00629         }
00630 
00631         /*
00632          * Length (dataPtr->result) == 0, toRead > 0 here. Use the incoming
00633          * 'buf'! as target to store the intermediary information read from
00634          * the underlying channel.
00635          *
00636          * Ask the tcl level how much data it allows us to read from the
00637          * underlying channel. This feature allows the transform to signal EOF
00638          * upstream although there is none downstream. Useful to control an
00639          * unbounded 'fcopy', either through counting bytes, or by pattern
00640          * matching.
00641          */
00642 
00643         ExecuteCallback(dataPtr, NULL, A_QUERY_MAXREAD, NULL, 0,
00644                 TRANSMIT_NUM /* -> maxRead */, P_PRESERVE);
00645 
00646         if (dataPtr->maxRead >= 0) {
00647             if (dataPtr->maxRead < toRead) {
00648                 toRead = dataPtr->maxRead;
00649             }
00650         } /* else: 'maxRead < 0' == Accept the current value of toRead. */
00651         if (toRead <= 0) {
00652             return gotBytes;
00653         }
00654 
00655         /*
00656          * Get bytes from the underlying channel.
00657          */
00658 
00659         read = Tcl_ReadRaw(downChan, buf, toRead);
00660         if (read < 0) {
00661             /*
00662              * Report errors to caller. EAGAIN is a special situation. If we
00663              * had some data before we report that instead of the request to
00664              * re-try.
00665              */
00666 
00667             if ((Tcl_GetErrno() == EAGAIN) && (gotBytes > 0)) {
00668                 return gotBytes;
00669             }
00670 
00671             *errorCodePtr = Tcl_GetErrno();
00672             return -1;
00673         } else if (read == 0) {
00674             /*
00675              * Check wether we hit on EOF in the underlying channel or not. If
00676              * not differentiate between blocking and non-blocking modes. In
00677              * non-blocking mode we ran temporarily out of data. Signal this
00678              * to the caller via EWOULDBLOCK and error return (-1). In the
00679              * other cases we simply return what we got and let the caller
00680              * wait for more. On the other hand, if we got an EOF we have to
00681              * convert and flush all waiting partial data.
00682              */
00683 
00684             if (!Tcl_Eof(downChan)) {
00685                 if ((gotBytes == 0) && (dataPtr->flags & CHANNEL_ASYNC)) {
00686                     *errorCodePtr = EWOULDBLOCK;
00687                     return -1;
00688                 }
00689                 return gotBytes;
00690             }
00691 
00692             if (dataPtr->readIsFlushed) {
00693                 /*
00694                  * Already flushed, nothing to do anymore.
00695                  */
00696 
00697                 return gotBytes;
00698             }
00699 
00700             dataPtr->readIsFlushed = 1;
00701             ExecuteCallback(dataPtr, NULL, A_FLUSH_READ, NULL, 0,
00702                     TRANSMIT_IBUF, P_PRESERVE);
00703 
00704             if (ResultEmpty(&dataPtr->result)) {
00705                 /*
00706                  * We had nothing to flush.
00707                  */
00708 
00709                 return gotBytes;
00710             }
00711 
00712             continue;           /* at: while (toRead > 0) */
00713         } /* read == 0 */
00714 
00715         /*
00716          * Transform the read chunk and add the result to our read buffer
00717          * (dataPtr->result).
00718          */
00719 
00720         if (ExecuteCallback(dataPtr, NULL, A_READ, UCHARP(buf), read,
00721                 TRANSMIT_IBUF, P_PRESERVE) != TCL_OK) {
00722             *errorCodePtr = EINVAL;
00723             return -1;
00724         }
00725     } /* while toRead > 0 */
00726 
00727     return gotBytes;
00728 }
00729 
00730 /*
00731  *----------------------------------------------------------------------
00732  *
00733  * TransformOutputProc --
00734  *
00735  *      Called by the generic IO system to convert data waiting to be written.
00736  *
00737  * Side effects:
00738  *      As defined by the transformation.
00739  *
00740  * Result:
00741  *      A transformed buffer.
00742  *
00743  *----------------------------------------------------------------------
00744  */
00745 
00746 static int
00747 TransformOutputProc(
00748     ClientData instanceData,
00749     const char *buf,
00750     int toWrite,
00751     int *errorCodePtr)
00752 {
00753     TransformChannelData *dataPtr = instanceData;
00754 
00755     /*
00756      * Should assert(dataPtr->mode & TCL_WRITABLE);
00757      */
00758 
00759     if (toWrite == 0) {
00760         /*
00761          * Catch a no-op.
00762          */
00763 
00764         return 0;
00765     }
00766 
00767     if (ExecuteCallback(dataPtr, NULL, A_WRITE, UCHARP(buf), toWrite,
00768             TRANSMIT_DOWN, P_NO_PRESERVE) != TCL_OK) {
00769         *errorCodePtr = EINVAL;
00770         return -1;
00771     }
00772 
00773     return toWrite;
00774 }
00775 
00776 /*
00777  *----------------------------------------------------------------------
00778  *
00779  * TransformSeekProc --
00780  *
00781  *      This procedure is called by the generic IO level to move the access
00782  *      point in a channel.
00783  *
00784  * Side effects:
00785  *      Moves the location at which the channel will be accessed in future
00786  *      operations. Flushes all transformation buffers, then forwards it to
00787  *      the underlying channel.
00788  *
00789  * Result:
00790  *      -1 if failed, the new position if successful. An output argument
00791  *      contains the POSIX error code if an error occurred, or zero.
00792  *
00793  *----------------------------------------------------------------------
00794  */
00795 
00796 static int
00797 TransformSeekProc(
00798     ClientData instanceData,    /* The channel to manipulate. */
00799     long offset,                /* Size of movement. */
00800     int mode,                   /* How to move. */
00801     int *errorCodePtr)          /* Location of error flag. */
00802 {
00803     TransformChannelData *dataPtr = instanceData;
00804     Tcl_Channel parent = Tcl_GetStackedChannel(dataPtr->self);
00805     Tcl_ChannelType *parentType = Tcl_GetChannelType(parent);
00806     Tcl_DriverSeekProc *parentSeekProc = Tcl_ChannelSeekProc(parentType);
00807 
00808     if ((offset == 0) && (mode == SEEK_CUR)) {
00809         /*
00810          * This is no seek but a request to tell the caller the current
00811          * location. Simply pass the request down.
00812          */
00813 
00814         return parentSeekProc(Tcl_GetChannelInstanceData(parent), offset,
00815                 mode, errorCodePtr);
00816     }
00817 
00818     /*
00819      * It is a real request to change the position. Flush all data waiting for
00820      * output and discard everything in the input buffers. Then pass the
00821      * request down, unchanged.
00822      */
00823 
00824     if (dataPtr->mode & TCL_WRITABLE) {
00825         ExecuteCallback(dataPtr, NULL, A_FLUSH_WRITE, NULL, 0, TRANSMIT_DOWN,
00826                 P_NO_PRESERVE);
00827     }
00828 
00829     if (dataPtr->mode & TCL_READABLE) {
00830         ExecuteCallback(dataPtr, NULL, A_CLEAR_READ, NULL, 0, TRANSMIT_DONT,
00831                 P_NO_PRESERVE);
00832         ResultClear(&dataPtr->result);
00833         dataPtr->readIsFlushed = 0;
00834     }
00835 
00836     return parentSeekProc(Tcl_GetChannelInstanceData(parent), offset, mode,
00837             errorCodePtr);
00838 }
00839 
00840 /*
00841  *----------------------------------------------------------------------
00842  *
00843  * TransformWideSeekProc --
00844  *
00845  *      This procedure is called by the generic IO level to move the access
00846  *      point in a channel, with a (potentially) 64-bit offset.
00847  *
00848  * Side effects:
00849  *      Moves the location at which the channel will be accessed in future
00850  *      operations. Flushes all transformation buffers, then forwards it to
00851  *      the underlying channel.
00852  *
00853  * Result:
00854  *      -1 if failed, the new position if successful. An output argument
00855  *      contains the POSIX error code if an error occurred, or zero.
00856  *
00857  *----------------------------------------------------------------------
00858  */
00859 
00860 static Tcl_WideInt
00861 TransformWideSeekProc(
00862     ClientData instanceData,    /* The channel to manipulate. */
00863     Tcl_WideInt offset,         /* Size of movement. */
00864     int mode,                   /* How to move. */
00865     int *errorCodePtr)          /* Location of error flag. */
00866 {
00867     TransformChannelData *dataPtr = instanceData;
00868     Tcl_Channel parent = Tcl_GetStackedChannel(dataPtr->self);
00869     Tcl_ChannelType *parentType = Tcl_GetChannelType(parent);
00870     Tcl_DriverSeekProc *parentSeekProc = Tcl_ChannelSeekProc(parentType);
00871     Tcl_DriverWideSeekProc *parentWideSeekProc =
00872             Tcl_ChannelWideSeekProc(parentType);
00873     ClientData parentData = Tcl_GetChannelInstanceData(parent);
00874 
00875     if ((offset == Tcl_LongAsWide(0)) && (mode == SEEK_CUR)) {
00876         /*
00877          * This is no seek but a request to tell the caller the current
00878          * location. Simply pass the request down.
00879          */
00880 
00881         if (parentWideSeekProc != NULL) {
00882             return parentWideSeekProc(parentData, offset, mode, errorCodePtr);
00883         }
00884 
00885         return Tcl_LongAsWide(parentSeekProc(parentData, 0, mode,
00886                 errorCodePtr));
00887     }
00888 
00889     /*
00890      * It is a real request to change the position. Flush all data waiting for
00891      * output and discard everything in the input buffers. Then pass the
00892      * request down, unchanged.
00893      */
00894 
00895     if (dataPtr->mode & TCL_WRITABLE) {
00896         ExecuteCallback(dataPtr, NULL, A_FLUSH_WRITE, NULL, 0, TRANSMIT_DOWN,
00897                 P_NO_PRESERVE);
00898     }
00899 
00900     if (dataPtr->mode & TCL_READABLE) {
00901         ExecuteCallback(dataPtr, NULL, A_CLEAR_READ, NULL, 0, TRANSMIT_DONT,
00902                 P_NO_PRESERVE);
00903         ResultClear(&dataPtr->result);
00904         dataPtr->readIsFlushed = 0;
00905     }
00906 
00907     /*
00908      * If we have a wide seek capability, we should stick with that.
00909      */
00910 
00911     if (parentWideSeekProc != NULL) {
00912         return parentWideSeekProc(parentData, offset, mode, errorCodePtr);
00913     }
00914 
00915     /*
00916      * We're transferring to narrow seeks at this point; this is a bit complex
00917      * because we have to check whether the seek is possible first (i.e.
00918      * whether we are losing information in truncating the bits of the
00919      * offset). Luckily, there's a defined error for what happens when trying
00920      * to go out of the representable range.
00921      */
00922 
00923     if (offset<Tcl_LongAsWide(LONG_MIN) || offset>Tcl_LongAsWide(LONG_MAX)) {
00924         *errorCodePtr = EOVERFLOW;
00925         return Tcl_LongAsWide(-1);
00926     }
00927 
00928     return Tcl_LongAsWide(parentSeekProc(parentData, Tcl_WideAsLong(offset),
00929             mode, errorCodePtr));
00930 }
00931 
00932 /*
00933  *----------------------------------------------------------------------
00934  *
00935  * TransformSetOptionProc --
00936  *
00937  *      Called by generic layer to handle the reconfiguration of channel
00938  *      specific options. As this channel type does not have such, it simply
00939  *      passes all requests downstream.
00940  *
00941  * Side effects:
00942  *      As defined by the channel downstream.
00943  *
00944  * Result:
00945  *      A standard TCL error code.
00946  *
00947  *----------------------------------------------------------------------
00948  */
00949 
00950 static int
00951 TransformSetOptionProc(
00952     ClientData instanceData,
00953     Tcl_Interp *interp,
00954     const char *optionName,
00955     const char *value)
00956 {
00957     TransformChannelData *dataPtr = instanceData;
00958     Tcl_Channel downChan = Tcl_GetStackedChannel(dataPtr->self);
00959     Tcl_DriverSetOptionProc *setOptionProc;
00960 
00961     setOptionProc = Tcl_ChannelSetOptionProc(Tcl_GetChannelType(downChan));
00962     if (setOptionProc == NULL) {
00963         return TCL_ERROR;
00964     }
00965 
00966     return setOptionProc(Tcl_GetChannelInstanceData(downChan), interp,
00967             optionName, value);
00968 }
00969 
00970 /*
00971  *----------------------------------------------------------------------
00972  *
00973  * TransformGetOptionProc --
00974  *
00975  *      Called by generic layer to handle requests for the values of channel
00976  *      specific options. As this channel type does not have such, it simply
00977  *      passes all requests downstream.
00978  *
00979  * Side effects:
00980  *      As defined by the channel downstream.
00981  *
00982  * Result:
00983  *      A standard TCL error code.
00984  *
00985  *----------------------------------------------------------------------
00986  */
00987 
00988 static int
00989 TransformGetOptionProc(
00990     ClientData instanceData,
00991     Tcl_Interp *interp,
00992     const char *optionName,
00993     Tcl_DString *dsPtr)
00994 {
00995     TransformChannelData *dataPtr = instanceData;
00996     Tcl_Channel downChan = Tcl_GetStackedChannel(dataPtr->self);
00997     Tcl_DriverGetOptionProc *getOptionProc;
00998 
00999     getOptionProc = Tcl_ChannelGetOptionProc(Tcl_GetChannelType(downChan));
01000     if (getOptionProc != NULL) {
01001         return getOptionProc(Tcl_GetChannelInstanceData(downChan), interp,
01002                 optionName, dsPtr);
01003     } else if (optionName == NULL) {
01004         /*
01005          * Request is query for all options, this is ok.
01006          */
01007 
01008         return TCL_OK;
01009     }
01010 
01011     /*
01012      * Request for a specific option has to fail, since we don't have any.
01013      */
01014 
01015     return TCL_ERROR;
01016 }
01017 
01018 /*
01019  *----------------------------------------------------------------------
01020  *
01021  * TransformWatchProc --
01022  *
01023  *      Initialize the notifier to watch for events from this channel.
01024  *
01025  * Side effects:
01026  *      Sets up the notifier so that a future event on the channel will be
01027  *      seen by Tcl.
01028  *
01029  * Result:
01030  *      None.
01031  *
01032  *----------------------------------------------------------------------
01033  */
01034 
01035         /* ARGSUSED */
01036 static void
01037 TransformWatchProc(
01038     ClientData instanceData,    /* Channel to watch. */
01039     int mask)                   /* Events of interest. */
01040 {
01041     TransformChannelData *dataPtr = instanceData;
01042     Tcl_Channel downChan;
01043 
01044     /*
01045      * The caller expressed interest in events occuring for this channel. We
01046      * are forwarding the call to the underlying channel now.
01047      */
01048 
01049     dataPtr->watchMask = mask;
01050 
01051     /*
01052      * No channel handlers any more. We will be notified automatically about
01053      * events on the channel below via a call to our 'TransformNotifyProc'.
01054      * But we have to pass the interest down now. We are allowed to add
01055      * additional 'interest' to the mask if we want to. But this
01056      * transformation has no such interest. It just passes the request down,
01057      * unchanged.
01058      */
01059 
01060     downChan = Tcl_GetStackedChannel(dataPtr->self);
01061 
01062     Tcl_GetChannelType(downChan)->watchProc(
01063             Tcl_GetChannelInstanceData(downChan), mask);
01064 
01065     /*
01066      * Management of the internal timer.
01067      */
01068 
01069     if ((dataPtr->timer != NULL) &&
01070             (!(mask & TCL_READABLE) || ResultEmpty(&dataPtr->result))) {
01071         /*
01072          * A pending timer exists, but either is there no (more) interest in
01073          * the events it generates or nothing is available for reading, so
01074          * remove it.
01075          */
01076 
01077         Tcl_DeleteTimerHandler(dataPtr->timer);
01078         dataPtr->timer = NULL;
01079     }
01080 
01081     if ((dataPtr->timer == NULL) && (mask & TCL_READABLE)
01082             && !ResultEmpty(&dataPtr->result)) {
01083         /*
01084          * There is no pending timer, but there is interest in readable events
01085          * and we actually have data waiting, so generate a timer to flush
01086          * that.
01087          */
01088 
01089         dataPtr->timer = Tcl_CreateTimerHandler(FLUSH_DELAY,
01090                 TransformChannelHandlerTimer, dataPtr);
01091     }
01092 }
01093 
01094 /*
01095  *----------------------------------------------------------------------
01096  *
01097  * TransformGetFileHandleProc --
01098  *
01099  *      Called from Tcl_GetChannelHandle to retrieve OS specific file handle
01100  *      from inside this channel.
01101  *
01102  * Side effects:
01103  *      None.
01104  *
01105  * Result:
01106  *      The appropriate Tcl_File or NULL if not present.
01107  *
01108  *----------------------------------------------------------------------
01109  */
01110 
01111 static int
01112 TransformGetFileHandleProc(
01113     ClientData instanceData,    /* Channel to query. */
01114     int direction,              /* Direction of interest. */
01115     ClientData *handlePtr)      /* Place to store the handle into. */
01116 {
01117     TransformChannelData *dataPtr = instanceData;
01118 
01119     /*
01120      * Return the handle belonging to parent channel. IOW, pass the request
01121      * down and the result up.
01122      */
01123 
01124     return Tcl_GetChannelHandle(Tcl_GetStackedChannel(dataPtr->self),
01125             direction, handlePtr);
01126 }
01127 
01128 /*
01129  *----------------------------------------------------------------------
01130  *
01131  * TransformNotifyProc --
01132  *
01133  *      Handler called by Tcl to inform us of activity on the underlying
01134  *      channel.
01135  *
01136  * Side effects:
01137  *      May process the incoming event by itself.
01138  *
01139  * Result:
01140  *      None.
01141  *
01142  *----------------------------------------------------------------------
01143  */
01144 
01145 static int
01146 TransformNotifyProc(
01147     ClientData clientData,      /* The state of the notified
01148                                  * transformation. */
01149     int mask)                   /* The mask of occuring events. */
01150 {
01151     TransformChannelData *dataPtr = clientData;
01152 
01153     /*
01154      * An event occured in the underlying channel. This transformation doesn't
01155      * process such events thus returns the incoming mask unchanged.
01156      */
01157 
01158     if (dataPtr->timer != NULL) {
01159         /*
01160          * Delete an existing timer. It was not fired, yet we are here, so the
01161          * channel below generated such an event and we don't have to. The
01162          * renewal of the interest after the execution of channel handlers
01163          * will eventually cause us to recreate the timer (in
01164          * TransformWatchProc).
01165          */
01166 
01167         Tcl_DeleteTimerHandler(dataPtr->timer);
01168         dataPtr->timer = NULL;
01169     }
01170     return mask;
01171 }
01172 
01173 /*
01174  *----------------------------------------------------------------------
01175  *
01176  * TransformChannelHandlerTimer --
01177  *
01178  *      Called by the notifier (-> timer) to flush out information waiting in
01179  *      the input buffer.
01180  *
01181  * Side effects:
01182  *      As of 'Tcl_NotifyChannel'.
01183  *
01184  * Result:
01185  *      None.
01186  *
01187  *----------------------------------------------------------------------
01188  */
01189 
01190 static void
01191 TransformChannelHandlerTimer(
01192     ClientData clientData)      /* Transformation to query. */
01193 {
01194     TransformChannelData *dataPtr = clientData;
01195 
01196     dataPtr->timer = NULL;
01197     if (!(dataPtr->watchMask&TCL_READABLE) || ResultEmpty(&dataPtr->result)) {
01198         /*
01199          * The timer fired, but either is there no (more) interest in the
01200          * events it generates or nothing is available for reading, so ignore
01201          * it and don't recreate it.
01202          */
01203 
01204         return;
01205     }
01206     Tcl_NotifyChannel(dataPtr->self, TCL_READABLE);
01207 }
01208 
01209 /*
01210  *----------------------------------------------------------------------
01211  *
01212  * ResultClear --
01213  *
01214  *      Deallocates any memory allocated by 'ResultAdd'.
01215  *
01216  * Side effects:
01217  *      See above.
01218  *
01219  * Result:
01220  *      None.
01221  *
01222  *----------------------------------------------------------------------
01223  */
01224 
01225 static inline void
01226 ResultClear(
01227     ResultBuffer *r)            /* Reference to the buffer to clear out. */
01228 {
01229     r->used = 0;
01230 
01231     if (r->allocated) {
01232         ckfree((char *) r->buf);
01233         r->buf = NULL;
01234         r->allocated = 0;
01235     }
01236 }
01237 
01238 /*
01239  *----------------------------------------------------------------------
01240  *
01241  * ResultInit --
01242  *
01243  *      Initializes the specified buffer structure. The structure will contain
01244  *      valid information for an emtpy buffer.
01245  *
01246  * Side effects:
01247  *      See above.
01248  *
01249  * Result:
01250  *      None.
01251  *
01252  *----------------------------------------------------------------------
01253  */
01254 
01255 static inline void
01256 ResultInit(
01257     ResultBuffer *r)            /* Reference to the structure to
01258                                  * initialize. */
01259 {
01260     r->used = 0;
01261     r->allocated = 0;
01262     r->buf = NULL;
01263 }
01264 
01265 /*
01266  *----------------------------------------------------------------------
01267  *
01268  * ResultEmpty --
01269  *
01270  *      Returns whether the number of bytes stored in the buffer is zero.
01271  *
01272  * Side effects:
01273  *      None.
01274  *
01275  * Result:
01276  *      A boolean.
01277  *
01278  *----------------------------------------------------------------------
01279  */
01280 
01281 static inline int
01282 ResultEmpty(
01283     ResultBuffer *r)            /* The structure to query. */
01284 {
01285     return r->used == 0;
01286 }
01287 
01288 /*
01289  *----------------------------------------------------------------------
01290  *
01291  * ResultCopy --
01292  *
01293  *      Copies the requested number of bytes from the buffer into the
01294  *      specified array and removes them from the buffer afterward. Copies
01295  *      less if there is not enough data in the buffer.
01296  *
01297  * Side effects:
01298  *      See above.
01299  *
01300  * Result:
01301  *      The number of actually copied bytes, possibly less than 'toRead'.
01302  *
01303  *----------------------------------------------------------------------
01304  */
01305 
01306 static inline int
01307 ResultCopy(
01308     ResultBuffer *r,            /* The buffer to read from. */
01309     unsigned char *buf,         /* The buffer to copy into. */
01310     size_t toRead)              /* Number of requested bytes. */
01311 {
01312     if (r->used == 0) {
01313         /*
01314          * Nothing to copy in the case of an empty buffer.
01315          */
01316 
01317         return 0;
01318     } else if (r->used == toRead) {
01319         /*
01320          * We have just enough. Copy everything to the caller.
01321          */
01322 
01323         memcpy(buf, r->buf, toRead);
01324         r->used = 0;
01325     } else if (r->used > toRead) {
01326         /*
01327          * The internal buffer contains more than requested. Copy the
01328          * requested subset to the caller, and shift the remaining bytes down.
01329          */
01330 
01331         memcpy(buf, r->buf, toRead);
01332         memmove(r->buf, r->buf + toRead, r->used - toRead);
01333         r->used -= toRead;
01334     } else {
01335         /*
01336          * There is not enough in the buffer to satisfy the caller, so take
01337          * everything.
01338          */
01339 
01340         memcpy(buf, r->buf, r->used);
01341         toRead = r->used;
01342         r->used = 0;
01343     }
01344     return toRead;
01345 }
01346 
01347 /*
01348  *----------------------------------------------------------------------
01349  *
01350  * ResultAdd --
01351  *
01352  *      Adds the bytes in the specified array to the buffer, by appending it.
01353  *
01354  * Side effects:
01355  *      See above.
01356  *
01357  * Result:
01358  *      None.
01359  *
01360  *----------------------------------------------------------------------
01361  */
01362 
01363 static inline void
01364 ResultAdd(
01365     ResultBuffer *r,            /* The buffer to extend. */
01366     unsigned char *buf,         /* The buffer to read from. */
01367     size_t toWrite)             /* The number of bytes in 'buf'. */
01368 {
01369     if (r->used + toWrite > r->allocated) {
01370         /*
01371          * Extension of the internal buffer is required.
01372          */
01373 
01374         if (r->allocated == 0) {
01375             r->allocated = toWrite + INCREMENT;
01376             r->buf = UCHARP(ckalloc(r->allocated));
01377         } else {
01378             r->allocated += toWrite + INCREMENT;
01379             r->buf = UCHARP(ckrealloc((char *) r->buf, r->allocated));
01380         }
01381     }
01382 
01383     /*
01384      * Now we may copy the data.
01385      */
01386 
01387     memcpy(r->buf + r->used, buf, toWrite);
01388     r->used += toWrite;
01389 }
01390 
01391 /*
01392  * Local Variables:
01393  * mode: c
01394  * c-basic-offset: 4
01395  * fill-column: 78
01396  * End:
01397  */



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