os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclIOGT.c
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 +}