os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclIOGT.c
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclIOGT.c	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,1453 @@
     1.4 +/*
     1.5 + * tclIOGT.c --
     1.6 + *
     1.7 + *	Implements a generic transformation exposing the underlying API
     1.8 + *	at the script level.  Contributed by Andreas Kupries.
     1.9 + *
    1.10 + * Copyright (c) 2000 Ajuba Solutions
    1.11 + * Copyright (c) 1999-2000 Andreas Kupries (a.kupries@westend.com)
    1.12 + *
    1.13 + * See the file "license.terms" for information on usage and redistribution
    1.14 + * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    1.15 + *
    1.16 + * CVS: $Id: tclIOGT.c,v 1.7.2.2 2006/08/30 17:24:07 hobbs Exp $
    1.17 + */
    1.18 +
    1.19 +#include "tclInt.h"
    1.20 +#include "tclPort.h"
    1.21 +#include "tclIO.h"
    1.22 +
    1.23 +
    1.24 +/*
    1.25 + * Forward declarations of internal procedures.
    1.26 + * First the driver procedures of the transformation.
    1.27 + */
    1.28 +
    1.29 +static int		TransformBlockModeProc _ANSI_ARGS_ ((
    1.30 +				ClientData instanceData, int mode));
    1.31 +static int		TransformCloseProc _ANSI_ARGS_ ((
    1.32 +				ClientData instanceData, Tcl_Interp* interp));
    1.33 +static int		TransformInputProc _ANSI_ARGS_ ((
    1.34 +				ClientData instanceData,
    1.35 +				char* buf, int toRead, int* errorCodePtr));
    1.36 +static int		TransformOutputProc _ANSI_ARGS_ ((
    1.37 +				ClientData instanceData, CONST char *buf,
    1.38 +				int toWrite, int* errorCodePtr));
    1.39 +static int		TransformSeekProc _ANSI_ARGS_ ((
    1.40 +				ClientData instanceData, long offset,
    1.41 +				int mode, int* errorCodePtr));
    1.42 +static int		TransformSetOptionProc _ANSI_ARGS_((
    1.43 +				ClientData instanceData, Tcl_Interp *interp,
    1.44 +				CONST char *optionName, CONST char *value));
    1.45 +static int		TransformGetOptionProc _ANSI_ARGS_((
    1.46 +				ClientData instanceData, Tcl_Interp *interp,
    1.47 +				CONST char *optionName, Tcl_DString *dsPtr));
    1.48 +static void		TransformWatchProc _ANSI_ARGS_ ((
    1.49 +				ClientData instanceData, int mask));
    1.50 +static int		TransformGetFileHandleProc _ANSI_ARGS_ ((
    1.51 +				ClientData instanceData, int direction,
    1.52 +				ClientData* handlePtr));
    1.53 +static int		TransformNotifyProc _ANSI_ARGS_ ((
    1.54 +				ClientData instanceData, int mask));
    1.55 +static Tcl_WideInt	TransformWideSeekProc _ANSI_ARGS_ ((
    1.56 +				ClientData instanceData, Tcl_WideInt offset,
    1.57 +				int mode, int* errorCodePtr));
    1.58 +
    1.59 +/*
    1.60 + * Forward declarations of internal procedures.
    1.61 + * Secondly the procedures for handling and generating fileeevents.
    1.62 + */
    1.63 +
    1.64 +static void		TransformChannelHandlerTimer _ANSI_ARGS_ ((
    1.65 +				ClientData clientData));
    1.66 +
    1.67 +/*
    1.68 + * Forward declarations of internal procedures.
    1.69 + * Third, helper procedures encapsulating essential tasks.
    1.70 + */
    1.71 +
    1.72 +typedef struct TransformChannelData TransformChannelData;
    1.73 +
    1.74 +static int		ExecuteCallback _ANSI_ARGS_ ((
    1.75 +				TransformChannelData* ctrl, Tcl_Interp* interp,
    1.76 +				unsigned char* op, unsigned char* buf,
    1.77 +				int bufLen, int transmit, int preserve));
    1.78 +
    1.79 +/*
    1.80 + * Action codes to give to 'ExecuteCallback' (argument 'transmit')
    1.81 + * confering to the procedure what to do with the result of the script
    1.82 + * it calls.
    1.83 + */
    1.84 +
    1.85 +#define TRANSMIT_DONT  (0) /* No transfer to do */
    1.86 +#define TRANSMIT_DOWN  (1) /* Transfer to the underlying channel */
    1.87 +#define TRANSMIT_SELF  (2) /* Transfer into our channel. */
    1.88 +#define TRANSMIT_IBUF  (3) /* Transfer to internal input buffer */
    1.89 +#define TRANSMIT_NUM   (4) /* Transfer number to 'maxRead' */
    1.90 +
    1.91 +/*
    1.92 + * Codes for 'preserve' of 'ExecuteCallback'
    1.93 + */
    1.94 +
    1.95 +#define P_PRESERVE    (1)
    1.96 +#define P_NO_PRESERVE (0)
    1.97 +
    1.98 +/*
    1.99 + * Strings for the action codes delivered to the script implementing
   1.100 + * a transformation. Argument 'op' of 'ExecuteCallback'.
   1.101 + */
   1.102 +
   1.103 +#define A_CREATE_WRITE	(UCHARP ("create/write"))
   1.104 +#define A_DELETE_WRITE	(UCHARP ("delete/write"))
   1.105 +#define A_FLUSH_WRITE	(UCHARP ("flush/write"))
   1.106 +#define A_WRITE		(UCHARP ("write"))
   1.107 +
   1.108 +#define A_CREATE_READ	(UCHARP ("create/read"))
   1.109 +#define A_DELETE_READ	(UCHARP ("delete/read"))
   1.110 +#define A_FLUSH_READ	(UCHARP ("flush/read"))
   1.111 +#define A_READ		(UCHARP ("read"))
   1.112 +
   1.113 +#define A_QUERY_MAXREAD (UCHARP ("query/maxRead"))
   1.114 +#define A_CLEAR_READ	(UCHARP ("clear/read"))
   1.115 +
   1.116 +/*
   1.117 + * Management of a simple buffer.
   1.118 + */
   1.119 +
   1.120 +typedef struct ResultBuffer ResultBuffer;
   1.121 +
   1.122 +static void		ResultClear  _ANSI_ARGS_ ((ResultBuffer* r));
   1.123 +static void		ResultInit   _ANSI_ARGS_ ((ResultBuffer* r));
   1.124 +static int		ResultLength _ANSI_ARGS_ ((ResultBuffer* r));
   1.125 +static int		ResultCopy   _ANSI_ARGS_ ((ResultBuffer* r,
   1.126 +				unsigned char* buf, int toRead));
   1.127 +static void		ResultAdd    _ANSI_ARGS_ ((ResultBuffer* r,
   1.128 +				unsigned char* buf, int toWrite));
   1.129 +
   1.130 +/*
   1.131 + * This structure describes the channel type structure for tcl based
   1.132 + * transformations.
   1.133 + */
   1.134 +
   1.135 +static Tcl_ChannelType transformChannelType = {
   1.136 +    "transform",			/* Type name. */
   1.137 +    TCL_CHANNEL_VERSION_3,
   1.138 +    TransformCloseProc,			/* Close proc. */
   1.139 +    TransformInputProc,			/* Input proc. */
   1.140 +    TransformOutputProc,		/* Output proc. */
   1.141 +    TransformSeekProc,			/* Seek proc. */
   1.142 +    TransformSetOptionProc,		/* Set option proc. */
   1.143 +    TransformGetOptionProc,		/* Get option proc. */
   1.144 +    TransformWatchProc,			/* Initialize notifier. */
   1.145 +    TransformGetFileHandleProc,		/* Get OS handles out of channel. */
   1.146 +    NULL,				/* close2proc */
   1.147 +    TransformBlockModeProc,		/* Set blocking/nonblocking mode.*/
   1.148 +    NULL,				/* Flush proc. */
   1.149 +    TransformNotifyProc,                /* Handling of events bubbling up */
   1.150 +    TransformWideSeekProc,		/* Wide seek proc */
   1.151 +};
   1.152 +
   1.153 +/*
   1.154 + * Possible values for 'flags' field in control structure, see below.
   1.155 + */
   1.156 +
   1.157 +#define CHANNEL_ASYNC		(1<<0) /* non-blocking mode */
   1.158 +
   1.159 +/*
   1.160 + * Definition of the structure containing the information about the
   1.161 + * internal input buffer.
   1.162 + */
   1.163 +
   1.164 +struct ResultBuffer {
   1.165 +    unsigned char* buf;       /* Reference to the buffer area */
   1.166 +    int		   allocated; /* Allocated size of the buffer area */
   1.167 +    int		   used;      /* Number of bytes in the buffer, <= allocated */
   1.168 +};
   1.169 +
   1.170 +/*
   1.171 + * Additional bytes to allocate during buffer expansion
   1.172 + */
   1.173 +
   1.174 +#define INCREMENT (512)
   1.175 +
   1.176 +/*
   1.177 + * Number of milliseconds to wait before firing an event to flush
   1.178 + * out information waiting in buffers (fileevent support).
   1.179 + */
   1.180 +
   1.181 +#define FLUSH_DELAY (5)
   1.182 +
   1.183 +/*
   1.184 + * Convenience macro to make some casts easier to use.
   1.185 + */
   1.186 +
   1.187 +#define UCHARP(x) ((unsigned char*) (x))
   1.188 +#define NO_INTERP ((Tcl_Interp*) NULL)
   1.189 +
   1.190 +/*
   1.191 + * Definition of a structure used by all transformations generated here to
   1.192 + * maintain their local state.
   1.193 + */
   1.194 +
   1.195 +struct TransformChannelData {
   1.196 +
   1.197 +    /*
   1.198 +     * General section. Data to integrate the transformation into the channel
   1.199 +     * system.
   1.200 +     */
   1.201 +
   1.202 +    Tcl_Channel self;     /* Our own Channel handle */
   1.203 +    int readIsFlushed;    /* Flag to note wether in.flushProc was called or not
   1.204 +			   */
   1.205 +    int flags;            /* Currently CHANNEL_ASYNC or zero */
   1.206 +    int watchMask;        /* Current watch/event/interest mask */
   1.207 +    int mode;             /* mode of parent channel, OR'ed combination of
   1.208 +			   * TCL_READABLE, TCL_WRITABLE */
   1.209 +    Tcl_TimerToken timer; /* Timer for automatic flushing of information
   1.210 +			   * sitting in an internal buffer. Required for full
   1.211 +			   * fileevent support */
   1.212 +    /*
   1.213 +     * Transformation specific data.
   1.214 +     */
   1.215 +
   1.216 +    int maxRead;            /* Maximum allowed number of bytes to read, as
   1.217 +			     * given to us by the tcl script implementing the
   1.218 +			     * transformation. */
   1.219 +    Tcl_Interp*    interp;  /* Reference to the interpreter which created the
   1.220 +			     * transformation. Used to execute the code
   1.221 +			     * below. */
   1.222 +    Tcl_Obj*       command; /* Tcl code to execute for a buffer */
   1.223 +    ResultBuffer   result;  /* Internal buffer used to store the result of a
   1.224 +			     * transformation of incoming data. Additionally
   1.225 +			     * serves as buffer of all data not yet consumed by
   1.226 +			     * the reader. */
   1.227 +};
   1.228 +
   1.229 +
   1.230 +/*
   1.231 + *----------------------------------------------------------------------
   1.232 + *
   1.233 + * TclChannelTransform --
   1.234 + *
   1.235 + *	Implements the Tcl "testchannel transform" debugging command.
   1.236 + *	This is part of the testing environment.  This sets up a tcl
   1.237 + *	script (cmdObjPtr) to be used as a transform on the channel.
   1.238 + *
   1.239 + * Results:
   1.240 + *	A standard Tcl result.
   1.241 + *
   1.242 + * Side effects:
   1.243 + *	None.
   1.244 + *
   1.245 + *----------------------------------------------------------------------
   1.246 + */
   1.247 +
   1.248 +	/* ARGSUSED */
   1.249 +int
   1.250 +TclChannelTransform(interp, chan, cmdObjPtr)
   1.251 +    Tcl_Interp	*interp;	/* Interpreter for result. */
   1.252 +    Tcl_Channel chan;		/* Channel to transform. */
   1.253 +    Tcl_Obj	*cmdObjPtr;	/* Script to use for transform. */
   1.254 +{
   1.255 +    Channel			*chanPtr;	/* The actual channel. */
   1.256 +    ChannelState		*statePtr;	/* state info for channel */
   1.257 +    int				mode;		/* rw mode of the channel */
   1.258 +    TransformChannelData	*dataPtr;
   1.259 +    int				res;
   1.260 +    Tcl_DString			ds;
   1.261 +
   1.262 +    if (chan == (Tcl_Channel) NULL) {
   1.263 +	return TCL_ERROR;
   1.264 +    }
   1.265 +    chanPtr	= (Channel *) chan;
   1.266 +    statePtr	= chanPtr->state;
   1.267 +    chanPtr	= statePtr->topChanPtr;
   1.268 +    chan	= (Tcl_Channel) chanPtr;
   1.269 +    mode	= (statePtr->flags & (TCL_READABLE|TCL_WRITABLE));
   1.270 +
   1.271 +    /*
   1.272 +     * Now initialize the transformation state and stack it upon the
   1.273 +     * specified channel. One of the necessary things to do is to
   1.274 +     * retrieve the blocking regime of the underlying channel and to
   1.275 +     * use the same for us too.
   1.276 +     */
   1.277 +
   1.278 +    dataPtr = (TransformChannelData*) ckalloc(sizeof(TransformChannelData));
   1.279 +
   1.280 +    Tcl_DStringInit (&ds);
   1.281 +    Tcl_GetChannelOption(interp, chan, "-blocking", &ds);
   1.282 +
   1.283 +    dataPtr->readIsFlushed = 0;
   1.284 +    dataPtr->flags	= 0;
   1.285 +
   1.286 +    if (ds.string[0] == '0') {
   1.287 +	dataPtr->flags |= CHANNEL_ASYNC;
   1.288 +    }
   1.289 +
   1.290 +    Tcl_DStringFree (&ds);
   1.291 +
   1.292 +    dataPtr->self	= chan;
   1.293 +    dataPtr->watchMask	= 0;
   1.294 +    dataPtr->mode	= mode;
   1.295 +    dataPtr->timer	= (Tcl_TimerToken) NULL;
   1.296 +    dataPtr->maxRead	= 4096; /* Initial value not relevant */
   1.297 +    dataPtr->interp	= interp;
   1.298 +    dataPtr->command	= cmdObjPtr;
   1.299 +
   1.300 +    Tcl_IncrRefCount(dataPtr->command);
   1.301 +
   1.302 +    ResultInit(&dataPtr->result);
   1.303 +
   1.304 +    dataPtr->self = Tcl_StackChannel(interp, &transformChannelType,
   1.305 +	    (ClientData) dataPtr, mode, chan);
   1.306 +    if (dataPtr->self == (Tcl_Channel) NULL) {
   1.307 +	Tcl_AppendResult(interp, "\nfailed to stack channel \"",
   1.308 +		Tcl_GetChannelName(chan), "\"", (char *) NULL);
   1.309 +
   1.310 +	Tcl_DecrRefCount(dataPtr->command);
   1.311 +	ResultClear(&dataPtr->result);
   1.312 +	ckfree((VOID *) dataPtr);
   1.313 +	return TCL_ERROR;
   1.314 +    }
   1.315 +
   1.316 +    /*
   1.317 +     * At last initialize the transformation at the script level.
   1.318 +     */
   1.319 +
   1.320 +    if (dataPtr->mode & TCL_WRITABLE) {
   1.321 +	res = ExecuteCallback (dataPtr, NO_INTERP, A_CREATE_WRITE,
   1.322 +		NULL, 0, TRANSMIT_DONT, P_NO_PRESERVE);
   1.323 +
   1.324 +	if (res != TCL_OK) {
   1.325 +	    Tcl_UnstackChannel(interp, chan);
   1.326 +	    return TCL_ERROR;
   1.327 +	}
   1.328 +    }
   1.329 +
   1.330 +    if (dataPtr->mode & TCL_READABLE) {
   1.331 +	res = ExecuteCallback (dataPtr, NO_INTERP, A_CREATE_READ,
   1.332 +		NULL, 0, TRANSMIT_DONT, P_NO_PRESERVE);
   1.333 +
   1.334 +	if (res != TCL_OK) {
   1.335 +	    ExecuteCallback (dataPtr, NO_INTERP, A_DELETE_WRITE,
   1.336 +		    NULL, 0, TRANSMIT_DONT, P_NO_PRESERVE);
   1.337 +
   1.338 +	    Tcl_UnstackChannel(interp, chan);
   1.339 +	    return TCL_ERROR;
   1.340 +	}
   1.341 +    }
   1.342 +
   1.343 +    return TCL_OK;
   1.344 +}
   1.345 +
   1.346 +/*
   1.347 + *------------------------------------------------------*
   1.348 + *
   1.349 + *	ExecuteCallback --
   1.350 + *
   1.351 + *	Executes the defined callback for buffer and
   1.352 + *	operation.
   1.353 + *
   1.354 + *	Sideeffects:
   1.355 + *		As of the executed tcl script.
   1.356 + *
   1.357 + *	Result:
   1.358 + *		A standard TCL error code. In case of an
   1.359 + *		error a message is left in the result area
   1.360 + *		of the specified interpreter.
   1.361 + *
   1.362 + *------------------------------------------------------*
   1.363 + */
   1.364 +
   1.365 +static int
   1.366 +ExecuteCallback (dataPtr, interp, op, buf, bufLen, transmit, preserve)
   1.367 +    TransformChannelData* dataPtr;  /* Transformation with the callback */
   1.368 +    Tcl_Interp*           interp;   /* Current interpreter, possibly NULL */
   1.369 +    unsigned char*        op;       /* Operation invoking the callback */
   1.370 +    unsigned char*        buf;      /* Buffer to give to the script. */
   1.371 +    int			  bufLen;   /* Ands its length */
   1.372 +    int                   transmit; /* Flag, determines whether the result
   1.373 +				     * of the callback is sent to the
   1.374 +				     * underlying channel or not. */
   1.375 +    int                   preserve; /* Flag. If true the procedure will
   1.376 +				     * preserver the result state of all
   1.377 +				     * accessed interpreters. */
   1.378 +{
   1.379 +    /*
   1.380 +     * Step 1, create the complete command to execute. Do this by appending
   1.381 +     * operation and buffer to operate upon to a copy of the callback
   1.382 +     * definition. We *cannot* create a list containing 3 objects and then use
   1.383 +     * 'Tcl_EvalObjv', because the command may contain additional prefixed
   1.384 +     * arguments. Feather's curried commands would come in handy here.
   1.385 +     */
   1.386 +
   1.387 +    Tcl_Obj* resObj;		    /* See below, switch (transmit) */
   1.388 +    int resLen;
   1.389 +    unsigned char* resBuf;
   1.390 +    Tcl_SavedResult ciSave;
   1.391 +    int res = TCL_OK;
   1.392 +    Tcl_Obj* command = Tcl_DuplicateObj (dataPtr->command);
   1.393 +    Tcl_Obj* temp;
   1.394 +
   1.395 +    if (preserve) {
   1.396 +	Tcl_SaveResult (dataPtr->interp, &ciSave);
   1.397 +    }
   1.398 +
   1.399 +    if (command == (Tcl_Obj*) NULL) {
   1.400 +        /* Memory allocation problem */
   1.401 +        res = TCL_ERROR;
   1.402 +        goto cleanup;
   1.403 +    }
   1.404 +
   1.405 +    Tcl_IncrRefCount(command);
   1.406 +
   1.407 +    temp = Tcl_NewStringObj((char*) op, -1);
   1.408 +
   1.409 +    if (temp == (Tcl_Obj*) NULL) {
   1.410 +        /* Memory allocation problem */
   1.411 +        res = TCL_ERROR;
   1.412 +        goto cleanup;
   1.413 +    }
   1.414 +
   1.415 +    res = Tcl_ListObjAppendElement(dataPtr->interp, command, temp);
   1.416 +
   1.417 +    if (res != TCL_OK)
   1.418 +	goto cleanup;
   1.419 +
   1.420 +    /*
   1.421 +     * Use a byte-array to prevent the misinterpretation of binary data
   1.422 +     * coming through as UTF while at the tcl level.
   1.423 +     */
   1.424 +
   1.425 +    temp = Tcl_NewByteArrayObj(buf, bufLen);
   1.426 +
   1.427 +    if (temp == (Tcl_Obj*) NULL) {
   1.428 +        /* Memory allocation problem */
   1.429 +	res = TCL_ERROR;
   1.430 +        goto cleanup;
   1.431 +    }
   1.432 +
   1.433 +    res = Tcl_ListObjAppendElement (dataPtr->interp, command, temp);
   1.434 +
   1.435 +    if (res != TCL_OK)
   1.436 +        goto cleanup;
   1.437 +
   1.438 +    /*
   1.439 +     * Step 2, execute the command at the global level of the interpreter
   1.440 +     * used to create the transformation. Destroy the command afterward.
   1.441 +     * If an error occured and the current interpreter is defined and not
   1.442 +     * equal to the interpreter for the callback, then copy the error
   1.443 +     * message into current interpreter. Don't copy if in preservation mode.
   1.444 +     */
   1.445 +
   1.446 +    res = Tcl_EvalObjEx(dataPtr->interp, command, TCL_EVAL_GLOBAL);
   1.447 +    Tcl_DecrRefCount (command);
   1.448 +    command = (Tcl_Obj*) NULL;
   1.449 +
   1.450 +    if ((res != TCL_OK) && (interp != NO_INTERP) &&
   1.451 +	    (dataPtr->interp != interp) && !preserve) {
   1.452 +        Tcl_SetObjResult(interp, Tcl_GetObjResult(dataPtr->interp));
   1.453 +	return res;
   1.454 +    }
   1.455 +
   1.456 +    /*
   1.457 +     * Step 3, transmit a possible conversion result to the underlying
   1.458 +     * channel, or ourselves.
   1.459 +     */
   1.460 +
   1.461 +    switch (transmit) {
   1.462 +	case TRANSMIT_DONT:
   1.463 +	    /* nothing to do */
   1.464 +	    break;
   1.465 +
   1.466 +	case TRANSMIT_DOWN:
   1.467 +	    resObj = Tcl_GetObjResult(dataPtr->interp);
   1.468 +	    resBuf = (unsigned char*) Tcl_GetByteArrayFromObj(resObj, &resLen);
   1.469 +	    Tcl_WriteRaw(Tcl_GetStackedChannel(dataPtr->self),
   1.470 +		    (char*) resBuf, resLen);
   1.471 +	    break;
   1.472 +
   1.473 +	case TRANSMIT_SELF:
   1.474 +	    resObj = Tcl_GetObjResult (dataPtr->interp);
   1.475 +	    resBuf = (unsigned char*) Tcl_GetByteArrayFromObj(resObj, &resLen);
   1.476 +	    Tcl_WriteRaw(dataPtr->self, (char*) resBuf, resLen);
   1.477 +	    break;
   1.478 +
   1.479 +	case TRANSMIT_IBUF:
   1.480 +	    resObj = Tcl_GetObjResult (dataPtr->interp);
   1.481 +	    resBuf = (unsigned char*) Tcl_GetByteArrayFromObj(resObj, &resLen);
   1.482 +	    ResultAdd(&dataPtr->result, resBuf, resLen);
   1.483 +	    break;
   1.484 +
   1.485 +	case TRANSMIT_NUM:
   1.486 +	    /* Interpret result as integer number */
   1.487 +	    resObj = Tcl_GetObjResult (dataPtr->interp);
   1.488 +	    Tcl_GetIntFromObj(dataPtr->interp, resObj, &dataPtr->maxRead);
   1.489 +	    break;
   1.490 +    }
   1.491 +
   1.492 +    Tcl_ResetResult(dataPtr->interp);
   1.493 +
   1.494 +    if (preserve) {
   1.495 +	Tcl_RestoreResult(dataPtr->interp, &ciSave);
   1.496 +    }
   1.497 +
   1.498 +    return res;
   1.499 +
   1.500 +    cleanup:
   1.501 +    if (preserve) {
   1.502 +	Tcl_RestoreResult(dataPtr->interp, &ciSave);
   1.503 +    }
   1.504 +
   1.505 +    if (command != (Tcl_Obj*) NULL) {
   1.506 +        Tcl_DecrRefCount(command);
   1.507 +    }
   1.508 +
   1.509 +    return res;
   1.510 +}
   1.511 +
   1.512 +/*
   1.513 + *------------------------------------------------------*
   1.514 + *
   1.515 + *	TransformBlockModeProc --
   1.516 + *
   1.517 + *	Trap handler. Called by the generic IO system
   1.518 + *	during option processing to change the blocking
   1.519 + *	mode of the channel.
   1.520 + *
   1.521 + *	Sideeffects:
   1.522 + *		Forwards the request to the underlying
   1.523 + *		channel.
   1.524 + *
   1.525 + *	Result:
   1.526 + *		0 if successful, errno when failed.
   1.527 + *
   1.528 + *------------------------------------------------------*
   1.529 + */
   1.530 +
   1.531 +static int
   1.532 +TransformBlockModeProc (instanceData, mode)
   1.533 +    ClientData  instanceData; /* State of transformation */
   1.534 +    int         mode;         /* New blocking mode */
   1.535 +{
   1.536 +    TransformChannelData* dataPtr = (TransformChannelData*) instanceData;
   1.537 +
   1.538 +    if (mode == TCL_MODE_NONBLOCKING) {
   1.539 +        dataPtr->flags |= CHANNEL_ASYNC;
   1.540 +    } else {
   1.541 +        dataPtr->flags &= ~(CHANNEL_ASYNC);
   1.542 +    }
   1.543 +    return 0;
   1.544 +}
   1.545 +
   1.546 +/*
   1.547 + *------------------------------------------------------*
   1.548 + *
   1.549 + *	TransformCloseProc --
   1.550 + *
   1.551 + *	Trap handler. Called by the generic IO system
   1.552 + *	during destruction of the transformation channel.
   1.553 + *
   1.554 + *	Sideeffects:
   1.555 + *		Releases the memory allocated in
   1.556 + *		'Tcl_TransformObjCmd'.
   1.557 + *
   1.558 + *	Result:
   1.559 + *		None.
   1.560 + *
   1.561 + *------------------------------------------------------*
   1.562 + */
   1.563 +
   1.564 +static int
   1.565 +TransformCloseProc (instanceData, interp)
   1.566 +    ClientData  instanceData;
   1.567 +    Tcl_Interp* interp;
   1.568 +{
   1.569 +    TransformChannelData* dataPtr = (TransformChannelData*) instanceData;
   1.570 +
   1.571 +    /*
   1.572 +     * Important: In this procedure 'dataPtr->self' already points to
   1.573 +     * the underlying channel.
   1.574 +     */
   1.575 +
   1.576 +    /*
   1.577 +     * There is no need to cancel an existing channel handler, this is already
   1.578 +     * done. Either by 'Tcl_UnstackChannel' or by the general cleanup in
   1.579 +     * 'Tcl_Close'.
   1.580 +     *
   1.581 +     * But we have to cancel an active timer to prevent it from firing on the
   1.582 +     * removed channel.
   1.583 +     */
   1.584 +
   1.585 +    if (dataPtr->timer != (Tcl_TimerToken) NULL) {
   1.586 +        Tcl_DeleteTimerHandler (dataPtr->timer);
   1.587 +	dataPtr->timer = (Tcl_TimerToken) NULL;
   1.588 +    }
   1.589 +
   1.590 +    /*
   1.591 +     * Now flush data waiting in internal buffers to output and input. The
   1.592 +     * input must be done despite the fact that there is no real receiver
   1.593 +     * for it anymore. But the scripts might have sideeffects other parts
   1.594 +     * of the system rely on (f.e. signaling the close to interested parties).
   1.595 +     */
   1.596 +
   1.597 +    if (dataPtr->mode & TCL_WRITABLE) {
   1.598 +        ExecuteCallback (dataPtr, interp, A_FLUSH_WRITE,
   1.599 +		NULL, 0, TRANSMIT_DOWN, 1);
   1.600 +    }
   1.601 +
   1.602 +    if ((dataPtr->mode & TCL_READABLE) && !dataPtr->readIsFlushed) {
   1.603 +	dataPtr->readIsFlushed = 1;
   1.604 +        ExecuteCallback (dataPtr, interp, A_FLUSH_READ,
   1.605 +		NULL, 0, TRANSMIT_IBUF, 1);
   1.606 +    }
   1.607 +
   1.608 +    if (dataPtr->mode & TCL_WRITABLE) {
   1.609 +        ExecuteCallback (dataPtr, interp, A_DELETE_WRITE,
   1.610 +		NULL, 0, TRANSMIT_DONT, 1);
   1.611 +    }
   1.612 +
   1.613 +    if (dataPtr->mode & TCL_READABLE) {
   1.614 +        ExecuteCallback (dataPtr, interp, A_DELETE_READ,
   1.615 +		NULL, 0, TRANSMIT_DONT, 1);
   1.616 +    }
   1.617 +
   1.618 +    /*
   1.619 +     * General cleanup
   1.620 +     */
   1.621 +
   1.622 +    ResultClear(&dataPtr->result);
   1.623 +    Tcl_DecrRefCount(dataPtr->command);
   1.624 +    ckfree((VOID*) dataPtr);
   1.625 +
   1.626 +    return TCL_OK;
   1.627 +}
   1.628 +
   1.629 +/*
   1.630 + *------------------------------------------------------*
   1.631 + *
   1.632 + *	TransformInputProc --
   1.633 + *
   1.634 + *	Called by the generic IO system to convert read data.
   1.635 + *
   1.636 + *	Sideeffects:
   1.637 + *		As defined by the conversion.
   1.638 + *
   1.639 + *	Result:
   1.640 + *		A transformed buffer.
   1.641 + *
   1.642 + *------------------------------------------------------*
   1.643 + */
   1.644 +
   1.645 +static int
   1.646 +TransformInputProc (instanceData, buf, toRead, errorCodePtr)
   1.647 +    ClientData instanceData;
   1.648 +    char*      buf;
   1.649 +    int	       toRead;
   1.650 +    int*       errorCodePtr;
   1.651 +{
   1.652 +    TransformChannelData* dataPtr = (TransformChannelData*) instanceData;
   1.653 +    int gotBytes, read, res, copied;
   1.654 +    Tcl_Channel downChan;
   1.655 +
   1.656 +    /* should assert (dataPtr->mode & TCL_READABLE) */
   1.657 +
   1.658 +    if (toRead == 0) {
   1.659 +	/* Catch a no-op.
   1.660 +	 */
   1.661 +	return 0;
   1.662 +    }
   1.663 +
   1.664 +    gotBytes = 0;
   1.665 +    downChan = Tcl_GetStackedChannel(dataPtr->self);
   1.666 +
   1.667 +    while (toRead > 0) {
   1.668 +        /*
   1.669 +	 * Loop until the request is satisfied (or no data is available from
   1.670 +	 * below, possibly EOF).
   1.671 +	 */
   1.672 +
   1.673 +        copied    = ResultCopy (&dataPtr->result, UCHARP (buf), toRead);
   1.674 +
   1.675 +	toRead   -= copied;
   1.676 +	buf      += copied;
   1.677 +	gotBytes += copied;
   1.678 +
   1.679 +	if (toRead == 0) {
   1.680 +	    /* The request was completely satisfied from our buffers.
   1.681 +	     * We can break out of the loop and return to the caller.
   1.682 +	     */
   1.683 +	    return gotBytes;
   1.684 +	}
   1.685 +
   1.686 +	/*
   1.687 +	 * Length (dataPtr->result) == 0, toRead > 0 here . Use the incoming
   1.688 +	 * 'buf'! as target to store the intermediary information read
   1.689 +	 * from the underlying channel.
   1.690 +	 *
   1.691 +	 * Ask the tcl level how much data it allows us to read from
   1.692 +	 * the underlying channel. This feature allows the transform to
   1.693 +	 * signal EOF upstream although there is none downstream. Useful
   1.694 +	 * to control an unbounded 'fcopy', either through counting bytes,
   1.695 +	 * or by pattern matching.
   1.696 +	 */
   1.697 +
   1.698 +	ExecuteCallback (dataPtr, NO_INTERP, A_QUERY_MAXREAD,
   1.699 +		NULL, 0, TRANSMIT_NUM /* -> maxRead */, 1);
   1.700 +
   1.701 +	if (dataPtr->maxRead >= 0) {
   1.702 +	    if (dataPtr->maxRead < toRead) {
   1.703 +	        toRead = dataPtr->maxRead;
   1.704 +	    }
   1.705 +	} /* else: 'maxRead < 0' == Accept the current value of toRead */
   1.706 +
   1.707 +	if (toRead <= 0) {
   1.708 +	    return gotBytes;
   1.709 +	}
   1.710 +
   1.711 +	read = Tcl_ReadRaw(downChan, buf, toRead);
   1.712 +
   1.713 +	if (read < 0) {
   1.714 +	    /* Report errors to caller. EAGAIN is a special situation.
   1.715 +	     * If we had some data before we report that instead of the
   1.716 +	     * request to re-try.
   1.717 +	     */
   1.718 +
   1.719 +	    if ((Tcl_GetErrno() == EAGAIN) && (gotBytes > 0)) {
   1.720 +	        return gotBytes;
   1.721 +	    }
   1.722 +
   1.723 +	    *errorCodePtr = Tcl_GetErrno();
   1.724 +	    return -1;      
   1.725 +	}
   1.726 +
   1.727 +	if (read == 0) {
   1.728 +	    /*
   1.729 +	     * Check wether we hit on EOF in the underlying channel or
   1.730 +	     * not. If not differentiate between blocking and
   1.731 +	     * non-blocking modes. In non-blocking mode we ran
   1.732 +	     * temporarily out of data. Signal this to the caller via
   1.733 +	     * EWOULDBLOCK and error return (-1). In the other cases
   1.734 +	     * we simply return what we got and let the caller wait
   1.735 +	     * for more. On the other hand, if we got an EOF we have
   1.736 +	     * to convert and flush all waiting partial data.
   1.737 +	     */
   1.738 +
   1.739 +	    if (! Tcl_Eof (downChan)) {
   1.740 +	        if ((gotBytes == 0) && (dataPtr->flags & CHANNEL_ASYNC)) {
   1.741 +		    *errorCodePtr = EWOULDBLOCK;
   1.742 +		    return -1;
   1.743 +		} else {
   1.744 +		    return gotBytes;
   1.745 +		}
   1.746 +	    } else {
   1.747 +	        if (dataPtr->readIsFlushed) {
   1.748 +		    /* Already flushed, nothing to do anymore
   1.749 +		     */
   1.750 +		    return gotBytes;
   1.751 +		}
   1.752 +
   1.753 +		dataPtr->readIsFlushed = 1;
   1.754 +
   1.755 +		ExecuteCallback (dataPtr, NO_INTERP, A_FLUSH_READ,
   1.756 +			NULL, 0, TRANSMIT_IBUF, P_PRESERVE);
   1.757 +
   1.758 +		if (ResultLength (&dataPtr->result) == 0) {
   1.759 +		    /* we had nothing to flush */
   1.760 +		    return gotBytes;
   1.761 +		}
   1.762 +
   1.763 +		continue; /* at: while (toRead > 0) */
   1.764 +	    }
   1.765 +	} /* read == 0 */
   1.766 +
   1.767 +	/* Transform the read chunk and add the result to our
   1.768 +	 * read buffer (dataPtr->result)
   1.769 +	 */
   1.770 +
   1.771 +	res = ExecuteCallback (dataPtr, NO_INTERP, A_READ,
   1.772 +		UCHARP (buf), read, TRANSMIT_IBUF, P_PRESERVE);
   1.773 +
   1.774 +	if (res != TCL_OK) {
   1.775 +	    *errorCodePtr = EINVAL;
   1.776 +	    return -1;
   1.777 +	}
   1.778 +    } /* while toRead > 0 */
   1.779 +
   1.780 +    return gotBytes;
   1.781 +}
   1.782 +
   1.783 +/*
   1.784 + *------------------------------------------------------*
   1.785 + *
   1.786 + *	TransformOutputProc --
   1.787 + *
   1.788 + *	Called by the generic IO system to convert data
   1.789 + *	waiting to be written.
   1.790 + *
   1.791 + *	Sideeffects:
   1.792 + *		As defined by the transformation.
   1.793 + *
   1.794 + *	Result:
   1.795 + *		A transformed buffer.
   1.796 + *
   1.797 + *------------------------------------------------------*
   1.798 + */
   1.799 +
   1.800 +static int
   1.801 +TransformOutputProc (instanceData, buf, toWrite, errorCodePtr)
   1.802 +    ClientData instanceData;
   1.803 +    CONST char*      buf;
   1.804 +    int        toWrite;
   1.805 +    int*       errorCodePtr;
   1.806 +{
   1.807 +    TransformChannelData* dataPtr = (TransformChannelData*) instanceData;
   1.808 +    int res;
   1.809 +
   1.810 +    /* should assert (dataPtr->mode & TCL_WRITABLE) */
   1.811 +
   1.812 +    if (toWrite == 0) {
   1.813 +	/* Catch a no-op.
   1.814 +	 */
   1.815 +	return 0;
   1.816 +    }
   1.817 +
   1.818 +    res = ExecuteCallback (dataPtr, NO_INTERP, A_WRITE,
   1.819 +	    UCHARP (buf), toWrite,
   1.820 +	    TRANSMIT_DOWN, P_NO_PRESERVE);
   1.821 +
   1.822 +    if (res != TCL_OK) {
   1.823 +        *errorCodePtr = EINVAL;
   1.824 +	return -1;
   1.825 +    }
   1.826 +
   1.827 +    return toWrite;
   1.828 +}
   1.829 +
   1.830 +/*
   1.831 + *------------------------------------------------------*
   1.832 + *
   1.833 + *	TransformSeekProc --
   1.834 + *
   1.835 + *	This procedure is called by the generic IO level
   1.836 + *	to move the access point in a channel.
   1.837 + *
   1.838 + *	Sideeffects:
   1.839 + *		Moves the location at which the channel
   1.840 + *		will be accessed in future operations.
   1.841 + *		Flushes all transformation buffers, then
   1.842 + *		forwards it to the underlying channel.
   1.843 + *
   1.844 + *	Result:
   1.845 + *		-1 if failed, the new position if
   1.846 + *		successful. An output argument contains
   1.847 + *		the POSIX error code if an error
   1.848 + *		occurred, or zero.
   1.849 + *
   1.850 + *------------------------------------------------------*
   1.851 + */
   1.852 +
   1.853 +static int
   1.854 +TransformSeekProc (instanceData, offset, mode, errorCodePtr)
   1.855 +    ClientData  instanceData;	/* The channel to manipulate */
   1.856 +    long	offset;		/* Size of movement. */
   1.857 +    int         mode;		/* How to move */
   1.858 +    int*        errorCodePtr;	/* Location of error flag. */
   1.859 +{
   1.860 +    TransformChannelData* dataPtr	= (TransformChannelData*) instanceData;
   1.861 +    Tcl_Channel           parent        = Tcl_GetStackedChannel(dataPtr->self);
   1.862 +    Tcl_ChannelType*      parentType	= Tcl_GetChannelType(parent);
   1.863 +    Tcl_DriverSeekProc*   parentSeekProc = Tcl_ChannelSeekProc(parentType);
   1.864 +
   1.865 +    if ((offset == 0) && (mode == SEEK_CUR)) {
   1.866 +        /* This is no seek but a request to tell the caller the current
   1.867 +	 * location. Simply pass the request down.
   1.868 +	 */
   1.869 +
   1.870 +	return (*parentSeekProc) (Tcl_GetChannelInstanceData(parent),
   1.871 +		offset, mode, errorCodePtr);
   1.872 +    }
   1.873 +
   1.874 +    /*
   1.875 +     * It is a real request to change the position. Flush all data waiting
   1.876 +     * for output and discard everything in the input buffers. Then pass
   1.877 +     * the request down, unchanged.
   1.878 +     */
   1.879 +
   1.880 +    if (dataPtr->mode & TCL_WRITABLE) {
   1.881 +        ExecuteCallback (dataPtr, NO_INTERP, A_FLUSH_WRITE,
   1.882 +		NULL, 0, TRANSMIT_DOWN, P_NO_PRESERVE);
   1.883 +    }
   1.884 +
   1.885 +    if (dataPtr->mode & TCL_READABLE) {
   1.886 +        ExecuteCallback (dataPtr, NO_INTERP, A_CLEAR_READ,
   1.887 +		NULL, 0, TRANSMIT_DONT, P_NO_PRESERVE);
   1.888 +	ResultClear(&dataPtr->result);
   1.889 +	dataPtr->readIsFlushed = 0;
   1.890 +    }
   1.891 +
   1.892 +    return (*parentSeekProc) (Tcl_GetChannelInstanceData(parent),
   1.893 +	    offset, mode, errorCodePtr);
   1.894 +}
   1.895 +
   1.896 +/*
   1.897 + *----------------------------------------------------------------------
   1.898 + *
   1.899 + * TransformWideSeekProc --
   1.900 + *
   1.901 + *	This procedure is called by the generic IO level to move the
   1.902 + *	access point in a channel, with a (potentially) 64-bit offset.
   1.903 + *
   1.904 + * Side effects:
   1.905 + *	Moves the location at which the channel will be accessed in
   1.906 + *	future operations.  Flushes all transformation buffers, then
   1.907 + *	forwards it to the underlying channel.
   1.908 + *
   1.909 + * Result:
   1.910 + *	-1 if failed, the new position if successful. An output
   1.911 + *	argument contains the POSIX error code if an error occurred,
   1.912 + *	or zero.
   1.913 + *
   1.914 + *----------------------------------------------------------------------
   1.915 + */
   1.916 +
   1.917 +static Tcl_WideInt
   1.918 +TransformWideSeekProc (instanceData, offset, mode, errorCodePtr)
   1.919 +    ClientData  instanceData;	/* The channel to manipulate */
   1.920 +    Tcl_WideInt offset;		/* Size of movement. */
   1.921 +    int         mode;		/* How to move */
   1.922 +    int*        errorCodePtr;	/* Location of error flag. */
   1.923 +{
   1.924 +    TransformChannelData* dataPtr =
   1.925 +	(TransformChannelData*) instanceData;
   1.926 +    Tcl_Channel parent =
   1.927 +	Tcl_GetStackedChannel(dataPtr->self);
   1.928 +    Tcl_ChannelType* parentType	=
   1.929 +	Tcl_GetChannelType(parent);
   1.930 +    Tcl_DriverSeekProc* parentSeekProc =
   1.931 +	Tcl_ChannelSeekProc(parentType);
   1.932 +    Tcl_DriverWideSeekProc* parentWideSeekProc =
   1.933 +	Tcl_ChannelWideSeekProc(parentType);
   1.934 +    ClientData parentData =
   1.935 +	Tcl_GetChannelInstanceData(parent);
   1.936 +
   1.937 +    if ((offset == Tcl_LongAsWide(0)) && (mode == SEEK_CUR)) {
   1.938 +        /*
   1.939 +	 * This is no seek but a request to tell the caller the current
   1.940 +	 * location. Simply pass the request down.
   1.941 +	 */
   1.942 +
   1.943 +	if (parentWideSeekProc != NULL) {
   1.944 +	    return (*parentWideSeekProc) (parentData, offset, mode,
   1.945 +		    errorCodePtr);
   1.946 +	}
   1.947 +
   1.948 +	return Tcl_LongAsWide((*parentSeekProc) (parentData, 0, mode,
   1.949 +		errorCodePtr));
   1.950 +    }
   1.951 +
   1.952 +    /*
   1.953 +     * It is a real request to change the position. Flush all data waiting
   1.954 +     * for output and discard everything in the input buffers. Then pass
   1.955 +     * the request down, unchanged.
   1.956 +     */
   1.957 +
   1.958 +    if (dataPtr->mode & TCL_WRITABLE) {
   1.959 +        ExecuteCallback (dataPtr, NO_INTERP, A_FLUSH_WRITE,
   1.960 +		NULL, 0, TRANSMIT_DOWN, P_NO_PRESERVE);
   1.961 +    }
   1.962 +
   1.963 +    if (dataPtr->mode & TCL_READABLE) {
   1.964 +        ExecuteCallback (dataPtr, NO_INTERP, A_CLEAR_READ,
   1.965 +		NULL, 0, TRANSMIT_DONT, P_NO_PRESERVE);
   1.966 +	ResultClear(&dataPtr->result);
   1.967 +	dataPtr->readIsFlushed = 0;
   1.968 +    }
   1.969 +
   1.970 +    /*
   1.971 +     * If we have a wide seek capability, we should stick with that.
   1.972 +     */
   1.973 +    if (parentWideSeekProc != NULL) {
   1.974 +	return (*parentWideSeekProc) (parentData, offset, mode, errorCodePtr);
   1.975 +    }
   1.976 +
   1.977 +    /*
   1.978 +     * We're transferring to narrow seeks at this point; this is a bit
   1.979 +     * complex because we have to check whether the seek is possible
   1.980 +     * first (i.e. whether we are losing information in truncating the
   1.981 +     * bits of the offset.)  Luckily, there's a defined error for what
   1.982 +     * happens when trying to go out of the representable range.
   1.983 +     */
   1.984 +    if (offset<Tcl_LongAsWide(LONG_MIN) || offset>Tcl_LongAsWide(LONG_MAX)) {
   1.985 +	*errorCodePtr = EOVERFLOW;
   1.986 +	return Tcl_LongAsWide(-1);
   1.987 +    }
   1.988 +    return Tcl_LongAsWide((*parentSeekProc) (parentData,
   1.989 +	    Tcl_WideAsLong(offset), mode, errorCodePtr));
   1.990 +}
   1.991 +
   1.992 +/*
   1.993 + *------------------------------------------------------*
   1.994 + *
   1.995 + *	TransformSetOptionProc --
   1.996 + *
   1.997 + *	Called by generic layer to handle the reconfi-
   1.998 + *	guration of channel specific options. As this
   1.999 + *	channel type does not have such, it simply passes
  1.1000 + *	all requests downstream.
  1.1001 + *
  1.1002 + *	Sideeffects:
  1.1003 + *		As defined by the channel downstream.
  1.1004 + *
  1.1005 + *	Result:
  1.1006 + *		A standard TCL error code.
  1.1007 + *
  1.1008 + *------------------------------------------------------*
  1.1009 + */
  1.1010 +
  1.1011 +static int
  1.1012 +TransformSetOptionProc (instanceData, interp, optionName, value)
  1.1013 +    ClientData instanceData;
  1.1014 +    Tcl_Interp *interp;
  1.1015 +    CONST char *optionName;
  1.1016 +    CONST char *value;
  1.1017 +{
  1.1018 +    TransformChannelData* dataPtr = (TransformChannelData*) instanceData;
  1.1019 +    Tcl_Channel downChan = Tcl_GetStackedChannel(dataPtr->self);
  1.1020 +    Tcl_DriverSetOptionProc *setOptionProc;
  1.1021 +
  1.1022 +    setOptionProc = Tcl_ChannelSetOptionProc(Tcl_GetChannelType(downChan));
  1.1023 +    if (setOptionProc != NULL) {
  1.1024 +	return (*setOptionProc)(Tcl_GetChannelInstanceData(downChan),
  1.1025 +		interp, optionName, value);
  1.1026 +    }
  1.1027 +    return TCL_ERROR;
  1.1028 +}
  1.1029 +
  1.1030 +/*
  1.1031 + *------------------------------------------------------*
  1.1032 + *
  1.1033 + *	TransformGetOptionProc --
  1.1034 + *
  1.1035 + *	Called by generic layer to handle requests for
  1.1036 + *	the values of channel specific options. As this
  1.1037 + *	channel type does not have such, it simply passes
  1.1038 + *	all requests downstream.
  1.1039 + *
  1.1040 + *	Sideeffects:
  1.1041 + *		As defined by the channel downstream.
  1.1042 + *
  1.1043 + *	Result:
  1.1044 + *		A standard TCL error code.
  1.1045 + *
  1.1046 + *------------------------------------------------------*
  1.1047 + */
  1.1048 +
  1.1049 +static int
  1.1050 +TransformGetOptionProc (instanceData, interp, optionName, dsPtr)
  1.1051 +    ClientData   instanceData;
  1.1052 +    Tcl_Interp*  interp;
  1.1053 +    CONST char*        optionName;
  1.1054 +    Tcl_DString* dsPtr;
  1.1055 +{
  1.1056 +    TransformChannelData* dataPtr = (TransformChannelData*) instanceData;
  1.1057 +    Tcl_Channel downChan = Tcl_GetStackedChannel(dataPtr->self);
  1.1058 +    Tcl_DriverGetOptionProc *getOptionProc;
  1.1059 +
  1.1060 +    getOptionProc = Tcl_ChannelGetOptionProc(Tcl_GetChannelType(downChan));
  1.1061 +    if (getOptionProc != NULL) {
  1.1062 +	return (*getOptionProc)(Tcl_GetChannelInstanceData(downChan),
  1.1063 +		interp, optionName, dsPtr);
  1.1064 +    } else if (optionName == (CONST char*) NULL) {
  1.1065 +	/*
  1.1066 +	 * Request is query for all options, this is ok.
  1.1067 +	 */
  1.1068 +	return TCL_OK;
  1.1069 +    }
  1.1070 +    /*
  1.1071 +     * Request for a specific option has to fail, we don't have any.
  1.1072 +     */
  1.1073 +    return TCL_ERROR;
  1.1074 +}
  1.1075 +
  1.1076 +/*
  1.1077 + *------------------------------------------------------*
  1.1078 + *
  1.1079 + *	TransformWatchProc --
  1.1080 + *
  1.1081 + *	Initialize the notifier to watch for events from
  1.1082 + *	this channel.
  1.1083 + *
  1.1084 + *	Sideeffects:
  1.1085 + *		Sets up the notifier so that a future
  1.1086 + *		event on the channel will be seen by Tcl.
  1.1087 + *
  1.1088 + *	Result:
  1.1089 + *		None.
  1.1090 + *
  1.1091 + *------------------------------------------------------*
  1.1092 + */
  1.1093 +	/* ARGSUSED */
  1.1094 +static void
  1.1095 +TransformWatchProc (instanceData, mask)
  1.1096 +    ClientData instanceData;	/* Channel to watch */
  1.1097 +    int        mask;		/* Events of interest */
  1.1098 +{
  1.1099 +    /* The caller expressed interest in events occuring for this
  1.1100 +     * channel. We are forwarding the call to the underlying
  1.1101 +     * channel now.
  1.1102 +     */
  1.1103 +
  1.1104 +    TransformChannelData* dataPtr = (TransformChannelData*) instanceData;
  1.1105 +    Tcl_Channel     downChan;
  1.1106 +
  1.1107 +    dataPtr->watchMask = mask;
  1.1108 +
  1.1109 +    /* No channel handlers any more. We will be notified automatically
  1.1110 +     * about events on the channel below via a call to our
  1.1111 +     * 'TransformNotifyProc'. But we have to pass the interest down now.
  1.1112 +     * We are allowed to add additional 'interest' to the mask if we want
  1.1113 +     * to. But this transformation has no such interest. It just passes
  1.1114 +     * the request down, unchanged.
  1.1115 +     */
  1.1116 +
  1.1117 +    downChan = Tcl_GetStackedChannel(dataPtr->self);
  1.1118 +
  1.1119 +    (Tcl_GetChannelType(downChan))
  1.1120 +	->watchProc(Tcl_GetChannelInstanceData(downChan), mask);
  1.1121 +
  1.1122 +    /*
  1.1123 +     * Management of the internal timer.
  1.1124 +     */
  1.1125 +
  1.1126 +    if ((dataPtr->timer != (Tcl_TimerToken) NULL) &&
  1.1127 +	    (!(mask & TCL_READABLE) || (ResultLength(&dataPtr->result) == 0))) {
  1.1128 +
  1.1129 +        /* A pending timer exists, but either is there no (more)
  1.1130 +	 * interest in the events it generates or nothing is availablee
  1.1131 +	 * for reading, so remove it.
  1.1132 +	 */
  1.1133 +
  1.1134 +        Tcl_DeleteTimerHandler (dataPtr->timer);
  1.1135 +	dataPtr->timer = (Tcl_TimerToken) NULL;
  1.1136 +    }
  1.1137 +
  1.1138 +    if ((dataPtr->timer == (Tcl_TimerToken) NULL) &&
  1.1139 +	    (mask & TCL_READABLE) && (ResultLength (&dataPtr->result) > 0)) {
  1.1140 +
  1.1141 +        /* There is no pending timer, but there is interest in readable
  1.1142 +	 * events and we actually have data waiting, so generate a timer
  1.1143 +	 * to flush that.
  1.1144 +	 */
  1.1145 +
  1.1146 +	dataPtr->timer = Tcl_CreateTimerHandler (FLUSH_DELAY,
  1.1147 +		TransformChannelHandlerTimer, (ClientData) dataPtr);
  1.1148 +    }
  1.1149 +}
  1.1150 +
  1.1151 +/*
  1.1152 + *------------------------------------------------------*
  1.1153 + *
  1.1154 + *	TransformGetFileHandleProc --
  1.1155 + *
  1.1156 + *	Called from Tcl_GetChannelHandle to retrieve
  1.1157 + *	OS specific file handle from inside this channel.
  1.1158 + *
  1.1159 + *	Sideeffects:
  1.1160 + *		None.
  1.1161 + *
  1.1162 + *	Result:
  1.1163 + *		The appropriate Tcl_File or NULL if not
  1.1164 + *		present. 
  1.1165 + *
  1.1166 + *------------------------------------------------------*
  1.1167 + */
  1.1168 +static int
  1.1169 +TransformGetFileHandleProc (instanceData, direction, handlePtr)
  1.1170 +    ClientData  instanceData;	/* Channel to query */
  1.1171 +    int         direction;	/* Direction of interest */
  1.1172 +    ClientData* handlePtr;	/* Place to store the handle into */
  1.1173 +{
  1.1174 +    /*
  1.1175 +     * Return the handle belonging to parent channel.
  1.1176 +     * IOW, pass the request down and the result up.
  1.1177 +     */
  1.1178 +
  1.1179 +    TransformChannelData* dataPtr = (TransformChannelData*) instanceData;
  1.1180 +
  1.1181 +    return Tcl_GetChannelHandle(Tcl_GetStackedChannel(dataPtr->self),
  1.1182 +	    direction, handlePtr);
  1.1183 +}
  1.1184 +
  1.1185 +/*
  1.1186 + *------------------------------------------------------*
  1.1187 + *
  1.1188 + *	TransformNotifyProc --
  1.1189 + *
  1.1190 + *	------------------------------------------------*
  1.1191 + *	Handler called by Tcl to inform us of activity
  1.1192 + *	on the underlying channel.
  1.1193 + *	------------------------------------------------*
  1.1194 + *
  1.1195 + *	Sideeffects:
  1.1196 + *		May process the incoming event by itself.
  1.1197 + *
  1.1198 + *	Result:
  1.1199 + *		None.
  1.1200 + *
  1.1201 + *------------------------------------------------------*
  1.1202 + */
  1.1203 +
  1.1204 +static int
  1.1205 +TransformNotifyProc (clientData, mask)
  1.1206 +    ClientData	   clientData; /* The state of the notified transformation */
  1.1207 +    int		   mask;       /* The mask of occuring events */
  1.1208 +{
  1.1209 +    TransformChannelData* dataPtr = (TransformChannelData*) clientData;
  1.1210 +
  1.1211 +    /*
  1.1212 +     * An event occured in the underlying channel.  This
  1.1213 +     * transformation doesn't process such events thus returns the
  1.1214 +     * incoming mask unchanged.
  1.1215 +     */
  1.1216 +
  1.1217 +    if (dataPtr->timer != (Tcl_TimerToken) NULL) {
  1.1218 +	/*
  1.1219 +	 * Delete an existing timer. It was not fired, yet we are
  1.1220 +	 * here, so the channel below generated such an event and we
  1.1221 +	 * don't have to. The renewal of the interest after the
  1.1222 +	 * execution of channel handlers will eventually cause us to
  1.1223 +	 * recreate the timer (in TransformWatchProc).
  1.1224 +	 */
  1.1225 +
  1.1226 +	Tcl_DeleteTimerHandler (dataPtr->timer);
  1.1227 +	dataPtr->timer = (Tcl_TimerToken) NULL;
  1.1228 +    }
  1.1229 +
  1.1230 +    return mask;
  1.1231 +}
  1.1232 +
  1.1233 +/*
  1.1234 + *------------------------------------------------------*
  1.1235 + *
  1.1236 + *	TransformChannelHandlerTimer --
  1.1237 + *
  1.1238 + *	Called by the notifier (-> timer) to flush out
  1.1239 + *	information waiting in the input buffer.
  1.1240 + *
  1.1241 + *	Sideeffects:
  1.1242 + *		As of 'Tcl_NotifyChannel'.
  1.1243 + *
  1.1244 + *	Result:
  1.1245 + *		None.
  1.1246 + *
  1.1247 + *------------------------------------------------------*
  1.1248 + */
  1.1249 +
  1.1250 +static void
  1.1251 +TransformChannelHandlerTimer (clientData)
  1.1252 +    ClientData clientData; /* Transformation to query */
  1.1253 +{
  1.1254 +    TransformChannelData* dataPtr = (TransformChannelData*) clientData;
  1.1255 +
  1.1256 +    dataPtr->timer = (Tcl_TimerToken) NULL;
  1.1257 +
  1.1258 +    if (!(dataPtr->watchMask & TCL_READABLE) ||
  1.1259 +	    (ResultLength (&dataPtr->result) == 0)) {
  1.1260 +	/* The timer fired, but either is there no (more)
  1.1261 +	 * interest in the events it generates or nothing is available
  1.1262 +	 * for reading, so ignore it and don't recreate it.
  1.1263 +	 */
  1.1264 +
  1.1265 +	return;
  1.1266 +    }
  1.1267 +
  1.1268 +    Tcl_NotifyChannel(dataPtr->self, TCL_READABLE);
  1.1269 +}
  1.1270 +
  1.1271 +/*
  1.1272 + *------------------------------------------------------*
  1.1273 + *
  1.1274 + *	ResultClear --
  1.1275 + *
  1.1276 + *	Deallocates any memory allocated by 'ResultAdd'.
  1.1277 + *
  1.1278 + *	Sideeffects:
  1.1279 + *		See above.
  1.1280 + *
  1.1281 + *	Result:
  1.1282 + *		None.
  1.1283 + *
  1.1284 + *------------------------------------------------------*
  1.1285 + */
  1.1286 +
  1.1287 +static void
  1.1288 +ResultClear (r)
  1.1289 +    ResultBuffer* r; /* Reference to the buffer to clear out */
  1.1290 +{
  1.1291 +    r->used = 0;
  1.1292 +
  1.1293 +    if (r->allocated) {
  1.1294 +        ckfree((char*) r->buf);
  1.1295 +	r->buf       = UCHARP (NULL);
  1.1296 +	r->allocated = 0;
  1.1297 +    }
  1.1298 +}
  1.1299 +
  1.1300 +/*
  1.1301 + *------------------------------------------------------*
  1.1302 + *
  1.1303 + *	ResultInit --
  1.1304 + *
  1.1305 + *	Initializes the specified buffer structure. The
  1.1306 + *	structure will contain valid information for an
  1.1307 + *	emtpy buffer.
  1.1308 + *
  1.1309 + *	Sideeffects:
  1.1310 + *		See above.
  1.1311 + *
  1.1312 + *	Result:
  1.1313 + *		None.
  1.1314 + *
  1.1315 + *------------------------------------------------------*
  1.1316 + */
  1.1317 +
  1.1318 +static void
  1.1319 +ResultInit (r)
  1.1320 +    ResultBuffer* r; /* Reference to the structure to initialize */
  1.1321 +{
  1.1322 +    r->used      = 0;
  1.1323 +    r->allocated = 0;
  1.1324 +    r->buf       = UCHARP (NULL);
  1.1325 +}
  1.1326 +
  1.1327 +/*
  1.1328 + *------------------------------------------------------*
  1.1329 + *
  1.1330 + *	ResultLength --
  1.1331 + *
  1.1332 + *	Returns the number of bytes stored in the buffer.
  1.1333 + *
  1.1334 + *	Sideeffects:
  1.1335 + *		None.
  1.1336 + *
  1.1337 + *	Result:
  1.1338 + *		An integer, see above too.
  1.1339 + *
  1.1340 + *------------------------------------------------------*
  1.1341 + */
  1.1342 +
  1.1343 +static int
  1.1344 +ResultLength (r)
  1.1345 +    ResultBuffer* r; /* The structure to query */
  1.1346 +{
  1.1347 +    return r->used;
  1.1348 +}
  1.1349 +
  1.1350 +/*
  1.1351 + *------------------------------------------------------*
  1.1352 + *
  1.1353 + *	ResultCopy --
  1.1354 + *
  1.1355 + *	Copies the requested number of bytes from the
  1.1356 + *	buffer into the specified array and removes them
  1.1357 + *	from the buffer afterward. Copies less if there
  1.1358 + *	is not enough data in the buffer.
  1.1359 + *
  1.1360 + *	Sideeffects:
  1.1361 + *		See above.
  1.1362 + *
  1.1363 + *	Result:
  1.1364 + *		The number of actually copied bytes,
  1.1365 + *		possibly less than 'toRead'.
  1.1366 + *
  1.1367 + *------------------------------------------------------*
  1.1368 + */
  1.1369 +
  1.1370 +static int
  1.1371 +ResultCopy (r, buf, toRead)
  1.1372 +    ResultBuffer*  r;      /* The buffer to read from */
  1.1373 +    unsigned char* buf;    /* The buffer to copy into */
  1.1374 +    int		   toRead; /* Number of requested bytes */
  1.1375 +{
  1.1376 +    if (r->used == 0) {
  1.1377 +        /* Nothing to copy in the case of an empty buffer.
  1.1378 +	 */
  1.1379 +
  1.1380 +        return 0;
  1.1381 +    }
  1.1382 +
  1.1383 +    if (r->used == toRead) {
  1.1384 +        /* We have just enough. Copy everything to the caller.
  1.1385 +	 */
  1.1386 +
  1.1387 +        memcpy ((VOID*) buf, (VOID*) r->buf, (size_t) toRead);
  1.1388 +	r->used = 0;
  1.1389 +	return toRead;
  1.1390 +    }
  1.1391 +
  1.1392 +    if (r->used > toRead) {
  1.1393 +        /* The internal buffer contains more than requested.
  1.1394 +	 * Copy the requested subset to the caller, and shift
  1.1395 +	 * the remaining bytes down.
  1.1396 +	 */
  1.1397 +
  1.1398 +        memcpy  ((VOID*) buf,    (VOID*) r->buf,            (size_t) toRead);
  1.1399 +	memmove ((VOID*) r->buf, (VOID*) (r->buf + toRead),
  1.1400 +		(size_t) r->used - toRead);
  1.1401 +
  1.1402 +	r->used -= toRead;
  1.1403 +	return toRead;
  1.1404 +    }
  1.1405 +
  1.1406 +    /* There is not enough in the buffer to satisfy the caller, so
  1.1407 +     * take everything.
  1.1408 +     */
  1.1409 +
  1.1410 +    memcpy((VOID*) buf, (VOID*) r->buf, (size_t) r->used);
  1.1411 +    toRead  = r->used;
  1.1412 +    r->used = 0;
  1.1413 +    return toRead;
  1.1414 +}
  1.1415 +
  1.1416 +/*
  1.1417 + *------------------------------------------------------*
  1.1418 + *
  1.1419 + *	ResultAdd --
  1.1420 + *
  1.1421 + *	Adds the bytes in the specified array to the
  1.1422 + *	buffer, by appending it.
  1.1423 + *
  1.1424 + *	Sideeffects:
  1.1425 + *		See above.
  1.1426 + *
  1.1427 + *	Result:
  1.1428 + *		None.
  1.1429 + *
  1.1430 + *------------------------------------------------------*
  1.1431 + */
  1.1432 +
  1.1433 +static void
  1.1434 +ResultAdd (r, buf, toWrite)
  1.1435 +    ResultBuffer*  r;       /* The buffer to extend */
  1.1436 +    unsigned char* buf;     /* The buffer to read from */
  1.1437 +    int		   toWrite; /* The number of bytes in 'buf' */
  1.1438 +{
  1.1439 +    if ((r->used + toWrite) > r->allocated) {
  1.1440 +        /* Extension of the internal buffer is required.
  1.1441 +	 */
  1.1442 +
  1.1443 +        if (r->allocated == 0) {
  1.1444 +	    r->allocated = toWrite + INCREMENT;
  1.1445 +	    r->buf       = UCHARP (ckalloc((unsigned) r->allocated));
  1.1446 +	} else {
  1.1447 +	    r->allocated += toWrite + INCREMENT;
  1.1448 +	    r->buf        = UCHARP (ckrealloc((char*) r->buf,
  1.1449 +		    (unsigned) r->allocated));
  1.1450 +	}
  1.1451 +    }
  1.1452 +
  1.1453 +    /* now copy data */
  1.1454 +    memcpy(r->buf + r->used, buf, (size_t) toWrite);
  1.1455 +    r->used += toWrite;
  1.1456 +}