tclIOGT.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:39k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /*
  2.  * tclIOGT.c --
  3.  *
  4.  * Implements a generic transformation exposing the underlying API
  5.  * at the script level.  Contributed by Andreas Kupries.
  6.  *
  7.  * Copyright (c) 2000 Ajuba Solutions
  8.  * Copyright (c) 1999-2000 Andreas Kupries (a.kupries@westend.com)
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  *
  13.  * CVS: $Id: tclIOGT.c,v 1.7.2.2 2006/08/30 17:24:07 hobbs Exp $
  14.  */
  15. #include "tclInt.h"
  16. #include "tclPort.h"
  17. #include "tclIO.h"
  18. /*
  19.  * Forward declarations of internal procedures.
  20.  * First the driver procedures of the transformation.
  21.  */
  22. static int TransformBlockModeProc _ANSI_ARGS_ ((
  23. ClientData instanceData, int mode));
  24. static int TransformCloseProc _ANSI_ARGS_ ((
  25. ClientData instanceData, Tcl_Interp* interp));
  26. static int TransformInputProc _ANSI_ARGS_ ((
  27. ClientData instanceData,
  28. char* buf, int toRead, int* errorCodePtr));
  29. static int TransformOutputProc _ANSI_ARGS_ ((
  30. ClientData instanceData, CONST char *buf,
  31. int toWrite, int* errorCodePtr));
  32. static int TransformSeekProc _ANSI_ARGS_ ((
  33. ClientData instanceData, long offset,
  34. int mode, int* errorCodePtr));
  35. static int TransformSetOptionProc _ANSI_ARGS_((
  36. ClientData instanceData, Tcl_Interp *interp,
  37. CONST char *optionName, CONST char *value));
  38. static int TransformGetOptionProc _ANSI_ARGS_((
  39. ClientData instanceData, Tcl_Interp *interp,
  40. CONST char *optionName, Tcl_DString *dsPtr));
  41. static void TransformWatchProc _ANSI_ARGS_ ((
  42. ClientData instanceData, int mask));
  43. static int TransformGetFileHandleProc _ANSI_ARGS_ ((
  44. ClientData instanceData, int direction,
  45. ClientData* handlePtr));
  46. static int TransformNotifyProc _ANSI_ARGS_ ((
  47. ClientData instanceData, int mask));
  48. static Tcl_WideInt TransformWideSeekProc _ANSI_ARGS_ ((
  49. ClientData instanceData, Tcl_WideInt offset,
  50. int mode, int* errorCodePtr));
  51. /*
  52.  * Forward declarations of internal procedures.
  53.  * Secondly the procedures for handling and generating fileeevents.
  54.  */
  55. static void TransformChannelHandlerTimer _ANSI_ARGS_ ((
  56. ClientData clientData));
  57. /*
  58.  * Forward declarations of internal procedures.
  59.  * Third, helper procedures encapsulating essential tasks.
  60.  */
  61. typedef struct TransformChannelData TransformChannelData;
  62. static int ExecuteCallback _ANSI_ARGS_ ((
  63. TransformChannelData* ctrl, Tcl_Interp* interp,
  64. unsigned char* op, unsigned char* buf,
  65. int bufLen, int transmit, int preserve));
  66. /*
  67.  * Action codes to give to 'ExecuteCallback' (argument 'transmit')
  68.  * confering to the procedure what to do with the result of the script
  69.  * it calls.
  70.  */
  71. #define TRANSMIT_DONT  (0) /* No transfer to do */
  72. #define TRANSMIT_DOWN  (1) /* Transfer to the underlying channel */
  73. #define TRANSMIT_SELF  (2) /* Transfer into our channel. */
  74. #define TRANSMIT_IBUF  (3) /* Transfer to internal input buffer */
  75. #define TRANSMIT_NUM   (4) /* Transfer number to 'maxRead' */
  76. /*
  77.  * Codes for 'preserve' of 'ExecuteCallback'
  78.  */
  79. #define P_PRESERVE    (1)
  80. #define P_NO_PRESERVE (0)
  81. /*
  82.  * Strings for the action codes delivered to the script implementing
  83.  * a transformation. Argument 'op' of 'ExecuteCallback'.
  84.  */
  85. #define A_CREATE_WRITE (UCHARP ("create/write"))
  86. #define A_DELETE_WRITE (UCHARP ("delete/write"))
  87. #define A_FLUSH_WRITE (UCHARP ("flush/write"))
  88. #define A_WRITE (UCHARP ("write"))
  89. #define A_CREATE_READ (UCHARP ("create/read"))
  90. #define A_DELETE_READ (UCHARP ("delete/read"))
  91. #define A_FLUSH_READ (UCHARP ("flush/read"))
  92. #define A_READ (UCHARP ("read"))
  93. #define A_QUERY_MAXREAD (UCHARP ("query/maxRead"))
  94. #define A_CLEAR_READ (UCHARP ("clear/read"))
  95. /*
  96.  * Management of a simple buffer.
  97.  */
  98. typedef struct ResultBuffer ResultBuffer;
  99. static void ResultClear  _ANSI_ARGS_ ((ResultBuffer* r));
  100. static void ResultInit   _ANSI_ARGS_ ((ResultBuffer* r));
  101. static int ResultLength _ANSI_ARGS_ ((ResultBuffer* r));
  102. static int ResultCopy   _ANSI_ARGS_ ((ResultBuffer* r,
  103. unsigned char* buf, int toRead));
  104. static void ResultAdd    _ANSI_ARGS_ ((ResultBuffer* r,
  105. unsigned char* buf, int toWrite));
  106. /*
  107.  * This structure describes the channel type structure for tcl based
  108.  * transformations.
  109.  */
  110. static Tcl_ChannelType transformChannelType = {
  111.     "transform", /* Type name. */
  112.     TCL_CHANNEL_VERSION_3,
  113.     TransformCloseProc, /* Close proc. */
  114.     TransformInputProc, /* Input proc. */
  115.     TransformOutputProc, /* Output proc. */
  116.     TransformSeekProc, /* Seek proc. */
  117.     TransformSetOptionProc, /* Set option proc. */
  118.     TransformGetOptionProc, /* Get option proc. */
  119.     TransformWatchProc, /* Initialize notifier. */
  120.     TransformGetFileHandleProc, /* Get OS handles out of channel. */
  121.     NULL, /* close2proc */
  122.     TransformBlockModeProc, /* Set blocking/nonblocking mode.*/
  123.     NULL, /* Flush proc. */
  124.     TransformNotifyProc,                /* Handling of events bubbling up */
  125.     TransformWideSeekProc, /* Wide seek proc */
  126. };
  127. /*
  128.  * Possible values for 'flags' field in control structure, see below.
  129.  */
  130. #define CHANNEL_ASYNC (1<<0) /* non-blocking mode */
  131. /*
  132.  * Definition of the structure containing the information about the
  133.  * internal input buffer.
  134.  */
  135. struct ResultBuffer {
  136.     unsigned char* buf;       /* Reference to the buffer area */
  137.     int    allocated; /* Allocated size of the buffer area */
  138.     int    used;      /* Number of bytes in the buffer, <= allocated */
  139. };
  140. /*
  141.  * Additional bytes to allocate during buffer expansion
  142.  */
  143. #define INCREMENT (512)
  144. /*
  145.  * Number of milliseconds to wait before firing an event to flush
  146.  * out information waiting in buffers (fileevent support).
  147.  */
  148. #define FLUSH_DELAY (5)
  149. /*
  150.  * Convenience macro to make some casts easier to use.
  151.  */
  152. #define UCHARP(x) ((unsigned char*) (x))
  153. #define NO_INTERP ((Tcl_Interp*) NULL)
  154. /*
  155.  * Definition of a structure used by all transformations generated here to
  156.  * maintain their local state.
  157.  */
  158. struct TransformChannelData {
  159.     /*
  160.      * General section. Data to integrate the transformation into the channel
  161.      * system.
  162.      */
  163.     Tcl_Channel self;     /* Our own Channel handle */
  164.     int readIsFlushed;    /* Flag to note wether in.flushProc was called or not
  165.    */
  166.     int flags;            /* Currently CHANNEL_ASYNC or zero */
  167.     int watchMask;        /* Current watch/event/interest mask */
  168.     int mode;             /* mode of parent channel, OR'ed combination of
  169.    * TCL_READABLE, TCL_WRITABLE */
  170.     Tcl_TimerToken timer; /* Timer for automatic flushing of information
  171.    * sitting in an internal buffer. Required for full
  172.    * fileevent support */
  173.     /*
  174.      * Transformation specific data.
  175.      */
  176.     int maxRead;            /* Maximum allowed number of bytes to read, as
  177.      * given to us by the tcl script implementing the
  178.      * transformation. */
  179.     Tcl_Interp*    interp;  /* Reference to the interpreter which created the
  180.      * transformation. Used to execute the code
  181.      * below. */
  182.     Tcl_Obj*       command; /* Tcl code to execute for a buffer */
  183.     ResultBuffer   result;  /* Internal buffer used to store the result of a
  184.      * transformation of incoming data. Additionally
  185.      * serves as buffer of all data not yet consumed by
  186.      * the reader. */
  187. };
  188. /*
  189.  *----------------------------------------------------------------------
  190.  *
  191.  * TclChannelTransform --
  192.  *
  193.  * Implements the Tcl "testchannel transform" debugging command.
  194.  * This is part of the testing environment.  This sets up a tcl
  195.  * script (cmdObjPtr) to be used as a transform on the channel.
  196.  *
  197.  * Results:
  198.  * A standard Tcl result.
  199.  *
  200.  * Side effects:
  201.  * None.
  202.  *
  203.  *----------------------------------------------------------------------
  204.  */
  205. /* ARGSUSED */
  206. int
  207. TclChannelTransform(interp, chan, cmdObjPtr)
  208.     Tcl_Interp *interp; /* Interpreter for result. */
  209.     Tcl_Channel chan; /* Channel to transform. */
  210.     Tcl_Obj *cmdObjPtr; /* Script to use for transform. */
  211. {
  212.     Channel *chanPtr; /* The actual channel. */
  213.     ChannelState *statePtr; /* state info for channel */
  214.     int mode; /* rw mode of the channel */
  215.     TransformChannelData *dataPtr;
  216.     int res;
  217.     Tcl_DString ds;
  218.     if (chan == (Tcl_Channel) NULL) {
  219. return TCL_ERROR;
  220.     }
  221.     chanPtr = (Channel *) chan;
  222.     statePtr = chanPtr->state;
  223.     chanPtr = statePtr->topChanPtr;
  224.     chan = (Tcl_Channel) chanPtr;
  225.     mode = (statePtr->flags & (TCL_READABLE|TCL_WRITABLE));
  226.     /*
  227.      * Now initialize the transformation state and stack it upon the
  228.      * specified channel. One of the necessary things to do is to
  229.      * retrieve the blocking regime of the underlying channel and to
  230.      * use the same for us too.
  231.      */
  232.     dataPtr = (TransformChannelData*) ckalloc(sizeof(TransformChannelData));
  233.     Tcl_DStringInit (&ds);
  234.     Tcl_GetChannelOption(interp, chan, "-blocking", &ds);
  235.     dataPtr->readIsFlushed = 0;
  236.     dataPtr->flags = 0;
  237.     if (ds.string[0] == '0') {
  238. dataPtr->flags |= CHANNEL_ASYNC;
  239.     }
  240.     Tcl_DStringFree (&ds);
  241.     dataPtr->self = chan;
  242.     dataPtr->watchMask = 0;
  243.     dataPtr->mode = mode;
  244.     dataPtr->timer = (Tcl_TimerToken) NULL;
  245.     dataPtr->maxRead = 4096; /* Initial value not relevant */
  246.     dataPtr->interp = interp;
  247.     dataPtr->command = cmdObjPtr;
  248.     Tcl_IncrRefCount(dataPtr->command);
  249.     ResultInit(&dataPtr->result);
  250.     dataPtr->self = Tcl_StackChannel(interp, &transformChannelType,
  251.     (ClientData) dataPtr, mode, chan);
  252.     if (dataPtr->self == (Tcl_Channel) NULL) {
  253. Tcl_AppendResult(interp, "nfailed to stack channel "",
  254. Tcl_GetChannelName(chan), """, (char *) NULL);
  255. Tcl_DecrRefCount(dataPtr->command);
  256. ResultClear(&dataPtr->result);
  257. ckfree((VOID *) dataPtr);
  258. return TCL_ERROR;
  259.     }
  260.     /*
  261.      * At last initialize the transformation at the script level.
  262.      */
  263.     if (dataPtr->mode & TCL_WRITABLE) {
  264. res = ExecuteCallback (dataPtr, NO_INTERP, A_CREATE_WRITE,
  265. NULL, 0, TRANSMIT_DONT, P_NO_PRESERVE);
  266. if (res != TCL_OK) {
  267.     Tcl_UnstackChannel(interp, chan);
  268.     return TCL_ERROR;
  269. }
  270.     }
  271.     if (dataPtr->mode & TCL_READABLE) {
  272. res = ExecuteCallback (dataPtr, NO_INTERP, A_CREATE_READ,
  273. NULL, 0, TRANSMIT_DONT, P_NO_PRESERVE);
  274. if (res != TCL_OK) {
  275.     ExecuteCallback (dataPtr, NO_INTERP, A_DELETE_WRITE,
  276.     NULL, 0, TRANSMIT_DONT, P_NO_PRESERVE);
  277.     Tcl_UnstackChannel(interp, chan);
  278.     return TCL_ERROR;
  279. }
  280.     }
  281.     return TCL_OK;
  282. }
  283. /*
  284.  *------------------------------------------------------*
  285.  *
  286.  * ExecuteCallback --
  287.  *
  288.  * Executes the defined callback for buffer and
  289.  * operation.
  290.  *
  291.  * Sideeffects:
  292.  * As of the executed tcl script.
  293.  *
  294.  * Result:
  295.  * A standard TCL error code. In case of an
  296.  * error a message is left in the result area
  297.  * of the specified interpreter.
  298.  *
  299.  *------------------------------------------------------*
  300.  */
  301. static int
  302. ExecuteCallback (dataPtr, interp, op, buf, bufLen, transmit, preserve)
  303.     TransformChannelData* dataPtr;  /* Transformation with the callback */
  304.     Tcl_Interp*           interp;   /* Current interpreter, possibly NULL */
  305.     unsigned char*        op;       /* Operation invoking the callback */
  306.     unsigned char*        buf;      /* Buffer to give to the script. */
  307.     int   bufLen;   /* Ands its length */
  308.     int                   transmit; /* Flag, determines whether the result
  309.      * of the callback is sent to the
  310.      * underlying channel or not. */
  311.     int                   preserve; /* Flag. If true the procedure will
  312.      * preserver the result state of all
  313.      * accessed interpreters. */
  314. {
  315.     /*
  316.      * Step 1, create the complete command to execute. Do this by appending
  317.      * operation and buffer to operate upon to a copy of the callback
  318.      * definition. We *cannot* create a list containing 3 objects and then use
  319.      * 'Tcl_EvalObjv', because the command may contain additional prefixed
  320.      * arguments. Feather's curried commands would come in handy here.
  321.      */
  322.     Tcl_Obj* resObj;     /* See below, switch (transmit) */
  323.     int resLen;
  324.     unsigned char* resBuf;
  325.     Tcl_SavedResult ciSave;
  326.     int res = TCL_OK;
  327.     Tcl_Obj* command = Tcl_DuplicateObj (dataPtr->command);
  328.     Tcl_Obj* temp;
  329.     if (preserve) {
  330. Tcl_SaveResult (dataPtr->interp, &ciSave);
  331.     }
  332.     if (command == (Tcl_Obj*) NULL) {
  333.         /* Memory allocation problem */
  334.         res = TCL_ERROR;
  335.         goto cleanup;
  336.     }
  337.     Tcl_IncrRefCount(command);
  338.     temp = Tcl_NewStringObj((char*) op, -1);
  339.     if (temp == (Tcl_Obj*) NULL) {
  340.         /* Memory allocation problem */
  341.         res = TCL_ERROR;
  342.         goto cleanup;
  343.     }
  344.     res = Tcl_ListObjAppendElement(dataPtr->interp, command, temp);
  345.     if (res != TCL_OK)
  346. goto cleanup;
  347.     /*
  348.      * Use a byte-array to prevent the misinterpretation of binary data
  349.      * coming through as UTF while at the tcl level.
  350.      */
  351.     temp = Tcl_NewByteArrayObj(buf, bufLen);
  352.     if (temp == (Tcl_Obj*) NULL) {
  353.         /* Memory allocation problem */
  354. res = TCL_ERROR;
  355.         goto cleanup;
  356.     }
  357.     res = Tcl_ListObjAppendElement (dataPtr->interp, command, temp);
  358.     if (res != TCL_OK)
  359.         goto cleanup;
  360.     /*
  361.      * Step 2, execute the command at the global level of the interpreter
  362.      * used to create the transformation. Destroy the command afterward.
  363.      * If an error occured and the current interpreter is defined and not
  364.      * equal to the interpreter for the callback, then copy the error
  365.      * message into current interpreter. Don't copy if in preservation mode.
  366.      */
  367.     res = Tcl_EvalObjEx(dataPtr->interp, command, TCL_EVAL_GLOBAL);
  368.     Tcl_DecrRefCount (command);
  369.     command = (Tcl_Obj*) NULL;
  370.     if ((res != TCL_OK) && (interp != NO_INTERP) &&
  371.     (dataPtr->interp != interp) && !preserve) {
  372.         Tcl_SetObjResult(interp, Tcl_GetObjResult(dataPtr->interp));
  373. return res;
  374.     }
  375.     /*
  376.      * Step 3, transmit a possible conversion result to the underlying
  377.      * channel, or ourselves.
  378.      */
  379.     switch (transmit) {
  380. case TRANSMIT_DONT:
  381.     /* nothing to do */
  382.     break;
  383. case TRANSMIT_DOWN:
  384.     resObj = Tcl_GetObjResult(dataPtr->interp);
  385.     resBuf = (unsigned char*) Tcl_GetByteArrayFromObj(resObj, &resLen);
  386.     Tcl_WriteRaw(Tcl_GetStackedChannel(dataPtr->self),
  387.     (char*) resBuf, resLen);
  388.     break;
  389. case TRANSMIT_SELF:
  390.     resObj = Tcl_GetObjResult (dataPtr->interp);
  391.     resBuf = (unsigned char*) Tcl_GetByteArrayFromObj(resObj, &resLen);
  392.     Tcl_WriteRaw(dataPtr->self, (char*) resBuf, resLen);
  393.     break;
  394. case TRANSMIT_IBUF:
  395.     resObj = Tcl_GetObjResult (dataPtr->interp);
  396.     resBuf = (unsigned char*) Tcl_GetByteArrayFromObj(resObj, &resLen);
  397.     ResultAdd(&dataPtr->result, resBuf, resLen);
  398.     break;
  399. case TRANSMIT_NUM:
  400.     /* Interpret result as integer number */
  401.     resObj = Tcl_GetObjResult (dataPtr->interp);
  402.     Tcl_GetIntFromObj(dataPtr->interp, resObj, &dataPtr->maxRead);
  403.     break;
  404.     }
  405.     Tcl_ResetResult(dataPtr->interp);
  406.     if (preserve) {
  407. Tcl_RestoreResult(dataPtr->interp, &ciSave);
  408.     }
  409.     return res;
  410.     cleanup:
  411.     if (preserve) {
  412. Tcl_RestoreResult(dataPtr->interp, &ciSave);
  413.     }
  414.     if (command != (Tcl_Obj*) NULL) {
  415.         Tcl_DecrRefCount(command);
  416.     }
  417.     return res;
  418. }
  419. /*
  420.  *------------------------------------------------------*
  421.  *
  422.  * TransformBlockModeProc --
  423.  *
  424.  * Trap handler. Called by the generic IO system
  425.  * during option processing to change the blocking
  426.  * mode of the channel.
  427.  *
  428.  * Sideeffects:
  429.  * Forwards the request to the underlying
  430.  * channel.
  431.  *
  432.  * Result:
  433.  * 0 if successful, errno when failed.
  434.  *
  435.  *------------------------------------------------------*
  436.  */
  437. static int
  438. TransformBlockModeProc (instanceData, mode)
  439.     ClientData  instanceData; /* State of transformation */
  440.     int         mode;         /* New blocking mode */
  441. {
  442.     TransformChannelData* dataPtr = (TransformChannelData*) instanceData;
  443.     if (mode == TCL_MODE_NONBLOCKING) {
  444.         dataPtr->flags |= CHANNEL_ASYNC;
  445.     } else {
  446.         dataPtr->flags &= ~(CHANNEL_ASYNC);
  447.     }
  448.     return 0;
  449. }
  450. /*
  451.  *------------------------------------------------------*
  452.  *
  453.  * TransformCloseProc --
  454.  *
  455.  * Trap handler. Called by the generic IO system
  456.  * during destruction of the transformation channel.
  457.  *
  458.  * Sideeffects:
  459.  * Releases the memory allocated in
  460.  * 'Tcl_TransformObjCmd'.
  461.  *
  462.  * Result:
  463.  * None.
  464.  *
  465.  *------------------------------------------------------*
  466.  */
  467. static int
  468. TransformCloseProc (instanceData, interp)
  469.     ClientData  instanceData;
  470.     Tcl_Interp* interp;
  471. {
  472.     TransformChannelData* dataPtr = (TransformChannelData*) instanceData;
  473.     /*
  474.      * Important: In this procedure 'dataPtr->self' already points to
  475.      * the underlying channel.
  476.      */
  477.     /*
  478.      * There is no need to cancel an existing channel handler, this is already
  479.      * done. Either by 'Tcl_UnstackChannel' or by the general cleanup in
  480.      * 'Tcl_Close'.
  481.      *
  482.      * But we have to cancel an active timer to prevent it from firing on the
  483.      * removed channel.
  484.      */
  485.     if (dataPtr->timer != (Tcl_TimerToken) NULL) {
  486.         Tcl_DeleteTimerHandler (dataPtr->timer);
  487. dataPtr->timer = (Tcl_TimerToken) NULL;
  488.     }
  489.     /*
  490.      * Now flush data waiting in internal buffers to output and input. The
  491.      * input must be done despite the fact that there is no real receiver
  492.      * for it anymore. But the scripts might have sideeffects other parts
  493.      * of the system rely on (f.e. signaling the close to interested parties).
  494.      */
  495.     if (dataPtr->mode & TCL_WRITABLE) {
  496.         ExecuteCallback (dataPtr, interp, A_FLUSH_WRITE,
  497. NULL, 0, TRANSMIT_DOWN, 1);
  498.     }
  499.     if ((dataPtr->mode & TCL_READABLE) && !dataPtr->readIsFlushed) {
  500. dataPtr->readIsFlushed = 1;
  501.         ExecuteCallback (dataPtr, interp, A_FLUSH_READ,
  502. NULL, 0, TRANSMIT_IBUF, 1);
  503.     }
  504.     if (dataPtr->mode & TCL_WRITABLE) {
  505.         ExecuteCallback (dataPtr, interp, A_DELETE_WRITE,
  506. NULL, 0, TRANSMIT_DONT, 1);
  507.     }
  508.     if (dataPtr->mode & TCL_READABLE) {
  509.         ExecuteCallback (dataPtr, interp, A_DELETE_READ,
  510. NULL, 0, TRANSMIT_DONT, 1);
  511.     }
  512.     /*
  513.      * General cleanup
  514.      */
  515.     ResultClear(&dataPtr->result);
  516.     Tcl_DecrRefCount(dataPtr->command);
  517.     ckfree((VOID*) dataPtr);
  518.     return TCL_OK;
  519. }
  520. /*
  521.  *------------------------------------------------------*
  522.  *
  523.  * TransformInputProc --
  524.  *
  525.  * Called by the generic IO system to convert read data.
  526.  *
  527.  * Sideeffects:
  528.  * As defined by the conversion.
  529.  *
  530.  * Result:
  531.  * A transformed buffer.
  532.  *
  533.  *------------------------------------------------------*
  534.  */
  535. static int
  536. TransformInputProc (instanceData, buf, toRead, errorCodePtr)
  537.     ClientData instanceData;
  538.     char*      buf;
  539.     int        toRead;
  540.     int*       errorCodePtr;
  541. {
  542.     TransformChannelData* dataPtr = (TransformChannelData*) instanceData;
  543.     int gotBytes, read, res, copied;
  544.     Tcl_Channel downChan;
  545.     /* should assert (dataPtr->mode & TCL_READABLE) */
  546.     if (toRead == 0) {
  547. /* Catch a no-op.
  548.  */
  549. return 0;
  550.     }
  551.     gotBytes = 0;
  552.     downChan = Tcl_GetStackedChannel(dataPtr->self);
  553.     while (toRead > 0) {
  554.         /*
  555.  * Loop until the request is satisfied (or no data is available from
  556.  * below, possibly EOF).
  557.  */
  558.         copied    = ResultCopy (&dataPtr->result, UCHARP (buf), toRead);
  559. toRead   -= copied;
  560. buf      += copied;
  561. gotBytes += copied;
  562. if (toRead == 0) {
  563.     /* The request was completely satisfied from our buffers.
  564.      * We can break out of the loop and return to the caller.
  565.      */
  566.     return gotBytes;
  567. }
  568. /*
  569.  * Length (dataPtr->result) == 0, toRead > 0 here . Use the incoming
  570.  * 'buf'! as target to store the intermediary information read
  571.  * from the underlying channel.
  572.  *
  573.  * Ask the tcl level how much data it allows us to read from
  574.  * the underlying channel. This feature allows the transform to
  575.  * signal EOF upstream although there is none downstream. Useful
  576.  * to control an unbounded 'fcopy', either through counting bytes,
  577.  * or by pattern matching.
  578.  */
  579. ExecuteCallback (dataPtr, NO_INTERP, A_QUERY_MAXREAD,
  580. NULL, 0, TRANSMIT_NUM /* -> maxRead */, 1);
  581. if (dataPtr->maxRead >= 0) {
  582.     if (dataPtr->maxRead < toRead) {
  583.         toRead = dataPtr->maxRead;
  584.     }
  585. } /* else: 'maxRead < 0' == Accept the current value of toRead */
  586. if (toRead <= 0) {
  587.     return gotBytes;
  588. }
  589. read = Tcl_ReadRaw(downChan, buf, toRead);
  590. if (read < 0) {
  591.     /* Report errors to caller. EAGAIN is a special situation.
  592.      * If we had some data before we report that instead of the
  593.      * request to re-try.
  594.      */
  595.     if ((Tcl_GetErrno() == EAGAIN) && (gotBytes > 0)) {
  596.         return gotBytes;
  597.     }
  598.     *errorCodePtr = Tcl_GetErrno();
  599.     return -1;      
  600. }
  601. if (read == 0) {
  602.     /*
  603.      * Check wether we hit on EOF in the underlying channel or
  604.      * not. If not differentiate between blocking and
  605.      * non-blocking modes. In non-blocking mode we ran
  606.      * temporarily out of data. Signal this to the caller via
  607.      * EWOULDBLOCK and error return (-1). In the other cases
  608.      * we simply return what we got and let the caller wait
  609.      * for more. On the other hand, if we got an EOF we have
  610.      * to convert and flush all waiting partial data.
  611.      */
  612.     if (! Tcl_Eof (downChan)) {
  613.         if ((gotBytes == 0) && (dataPtr->flags & CHANNEL_ASYNC)) {
  614.     *errorCodePtr = EWOULDBLOCK;
  615.     return -1;
  616. } else {
  617.     return gotBytes;
  618. }
  619.     } else {
  620.         if (dataPtr->readIsFlushed) {
  621.     /* Already flushed, nothing to do anymore
  622.      */
  623.     return gotBytes;
  624. }
  625. dataPtr->readIsFlushed = 1;
  626. ExecuteCallback (dataPtr, NO_INTERP, A_FLUSH_READ,
  627. NULL, 0, TRANSMIT_IBUF, P_PRESERVE);
  628. if (ResultLength (&dataPtr->result) == 0) {
  629.     /* we had nothing to flush */
  630.     return gotBytes;
  631. }
  632. continue; /* at: while (toRead > 0) */
  633.     }
  634. } /* read == 0 */
  635. /* Transform the read chunk and add the result to our
  636.  * read buffer (dataPtr->result)
  637.  */
  638. res = ExecuteCallback (dataPtr, NO_INTERP, A_READ,
  639. UCHARP (buf), read, TRANSMIT_IBUF, P_PRESERVE);
  640. if (res != TCL_OK) {
  641.     *errorCodePtr = EINVAL;
  642.     return -1;
  643. }
  644.     } /* while toRead > 0 */
  645.     return gotBytes;
  646. }
  647. /*
  648.  *------------------------------------------------------*
  649.  *
  650.  * TransformOutputProc --
  651.  *
  652.  * Called by the generic IO system to convert data
  653.  * waiting to be written.
  654.  *
  655.  * Sideeffects:
  656.  * As defined by the transformation.
  657.  *
  658.  * Result:
  659.  * A transformed buffer.
  660.  *
  661.  *------------------------------------------------------*
  662.  */
  663. static int
  664. TransformOutputProc (instanceData, buf, toWrite, errorCodePtr)
  665.     ClientData instanceData;
  666.     CONST char*      buf;
  667.     int        toWrite;
  668.     int*       errorCodePtr;
  669. {
  670.     TransformChannelData* dataPtr = (TransformChannelData*) instanceData;
  671.     int res;
  672.     /* should assert (dataPtr->mode & TCL_WRITABLE) */
  673.     if (toWrite == 0) {
  674. /* Catch a no-op.
  675.  */
  676. return 0;
  677.     }
  678.     res = ExecuteCallback (dataPtr, NO_INTERP, A_WRITE,
  679.     UCHARP (buf), toWrite,
  680.     TRANSMIT_DOWN, P_NO_PRESERVE);
  681.     if (res != TCL_OK) {
  682.         *errorCodePtr = EINVAL;
  683. return -1;
  684.     }
  685.     return toWrite;
  686. }
  687. /*
  688.  *------------------------------------------------------*
  689.  *
  690.  * TransformSeekProc --
  691.  *
  692.  * This procedure is called by the generic IO level
  693.  * to move the access point in a channel.
  694.  *
  695.  * Sideeffects:
  696.  * Moves the location at which the channel
  697.  * will be accessed in future operations.
  698.  * Flushes all transformation buffers, then
  699.  * forwards it to the underlying channel.
  700.  *
  701.  * Result:
  702.  * -1 if failed, the new position if
  703.  * successful. An output argument contains
  704.  * the POSIX error code if an error
  705.  * occurred, or zero.
  706.  *
  707.  *------------------------------------------------------*
  708.  */
  709. static int
  710. TransformSeekProc (instanceData, offset, mode, errorCodePtr)
  711.     ClientData  instanceData; /* The channel to manipulate */
  712.     long offset; /* Size of movement. */
  713.     int         mode; /* How to move */
  714.     int*        errorCodePtr; /* Location of error flag. */
  715. {
  716.     TransformChannelData* dataPtr = (TransformChannelData*) instanceData;
  717.     Tcl_Channel           parent        = Tcl_GetStackedChannel(dataPtr->self);
  718.     Tcl_ChannelType*      parentType = Tcl_GetChannelType(parent);
  719.     Tcl_DriverSeekProc*   parentSeekProc = Tcl_ChannelSeekProc(parentType);
  720.     if ((offset == 0) && (mode == SEEK_CUR)) {
  721.         /* This is no seek but a request to tell the caller the current
  722.  * location. Simply pass the request down.
  723.  */
  724. return (*parentSeekProc) (Tcl_GetChannelInstanceData(parent),
  725. offset, mode, errorCodePtr);
  726.     }
  727.     /*
  728.      * It is a real request to change the position. Flush all data waiting
  729.      * for output and discard everything in the input buffers. Then pass
  730.      * the request down, unchanged.
  731.      */
  732.     if (dataPtr->mode & TCL_WRITABLE) {
  733.         ExecuteCallback (dataPtr, NO_INTERP, A_FLUSH_WRITE,
  734. NULL, 0, TRANSMIT_DOWN, P_NO_PRESERVE);
  735.     }
  736.     if (dataPtr->mode & TCL_READABLE) {
  737.         ExecuteCallback (dataPtr, NO_INTERP, A_CLEAR_READ,
  738. NULL, 0, TRANSMIT_DONT, P_NO_PRESERVE);
  739. ResultClear(&dataPtr->result);
  740. dataPtr->readIsFlushed = 0;
  741.     }
  742.     return (*parentSeekProc) (Tcl_GetChannelInstanceData(parent),
  743.     offset, mode, errorCodePtr);
  744. }
  745. /*
  746.  *----------------------------------------------------------------------
  747.  *
  748.  * TransformWideSeekProc --
  749.  *
  750.  * This procedure is called by the generic IO level to move the
  751.  * access point in a channel, with a (potentially) 64-bit offset.
  752.  *
  753.  * Side effects:
  754.  * Moves the location at which the channel will be accessed in
  755.  * future operations.  Flushes all transformation buffers, then
  756.  * forwards it to the underlying channel.
  757.  *
  758.  * Result:
  759.  * -1 if failed, the new position if successful. An output
  760.  * argument contains the POSIX error code if an error occurred,
  761.  * or zero.
  762.  *
  763.  *----------------------------------------------------------------------
  764.  */
  765. static Tcl_WideInt
  766. TransformWideSeekProc (instanceData, offset, mode, errorCodePtr)
  767.     ClientData  instanceData; /* The channel to manipulate */
  768.     Tcl_WideInt offset; /* Size of movement. */
  769.     int         mode; /* How to move */
  770.     int*        errorCodePtr; /* Location of error flag. */
  771. {
  772.     TransformChannelData* dataPtr =
  773. (TransformChannelData*) instanceData;
  774.     Tcl_Channel parent =
  775. Tcl_GetStackedChannel(dataPtr->self);
  776.     Tcl_ChannelType* parentType =
  777. Tcl_GetChannelType(parent);
  778.     Tcl_DriverSeekProc* parentSeekProc =
  779. Tcl_ChannelSeekProc(parentType);
  780.     Tcl_DriverWideSeekProc* parentWideSeekProc =
  781. Tcl_ChannelWideSeekProc(parentType);
  782.     ClientData parentData =
  783. Tcl_GetChannelInstanceData(parent);
  784.     if ((offset == Tcl_LongAsWide(0)) && (mode == SEEK_CUR)) {
  785.         /*
  786.  * This is no seek but a request to tell the caller the current
  787.  * location. Simply pass the request down.
  788.  */
  789. if (parentWideSeekProc != NULL) {
  790.     return (*parentWideSeekProc) (parentData, offset, mode,
  791.     errorCodePtr);
  792. }
  793. return Tcl_LongAsWide((*parentSeekProc) (parentData, 0, mode,
  794. errorCodePtr));
  795.     }
  796.     /*
  797.      * It is a real request to change the position. Flush all data waiting
  798.      * for output and discard everything in the input buffers. Then pass
  799.      * the request down, unchanged.
  800.      */
  801.     if (dataPtr->mode & TCL_WRITABLE) {
  802.         ExecuteCallback (dataPtr, NO_INTERP, A_FLUSH_WRITE,
  803. NULL, 0, TRANSMIT_DOWN, P_NO_PRESERVE);
  804.     }
  805.     if (dataPtr->mode & TCL_READABLE) {
  806.         ExecuteCallback (dataPtr, NO_INTERP, A_CLEAR_READ,
  807. NULL, 0, TRANSMIT_DONT, P_NO_PRESERVE);
  808. ResultClear(&dataPtr->result);
  809. dataPtr->readIsFlushed = 0;
  810.     }
  811.     /*
  812.      * If we have a wide seek capability, we should stick with that.
  813.      */
  814.     if (parentWideSeekProc != NULL) {
  815. return (*parentWideSeekProc) (parentData, offset, mode, errorCodePtr);
  816.     }
  817.     /*
  818.      * We're transferring to narrow seeks at this point; this is a bit
  819.      * complex because we have to check whether the seek is possible
  820.      * first (i.e. whether we are losing information in truncating the
  821.      * bits of the offset.)  Luckily, there's a defined error for what
  822.      * happens when trying to go out of the representable range.
  823.      */
  824.     if (offset<Tcl_LongAsWide(LONG_MIN) || offset>Tcl_LongAsWide(LONG_MAX)) {
  825. *errorCodePtr = EOVERFLOW;
  826. return Tcl_LongAsWide(-1);
  827.     }
  828.     return Tcl_LongAsWide((*parentSeekProc) (parentData,
  829.     Tcl_WideAsLong(offset), mode, errorCodePtr));
  830. }
  831. /*
  832.  *------------------------------------------------------*
  833.  *
  834.  * TransformSetOptionProc --
  835.  *
  836.  * Called by generic layer to handle the reconfi-
  837.  * guration of channel specific options. As this
  838.  * channel type does not have such, it simply passes
  839.  * all requests downstream.
  840.  *
  841.  * Sideeffects:
  842.  * As defined by the channel downstream.
  843.  *
  844.  * Result:
  845.  * A standard TCL error code.
  846.  *
  847.  *------------------------------------------------------*
  848.  */
  849. static int
  850. TransformSetOptionProc (instanceData, interp, optionName, value)
  851.     ClientData instanceData;
  852.     Tcl_Interp *interp;
  853.     CONST char *optionName;
  854.     CONST char *value;
  855. {
  856.     TransformChannelData* dataPtr = (TransformChannelData*) instanceData;
  857.     Tcl_Channel downChan = Tcl_GetStackedChannel(dataPtr->self);
  858.     Tcl_DriverSetOptionProc *setOptionProc;
  859.     setOptionProc = Tcl_ChannelSetOptionProc(Tcl_GetChannelType(downChan));
  860.     if (setOptionProc != NULL) {
  861. return (*setOptionProc)(Tcl_GetChannelInstanceData(downChan),
  862. interp, optionName, value);
  863.     }
  864.     return TCL_ERROR;
  865. }
  866. /*
  867.  *------------------------------------------------------*
  868.  *
  869.  * TransformGetOptionProc --
  870.  *
  871.  * Called by generic layer to handle requests for
  872.  * the values of channel specific options. As this
  873.  * channel type does not have such, it simply passes
  874.  * all requests downstream.
  875.  *
  876.  * Sideeffects:
  877.  * As defined by the channel downstream.
  878.  *
  879.  * Result:
  880.  * A standard TCL error code.
  881.  *
  882.  *------------------------------------------------------*
  883.  */
  884. static int
  885. TransformGetOptionProc (instanceData, interp, optionName, dsPtr)
  886.     ClientData   instanceData;
  887.     Tcl_Interp*  interp;
  888.     CONST char*        optionName;
  889.     Tcl_DString* dsPtr;
  890. {
  891.     TransformChannelData* dataPtr = (TransformChannelData*) instanceData;
  892.     Tcl_Channel downChan = Tcl_GetStackedChannel(dataPtr->self);
  893.     Tcl_DriverGetOptionProc *getOptionProc;
  894.     getOptionProc = Tcl_ChannelGetOptionProc(Tcl_GetChannelType(downChan));
  895.     if (getOptionProc != NULL) {
  896. return (*getOptionProc)(Tcl_GetChannelInstanceData(downChan),
  897. interp, optionName, dsPtr);
  898.     } else if (optionName == (CONST char*) NULL) {
  899. /*
  900.  * Request is query for all options, this is ok.
  901.  */
  902. return TCL_OK;
  903.     }
  904.     /*
  905.      * Request for a specific option has to fail, we don't have any.
  906.      */
  907.     return TCL_ERROR;
  908. }
  909. /*
  910.  *------------------------------------------------------*
  911.  *
  912.  * TransformWatchProc --
  913.  *
  914.  * Initialize the notifier to watch for events from
  915.  * this channel.
  916.  *
  917.  * Sideeffects:
  918.  * Sets up the notifier so that a future
  919.  * event on the channel will be seen by Tcl.
  920.  *
  921.  * Result:
  922.  * None.
  923.  *
  924.  *------------------------------------------------------*
  925.  */
  926. /* ARGSUSED */
  927. static void
  928. TransformWatchProc (instanceData, mask)
  929.     ClientData instanceData; /* Channel to watch */
  930.     int        mask; /* Events of interest */
  931. {
  932.     /* The caller expressed interest in events occuring for this
  933.      * channel. We are forwarding the call to the underlying
  934.      * channel now.
  935.      */
  936.     TransformChannelData* dataPtr = (TransformChannelData*) instanceData;
  937.     Tcl_Channel     downChan;
  938.     dataPtr->watchMask = mask;
  939.     /* No channel handlers any more. We will be notified automatically
  940.      * about events on the channel below via a call to our
  941.      * 'TransformNotifyProc'. But we have to pass the interest down now.
  942.      * We are allowed to add additional 'interest' to the mask if we want
  943.      * to. But this transformation has no such interest. It just passes
  944.      * the request down, unchanged.
  945.      */
  946.     downChan = Tcl_GetStackedChannel(dataPtr->self);
  947.     (Tcl_GetChannelType(downChan))
  948. ->watchProc(Tcl_GetChannelInstanceData(downChan), mask);
  949.     /*
  950.      * Management of the internal timer.
  951.      */
  952.     if ((dataPtr->timer != (Tcl_TimerToken) NULL) &&
  953.     (!(mask & TCL_READABLE) || (ResultLength(&dataPtr->result) == 0))) {
  954.         /* A pending timer exists, but either is there no (more)
  955.  * interest in the events it generates or nothing is availablee
  956.  * for reading, so remove it.
  957.  */
  958.         Tcl_DeleteTimerHandler (dataPtr->timer);
  959. dataPtr->timer = (Tcl_TimerToken) NULL;
  960.     }
  961.     if ((dataPtr->timer == (Tcl_TimerToken) NULL) &&
  962.     (mask & TCL_READABLE) && (ResultLength (&dataPtr->result) > 0)) {
  963.         /* There is no pending timer, but there is interest in readable
  964.  * events and we actually have data waiting, so generate a timer
  965.  * to flush that.
  966.  */
  967. dataPtr->timer = Tcl_CreateTimerHandler (FLUSH_DELAY,
  968. TransformChannelHandlerTimer, (ClientData) dataPtr);
  969.     }
  970. }
  971. /*
  972.  *------------------------------------------------------*
  973.  *
  974.  * TransformGetFileHandleProc --
  975.  *
  976.  * Called from Tcl_GetChannelHandle to retrieve
  977.  * OS specific file handle from inside this channel.
  978.  *
  979.  * Sideeffects:
  980.  * None.
  981.  *
  982.  * Result:
  983.  * The appropriate Tcl_File or NULL if not
  984.  * present. 
  985.  *
  986.  *------------------------------------------------------*
  987.  */
  988. static int
  989. TransformGetFileHandleProc (instanceData, direction, handlePtr)
  990.     ClientData  instanceData; /* Channel to query */
  991.     int         direction; /* Direction of interest */
  992.     ClientData* handlePtr; /* Place to store the handle into */
  993. {
  994.     /*
  995.      * Return the handle belonging to parent channel.
  996.      * IOW, pass the request down and the result up.
  997.      */
  998.     TransformChannelData* dataPtr = (TransformChannelData*) instanceData;
  999.     return Tcl_GetChannelHandle(Tcl_GetStackedChannel(dataPtr->self),
  1000.     direction, handlePtr);
  1001. }
  1002. /*
  1003.  *------------------------------------------------------*
  1004.  *
  1005.  * TransformNotifyProc --
  1006.  *
  1007.  * ------------------------------------------------*
  1008.  * Handler called by Tcl to inform us of activity
  1009.  * on the underlying channel.
  1010.  * ------------------------------------------------*
  1011.  *
  1012.  * Sideeffects:
  1013.  * May process the incoming event by itself.
  1014.  *
  1015.  * Result:
  1016.  * None.
  1017.  *
  1018.  *------------------------------------------------------*
  1019.  */
  1020. static int
  1021. TransformNotifyProc (clientData, mask)
  1022.     ClientData    clientData; /* The state of the notified transformation */
  1023.     int    mask;       /* The mask of occuring events */
  1024. {
  1025.     TransformChannelData* dataPtr = (TransformChannelData*) clientData;
  1026.     /*
  1027.      * An event occured in the underlying channel.  This
  1028.      * transformation doesn't process such events thus returns the
  1029.      * incoming mask unchanged.
  1030.      */
  1031.     if (dataPtr->timer != (Tcl_TimerToken) NULL) {
  1032. /*
  1033.  * Delete an existing timer. It was not fired, yet we are
  1034.  * here, so the channel below generated such an event and we
  1035.  * don't have to. The renewal of the interest after the
  1036.  * execution of channel handlers will eventually cause us to
  1037.  * recreate the timer (in TransformWatchProc).
  1038.  */
  1039. Tcl_DeleteTimerHandler (dataPtr->timer);
  1040. dataPtr->timer = (Tcl_TimerToken) NULL;
  1041.     }
  1042.     return mask;
  1043. }
  1044. /*
  1045.  *------------------------------------------------------*
  1046.  *
  1047.  * TransformChannelHandlerTimer --
  1048.  *
  1049.  * Called by the notifier (-> timer) to flush out
  1050.  * information waiting in the input buffer.
  1051.  *
  1052.  * Sideeffects:
  1053.  * As of 'Tcl_NotifyChannel'.
  1054.  *
  1055.  * Result:
  1056.  * None.
  1057.  *
  1058.  *------------------------------------------------------*
  1059.  */
  1060. static void
  1061. TransformChannelHandlerTimer (clientData)
  1062.     ClientData clientData; /* Transformation to query */
  1063. {
  1064.     TransformChannelData* dataPtr = (TransformChannelData*) clientData;
  1065.     dataPtr->timer = (Tcl_TimerToken) NULL;
  1066.     if (!(dataPtr->watchMask & TCL_READABLE) ||
  1067.     (ResultLength (&dataPtr->result) == 0)) {
  1068. /* The timer fired, but either is there no (more)
  1069.  * interest in the events it generates or nothing is available
  1070.  * for reading, so ignore it and don't recreate it.
  1071.  */
  1072. return;
  1073.     }
  1074.     Tcl_NotifyChannel(dataPtr->self, TCL_READABLE);
  1075. }
  1076. /*
  1077.  *------------------------------------------------------*
  1078.  *
  1079.  * ResultClear --
  1080.  *
  1081.  * Deallocates any memory allocated by 'ResultAdd'.
  1082.  *
  1083.  * Sideeffects:
  1084.  * See above.
  1085.  *
  1086.  * Result:
  1087.  * None.
  1088.  *
  1089.  *------------------------------------------------------*
  1090.  */
  1091. static void
  1092. ResultClear (r)
  1093.     ResultBuffer* r; /* Reference to the buffer to clear out */
  1094. {
  1095.     r->used = 0;
  1096.     if (r->allocated) {
  1097.         ckfree((char*) r->buf);
  1098. r->buf       = UCHARP (NULL);
  1099. r->allocated = 0;
  1100.     }
  1101. }
  1102. /*
  1103.  *------------------------------------------------------*
  1104.  *
  1105.  * ResultInit --
  1106.  *
  1107.  * Initializes the specified buffer structure. The
  1108.  * structure will contain valid information for an
  1109.  * emtpy buffer.
  1110.  *
  1111.  * Sideeffects:
  1112.  * See above.
  1113.  *
  1114.  * Result:
  1115.  * None.
  1116.  *
  1117.  *------------------------------------------------------*
  1118.  */
  1119. static void
  1120. ResultInit (r)
  1121.     ResultBuffer* r; /* Reference to the structure to initialize */
  1122. {
  1123.     r->used      = 0;
  1124.     r->allocated = 0;
  1125.     r->buf       = UCHARP (NULL);
  1126. }
  1127. /*
  1128.  *------------------------------------------------------*
  1129.  *
  1130.  * ResultLength --
  1131.  *
  1132.  * Returns the number of bytes stored in the buffer.
  1133.  *
  1134.  * Sideeffects:
  1135.  * None.
  1136.  *
  1137.  * Result:
  1138.  * An integer, see above too.
  1139.  *
  1140.  *------------------------------------------------------*
  1141.  */
  1142. static int
  1143. ResultLength (r)
  1144.     ResultBuffer* r; /* The structure to query */
  1145. {
  1146.     return r->used;
  1147. }
  1148. /*
  1149.  *------------------------------------------------------*
  1150.  *
  1151.  * ResultCopy --
  1152.  *
  1153.  * Copies the requested number of bytes from the
  1154.  * buffer into the specified array and removes them
  1155.  * from the buffer afterward. Copies less if there
  1156.  * is not enough data in the buffer.
  1157.  *
  1158.  * Sideeffects:
  1159.  * See above.
  1160.  *
  1161.  * Result:
  1162.  * The number of actually copied bytes,
  1163.  * possibly less than 'toRead'.
  1164.  *
  1165.  *------------------------------------------------------*
  1166.  */
  1167. static int
  1168. ResultCopy (r, buf, toRead)
  1169.     ResultBuffer*  r;      /* The buffer to read from */
  1170.     unsigned char* buf;    /* The buffer to copy into */
  1171.     int    toRead; /* Number of requested bytes */
  1172. {
  1173.     if (r->used == 0) {
  1174.         /* Nothing to copy in the case of an empty buffer.
  1175.  */
  1176.         return 0;
  1177.     }
  1178.     if (r->used == toRead) {
  1179.         /* We have just enough. Copy everything to the caller.
  1180.  */
  1181.         memcpy ((VOID*) buf, (VOID*) r->buf, (size_t) toRead);
  1182. r->used = 0;
  1183. return toRead;
  1184.     }
  1185.     if (r->used > toRead) {
  1186.         /* The internal buffer contains more than requested.
  1187.  * Copy the requested subset to the caller, and shift
  1188.  * the remaining bytes down.
  1189.  */
  1190.         memcpy  ((VOID*) buf,    (VOID*) r->buf,            (size_t) toRead);
  1191. memmove ((VOID*) r->buf, (VOID*) (r->buf + toRead),
  1192. (size_t) r->used - toRead);
  1193. r->used -= toRead;
  1194. return toRead;
  1195.     }
  1196.     /* There is not enough in the buffer to satisfy the caller, so
  1197.      * take everything.
  1198.      */
  1199.     memcpy((VOID*) buf, (VOID*) r->buf, (size_t) r->used);
  1200.     toRead  = r->used;
  1201.     r->used = 0;
  1202.     return toRead;
  1203. }
  1204. /*
  1205.  *------------------------------------------------------*
  1206.  *
  1207.  * ResultAdd --
  1208.  *
  1209.  * Adds the bytes in the specified array to the
  1210.  * buffer, by appending it.
  1211.  *
  1212.  * Sideeffects:
  1213.  * See above.
  1214.  *
  1215.  * Result:
  1216.  * None.
  1217.  *
  1218.  *------------------------------------------------------*
  1219.  */
  1220. static void
  1221. ResultAdd (r, buf, toWrite)
  1222.     ResultBuffer*  r;       /* The buffer to extend */
  1223.     unsigned char* buf;     /* The buffer to read from */
  1224.     int    toWrite; /* The number of bytes in 'buf' */
  1225. {
  1226.     if ((r->used + toWrite) > r->allocated) {
  1227.         /* Extension of the internal buffer is required.
  1228.  */
  1229.         if (r->allocated == 0) {
  1230.     r->allocated = toWrite + INCREMENT;
  1231.     r->buf       = UCHARP (ckalloc((unsigned) r->allocated));
  1232. } else {
  1233.     r->allocated += toWrite + INCREMENT;
  1234.     r->buf        = UCHARP (ckrealloc((char*) r->buf,
  1235.     (unsigned) r->allocated));
  1236. }
  1237.     }
  1238.     /* now copy data */
  1239.     memcpy(r->buf + r->used, buf, (size_t) toWrite);
  1240.     r->used += toWrite;
  1241. }