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