os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclIO.c
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclIO.c Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,9474 @@
1.4 +/*
1.5 + * tclIO.c --
1.6 + *
1.7 + * This file provides the generic portions (those that are the same on
1.8 + * all platforms and for all channel types) of Tcl's IO facilities.
1.9 + *
1.10 + * Copyright (c) 1998-2000 Ajuba Solutions
1.11 + * Copyright (c) 1995-1997 Sun Microsystems, Inc.
1.12 + * Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved.
1.13 + *
1.14 + * See the file "license.terms" for information on usage and redistribution
1.15 + * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
1.16 + *
1.17 + * RCS: @(#) $Id: tclIO.c,v 1.61.2.23 2007/05/24 19:31:55 dgp Exp $
1.18 + */
1.19 +
1.20 +#include "tclInt.h"
1.21 +#include "tclPort.h"
1.22 +#include "tclIO.h"
1.23 +#include <assert.h>
1.24 +#if defined(__SYMBIAN32__) && defined(__WINSCW__)
1.25 +#include "tclSymbianGlobals.h"
1.26 +#define dataKey getdataKey(3)
1.27 +#endif
1.28 +
1.29 +#ifndef TCL_INHERIT_STD_CHANNELS
1.30 +#define TCL_INHERIT_STD_CHANNELS 1
1.31 +#endif
1.32 +
1.33 +
1.34 +/*
1.35 + * All static variables used in this file are collected into a single
1.36 + * instance of the following structure. For multi-threaded implementations,
1.37 + * there is one instance of this structure for each thread.
1.38 + *
1.39 + * Notice that different structures with the same name appear in other
1.40 + * files. The structure defined below is used in this file only.
1.41 + */
1.42 +
1.43 +typedef struct ThreadSpecificData {
1.44 +
1.45 + /*
1.46 + * This variable holds the list of nested ChannelHandlerEventProc
1.47 + * invocations.
1.48 + */
1.49 + NextChannelHandler *nestedHandlerPtr;
1.50 +
1.51 + /*
1.52 + * List of all channels currently open, indexed by ChannelState,
1.53 + * as only one ChannelState exists per set of stacked channels.
1.54 + */
1.55 + ChannelState *firstCSPtr;
1.56 +#ifdef oldcode
1.57 + /*
1.58 + * Has a channel exit handler been created yet?
1.59 + */
1.60 + int channelExitHandlerCreated;
1.61 +
1.62 + /*
1.63 + * Has the channel event source been created and registered with the
1.64 + * notifier?
1.65 + */
1.66 + int channelEventSourceCreated;
1.67 +#endif
1.68 + /*
1.69 + * Static variables to hold channels for stdin, stdout and stderr.
1.70 + */
1.71 + Tcl_Channel stdinChannel;
1.72 + int stdinInitialized;
1.73 + Tcl_Channel stdoutChannel;
1.74 + int stdoutInitialized;
1.75 + Tcl_Channel stderrChannel;
1.76 + int stderrInitialized;
1.77 +
1.78 +} ThreadSpecificData;
1.79 +
1.80 +#if !defined(__SYMBIAN32__) || !defined(__WINSCW__)
1.81 +static Tcl_ThreadDataKey dataKey;
1.82 +#endif
1.83 +
1.84 +/*
1.85 + * Static functions in this file:
1.86 + */
1.87 +
1.88 +static ChannelBuffer * AllocChannelBuffer _ANSI_ARGS_((int length));
1.89 +static void ChannelTimerProc _ANSI_ARGS_((
1.90 + ClientData clientData));
1.91 +static int CheckChannelErrors _ANSI_ARGS_((ChannelState *statePtr,
1.92 + int direction));
1.93 +static int CheckFlush _ANSI_ARGS_((Channel *chanPtr,
1.94 + ChannelBuffer *bufPtr, int newlineFlag));
1.95 +static int CheckForDeadChannel _ANSI_ARGS_((Tcl_Interp *interp,
1.96 + ChannelState *statePtr));
1.97 +static void CheckForStdChannelsBeingClosed _ANSI_ARGS_((
1.98 + Tcl_Channel chan));
1.99 +static void CleanupChannelHandlers _ANSI_ARGS_((
1.100 + Tcl_Interp *interp, Channel *chanPtr));
1.101 +static int CloseChannel _ANSI_ARGS_((Tcl_Interp *interp,
1.102 + Channel *chanPtr, int errorCode));
1.103 +static void CommonGetsCleanup _ANSI_ARGS_((Channel *chanPtr,
1.104 + Tcl_Encoding encoding));
1.105 +static int CopyAndTranslateBuffer _ANSI_ARGS_((
1.106 + ChannelState *statePtr, char *result,
1.107 + int space));
1.108 +static int CopyBuffer _ANSI_ARGS_((
1.109 + Channel *chanPtr, char *result, int space));
1.110 +static int CopyData _ANSI_ARGS_((CopyState *csPtr, int mask));
1.111 +static void CopyEventProc _ANSI_ARGS_((ClientData clientData,
1.112 + int mask));
1.113 +static void CreateScriptRecord _ANSI_ARGS_((
1.114 + Tcl_Interp *interp, Channel *chanPtr,
1.115 + int mask, Tcl_Obj *scriptPtr));
1.116 +static void DeleteChannelTable _ANSI_ARGS_((
1.117 + ClientData clientData, Tcl_Interp *interp));
1.118 +static void DeleteScriptRecord _ANSI_ARGS_((Tcl_Interp *interp,
1.119 + Channel *chanPtr, int mask));
1.120 +static int DetachChannel _ANSI_ARGS_((Tcl_Interp *interp,
1.121 + Tcl_Channel chan));
1.122 +static void DiscardInputQueued _ANSI_ARGS_((ChannelState *statePtr,
1.123 + int discardSavedBuffers));
1.124 +static void DiscardOutputQueued _ANSI_ARGS_((
1.125 + ChannelState *chanPtr));
1.126 +static int DoRead _ANSI_ARGS_((Channel *chanPtr, char *srcPtr,
1.127 + int slen));
1.128 +static int DoWrite _ANSI_ARGS_((Channel *chanPtr, CONST char *src,
1.129 + int srcLen));
1.130 +static int DoReadChars _ANSI_ARGS_ ((Channel* chan,
1.131 + Tcl_Obj* objPtr, int toRead, int appendFlag));
1.132 +static int DoWriteChars _ANSI_ARGS_ ((Channel* chan,
1.133 + CONST char* src, int len));
1.134 +static int FilterInputBytes _ANSI_ARGS_((Channel *chanPtr,
1.135 + GetsState *statePtr));
1.136 +static int FlushChannel _ANSI_ARGS_((Tcl_Interp *interp,
1.137 + Channel *chanPtr, int calledFromAsyncFlush));
1.138 +static Tcl_HashTable * GetChannelTable _ANSI_ARGS_((Tcl_Interp *interp));
1.139 +static int GetInput _ANSI_ARGS_((Channel *chanPtr));
1.140 +static int HaveVersion _ANSI_ARGS_((Tcl_ChannelType *typePtr,
1.141 + Tcl_ChannelTypeVersion minimumVersion));
1.142 +static void PeekAhead _ANSI_ARGS_((Channel *chanPtr,
1.143 + char **dstEndPtr, GetsState *gsPtr));
1.144 +static int ReadBytes _ANSI_ARGS_((ChannelState *statePtr,
1.145 + Tcl_Obj *objPtr, int charsLeft,
1.146 + int *offsetPtr));
1.147 +static int ReadChars _ANSI_ARGS_((ChannelState *statePtr,
1.148 + Tcl_Obj *objPtr, int charsLeft,
1.149 + int *offsetPtr, int *factorPtr));
1.150 +static void RecycleBuffer _ANSI_ARGS_((ChannelState *statePtr,
1.151 + ChannelBuffer *bufPtr, int mustDiscard));
1.152 +static int StackSetBlockMode _ANSI_ARGS_((Channel *chanPtr,
1.153 + int mode));
1.154 +static int SetBlockMode _ANSI_ARGS_((Tcl_Interp *interp,
1.155 + Channel *chanPtr, int mode));
1.156 +static void StopCopy _ANSI_ARGS_((CopyState *csPtr));
1.157 +static int TranslateInputEOL _ANSI_ARGS_((ChannelState *statePtr,
1.158 + char *dst, CONST char *src,
1.159 + int *dstLenPtr, int *srcLenPtr));
1.160 +static int TranslateOutputEOL _ANSI_ARGS_((ChannelState *statePtr,
1.161 + char *dst, CONST char *src,
1.162 + int *dstLenPtr, int *srcLenPtr));
1.163 +static void UpdateInterest _ANSI_ARGS_((Channel *chanPtr));
1.164 +static int WriteBytes _ANSI_ARGS_((Channel *chanPtr,
1.165 + CONST char *src, int srcLen));
1.166 +static int WriteChars _ANSI_ARGS_((Channel *chanPtr,
1.167 + CONST char *src, int srcLen));
1.168 +
1.169 +
1.170 +/*
1.171 + *---------------------------------------------------------------------------
1.172 + *
1.173 + * TclInitIOSubsystem --
1.174 + *
1.175 + * Initialize all resources used by this subsystem on a per-process
1.176 + * basis.
1.177 + *
1.178 + * Results:
1.179 + * None.
1.180 + *
1.181 + * Side effects:
1.182 + * Depends on the memory subsystems.
1.183 + *
1.184 + *---------------------------------------------------------------------------
1.185 + */
1.186 +
1.187 +void
1.188 +TclInitIOSubsystem()
1.189 +{
1.190 + /*
1.191 + * By fetching thread local storage we take care of
1.192 + * allocating it for each thread.
1.193 + */
1.194 + (void) TCL_TSD_INIT(&dataKey);
1.195 +}
1.196 +
1.197 +/*
1.198 + *-------------------------------------------------------------------------
1.199 + *
1.200 + * TclFinalizeIOSubsystem --
1.201 + *
1.202 + * Releases all resources used by this subsystem on a per-thread
1.203 + * basis. Closes all extant channels that have not already been
1.204 + * closed because they were not owned by any interp.
1.205 + *
1.206 + * Results:
1.207 + * None.
1.208 + *
1.209 + * Side effects:
1.210 + * Depends on encoding and memory subsystems.
1.211 + *
1.212 + *-------------------------------------------------------------------------
1.213 + */
1.214 +
1.215 + /* ARGSUSED */
1.216 +void
1.217 +TclFinalizeIOSubsystem(void)
1.218 +{
1.219 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.220 + Channel *chanPtr = NULL; /* Iterates over open channels. */
1.221 + ChannelState *statePtr; /* State of channel stack */
1.222 + int active = 1; /* Flag == 1 while there's still work to do */
1.223 +
1.224 + /*
1.225 + * Walk all channel state structures known to this thread and
1.226 + * close corresponding channels.
1.227 + */
1.228 +
1.229 + while (active) {
1.230 +
1.231 + /*
1.232 + * Iterate through the open channel list, and find the first
1.233 + * channel that isn't dead. We start from the head of the list
1.234 + * each time, because the close action on one channel can close
1.235 + * others.
1.236 + */
1.237 +
1.238 + active = 0;
1.239 + for (statePtr = tsdPtr->firstCSPtr;
1.240 + statePtr != NULL;
1.241 + statePtr = statePtr->nextCSPtr) {
1.242 + chanPtr = statePtr->topChanPtr;
1.243 + if (!(statePtr->flags & CHANNEL_DEAD)) {
1.244 + active = 1;
1.245 + break;
1.246 + }
1.247 + }
1.248 +
1.249 + /*
1.250 + * We've found a live channel. Close it.
1.251 + */
1.252 +
1.253 + if (active) {
1.254 +
1.255 + /*
1.256 + * Set the channel back into blocking mode to ensure that we
1.257 + * wait for all data to flush out.
1.258 + */
1.259 +
1.260 + (void) Tcl_SetChannelOption(NULL, (Tcl_Channel) chanPtr,
1.261 + "-blocking", "on");
1.262 +
1.263 + if ((chanPtr == (Channel *) tsdPtr->stdinChannel) ||
1.264 + (chanPtr == (Channel *) tsdPtr->stdoutChannel) ||
1.265 + (chanPtr == (Channel *) tsdPtr->stderrChannel)) {
1.266 + /*
1.267 + * Decrement the refcount which was earlier artificially
1.268 + * bumped up to keep the channel from being closed.
1.269 + */
1.270 +
1.271 + statePtr->refCount--;
1.272 + }
1.273 +
1.274 + if (statePtr->refCount <= 0) {
1.275 + /*
1.276 + * Close it only if the refcount indicates that the channel
1.277 + * is not referenced from any interpreter. If it is, that
1.278 + * interpreter will close the channel when it gets destroyed.
1.279 + */
1.280 +
1.281 + (void) Tcl_Close(NULL, (Tcl_Channel) chanPtr);
1.282 + } else {
1.283 + /*
1.284 + * The refcount is greater than zero, so flush the channel.
1.285 + */
1.286 +
1.287 + Tcl_Flush((Tcl_Channel) chanPtr);
1.288 +
1.289 + /*
1.290 + * Call the device driver to actually close the underlying
1.291 + * device for this channel.
1.292 + */
1.293 +
1.294 + if (chanPtr->typePtr->closeProc != TCL_CLOSE2PROC) {
1.295 + (chanPtr->typePtr->closeProc)(chanPtr->instanceData, NULL);
1.296 + } else {
1.297 + (chanPtr->typePtr->close2Proc)(chanPtr->instanceData,
1.298 + NULL, 0);
1.299 + }
1.300 +
1.301 + /*
1.302 + * Finally, we clean up the fields in the channel data
1.303 + * structure since all of them have been deleted already.
1.304 + * We mark the channel with CHANNEL_DEAD to prevent any
1.305 + * further IO operations
1.306 + * on it.
1.307 + */
1.308 +
1.309 + chanPtr->instanceData = NULL;
1.310 + statePtr->flags |= CHANNEL_DEAD;
1.311 + }
1.312 + }
1.313 + }
1.314 +
1.315 + TclpFinalizeSockets();
1.316 + TclpFinalizePipes();
1.317 +}
1.318 +
1.319 +
1.320 +/*
1.321 + *----------------------------------------------------------------------
1.322 + *
1.323 + * Tcl_SetStdChannel --
1.324 + *
1.325 + * This function is used to change the channels that are used
1.326 + * for stdin/stdout/stderr in new interpreters.
1.327 + *
1.328 + * Results:
1.329 + * None
1.330 + *
1.331 + * Side effects:
1.332 + * None.
1.333 + *
1.334 + *----------------------------------------------------------------------
1.335 + */
1.336 +
1.337 +EXPORT_C void
1.338 +Tcl_SetStdChannel(channel, type)
1.339 + Tcl_Channel channel;
1.340 + int type; /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
1.341 +{
1.342 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.343 + switch (type) {
1.344 + case TCL_STDIN:
1.345 + tsdPtr->stdinInitialized = 1;
1.346 + tsdPtr->stdinChannel = channel;
1.347 + break;
1.348 + case TCL_STDOUT:
1.349 + tsdPtr->stdoutInitialized = 1;
1.350 + tsdPtr->stdoutChannel = channel;
1.351 + break;
1.352 + case TCL_STDERR:
1.353 + tsdPtr->stderrInitialized = 1;
1.354 + tsdPtr->stderrChannel = channel;
1.355 + break;
1.356 + }
1.357 +}
1.358 +
1.359 +/*
1.360 + *----------------------------------------------------------------------
1.361 + *
1.362 + * Tcl_GetStdChannel --
1.363 + *
1.364 + * Returns the specified standard channel.
1.365 + *
1.366 + * Results:
1.367 + * Returns the specified standard channel, or NULL.
1.368 + *
1.369 + * Side effects:
1.370 + * May cause the creation of a standard channel and the underlying
1.371 + * file.
1.372 + *
1.373 + *----------------------------------------------------------------------
1.374 + */
1.375 +EXPORT_C Tcl_Channel
1.376 +Tcl_GetStdChannel(type)
1.377 + int type; /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
1.378 +{
1.379 + Tcl_Channel channel = NULL;
1.380 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.381 +
1.382 + /*
1.383 + * If the channels were not created yet, create them now and
1.384 + * store them in the static variables.
1.385 + */
1.386 +
1.387 + switch (type) {
1.388 + case TCL_STDIN:
1.389 + if (!tsdPtr->stdinInitialized) {
1.390 + tsdPtr->stdinChannel = TclpGetDefaultStdChannel(TCL_STDIN);
1.391 + tsdPtr->stdinInitialized = 1;
1.392 +
1.393 + /*
1.394 + * Artificially bump the refcount to ensure that the channel
1.395 + * is only closed on exit.
1.396 + *
1.397 + * NOTE: Must only do this if stdinChannel is not NULL. It
1.398 + * can be NULL in situations where Tcl is unable to connect
1.399 + * to the standard input.
1.400 + */
1.401 +
1.402 + if (tsdPtr->stdinChannel != (Tcl_Channel) NULL) {
1.403 + (void) Tcl_RegisterChannel((Tcl_Interp *) NULL,
1.404 + tsdPtr->stdinChannel);
1.405 + }
1.406 + }
1.407 + channel = tsdPtr->stdinChannel;
1.408 + break;
1.409 + case TCL_STDOUT:
1.410 + if (!tsdPtr->stdoutInitialized) {
1.411 + tsdPtr->stdoutChannel = TclpGetDefaultStdChannel(TCL_STDOUT);
1.412 + tsdPtr->stdoutInitialized = 1;
1.413 + if (tsdPtr->stdoutChannel != (Tcl_Channel) NULL) {
1.414 + (void) Tcl_RegisterChannel((Tcl_Interp *) NULL,
1.415 + tsdPtr->stdoutChannel);
1.416 + }
1.417 + }
1.418 + channel = tsdPtr->stdoutChannel;
1.419 + break;
1.420 + case TCL_STDERR:
1.421 + if (!tsdPtr->stderrInitialized) {
1.422 + tsdPtr->stderrChannel = TclpGetDefaultStdChannel(TCL_STDERR);
1.423 + tsdPtr->stderrInitialized = 1;
1.424 + if (tsdPtr->stderrChannel != (Tcl_Channel) NULL) {
1.425 + (void) Tcl_RegisterChannel((Tcl_Interp *) NULL,
1.426 + tsdPtr->stderrChannel);
1.427 + }
1.428 + }
1.429 + channel = tsdPtr->stderrChannel;
1.430 + break;
1.431 + }
1.432 + return channel;
1.433 +}
1.434 +
1.435 +
1.436 +/*
1.437 + *----------------------------------------------------------------------
1.438 + *
1.439 + * Tcl_CreateCloseHandler
1.440 + *
1.441 + * Creates a close callback which will be called when the channel is
1.442 + * closed.
1.443 + *
1.444 + * Results:
1.445 + * None.
1.446 + *
1.447 + * Side effects:
1.448 + * Causes the callback to be called in the future when the channel
1.449 + * will be closed.
1.450 + *
1.451 + *----------------------------------------------------------------------
1.452 + */
1.453 +
1.454 +EXPORT_C void
1.455 +Tcl_CreateCloseHandler(chan, proc, clientData)
1.456 + Tcl_Channel chan; /* The channel for which to create the
1.457 + * close callback. */
1.458 + Tcl_CloseProc *proc; /* The callback routine to call when the
1.459 + * channel will be closed. */
1.460 + ClientData clientData; /* Arbitrary data to pass to the
1.461 + * close callback. */
1.462 +{
1.463 + ChannelState *statePtr;
1.464 + CloseCallback *cbPtr;
1.465 +
1.466 + statePtr = ((Channel *) chan)->state;
1.467 +
1.468 + cbPtr = (CloseCallback *) ckalloc((unsigned) sizeof(CloseCallback));
1.469 + cbPtr->proc = proc;
1.470 + cbPtr->clientData = clientData;
1.471 +
1.472 + cbPtr->nextPtr = statePtr->closeCbPtr;
1.473 + statePtr->closeCbPtr = cbPtr;
1.474 +}
1.475 +
1.476 +/*
1.477 + *----------------------------------------------------------------------
1.478 + *
1.479 + * Tcl_DeleteCloseHandler --
1.480 + *
1.481 + * Removes a callback that would have been called on closing
1.482 + * the channel. If there is no matching callback then this
1.483 + * function has no effect.
1.484 + *
1.485 + * Results:
1.486 + * None.
1.487 + *
1.488 + * Side effects:
1.489 + * The callback will not be called in the future when the channel
1.490 + * is eventually closed.
1.491 + *
1.492 + *----------------------------------------------------------------------
1.493 + */
1.494 +
1.495 +EXPORT_C void
1.496 +Tcl_DeleteCloseHandler(chan, proc, clientData)
1.497 + Tcl_Channel chan; /* The channel for which to cancel the
1.498 + * close callback. */
1.499 + Tcl_CloseProc *proc; /* The procedure for the callback to
1.500 + * remove. */
1.501 + ClientData clientData; /* The callback data for the callback
1.502 + * to remove. */
1.503 +{
1.504 + ChannelState *statePtr;
1.505 + CloseCallback *cbPtr, *cbPrevPtr;
1.506 +
1.507 + statePtr = ((Channel *) chan)->state;
1.508 + for (cbPtr = statePtr->closeCbPtr, cbPrevPtr = (CloseCallback *) NULL;
1.509 + cbPtr != (CloseCallback *) NULL;
1.510 + cbPtr = cbPtr->nextPtr) {
1.511 + if ((cbPtr->proc == proc) && (cbPtr->clientData == clientData)) {
1.512 + if (cbPrevPtr == (CloseCallback *) NULL) {
1.513 + statePtr->closeCbPtr = cbPtr->nextPtr;
1.514 + }
1.515 + ckfree((char *) cbPtr);
1.516 + break;
1.517 + } else {
1.518 + cbPrevPtr = cbPtr;
1.519 + }
1.520 + }
1.521 +}
1.522 +
1.523 +/*
1.524 + *----------------------------------------------------------------------
1.525 + *
1.526 + * GetChannelTable --
1.527 + *
1.528 + * Gets and potentially initializes the channel table for an
1.529 + * interpreter. If it is initializing the table it also inserts
1.530 + * channels for stdin, stdout and stderr if the interpreter is
1.531 + * trusted.
1.532 + *
1.533 + * Results:
1.534 + * A pointer to the hash table created, for use by the caller.
1.535 + *
1.536 + * Side effects:
1.537 + * Initializes the channel table for an interpreter. May create
1.538 + * channels for stdin, stdout and stderr.
1.539 + *
1.540 + *----------------------------------------------------------------------
1.541 + */
1.542 +
1.543 +static Tcl_HashTable *
1.544 +GetChannelTable(interp)
1.545 + Tcl_Interp *interp;
1.546 +{
1.547 + Tcl_HashTable *hTblPtr; /* Hash table of channels. */
1.548 + Tcl_Channel stdinChan, stdoutChan, stderrChan;
1.549 +
1.550 + hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
1.551 + if (hTblPtr == (Tcl_HashTable *) NULL) {
1.552 + hTblPtr = (Tcl_HashTable *) ckalloc((unsigned) sizeof(Tcl_HashTable));
1.553 + Tcl_InitHashTable(hTblPtr, TCL_STRING_KEYS);
1.554 +
1.555 + (void) Tcl_SetAssocData(interp, "tclIO",
1.556 + (Tcl_InterpDeleteProc *) DeleteChannelTable,
1.557 + (ClientData) hTblPtr);
1.558 +
1.559 + /*
1.560 + * If the interpreter is trusted (not "safe"), insert channels
1.561 + * for stdin, stdout and stderr (possibly creating them in the
1.562 + * process).
1.563 + */
1.564 +
1.565 + if (Tcl_IsSafe(interp) == 0) {
1.566 + stdinChan = Tcl_GetStdChannel(TCL_STDIN);
1.567 + if (stdinChan != NULL) {
1.568 + Tcl_RegisterChannel(interp, stdinChan);
1.569 + }
1.570 + stdoutChan = Tcl_GetStdChannel(TCL_STDOUT);
1.571 + if (stdoutChan != NULL) {
1.572 + Tcl_RegisterChannel(interp, stdoutChan);
1.573 + }
1.574 + stderrChan = Tcl_GetStdChannel(TCL_STDERR);
1.575 + if (stderrChan != NULL) {
1.576 + Tcl_RegisterChannel(interp, stderrChan);
1.577 + }
1.578 + }
1.579 +
1.580 + }
1.581 + return hTblPtr;
1.582 +}
1.583 +
1.584 +/*
1.585 + *----------------------------------------------------------------------
1.586 + *
1.587 + * DeleteChannelTable --
1.588 + *
1.589 + * Deletes the channel table for an interpreter, closing any open
1.590 + * channels whose refcount reaches zero. This procedure is invoked
1.591 + * when an interpreter is deleted, via the AssocData cleanup
1.592 + * mechanism.
1.593 + *
1.594 + * Results:
1.595 + * None.
1.596 + *
1.597 + * Side effects:
1.598 + * Deletes the hash table of channels. May close channels. May flush
1.599 + * output on closed channels. Removes any channeEvent handlers that were
1.600 + * registered in this interpreter.
1.601 + *
1.602 + *----------------------------------------------------------------------
1.603 + */
1.604 +
1.605 +static void
1.606 +DeleteChannelTable(clientData, interp)
1.607 + ClientData clientData; /* The per-interpreter data structure. */
1.608 + Tcl_Interp *interp; /* The interpreter being deleted. */
1.609 +{
1.610 + Tcl_HashTable *hTblPtr; /* The hash table. */
1.611 + Tcl_HashSearch hSearch; /* Search variable. */
1.612 + Tcl_HashEntry *hPtr; /* Search variable. */
1.613 + Channel *chanPtr; /* Channel being deleted. */
1.614 + ChannelState *statePtr; /* State of Channel being deleted. */
1.615 + EventScriptRecord *sPtr, *prevPtr, *nextPtr;
1.616 + /* Variables to loop over all channel events
1.617 + * registered, to delete the ones that refer
1.618 + * to the interpreter being deleted. */
1.619 +
1.620 + /*
1.621 + * Delete all the registered channels - this will close channels whose
1.622 + * refcount reaches zero.
1.623 + */
1.624 +
1.625 + hTblPtr = (Tcl_HashTable *) clientData;
1.626 + for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
1.627 + hPtr != (Tcl_HashEntry *) NULL;
1.628 + hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch)) {
1.629 +
1.630 + chanPtr = (Channel *) Tcl_GetHashValue(hPtr);
1.631 + statePtr = chanPtr->state;
1.632 +
1.633 + /*
1.634 + * Remove any fileevents registered in this interpreter.
1.635 + */
1.636 +
1.637 + for (sPtr = statePtr->scriptRecordPtr,
1.638 + prevPtr = (EventScriptRecord *) NULL;
1.639 + sPtr != (EventScriptRecord *) NULL;
1.640 + sPtr = nextPtr) {
1.641 + nextPtr = sPtr->nextPtr;
1.642 + if (sPtr->interp == interp) {
1.643 + if (prevPtr == (EventScriptRecord *) NULL) {
1.644 + statePtr->scriptRecordPtr = nextPtr;
1.645 + } else {
1.646 + prevPtr->nextPtr = nextPtr;
1.647 + }
1.648 +
1.649 + Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
1.650 + TclChannelEventScriptInvoker, (ClientData) sPtr);
1.651 +
1.652 + Tcl_DecrRefCount(sPtr->scriptPtr);
1.653 + ckfree((char *) sPtr);
1.654 + } else {
1.655 + prevPtr = sPtr;
1.656 + }
1.657 + }
1.658 +
1.659 + /*
1.660 + * Cannot call Tcl_UnregisterChannel because that procedure calls
1.661 + * Tcl_GetAssocData to get the channel table, which might already
1.662 + * be inaccessible from the interpreter structure. Instead, we
1.663 + * emulate the behavior of Tcl_UnregisterChannel directly here.
1.664 + */
1.665 +
1.666 + Tcl_DeleteHashEntry(hPtr);
1.667 + statePtr->refCount--;
1.668 + if (statePtr->refCount <= 0) {
1.669 + if (!(statePtr->flags & BG_FLUSH_SCHEDULED)) {
1.670 + (void) Tcl_Close(interp, (Tcl_Channel) chanPtr);
1.671 + }
1.672 + }
1.673 + }
1.674 + Tcl_DeleteHashTable(hTblPtr);
1.675 + ckfree((char *) hTblPtr);
1.676 +}
1.677 +
1.678 +/*
1.679 + *----------------------------------------------------------------------
1.680 + *
1.681 + * CheckForStdChannelsBeingClosed --
1.682 + *
1.683 + * Perform special handling for standard channels being closed. When
1.684 + * given a standard channel, if the refcount is now 1, it means that
1.685 + * the last reference to the standard channel is being explicitly
1.686 + * closed. Now bump the refcount artificially down to 0, to ensure the
1.687 + * normal handling of channels being closed will occur. Also reset the
1.688 + * static pointer to the channel to NULL, to avoid dangling references.
1.689 + *
1.690 + * Results:
1.691 + * None.
1.692 + *
1.693 + * Side effects:
1.694 + * Manipulates the refcount on standard channels. May smash the global
1.695 + * static pointer to a standard channel.
1.696 + *
1.697 + *----------------------------------------------------------------------
1.698 + */
1.699 +
1.700 +static void
1.701 +CheckForStdChannelsBeingClosed(chan)
1.702 + Tcl_Channel chan;
1.703 +{
1.704 + ChannelState *statePtr = ((Channel *) chan)->state;
1.705 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.706 +
1.707 + if ((chan == tsdPtr->stdinChannel) && (tsdPtr->stdinInitialized)) {
1.708 + if (statePtr->refCount < 2) {
1.709 + statePtr->refCount = 0;
1.710 + tsdPtr->stdinChannel = NULL;
1.711 + return;
1.712 + }
1.713 + } else if ((chan == tsdPtr->stdoutChannel)
1.714 + && (tsdPtr->stdoutInitialized)) {
1.715 + if (statePtr->refCount < 2) {
1.716 + statePtr->refCount = 0;
1.717 + tsdPtr->stdoutChannel = NULL;
1.718 + return;
1.719 + }
1.720 + } else if ((chan == tsdPtr->stderrChannel)
1.721 + && (tsdPtr->stderrInitialized)) {
1.722 + if (statePtr->refCount < 2) {
1.723 + statePtr->refCount = 0;
1.724 + tsdPtr->stderrChannel = NULL;
1.725 + return;
1.726 + }
1.727 + }
1.728 +}
1.729 +
1.730 +/*
1.731 + *----------------------------------------------------------------------
1.732 + *
1.733 + * Tcl_IsStandardChannel --
1.734 + *
1.735 + * Test if the given channel is a standard channel. No attempt
1.736 + * is made to check if the channel or the standard channels
1.737 + * are initialized or otherwise valid.
1.738 + *
1.739 + * Results:
1.740 + * Returns 1 if true, 0 if false.
1.741 + *
1.742 + * Side effects:
1.743 + * None.
1.744 + *
1.745 + *----------------------------------------------------------------------
1.746 + */
1.747 +EXPORT_C int
1.748 +Tcl_IsStandardChannel(chan)
1.749 + Tcl_Channel chan; /* Channel to check. */
1.750 +{
1.751 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.752 +
1.753 + if ((chan == tsdPtr->stdinChannel)
1.754 + || (chan == tsdPtr->stdoutChannel)
1.755 + || (chan == tsdPtr->stderrChannel)) {
1.756 + return 1;
1.757 + } else {
1.758 + return 0;
1.759 + }
1.760 +}
1.761 +
1.762 +/*
1.763 + *----------------------------------------------------------------------
1.764 + *
1.765 + * Tcl_RegisterChannel --
1.766 + *
1.767 + * Adds an already-open channel to the channel table of an interpreter.
1.768 + * If the interpreter passed as argument is NULL, it only increments
1.769 + * the channel refCount.
1.770 + *
1.771 + * Results:
1.772 + * None.
1.773 + *
1.774 + * Side effects:
1.775 + * May increment the reference count of a channel.
1.776 + *
1.777 + *----------------------------------------------------------------------
1.778 + */
1.779 +
1.780 +EXPORT_C void
1.781 +Tcl_RegisterChannel(interp, chan)
1.782 + Tcl_Interp *interp; /* Interpreter in which to add the channel. */
1.783 + Tcl_Channel chan; /* The channel to add to this interpreter
1.784 + * channel table. */
1.785 +{
1.786 + Tcl_HashTable *hTblPtr; /* Hash table of channels. */
1.787 + Tcl_HashEntry *hPtr; /* Search variable. */
1.788 + int new; /* Is the hash entry new or does it exist? */
1.789 + Channel *chanPtr; /* The actual channel. */
1.790 + ChannelState *statePtr; /* State of the actual channel. */
1.791 +
1.792 + /*
1.793 + * Always (un)register bottom-most channel in the stack. This makes
1.794 + * management of the channel list easier because no manipulation is
1.795 + * necessary during (un)stack operation.
1.796 + */
1.797 + chanPtr = ((Channel *) chan)->state->bottomChanPtr;
1.798 + statePtr = chanPtr->state;
1.799 +
1.800 + if (statePtr->channelName == (CONST char *) NULL) {
1.801 + panic("Tcl_RegisterChannel: channel without name");
1.802 + }
1.803 + if (interp != (Tcl_Interp *) NULL) {
1.804 + hTblPtr = GetChannelTable(interp);
1.805 + hPtr = Tcl_CreateHashEntry(hTblPtr, statePtr->channelName, &new);
1.806 + if (new == 0) {
1.807 + if (chan == (Tcl_Channel) Tcl_GetHashValue(hPtr)) {
1.808 + return;
1.809 + }
1.810 +
1.811 + panic("Tcl_RegisterChannel: duplicate channel names");
1.812 + }
1.813 + Tcl_SetHashValue(hPtr, (ClientData) chanPtr);
1.814 + }
1.815 +
1.816 + statePtr->refCount++;
1.817 +}
1.818 +
1.819 +/*
1.820 + *----------------------------------------------------------------------
1.821 + *
1.822 + * Tcl_UnregisterChannel --
1.823 + *
1.824 + * Deletes the hash entry for a channel associated with an interpreter.
1.825 + * If the interpreter given as argument is NULL, it only decrements the
1.826 + * reference count. (This all happens in the Tcl_DetachChannel helper
1.827 + * function).
1.828 + *
1.829 + * Finally, if the reference count of the channel drops to zero,
1.830 + * it is deleted.
1.831 + *
1.832 + * Results:
1.833 + * A standard Tcl result.
1.834 + *
1.835 + * Side effects:
1.836 + * Calls Tcl_DetachChannel which deletes the hash entry for a channel
1.837 + * associated with an interpreter.
1.838 + *
1.839 + * May delete the channel, which can have a variety of consequences,
1.840 + * especially if we are forced to close the channel.
1.841 + *
1.842 + *----------------------------------------------------------------------
1.843 + */
1.844 +
1.845 +EXPORT_C int
1.846 +Tcl_UnregisterChannel(interp, chan)
1.847 + Tcl_Interp *interp; /* Interpreter in which channel is defined. */
1.848 + Tcl_Channel chan; /* Channel to delete. */
1.849 +{
1.850 + ChannelState *statePtr; /* State of the real channel. */
1.851 +
1.852 + statePtr = ((Channel *) chan)->state->bottomChanPtr->state;
1.853 +
1.854 + if (statePtr->flags & CHANNEL_INCLOSE) {
1.855 + if (interp != (Tcl_Interp*) NULL) {
1.856 + Tcl_AppendResult(interp,
1.857 + "Illegal recursive call to close through close-handler of channel",
1.858 + (char *) NULL);
1.859 + }
1.860 + return TCL_ERROR;
1.861 + }
1.862 +
1.863 + if (DetachChannel(interp, chan) != TCL_OK) {
1.864 + return TCL_OK;
1.865 + }
1.866 +
1.867 + statePtr = ((Channel *) chan)->state->bottomChanPtr->state;
1.868 +
1.869 + /*
1.870 + * Perform special handling for standard channels being closed. If the
1.871 + * refCount is now 1 it means that the last reference to the standard
1.872 + * channel is being explicitly closed, so bump the refCount down
1.873 + * artificially to 0. This will ensure that the channel is actually
1.874 + * closed, below. Also set the static pointer to NULL for the channel.
1.875 + */
1.876 +
1.877 + CheckForStdChannelsBeingClosed(chan);
1.878 +
1.879 + /*
1.880 + * If the refCount reached zero, close the actual channel.
1.881 + */
1.882 +
1.883 + if (statePtr->refCount <= 0) {
1.884 +
1.885 + /*
1.886 + * Ensure that if there is another buffer, it gets flushed
1.887 + * whether or not we are doing a background flush.
1.888 + */
1.889 +
1.890 + if ((statePtr->curOutPtr != NULL) &&
1.891 + (statePtr->curOutPtr->nextAdded >
1.892 + statePtr->curOutPtr->nextRemoved)) {
1.893 + statePtr->flags |= BUFFER_READY;
1.894 + }
1.895 + Tcl_Preserve((ClientData)statePtr);
1.896 + if (!(statePtr->flags & BG_FLUSH_SCHEDULED)) {
1.897 + /* We don't want to re-enter Tcl_Close */
1.898 + if (!(statePtr->flags & CHANNEL_CLOSED)) {
1.899 + if (Tcl_Close(interp, chan) != TCL_OK) {
1.900 + statePtr->flags |= CHANNEL_CLOSED;
1.901 + Tcl_Release((ClientData)statePtr);
1.902 + return TCL_ERROR;
1.903 + }
1.904 + }
1.905 + }
1.906 + statePtr->flags |= CHANNEL_CLOSED;
1.907 + Tcl_Release((ClientData)statePtr);
1.908 + }
1.909 + return TCL_OK;
1.910 +}
1.911 +
1.912 +/*
1.913 + *----------------------------------------------------------------------
1.914 + *
1.915 + * Tcl_DetachChannel --
1.916 + *
1.917 + * Deletes the hash entry for a channel associated with an interpreter.
1.918 + * If the interpreter given as argument is NULL, it only decrements the
1.919 + * reference count. Even if the ref count drops to zero, the
1.920 + * channel is NOT closed or cleaned up. This allows a channel to
1.921 + * be detached from an interpreter and left in the same state it
1.922 + * was in when it was originally returned by 'Tcl_OpenFileChannel',
1.923 + * for example.
1.924 + *
1.925 + * This function cannot be used on the standard channels, and
1.926 + * will return TCL_ERROR if that is attempted.
1.927 + *
1.928 + * This function should only be necessary for special purposes
1.929 + * in which you need to generate a pristine channel from one
1.930 + * that has already been used. All ordinary purposes will almost
1.931 + * always want to use Tcl_UnregisterChannel instead.
1.932 + *
1.933 + * Provided the channel is not attached to any other interpreter,
1.934 + * it can then be closed with Tcl_Close, rather than with
1.935 + * Tcl_UnregisterChannel.
1.936 + *
1.937 + * Results:
1.938 + * A standard Tcl result. If the channel is not currently registered
1.939 + * with the given interpreter, TCL_ERROR is returned, otherwise
1.940 + * TCL_OK. However no error messages are left in the interp's result.
1.941 + *
1.942 + * Side effects:
1.943 + * Deletes the hash entry for a channel associated with an
1.944 + * interpreter.
1.945 + *
1.946 + *----------------------------------------------------------------------
1.947 + */
1.948 +
1.949 +EXPORT_C int
1.950 +Tcl_DetachChannel(interp, chan)
1.951 + Tcl_Interp *interp; /* Interpreter in which channel is defined. */
1.952 + Tcl_Channel chan; /* Channel to delete. */
1.953 +{
1.954 + if (Tcl_IsStandardChannel(chan)) {
1.955 + return TCL_ERROR;
1.956 + }
1.957 +
1.958 + return DetachChannel(interp, chan);
1.959 +}
1.960 +
1.961 +/*
1.962 + *----------------------------------------------------------------------
1.963 + *
1.964 + * DetachChannel --
1.965 + *
1.966 + * Deletes the hash entry for a channel associated with an interpreter.
1.967 + * If the interpreter given as argument is NULL, it only decrements the
1.968 + * reference count. Even if the ref count drops to zero, the
1.969 + * channel is NOT closed or cleaned up. This allows a channel to
1.970 + * be detached from an interpreter and left in the same state it
1.971 + * was in when it was originally returned by 'Tcl_OpenFileChannel',
1.972 + * for example.
1.973 + *
1.974 + * Results:
1.975 + * A standard Tcl result. If the channel is not currently registered
1.976 + * with the given interpreter, TCL_ERROR is returned, otherwise
1.977 + * TCL_OK. However no error messages are left in the interp's result.
1.978 + *
1.979 + * Side effects:
1.980 + * Deletes the hash entry for a channel associated with an
1.981 + * interpreter.
1.982 + *
1.983 + *----------------------------------------------------------------------
1.984 + */
1.985 +
1.986 +static int
1.987 +DetachChannel(interp, chan)
1.988 + Tcl_Interp *interp; /* Interpreter in which channel is defined. */
1.989 + Tcl_Channel chan; /* Channel to delete. */
1.990 +{
1.991 + Tcl_HashTable *hTblPtr; /* Hash table of channels. */
1.992 + Tcl_HashEntry *hPtr; /* Search variable. */
1.993 + Channel *chanPtr; /* The real IO channel. */
1.994 + ChannelState *statePtr; /* State of the real channel. */
1.995 +
1.996 + /*
1.997 + * Always (un)register bottom-most channel in the stack. This makes
1.998 + * management of the channel list easier because no manipulation is
1.999 + * necessary during (un)stack operation.
1.1000 + */
1.1001 + chanPtr = ((Channel *) chan)->state->bottomChanPtr;
1.1002 + statePtr = chanPtr->state;
1.1003 +
1.1004 + if (interp != (Tcl_Interp *) NULL) {
1.1005 + hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
1.1006 + if (hTblPtr == (Tcl_HashTable *) NULL) {
1.1007 + return TCL_ERROR;
1.1008 + }
1.1009 + hPtr = Tcl_FindHashEntry(hTblPtr, statePtr->channelName);
1.1010 + if (hPtr == (Tcl_HashEntry *) NULL) {
1.1011 + return TCL_ERROR;
1.1012 + }
1.1013 + if ((Channel *) Tcl_GetHashValue(hPtr) != chanPtr) {
1.1014 + return TCL_ERROR;
1.1015 + }
1.1016 + Tcl_DeleteHashEntry(hPtr);
1.1017 +
1.1018 + /*
1.1019 + * Remove channel handlers that refer to this interpreter, so that they
1.1020 + * will not be present if the actual close is delayed and more events
1.1021 + * happen on the channel. This may occur if the channel is shared
1.1022 + * between several interpreters, or if the channel has async
1.1023 + * flushing active.
1.1024 + */
1.1025 +
1.1026 + CleanupChannelHandlers(interp, chanPtr);
1.1027 + }
1.1028 +
1.1029 + statePtr->refCount--;
1.1030 +
1.1031 + return TCL_OK;
1.1032 +}
1.1033 +
1.1034 +
1.1035 +/*
1.1036 + *---------------------------------------------------------------------------
1.1037 + *
1.1038 + * Tcl_GetChannel --
1.1039 + *
1.1040 + * Finds an existing Tcl_Channel structure by name in a given
1.1041 + * interpreter. This function is public because it is used by
1.1042 + * channel-type-specific functions.
1.1043 + *
1.1044 + * Results:
1.1045 + * A Tcl_Channel or NULL on failure. If failed, interp's result
1.1046 + * object contains an error message. *modePtr is filled with the
1.1047 + * modes in which the channel was opened.
1.1048 + *
1.1049 + * Side effects:
1.1050 + * None.
1.1051 + *
1.1052 + *---------------------------------------------------------------------------
1.1053 + */
1.1054 +
1.1055 +EXPORT_C Tcl_Channel
1.1056 +Tcl_GetChannel(interp, chanName, modePtr)
1.1057 + Tcl_Interp *interp; /* Interpreter in which to find or create
1.1058 + * the channel. */
1.1059 + CONST char *chanName; /* The name of the channel. */
1.1060 + int *modePtr; /* Where to store the mode in which the
1.1061 + * channel was opened? Will contain an ORed
1.1062 + * combination of TCL_READABLE and
1.1063 + * TCL_WRITABLE, if non-NULL. */
1.1064 +{
1.1065 + Channel *chanPtr; /* The actual channel. */
1.1066 + Tcl_HashTable *hTblPtr; /* Hash table of channels. */
1.1067 + Tcl_HashEntry *hPtr; /* Search variable. */
1.1068 + CONST char *name; /* Translated name. */
1.1069 +
1.1070 + /*
1.1071 + * Substitute "stdin", etc. Note that even though we immediately
1.1072 + * find the channel using Tcl_GetStdChannel, we still need to look
1.1073 + * it up in the specified interpreter to ensure that it is present
1.1074 + * in the channel table. Otherwise, safe interpreters would always
1.1075 + * have access to the standard channels.
1.1076 + */
1.1077 +
1.1078 + name = chanName;
1.1079 + if ((chanName[0] == 's') && (chanName[1] == 't')) {
1.1080 + chanPtr = NULL;
1.1081 + if (strcmp(chanName, "stdin") == 0) {
1.1082 + chanPtr = (Channel *) Tcl_GetStdChannel(TCL_STDIN);
1.1083 + } else if (strcmp(chanName, "stdout") == 0) {
1.1084 + chanPtr = (Channel *) Tcl_GetStdChannel(TCL_STDOUT);
1.1085 + } else if (strcmp(chanName, "stderr") == 0) {
1.1086 + chanPtr = (Channel *) Tcl_GetStdChannel(TCL_STDERR);
1.1087 + }
1.1088 + if (chanPtr != NULL) {
1.1089 + name = chanPtr->state->channelName;
1.1090 + }
1.1091 + }
1.1092 +
1.1093 + hTblPtr = GetChannelTable(interp);
1.1094 + hPtr = Tcl_FindHashEntry(hTblPtr, name);
1.1095 + if (hPtr == (Tcl_HashEntry *) NULL) {
1.1096 + Tcl_AppendResult(interp, "can not find channel named \"",
1.1097 + chanName, "\"", (char *) NULL);
1.1098 + return NULL;
1.1099 + }
1.1100 +
1.1101 + /*
1.1102 + * Always return bottom-most channel in the stack. This one lives
1.1103 + * the longest - other channels may go away unnoticed.
1.1104 + * The other APIs compensate where necessary to retrieve the
1.1105 + * topmost channel again.
1.1106 + */
1.1107 + chanPtr = (Channel *) Tcl_GetHashValue(hPtr);
1.1108 + chanPtr = chanPtr->state->bottomChanPtr;
1.1109 + if (modePtr != NULL) {
1.1110 + *modePtr = (chanPtr->state->flags & (TCL_READABLE|TCL_WRITABLE));
1.1111 + }
1.1112 +
1.1113 + return (Tcl_Channel) chanPtr;
1.1114 +}
1.1115 +
1.1116 +/*
1.1117 + *----------------------------------------------------------------------
1.1118 + *
1.1119 + * Tcl_CreateChannel --
1.1120 + *
1.1121 + * Creates a new entry in the hash table for a Tcl_Channel
1.1122 + * record.
1.1123 + *
1.1124 + * Results:
1.1125 + * Returns the new Tcl_Channel.
1.1126 + *
1.1127 + * Side effects:
1.1128 + * Creates a new Tcl_Channel instance and inserts it into the
1.1129 + * hash table.
1.1130 + *
1.1131 + *----------------------------------------------------------------------
1.1132 + */
1.1133 +
1.1134 +EXPORT_C Tcl_Channel
1.1135 +Tcl_CreateChannel(typePtr, chanName, instanceData, mask)
1.1136 + Tcl_ChannelType *typePtr; /* The channel type record. */
1.1137 + CONST char *chanName; /* Name of channel to record. */
1.1138 + ClientData instanceData; /* Instance specific data. */
1.1139 + int mask; /* TCL_READABLE & TCL_WRITABLE to indicate
1.1140 + * if the channel is readable, writable. */
1.1141 +{
1.1142 + Channel *chanPtr; /* The channel structure newly created. */
1.1143 + ChannelState *statePtr; /* The stack-level independent state info
1.1144 + * for the channel. */
1.1145 + CONST char *name;
1.1146 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.1147 +
1.1148 + /*
1.1149 + * With the change of the Tcl_ChannelType structure to use a version in
1.1150 + * 8.3.2+, we have to make sure that our assumption that the structure
1.1151 + * remains a binary compatible size is true.
1.1152 + *
1.1153 + * If this assertion fails on some system, then it can be removed
1.1154 + * only if the user recompiles code with older channel drivers in
1.1155 + * the new system as well.
1.1156 + */
1.1157 +
1.1158 + assert(sizeof(Tcl_ChannelTypeVersion) == sizeof(Tcl_DriverBlockModeProc*));
1.1159 +
1.1160 + /*
1.1161 + * JH: We could subsequently memset these to 0 to avoid the
1.1162 + * numerous assignments to 0/NULL below.
1.1163 + */
1.1164 + chanPtr = (Channel *) ckalloc((unsigned) sizeof(Channel));
1.1165 + statePtr = (ChannelState *) ckalloc((unsigned) sizeof(ChannelState));
1.1166 + chanPtr->state = statePtr;
1.1167 +
1.1168 + chanPtr->instanceData = instanceData;
1.1169 + chanPtr->typePtr = typePtr;
1.1170 +
1.1171 + /*
1.1172 + * Set all the bits that are part of the stack-independent state
1.1173 + * information for the channel.
1.1174 + */
1.1175 +
1.1176 + if (chanName != (char *) NULL) {
1.1177 + char *tmp = ckalloc((unsigned) (strlen(chanName) + 1));
1.1178 + statePtr->channelName = tmp;
1.1179 + strcpy(tmp, chanName);
1.1180 + } else {
1.1181 + panic("Tcl_CreateChannel: NULL channel name");
1.1182 + }
1.1183 +
1.1184 + statePtr->flags = mask;
1.1185 +
1.1186 + /*
1.1187 + * Set the channel to system default encoding.
1.1188 + */
1.1189 +
1.1190 + statePtr->encoding = NULL;
1.1191 + name = Tcl_GetEncodingName(NULL);
1.1192 + if (strcmp(name, "binary") != 0) {
1.1193 + statePtr->encoding = Tcl_GetEncoding(NULL, name);
1.1194 + }
1.1195 + statePtr->inputEncodingState = NULL;
1.1196 + statePtr->inputEncodingFlags = TCL_ENCODING_START;
1.1197 + statePtr->outputEncodingState = NULL;
1.1198 + statePtr->outputEncodingFlags = TCL_ENCODING_START;
1.1199 +
1.1200 + /*
1.1201 + * Set the channel up initially in AUTO input translation mode to
1.1202 + * accept "\n", "\r" and "\r\n". Output translation mode is set to
1.1203 + * a platform specific default value. The eofChar is set to 0 for both
1.1204 + * input and output, so that Tcl does not look for an in-file EOF
1.1205 + * indicator (e.g. ^Z) and does not append an EOF indicator to files.
1.1206 + */
1.1207 +
1.1208 + statePtr->inputTranslation = TCL_TRANSLATE_AUTO;
1.1209 + statePtr->outputTranslation = TCL_PLATFORM_TRANSLATION;
1.1210 + statePtr->inEofChar = 0;
1.1211 + statePtr->outEofChar = 0;
1.1212 +
1.1213 + statePtr->unreportedError = 0;
1.1214 + statePtr->refCount = 0;
1.1215 + statePtr->closeCbPtr = (CloseCallback *) NULL;
1.1216 + statePtr->curOutPtr = (ChannelBuffer *) NULL;
1.1217 + statePtr->outQueueHead = (ChannelBuffer *) NULL;
1.1218 + statePtr->outQueueTail = (ChannelBuffer *) NULL;
1.1219 + statePtr->saveInBufPtr = (ChannelBuffer *) NULL;
1.1220 + statePtr->inQueueHead = (ChannelBuffer *) NULL;
1.1221 + statePtr->inQueueTail = (ChannelBuffer *) NULL;
1.1222 + statePtr->chPtr = (ChannelHandler *) NULL;
1.1223 + statePtr->interestMask = 0;
1.1224 + statePtr->scriptRecordPtr = (EventScriptRecord *) NULL;
1.1225 + statePtr->bufSize = CHANNELBUFFER_DEFAULT_SIZE;
1.1226 + statePtr->timer = NULL;
1.1227 + statePtr->csPtr = NULL;
1.1228 +
1.1229 + statePtr->outputStage = NULL;
1.1230 + if ((statePtr->encoding != NULL) && (statePtr->flags & TCL_WRITABLE)) {
1.1231 + statePtr->outputStage = (char *)
1.1232 + ckalloc((unsigned) (statePtr->bufSize + 2));
1.1233 + }
1.1234 +
1.1235 + /*
1.1236 + * As we are creating the channel, it is obviously the top for now
1.1237 + */
1.1238 + statePtr->topChanPtr = chanPtr;
1.1239 + statePtr->bottomChanPtr = chanPtr;
1.1240 + chanPtr->downChanPtr = (Channel *) NULL;
1.1241 + chanPtr->upChanPtr = (Channel *) NULL;
1.1242 + chanPtr->inQueueHead = (ChannelBuffer*) NULL;
1.1243 + chanPtr->inQueueTail = (ChannelBuffer*) NULL;
1.1244 +
1.1245 + /*
1.1246 + * Link the channel into the list of all channels; create an on-exit
1.1247 + * handler if there is not one already, to close off all the channels
1.1248 + * in the list on exit.
1.1249 + *
1.1250 + * JH: Could call Tcl_SpliceChannel, but need to avoid NULL check.
1.1251 + *
1.1252 + * TIP #218.
1.1253 + * AK: Just initialize the field to NULL before invoking Tcl_SpliceChannel
1.1254 + * We need Tcl_SpliceChannel, for the threadAction calls.
1.1255 + * There is no real reason to duplicate all of this.
1.1256 + * NOTE: All drivers using thread actions now have to perform their TSD
1.1257 + * manipulation only in their thread action proc. Doing it when
1.1258 + * creating their instance structures will collide with the thread
1.1259 + * action activity and lead to damaged lists.
1.1260 + */
1.1261 +
1.1262 + statePtr->nextCSPtr = (ChannelState *) NULL;
1.1263 + Tcl_SpliceChannel ((Tcl_Channel) chanPtr);
1.1264 +
1.1265 + /*
1.1266 + * Install this channel in the first empty standard channel slot, if
1.1267 + * the channel was previously closed explicitly.
1.1268 + */
1.1269 +#if TCL_INHERIT_STD_CHANNELS
1.1270 + if ((tsdPtr->stdinChannel == NULL) &&
1.1271 + (tsdPtr->stdinInitialized == 1)) {
1.1272 + Tcl_SetStdChannel((Tcl_Channel) chanPtr, TCL_STDIN);
1.1273 + Tcl_RegisterChannel((Tcl_Interp *) NULL, (Tcl_Channel) chanPtr);
1.1274 + } else if ((tsdPtr->stdoutChannel == NULL) &&
1.1275 + (tsdPtr->stdoutInitialized == 1)) {
1.1276 + Tcl_SetStdChannel((Tcl_Channel) chanPtr, TCL_STDOUT);
1.1277 + Tcl_RegisterChannel((Tcl_Interp *) NULL, (Tcl_Channel) chanPtr);
1.1278 + } else if ((tsdPtr->stderrChannel == NULL) &&
1.1279 + (tsdPtr->stderrInitialized == 1)) {
1.1280 + Tcl_SetStdChannel((Tcl_Channel) chanPtr, TCL_STDERR);
1.1281 + Tcl_RegisterChannel((Tcl_Interp *) NULL, (Tcl_Channel) chanPtr);
1.1282 + }
1.1283 +#endif
1.1284 + return (Tcl_Channel) chanPtr;
1.1285 +}
1.1286 +
1.1287 +/*
1.1288 + *----------------------------------------------------------------------
1.1289 + *
1.1290 + * Tcl_StackChannel --
1.1291 + *
1.1292 + * Replaces an entry in the hash table for a Tcl_Channel
1.1293 + * record. The replacement is a new channel with same name,
1.1294 + * it supercedes the replaced channel. Input and output of
1.1295 + * the superceded channel is now going through the newly
1.1296 + * created channel and allows the arbitrary filtering/manipulation
1.1297 + * of the dataflow.
1.1298 + *
1.1299 + * Andreas Kupries <a.kupries@westend.com>, 12/13/1998
1.1300 + * "Trf-Patch for filtering channels"
1.1301 + *
1.1302 + * Results:
1.1303 + * Returns the new Tcl_Channel, which actually contains the
1.1304 + * saved information about prevChan.
1.1305 + *
1.1306 + * Side effects:
1.1307 + * A new channel structure is allocated and linked below
1.1308 + * the existing channel. The channel operations and client
1.1309 + * data of the existing channel are copied down to the newly
1.1310 + * created channel, and the current channel has its operations
1.1311 + * replaced by the new typePtr.
1.1312 + *
1.1313 + *----------------------------------------------------------------------
1.1314 + */
1.1315 +
1.1316 +EXPORT_C Tcl_Channel
1.1317 +Tcl_StackChannel(interp, typePtr, instanceData, mask, prevChan)
1.1318 + Tcl_Interp *interp; /* The interpreter we are working in */
1.1319 + Tcl_ChannelType *typePtr; /* The channel type record for the new
1.1320 + * channel. */
1.1321 + ClientData instanceData; /* Instance specific data for the new
1.1322 + * channel. */
1.1323 + int mask; /* TCL_READABLE & TCL_WRITABLE to indicate
1.1324 + * if the channel is readable, writable. */
1.1325 + Tcl_Channel prevChan; /* The channel structure to replace */
1.1326 +{
1.1327 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.1328 + Channel *chanPtr, *prevChanPtr;
1.1329 + ChannelState *statePtr;
1.1330 +
1.1331 + /*
1.1332 + * Find the given channel in the list of all channels.
1.1333 + * If we don't find it, then it was never registered correctly.
1.1334 + *
1.1335 + * This operation should occur at the top of a channel stack.
1.1336 + */
1.1337 +
1.1338 + statePtr = (ChannelState *) tsdPtr->firstCSPtr;
1.1339 + prevChanPtr = ((Channel *) prevChan)->state->topChanPtr;
1.1340 +
1.1341 + while ((statePtr != NULL) && (statePtr->topChanPtr != prevChanPtr)) {
1.1342 + statePtr = statePtr->nextCSPtr;
1.1343 + }
1.1344 +
1.1345 + if (statePtr == NULL) {
1.1346 + if (interp) {
1.1347 + Tcl_AppendResult(interp, "couldn't find state for channel \"",
1.1348 + Tcl_GetChannelName(prevChan), "\"", (char *) NULL);
1.1349 + }
1.1350 + return (Tcl_Channel) NULL;
1.1351 + }
1.1352 +
1.1353 + /*
1.1354 + * Here we check if the given "mask" matches the "flags"
1.1355 + * of the already existing channel.
1.1356 + *
1.1357 + * | - | R | W | RW |
1.1358 + * --+---+---+---+----+ <=> 0 != (chan->mask & prevChan->mask)
1.1359 + * - | | | | |
1.1360 + * R | | + | | + | The superceding channel is allowed to
1.1361 + * W | | | + | + | restrict the capabilities of the
1.1362 + * RW| | + | + | + | superceded one !
1.1363 + * --+---+---+---+----+
1.1364 + */
1.1365 +
1.1366 + if ((mask & (statePtr->flags & (TCL_READABLE | TCL_WRITABLE))) == 0) {
1.1367 + if (interp) {
1.1368 + Tcl_AppendResult(interp,
1.1369 + "reading and writing both disallowed for channel \"",
1.1370 + Tcl_GetChannelName(prevChan), "\"", (char *) NULL);
1.1371 + }
1.1372 + return (Tcl_Channel) NULL;
1.1373 + }
1.1374 +
1.1375 + /*
1.1376 + * Flush the buffers. This ensures that any data still in them
1.1377 + * at this time is not handled by the new transformation. Restrict
1.1378 + * this to writable channels. Take care to hide a possible bg-copy
1.1379 + * in progress from Tcl_Flush and the CheckForChannelErrors inside.
1.1380 + */
1.1381 +
1.1382 + if ((mask & TCL_WRITABLE) != 0) {
1.1383 + CopyState *csPtr;
1.1384 +
1.1385 + csPtr = statePtr->csPtr;
1.1386 + statePtr->csPtr = (CopyState*) NULL;
1.1387 +
1.1388 + if (Tcl_Flush((Tcl_Channel) prevChanPtr) != TCL_OK) {
1.1389 + statePtr->csPtr = csPtr;
1.1390 + if (interp) {
1.1391 + Tcl_AppendResult(interp, "could not flush channel \"",
1.1392 + Tcl_GetChannelName(prevChan), "\"", (char *) NULL);
1.1393 + }
1.1394 + return (Tcl_Channel) NULL;
1.1395 + }
1.1396 +
1.1397 + statePtr->csPtr = csPtr;
1.1398 + }
1.1399 + /*
1.1400 + * Discard any input in the buffers. They are not yet read by the
1.1401 + * user of the channel, so they have to go through the new
1.1402 + * transformation before reading. As the buffers contain the
1.1403 + * untransformed form their contents are not only useless but actually
1.1404 + * distorts our view of the system.
1.1405 + *
1.1406 + * To preserve the information without having to read them again and
1.1407 + * to avoid problems with the location in the channel (seeking might
1.1408 + * be impossible) we move the buffers from the common state structure
1.1409 + * into the channel itself. We use the buffers in the channel below
1.1410 + * the new transformation to hold the data. In the future this allows
1.1411 + * us to write transformations which pre-read data and push the unused
1.1412 + * part back when they are going away.
1.1413 + */
1.1414 +
1.1415 + if (((mask & TCL_READABLE) != 0) &&
1.1416 + (statePtr->inQueueHead != (ChannelBuffer*) NULL)) {
1.1417 + /*
1.1418 + * Remark: It is possible that the channel buffers contain data from
1.1419 + * some earlier push-backs.
1.1420 + */
1.1421 +
1.1422 + statePtr->inQueueTail->nextPtr = prevChanPtr->inQueueHead;
1.1423 + prevChanPtr->inQueueHead = statePtr->inQueueHead;
1.1424 +
1.1425 + if (prevChanPtr->inQueueTail == (ChannelBuffer*) NULL) {
1.1426 + prevChanPtr->inQueueTail = statePtr->inQueueTail;
1.1427 + }
1.1428 +
1.1429 + statePtr->inQueueHead = (ChannelBuffer*) NULL;
1.1430 + statePtr->inQueueTail = (ChannelBuffer*) NULL;
1.1431 + }
1.1432 +
1.1433 + chanPtr = (Channel *) ckalloc((unsigned) sizeof(Channel));
1.1434 +
1.1435 + /*
1.1436 + * Save some of the current state into the new structure,
1.1437 + * reinitialize the parts which will stay with the transformation.
1.1438 + *
1.1439 + * Remarks:
1.1440 + */
1.1441 +
1.1442 + chanPtr->state = statePtr;
1.1443 + chanPtr->instanceData = instanceData;
1.1444 + chanPtr->typePtr = typePtr;
1.1445 + chanPtr->downChanPtr = prevChanPtr;
1.1446 + chanPtr->upChanPtr = (Channel *) NULL;
1.1447 + chanPtr->inQueueHead = (ChannelBuffer*) NULL;
1.1448 + chanPtr->inQueueTail = (ChannelBuffer*) NULL;
1.1449 +
1.1450 + /*
1.1451 + * Place new block at the head of a possibly existing list of previously
1.1452 + * stacked channels.
1.1453 + */
1.1454 +
1.1455 + prevChanPtr->upChanPtr = chanPtr;
1.1456 + statePtr->topChanPtr = chanPtr;
1.1457 +
1.1458 + return (Tcl_Channel) chanPtr;
1.1459 +}
1.1460 +
1.1461 +/*
1.1462 + *----------------------------------------------------------------------
1.1463 + *
1.1464 + * Tcl_UnstackChannel --
1.1465 + *
1.1466 + * Unstacks an entry in the hash table for a Tcl_Channel
1.1467 + * record. This is the reverse to 'Tcl_StackChannel'.
1.1468 + *
1.1469 + * Results:
1.1470 + * A standard Tcl result.
1.1471 + *
1.1472 + * Side effects:
1.1473 + * If TCL_ERROR is returned, the posix error code will be set
1.1474 + * with Tcl_SetErrno.
1.1475 + *
1.1476 + *----------------------------------------------------------------------
1.1477 + */
1.1478 +
1.1479 +EXPORT_C int
1.1480 +Tcl_UnstackChannel (interp, chan)
1.1481 + Tcl_Interp *interp; /* The interpreter we are working in */
1.1482 + Tcl_Channel chan; /* The channel to unstack */
1.1483 +{
1.1484 + Channel *chanPtr = (Channel *) chan;
1.1485 + ChannelState *statePtr = chanPtr->state;
1.1486 + int result = 0;
1.1487 +
1.1488 + /*
1.1489 + * This operation should occur at the top of a channel stack.
1.1490 + */
1.1491 +
1.1492 + chanPtr = statePtr->topChanPtr;
1.1493 +
1.1494 + if (chanPtr->downChanPtr != (Channel *) NULL) {
1.1495 + /*
1.1496 + * Instead of manipulating the per-thread / per-interp list/hashtable
1.1497 + * of registered channels we wind down the state of the transformation,
1.1498 + * and then restore the state of underlying channel into the old
1.1499 + * structure.
1.1500 + */
1.1501 + Channel *downChanPtr = chanPtr->downChanPtr;
1.1502 +
1.1503 + /*
1.1504 + * Flush the buffers. This ensures that any data still in them
1.1505 + * at this time _is_ handled by the transformation we are unstacking
1.1506 + * right now. Restrict this to writable channels. Take care to hide
1.1507 + * a possible bg-copy in progress from Tcl_Flush and the
1.1508 + * CheckForChannelErrors inside.
1.1509 + */
1.1510 +
1.1511 + if (statePtr->flags & TCL_WRITABLE) {
1.1512 + CopyState* csPtr;
1.1513 +
1.1514 + csPtr = statePtr->csPtr;
1.1515 + statePtr->csPtr = (CopyState*) NULL;
1.1516 +
1.1517 + if (Tcl_Flush((Tcl_Channel) chanPtr) != TCL_OK) {
1.1518 + statePtr->csPtr = csPtr;
1.1519 + if (interp) {
1.1520 + Tcl_AppendResult(interp, "could not flush channel \"",
1.1521 + Tcl_GetChannelName((Tcl_Channel) chanPtr), "\"",
1.1522 + (char *) NULL);
1.1523 + }
1.1524 + return TCL_ERROR;
1.1525 + }
1.1526 +
1.1527 + statePtr->csPtr = csPtr;
1.1528 + }
1.1529 +
1.1530 + /*
1.1531 + * Anything in the input queue and the push-back buffers of
1.1532 + * the transformation going away is transformed data, but not
1.1533 + * yet read. As unstacking means that the caller does not want
1.1534 + * to see transformed data any more we have to discard these
1.1535 + * bytes. To avoid writing an analogue to 'DiscardInputQueued'
1.1536 + * we move the information in the push back buffers to the
1.1537 + * input queue and then call 'DiscardInputQueued' on that.
1.1538 + */
1.1539 +
1.1540 + if (((statePtr->flags & TCL_READABLE) != 0) &&
1.1541 + ((statePtr->inQueueHead != (ChannelBuffer*) NULL) ||
1.1542 + (chanPtr->inQueueHead != (ChannelBuffer*) NULL))) {
1.1543 +
1.1544 + if ((statePtr->inQueueHead != (ChannelBuffer*) NULL) &&
1.1545 + (chanPtr->inQueueHead != (ChannelBuffer*) NULL)) {
1.1546 + statePtr->inQueueTail->nextPtr = chanPtr->inQueueHead;
1.1547 + statePtr->inQueueTail = chanPtr->inQueueTail;
1.1548 + statePtr->inQueueHead = statePtr->inQueueTail;
1.1549 +
1.1550 + } else if (chanPtr->inQueueHead != (ChannelBuffer*) NULL) {
1.1551 + statePtr->inQueueHead = chanPtr->inQueueHead;
1.1552 + statePtr->inQueueTail = chanPtr->inQueueTail;
1.1553 + }
1.1554 +
1.1555 + chanPtr->inQueueHead = (ChannelBuffer*) NULL;
1.1556 + chanPtr->inQueueTail = (ChannelBuffer*) NULL;
1.1557 +
1.1558 + DiscardInputQueued (statePtr, 0);
1.1559 + }
1.1560 +
1.1561 + statePtr->topChanPtr = downChanPtr;
1.1562 + downChanPtr->upChanPtr = (Channel *) NULL;
1.1563 +
1.1564 + /*
1.1565 + * Leave this link intact for closeproc
1.1566 + * chanPtr->downChanPtr = (Channel *) NULL;
1.1567 + */
1.1568 +
1.1569 + /*
1.1570 + * Close and free the channel driver state.
1.1571 + */
1.1572 +
1.1573 + if (chanPtr->typePtr->closeProc != TCL_CLOSE2PROC) {
1.1574 + result = (chanPtr->typePtr->closeProc)(chanPtr->instanceData,
1.1575 + interp);
1.1576 + } else {
1.1577 + result = (chanPtr->typePtr->close2Proc)(chanPtr->instanceData,
1.1578 + interp, 0);
1.1579 + }
1.1580 +
1.1581 + chanPtr->typePtr = NULL;
1.1582 + /*
1.1583 + * AK: Tcl_NotifyChannel may hold a reference to this block of memory
1.1584 + */
1.1585 + Tcl_EventuallyFree((ClientData) chanPtr, TCL_DYNAMIC);
1.1586 + UpdateInterest(downChanPtr);
1.1587 +
1.1588 + if (result != 0) {
1.1589 + Tcl_SetErrno(result);
1.1590 + return TCL_ERROR;
1.1591 + }
1.1592 + } else {
1.1593 + /*
1.1594 + * This channel does not cover another one.
1.1595 + * Simply do a close, if necessary.
1.1596 + */
1.1597 +
1.1598 + if (statePtr->refCount <= 0) {
1.1599 + if (Tcl_Close(interp, chan) != TCL_OK) {
1.1600 + return TCL_ERROR;
1.1601 + }
1.1602 + }
1.1603 + }
1.1604 +
1.1605 + return TCL_OK;
1.1606 +}
1.1607 +
1.1608 +/*
1.1609 + *----------------------------------------------------------------------
1.1610 + *
1.1611 + * Tcl_GetStackedChannel --
1.1612 + *
1.1613 + * Determines whether the specified channel is stacked upon another.
1.1614 + *
1.1615 + * Results:
1.1616 + * NULL if the channel is not stacked upon another one, or a reference
1.1617 + * to the channel it is stacked upon. This reference can be used in
1.1618 + * queries, but modification is not allowed.
1.1619 + *
1.1620 + * Side effects:
1.1621 + * None.
1.1622 + *
1.1623 + *----------------------------------------------------------------------
1.1624 + */
1.1625 +
1.1626 +EXPORT_C Tcl_Channel
1.1627 +Tcl_GetStackedChannel(chan)
1.1628 + Tcl_Channel chan;
1.1629 +{
1.1630 + Channel *chanPtr = (Channel *) chan; /* The actual channel. */
1.1631 +
1.1632 + return (Tcl_Channel) chanPtr->downChanPtr;
1.1633 +}
1.1634 +
1.1635 +/*
1.1636 + *----------------------------------------------------------------------
1.1637 + *
1.1638 + * Tcl_GetTopChannel --
1.1639 + *
1.1640 + * Returns the top channel of a channel stack.
1.1641 + *
1.1642 + * Results:
1.1643 + * NULL if the channel is not stacked upon another one, or a reference
1.1644 + * to the channel it is stacked upon. This reference can be used in
1.1645 + * queries, but modification is not allowed.
1.1646 + *
1.1647 + * Side effects:
1.1648 + * None.
1.1649 + *
1.1650 + *----------------------------------------------------------------------
1.1651 + */
1.1652 +
1.1653 +EXPORT_C Tcl_Channel
1.1654 +Tcl_GetTopChannel(chan)
1.1655 + Tcl_Channel chan;
1.1656 +{
1.1657 + Channel *chanPtr = (Channel *) chan; /* The actual channel. */
1.1658 +
1.1659 + return (Tcl_Channel) chanPtr->state->topChanPtr;
1.1660 +}
1.1661 +
1.1662 +/*
1.1663 + *----------------------------------------------------------------------
1.1664 + *
1.1665 + * Tcl_GetChannelInstanceData --
1.1666 + *
1.1667 + * Returns the client data associated with a channel.
1.1668 + *
1.1669 + * Results:
1.1670 + * The client data.
1.1671 + *
1.1672 + * Side effects:
1.1673 + * None.
1.1674 + *
1.1675 + *----------------------------------------------------------------------
1.1676 + */
1.1677 +
1.1678 +EXPORT_C ClientData
1.1679 +Tcl_GetChannelInstanceData(chan)
1.1680 + Tcl_Channel chan; /* Channel for which to return client data. */
1.1681 +{
1.1682 + Channel *chanPtr = (Channel *) chan; /* The actual channel. */
1.1683 +
1.1684 + return chanPtr->instanceData;
1.1685 +}
1.1686 +
1.1687 +/*
1.1688 + *----------------------------------------------------------------------
1.1689 + *
1.1690 + * Tcl_GetChannelThread --
1.1691 + *
1.1692 + * Given a channel structure, returns the thread managing it.
1.1693 + * TIP #10
1.1694 + *
1.1695 + * Results:
1.1696 + * Returns the id of the thread managing the channel.
1.1697 + *
1.1698 + * Side effects:
1.1699 + * None.
1.1700 + *
1.1701 + *----------------------------------------------------------------------
1.1702 + */
1.1703 +
1.1704 +EXPORT_C Tcl_ThreadId
1.1705 +Tcl_GetChannelThread(chan)
1.1706 + Tcl_Channel chan; /* The channel to return managing thread for. */
1.1707 +{
1.1708 + Channel *chanPtr = (Channel *) chan; /* The actual channel. */
1.1709 +
1.1710 + return chanPtr->state->managingThread;
1.1711 +}
1.1712 +
1.1713 +/*
1.1714 + *----------------------------------------------------------------------
1.1715 + *
1.1716 + * Tcl_GetChannelType --
1.1717 + *
1.1718 + * Given a channel structure, returns the channel type structure.
1.1719 + *
1.1720 + * Results:
1.1721 + * Returns a pointer to the channel type structure.
1.1722 + *
1.1723 + * Side effects:
1.1724 + * None.
1.1725 + *
1.1726 + *----------------------------------------------------------------------
1.1727 + */
1.1728 +
1.1729 +EXPORT_C Tcl_ChannelType *
1.1730 +Tcl_GetChannelType(chan)
1.1731 + Tcl_Channel chan; /* The channel to return type for. */
1.1732 +{
1.1733 + Channel *chanPtr = (Channel *) chan; /* The actual channel. */
1.1734 +
1.1735 + return chanPtr->typePtr;
1.1736 +}
1.1737 +
1.1738 +/*
1.1739 + *----------------------------------------------------------------------
1.1740 + *
1.1741 + * Tcl_GetChannelMode --
1.1742 + *
1.1743 + * Computes a mask indicating whether the channel is open for
1.1744 + * reading and writing.
1.1745 + *
1.1746 + * Results:
1.1747 + * An OR-ed combination of TCL_READABLE and TCL_WRITABLE.
1.1748 + *
1.1749 + * Side effects:
1.1750 + * None.
1.1751 + *
1.1752 + *----------------------------------------------------------------------
1.1753 + */
1.1754 +
1.1755 +EXPORT_C int
1.1756 +Tcl_GetChannelMode(chan)
1.1757 + Tcl_Channel chan; /* The channel for which the mode is
1.1758 + * being computed. */
1.1759 +{
1.1760 + ChannelState *statePtr = ((Channel *) chan)->state;
1.1761 + /* State of actual channel. */
1.1762 +
1.1763 + return (statePtr->flags & (TCL_READABLE | TCL_WRITABLE));
1.1764 +}
1.1765 +
1.1766 +/*
1.1767 + *----------------------------------------------------------------------
1.1768 + *
1.1769 + * Tcl_GetChannelName --
1.1770 + *
1.1771 + * Returns the string identifying the channel name.
1.1772 + *
1.1773 + * Results:
1.1774 + * The string containing the channel name. This memory is
1.1775 + * owned by the generic layer and should not be modified by
1.1776 + * the caller.
1.1777 + *
1.1778 + * Side effects:
1.1779 + * None.
1.1780 + *
1.1781 + *----------------------------------------------------------------------
1.1782 + */
1.1783 +
1.1784 +EXPORT_C CONST char *
1.1785 +Tcl_GetChannelName(chan)
1.1786 + Tcl_Channel chan; /* The channel for which to return the name. */
1.1787 +{
1.1788 + ChannelState *statePtr; /* State of actual channel. */
1.1789 +
1.1790 + statePtr = ((Channel *) chan)->state;
1.1791 + return statePtr->channelName;
1.1792 +}
1.1793 +
1.1794 +/*
1.1795 + *----------------------------------------------------------------------
1.1796 + *
1.1797 + * Tcl_GetChannelHandle --
1.1798 + *
1.1799 + * Returns an OS handle associated with a channel.
1.1800 + *
1.1801 + * Results:
1.1802 + * Returns TCL_OK and places the handle in handlePtr, or returns
1.1803 + * TCL_ERROR on failure.
1.1804 + *
1.1805 + * Side effects:
1.1806 + * None.
1.1807 + *
1.1808 + *----------------------------------------------------------------------
1.1809 + */
1.1810 +
1.1811 +EXPORT_C int
1.1812 +Tcl_GetChannelHandle(chan, direction, handlePtr)
1.1813 + Tcl_Channel chan; /* The channel to get file from. */
1.1814 + int direction; /* TCL_WRITABLE or TCL_READABLE. */
1.1815 + ClientData *handlePtr; /* Where to store handle */
1.1816 +{
1.1817 + Channel *chanPtr; /* The actual channel. */
1.1818 + ClientData handle;
1.1819 + int result;
1.1820 +
1.1821 + chanPtr = ((Channel *) chan)->state->bottomChanPtr;
1.1822 + result = (chanPtr->typePtr->getHandleProc)(chanPtr->instanceData,
1.1823 + direction, &handle);
1.1824 + if (handlePtr) {
1.1825 + *handlePtr = handle;
1.1826 + }
1.1827 + return result;
1.1828 +}
1.1829 +
1.1830 +/*
1.1831 + *---------------------------------------------------------------------------
1.1832 + *
1.1833 + * AllocChannelBuffer --
1.1834 + *
1.1835 + * A channel buffer has BUFFER_PADDING bytes extra at beginning to
1.1836 + * hold any bytes of a native-encoding character that got split by
1.1837 + * the end of the previous buffer and need to be moved to the
1.1838 + * beginning of the next buffer to make a contiguous string so it
1.1839 + * can be converted to UTF-8.
1.1840 + *
1.1841 + * A channel buffer has BUFFER_PADDING bytes extra at the end to
1.1842 + * hold any bytes of a native-encoding character (generated from a
1.1843 + * UTF-8 character) that overflow past the end of the buffer and
1.1844 + * need to be moved to the next buffer.
1.1845 + *
1.1846 + * Results:
1.1847 + * A newly allocated channel buffer.
1.1848 + *
1.1849 + * Side effects:
1.1850 + * None.
1.1851 + *
1.1852 + *---------------------------------------------------------------------------
1.1853 + */
1.1854 +
1.1855 +static ChannelBuffer *
1.1856 +AllocChannelBuffer(length)
1.1857 + int length; /* Desired length of channel buffer. */
1.1858 +{
1.1859 + ChannelBuffer *bufPtr;
1.1860 + int n;
1.1861 +
1.1862 + n = length + CHANNELBUFFER_HEADER_SIZE + BUFFER_PADDING + BUFFER_PADDING;
1.1863 + bufPtr = (ChannelBuffer *) ckalloc((unsigned) n);
1.1864 + bufPtr->nextAdded = BUFFER_PADDING;
1.1865 + bufPtr->nextRemoved = BUFFER_PADDING;
1.1866 + bufPtr->bufLength = length + BUFFER_PADDING;
1.1867 + bufPtr->nextPtr = (ChannelBuffer *) NULL;
1.1868 + return bufPtr;
1.1869 +}
1.1870 +
1.1871 +/*
1.1872 + *----------------------------------------------------------------------
1.1873 + *
1.1874 + * RecycleBuffer --
1.1875 + *
1.1876 + * Helper function to recycle input and output buffers. Ensures
1.1877 + * that two input buffers are saved (one in the input queue and
1.1878 + * another in the saveInBufPtr field) and that curOutPtr is set
1.1879 + * to a buffer. Only if these conditions are met is the buffer
1.1880 + * freed to the OS.
1.1881 + *
1.1882 + * Results:
1.1883 + * None.
1.1884 + *
1.1885 + * Side effects:
1.1886 + * May free a buffer to the OS.
1.1887 + *
1.1888 + *----------------------------------------------------------------------
1.1889 + */
1.1890 +
1.1891 +static void
1.1892 +RecycleBuffer(statePtr, bufPtr, mustDiscard)
1.1893 + ChannelState *statePtr; /* ChannelState in which to recycle buffers. */
1.1894 + ChannelBuffer *bufPtr; /* The buffer to recycle. */
1.1895 + int mustDiscard; /* If nonzero, free the buffer to the
1.1896 + * OS, always. */
1.1897 +{
1.1898 + /*
1.1899 + * Do we have to free the buffer to the OS?
1.1900 + */
1.1901 +
1.1902 + if (mustDiscard) {
1.1903 + ckfree((char *) bufPtr);
1.1904 + return;
1.1905 + }
1.1906 +
1.1907 + /*
1.1908 + * Only save buffers which are at least as big as the requested
1.1909 + * buffersize for the channel. This is to honor dynamic changes
1.1910 + * of the buffersize made by the user.
1.1911 + */
1.1912 +
1.1913 + if ((bufPtr->bufLength - BUFFER_PADDING) < statePtr->bufSize) {
1.1914 + ckfree((char *) bufPtr);
1.1915 + return;
1.1916 + }
1.1917 +
1.1918 + /*
1.1919 + * Only save buffers for the input queue if the channel is readable.
1.1920 + */
1.1921 +
1.1922 + if (statePtr->flags & TCL_READABLE) {
1.1923 + if (statePtr->inQueueHead == (ChannelBuffer *) NULL) {
1.1924 + statePtr->inQueueHead = bufPtr;
1.1925 + statePtr->inQueueTail = bufPtr;
1.1926 + goto keepit;
1.1927 + }
1.1928 + if (statePtr->saveInBufPtr == (ChannelBuffer *) NULL) {
1.1929 + statePtr->saveInBufPtr = bufPtr;
1.1930 + goto keepit;
1.1931 + }
1.1932 + }
1.1933 +
1.1934 + /*
1.1935 + * Only save buffers for the output queue if the channel is writable.
1.1936 + */
1.1937 +
1.1938 + if (statePtr->flags & TCL_WRITABLE) {
1.1939 + if (statePtr->curOutPtr == (ChannelBuffer *) NULL) {
1.1940 + statePtr->curOutPtr = bufPtr;
1.1941 + goto keepit;
1.1942 + }
1.1943 + }
1.1944 +
1.1945 + /*
1.1946 + * If we reached this code we return the buffer to the OS.
1.1947 + */
1.1948 +
1.1949 + ckfree((char *) bufPtr);
1.1950 + return;
1.1951 +
1.1952 + keepit:
1.1953 + bufPtr->nextRemoved = BUFFER_PADDING;
1.1954 + bufPtr->nextAdded = BUFFER_PADDING;
1.1955 + bufPtr->nextPtr = (ChannelBuffer *) NULL;
1.1956 +}
1.1957 +
1.1958 +/*
1.1959 + *----------------------------------------------------------------------
1.1960 + *
1.1961 + * DiscardOutputQueued --
1.1962 + *
1.1963 + * Discards all output queued in the output queue of a channel.
1.1964 + *
1.1965 + * Results:
1.1966 + * None.
1.1967 + *
1.1968 + * Side effects:
1.1969 + * Recycles buffers.
1.1970 + *
1.1971 + *----------------------------------------------------------------------
1.1972 + */
1.1973 +
1.1974 +static void
1.1975 +DiscardOutputQueued(statePtr)
1.1976 + ChannelState *statePtr; /* ChannelState for which to discard output. */
1.1977 +{
1.1978 + ChannelBuffer *bufPtr;
1.1979 +
1.1980 + while (statePtr->outQueueHead != (ChannelBuffer *) NULL) {
1.1981 + bufPtr = statePtr->outQueueHead;
1.1982 + statePtr->outQueueHead = bufPtr->nextPtr;
1.1983 + RecycleBuffer(statePtr, bufPtr, 0);
1.1984 + }
1.1985 + statePtr->outQueueHead = (ChannelBuffer *) NULL;
1.1986 + statePtr->outQueueTail = (ChannelBuffer *) NULL;
1.1987 +}
1.1988 +
1.1989 +/*
1.1990 + *----------------------------------------------------------------------
1.1991 + *
1.1992 + * CheckForDeadChannel --
1.1993 + *
1.1994 + * This function checks is a given channel is Dead.
1.1995 + * (A channel that has been closed but not yet deallocated.)
1.1996 + *
1.1997 + * Results:
1.1998 + * True (1) if channel is Dead, False (0) if channel is Ok
1.1999 + *
1.2000 + * Side effects:
1.2001 + * None
1.2002 + *
1.2003 + *----------------------------------------------------------------------
1.2004 + */
1.2005 +
1.2006 +static int
1.2007 +CheckForDeadChannel(interp, statePtr)
1.2008 + Tcl_Interp *interp; /* For error reporting (can be NULL) */
1.2009 + ChannelState *statePtr; /* The channel state to check. */
1.2010 +{
1.2011 + if (statePtr->flags & CHANNEL_DEAD) {
1.2012 + Tcl_SetErrno(EINVAL);
1.2013 + if (interp) {
1.2014 + Tcl_AppendResult(interp,
1.2015 + "unable to access channel: invalid channel",
1.2016 + (char *) NULL);
1.2017 + }
1.2018 + return 1;
1.2019 + }
1.2020 + return 0;
1.2021 +}
1.2022 +
1.2023 +/*
1.2024 + *----------------------------------------------------------------------
1.2025 + *
1.2026 + * FlushChannel --
1.2027 + *
1.2028 + * This function flushes as much of the queued output as is possible
1.2029 + * now. If calledFromAsyncFlush is nonzero, it is being called in an
1.2030 + * event handler to flush channel output asynchronously.
1.2031 + *
1.2032 + * Results:
1.2033 + * 0 if successful, else the error code that was returned by the
1.2034 + * channel type operation.
1.2035 + *
1.2036 + * Side effects:
1.2037 + * May produce output on a channel. May block indefinitely if the
1.2038 + * channel is synchronous. May schedule an async flush on the channel.
1.2039 + * May recycle memory for buffers in the output queue.
1.2040 + *
1.2041 + *----------------------------------------------------------------------
1.2042 + */
1.2043 +
1.2044 +static int
1.2045 +FlushChannel(interp, chanPtr, calledFromAsyncFlush)
1.2046 + Tcl_Interp *interp; /* For error reporting during close. */
1.2047 + Channel *chanPtr; /* The channel to flush on. */
1.2048 + int calledFromAsyncFlush; /* If nonzero then we are being
1.2049 + * called from an asynchronous
1.2050 + * flush callback. */
1.2051 +{
1.2052 + ChannelState *statePtr = chanPtr->state;
1.2053 + /* State of the channel stack. */
1.2054 + ChannelBuffer *bufPtr; /* Iterates over buffered output
1.2055 + * queue. */
1.2056 + int toWrite; /* Amount of output data in current
1.2057 + * buffer available to be written. */
1.2058 + int written; /* Amount of output data actually
1.2059 + * written in current round. */
1.2060 + int errorCode = 0; /* Stores POSIX error codes from
1.2061 + * channel driver operations. */
1.2062 + int wroteSome = 0; /* Set to one if any data was
1.2063 + * written to the driver. */
1.2064 +
1.2065 + /*
1.2066 + * Prevent writing on a dead channel -- a channel that has been closed
1.2067 + * but not yet deallocated. This can occur if the exit handler for the
1.2068 + * channel deallocation runs before all channels are deregistered in
1.2069 + * all interpreters.
1.2070 + */
1.2071 +
1.2072 + if (CheckForDeadChannel(interp, statePtr)) return -1;
1.2073 +
1.2074 + /*
1.2075 + * Loop over the queued buffers and attempt to flush as
1.2076 + * much as possible of the queued output to the channel.
1.2077 + */
1.2078 +
1.2079 + while (1) {
1.2080 +
1.2081 + /*
1.2082 + * If the queue is empty and there is a ready current buffer, OR if
1.2083 + * the current buffer is full, then move the current buffer to the
1.2084 + * queue.
1.2085 + */
1.2086 +
1.2087 + if (((statePtr->curOutPtr != (ChannelBuffer *) NULL) &&
1.2088 + (statePtr->curOutPtr->nextAdded == statePtr->curOutPtr->bufLength))
1.2089 + || ((statePtr->flags & BUFFER_READY) &&
1.2090 + (statePtr->outQueueHead == (ChannelBuffer *) NULL))) {
1.2091 + statePtr->flags &= (~(BUFFER_READY));
1.2092 + statePtr->curOutPtr->nextPtr = (ChannelBuffer *) NULL;
1.2093 + if (statePtr->outQueueHead == (ChannelBuffer *) NULL) {
1.2094 + statePtr->outQueueHead = statePtr->curOutPtr;
1.2095 + } else {
1.2096 + statePtr->outQueueTail->nextPtr = statePtr->curOutPtr;
1.2097 + }
1.2098 + statePtr->outQueueTail = statePtr->curOutPtr;
1.2099 + statePtr->curOutPtr = (ChannelBuffer *) NULL;
1.2100 + }
1.2101 + bufPtr = statePtr->outQueueHead;
1.2102 +
1.2103 + /*
1.2104 + * If we are not being called from an async flush and an async
1.2105 + * flush is active, we just return without producing any output.
1.2106 + */
1.2107 +
1.2108 + if ((!calledFromAsyncFlush) &&
1.2109 + (statePtr->flags & BG_FLUSH_SCHEDULED)) {
1.2110 + return 0;
1.2111 + }
1.2112 +
1.2113 + /*
1.2114 + * If the output queue is still empty, break out of the while loop.
1.2115 + */
1.2116 +
1.2117 + if (bufPtr == (ChannelBuffer *) NULL) {
1.2118 + break; /* Out of the "while (1)". */
1.2119 + }
1.2120 +
1.2121 + /*
1.2122 + * Produce the output on the channel.
1.2123 + */
1.2124 +
1.2125 + toWrite = bufPtr->nextAdded - bufPtr->nextRemoved;
1.2126 + written = (chanPtr->typePtr->outputProc) (chanPtr->instanceData,
1.2127 + bufPtr->buf + bufPtr->nextRemoved, toWrite,
1.2128 + &errorCode);
1.2129 +
1.2130 + /*
1.2131 + * If the write failed completely attempt to start the asynchronous
1.2132 + * flush mechanism and break out of this loop - do not attempt to
1.2133 + * write any more output at this time.
1.2134 + */
1.2135 +
1.2136 + if (written < 0) {
1.2137 +
1.2138 + /*
1.2139 + * If the last attempt to write was interrupted, simply retry.
1.2140 + */
1.2141 +
1.2142 + if (errorCode == EINTR) {
1.2143 + errorCode = 0;
1.2144 + continue;
1.2145 + }
1.2146 +
1.2147 + /*
1.2148 + * If the channel is non-blocking and we would have blocked,
1.2149 + * start a background flushing handler and break out of the loop.
1.2150 + */
1.2151 +
1.2152 + if ((errorCode == EWOULDBLOCK) || (errorCode == EAGAIN)) {
1.2153 + /*
1.2154 + * This used to check for CHANNEL_NONBLOCKING, and panic
1.2155 + * if the channel was blocking. However, it appears
1.2156 + * that setting stdin to -blocking 0 has some effect on
1.2157 + * the stdout when it's a tty channel (dup'ed underneath)
1.2158 + */
1.2159 + if (!(statePtr->flags & BG_FLUSH_SCHEDULED)) {
1.2160 + statePtr->flags |= BG_FLUSH_SCHEDULED;
1.2161 + UpdateInterest(chanPtr);
1.2162 + }
1.2163 + errorCode = 0;
1.2164 + break;
1.2165 + }
1.2166 +
1.2167 + /*
1.2168 + * Decide whether to report the error upwards or defer it.
1.2169 + */
1.2170 +
1.2171 + if (calledFromAsyncFlush) {
1.2172 + if (statePtr->unreportedError == 0) {
1.2173 + statePtr->unreportedError = errorCode;
1.2174 + }
1.2175 + } else {
1.2176 + Tcl_SetErrno(errorCode);
1.2177 + if (interp != NULL) {
1.2178 +
1.2179 + /*
1.2180 + * Casting away CONST here is safe because the
1.2181 + * TCL_VOLATILE flag guarantees CONST treatment
1.2182 + * of the Posix error string.
1.2183 + */
1.2184 +
1.2185 + Tcl_SetResult(interp,
1.2186 + (char *) Tcl_PosixError(interp), TCL_VOLATILE);
1.2187 + }
1.2188 + }
1.2189 +
1.2190 + /*
1.2191 + * When we get an error we throw away all the output
1.2192 + * currently queued.
1.2193 + */
1.2194 +
1.2195 + DiscardOutputQueued(statePtr);
1.2196 + continue;
1.2197 + } else {
1.2198 + wroteSome = 1;
1.2199 + }
1.2200 +
1.2201 + bufPtr->nextRemoved += written;
1.2202 +
1.2203 + /*
1.2204 + * If this buffer is now empty, recycle it.
1.2205 + */
1.2206 +
1.2207 + if (bufPtr->nextRemoved == bufPtr->nextAdded) {
1.2208 + statePtr->outQueueHead = bufPtr->nextPtr;
1.2209 + if (statePtr->outQueueHead == (ChannelBuffer *) NULL) {
1.2210 + statePtr->outQueueTail = (ChannelBuffer *) NULL;
1.2211 + }
1.2212 + RecycleBuffer(statePtr, bufPtr, 0);
1.2213 + }
1.2214 + } /* Closes "while (1)". */
1.2215 +
1.2216 + /*
1.2217 + * If we wrote some data while flushing in the background, we are done.
1.2218 + * We can't finish the background flush until we run out of data and
1.2219 + * the channel becomes writable again. This ensures that all of the
1.2220 + * pending data has been flushed at the system level.
1.2221 + */
1.2222 +
1.2223 + if (statePtr->flags & BG_FLUSH_SCHEDULED) {
1.2224 + if (wroteSome) {
1.2225 + return errorCode;
1.2226 + } else if (statePtr->outQueueHead == (ChannelBuffer *) NULL) {
1.2227 + statePtr->flags &= (~(BG_FLUSH_SCHEDULED));
1.2228 + (chanPtr->typePtr->watchProc)(chanPtr->instanceData,
1.2229 + statePtr->interestMask);
1.2230 + }
1.2231 + }
1.2232 +
1.2233 + /*
1.2234 + * If the channel is flagged as closed, delete it when the refCount
1.2235 + * drops to zero, the output queue is empty and there is no output
1.2236 + * in the current output buffer.
1.2237 + */
1.2238 +
1.2239 + if ((statePtr->flags & CHANNEL_CLOSED) && (statePtr->refCount <= 0) &&
1.2240 + (statePtr->outQueueHead == (ChannelBuffer *) NULL) &&
1.2241 + ((statePtr->curOutPtr == (ChannelBuffer *) NULL) ||
1.2242 + (statePtr->curOutPtr->nextAdded ==
1.2243 + statePtr->curOutPtr->nextRemoved))) {
1.2244 + return CloseChannel(interp, chanPtr, errorCode);
1.2245 + }
1.2246 + return errorCode;
1.2247 +}
1.2248 +
1.2249 +/*
1.2250 + *----------------------------------------------------------------------
1.2251 + *
1.2252 + * CloseChannel --
1.2253 + *
1.2254 + * Utility procedure to close a channel and free associated resources.
1.2255 + *
1.2256 + * If the channel was stacked, then the it will copy the necessary
1.2257 + * elements of the NEXT channel into the TOP channel, in essence
1.2258 + * unstacking the channel. The NEXT channel will then be freed.
1.2259 + *
1.2260 + * If the channel was not stacked, then we will free all the bits
1.2261 + * for the TOP channel, including the data structure itself.
1.2262 + *
1.2263 + * Results:
1.2264 + * 1 if the channel was stacked, 0 otherwise.
1.2265 + *
1.2266 + * Side effects:
1.2267 + * May close the actual channel; may free memory.
1.2268 + * May change the value of errno.
1.2269 + *
1.2270 + *----------------------------------------------------------------------
1.2271 + */
1.2272 +
1.2273 +static int
1.2274 +CloseChannel(interp, chanPtr, errorCode)
1.2275 + Tcl_Interp *interp; /* For error reporting. */
1.2276 + Channel *chanPtr; /* The channel to close. */
1.2277 + int errorCode; /* Status of operation so far. */
1.2278 +{
1.2279 + int result = 0; /* Of calling driver close
1.2280 + * operation. */
1.2281 + ChannelState *statePtr; /* state of the channel stack. */
1.2282 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.2283 +
1.2284 + if (chanPtr == NULL) {
1.2285 + return result;
1.2286 + }
1.2287 + statePtr = chanPtr->state;
1.2288 +
1.2289 + /*
1.2290 + * No more input can be consumed so discard any leftover input.
1.2291 + */
1.2292 +
1.2293 + DiscardInputQueued(statePtr, 1);
1.2294 +
1.2295 + /*
1.2296 + * Discard a leftover buffer in the current output buffer field.
1.2297 + */
1.2298 +
1.2299 + if (statePtr->curOutPtr != (ChannelBuffer *) NULL) {
1.2300 + ckfree((char *) statePtr->curOutPtr);
1.2301 + statePtr->curOutPtr = (ChannelBuffer *) NULL;
1.2302 + }
1.2303 +
1.2304 + /*
1.2305 + * The caller guarantees that there are no more buffers
1.2306 + * queued for output.
1.2307 + */
1.2308 +
1.2309 + if (statePtr->outQueueHead != (ChannelBuffer *) NULL) {
1.2310 + panic("TclFlush, closed channel: queued output left");
1.2311 + }
1.2312 +
1.2313 + /*
1.2314 + * If the EOF character is set in the channel, append that to the
1.2315 + * output device.
1.2316 + */
1.2317 +
1.2318 + if ((statePtr->outEofChar != 0) && (statePtr->flags & TCL_WRITABLE)) {
1.2319 + int dummy;
1.2320 + char c;
1.2321 +
1.2322 + c = (char) statePtr->outEofChar;
1.2323 + (chanPtr->typePtr->outputProc) (chanPtr->instanceData, &c, 1, &dummy);
1.2324 + }
1.2325 +
1.2326 + /*
1.2327 + * Remove this channel from of the list of all channels.
1.2328 + */
1.2329 + Tcl_CutChannel((Tcl_Channel) chanPtr);
1.2330 +
1.2331 + /*
1.2332 + * Close and free the channel driver state.
1.2333 + */
1.2334 +
1.2335 + if (chanPtr->typePtr->closeProc != TCL_CLOSE2PROC) {
1.2336 + result = (chanPtr->typePtr->closeProc)(chanPtr->instanceData, interp);
1.2337 + } else {
1.2338 + result = (chanPtr->typePtr->close2Proc)(chanPtr->instanceData, interp,
1.2339 + 0);
1.2340 + }
1.2341 +
1.2342 + /*
1.2343 + * Some resources can be cleared only if the bottom channel
1.2344 + * in a stack is closed. All the other channels in the stack
1.2345 + * are not allowed to remove.
1.2346 + */
1.2347 +
1.2348 + if (chanPtr == statePtr->bottomChanPtr) {
1.2349 + if (statePtr->channelName != (char *) NULL) {
1.2350 + ckfree((char *) statePtr->channelName);
1.2351 + statePtr->channelName = NULL;
1.2352 + }
1.2353 +
1.2354 + Tcl_FreeEncoding(statePtr->encoding);
1.2355 + if (statePtr->outputStage != NULL) {
1.2356 + ckfree((char *) statePtr->outputStage);
1.2357 + statePtr->outputStage = (char *) NULL;
1.2358 + }
1.2359 + }
1.2360 +
1.2361 + /*
1.2362 + * If we are being called synchronously, report either
1.2363 + * any latent error on the channel or the current error.
1.2364 + */
1.2365 +
1.2366 + if (statePtr->unreportedError != 0) {
1.2367 + errorCode = statePtr->unreportedError;
1.2368 + }
1.2369 + if (errorCode == 0) {
1.2370 + errorCode = result;
1.2371 + if (errorCode != 0) {
1.2372 + Tcl_SetErrno(errorCode);
1.2373 + }
1.2374 + }
1.2375 +
1.2376 + /*
1.2377 + * Cancel any outstanding timer.
1.2378 + */
1.2379 +
1.2380 + Tcl_DeleteTimerHandler(statePtr->timer);
1.2381 +
1.2382 + /*
1.2383 + * Mark the channel as deleted by clearing the type structure.
1.2384 + */
1.2385 +
1.2386 + if (chanPtr->downChanPtr != (Channel *) NULL) {
1.2387 + Channel *downChanPtr = chanPtr->downChanPtr;
1.2388 +
1.2389 + statePtr->nextCSPtr = tsdPtr->firstCSPtr;
1.2390 + tsdPtr->firstCSPtr = statePtr;
1.2391 +
1.2392 + statePtr->topChanPtr = downChanPtr;
1.2393 + downChanPtr->upChanPtr = (Channel *) NULL;
1.2394 + chanPtr->typePtr = NULL;
1.2395 +
1.2396 + Tcl_EventuallyFree((ClientData) chanPtr, TCL_DYNAMIC);
1.2397 + return Tcl_Close(interp, (Tcl_Channel) downChanPtr);
1.2398 + }
1.2399 +
1.2400 + /*
1.2401 + * There is only the TOP Channel, so we free the remaining
1.2402 + * pointers we have and then ourselves. Since this is the
1.2403 + * last of the channels in the stack, make sure to free the
1.2404 + * ChannelState structure associated with it. We use
1.2405 + * Tcl_EventuallyFree to allow for any last
1.2406 + */
1.2407 + chanPtr->typePtr = NULL;
1.2408 +
1.2409 + Tcl_EventuallyFree((ClientData) statePtr, TCL_DYNAMIC);
1.2410 + Tcl_EventuallyFree((ClientData) chanPtr, TCL_DYNAMIC);
1.2411 +
1.2412 + return errorCode;
1.2413 +}
1.2414 +
1.2415 +/*
1.2416 + *----------------------------------------------------------------------
1.2417 + *
1.2418 + * Tcl_CutChannel --
1.2419 + *
1.2420 + * Removes a channel from the (thread-)global list of all channels
1.2421 + * (in that thread). This is actually the statePtr for the stack
1.2422 + * of channel.
1.2423 + *
1.2424 + * Results:
1.2425 + * Nothing.
1.2426 + *
1.2427 + * Side effects:
1.2428 + * Resets the field 'nextCSPtr' of the specified channel state to NULL.
1.2429 + *
1.2430 + * NOTE:
1.2431 + * The channel to cut out of the list must not be referenced
1.2432 + * in any interpreter. This is something this procedure cannot
1.2433 + * check (despite the refcount) because the caller usually wants
1.2434 + * fiddle with the channel (like transfering it to a different
1.2435 + * thread) and thus keeps the refcount artifically high to prevent
1.2436 + * its destruction.
1.2437 + *
1.2438 + *----------------------------------------------------------------------
1.2439 + */
1.2440 +
1.2441 +EXPORT_C void
1.2442 +Tcl_CutChannel(chan)
1.2443 + Tcl_Channel chan; /* The channel being removed. Must
1.2444 + * not be referenced in any
1.2445 + * interpreter. */
1.2446 +{
1.2447 + ThreadSpecificData* tsdPtr = TCL_TSD_INIT(&dataKey);
1.2448 + ChannelState *prevCSPtr; /* Preceding channel state in list of
1.2449 + * all states - used to splice a
1.2450 + * channel out of the list on close. */
1.2451 + ChannelState *statePtr = ((Channel *) chan)->state;
1.2452 + /* state of the channel stack. */
1.2453 + Tcl_DriverThreadActionProc *threadActionProc;
1.2454 +
1.2455 + /*
1.2456 + * Remove this channel from of the list of all channels
1.2457 + * (in the current thread).
1.2458 + */
1.2459 +
1.2460 + if (tsdPtr->firstCSPtr && (statePtr == tsdPtr->firstCSPtr)) {
1.2461 + tsdPtr->firstCSPtr = statePtr->nextCSPtr;
1.2462 + } else {
1.2463 + for (prevCSPtr = tsdPtr->firstCSPtr;
1.2464 + prevCSPtr && (prevCSPtr->nextCSPtr != statePtr);
1.2465 + prevCSPtr = prevCSPtr->nextCSPtr) {
1.2466 + /* Empty loop body. */
1.2467 + }
1.2468 + if (prevCSPtr == (ChannelState *) NULL) {
1.2469 + panic("FlushChannel: damaged channel list");
1.2470 + }
1.2471 + prevCSPtr->nextCSPtr = statePtr->nextCSPtr;
1.2472 + }
1.2473 +
1.2474 + statePtr->nextCSPtr = (ChannelState *) NULL;
1.2475 +
1.2476 + /* TIP #218, Channel Thread Actions */
1.2477 + threadActionProc = Tcl_ChannelThreadActionProc (Tcl_GetChannelType (chan));
1.2478 + if (threadActionProc != NULL) {
1.2479 + (*threadActionProc) (Tcl_GetChannelInstanceData(chan),
1.2480 + TCL_CHANNEL_THREAD_REMOVE);
1.2481 + }
1.2482 +}
1.2483 +
1.2484 +/*
1.2485 + *----------------------------------------------------------------------
1.2486 + *
1.2487 + * Tcl_SpliceChannel --
1.2488 + *
1.2489 + * Adds a channel to the (thread-)global list of all channels
1.2490 + * (in that thread). Expects that the field 'nextChanPtr' in
1.2491 + * the channel is set to NULL.
1.2492 + *
1.2493 + * Results:
1.2494 + * Nothing.
1.2495 + *
1.2496 + * Side effects:
1.2497 + * Nothing.
1.2498 + *
1.2499 + * NOTE:
1.2500 + * The channel to splice into the list must not be referenced in any
1.2501 + * interpreter. This is something this procedure cannot check
1.2502 + * (despite the refcount) because the caller usually wants figgle
1.2503 + * with the channel (like transfering it to a different thread)
1.2504 + * and thus keeps the refcount artifically high to prevent its
1.2505 + * destruction.
1.2506 + *
1.2507 + *----------------------------------------------------------------------
1.2508 + */
1.2509 +
1.2510 +EXPORT_C void
1.2511 +Tcl_SpliceChannel(chan)
1.2512 + Tcl_Channel chan; /* The channel being added. Must
1.2513 + * not be referenced in any
1.2514 + * interpreter. */
1.2515 +{
1.2516 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.2517 + ChannelState *statePtr = ((Channel *) chan)->state;
1.2518 + Tcl_DriverThreadActionProc *threadActionProc;
1.2519 +
1.2520 + if (statePtr->nextCSPtr != (ChannelState *) NULL) {
1.2521 + panic("Tcl_SpliceChannel: trying to add channel used in different list");
1.2522 + }
1.2523 +
1.2524 + statePtr->nextCSPtr = tsdPtr->firstCSPtr;
1.2525 + tsdPtr->firstCSPtr = statePtr;
1.2526 +
1.2527 + /*
1.2528 + * TIP #10. Mark the current thread as the new one managing this
1.2529 + * channel. Note: 'Tcl_GetCurrentThread' returns sensible
1.2530 + * values even for a non-threaded core.
1.2531 + */
1.2532 +
1.2533 + statePtr->managingThread = Tcl_GetCurrentThread ();
1.2534 +
1.2535 + /* TIP #218, Channel Thread Actions */
1.2536 + threadActionProc = Tcl_ChannelThreadActionProc (Tcl_GetChannelType (chan));
1.2537 + if (threadActionProc != NULL) {
1.2538 + (*threadActionProc) (Tcl_GetChannelInstanceData(chan),
1.2539 + TCL_CHANNEL_THREAD_INSERT);
1.2540 + }
1.2541 +}
1.2542 +
1.2543 +/*
1.2544 + *----------------------------------------------------------------------
1.2545 + *
1.2546 + * Tcl_Close --
1.2547 + *
1.2548 + * Closes a channel.
1.2549 + *
1.2550 + * Results:
1.2551 + * A standard Tcl result.
1.2552 + *
1.2553 + * Side effects:
1.2554 + * Closes the channel if this is the last reference.
1.2555 + *
1.2556 + * NOTE:
1.2557 + * Tcl_Close removes the channel as far as the user is concerned.
1.2558 + * However, it may continue to exist for a while longer if it has
1.2559 + * a background flush scheduled. The device itself is eventually
1.2560 + * closed and the channel record removed, in CloseChannel, above.
1.2561 + *
1.2562 + *----------------------------------------------------------------------
1.2563 + */
1.2564 +
1.2565 + /* ARGSUSED */
1.2566 +EXPORT_C int
1.2567 +Tcl_Close(interp, chan)
1.2568 + Tcl_Interp *interp; /* Interpreter for errors. */
1.2569 + Tcl_Channel chan; /* The channel being closed. Must
1.2570 + * not be referenced in any
1.2571 + * interpreter. */
1.2572 +{
1.2573 + CloseCallback *cbPtr; /* Iterate over close callbacks
1.2574 + * for this channel. */
1.2575 + Channel *chanPtr; /* The real IO channel. */
1.2576 + ChannelState *statePtr; /* State of real IO channel. */
1.2577 + int result; /* Of calling FlushChannel. */
1.2578 +
1.2579 + if (chan == (Tcl_Channel) NULL) {
1.2580 + return TCL_OK;
1.2581 + }
1.2582 +
1.2583 + /*
1.2584 + * Perform special handling for standard channels being closed. If the
1.2585 + * refCount is now 1 it means that the last reference to the standard
1.2586 + * channel is being explicitly closed, so bump the refCount down
1.2587 + * artificially to 0. This will ensure that the channel is actually
1.2588 + * closed, below. Also set the static pointer to NULL for the channel.
1.2589 + */
1.2590 +
1.2591 + CheckForStdChannelsBeingClosed(chan);
1.2592 +
1.2593 + /*
1.2594 + * This operation should occur at the top of a channel stack.
1.2595 + */
1.2596 +
1.2597 + chanPtr = (Channel *) chan;
1.2598 + statePtr = chanPtr->state;
1.2599 + chanPtr = statePtr->topChanPtr;
1.2600 +
1.2601 + if (statePtr->refCount > 0) {
1.2602 + panic("called Tcl_Close on channel with refCount > 0");
1.2603 + }
1.2604 +
1.2605 + if (statePtr->flags & CHANNEL_INCLOSE) {
1.2606 + if (interp) {
1.2607 + Tcl_AppendResult(interp,
1.2608 + "Illegal recursive call to close through close-handler of channel",
1.2609 + (char *) NULL);
1.2610 + }
1.2611 + return TCL_ERROR;
1.2612 + }
1.2613 + statePtr->flags |= CHANNEL_INCLOSE;
1.2614 +
1.2615 + /*
1.2616 + * When the channel has an escape sequence driven encoding such as
1.2617 + * iso2022, the terminated escape sequence must write to the buffer.
1.2618 + */
1.2619 + if ((statePtr->encoding != NULL) && (statePtr->curOutPtr != NULL)
1.2620 + && (CheckChannelErrors(statePtr, TCL_WRITABLE) == 0)) {
1.2621 + statePtr->outputEncodingFlags |= TCL_ENCODING_END;
1.2622 + WriteChars(chanPtr, "", 0);
1.2623 + }
1.2624 +
1.2625 + Tcl_ClearChannelHandlers(chan);
1.2626 +
1.2627 + /*
1.2628 + * Invoke the registered close callbacks and delete their records.
1.2629 + */
1.2630 +
1.2631 + while (statePtr->closeCbPtr != (CloseCallback *) NULL) {
1.2632 + cbPtr = statePtr->closeCbPtr;
1.2633 + statePtr->closeCbPtr = cbPtr->nextPtr;
1.2634 + (cbPtr->proc) (cbPtr->clientData);
1.2635 + ckfree((char *) cbPtr);
1.2636 + }
1.2637 +
1.2638 + statePtr->flags &= ~CHANNEL_INCLOSE;
1.2639 +
1.2640 + /*
1.2641 + * Ensure that the last output buffer will be flushed.
1.2642 + */
1.2643 +
1.2644 + if ((statePtr->curOutPtr != (ChannelBuffer *) NULL) &&
1.2645 + (statePtr->curOutPtr->nextAdded > statePtr->curOutPtr->nextRemoved)) {
1.2646 + statePtr->flags |= BUFFER_READY;
1.2647 + }
1.2648 +
1.2649 + /*
1.2650 + * If this channel supports it, close the read side, since we don't need it
1.2651 + * anymore and this will help avoid deadlocks on some channel types.
1.2652 + */
1.2653 +
1.2654 + if (chanPtr->typePtr->closeProc == TCL_CLOSE2PROC) {
1.2655 + result = (chanPtr->typePtr->close2Proc)(chanPtr->instanceData, interp,
1.2656 + TCL_CLOSE_READ);
1.2657 + } else {
1.2658 + result = 0;
1.2659 + }
1.2660 +
1.2661 + /*
1.2662 + * The call to FlushChannel will flush any queued output and invoke
1.2663 + * the close function of the channel driver, or it will set up the
1.2664 + * channel to be flushed and closed asynchronously.
1.2665 + */
1.2666 +
1.2667 + statePtr->flags |= CHANNEL_CLOSED;
1.2668 + if ((FlushChannel(interp, chanPtr, 0) != 0) || (result != 0)) {
1.2669 + return TCL_ERROR;
1.2670 + }
1.2671 + return TCL_OK;
1.2672 +}
1.2673 +
1.2674 +/*
1.2675 + *----------------------------------------------------------------------
1.2676 + *
1.2677 + * Tcl_ClearChannelHandlers --
1.2678 + *
1.2679 + * Removes all channel handlers and event scripts from the channel,
1.2680 + * cancels all background copies involving the channel and any interest
1.2681 + * in events.
1.2682 + *
1.2683 + * Results:
1.2684 + * None.
1.2685 + *
1.2686 + * Side effects:
1.2687 + * See above. Deallocates memory.
1.2688 + *
1.2689 + *----------------------------------------------------------------------
1.2690 + */
1.2691 +
1.2692 +EXPORT_C void
1.2693 +Tcl_ClearChannelHandlers (channel)
1.2694 + Tcl_Channel channel;
1.2695 +{
1.2696 + ChannelHandler *chPtr, *chNext; /* Iterate over channel handlers. */
1.2697 + EventScriptRecord *ePtr, *eNextPtr; /* Iterate over eventscript records. */
1.2698 + Channel *chanPtr; /* The real IO channel. */
1.2699 + ChannelState *statePtr; /* State of real IO channel. */
1.2700 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.2701 + NextChannelHandler *nhPtr;
1.2702 +
1.2703 + /*
1.2704 + * This operation should occur at the top of a channel stack.
1.2705 + */
1.2706 +
1.2707 + chanPtr = (Channel *) channel;
1.2708 + statePtr = chanPtr->state;
1.2709 + chanPtr = statePtr->topChanPtr;
1.2710 +
1.2711 + /*
1.2712 + * Cancel any outstanding timer.
1.2713 + */
1.2714 +
1.2715 + Tcl_DeleteTimerHandler(statePtr->timer);
1.2716 +
1.2717 + /*
1.2718 + * Remove any references to channel handlers for this channel that
1.2719 + * may be about to be invoked.
1.2720 + */
1.2721 +
1.2722 + for (nhPtr = tsdPtr->nestedHandlerPtr;
1.2723 + nhPtr != (NextChannelHandler *) NULL;
1.2724 + nhPtr = nhPtr->nestedHandlerPtr) {
1.2725 + if (nhPtr->nextHandlerPtr &&
1.2726 + (nhPtr->nextHandlerPtr->chanPtr == chanPtr)) {
1.2727 + nhPtr->nextHandlerPtr = NULL;
1.2728 + }
1.2729 + }
1.2730 +
1.2731 + /*
1.2732 + * Remove all the channel handler records attached to the channel
1.2733 + * itself.
1.2734 + */
1.2735 +
1.2736 + for (chPtr = statePtr->chPtr;
1.2737 + chPtr != (ChannelHandler *) NULL;
1.2738 + chPtr = chNext) {
1.2739 + chNext = chPtr->nextPtr;
1.2740 + ckfree((char *) chPtr);
1.2741 + }
1.2742 + statePtr->chPtr = (ChannelHandler *) NULL;
1.2743 +
1.2744 + /*
1.2745 + * Cancel any pending copy operation.
1.2746 + */
1.2747 +
1.2748 + StopCopy(statePtr->csPtr);
1.2749 +
1.2750 + /*
1.2751 + * Must set the interest mask now to 0, otherwise infinite loops
1.2752 + * will occur if Tcl_DoOneEvent is called before the channel is
1.2753 + * finally deleted in FlushChannel. This can happen if the channel
1.2754 + * has a background flush active.
1.2755 + */
1.2756 +
1.2757 + statePtr->interestMask = 0;
1.2758 +
1.2759 + /*
1.2760 + * Remove any EventScript records for this channel.
1.2761 + */
1.2762 +
1.2763 + for (ePtr = statePtr->scriptRecordPtr;
1.2764 + ePtr != (EventScriptRecord *) NULL;
1.2765 + ePtr = eNextPtr) {
1.2766 + eNextPtr = ePtr->nextPtr;
1.2767 + Tcl_DecrRefCount(ePtr->scriptPtr);
1.2768 + ckfree((char *) ePtr);
1.2769 + }
1.2770 + statePtr->scriptRecordPtr = (EventScriptRecord *) NULL;
1.2771 +}
1.2772 +
1.2773 +/*
1.2774 + *----------------------------------------------------------------------
1.2775 + *
1.2776 + * Tcl_Write --
1.2777 + *
1.2778 + * Puts a sequence of bytes into an output buffer, may queue the
1.2779 + * buffer for output if it gets full, and also remembers whether the
1.2780 + * current buffer is ready e.g. if it contains a newline and we are in
1.2781 + * line buffering mode. Compensates stacking, i.e. will redirect the
1.2782 + * data from the specified channel to the topmost channel in a stack.
1.2783 + *
1.2784 + * No encoding conversions are applied to the bytes being read.
1.2785 + *
1.2786 + * Results:
1.2787 + * The number of bytes written or -1 in case of error. If -1,
1.2788 + * Tcl_GetErrno will return the error code.
1.2789 + *
1.2790 + * Side effects:
1.2791 + * May buffer up output and may cause output to be produced on the
1.2792 + * channel.
1.2793 + *
1.2794 + *----------------------------------------------------------------------
1.2795 + */
1.2796 +
1.2797 +EXPORT_C int
1.2798 +Tcl_Write(chan, src, srcLen)
1.2799 + Tcl_Channel chan; /* The channel to buffer output for. */
1.2800 + CONST char *src; /* Data to queue in output buffer. */
1.2801 + int srcLen; /* Length of data in bytes, or < 0 for
1.2802 + * strlen(). */
1.2803 +{
1.2804 + /*
1.2805 + * Always use the topmost channel of the stack
1.2806 + */
1.2807 + Channel *chanPtr;
1.2808 + ChannelState *statePtr; /* state info for channel */
1.2809 +
1.2810 + statePtr = ((Channel *) chan)->state;
1.2811 + chanPtr = statePtr->topChanPtr;
1.2812 +
1.2813 + if (CheckChannelErrors(statePtr, TCL_WRITABLE) != 0) {
1.2814 + return -1;
1.2815 + }
1.2816 +
1.2817 + if (srcLen < 0) {
1.2818 + srcLen = strlen(src);
1.2819 + }
1.2820 + return DoWrite(chanPtr, src, srcLen);
1.2821 +}
1.2822 +
1.2823 +/*
1.2824 + *----------------------------------------------------------------------
1.2825 + *
1.2826 + * Tcl_WriteRaw --
1.2827 + *
1.2828 + * Puts a sequence of bytes into an output buffer, may queue the
1.2829 + * buffer for output if it gets full, and also remembers whether the
1.2830 + * current buffer is ready e.g. if it contains a newline and we are in
1.2831 + * line buffering mode. Writes directly to the driver of the channel,
1.2832 + * does not compensate for stacking.
1.2833 + *
1.2834 + * No encoding conversions are applied to the bytes being read.
1.2835 + *
1.2836 + * Results:
1.2837 + * The number of bytes written or -1 in case of error. If -1,
1.2838 + * Tcl_GetErrno will return the error code.
1.2839 + *
1.2840 + * Side effects:
1.2841 + * May buffer up output and may cause output to be produced on the
1.2842 + * channel.
1.2843 + *
1.2844 + *----------------------------------------------------------------------
1.2845 + */
1.2846 +
1.2847 +EXPORT_C int
1.2848 +Tcl_WriteRaw(chan, src, srcLen)
1.2849 + Tcl_Channel chan; /* The channel to buffer output for. */
1.2850 + CONST char *src; /* Data to queue in output buffer. */
1.2851 + int srcLen; /* Length of data in bytes, or < 0 for
1.2852 + * strlen(). */
1.2853 +{
1.2854 + Channel *chanPtr = ((Channel *) chan);
1.2855 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.2856 + int errorCode, written;
1.2857 +
1.2858 + if (CheckChannelErrors(statePtr, TCL_WRITABLE | CHANNEL_RAW_MODE) != 0) {
1.2859 + return -1;
1.2860 + }
1.2861 +
1.2862 + if (srcLen < 0) {
1.2863 + srcLen = strlen(src);
1.2864 + }
1.2865 +
1.2866 + /*
1.2867 + * Go immediately to the driver, do all the error handling by ourselves.
1.2868 + * The code was stolen from 'FlushChannel'.
1.2869 + */
1.2870 +
1.2871 + written = (chanPtr->typePtr->outputProc) (chanPtr->instanceData,
1.2872 + src, srcLen, &errorCode);
1.2873 +
1.2874 + if (written < 0) {
1.2875 + Tcl_SetErrno(errorCode);
1.2876 + }
1.2877 +
1.2878 + return written;
1.2879 +}
1.2880 +
1.2881 +/*
1.2882 + *---------------------------------------------------------------------------
1.2883 + *
1.2884 + * Tcl_WriteChars --
1.2885 + *
1.2886 + * Takes a sequence of UTF-8 characters and converts them for output
1.2887 + * using the channel's current encoding, may queue the buffer for
1.2888 + * output if it gets full, and also remembers whether the current
1.2889 + * buffer is ready e.g. if it contains a newline and we are in
1.2890 + * line buffering mode. Compensates stacking, i.e. will redirect the
1.2891 + * data from the specified channel to the topmost channel in a stack.
1.2892 + *
1.2893 + * Results:
1.2894 + * The number of bytes written or -1 in case of error. If -1,
1.2895 + * Tcl_GetErrno will return the error code.
1.2896 + *
1.2897 + * Side effects:
1.2898 + * May buffer up output and may cause output to be produced on the
1.2899 + * channel.
1.2900 + *
1.2901 + *----------------------------------------------------------------------
1.2902 + */
1.2903 +
1.2904 +EXPORT_C int
1.2905 +Tcl_WriteChars(chan, src, len)
1.2906 + Tcl_Channel chan; /* The channel to buffer output for. */
1.2907 + CONST char *src; /* UTF-8 characters to queue in output buffer. */
1.2908 + int len; /* Length of string in bytes, or < 0 for
1.2909 + * strlen(). */
1.2910 +{
1.2911 + ChannelState *statePtr; /* state info for channel */
1.2912 +
1.2913 + statePtr = ((Channel *) chan)->state;
1.2914 +
1.2915 + if (CheckChannelErrors(statePtr, TCL_WRITABLE) != 0) {
1.2916 + return -1;
1.2917 + }
1.2918 +
1.2919 + return DoWriteChars ((Channel*) chan, src, len);
1.2920 +}
1.2921 +
1.2922 +/*
1.2923 + *---------------------------------------------------------------------------
1.2924 + *
1.2925 + * DoWriteChars --
1.2926 + *
1.2927 + * Takes a sequence of UTF-8 characters and converts them for output
1.2928 + * using the channel's current encoding, may queue the buffer for
1.2929 + * output if it gets full, and also remembers whether the current
1.2930 + * buffer is ready e.g. if it contains a newline and we are in
1.2931 + * line buffering mode. Compensates stacking, i.e. will redirect the
1.2932 + * data from the specified channel to the topmost channel in a stack.
1.2933 + *
1.2934 + * Results:
1.2935 + * The number of bytes written or -1 in case of error. If -1,
1.2936 + * Tcl_GetErrno will return the error code.
1.2937 + *
1.2938 + * Side effects:
1.2939 + * May buffer up output and may cause output to be produced on the
1.2940 + * channel.
1.2941 + *
1.2942 + *----------------------------------------------------------------------
1.2943 + */
1.2944 +
1.2945 +static int
1.2946 +DoWriteChars(chanPtr, src, len)
1.2947 + Channel* chanPtr; /* The channel to buffer output for. */
1.2948 + CONST char *src; /* UTF-8 characters to queue in output buffer. */
1.2949 + int len; /* Length of string in bytes, or < 0 for
1.2950 + * strlen(). */
1.2951 +{
1.2952 + /*
1.2953 + * Always use the topmost channel of the stack
1.2954 + */
1.2955 + ChannelState *statePtr; /* state info for channel */
1.2956 +
1.2957 + statePtr = chanPtr->state;
1.2958 + chanPtr = statePtr->topChanPtr;
1.2959 +
1.2960 + if (len < 0) {
1.2961 + len = strlen(src);
1.2962 + }
1.2963 + if (statePtr->encoding == NULL) {
1.2964 + /*
1.2965 + * Inefficient way to convert UTF-8 to byte-array, but the
1.2966 + * code parallels the way it is done for objects.
1.2967 + */
1.2968 +
1.2969 + Tcl_Obj *objPtr;
1.2970 + int result;
1.2971 +
1.2972 + objPtr = Tcl_NewStringObj(src, len);
1.2973 + src = (char *) Tcl_GetByteArrayFromObj(objPtr, &len);
1.2974 + result = WriteBytes(chanPtr, src, len);
1.2975 + Tcl_DecrRefCount(objPtr);
1.2976 + return result;
1.2977 + }
1.2978 + return WriteChars(chanPtr, src, len);
1.2979 +}
1.2980 +
1.2981 +/*
1.2982 + *---------------------------------------------------------------------------
1.2983 + *
1.2984 + * Tcl_WriteObj --
1.2985 + *
1.2986 + * Takes the Tcl object and queues its contents for output. If the
1.2987 + * encoding of the channel is NULL, takes the byte-array representation
1.2988 + * of the object and queues those bytes for output. Otherwise, takes
1.2989 + * the characters in the UTF-8 (string) representation of the object
1.2990 + * and converts them for output using the channel's current encoding.
1.2991 + * May flush internal buffers to output if one becomes full or is ready
1.2992 + * for some other reason, e.g. if it contains a newline and the channel
1.2993 + * is in line buffering mode.
1.2994 + *
1.2995 + * Results:
1.2996 + * The number of bytes written or -1 in case of error. If -1,
1.2997 + * Tcl_GetErrno() will return the error code.
1.2998 + *
1.2999 + * Side effects:
1.3000 + * May buffer up output and may cause output to be produced on the
1.3001 + * channel.
1.3002 + *
1.3003 + *----------------------------------------------------------------------
1.3004 + */
1.3005 +
1.3006 +EXPORT_C int
1.3007 +Tcl_WriteObj(chan, objPtr)
1.3008 + Tcl_Channel chan; /* The channel to buffer output for. */
1.3009 + Tcl_Obj *objPtr; /* The object to write. */
1.3010 +{
1.3011 + /*
1.3012 + * Always use the topmost channel of the stack
1.3013 + */
1.3014 + Channel *chanPtr;
1.3015 + ChannelState *statePtr; /* state info for channel */
1.3016 + char *src;
1.3017 + int srcLen;
1.3018 +
1.3019 + statePtr = ((Channel *) chan)->state;
1.3020 + chanPtr = statePtr->topChanPtr;
1.3021 +
1.3022 + if (CheckChannelErrors(statePtr, TCL_WRITABLE) != 0) {
1.3023 + return -1;
1.3024 + }
1.3025 + if (statePtr->encoding == NULL) {
1.3026 + src = (char *) Tcl_GetByteArrayFromObj(objPtr, &srcLen);
1.3027 + return WriteBytes(chanPtr, src, srcLen);
1.3028 + } else {
1.3029 + src = Tcl_GetStringFromObj(objPtr, &srcLen);
1.3030 + return WriteChars(chanPtr, src, srcLen);
1.3031 + }
1.3032 +}
1.3033 +
1.3034 +/*
1.3035 + *----------------------------------------------------------------------
1.3036 + *
1.3037 + * WriteBytes --
1.3038 + *
1.3039 + * Write a sequence of bytes into an output buffer, may queue the
1.3040 + * buffer for output if it gets full, and also remembers whether the
1.3041 + * current buffer is ready e.g. if it contains a newline and we are in
1.3042 + * line buffering mode.
1.3043 + *
1.3044 + * Results:
1.3045 + * The number of bytes written or -1 in case of error. If -1,
1.3046 + * Tcl_GetErrno will return the error code.
1.3047 + *
1.3048 + * Side effects:
1.3049 + * May buffer up output and may cause output to be produced on the
1.3050 + * channel.
1.3051 + *
1.3052 + *----------------------------------------------------------------------
1.3053 + */
1.3054 +
1.3055 +static int
1.3056 +WriteBytes(chanPtr, src, srcLen)
1.3057 + Channel *chanPtr; /* The channel to buffer output for. */
1.3058 + CONST char *src; /* Bytes to write. */
1.3059 + int srcLen; /* Number of bytes to write. */
1.3060 +{
1.3061 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.3062 + ChannelBuffer *bufPtr;
1.3063 + char *dst;
1.3064 + int dstMax, sawLF, savedLF, total, dstLen, toWrite;
1.3065 +
1.3066 + total = 0;
1.3067 + sawLF = 0;
1.3068 + savedLF = 0;
1.3069 +
1.3070 + /*
1.3071 + * Loop over all bytes in src, storing them in output buffer with
1.3072 + * proper EOL translation.
1.3073 + */
1.3074 +
1.3075 + while (srcLen + savedLF > 0) {
1.3076 + bufPtr = statePtr->curOutPtr;
1.3077 + if (bufPtr == NULL) {
1.3078 + bufPtr = AllocChannelBuffer(statePtr->bufSize);
1.3079 + statePtr->curOutPtr = bufPtr;
1.3080 + }
1.3081 + dst = bufPtr->buf + bufPtr->nextAdded;
1.3082 + dstMax = bufPtr->bufLength - bufPtr->nextAdded;
1.3083 + dstLen = dstMax;
1.3084 +
1.3085 + toWrite = dstLen;
1.3086 + if (toWrite > srcLen) {
1.3087 + toWrite = srcLen;
1.3088 + }
1.3089 +
1.3090 + if (savedLF) {
1.3091 + /*
1.3092 + * A '\n' was left over from last call to TranslateOutputEOL()
1.3093 + * and we need to store it in this buffer. If the channel is
1.3094 + * line-based, we will need to flush it.
1.3095 + */
1.3096 +
1.3097 + *dst++ = '\n';
1.3098 + dstLen--;
1.3099 + sawLF++;
1.3100 + }
1.3101 + sawLF += TranslateOutputEOL(statePtr, dst, src, &dstLen, &toWrite);
1.3102 + dstLen += savedLF;
1.3103 + savedLF = 0;
1.3104 +
1.3105 + if (dstLen > dstMax) {
1.3106 + savedLF = 1;
1.3107 + dstLen = dstMax;
1.3108 + }
1.3109 + bufPtr->nextAdded += dstLen;
1.3110 + if (CheckFlush(chanPtr, bufPtr, sawLF) != 0) {
1.3111 + return -1;
1.3112 + }
1.3113 + total += dstLen;
1.3114 + src += toWrite;
1.3115 + srcLen -= toWrite;
1.3116 + sawLF = 0;
1.3117 + }
1.3118 + return total;
1.3119 +}
1.3120 +
1.3121 +/*
1.3122 + *----------------------------------------------------------------------
1.3123 + *
1.3124 + * WriteChars --
1.3125 + *
1.3126 + * Convert UTF-8 bytes to the channel's external encoding and
1.3127 + * write the produced bytes into an output buffer, may queue the
1.3128 + * buffer for output if it gets full, and also remembers whether the
1.3129 + * current buffer is ready e.g. if it contains a newline and we are in
1.3130 + * line buffering mode.
1.3131 + *
1.3132 + * Results:
1.3133 + * The number of bytes written or -1 in case of error. If -1,
1.3134 + * Tcl_GetErrno will return the error code.
1.3135 + *
1.3136 + * Side effects:
1.3137 + * May buffer up output and may cause output to be produced on the
1.3138 + * channel.
1.3139 + *
1.3140 + *----------------------------------------------------------------------
1.3141 + */
1.3142 +
1.3143 +static int
1.3144 +WriteChars(chanPtr, src, srcLen)
1.3145 + Channel *chanPtr; /* The channel to buffer output for. */
1.3146 + CONST char *src; /* UTF-8 string to write. */
1.3147 + int srcLen; /* Length of UTF-8 string in bytes. */
1.3148 +{
1.3149 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.3150 + ChannelBuffer *bufPtr;
1.3151 + char *dst, *stage;
1.3152 + int saved, savedLF, sawLF, total, dstLen, stageMax, dstWrote;
1.3153 + int stageLen, toWrite, stageRead, endEncoding, result;
1.3154 + int consumedSomething;
1.3155 + Tcl_Encoding encoding;
1.3156 + char safe[BUFFER_PADDING];
1.3157 +
1.3158 + total = 0;
1.3159 + sawLF = 0;
1.3160 + savedLF = 0;
1.3161 + saved = 0;
1.3162 + encoding = statePtr->encoding;
1.3163 +
1.3164 + /*
1.3165 + * Write the terminated escape sequence even if srcLen is 0.
1.3166 + */
1.3167 +
1.3168 + endEncoding = ((statePtr->outputEncodingFlags & TCL_ENCODING_END) != 0);
1.3169 +
1.3170 + /*
1.3171 + * Loop over all UTF-8 characters in src, storing them in staging buffer
1.3172 + * with proper EOL translation.
1.3173 + */
1.3174 +
1.3175 + consumedSomething = 1;
1.3176 + while (consumedSomething && (srcLen + savedLF + endEncoding > 0)) {
1.3177 + consumedSomething = 0;
1.3178 + stage = statePtr->outputStage;
1.3179 + stageMax = statePtr->bufSize;
1.3180 + stageLen = stageMax;
1.3181 +
1.3182 + toWrite = stageLen;
1.3183 + if (toWrite > srcLen) {
1.3184 + toWrite = srcLen;
1.3185 + }
1.3186 +
1.3187 + if (savedLF) {
1.3188 + /*
1.3189 + * A '\n' was left over from last call to TranslateOutputEOL()
1.3190 + * and we need to store it in the staging buffer. If the
1.3191 + * channel is line-based, we will need to flush the output
1.3192 + * buffer (after translating the staging buffer).
1.3193 + */
1.3194 +
1.3195 + *stage++ = '\n';
1.3196 + stageLen--;
1.3197 + sawLF++;
1.3198 + }
1.3199 + sawLF += TranslateOutputEOL(statePtr, stage, src, &stageLen, &toWrite);
1.3200 +
1.3201 + stage -= savedLF;
1.3202 + stageLen += savedLF;
1.3203 + savedLF = 0;
1.3204 +
1.3205 + if (stageLen > stageMax) {
1.3206 + savedLF = 1;
1.3207 + stageLen = stageMax;
1.3208 + }
1.3209 + src += toWrite;
1.3210 + srcLen -= toWrite;
1.3211 +
1.3212 + /*
1.3213 + * Loop over all UTF-8 characters in staging buffer, converting them
1.3214 + * to external encoding, storing them in output buffer.
1.3215 + */
1.3216 +
1.3217 + while (stageLen + saved + endEncoding > 0) {
1.3218 + bufPtr = statePtr->curOutPtr;
1.3219 + if (bufPtr == NULL) {
1.3220 + bufPtr = AllocChannelBuffer(statePtr->bufSize);
1.3221 + statePtr->curOutPtr = bufPtr;
1.3222 + }
1.3223 + dst = bufPtr->buf + bufPtr->nextAdded;
1.3224 + dstLen = bufPtr->bufLength - bufPtr->nextAdded;
1.3225 +
1.3226 + if (saved != 0) {
1.3227 + /*
1.3228 + * Here's some translated bytes left over from the last
1.3229 + * buffer that we need to stick at the beginning of this
1.3230 + * buffer.
1.3231 + */
1.3232 +
1.3233 + memcpy((VOID *) dst, (VOID *) safe, (size_t) saved);
1.3234 + bufPtr->nextAdded += saved;
1.3235 + dst += saved;
1.3236 + dstLen -= saved;
1.3237 + saved = 0;
1.3238 + }
1.3239 +
1.3240 + result = Tcl_UtfToExternal(NULL, encoding, stage, stageLen,
1.3241 + statePtr->outputEncodingFlags,
1.3242 + &statePtr->outputEncodingState, dst,
1.3243 + dstLen + BUFFER_PADDING, &stageRead, &dstWrote, NULL);
1.3244 +
1.3245 + /* Fix for SF #506297, reported by Martin Forssen
1.3246 + * <ruric@users.sourceforge.net>.
1.3247 + *
1.3248 + * The encoding chosen in the script exposing the bug writes out
1.3249 + * three intro characters when TCL_ENCODING_START is set, but does
1.3250 + * not consume any input as TCL_ENCODING_END is cleared. As some
1.3251 + * output was generated the enclosing loop calls UtfToExternal
1.3252 + * again, again with START set. Three more characters in the out
1.3253 + * and still no use of input ... To break this infinite loop we
1.3254 + * remove TCL_ENCODING_START from the set of flags after the first
1.3255 + * call (no condition is required, the later calls remove an unset
1.3256 + * flag, which is a no-op). This causes the subsequent calls to
1.3257 + * UtfToExternal to consume and convert the actual input.
1.3258 + */
1.3259 +
1.3260 + statePtr->outputEncodingFlags &= ~TCL_ENCODING_START;
1.3261 + /*
1.3262 + * The following code must be executed only when result is not 0.
1.3263 + */
1.3264 + if (result && ((stageRead + dstWrote) == 0)) {
1.3265 + /*
1.3266 + * We have an incomplete UTF-8 character at the end of the
1.3267 + * staging buffer. It will get moved to the beginning of the
1.3268 + * staging buffer followed by more bytes from src.
1.3269 + */
1.3270 +
1.3271 + src -= stageLen;
1.3272 + srcLen += stageLen;
1.3273 + stageLen = 0;
1.3274 + savedLF = 0;
1.3275 + break;
1.3276 + }
1.3277 + bufPtr->nextAdded += dstWrote;
1.3278 + if (bufPtr->nextAdded > bufPtr->bufLength) {
1.3279 + /*
1.3280 + * When translating from UTF-8 to external encoding, we
1.3281 + * allowed the translation to produce a character that
1.3282 + * crossed the end of the output buffer, so that we would
1.3283 + * get a completely full buffer before flushing it. The
1.3284 + * extra bytes will be moved to the beginning of the next
1.3285 + * buffer.
1.3286 + */
1.3287 +
1.3288 + saved = bufPtr->nextAdded - bufPtr->bufLength;
1.3289 + memcpy((VOID *) safe, (VOID *) (dst + dstLen), (size_t) saved);
1.3290 + bufPtr->nextAdded = bufPtr->bufLength;
1.3291 + }
1.3292 + if (CheckFlush(chanPtr, bufPtr, sawLF) != 0) {
1.3293 + return -1;
1.3294 + }
1.3295 +
1.3296 + total += dstWrote;
1.3297 + stage += stageRead;
1.3298 + stageLen -= stageRead;
1.3299 + sawLF = 0;
1.3300 +
1.3301 + consumedSomething = 1;
1.3302 +
1.3303 + /*
1.3304 + * If all translated characters are written to the buffer,
1.3305 + * endEncoding is set to 0 because the escape sequence may be
1.3306 + * output.
1.3307 + */
1.3308 +
1.3309 + if ((stageLen + saved == 0) && (result == 0)) {
1.3310 + endEncoding = 0;
1.3311 + }
1.3312 + }
1.3313 + }
1.3314 +
1.3315 + /* If nothing was written and it happened because there was no progress
1.3316 + * in the UTF conversion, we throw an error.
1.3317 + */
1.3318 +
1.3319 + if (!consumedSomething && (total == 0)) {
1.3320 + Tcl_SetErrno (EINVAL);
1.3321 + return -1;
1.3322 + }
1.3323 + return total;
1.3324 +}
1.3325 +
1.3326 +/*
1.3327 + *---------------------------------------------------------------------------
1.3328 + *
1.3329 + * TranslateOutputEOL --
1.3330 + *
1.3331 + * Helper function for WriteBytes() and WriteChars(). Converts the
1.3332 + * '\n' characters in the source buffer into the appropriate EOL
1.3333 + * form specified by the output translation mode.
1.3334 + *
1.3335 + * EOL translation stops either when the source buffer is empty
1.3336 + * or the output buffer is full.
1.3337 + *
1.3338 + * When converting to CRLF mode and there is only 1 byte left in
1.3339 + * the output buffer, this routine stores the '\r' in the last
1.3340 + * byte and then stores the '\n' in the byte just past the end of the
1.3341 + * buffer. The caller is responsible for passing in a buffer that
1.3342 + * is large enough to hold the extra byte.
1.3343 + *
1.3344 + * Results:
1.3345 + * The return value is 1 if a '\n' was translated from the source
1.3346 + * buffer, or 0 otherwise -- this can be used by the caller to
1.3347 + * decide to flush a line-based channel even though the channel
1.3348 + * buffer is not full.
1.3349 + *
1.3350 + * *dstLenPtr is filled with how many bytes of the output buffer
1.3351 + * were used. As mentioned above, this can be one more that
1.3352 + * the output buffer's specified length if a CRLF was stored.
1.3353 + *
1.3354 + * *srcLenPtr is filled with how many bytes of the source buffer
1.3355 + * were consumed.
1.3356 + *
1.3357 + * Side effects:
1.3358 + * It may be obvious, but bears mentioning that when converting
1.3359 + * in CRLF mode (which requires two bytes of storage in the output
1.3360 + * buffer), the number of bytes consumed from the source buffer
1.3361 + * will be less than the number of bytes stored in the output buffer.
1.3362 + *
1.3363 + *---------------------------------------------------------------------------
1.3364 + */
1.3365 +
1.3366 +static int
1.3367 +TranslateOutputEOL(statePtr, dst, src, dstLenPtr, srcLenPtr)
1.3368 + ChannelState *statePtr; /* Channel being read, for translation and
1.3369 + * buffering modes. */
1.3370 + char *dst; /* Output buffer filled with UTF-8 chars by
1.3371 + * applying appropriate EOL translation to
1.3372 + * source characters. */
1.3373 + CONST char *src; /* Source UTF-8 characters. */
1.3374 + int *dstLenPtr; /* On entry, the maximum length of output
1.3375 + * buffer in bytes. On exit, the number of
1.3376 + * bytes actually used in output buffer. */
1.3377 + int *srcLenPtr; /* On entry, the length of source buffer.
1.3378 + * On exit, the number of bytes read from
1.3379 + * the source buffer. */
1.3380 +{
1.3381 + char *dstEnd;
1.3382 + int srcLen, newlineFound;
1.3383 +
1.3384 + newlineFound = 0;
1.3385 + srcLen = *srcLenPtr;
1.3386 +
1.3387 + switch (statePtr->outputTranslation) {
1.3388 + case TCL_TRANSLATE_LF: {
1.3389 + for (dstEnd = dst + srcLen; dst < dstEnd; ) {
1.3390 + if (*src == '\n') {
1.3391 + newlineFound = 1;
1.3392 + }
1.3393 + *dst++ = *src++;
1.3394 + }
1.3395 + *dstLenPtr = srcLen;
1.3396 + break;
1.3397 + }
1.3398 + case TCL_TRANSLATE_CR: {
1.3399 + for (dstEnd = dst + srcLen; dst < dstEnd;) {
1.3400 + if (*src == '\n') {
1.3401 + *dst++ = '\r';
1.3402 + newlineFound = 1;
1.3403 + src++;
1.3404 + } else {
1.3405 + *dst++ = *src++;
1.3406 + }
1.3407 + }
1.3408 + *dstLenPtr = srcLen;
1.3409 + break;
1.3410 + }
1.3411 + case TCL_TRANSLATE_CRLF: {
1.3412 + /*
1.3413 + * Since this causes the number of bytes to grow, we
1.3414 + * start off trying to put 'srcLen' bytes into the
1.3415 + * output buffer, but allow it to store more bytes, as
1.3416 + * long as there's still source bytes and room in the
1.3417 + * output buffer.
1.3418 + */
1.3419 +
1.3420 + char *dstStart, *dstMax;
1.3421 + CONST char *srcStart;
1.3422 +
1.3423 + dstStart = dst;
1.3424 + dstMax = dst + *dstLenPtr;
1.3425 +
1.3426 + srcStart = src;
1.3427 +
1.3428 + if (srcLen < *dstLenPtr) {
1.3429 + dstEnd = dst + srcLen;
1.3430 + } else {
1.3431 + dstEnd = dst + *dstLenPtr;
1.3432 + }
1.3433 + while (dst < dstEnd) {
1.3434 + if (*src == '\n') {
1.3435 + if (dstEnd < dstMax) {
1.3436 + dstEnd++;
1.3437 + }
1.3438 + *dst++ = '\r';
1.3439 + newlineFound = 1;
1.3440 + }
1.3441 + *dst++ = *src++;
1.3442 + }
1.3443 + *srcLenPtr = src - srcStart;
1.3444 + *dstLenPtr = dst - dstStart;
1.3445 + break;
1.3446 + }
1.3447 + default: {
1.3448 + break;
1.3449 + }
1.3450 + }
1.3451 + return newlineFound;
1.3452 +}
1.3453 +
1.3454 +/*
1.3455 + *---------------------------------------------------------------------------
1.3456 + *
1.3457 + * CheckFlush --
1.3458 + *
1.3459 + * Helper function for WriteBytes() and WriteChars(). If the
1.3460 + * channel buffer is ready to be flushed, flush it.
1.3461 + *
1.3462 + * Results:
1.3463 + * The return value is -1 if there was a problem flushing the
1.3464 + * channel buffer, or 0 otherwise.
1.3465 + *
1.3466 + * Side effects:
1.3467 + * The buffer will be recycled if it is flushed.
1.3468 + *
1.3469 + *---------------------------------------------------------------------------
1.3470 + */
1.3471 +
1.3472 +static int
1.3473 +CheckFlush(chanPtr, bufPtr, newlineFlag)
1.3474 + Channel *chanPtr; /* Channel being read, for buffering mode. */
1.3475 + ChannelBuffer *bufPtr; /* Channel buffer to possibly flush. */
1.3476 + int newlineFlag; /* Non-zero if a the channel buffer
1.3477 + * contains a newline. */
1.3478 +{
1.3479 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.3480 + /*
1.3481 + * The current buffer is ready for output:
1.3482 + * 1. if it is full.
1.3483 + * 2. if it contains a newline and this channel is line-buffered.
1.3484 + * 3. if it contains any output and this channel is unbuffered.
1.3485 + */
1.3486 +
1.3487 + if ((statePtr->flags & BUFFER_READY) == 0) {
1.3488 + if (bufPtr->nextAdded == bufPtr->bufLength) {
1.3489 + statePtr->flags |= BUFFER_READY;
1.3490 + } else if (statePtr->flags & CHANNEL_LINEBUFFERED) {
1.3491 + if (newlineFlag != 0) {
1.3492 + statePtr->flags |= BUFFER_READY;
1.3493 + }
1.3494 + } else if (statePtr->flags & CHANNEL_UNBUFFERED) {
1.3495 + statePtr->flags |= BUFFER_READY;
1.3496 + }
1.3497 + }
1.3498 + if (statePtr->flags & BUFFER_READY) {
1.3499 + if (FlushChannel(NULL, chanPtr, 0) != 0) {
1.3500 + return -1;
1.3501 + }
1.3502 + }
1.3503 + return 0;
1.3504 +}
1.3505 +
1.3506 +/*
1.3507 + *---------------------------------------------------------------------------
1.3508 + *
1.3509 + * Tcl_Gets --
1.3510 + *
1.3511 + * Reads a complete line of input from the channel into a Tcl_DString.
1.3512 + *
1.3513 + * Results:
1.3514 + * Length of line read (in characters) or -1 if error, EOF, or blocked.
1.3515 + * If -1, use Tcl_GetErrno() to retrieve the POSIX error code for the
1.3516 + * error or condition that occurred.
1.3517 + *
1.3518 + * Side effects:
1.3519 + * May flush output on the channel. May cause input to be consumed
1.3520 + * from the channel.
1.3521 + *
1.3522 + *---------------------------------------------------------------------------
1.3523 + */
1.3524 +
1.3525 +EXPORT_C int
1.3526 +Tcl_Gets(chan, lineRead)
1.3527 + Tcl_Channel chan; /* Channel from which to read. */
1.3528 + Tcl_DString *lineRead; /* The line read will be appended to this
1.3529 + * DString as UTF-8 characters. The caller
1.3530 + * must have initialized it and is responsible
1.3531 + * for managing the storage. */
1.3532 +{
1.3533 + Tcl_Obj *objPtr;
1.3534 + int charsStored, length;
1.3535 + char *string;
1.3536 +
1.3537 + objPtr = Tcl_NewObj();
1.3538 + charsStored = Tcl_GetsObj(chan, objPtr);
1.3539 + if (charsStored > 0) {
1.3540 + string = Tcl_GetStringFromObj(objPtr, &length);
1.3541 + Tcl_DStringAppend(lineRead, string, length);
1.3542 + }
1.3543 + Tcl_DecrRefCount(objPtr);
1.3544 + return charsStored;
1.3545 +}
1.3546 +
1.3547 +/*
1.3548 + *---------------------------------------------------------------------------
1.3549 + *
1.3550 + * Tcl_GetsObj --
1.3551 + *
1.3552 + * Accumulate input from the input channel until end-of-line or
1.3553 + * end-of-file has been seen. Bytes read from the input channel
1.3554 + * are converted to UTF-8 using the encoding specified by the
1.3555 + * channel.
1.3556 + *
1.3557 + * Results:
1.3558 + * Number of characters accumulated in the object or -1 if error,
1.3559 + * blocked, or EOF. If -1, use Tcl_GetErrno() to retrieve the
1.3560 + * POSIX error code for the error or condition that occurred.
1.3561 + *
1.3562 + * Side effects:
1.3563 + * Consumes input from the channel.
1.3564 + *
1.3565 + * On reading EOF, leave channel pointing at EOF char.
1.3566 + * On reading EOL, leave channel pointing after EOL, but don't
1.3567 + * return EOL in dst buffer.
1.3568 + *
1.3569 + *---------------------------------------------------------------------------
1.3570 + */
1.3571 +
1.3572 +EXPORT_C int
1.3573 +Tcl_GetsObj(chan, objPtr)
1.3574 + Tcl_Channel chan; /* Channel from which to read. */
1.3575 + Tcl_Obj *objPtr; /* The line read will be appended to this
1.3576 + * object as UTF-8 characters. */
1.3577 +{
1.3578 + GetsState gs;
1.3579 + Channel *chanPtr = (Channel *) chan;
1.3580 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.3581 + ChannelBuffer *bufPtr;
1.3582 + int inEofChar, skip, copiedTotal, oldLength, oldFlags, oldRemoved;
1.3583 + Tcl_Encoding encoding;
1.3584 + char *dst, *dstEnd, *eol, *eof;
1.3585 + Tcl_EncodingState oldState;
1.3586 +
1.3587 + /*
1.3588 + * This operation should occur at the top of a channel stack.
1.3589 + */
1.3590 +
1.3591 + chanPtr = statePtr->topChanPtr;
1.3592 +
1.3593 + if (CheckChannelErrors(statePtr, TCL_READABLE) != 0) {
1.3594 + copiedTotal = -1;
1.3595 + goto done;
1.3596 + }
1.3597 +
1.3598 + bufPtr = statePtr->inQueueHead;
1.3599 + encoding = statePtr->encoding;
1.3600 +
1.3601 + /*
1.3602 + * Preserved so we can restore the channel's state in case we don't
1.3603 + * find a newline in the available input.
1.3604 + */
1.3605 +
1.3606 + Tcl_GetStringFromObj(objPtr, &oldLength);
1.3607 + oldFlags = statePtr->inputEncodingFlags;
1.3608 + oldState = statePtr->inputEncodingState;
1.3609 + oldRemoved = BUFFER_PADDING;
1.3610 + if (bufPtr != NULL) {
1.3611 + oldRemoved = bufPtr->nextRemoved;
1.3612 + }
1.3613 +
1.3614 + /*
1.3615 + * If there is no encoding, use "iso8859-1" -- Tcl_GetsObj() doesn't
1.3616 + * produce ByteArray objects. To avoid circularity problems,
1.3617 + * "iso8859-1" is builtin to Tcl.
1.3618 + */
1.3619 +
1.3620 + if (encoding == NULL) {
1.3621 + encoding = Tcl_GetEncoding(NULL, "iso8859-1");
1.3622 + }
1.3623 +
1.3624 + /*
1.3625 + * Object used by FilterInputBytes to keep track of how much data has
1.3626 + * been consumed from the channel buffers.
1.3627 + */
1.3628 +
1.3629 + gs.objPtr = objPtr;
1.3630 + gs.dstPtr = &dst;
1.3631 + gs.encoding = encoding;
1.3632 + gs.bufPtr = bufPtr;
1.3633 + gs.state = oldState;
1.3634 + gs.rawRead = 0;
1.3635 + gs.bytesWrote = 0;
1.3636 + gs.charsWrote = 0;
1.3637 + gs.totalChars = 0;
1.3638 +
1.3639 + dst = objPtr->bytes + oldLength;
1.3640 + dstEnd = dst;
1.3641 +
1.3642 + skip = 0;
1.3643 + eof = NULL;
1.3644 + inEofChar = statePtr->inEofChar;
1.3645 +
1.3646 + while (1) {
1.3647 + if (dst >= dstEnd) {
1.3648 + if (FilterInputBytes(chanPtr, &gs) != 0) {
1.3649 + goto restore;
1.3650 + }
1.3651 + dstEnd = dst + gs.bytesWrote;
1.3652 + }
1.3653 +
1.3654 + /*
1.3655 + * Remember if EOF char is seen, then look for EOL anyhow, because
1.3656 + * the EOL might be before the EOF char.
1.3657 + */
1.3658 +
1.3659 + if (inEofChar != '\0') {
1.3660 + for (eol = dst; eol < dstEnd; eol++) {
1.3661 + if (*eol == inEofChar) {
1.3662 + dstEnd = eol;
1.3663 + eof = eol;
1.3664 + break;
1.3665 + }
1.3666 + }
1.3667 + }
1.3668 +
1.3669 + /*
1.3670 + * On EOL, leave current file position pointing after the EOL, but
1.3671 + * don't store the EOL in the output string.
1.3672 + */
1.3673 +
1.3674 + switch (statePtr->inputTranslation) {
1.3675 + case TCL_TRANSLATE_LF: {
1.3676 + for (eol = dst; eol < dstEnd; eol++) {
1.3677 + if (*eol == '\n') {
1.3678 + skip = 1;
1.3679 + goto goteol;
1.3680 + }
1.3681 + }
1.3682 + break;
1.3683 + }
1.3684 + case TCL_TRANSLATE_CR: {
1.3685 + for (eol = dst; eol < dstEnd; eol++) {
1.3686 + if (*eol == '\r') {
1.3687 + skip = 1;
1.3688 + goto goteol;
1.3689 + }
1.3690 + }
1.3691 + break;
1.3692 + }
1.3693 + case TCL_TRANSLATE_CRLF: {
1.3694 + for (eol = dst; eol < dstEnd; eol++) {
1.3695 + if (*eol == '\r') {
1.3696 + eol++;
1.3697 + if (eol >= dstEnd) {
1.3698 + int offset;
1.3699 +
1.3700 + offset = eol - objPtr->bytes;
1.3701 + dst = dstEnd;
1.3702 + if (FilterInputBytes(chanPtr, &gs) != 0) {
1.3703 + goto restore;
1.3704 + }
1.3705 + dstEnd = dst + gs.bytesWrote;
1.3706 + eol = objPtr->bytes + offset;
1.3707 + if (eol >= dstEnd) {
1.3708 + skip = 0;
1.3709 + goto goteol;
1.3710 + }
1.3711 + }
1.3712 + if (*eol == '\n') {
1.3713 + eol--;
1.3714 + skip = 2;
1.3715 + goto goteol;
1.3716 + }
1.3717 + }
1.3718 + }
1.3719 + break;
1.3720 + }
1.3721 + case TCL_TRANSLATE_AUTO: {
1.3722 + eol = dst;
1.3723 + skip = 1;
1.3724 + if (statePtr->flags & INPUT_SAW_CR) {
1.3725 + statePtr->flags &= ~INPUT_SAW_CR;
1.3726 + if (*eol == '\n') {
1.3727 + /*
1.3728 + * Skip the raw bytes that make up the '\n'.
1.3729 + */
1.3730 +
1.3731 + char tmp[1 + TCL_UTF_MAX];
1.3732 + int rawRead;
1.3733 +
1.3734 + bufPtr = gs.bufPtr;
1.3735 + Tcl_ExternalToUtf(NULL, gs.encoding,
1.3736 + bufPtr->buf + bufPtr->nextRemoved,
1.3737 + gs.rawRead, statePtr->inputEncodingFlags,
1.3738 + &gs.state, tmp, 1 + TCL_UTF_MAX, &rawRead,
1.3739 + NULL, NULL);
1.3740 + bufPtr->nextRemoved += rawRead;
1.3741 + gs.rawRead -= rawRead;
1.3742 + gs.bytesWrote--;
1.3743 + gs.charsWrote--;
1.3744 + memmove(dst, dst + 1, (size_t) (dstEnd - dst));
1.3745 + dstEnd--;
1.3746 + }
1.3747 + }
1.3748 + for (eol = dst; eol < dstEnd; eol++) {
1.3749 + if (*eol == '\r') {
1.3750 + eol++;
1.3751 + if (eol == dstEnd) {
1.3752 + /*
1.3753 + * If buffer ended on \r, peek ahead to see if a
1.3754 + * \n is available.
1.3755 + */
1.3756 +
1.3757 + int offset;
1.3758 +
1.3759 + offset = eol - objPtr->bytes;
1.3760 + dst = dstEnd;
1.3761 + PeekAhead(chanPtr, &dstEnd, &gs);
1.3762 + eol = objPtr->bytes + offset;
1.3763 + if (eol >= dstEnd) {
1.3764 + eol--;
1.3765 + statePtr->flags |= INPUT_SAW_CR;
1.3766 + goto goteol;
1.3767 + }
1.3768 + }
1.3769 + if (*eol == '\n') {
1.3770 + skip++;
1.3771 + }
1.3772 + eol--;
1.3773 + goto goteol;
1.3774 + } else if (*eol == '\n') {
1.3775 + goto goteol;
1.3776 + }
1.3777 + }
1.3778 + }
1.3779 + }
1.3780 + if (eof != NULL) {
1.3781 + /*
1.3782 + * EOF character was seen. On EOF, leave current file position
1.3783 + * pointing at the EOF character, but don't store the EOF
1.3784 + * character in the output string.
1.3785 + */
1.3786 +
1.3787 + dstEnd = eof;
1.3788 + statePtr->flags |= (CHANNEL_EOF | CHANNEL_STICKY_EOF);
1.3789 + statePtr->inputEncodingFlags |= TCL_ENCODING_END;
1.3790 + }
1.3791 + if (statePtr->flags & CHANNEL_EOF) {
1.3792 + skip = 0;
1.3793 + eol = dstEnd;
1.3794 + if (eol == objPtr->bytes + oldLength) {
1.3795 + /*
1.3796 + * If we didn't append any bytes before encountering EOF,
1.3797 + * caller needs to see -1.
1.3798 + */
1.3799 +
1.3800 + Tcl_SetObjLength(objPtr, oldLength);
1.3801 + CommonGetsCleanup(chanPtr, encoding);
1.3802 + copiedTotal = -1;
1.3803 + goto done;
1.3804 + }
1.3805 + goto goteol;
1.3806 + }
1.3807 + dst = dstEnd;
1.3808 + }
1.3809 +
1.3810 + /*
1.3811 + * Found EOL or EOF, but the output buffer may now contain too many
1.3812 + * UTF-8 characters. We need to know how many raw bytes correspond to
1.3813 + * the number of UTF-8 characters we want, plus how many raw bytes
1.3814 + * correspond to the character(s) making up EOL (if any), so we can
1.3815 + * remove the correct number of bytes from the channel buffer.
1.3816 + */
1.3817 +
1.3818 + goteol:
1.3819 + bufPtr = gs.bufPtr;
1.3820 + statePtr->inputEncodingState = gs.state;
1.3821 + Tcl_ExternalToUtf(NULL, gs.encoding, bufPtr->buf + bufPtr->nextRemoved,
1.3822 + gs.rawRead, statePtr->inputEncodingFlags,
1.3823 + &statePtr->inputEncodingState, dst,
1.3824 + eol - dst + skip + TCL_UTF_MAX, &gs.rawRead, NULL,
1.3825 + &gs.charsWrote);
1.3826 + bufPtr->nextRemoved += gs.rawRead;
1.3827 +
1.3828 + /*
1.3829 + * Recycle all the emptied buffers.
1.3830 + */
1.3831 +
1.3832 + Tcl_SetObjLength(objPtr, eol - objPtr->bytes);
1.3833 + CommonGetsCleanup(chanPtr, encoding);
1.3834 + statePtr->flags &= ~CHANNEL_BLOCKED;
1.3835 + copiedTotal = gs.totalChars + gs.charsWrote - skip;
1.3836 + goto done;
1.3837 +
1.3838 + /*
1.3839 + * Couldn't get a complete line. This only happens if we get a error
1.3840 + * reading from the channel or we are non-blocking and there wasn't
1.3841 + * an EOL or EOF in the data available.
1.3842 + */
1.3843 +
1.3844 + restore:
1.3845 + bufPtr = statePtr->inQueueHead;
1.3846 + bufPtr->nextRemoved = oldRemoved;
1.3847 +
1.3848 + for (bufPtr = bufPtr->nextPtr; bufPtr != NULL; bufPtr = bufPtr->nextPtr) {
1.3849 + bufPtr->nextRemoved = BUFFER_PADDING;
1.3850 + }
1.3851 + CommonGetsCleanup(chanPtr, encoding);
1.3852 +
1.3853 + statePtr->inputEncodingState = oldState;
1.3854 + statePtr->inputEncodingFlags = oldFlags;
1.3855 + Tcl_SetObjLength(objPtr, oldLength);
1.3856 +
1.3857 + /*
1.3858 + * We didn't get a complete line so we need to indicate to UpdateInterest
1.3859 + * that the gets blocked. It will wait for more data instead of firing
1.3860 + * a timer, avoiding a busy wait. This is where we are assuming that the
1.3861 + * next operation is a gets. No more file events will be delivered on
1.3862 + * this channel until new data arrives or some operation is performed
1.3863 + * on the channel (e.g. gets, read, fconfigure) that changes the blocking
1.3864 + * state. Note that this means a file event will not be delivered even
1.3865 + * though a read would be able to consume the buffered data.
1.3866 + */
1.3867 +
1.3868 + statePtr->flags |= CHANNEL_NEED_MORE_DATA;
1.3869 + copiedTotal = -1;
1.3870 +
1.3871 + done:
1.3872 + /*
1.3873 + * Update the notifier state so we don't block while there is still
1.3874 + * data in the buffers.
1.3875 + */
1.3876 +
1.3877 + UpdateInterest(chanPtr);
1.3878 + return copiedTotal;
1.3879 +}
1.3880 +
1.3881 +/*
1.3882 + *---------------------------------------------------------------------------
1.3883 + *
1.3884 + * FilterInputBytes --
1.3885 + *
1.3886 + * Helper function for Tcl_GetsObj. Produces UTF-8 characters from
1.3887 + * raw bytes read from the channel.
1.3888 + *
1.3889 + * Consumes available bytes from channel buffers. When channel
1.3890 + * buffers are exhausted, reads more bytes from channel device into
1.3891 + * a new channel buffer. It is the caller's responsibility to
1.3892 + * free the channel buffers that have been exhausted.
1.3893 + *
1.3894 + * Results:
1.3895 + * The return value is -1 if there was an error reading from the
1.3896 + * channel, 0 otherwise.
1.3897 + *
1.3898 + * Side effects:
1.3899 + * Status object keeps track of how much data from channel buffers
1.3900 + * has been consumed and where UTF-8 bytes should be stored.
1.3901 + *
1.3902 + *---------------------------------------------------------------------------
1.3903 + */
1.3904 +
1.3905 +static int
1.3906 +FilterInputBytes(chanPtr, gsPtr)
1.3907 + Channel *chanPtr; /* Channel to read. */
1.3908 + GetsState *gsPtr; /* Current state of gets operation. */
1.3909 +{
1.3910 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.3911 + ChannelBuffer *bufPtr;
1.3912 + char *raw, *rawStart, *rawEnd;
1.3913 + char *dst;
1.3914 + int offset, toRead, dstNeeded, spaceLeft, result, rawLen, length;
1.3915 + Tcl_Obj *objPtr;
1.3916 +#define ENCODING_LINESIZE 20 /* Lower bound on how many bytes to convert
1.3917 + * at a time. Since we don't know a priori
1.3918 + * how many bytes of storage this many source
1.3919 + * bytes will use, we actually need at least
1.3920 + * ENCODING_LINESIZE * TCL_MAX_UTF bytes of
1.3921 + * room. */
1.3922 +
1.3923 + objPtr = gsPtr->objPtr;
1.3924 +
1.3925 + /*
1.3926 + * Subtract the number of bytes that were removed from channel buffer
1.3927 + * during last call.
1.3928 + */
1.3929 +
1.3930 + bufPtr = gsPtr->bufPtr;
1.3931 + if (bufPtr != NULL) {
1.3932 + bufPtr->nextRemoved += gsPtr->rawRead;
1.3933 + if (bufPtr->nextRemoved >= bufPtr->nextAdded) {
1.3934 + bufPtr = bufPtr->nextPtr;
1.3935 + }
1.3936 + }
1.3937 + gsPtr->totalChars += gsPtr->charsWrote;
1.3938 +
1.3939 + if ((bufPtr == NULL) || (bufPtr->nextAdded == BUFFER_PADDING)) {
1.3940 + /*
1.3941 + * All channel buffers were exhausted and the caller still hasn't
1.3942 + * seen EOL. Need to read more bytes from the channel device.
1.3943 + * Side effect is to allocate another channel buffer.
1.3944 + */
1.3945 +
1.3946 + read:
1.3947 + if (statePtr->flags & CHANNEL_BLOCKED) {
1.3948 + if (statePtr->flags & CHANNEL_NONBLOCKING) {
1.3949 + gsPtr->charsWrote = 0;
1.3950 + gsPtr->rawRead = 0;
1.3951 + return -1;
1.3952 + }
1.3953 + statePtr->flags &= ~CHANNEL_BLOCKED;
1.3954 + }
1.3955 + if (GetInput(chanPtr) != 0) {
1.3956 + gsPtr->charsWrote = 0;
1.3957 + gsPtr->rawRead = 0;
1.3958 + return -1;
1.3959 + }
1.3960 + bufPtr = statePtr->inQueueTail;
1.3961 + gsPtr->bufPtr = bufPtr;
1.3962 + }
1.3963 +
1.3964 + /*
1.3965 + * Convert some of the bytes from the channel buffer to UTF-8. Space in
1.3966 + * objPtr's string rep is used to hold the UTF-8 characters. Grow the
1.3967 + * string rep if we need more space.
1.3968 + */
1.3969 +
1.3970 + rawStart = bufPtr->buf + bufPtr->nextRemoved;
1.3971 + raw = rawStart;
1.3972 + rawEnd = bufPtr->buf + bufPtr->nextAdded;
1.3973 + rawLen = rawEnd - rawStart;
1.3974 +
1.3975 + dst = *gsPtr->dstPtr;
1.3976 + offset = dst - objPtr->bytes;
1.3977 + toRead = ENCODING_LINESIZE;
1.3978 + if (toRead > rawLen) {
1.3979 + toRead = rawLen;
1.3980 + }
1.3981 + dstNeeded = toRead * TCL_UTF_MAX + 1;
1.3982 + spaceLeft = objPtr->length - offset - TCL_UTF_MAX - 1;
1.3983 + if (dstNeeded > spaceLeft) {
1.3984 + length = offset * 2;
1.3985 + if (offset < dstNeeded) {
1.3986 + length = offset + dstNeeded;
1.3987 + }
1.3988 + length += TCL_UTF_MAX + 1;
1.3989 + Tcl_SetObjLength(objPtr, length);
1.3990 + spaceLeft = length - offset;
1.3991 + dst = objPtr->bytes + offset;
1.3992 + *gsPtr->dstPtr = dst;
1.3993 + }
1.3994 + gsPtr->state = statePtr->inputEncodingState;
1.3995 + result = Tcl_ExternalToUtf(NULL, gsPtr->encoding, raw, rawLen,
1.3996 + statePtr->inputEncodingFlags, &statePtr->inputEncodingState,
1.3997 + dst, spaceLeft, &gsPtr->rawRead, &gsPtr->bytesWrote,
1.3998 + &gsPtr->charsWrote);
1.3999 +
1.4000 + /*
1.4001 + * Make sure that if we go through 'gets', that we reset the
1.4002 + * TCL_ENCODING_START flag still. [Bug #523988]
1.4003 + */
1.4004 + statePtr->inputEncodingFlags &= ~TCL_ENCODING_START;
1.4005 +
1.4006 + if (result == TCL_CONVERT_MULTIBYTE) {
1.4007 + /*
1.4008 + * The last few bytes in this channel buffer were the start of a
1.4009 + * multibyte sequence. If this buffer was full, then move them to
1.4010 + * the next buffer so the bytes will be contiguous.
1.4011 + */
1.4012 +
1.4013 + ChannelBuffer *nextPtr;
1.4014 + int extra;
1.4015 +
1.4016 + nextPtr = bufPtr->nextPtr;
1.4017 + if (bufPtr->nextAdded < bufPtr->bufLength) {
1.4018 + if (gsPtr->rawRead > 0) {
1.4019 + /*
1.4020 + * Some raw bytes were converted to UTF-8. Fall through,
1.4021 + * returning those UTF-8 characters because a EOL might be
1.4022 + * present in them.
1.4023 + */
1.4024 + } else if (statePtr->flags & CHANNEL_EOF) {
1.4025 + /*
1.4026 + * There was a partial character followed by EOF on the
1.4027 + * device. Fall through, returning that nothing was found.
1.4028 + */
1.4029 +
1.4030 + bufPtr->nextRemoved = bufPtr->nextAdded;
1.4031 + } else {
1.4032 + /*
1.4033 + * There are no more cached raw bytes left. See if we can
1.4034 + * get some more.
1.4035 + */
1.4036 +
1.4037 + goto read;
1.4038 + }
1.4039 + } else {
1.4040 + if (nextPtr == NULL) {
1.4041 + nextPtr = AllocChannelBuffer(statePtr->bufSize);
1.4042 + bufPtr->nextPtr = nextPtr;
1.4043 + statePtr->inQueueTail = nextPtr;
1.4044 + }
1.4045 + extra = rawLen - gsPtr->rawRead;
1.4046 + memcpy((VOID *) (nextPtr->buf + BUFFER_PADDING - extra),
1.4047 + (VOID *) (raw + gsPtr->rawRead), (size_t) extra);
1.4048 + nextPtr->nextRemoved -= extra;
1.4049 + bufPtr->nextAdded -= extra;
1.4050 + }
1.4051 + }
1.4052 +
1.4053 + gsPtr->bufPtr = bufPtr;
1.4054 + return 0;
1.4055 +}
1.4056 +
1.4057 +/*
1.4058 + *---------------------------------------------------------------------------
1.4059 + *
1.4060 + * PeekAhead --
1.4061 + *
1.4062 + * Helper function used by Tcl_GetsObj(). Called when we've seen a
1.4063 + * \r at the end of the UTF-8 string and want to look ahead one
1.4064 + * character to see if it is a \n.
1.4065 + *
1.4066 + * Results:
1.4067 + * *gsPtr->dstPtr is filled with a pointer to the start of the range of
1.4068 + * UTF-8 characters that were found by peeking and *dstEndPtr is filled
1.4069 + * with a pointer to the bytes just after the end of the range.
1.4070 + *
1.4071 + * Side effects:
1.4072 + * If no more raw bytes were available in one of the channel buffers,
1.4073 + * tries to perform a non-blocking read to get more bytes from the
1.4074 + * channel device.
1.4075 + *
1.4076 + *---------------------------------------------------------------------------
1.4077 + */
1.4078 +
1.4079 +static void
1.4080 +PeekAhead(chanPtr, dstEndPtr, gsPtr)
1.4081 + Channel *chanPtr; /* The channel to read. */
1.4082 + char **dstEndPtr; /* Filled with pointer to end of new range
1.4083 + * of UTF-8 characters. */
1.4084 + GetsState *gsPtr; /* Current state of gets operation. */
1.4085 +{
1.4086 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.4087 + ChannelBuffer *bufPtr;
1.4088 + Tcl_DriverBlockModeProc *blockModeProc;
1.4089 + int bytesLeft;
1.4090 +
1.4091 + bufPtr = gsPtr->bufPtr;
1.4092 +
1.4093 + /*
1.4094 + * If there's any more raw input that's still buffered, we'll peek into
1.4095 + * that. Otherwise, only get more data from the channel driver if it
1.4096 + * looks like there might actually be more data. The assumption is that
1.4097 + * if the channel buffer is filled right up to the end, then there
1.4098 + * might be more data to read.
1.4099 + */
1.4100 +
1.4101 + blockModeProc = NULL;
1.4102 + if (bufPtr->nextPtr == NULL) {
1.4103 + bytesLeft = bufPtr->nextAdded - (bufPtr->nextRemoved + gsPtr->rawRead);
1.4104 + if (bytesLeft == 0) {
1.4105 + if (bufPtr->nextAdded < bufPtr->bufLength) {
1.4106 + /*
1.4107 + * Don't peek ahead if last read was short read.
1.4108 + */
1.4109 +
1.4110 + goto cleanup;
1.4111 + }
1.4112 + if ((statePtr->flags & CHANNEL_NONBLOCKING) == 0) {
1.4113 + blockModeProc = Tcl_ChannelBlockModeProc(chanPtr->typePtr);
1.4114 + if (blockModeProc == NULL) {
1.4115 + /*
1.4116 + * Don't peek ahead if cannot set non-blocking mode.
1.4117 + */
1.4118 +
1.4119 + goto cleanup;
1.4120 + }
1.4121 + StackSetBlockMode(chanPtr, TCL_MODE_NONBLOCKING);
1.4122 + }
1.4123 + }
1.4124 + }
1.4125 + if (FilterInputBytes(chanPtr, gsPtr) == 0) {
1.4126 + *dstEndPtr = *gsPtr->dstPtr + gsPtr->bytesWrote;
1.4127 + }
1.4128 + if (blockModeProc != NULL) {
1.4129 + StackSetBlockMode(chanPtr, TCL_MODE_BLOCKING);
1.4130 + }
1.4131 + return;
1.4132 +
1.4133 + cleanup:
1.4134 + bufPtr->nextRemoved += gsPtr->rawRead;
1.4135 + gsPtr->rawRead = 0;
1.4136 + gsPtr->totalChars += gsPtr->charsWrote;
1.4137 + gsPtr->bytesWrote = 0;
1.4138 + gsPtr->charsWrote = 0;
1.4139 +}
1.4140 +
1.4141 +/*
1.4142 + *---------------------------------------------------------------------------
1.4143 + *
1.4144 + * CommonGetsCleanup --
1.4145 + *
1.4146 + * Helper function for Tcl_GetsObj() to restore the channel after
1.4147 + * a "gets" operation.
1.4148 + *
1.4149 + * Results:
1.4150 + * None.
1.4151 + *
1.4152 + * Side effects:
1.4153 + * Encoding may be freed.
1.4154 + *
1.4155 + *---------------------------------------------------------------------------
1.4156 + */
1.4157 +
1.4158 +static void
1.4159 +CommonGetsCleanup(chanPtr, encoding)
1.4160 + Channel *chanPtr;
1.4161 + Tcl_Encoding encoding;
1.4162 +{
1.4163 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.4164 + ChannelBuffer *bufPtr, *nextPtr;
1.4165 +
1.4166 + bufPtr = statePtr->inQueueHead;
1.4167 + for ( ; bufPtr != NULL; bufPtr = nextPtr) {
1.4168 + nextPtr = bufPtr->nextPtr;
1.4169 + if (bufPtr->nextRemoved < bufPtr->nextAdded) {
1.4170 + break;
1.4171 + }
1.4172 + RecycleBuffer(statePtr, bufPtr, 0);
1.4173 + }
1.4174 + statePtr->inQueueHead = bufPtr;
1.4175 + if (bufPtr == NULL) {
1.4176 + statePtr->inQueueTail = NULL;
1.4177 + } else {
1.4178 + /*
1.4179 + * If any multi-byte characters were split across channel buffer
1.4180 + * boundaries, the split-up bytes were moved to the next channel
1.4181 + * buffer by FilterInputBytes(). Move the bytes back to their
1.4182 + * original buffer because the caller could change the channel's
1.4183 + * encoding which could change the interpretation of whether those
1.4184 + * bytes really made up multi-byte characters after all.
1.4185 + */
1.4186 +
1.4187 + nextPtr = bufPtr->nextPtr;
1.4188 + for ( ; nextPtr != NULL; nextPtr = bufPtr->nextPtr) {
1.4189 + int extra;
1.4190 +
1.4191 + extra = bufPtr->bufLength - bufPtr->nextAdded;
1.4192 + if (extra > 0) {
1.4193 + memcpy((VOID *) (bufPtr->buf + bufPtr->nextAdded),
1.4194 + (VOID *) (nextPtr->buf + BUFFER_PADDING - extra),
1.4195 + (size_t) extra);
1.4196 + bufPtr->nextAdded += extra;
1.4197 + nextPtr->nextRemoved = BUFFER_PADDING;
1.4198 + }
1.4199 + bufPtr = nextPtr;
1.4200 + }
1.4201 + }
1.4202 + if (statePtr->encoding == NULL) {
1.4203 + Tcl_FreeEncoding(encoding);
1.4204 + }
1.4205 +}
1.4206 +
1.4207 +/*
1.4208 + *----------------------------------------------------------------------
1.4209 + *
1.4210 + * Tcl_Read --
1.4211 + *
1.4212 + * Reads a given number of bytes from a channel. EOL and EOF
1.4213 + * translation is done on the bytes being read, so the the number
1.4214 + * of bytes consumed from the channel may not be equal to the
1.4215 + * number of bytes stored in the destination buffer.
1.4216 + *
1.4217 + * No encoding conversions are applied to the bytes being read.
1.4218 + *
1.4219 + * Results:
1.4220 + * The number of bytes read, or -1 on error. Use Tcl_GetErrno()
1.4221 + * to retrieve the error code for the error that occurred.
1.4222 + *
1.4223 + * Side effects:
1.4224 + * May cause input to be buffered.
1.4225 + *
1.4226 + *----------------------------------------------------------------------
1.4227 + */
1.4228 +
1.4229 +EXPORT_C int
1.4230 +Tcl_Read(chan, dst, bytesToRead)
1.4231 + Tcl_Channel chan; /* The channel from which to read. */
1.4232 + char *dst; /* Where to store input read. */
1.4233 + int bytesToRead; /* Maximum number of bytes to read. */
1.4234 +{
1.4235 + Channel *chanPtr = (Channel *) chan;
1.4236 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.4237 +
1.4238 + /*
1.4239 + * This operation should occur at the top of a channel stack.
1.4240 + */
1.4241 +
1.4242 + chanPtr = statePtr->topChanPtr;
1.4243 +
1.4244 + if (CheckChannelErrors(statePtr, TCL_READABLE) != 0) {
1.4245 + return -1;
1.4246 + }
1.4247 +
1.4248 + return DoRead(chanPtr, dst, bytesToRead);
1.4249 +}
1.4250 +
1.4251 +/*
1.4252 + *----------------------------------------------------------------------
1.4253 + *
1.4254 + * Tcl_ReadRaw --
1.4255 + *
1.4256 + * Reads a given number of bytes from a channel. EOL and EOF
1.4257 + * translation is done on the bytes being read, so the the number
1.4258 + * of bytes consumed from the channel may not be equal to the
1.4259 + * number of bytes stored in the destination buffer.
1.4260 + *
1.4261 + * No encoding conversions are applied to the bytes being read.
1.4262 + *
1.4263 + * Results:
1.4264 + * The number of bytes read, or -1 on error. Use Tcl_GetErrno()
1.4265 + * to retrieve the error code for the error that occurred.
1.4266 + *
1.4267 + * Side effects:
1.4268 + * May cause input to be buffered.
1.4269 + *
1.4270 + *----------------------------------------------------------------------
1.4271 + */
1.4272 +
1.4273 +EXPORT_C int
1.4274 +Tcl_ReadRaw(chan, bufPtr, bytesToRead)
1.4275 + Tcl_Channel chan; /* The channel from which to read. */
1.4276 + char *bufPtr; /* Where to store input read. */
1.4277 + int bytesToRead; /* Maximum number of bytes to read. */
1.4278 +{
1.4279 + Channel *chanPtr = (Channel *) chan;
1.4280 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.4281 + int nread, result;
1.4282 + int copied, copiedNow;
1.4283 +
1.4284 + /*
1.4285 + * The check below does too much because it will reject a call to this
1.4286 + * function with a channel which is part of an 'fcopy'. But we have to
1.4287 + * allow this here or else the chaining in the transformation drivers
1.4288 + * will fail with 'file busy' error instead of retrieving and
1.4289 + * transforming the data to copy.
1.4290 + *
1.4291 + * We let the check procedure now believe that there is no fcopy in
1.4292 + * progress. A better solution than this might be an additional flag
1.4293 + * argument to switch off specific checks.
1.4294 + */
1.4295 +
1.4296 + if (CheckChannelErrors(statePtr, TCL_READABLE | CHANNEL_RAW_MODE) != 0) {
1.4297 + return -1;
1.4298 + }
1.4299 +
1.4300 + /*
1.4301 + * Check for information in the push-back buffers. If there is
1.4302 + * some, use it. Go to the driver only if there is none (anymore)
1.4303 + * and the caller requests more bytes.
1.4304 + */
1.4305 +
1.4306 + for (copied = 0; copied < bytesToRead; copied += copiedNow) {
1.4307 + copiedNow = CopyBuffer(chanPtr, bufPtr + copied,
1.4308 + bytesToRead - copied);
1.4309 + if (copiedNow == 0) {
1.4310 + if (statePtr->flags & CHANNEL_EOF) {
1.4311 + goto done;
1.4312 + }
1.4313 + if (statePtr->flags & CHANNEL_BLOCKED) {
1.4314 + if (statePtr->flags & CHANNEL_NONBLOCKING) {
1.4315 + goto done;
1.4316 + }
1.4317 + statePtr->flags &= (~(CHANNEL_BLOCKED));
1.4318 + }
1.4319 +
1.4320 +#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
1.4321 + /* [SF Tcl Bug 943274]. Better emulation of non-blocking
1.4322 + * channels for channels without BlockModeProc, by keeping
1.4323 + * track of true fileevents generated by the OS == Data
1.4324 + * waiting and reading if and only if we are sure to have
1.4325 + * data.
1.4326 + */
1.4327 +
1.4328 + if ((statePtr->flags & CHANNEL_NONBLOCKING) &&
1.4329 + (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL) &&
1.4330 + !(statePtr->flags & CHANNEL_HAS_MORE_DATA)) {
1.4331 +
1.4332 + /* We bypass the driver, it would block, as no data is available */
1.4333 + nread = -1;
1.4334 + result = EWOULDBLOCK;
1.4335 + } else {
1.4336 +#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
1.4337 + /*
1.4338 + * Now go to the driver to get as much as is possible to
1.4339 + * fill the remaining request. Do all the error handling
1.4340 + * by ourselves. The code was stolen from 'GetInput' and
1.4341 + * slightly adapted (different return value here).
1.4342 + *
1.4343 + * The case of 'bytesToRead == 0' at this point cannot happen.
1.4344 + */
1.4345 +
1.4346 + nread = (chanPtr->typePtr->inputProc)(chanPtr->instanceData,
1.4347 + bufPtr + copied, bytesToRead - copied, &result);
1.4348 +#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
1.4349 + }
1.4350 +#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
1.4351 + if (nread > 0) {
1.4352 + /*
1.4353 + * If we get a short read, signal up that we may be
1.4354 + * BLOCKED. We should avoid calling the driver because
1.4355 + * on some platforms we will block in the low level
1.4356 + * reading code even though the channel is set into
1.4357 + * nonblocking mode.
1.4358 + */
1.4359 +
1.4360 + if (nread < (bytesToRead - copied)) {
1.4361 + statePtr->flags |= CHANNEL_BLOCKED;
1.4362 + }
1.4363 +
1.4364 +#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
1.4365 + if (nread <= (bytesToRead - copied)) {
1.4366 + /* [SF Tcl Bug 943274] We have read the available
1.4367 + * data, clear flag */
1.4368 + statePtr->flags &= ~CHANNEL_HAS_MORE_DATA;
1.4369 + }
1.4370 +#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
1.4371 + } else if (nread == 0) {
1.4372 + statePtr->flags |= CHANNEL_EOF;
1.4373 + statePtr->inputEncodingFlags |= TCL_ENCODING_END;
1.4374 + } else if (nread < 0) {
1.4375 + if ((result == EWOULDBLOCK) || (result == EAGAIN)) {
1.4376 + if (copied > 0) {
1.4377 + /*
1.4378 + * Information that was copied earlier has precedence
1.4379 + * over EAGAIN/WOULDBLOCK handling.
1.4380 + */
1.4381 + return copied;
1.4382 + }
1.4383 +
1.4384 + statePtr->flags |= CHANNEL_BLOCKED;
1.4385 + result = EAGAIN;
1.4386 + }
1.4387 +
1.4388 + Tcl_SetErrno(result);
1.4389 + return -1;
1.4390 + }
1.4391 +
1.4392 + return copied + nread;
1.4393 + }
1.4394 + }
1.4395 +
1.4396 +done:
1.4397 + return copied;
1.4398 +}
1.4399 +
1.4400 +/*
1.4401 + *---------------------------------------------------------------------------
1.4402 + *
1.4403 + * Tcl_ReadChars --
1.4404 + *
1.4405 + * Reads from the channel until the requested number of characters
1.4406 + * have been seen, EOF is seen, or the channel would block. EOL
1.4407 + * and EOF translation is done. If reading binary data, the raw
1.4408 + * bytes are wrapped in a Tcl byte array object. Otherwise, the raw
1.4409 + * bytes are converted to UTF-8 using the channel's current encoding
1.4410 + * and stored in a Tcl string object.
1.4411 + *
1.4412 + * Results:
1.4413 + * The number of characters read, or -1 on error. Use Tcl_GetErrno()
1.4414 + * to retrieve the error code for the error that occurred.
1.4415 + *
1.4416 + * Side effects:
1.4417 + * May cause input to be buffered.
1.4418 + *
1.4419 + *---------------------------------------------------------------------------
1.4420 + */
1.4421 +
1.4422 +EXPORT_C int
1.4423 +Tcl_ReadChars(chan, objPtr, toRead, appendFlag)
1.4424 + Tcl_Channel chan; /* The channel to read. */
1.4425 + Tcl_Obj *objPtr; /* Input data is stored in this object. */
1.4426 + int toRead; /* Maximum number of characters to store,
1.4427 + * or -1 to read all available data (up to EOF
1.4428 + * or when channel blocks). */
1.4429 + int appendFlag; /* If non-zero, data read from the channel
1.4430 + * will be appended to the object. Otherwise,
1.4431 + * the data will replace the existing contents
1.4432 + * of the object. */
1.4433 +
1.4434 +{
1.4435 + Channel* chanPtr = (Channel *) chan;
1.4436 + ChannelState* statePtr = chanPtr->state; /* state info for channel */
1.4437 +
1.4438 + /*
1.4439 + * This operation should occur at the top of a channel stack.
1.4440 + */
1.4441 +
1.4442 + chanPtr = statePtr->topChanPtr;
1.4443 +
1.4444 + if (CheckChannelErrors(statePtr, TCL_READABLE) != 0) {
1.4445 + /*
1.4446 + * Update the notifier state so we don't block while there is still
1.4447 + * data in the buffers.
1.4448 + */
1.4449 + UpdateInterest(chanPtr);
1.4450 + return -1;
1.4451 + }
1.4452 +
1.4453 + return DoReadChars (chanPtr, objPtr, toRead, appendFlag);
1.4454 +}
1.4455 +/*
1.4456 + *---------------------------------------------------------------------------
1.4457 + *
1.4458 + * DoReadChars --
1.4459 + *
1.4460 + * Reads from the channel until the requested number of characters
1.4461 + * have been seen, EOF is seen, or the channel would block. EOL
1.4462 + * and EOF translation is done. If reading binary data, the raw
1.4463 + * bytes are wrapped in a Tcl byte array object. Otherwise, the raw
1.4464 + * bytes are converted to UTF-8 using the channel's current encoding
1.4465 + * and stored in a Tcl string object.
1.4466 + *
1.4467 + * Results:
1.4468 + * The number of characters read, or -1 on error. Use Tcl_GetErrno()
1.4469 + * to retrieve the error code for the error that occurred.
1.4470 + *
1.4471 + * Side effects:
1.4472 + * May cause input to be buffered.
1.4473 + *
1.4474 + *---------------------------------------------------------------------------
1.4475 + */
1.4476 +
1.4477 +static int
1.4478 +DoReadChars(chanPtr, objPtr, toRead, appendFlag)
1.4479 + Channel* chanPtr; /* The channel to read. */
1.4480 + Tcl_Obj *objPtr; /* Input data is stored in this object. */
1.4481 + int toRead; /* Maximum number of characters to store,
1.4482 + * or -1 to read all available data (up to EOF
1.4483 + * or when channel blocks). */
1.4484 + int appendFlag; /* If non-zero, data read from the channel
1.4485 + * will be appended to the object. Otherwise,
1.4486 + * the data will replace the existing contents
1.4487 + * of the object. */
1.4488 +
1.4489 +{
1.4490 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.4491 + ChannelBuffer *bufPtr;
1.4492 + int offset, factor, copied, copiedNow, result;
1.4493 + Tcl_Encoding encoding;
1.4494 +#define UTF_EXPANSION_FACTOR 1024
1.4495 +
1.4496 + /*
1.4497 + * This operation should occur at the top of a channel stack.
1.4498 + */
1.4499 +
1.4500 + chanPtr = statePtr->topChanPtr;
1.4501 + encoding = statePtr->encoding;
1.4502 + factor = UTF_EXPANSION_FACTOR;
1.4503 +
1.4504 + if (appendFlag == 0) {
1.4505 + if (encoding == NULL) {
1.4506 + Tcl_SetByteArrayLength(objPtr, 0);
1.4507 + } else {
1.4508 + Tcl_SetObjLength(objPtr, 0);
1.4509 + /*
1.4510 + * We're going to access objPtr->bytes directly, so
1.4511 + * we must ensure that this is actually a string
1.4512 + * object (otherwise it might have been pure Unicode).
1.4513 + */
1.4514 + Tcl_GetString(objPtr);
1.4515 + }
1.4516 + offset = 0;
1.4517 + } else {
1.4518 + if (encoding == NULL) {
1.4519 + Tcl_GetByteArrayFromObj(objPtr, &offset);
1.4520 + } else {
1.4521 + Tcl_GetStringFromObj(objPtr, &offset);
1.4522 + }
1.4523 + }
1.4524 +
1.4525 + for (copied = 0; (unsigned) toRead > 0; ) {
1.4526 + copiedNow = -1;
1.4527 + if (statePtr->inQueueHead != NULL) {
1.4528 + if (encoding == NULL) {
1.4529 + copiedNow = ReadBytes(statePtr, objPtr, toRead, &offset);
1.4530 + } else {
1.4531 + copiedNow = ReadChars(statePtr, objPtr, toRead, &offset,
1.4532 + &factor);
1.4533 + }
1.4534 +
1.4535 + /*
1.4536 + * If the current buffer is empty recycle it.
1.4537 + */
1.4538 +
1.4539 + bufPtr = statePtr->inQueueHead;
1.4540 + if (bufPtr->nextRemoved == bufPtr->nextAdded) {
1.4541 + ChannelBuffer *nextPtr;
1.4542 +
1.4543 + nextPtr = bufPtr->nextPtr;
1.4544 + RecycleBuffer(statePtr, bufPtr, 0);
1.4545 + statePtr->inQueueHead = nextPtr;
1.4546 + if (nextPtr == NULL) {
1.4547 + statePtr->inQueueTail = NULL;
1.4548 + }
1.4549 + }
1.4550 + }
1.4551 + if (copiedNow < 0) {
1.4552 + if (statePtr->flags & CHANNEL_EOF) {
1.4553 + break;
1.4554 + }
1.4555 + if (statePtr->flags & CHANNEL_BLOCKED) {
1.4556 + if (statePtr->flags & CHANNEL_NONBLOCKING) {
1.4557 + break;
1.4558 + }
1.4559 + statePtr->flags &= ~CHANNEL_BLOCKED;
1.4560 + }
1.4561 + result = GetInput(chanPtr);
1.4562 + if (result != 0) {
1.4563 + if (result == EAGAIN) {
1.4564 + break;
1.4565 + }
1.4566 + copied = -1;
1.4567 + goto done;
1.4568 + }
1.4569 + } else {
1.4570 + copied += copiedNow;
1.4571 + toRead -= copiedNow;
1.4572 + }
1.4573 + }
1.4574 + statePtr->flags &= ~CHANNEL_BLOCKED;
1.4575 + if (encoding == NULL) {
1.4576 + Tcl_SetByteArrayLength(objPtr, offset);
1.4577 + } else {
1.4578 + Tcl_SetObjLength(objPtr, offset);
1.4579 + }
1.4580 +
1.4581 + done:
1.4582 + /*
1.4583 + * Update the notifier state so we don't block while there is still
1.4584 + * data in the buffers.
1.4585 + */
1.4586 +
1.4587 + UpdateInterest(chanPtr);
1.4588 + return copied;
1.4589 +}
1.4590 +/*
1.4591 + *---------------------------------------------------------------------------
1.4592 + *
1.4593 + * ReadBytes --
1.4594 + *
1.4595 + * Reads from the channel until the requested number of bytes have
1.4596 + * been seen, EOF is seen, or the channel would block. Bytes from
1.4597 + * the channel are stored in objPtr as a ByteArray object. EOL
1.4598 + * and EOF translation are done.
1.4599 + *
1.4600 + * 'bytesToRead' can safely be a very large number because
1.4601 + * space is only allocated to hold data read from the channel
1.4602 + * as needed.
1.4603 + *
1.4604 + * Results:
1.4605 + * The return value is the number of bytes appended to the object
1.4606 + * and *offsetPtr is filled with the total number of bytes in the
1.4607 + * object (greater than the return value if there were already bytes
1.4608 + * in the object).
1.4609 + *
1.4610 + * Side effects:
1.4611 + * None.
1.4612 + *
1.4613 + *---------------------------------------------------------------------------
1.4614 + */
1.4615 +
1.4616 +static int
1.4617 +ReadBytes(statePtr, objPtr, bytesToRead, offsetPtr)
1.4618 + ChannelState *statePtr; /* State of the channel to read. */
1.4619 + Tcl_Obj *objPtr; /* Input data is appended to this ByteArray
1.4620 + * object. Its length is how much space
1.4621 + * has been allocated to hold data, not how
1.4622 + * many bytes of data have been stored in the
1.4623 + * object. */
1.4624 + int bytesToRead; /* Maximum number of bytes to store,
1.4625 + * or < 0 to get all available bytes.
1.4626 + * Bytes are obtained from the first
1.4627 + * buffer in the queue -- even if this number
1.4628 + * is larger than the number of bytes
1.4629 + * available in the first buffer, only the
1.4630 + * bytes from the first buffer are
1.4631 + * returned. */
1.4632 + int *offsetPtr; /* On input, contains how many bytes of
1.4633 + * objPtr have been used to hold data. On
1.4634 + * output, filled with how many bytes are now
1.4635 + * being used. */
1.4636 +{
1.4637 + int toRead, srcLen, offset, length, srcRead, dstWrote;
1.4638 + ChannelBuffer *bufPtr;
1.4639 + char *src, *dst;
1.4640 +
1.4641 + offset = *offsetPtr;
1.4642 +
1.4643 + bufPtr = statePtr->inQueueHead;
1.4644 + src = bufPtr->buf + bufPtr->nextRemoved;
1.4645 + srcLen = bufPtr->nextAdded - bufPtr->nextRemoved;
1.4646 +
1.4647 + toRead = bytesToRead;
1.4648 + if ((unsigned) toRead > (unsigned) srcLen) {
1.4649 + toRead = srcLen;
1.4650 + }
1.4651 +
1.4652 + dst = (char *) Tcl_GetByteArrayFromObj(objPtr, &length);
1.4653 + if (toRead > length - offset - 1) {
1.4654 + /*
1.4655 + * Double the existing size of the object or make enough room to
1.4656 + * hold all the characters we may get from the source buffer,
1.4657 + * whichever is larger.
1.4658 + */
1.4659 +
1.4660 + length = offset * 2;
1.4661 + if (offset < toRead) {
1.4662 + length = offset + toRead + 1;
1.4663 + }
1.4664 + dst = (char *) Tcl_SetByteArrayLength(objPtr, length);
1.4665 + }
1.4666 + dst += offset;
1.4667 +
1.4668 + if (statePtr->flags & INPUT_NEED_NL) {
1.4669 + statePtr->flags &= ~INPUT_NEED_NL;
1.4670 + if ((srcLen == 0) || (*src != '\n')) {
1.4671 + *dst = '\r';
1.4672 + *offsetPtr += 1;
1.4673 + return 1;
1.4674 + }
1.4675 + *dst++ = '\n';
1.4676 + src++;
1.4677 + srcLen--;
1.4678 + toRead--;
1.4679 + }
1.4680 +
1.4681 + srcRead = srcLen;
1.4682 + dstWrote = toRead;
1.4683 + if (TranslateInputEOL(statePtr, dst, src, &dstWrote, &srcRead) != 0) {
1.4684 + if (dstWrote == 0) {
1.4685 + return -1;
1.4686 + }
1.4687 + }
1.4688 + bufPtr->nextRemoved += srcRead;
1.4689 + *offsetPtr += dstWrote;
1.4690 + return dstWrote;
1.4691 +}
1.4692 +
1.4693 +/*
1.4694 + *---------------------------------------------------------------------------
1.4695 + *
1.4696 + * ReadChars --
1.4697 + *
1.4698 + * Reads from the channel until the requested number of UTF-8
1.4699 + * characters have been seen, EOF is seen, or the channel would
1.4700 + * block. Raw bytes from the channel are converted to UTF-8
1.4701 + * and stored in objPtr. EOL and EOF translation is done.
1.4702 + *
1.4703 + * 'charsToRead' can safely be a very large number because
1.4704 + * space is only allocated to hold data read from the channel
1.4705 + * as needed.
1.4706 + *
1.4707 + * Results:
1.4708 + * The return value is the number of characters appended to
1.4709 + * the object, *offsetPtr is filled with the number of bytes that
1.4710 + * were appended, and *factorPtr is filled with the expansion
1.4711 + * factor used to guess how many bytes of UTF-8 to allocate to
1.4712 + * hold N source bytes.
1.4713 + *
1.4714 + * Side effects:
1.4715 + * None.
1.4716 + *
1.4717 + *---------------------------------------------------------------------------
1.4718 + */
1.4719 +
1.4720 +static int
1.4721 +ReadChars(statePtr, objPtr, charsToRead, offsetPtr, factorPtr)
1.4722 + ChannelState *statePtr; /* State of channel to read. */
1.4723 + Tcl_Obj *objPtr; /* Input data is appended to this object.
1.4724 + * objPtr->length is how much space has been
1.4725 + * allocated to hold data, not how many bytes
1.4726 + * of data have been stored in the object. */
1.4727 + int charsToRead; /* Maximum number of characters to store,
1.4728 + * or -1 to get all available characters.
1.4729 + * Characters are obtained from the first
1.4730 + * buffer in the queue -- even if this number
1.4731 + * is larger than the number of characters
1.4732 + * available in the first buffer, only the
1.4733 + * characters from the first buffer are
1.4734 + * returned. */
1.4735 + int *offsetPtr; /* On input, contains how many bytes of
1.4736 + * objPtr have been used to hold data. On
1.4737 + * output, filled with how many bytes are now
1.4738 + * being used. */
1.4739 + int *factorPtr; /* On input, contains a guess of how many
1.4740 + * bytes need to be allocated to hold the
1.4741 + * result of converting N source bytes to
1.4742 + * UTF-8. On output, contains another guess
1.4743 + * based on the data seen so far. */
1.4744 +{
1.4745 + int toRead, factor, offset, spaceLeft, length, srcLen, dstNeeded;
1.4746 + int srcRead, dstWrote, numChars, dstRead;
1.4747 + ChannelBuffer *bufPtr;
1.4748 + char *src, *dst;
1.4749 + Tcl_EncodingState oldState;
1.4750 + int encEndFlagSuppressed = 0;
1.4751 +
1.4752 + factor = *factorPtr;
1.4753 + offset = *offsetPtr;
1.4754 +
1.4755 + bufPtr = statePtr->inQueueHead;
1.4756 + src = bufPtr->buf + bufPtr->nextRemoved;
1.4757 + srcLen = bufPtr->nextAdded - bufPtr->nextRemoved;
1.4758 +
1.4759 + toRead = charsToRead;
1.4760 + if ((unsigned)toRead > (unsigned)srcLen) {
1.4761 + toRead = srcLen;
1.4762 + }
1.4763 +
1.4764 + /*
1.4765 + * 'factor' is how much we guess that the bytes in the source buffer
1.4766 + * will expand when converted to UTF-8 chars. This guess comes from
1.4767 + * analyzing how many characters were produced by the previous
1.4768 + * pass.
1.4769 + */
1.4770 +
1.4771 + dstNeeded = toRead * factor / UTF_EXPANSION_FACTOR;
1.4772 + spaceLeft = objPtr->length - offset - TCL_UTF_MAX - 1;
1.4773 +
1.4774 + if (dstNeeded > spaceLeft) {
1.4775 + /*
1.4776 + * Double the existing size of the object or make enough room to
1.4777 + * hold all the characters we want from the source buffer,
1.4778 + * whichever is larger.
1.4779 + */
1.4780 +
1.4781 + length = offset * 2;
1.4782 + if (offset < dstNeeded) {
1.4783 + length = offset + dstNeeded;
1.4784 + }
1.4785 + spaceLeft = length - offset;
1.4786 + length += TCL_UTF_MAX + 1;
1.4787 + Tcl_SetObjLength(objPtr, length);
1.4788 + }
1.4789 + if (toRead == srcLen) {
1.4790 + /*
1.4791 + * Want to convert the whole buffer in one pass. If we have
1.4792 + * enough space, convert it using all available space in object
1.4793 + * rather than using the factor.
1.4794 + */
1.4795 +
1.4796 + dstNeeded = spaceLeft;
1.4797 + }
1.4798 + dst = objPtr->bytes + offset;
1.4799 +
1.4800 + /*
1.4801 + * SF Tcl Bug 1462248
1.4802 + * The cause of the crash reported in the referenced bug is this:
1.4803 + *
1.4804 + * - ReadChars, called with a single buffer, with a incomplete
1.4805 + * multi-byte character at the end (only the first byte of it).
1.4806 + * - Encoding translation fails, asks for more data
1.4807 + * - Data is read, and eof is reached, TCL_ENCODING_END (TEE) is set.
1.4808 + * - ReadChar is called again, converts the first buffer, but due
1.4809 + * to TEE it does not check for incomplete multi-byte data, and the
1.4810 + * character just after the end of the first buffer is a valid
1.4811 + * completion of the multi-byte header in the actual buffer. The
1.4812 + * conversion reads more characters from the buffer then present.
1.4813 + * This causes nextRemoved to overshoot nextAdded and the next
1.4814 + * reads compute a negative srcLen, cause further translations to
1.4815 + * fail, causing copying of data into the next buffer using bad
1.4816 + * arguments, causing the mecpy for to eventually fail.
1.4817 + *
1.4818 + * In the end it is a memory access bug spiraling out of control
1.4819 + * if the conditions are _just so_. And ultimate cause is that TEE
1.4820 + * is given to a conversion where it should not. TEE signals that
1.4821 + * this is the last buffer. Except in our case it is not.
1.4822 + *
1.4823 + * My solution is to suppress TEE if the first buffer is not the
1.4824 + * last. We will eventually need it given that EOF has been
1.4825 + * reached, but not right now. This is what the new flag
1.4826 + * "endEncSuppressFlag" is for.
1.4827 + *
1.4828 + * The bug in 'Tcl_Utf2UtfProc' where it read from memory behind
1.4829 + * the actual buffer has been fixed as well, and fixes the problem
1.4830 + * with the crash too, but this would still allow the generic
1.4831 + * layer to accidentially break a multi-byte sequence if the
1.4832 + * conditions are just right, because again the ExternalToUtf
1.4833 + * would be successful where it should not.
1.4834 + */
1.4835 +
1.4836 + if ((statePtr->inputEncodingFlags & TCL_ENCODING_END) &&
1.4837 + (bufPtr->nextPtr != NULL)) {
1.4838 +
1.4839 + /* TEE is set for a buffer which is not the last. Squash it
1.4840 + * for now, and restore it later, before yielding control to
1.4841 + * our caller.
1.4842 + */
1.4843 +
1.4844 + statePtr->inputEncodingFlags &= ~TCL_ENCODING_END;
1.4845 + encEndFlagSuppressed = 1;
1.4846 + }
1.4847 +
1.4848 + oldState = statePtr->inputEncodingState;
1.4849 + if (statePtr->flags & INPUT_NEED_NL) {
1.4850 + /*
1.4851 + * We want a '\n' because the last character we saw was '\r'.
1.4852 + */
1.4853 +
1.4854 + statePtr->flags &= ~INPUT_NEED_NL;
1.4855 + Tcl_ExternalToUtf(NULL, statePtr->encoding, src, srcLen,
1.4856 + statePtr->inputEncodingFlags, &statePtr->inputEncodingState,
1.4857 + dst, TCL_UTF_MAX + 1, &srcRead, &dstWrote, &numChars);
1.4858 + if ((dstWrote > 0) && (*dst == '\n')) {
1.4859 + /*
1.4860 + * The next char was a '\n'. Consume it and produce a '\n'.
1.4861 + */
1.4862 +
1.4863 + bufPtr->nextRemoved += srcRead;
1.4864 + } else {
1.4865 + /*
1.4866 + * The next char was not a '\n'. Produce a '\r'.
1.4867 + */
1.4868 +
1.4869 + *dst = '\r';
1.4870 + }
1.4871 + statePtr->inputEncodingFlags &= ~TCL_ENCODING_START;
1.4872 + *offsetPtr += 1;
1.4873 +
1.4874 + if (encEndFlagSuppressed) {
1.4875 + statePtr->inputEncodingFlags |= TCL_ENCODING_END;
1.4876 + }
1.4877 + return 1;
1.4878 + }
1.4879 +
1.4880 + Tcl_ExternalToUtf(NULL, statePtr->encoding, src, srcLen,
1.4881 + statePtr->inputEncodingFlags, &statePtr->inputEncodingState, dst,
1.4882 + dstNeeded + TCL_UTF_MAX, &srcRead, &dstWrote, &numChars);
1.4883 +
1.4884 + if (encEndFlagSuppressed) {
1.4885 + statePtr->inputEncodingFlags |= TCL_ENCODING_END;
1.4886 + }
1.4887 +
1.4888 + if (srcRead == 0) {
1.4889 + /*
1.4890 + * Not enough bytes in src buffer to make a complete char. Copy
1.4891 + * the bytes to the next buffer to make a new contiguous string,
1.4892 + * then tell the caller to fill the buffer with more bytes.
1.4893 + */
1.4894 +
1.4895 + ChannelBuffer *nextPtr;
1.4896 +
1.4897 + nextPtr = bufPtr->nextPtr;
1.4898 + if (nextPtr == NULL) {
1.4899 + if (srcLen > 0) {
1.4900 + /*
1.4901 + * There isn't enough data in the buffers to complete the next
1.4902 + * character, so we need to wait for more data before the next
1.4903 + * file event can be delivered.
1.4904 + *
1.4905 + * SF #478856.
1.4906 + *
1.4907 + * The exception to this is if the input buffer was
1.4908 + * completely empty before we tried to convert its
1.4909 + * contents. Nothing in, nothing out, and no incomplete
1.4910 + * character data. The conversion before the current one
1.4911 + * was complete.
1.4912 + */
1.4913 +
1.4914 + statePtr->flags |= CHANNEL_NEED_MORE_DATA;
1.4915 + }
1.4916 + return -1;
1.4917 + }
1.4918 +
1.4919 + /* Space is made at the beginning of the buffer to copy the
1.4920 + * previous unused bytes there. Check first if the buffer we
1.4921 + * are using actually has enough space at its beginning for
1.4922 + * the data we are copying. Because if not we will write over the
1.4923 + * buffer management information, especially the 'nextPtr'.
1.4924 + *
1.4925 + * Note that the BUFFER_PADDING (See AllocChannelBuffer) is
1.4926 + * used to prevent exactly this situation. I.e. it should
1.4927 + * never happen. Therefore it is ok to panic should it happen
1.4928 + * despite the precautions.
1.4929 + */
1.4930 +
1.4931 + if (nextPtr->nextRemoved - srcLen < 0) {
1.4932 + Tcl_Panic ("Buffer Underflow, BUFFER_PADDING not enough");
1.4933 + }
1.4934 +
1.4935 + nextPtr->nextRemoved -= srcLen;
1.4936 + memcpy((VOID *) (nextPtr->buf + nextPtr->nextRemoved), (VOID *) src,
1.4937 + (size_t) srcLen);
1.4938 + RecycleBuffer(statePtr, bufPtr, 0);
1.4939 + statePtr->inQueueHead = nextPtr;
1.4940 + return ReadChars(statePtr, objPtr, charsToRead, offsetPtr, factorPtr);
1.4941 + }
1.4942 +
1.4943 + dstRead = dstWrote;
1.4944 + if (TranslateInputEOL(statePtr, dst, dst, &dstWrote, &dstRead) != 0) {
1.4945 + /*
1.4946 + * Hit EOF char. How many bytes of src correspond to where the
1.4947 + * EOF was located in dst? Run the conversion again with an
1.4948 + * output buffer just big enough to hold the data so we can
1.4949 + * get the correct value for srcRead.
1.4950 + */
1.4951 +
1.4952 + if (dstWrote == 0) {
1.4953 + return -1;
1.4954 + }
1.4955 + statePtr->inputEncodingState = oldState;
1.4956 + Tcl_ExternalToUtf(NULL, statePtr->encoding, src, srcLen,
1.4957 + statePtr->inputEncodingFlags, &statePtr->inputEncodingState,
1.4958 + dst, dstRead + TCL_UTF_MAX, &srcRead, &dstWrote, &numChars);
1.4959 + TranslateInputEOL(statePtr, dst, dst, &dstWrote, &dstRead);
1.4960 + }
1.4961 +
1.4962 + /*
1.4963 + * The number of characters that we got may be less than the number
1.4964 + * that we started with because "\r\n" sequences may have been
1.4965 + * turned into just '\n' in dst.
1.4966 + */
1.4967 +
1.4968 + numChars -= (dstRead - dstWrote);
1.4969 +
1.4970 + if ((unsigned) numChars > (unsigned) toRead) {
1.4971 + /*
1.4972 + * Got too many chars.
1.4973 + */
1.4974 +
1.4975 + CONST char *eof;
1.4976 +
1.4977 + eof = Tcl_UtfAtIndex(dst, toRead);
1.4978 + statePtr->inputEncodingState = oldState;
1.4979 + Tcl_ExternalToUtf(NULL, statePtr->encoding, src, srcLen,
1.4980 + statePtr->inputEncodingFlags, &statePtr->inputEncodingState,
1.4981 + dst, eof - dst + TCL_UTF_MAX, &srcRead, &dstWrote, &numChars);
1.4982 + dstRead = dstWrote;
1.4983 + TranslateInputEOL(statePtr, dst, dst, &dstWrote, &dstRead);
1.4984 + numChars -= (dstRead - dstWrote);
1.4985 + }
1.4986 + statePtr->inputEncodingFlags &= ~TCL_ENCODING_START;
1.4987 +
1.4988 + bufPtr->nextRemoved += srcRead;
1.4989 + if (dstWrote > srcRead + 1) {
1.4990 + *factorPtr = dstWrote * UTF_EXPANSION_FACTOR / srcRead;
1.4991 + }
1.4992 + *offsetPtr += dstWrote;
1.4993 + return numChars;
1.4994 +}
1.4995 +
1.4996 +/*
1.4997 + *---------------------------------------------------------------------------
1.4998 + *
1.4999 + * TranslateInputEOL --
1.5000 + *
1.5001 + * Perform input EOL and EOF translation on the source buffer,
1.5002 + * leaving the translated result in the destination buffer.
1.5003 + *
1.5004 + * Results:
1.5005 + * The return value is 1 if the EOF character was found when copying
1.5006 + * bytes to the destination buffer, 0 otherwise.
1.5007 + *
1.5008 + * Side effects:
1.5009 + * None.
1.5010 + *
1.5011 + *---------------------------------------------------------------------------
1.5012 + */
1.5013 +
1.5014 +static int
1.5015 +TranslateInputEOL(statePtr, dstStart, srcStart, dstLenPtr, srcLenPtr)
1.5016 + ChannelState *statePtr; /* Channel being read, for EOL translation
1.5017 + * and EOF character. */
1.5018 + char *dstStart; /* Output buffer filled with chars by
1.5019 + * applying appropriate EOL translation to
1.5020 + * source characters. */
1.5021 + CONST char *srcStart; /* Source characters. */
1.5022 + int *dstLenPtr; /* On entry, the maximum length of output
1.5023 + * buffer in bytes; must be <= *srcLenPtr. On
1.5024 + * exit, the number of bytes actually used in
1.5025 + * output buffer. */
1.5026 + int *srcLenPtr; /* On entry, the length of source buffer.
1.5027 + * On exit, the number of bytes read from
1.5028 + * the source buffer. */
1.5029 +{
1.5030 + int dstLen, srcLen, inEofChar;
1.5031 + CONST char *eof;
1.5032 +
1.5033 + dstLen = *dstLenPtr;
1.5034 +
1.5035 + eof = NULL;
1.5036 + inEofChar = statePtr->inEofChar;
1.5037 + if (inEofChar != '\0') {
1.5038 + /*
1.5039 + * Find EOF in translated buffer then compress out the EOL. The
1.5040 + * source buffer may be much longer than the destination buffer --
1.5041 + * we only want to return EOF if the EOF has been copied to the
1.5042 + * destination buffer.
1.5043 + */
1.5044 +
1.5045 + CONST char *src, *srcMax;
1.5046 +
1.5047 + srcMax = srcStart + *srcLenPtr;
1.5048 + for (src = srcStart; src < srcMax; src++) {
1.5049 + if (*src == inEofChar) {
1.5050 + eof = src;
1.5051 + srcLen = src - srcStart;
1.5052 + if (srcLen < dstLen) {
1.5053 + dstLen = srcLen;
1.5054 + }
1.5055 + *srcLenPtr = srcLen;
1.5056 + break;
1.5057 + }
1.5058 + }
1.5059 + }
1.5060 + switch (statePtr->inputTranslation) {
1.5061 + case TCL_TRANSLATE_LF: {
1.5062 + if (dstStart != srcStart) {
1.5063 + memcpy((VOID *) dstStart, (VOID *) srcStart, (size_t) dstLen);
1.5064 + }
1.5065 + srcLen = dstLen;
1.5066 + break;
1.5067 + }
1.5068 + case TCL_TRANSLATE_CR: {
1.5069 + char *dst, *dstEnd;
1.5070 +
1.5071 + if (dstStart != srcStart) {
1.5072 + memcpy((VOID *) dstStart, (VOID *) srcStart, (size_t) dstLen);
1.5073 + }
1.5074 + dstEnd = dstStart + dstLen;
1.5075 + for (dst = dstStart; dst < dstEnd; dst++) {
1.5076 + if (*dst == '\r') {
1.5077 + *dst = '\n';
1.5078 + }
1.5079 + }
1.5080 + srcLen = dstLen;
1.5081 + break;
1.5082 + }
1.5083 + case TCL_TRANSLATE_CRLF: {
1.5084 + char *dst;
1.5085 + CONST char *src, *srcEnd, *srcMax;
1.5086 +
1.5087 + dst = dstStart;
1.5088 + src = srcStart;
1.5089 + srcEnd = srcStart + dstLen;
1.5090 + srcMax = srcStart + *srcLenPtr;
1.5091 +
1.5092 + for ( ; src < srcEnd; ) {
1.5093 + if (*src == '\r') {
1.5094 + src++;
1.5095 + if (src >= srcMax) {
1.5096 + statePtr->flags |= INPUT_NEED_NL;
1.5097 + } else if (*src == '\n') {
1.5098 + *dst++ = *src++;
1.5099 + } else {
1.5100 + *dst++ = '\r';
1.5101 + }
1.5102 + } else {
1.5103 + *dst++ = *src++;
1.5104 + }
1.5105 + }
1.5106 + srcLen = src - srcStart;
1.5107 + dstLen = dst - dstStart;
1.5108 + break;
1.5109 + }
1.5110 + case TCL_TRANSLATE_AUTO: {
1.5111 + char *dst;
1.5112 + CONST char *src, *srcEnd, *srcMax;
1.5113 +
1.5114 + dst = dstStart;
1.5115 + src = srcStart;
1.5116 + srcEnd = srcStart + dstLen;
1.5117 + srcMax = srcStart + *srcLenPtr;
1.5118 +
1.5119 + if ((statePtr->flags & INPUT_SAW_CR) && (src < srcMax)) {
1.5120 + if (*src == '\n') {
1.5121 + src++;
1.5122 + }
1.5123 + statePtr->flags &= ~INPUT_SAW_CR;
1.5124 + }
1.5125 + for ( ; src < srcEnd; ) {
1.5126 + if (*src == '\r') {
1.5127 + src++;
1.5128 + if (src >= srcMax) {
1.5129 + statePtr->flags |= INPUT_SAW_CR;
1.5130 + } else if (*src == '\n') {
1.5131 + if (srcEnd < srcMax) {
1.5132 + srcEnd++;
1.5133 + }
1.5134 + src++;
1.5135 + }
1.5136 + *dst++ = '\n';
1.5137 + } else {
1.5138 + *dst++ = *src++;
1.5139 + }
1.5140 + }
1.5141 + srcLen = src - srcStart;
1.5142 + dstLen = dst - dstStart;
1.5143 + break;
1.5144 + }
1.5145 + default: { /* lint. */
1.5146 + return 0;
1.5147 + }
1.5148 + }
1.5149 + *dstLenPtr = dstLen;
1.5150 +
1.5151 + if ((eof != NULL) && (srcStart + srcLen >= eof)) {
1.5152 + /*
1.5153 + * EOF character was seen in EOL translated range. Leave current
1.5154 + * file position pointing at the EOF character, but don't store the
1.5155 + * EOF character in the output string.
1.5156 + */
1.5157 +
1.5158 + statePtr->flags |= (CHANNEL_EOF | CHANNEL_STICKY_EOF);
1.5159 + statePtr->inputEncodingFlags |= TCL_ENCODING_END;
1.5160 + statePtr->flags &= ~(INPUT_SAW_CR | INPUT_NEED_NL);
1.5161 + return 1;
1.5162 + }
1.5163 +
1.5164 + *srcLenPtr = srcLen;
1.5165 + return 0;
1.5166 +}
1.5167 +
1.5168 +/*
1.5169 + *----------------------------------------------------------------------
1.5170 + *
1.5171 + * Tcl_Ungets --
1.5172 + *
1.5173 + * Causes the supplied string to be added to the input queue of
1.5174 + * the channel, at either the head or tail of the queue.
1.5175 + *
1.5176 + * Results:
1.5177 + * The number of bytes stored in the channel, or -1 on error.
1.5178 + *
1.5179 + * Side effects:
1.5180 + * Adds input to the input queue of a channel.
1.5181 + *
1.5182 + *----------------------------------------------------------------------
1.5183 + */
1.5184 +
1.5185 +EXPORT_C int
1.5186 +Tcl_Ungets(chan, str, len, atEnd)
1.5187 + Tcl_Channel chan; /* The channel for which to add the input. */
1.5188 + CONST char *str; /* The input itself. */
1.5189 + int len; /* The length of the input. */
1.5190 + int atEnd; /* If non-zero, add at end of queue; otherwise
1.5191 + * add at head of queue. */
1.5192 +{
1.5193 + Channel *chanPtr; /* The real IO channel. */
1.5194 + ChannelState *statePtr; /* State of actual channel. */
1.5195 + ChannelBuffer *bufPtr; /* Buffer to contain the data. */
1.5196 + int i, flags;
1.5197 +
1.5198 + chanPtr = (Channel *) chan;
1.5199 + statePtr = chanPtr->state;
1.5200 +
1.5201 + /*
1.5202 + * This operation should occur at the top of a channel stack.
1.5203 + */
1.5204 +
1.5205 + chanPtr = statePtr->topChanPtr;
1.5206 +
1.5207 + /*
1.5208 + * CheckChannelErrors clears too many flag bits in this one case.
1.5209 + */
1.5210 +
1.5211 + flags = statePtr->flags;
1.5212 + if (CheckChannelErrors(statePtr, TCL_READABLE) != 0) {
1.5213 + len = -1;
1.5214 + goto done;
1.5215 + }
1.5216 + statePtr->flags = flags;
1.5217 +
1.5218 + /*
1.5219 + * If we have encountered a sticky EOF, just punt without storing.
1.5220 + * (sticky EOF is set if we have seen the input eofChar, to prevent
1.5221 + * reading beyond the eofChar). Otherwise, clear the EOF flags, and
1.5222 + * clear the BLOCKED bit. We want to discover these conditions anew
1.5223 + * in each operation.
1.5224 + */
1.5225 +
1.5226 + if (statePtr->flags & CHANNEL_STICKY_EOF) {
1.5227 + goto done;
1.5228 + }
1.5229 + statePtr->flags &= (~(CHANNEL_BLOCKED | CHANNEL_EOF));
1.5230 +
1.5231 + bufPtr = AllocChannelBuffer(len);
1.5232 + for (i = 0; i < len; i++) {
1.5233 + bufPtr->buf[bufPtr->nextAdded++] = str[i];
1.5234 + }
1.5235 +
1.5236 + if (statePtr->inQueueHead == (ChannelBuffer *) NULL) {
1.5237 + bufPtr->nextPtr = (ChannelBuffer *) NULL;
1.5238 + statePtr->inQueueHead = bufPtr;
1.5239 + statePtr->inQueueTail = bufPtr;
1.5240 + } else if (atEnd) {
1.5241 + bufPtr->nextPtr = (ChannelBuffer *) NULL;
1.5242 + statePtr->inQueueTail->nextPtr = bufPtr;
1.5243 + statePtr->inQueueTail = bufPtr;
1.5244 + } else {
1.5245 + bufPtr->nextPtr = statePtr->inQueueHead;
1.5246 + statePtr->inQueueHead = bufPtr;
1.5247 + }
1.5248 +
1.5249 + done:
1.5250 + /*
1.5251 + * Update the notifier state so we don't block while there is still
1.5252 + * data in the buffers.
1.5253 + */
1.5254 +
1.5255 + UpdateInterest(chanPtr);
1.5256 + return len;
1.5257 +}
1.5258 +
1.5259 +/*
1.5260 + *----------------------------------------------------------------------
1.5261 + *
1.5262 + * Tcl_Flush --
1.5263 + *
1.5264 + * Flushes output data on a channel.
1.5265 + *
1.5266 + * Results:
1.5267 + * A standard Tcl result.
1.5268 + *
1.5269 + * Side effects:
1.5270 + * May flush output queued on this channel.
1.5271 + *
1.5272 + *----------------------------------------------------------------------
1.5273 + */
1.5274 +
1.5275 +EXPORT_C int
1.5276 +Tcl_Flush(chan)
1.5277 + Tcl_Channel chan; /* The Channel to flush. */
1.5278 +{
1.5279 + int result; /* Of calling FlushChannel. */
1.5280 + Channel *chanPtr = (Channel *) chan; /* The actual channel. */
1.5281 + ChannelState *statePtr = chanPtr->state; /* State of actual channel. */
1.5282 +
1.5283 + /*
1.5284 + * This operation should occur at the top of a channel stack.
1.5285 + */
1.5286 +
1.5287 + chanPtr = statePtr->topChanPtr;
1.5288 +
1.5289 + if (CheckChannelErrors(statePtr, TCL_WRITABLE) != 0) {
1.5290 + return -1;
1.5291 + }
1.5292 +
1.5293 + /*
1.5294 + * Force current output buffer to be output also.
1.5295 + */
1.5296 +
1.5297 + if ((statePtr->curOutPtr != NULL)
1.5298 + && (statePtr->curOutPtr->nextAdded > 0)) {
1.5299 + statePtr->flags |= BUFFER_READY;
1.5300 + }
1.5301 +
1.5302 + result = FlushChannel(NULL, chanPtr, 0);
1.5303 + if (result != 0) {
1.5304 + return TCL_ERROR;
1.5305 + }
1.5306 +
1.5307 + return TCL_OK;
1.5308 +}
1.5309 +
1.5310 +/*
1.5311 + *----------------------------------------------------------------------
1.5312 + *
1.5313 + * DiscardInputQueued --
1.5314 + *
1.5315 + * Discards any input read from the channel but not yet consumed
1.5316 + * by Tcl reading commands.
1.5317 + *
1.5318 + * Results:
1.5319 + * None.
1.5320 + *
1.5321 + * Side effects:
1.5322 + * May discard input from the channel. If discardLastBuffer is zero,
1.5323 + * leaves one buffer in place for back-filling.
1.5324 + *
1.5325 + *----------------------------------------------------------------------
1.5326 + */
1.5327 +
1.5328 +static void
1.5329 +DiscardInputQueued(statePtr, discardSavedBuffers)
1.5330 + ChannelState *statePtr; /* Channel on which to discard
1.5331 + * the queued input. */
1.5332 + int discardSavedBuffers; /* If non-zero, discard all buffers including
1.5333 + * last one. */
1.5334 +{
1.5335 + ChannelBuffer *bufPtr, *nxtPtr; /* Loop variables. */
1.5336 +
1.5337 + bufPtr = statePtr->inQueueHead;
1.5338 + statePtr->inQueueHead = (ChannelBuffer *) NULL;
1.5339 + statePtr->inQueueTail = (ChannelBuffer *) NULL;
1.5340 + for (; bufPtr != (ChannelBuffer *) NULL; bufPtr = nxtPtr) {
1.5341 + nxtPtr = bufPtr->nextPtr;
1.5342 + RecycleBuffer(statePtr, bufPtr, discardSavedBuffers);
1.5343 + }
1.5344 +
1.5345 + /*
1.5346 + * If discardSavedBuffers is nonzero, must also discard any previously
1.5347 + * saved buffer in the saveInBufPtr field.
1.5348 + */
1.5349 +
1.5350 + if (discardSavedBuffers) {
1.5351 + if (statePtr->saveInBufPtr != (ChannelBuffer *) NULL) {
1.5352 + ckfree((char *) statePtr->saveInBufPtr);
1.5353 + statePtr->saveInBufPtr = (ChannelBuffer *) NULL;
1.5354 + }
1.5355 + }
1.5356 +}
1.5357 +
1.5358 +/*
1.5359 + *---------------------------------------------------------------------------
1.5360 + *
1.5361 + * GetInput --
1.5362 + *
1.5363 + * Reads input data from a device into a channel buffer.
1.5364 + *
1.5365 + * Results:
1.5366 + * The return value is the Posix error code if an error occurred while
1.5367 + * reading from the file, or 0 otherwise.
1.5368 + *
1.5369 + * Side effects:
1.5370 + * Reads from the underlying device.
1.5371 + *
1.5372 + *---------------------------------------------------------------------------
1.5373 + */
1.5374 +
1.5375 +static int
1.5376 +GetInput(chanPtr)
1.5377 + Channel *chanPtr; /* Channel to read input from. */
1.5378 +{
1.5379 + int toRead; /* How much to read? */
1.5380 + int result; /* Of calling driver. */
1.5381 + int nread; /* How much was read from channel? */
1.5382 + ChannelBuffer *bufPtr; /* New buffer to add to input queue. */
1.5383 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.5384 +
1.5385 + /*
1.5386 + * Prevent reading from a dead channel -- a channel that has been closed
1.5387 + * but not yet deallocated, which can happen if the exit handler for
1.5388 + * channel cleanup has run but the channel is still registered in some
1.5389 + * interpreter.
1.5390 + */
1.5391 +
1.5392 + if (CheckForDeadChannel(NULL, statePtr)) {
1.5393 + return EINVAL;
1.5394 + }
1.5395 +
1.5396 + /*
1.5397 + * First check for more buffers in the pushback area of the
1.5398 + * topmost channel in the stack and use them. They can be the
1.5399 + * result of a transformation which went away without reading all
1.5400 + * the information placed in the area when it was stacked.
1.5401 + *
1.5402 + * Two possibilities for the state: No buffers in it, or a single
1.5403 + * empty buffer. In the latter case we can recycle it now.
1.5404 + */
1.5405 +
1.5406 + if (chanPtr->inQueueHead != (ChannelBuffer*) NULL) {
1.5407 + if (statePtr->inQueueHead != (ChannelBuffer*) NULL) {
1.5408 + RecycleBuffer(statePtr, statePtr->inQueueHead, 0);
1.5409 + statePtr->inQueueHead = (ChannelBuffer*) NULL;
1.5410 + }
1.5411 +
1.5412 + statePtr->inQueueHead = chanPtr->inQueueHead;
1.5413 + statePtr->inQueueTail = chanPtr->inQueueTail;
1.5414 + chanPtr->inQueueHead = (ChannelBuffer*) NULL;
1.5415 + chanPtr->inQueueTail = (ChannelBuffer*) NULL;
1.5416 + return 0;
1.5417 + }
1.5418 +
1.5419 + /*
1.5420 + * Nothing in the pushback area, fall back to the usual handling
1.5421 + * (driver, etc.)
1.5422 + */
1.5423 +
1.5424 + /*
1.5425 + * See if we can fill an existing buffer. If we can, read only
1.5426 + * as much as will fit in it. Otherwise allocate a new buffer,
1.5427 + * add it to the input queue and attempt to fill it to the max.
1.5428 + */
1.5429 +
1.5430 + bufPtr = statePtr->inQueueTail;
1.5431 + if ((bufPtr != NULL) && (bufPtr->nextAdded < bufPtr->bufLength)) {
1.5432 + toRead = bufPtr->bufLength - bufPtr->nextAdded;
1.5433 + } else {
1.5434 + bufPtr = statePtr->saveInBufPtr;
1.5435 + statePtr->saveInBufPtr = NULL;
1.5436 +
1.5437 + /*
1.5438 + * Check the actual buffersize against the requested
1.5439 + * buffersize. Buffers which are smaller than requested are
1.5440 + * squashed. This is done to honor dynamic changes of the
1.5441 + * buffersize made by the user.
1.5442 + */
1.5443 +
1.5444 + if ((bufPtr != NULL) && ((bufPtr->bufLength - BUFFER_PADDING) < statePtr->bufSize)) {
1.5445 + ckfree((char *) bufPtr);
1.5446 + bufPtr = NULL;
1.5447 + }
1.5448 +
1.5449 + if (bufPtr == NULL) {
1.5450 + bufPtr = AllocChannelBuffer(statePtr->bufSize);
1.5451 + }
1.5452 + bufPtr->nextPtr = (ChannelBuffer *) NULL;
1.5453 +
1.5454 + /* SF #427196: Use the actual size of the buffer to determine
1.5455 + * the number of bytes to read from the channel and not the
1.5456 + * size for new buffers. They can be different if the
1.5457 + * buffersize was changed between reads.
1.5458 + *
1.5459 + * Note: This affects performance negatively if the buffersize
1.5460 + * was extended but this small buffer is reused for all
1.5461 + * subsequent reads. The system never uses buffers with the
1.5462 + * requested bigger size in that case. An adjunct patch could
1.5463 + * try and delete all unused buffers it encounters and which
1.5464 + * are smaller than the formally requested buffersize.
1.5465 + */
1.5466 +
1.5467 + toRead = bufPtr->bufLength - bufPtr->nextAdded;
1.5468 +
1.5469 + if (statePtr->inQueueTail == NULL) {
1.5470 + statePtr->inQueueHead = bufPtr;
1.5471 + } else {
1.5472 + statePtr->inQueueTail->nextPtr = bufPtr;
1.5473 + }
1.5474 + statePtr->inQueueTail = bufPtr;
1.5475 + }
1.5476 +
1.5477 + /*
1.5478 + * If EOF is set, we should avoid calling the driver because on some
1.5479 + * platforms it is impossible to read from a device after EOF.
1.5480 + */
1.5481 +
1.5482 + if (statePtr->flags & CHANNEL_EOF) {
1.5483 + return 0;
1.5484 + }
1.5485 +
1.5486 +#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
1.5487 + /* [SF Tcl Bug 943274]. Better emulation of non-blocking channels
1.5488 + * for channels without BlockModeProc, by keeping track of true
1.5489 + * fileevents generated by the OS == Data waiting and reading if
1.5490 + * and only if we are sure to have data.
1.5491 + */
1.5492 +
1.5493 + if ((statePtr->flags & CHANNEL_NONBLOCKING) &&
1.5494 + (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL) &&
1.5495 + !(statePtr->flags & CHANNEL_HAS_MORE_DATA)) {
1.5496 +
1.5497 + /* Bypass the driver, it would block, as no data is available */
1.5498 + nread = -1;
1.5499 + result = EWOULDBLOCK;
1.5500 + } else {
1.5501 +#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
1.5502 +
1.5503 + nread = (chanPtr->typePtr->inputProc)(chanPtr->instanceData,
1.5504 + bufPtr->buf + bufPtr->nextAdded, toRead, &result);
1.5505 +
1.5506 +#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
1.5507 + }
1.5508 +#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
1.5509 +
1.5510 + if (nread > 0) {
1.5511 + bufPtr->nextAdded += nread;
1.5512 +
1.5513 + /*
1.5514 + * If we get a short read, signal up that we may be BLOCKED. We
1.5515 + * should avoid calling the driver because on some platforms we
1.5516 + * will block in the low level reading code even though the
1.5517 + * channel is set into nonblocking mode.
1.5518 + */
1.5519 +
1.5520 + if (nread < toRead) {
1.5521 + statePtr->flags |= CHANNEL_BLOCKED;
1.5522 + }
1.5523 +
1.5524 +#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
1.5525 + if (nread <= toRead) {
1.5526 + /* [SF Tcl Bug 943274] We have read the available data,
1.5527 + * clear flag */
1.5528 + statePtr->flags &= ~CHANNEL_HAS_MORE_DATA;
1.5529 + }
1.5530 +#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
1.5531 +
1.5532 + } else if (nread == 0) {
1.5533 + statePtr->flags |= CHANNEL_EOF;
1.5534 + statePtr->inputEncodingFlags |= TCL_ENCODING_END;
1.5535 + } else if (nread < 0) {
1.5536 + if ((result == EWOULDBLOCK) || (result == EAGAIN)) {
1.5537 + statePtr->flags |= CHANNEL_BLOCKED;
1.5538 + result = EAGAIN;
1.5539 + }
1.5540 + Tcl_SetErrno(result);
1.5541 + return result;
1.5542 + }
1.5543 + return 0;
1.5544 +}
1.5545 +
1.5546 +/*
1.5547 + *----------------------------------------------------------------------
1.5548 + *
1.5549 + * Tcl_Seek --
1.5550 + *
1.5551 + * Implements seeking on Tcl Channels. This is a public function
1.5552 + * so that other C facilities may be implemented on top of it.
1.5553 + *
1.5554 + * Results:
1.5555 + * The new access point or -1 on error. If error, use Tcl_GetErrno()
1.5556 + * to retrieve the POSIX error code for the error that occurred.
1.5557 + *
1.5558 + * Side effects:
1.5559 + * May flush output on the channel. May discard queued input.
1.5560 + *
1.5561 + *----------------------------------------------------------------------
1.5562 + */
1.5563 +
1.5564 +EXPORT_C Tcl_WideInt
1.5565 +Tcl_Seek(chan, offset, mode)
1.5566 + Tcl_Channel chan; /* The channel on which to seek. */
1.5567 + Tcl_WideInt offset; /* Offset to seek to. */
1.5568 + int mode; /* Relative to which location to seek? */
1.5569 +{
1.5570 + Channel *chanPtr = (Channel *) chan; /* The real IO channel. */
1.5571 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.5572 + int inputBuffered, outputBuffered;
1.5573 + /* # bytes held in buffers. */
1.5574 + int result; /* Of device driver operations. */
1.5575 + Tcl_WideInt curPos; /* Position on the device. */
1.5576 + int wasAsync; /* Was the channel nonblocking before the
1.5577 + * seek operation? If so, must restore to
1.5578 + * nonblocking mode after the seek. */
1.5579 +
1.5580 + if (CheckChannelErrors(statePtr, TCL_WRITABLE | TCL_READABLE) != 0) {
1.5581 + return Tcl_LongAsWide(-1);
1.5582 + }
1.5583 +
1.5584 + /*
1.5585 + * Disallow seek on dead channels -- channels that have been closed but
1.5586 + * not yet been deallocated. Such channels can be found if the exit
1.5587 + * handler for channel cleanup has run but the channel is still
1.5588 + * registered in an interpreter.
1.5589 + */
1.5590 +
1.5591 + if (CheckForDeadChannel(NULL, statePtr)) {
1.5592 + return Tcl_LongAsWide(-1);
1.5593 + }
1.5594 +
1.5595 + /*
1.5596 + * This operation should occur at the top of a channel stack.
1.5597 + */
1.5598 +
1.5599 + chanPtr = statePtr->topChanPtr;
1.5600 +
1.5601 + /*
1.5602 + * Disallow seek on channels whose type does not have a seek procedure
1.5603 + * defined. This means that the channel does not support seeking.
1.5604 + */
1.5605 +
1.5606 + if (chanPtr->typePtr->seekProc == (Tcl_DriverSeekProc *) NULL) {
1.5607 + Tcl_SetErrno(EINVAL);
1.5608 + return Tcl_LongAsWide(-1);
1.5609 + }
1.5610 +
1.5611 + /*
1.5612 + * Compute how much input and output is buffered. If both input and
1.5613 + * output is buffered, cannot compute the current position.
1.5614 + */
1.5615 +
1.5616 + inputBuffered = Tcl_InputBuffered(chan);
1.5617 + outputBuffered = Tcl_OutputBuffered(chan);
1.5618 +
1.5619 + if ((inputBuffered != 0) && (outputBuffered != 0)) {
1.5620 + Tcl_SetErrno(EFAULT);
1.5621 + return Tcl_LongAsWide(-1);
1.5622 + }
1.5623 +
1.5624 + /*
1.5625 + * If we are seeking relative to the current position, compute the
1.5626 + * corrected offset taking into account the amount of unread input.
1.5627 + */
1.5628 +
1.5629 + if (mode == SEEK_CUR) {
1.5630 + offset -= inputBuffered;
1.5631 + }
1.5632 +
1.5633 + /*
1.5634 + * Discard any queued input - this input should not be read after
1.5635 + * the seek.
1.5636 + */
1.5637 +
1.5638 + DiscardInputQueued(statePtr, 0);
1.5639 +
1.5640 + /*
1.5641 + * Reset EOF and BLOCKED flags. We invalidate them by moving the
1.5642 + * access point. Also clear CR related flags.
1.5643 + */
1.5644 +
1.5645 + statePtr->flags &=
1.5646 + (~(CHANNEL_EOF | CHANNEL_STICKY_EOF | CHANNEL_BLOCKED | INPUT_SAW_CR));
1.5647 +
1.5648 + /*
1.5649 + * If the channel is in asynchronous output mode, switch it back
1.5650 + * to synchronous mode and cancel any async flush that may be
1.5651 + * scheduled. After the flush, the channel will be put back into
1.5652 + * asynchronous output mode.
1.5653 + */
1.5654 +
1.5655 + wasAsync = 0;
1.5656 + if (statePtr->flags & CHANNEL_NONBLOCKING) {
1.5657 + wasAsync = 1;
1.5658 + result = StackSetBlockMode(chanPtr, TCL_MODE_BLOCKING);
1.5659 + if (result != 0) {
1.5660 + return Tcl_LongAsWide(-1);
1.5661 + }
1.5662 + statePtr->flags &= (~(CHANNEL_NONBLOCKING));
1.5663 + if (statePtr->flags & BG_FLUSH_SCHEDULED) {
1.5664 + statePtr->flags &= (~(BG_FLUSH_SCHEDULED));
1.5665 + }
1.5666 + }
1.5667 +
1.5668 + /*
1.5669 + * If the flush fails we cannot recover the original position. In
1.5670 + * that case the seek is not attempted because we do not know where
1.5671 + * the access position is - instead we return the error. FlushChannel
1.5672 + * has already called Tcl_SetErrno() to report the error upwards.
1.5673 + * If the flush succeeds we do the seek also.
1.5674 + */
1.5675 +
1.5676 + if (FlushChannel(NULL, chanPtr, 0) != 0) {
1.5677 + curPos = -1;
1.5678 + } else {
1.5679 +
1.5680 + /*
1.5681 + * Now seek to the new position in the channel as requested by the
1.5682 + * caller. Note that we prefer the wideSeekProc if that is
1.5683 + * available and non-NULL...
1.5684 + */
1.5685 +
1.5686 + if (HaveVersion(chanPtr->typePtr, TCL_CHANNEL_VERSION_3) &&
1.5687 + chanPtr->typePtr->wideSeekProc != NULL) {
1.5688 + curPos = (chanPtr->typePtr->wideSeekProc) (chanPtr->instanceData,
1.5689 + offset, mode, &result);
1.5690 + } else if (offset < Tcl_LongAsWide(LONG_MIN) ||
1.5691 + offset > Tcl_LongAsWide(LONG_MAX)) {
1.5692 + result = EOVERFLOW;
1.5693 + curPos = Tcl_LongAsWide(-1);
1.5694 + } else {
1.5695 + curPos = Tcl_LongAsWide((chanPtr->typePtr->seekProc) (
1.5696 + chanPtr->instanceData, Tcl_WideAsLong(offset), mode,
1.5697 + &result));
1.5698 + }
1.5699 + if (curPos == Tcl_LongAsWide(-1)) {
1.5700 + Tcl_SetErrno(result);
1.5701 + }
1.5702 + }
1.5703 +
1.5704 + /*
1.5705 + * Restore to nonblocking mode if that was the previous behavior.
1.5706 + *
1.5707 + * NOTE: Even if there was an async flush active we do not restore
1.5708 + * it now because we already flushed all the queued output, above.
1.5709 + */
1.5710 +
1.5711 + if (wasAsync) {
1.5712 + statePtr->flags |= CHANNEL_NONBLOCKING;
1.5713 + result = StackSetBlockMode(chanPtr, TCL_MODE_NONBLOCKING);
1.5714 + if (result != 0) {
1.5715 + return Tcl_LongAsWide(-1);
1.5716 + }
1.5717 + }
1.5718 +
1.5719 + return curPos;
1.5720 +}
1.5721 +
1.5722 +/*
1.5723 + *----------------------------------------------------------------------
1.5724 + *
1.5725 + * Tcl_Tell --
1.5726 + *
1.5727 + * Returns the position of the next character to be read/written on
1.5728 + * this channel.
1.5729 + *
1.5730 + * Results:
1.5731 + * A nonnegative integer on success, -1 on failure. If failed,
1.5732 + * use Tcl_GetErrno() to retrieve the POSIX error code for the
1.5733 + * error that occurred.
1.5734 + *
1.5735 + * Side effects:
1.5736 + * None.
1.5737 + *
1.5738 + *----------------------------------------------------------------------
1.5739 + */
1.5740 +
1.5741 +EXPORT_C Tcl_WideInt
1.5742 +Tcl_Tell(chan)
1.5743 + Tcl_Channel chan; /* The channel to return pos for. */
1.5744 +{
1.5745 + Channel *chanPtr = (Channel *) chan; /* The real IO channel. */
1.5746 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.5747 + int inputBuffered, outputBuffered; /* # bytes held in buffers. */
1.5748 + int result; /* Of calling device driver. */
1.5749 + Tcl_WideInt curPos; /* Position on device. */
1.5750 +
1.5751 + if (CheckChannelErrors(statePtr, TCL_WRITABLE | TCL_READABLE) != 0) {
1.5752 + return Tcl_LongAsWide(-1);
1.5753 + }
1.5754 +
1.5755 + /*
1.5756 + * Disallow tell on dead channels -- channels that have been closed but
1.5757 + * not yet been deallocated. Such channels can be found if the exit
1.5758 + * handler for channel cleanup has run but the channel is still
1.5759 + * registered in an interpreter.
1.5760 + */
1.5761 +
1.5762 + if (CheckForDeadChannel(NULL, statePtr)) {
1.5763 + return Tcl_LongAsWide(-1);
1.5764 + }
1.5765 +
1.5766 + /*
1.5767 + * This operation should occur at the top of a channel stack.
1.5768 + */
1.5769 +
1.5770 + chanPtr = statePtr->topChanPtr;
1.5771 +
1.5772 + /*
1.5773 + * Disallow tell on channels whose type does not have a seek procedure
1.5774 + * defined. This means that the channel does not support seeking.
1.5775 + */
1.5776 +
1.5777 + if (chanPtr->typePtr->seekProc == (Tcl_DriverSeekProc *) NULL) {
1.5778 + Tcl_SetErrno(EINVAL);
1.5779 + return Tcl_LongAsWide(-1);
1.5780 + }
1.5781 +
1.5782 + /*
1.5783 + * Compute how much input and output is buffered. If both input and
1.5784 + * output is buffered, cannot compute the current position.
1.5785 + */
1.5786 +
1.5787 + inputBuffered = Tcl_InputBuffered(chan);
1.5788 + outputBuffered = Tcl_OutputBuffered(chan);
1.5789 +
1.5790 + if ((inputBuffered != 0) && (outputBuffered != 0)) {
1.5791 + Tcl_SetErrno(EFAULT);
1.5792 + return Tcl_LongAsWide(-1);
1.5793 + }
1.5794 +
1.5795 + /*
1.5796 + * Get the current position in the device and compute the position
1.5797 + * where the next character will be read or written. Note that we
1.5798 + * prefer the wideSeekProc if that is available and non-NULL...
1.5799 + */
1.5800 +
1.5801 + if (HaveVersion(chanPtr->typePtr, TCL_CHANNEL_VERSION_3) &&
1.5802 + chanPtr->typePtr->wideSeekProc != NULL) {
1.5803 + curPos = (chanPtr->typePtr->wideSeekProc) (chanPtr->instanceData,
1.5804 + Tcl_LongAsWide(0), SEEK_CUR, &result);
1.5805 + } else {
1.5806 + curPos = Tcl_LongAsWide((chanPtr->typePtr->seekProc) (
1.5807 + chanPtr->instanceData, 0, SEEK_CUR, &result));
1.5808 + }
1.5809 + if (curPos == Tcl_LongAsWide(-1)) {
1.5810 + Tcl_SetErrno(result);
1.5811 + return Tcl_LongAsWide(-1);
1.5812 + }
1.5813 + if (inputBuffered != 0) {
1.5814 + return curPos - inputBuffered;
1.5815 + }
1.5816 + return curPos + outputBuffered;
1.5817 +}
1.5818 +
1.5819 +/*
1.5820 + *---------------------------------------------------------------------------
1.5821 + *
1.5822 + * Tcl_SeekOld, Tcl_TellOld --
1.5823 + *
1.5824 + * Backward-compatability versions of the seek/tell interface that
1.5825 + * do not support 64-bit offsets. This interface is not documented
1.5826 + * or expected to be supported indefinitely.
1.5827 + *
1.5828 + * Results:
1.5829 + * As for Tcl_Seek and Tcl_Tell respectively, except truncated to
1.5830 + * whatever value will fit in an 'int'.
1.5831 + *
1.5832 + * Side effects:
1.5833 + * As for Tcl_Seek and Tcl_Tell respectively.
1.5834 + *
1.5835 + *---------------------------------------------------------------------------
1.5836 + */
1.5837 +
1.5838 +EXPORT_C int
1.5839 +Tcl_SeekOld(chan, offset, mode)
1.5840 + Tcl_Channel chan; /* The channel on which to seek. */
1.5841 + int offset; /* Offset to seek to. */
1.5842 + int mode; /* Relative to which location to seek? */
1.5843 +{
1.5844 + Tcl_WideInt wOffset, wResult;
1.5845 +
1.5846 + wOffset = Tcl_LongAsWide((long)offset);
1.5847 + wResult = Tcl_Seek(chan, wOffset, mode);
1.5848 + return (int)Tcl_WideAsLong(wResult);
1.5849 +}
1.5850 +
1.5851 +EXPORT_C int
1.5852 +Tcl_TellOld(chan)
1.5853 + Tcl_Channel chan; /* The channel to return pos for. */
1.5854 +{
1.5855 + Tcl_WideInt wResult;
1.5856 +
1.5857 + wResult = Tcl_Tell(chan);
1.5858 + return (int)Tcl_WideAsLong(wResult);
1.5859 +}
1.5860 +
1.5861 +/*
1.5862 + *---------------------------------------------------------------------------
1.5863 + *
1.5864 + * CheckChannelErrors --
1.5865 + *
1.5866 + * See if the channel is in an ready state and can perform the
1.5867 + * desired operation.
1.5868 + *
1.5869 + * Results:
1.5870 + * The return value is 0 if the channel is OK, otherwise the
1.5871 + * return value is -1 and errno is set to indicate the error.
1.5872 + *
1.5873 + * Side effects:
1.5874 + * May clear the EOF and/or BLOCKED bits if reading from channel.
1.5875 + *
1.5876 + *---------------------------------------------------------------------------
1.5877 + */
1.5878 +
1.5879 +static int
1.5880 +CheckChannelErrors(statePtr, flags)
1.5881 + ChannelState *statePtr; /* Channel to check. */
1.5882 + int flags; /* Test if channel supports desired operation:
1.5883 + * TCL_READABLE, TCL_WRITABLE. Also indicates
1.5884 + * Raw read or write for special close
1.5885 + * processing*/
1.5886 +{
1.5887 + int direction = flags & (TCL_READABLE|TCL_WRITABLE);
1.5888 +
1.5889 + /*
1.5890 + * Check for unreported error.
1.5891 + */
1.5892 +
1.5893 + if (statePtr->unreportedError != 0) {
1.5894 + Tcl_SetErrno(statePtr->unreportedError);
1.5895 + statePtr->unreportedError = 0;
1.5896 + return -1;
1.5897 + }
1.5898 +
1.5899 + /*
1.5900 + * Only the raw read and write operations are allowed during close
1.5901 + * in order to drain data from stacked channels.
1.5902 + */
1.5903 +
1.5904 + if ((statePtr->flags & CHANNEL_CLOSED) &&
1.5905 + ((flags & CHANNEL_RAW_MODE) == 0)) {
1.5906 + Tcl_SetErrno(EACCES);
1.5907 + return -1;
1.5908 + }
1.5909 +
1.5910 + /*
1.5911 + * Fail if the channel is not opened for desired operation.
1.5912 + */
1.5913 +
1.5914 + if ((statePtr->flags & direction) == 0) {
1.5915 + Tcl_SetErrno(EACCES);
1.5916 + return -1;
1.5917 + }
1.5918 +
1.5919 + /*
1.5920 + * Fail if the channel is in the middle of a background copy.
1.5921 + *
1.5922 + * Don't do this tests for raw channels here or else the chaining in the
1.5923 + * transformation drivers will fail with 'file busy' error instead of
1.5924 + * retrieving and transforming the data to copy.
1.5925 + */
1.5926 +
1.5927 + if ((statePtr->csPtr != NULL) && ((flags & CHANNEL_RAW_MODE) == 0)) {
1.5928 + Tcl_SetErrno(EBUSY);
1.5929 + return -1;
1.5930 + }
1.5931 +
1.5932 + if (direction == TCL_READABLE) {
1.5933 + /*
1.5934 + * If we have not encountered a sticky EOF, clear the EOF bit
1.5935 + * (sticky EOF is set if we have seen the input eofChar, to prevent
1.5936 + * reading beyond the eofChar). Also, always clear the BLOCKED bit.
1.5937 + * We want to discover these conditions anew in each operation.
1.5938 + */
1.5939 +
1.5940 + if ((statePtr->flags & CHANNEL_STICKY_EOF) == 0) {
1.5941 + statePtr->flags &= ~CHANNEL_EOF;
1.5942 + }
1.5943 + statePtr->flags &= ~(CHANNEL_BLOCKED | CHANNEL_NEED_MORE_DATA);
1.5944 + }
1.5945 +
1.5946 + return 0;
1.5947 +}
1.5948 +
1.5949 +/*
1.5950 + *----------------------------------------------------------------------
1.5951 + *
1.5952 + * Tcl_Eof --
1.5953 + *
1.5954 + * Returns 1 if the channel is at EOF, 0 otherwise.
1.5955 + *
1.5956 + * Results:
1.5957 + * 1 or 0, always.
1.5958 + *
1.5959 + * Side effects:
1.5960 + * None.
1.5961 + *
1.5962 + *----------------------------------------------------------------------
1.5963 + */
1.5964 +
1.5965 +EXPORT_C int
1.5966 +Tcl_Eof(chan)
1.5967 + Tcl_Channel chan; /* Does this channel have EOF? */
1.5968 +{
1.5969 + ChannelState *statePtr = ((Channel *) chan)->state;
1.5970 + /* State of real channel structure. */
1.5971 +
1.5972 + return ((statePtr->flags & CHANNEL_STICKY_EOF) ||
1.5973 + ((statePtr->flags & CHANNEL_EOF) &&
1.5974 + (Tcl_InputBuffered(chan) == 0))) ? 1 : 0;
1.5975 +}
1.5976 +
1.5977 +/*
1.5978 + *----------------------------------------------------------------------
1.5979 + *
1.5980 + * Tcl_InputBlocked --
1.5981 + *
1.5982 + * Returns 1 if input is blocked on this channel, 0 otherwise.
1.5983 + *
1.5984 + * Results:
1.5985 + * 0 or 1, always.
1.5986 + *
1.5987 + * Side effects:
1.5988 + * None.
1.5989 + *
1.5990 + *----------------------------------------------------------------------
1.5991 + */
1.5992 +
1.5993 +EXPORT_C int
1.5994 +Tcl_InputBlocked(chan)
1.5995 + Tcl_Channel chan; /* Is this channel blocked? */
1.5996 +{
1.5997 + ChannelState *statePtr = ((Channel *) chan)->state;
1.5998 + /* State of real channel structure. */
1.5999 +
1.6000 + return (statePtr->flags & CHANNEL_BLOCKED) ? 1 : 0;
1.6001 +}
1.6002 +
1.6003 +/*
1.6004 + *----------------------------------------------------------------------
1.6005 + *
1.6006 + * Tcl_InputBuffered --
1.6007 + *
1.6008 + * Returns the number of bytes of input currently buffered in the
1.6009 + * common internal buffer of a channel.
1.6010 + *
1.6011 + * Results:
1.6012 + * The number of input bytes buffered, or zero if the channel is not
1.6013 + * open for reading.
1.6014 + *
1.6015 + * Side effects:
1.6016 + * None.
1.6017 + *
1.6018 + *----------------------------------------------------------------------
1.6019 + */
1.6020 +
1.6021 +EXPORT_C int
1.6022 +Tcl_InputBuffered(chan)
1.6023 + Tcl_Channel chan; /* The channel to query. */
1.6024 +{
1.6025 + ChannelState *statePtr = ((Channel *) chan)->state;
1.6026 + /* State of real channel structure. */
1.6027 + ChannelBuffer *bufPtr;
1.6028 + int bytesBuffered;
1.6029 +
1.6030 + for (bytesBuffered = 0, bufPtr = statePtr->inQueueHead;
1.6031 + bufPtr != (ChannelBuffer *) NULL;
1.6032 + bufPtr = bufPtr->nextPtr) {
1.6033 + bytesBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
1.6034 + }
1.6035 +
1.6036 + /*
1.6037 + * Don't forget the bytes in the topmost pushback area.
1.6038 + */
1.6039 +
1.6040 + for (bufPtr = statePtr->topChanPtr->inQueueHead;
1.6041 + bufPtr != (ChannelBuffer *) NULL;
1.6042 + bufPtr = bufPtr->nextPtr) {
1.6043 + bytesBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
1.6044 + }
1.6045 +
1.6046 + return bytesBuffered;
1.6047 +}
1.6048 +
1.6049 +/*
1.6050 + *----------------------------------------------------------------------
1.6051 + *
1.6052 + * Tcl_OutputBuffered --
1.6053 + *
1.6054 + * Returns the number of bytes of output currently buffered in the
1.6055 + * common internal buffer of a channel.
1.6056 + *
1.6057 + * Results:
1.6058 + * The number of output bytes buffered, or zero if the channel is not
1.6059 + * open for writing.
1.6060 + *
1.6061 + * Side effects:
1.6062 + * None.
1.6063 + *
1.6064 + *----------------------------------------------------------------------
1.6065 + */
1.6066 +
1.6067 +EXPORT_C int
1.6068 +Tcl_OutputBuffered(chan)
1.6069 + Tcl_Channel chan; /* The channel to query. */
1.6070 +{
1.6071 + ChannelState *statePtr = ((Channel *) chan)->state;
1.6072 + /* State of real channel structure. */
1.6073 + ChannelBuffer *bufPtr;
1.6074 + int bytesBuffered;
1.6075 +
1.6076 + for (bytesBuffered = 0, bufPtr = statePtr->outQueueHead;
1.6077 + bufPtr != (ChannelBuffer *) NULL;
1.6078 + bufPtr = bufPtr->nextPtr) {
1.6079 + bytesBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
1.6080 + }
1.6081 + if ((statePtr->curOutPtr != (ChannelBuffer *) NULL) &&
1.6082 + (statePtr->curOutPtr->nextAdded > statePtr->curOutPtr->nextRemoved)) {
1.6083 + statePtr->flags |= BUFFER_READY;
1.6084 + bytesBuffered +=
1.6085 + (statePtr->curOutPtr->nextAdded - statePtr->curOutPtr->nextRemoved);
1.6086 + }
1.6087 +
1.6088 + return bytesBuffered;
1.6089 +}
1.6090 +
1.6091 +/*
1.6092 + *----------------------------------------------------------------------
1.6093 + *
1.6094 + * Tcl_ChannelBuffered --
1.6095 + *
1.6096 + * Returns the number of bytes of input currently buffered in the
1.6097 + * internal buffer (push back area) of a channel.
1.6098 + *
1.6099 + * Results:
1.6100 + * The number of input bytes buffered, or zero if the channel is not
1.6101 + * open for reading.
1.6102 + *
1.6103 + * Side effects:
1.6104 + * None.
1.6105 + *
1.6106 + *----------------------------------------------------------------------
1.6107 + */
1.6108 +
1.6109 +EXPORT_C int
1.6110 +Tcl_ChannelBuffered(chan)
1.6111 + Tcl_Channel chan; /* The channel to query. */
1.6112 +{
1.6113 + Channel *chanPtr = (Channel *) chan;
1.6114 + /* real channel structure. */
1.6115 + ChannelBuffer *bufPtr;
1.6116 + int bytesBuffered;
1.6117 +
1.6118 + for (bytesBuffered = 0, bufPtr = chanPtr->inQueueHead;
1.6119 + bufPtr != (ChannelBuffer *) NULL;
1.6120 + bufPtr = bufPtr->nextPtr) {
1.6121 + bytesBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
1.6122 + }
1.6123 +
1.6124 + return bytesBuffered;
1.6125 +}
1.6126 +
1.6127 +/*
1.6128 + *----------------------------------------------------------------------
1.6129 + *
1.6130 + * Tcl_SetChannelBufferSize --
1.6131 + *
1.6132 + * Sets the size of buffers to allocate to store input or output
1.6133 + * in the channel. The size must be between 1 byte and 1 MByte.
1.6134 + *
1.6135 + * Results:
1.6136 + * None.
1.6137 + *
1.6138 + * Side effects:
1.6139 + * Sets the size of buffers subsequently allocated for this channel.
1.6140 + *
1.6141 + *----------------------------------------------------------------------
1.6142 + */
1.6143 +
1.6144 +EXPORT_C void
1.6145 +Tcl_SetChannelBufferSize(chan, sz)
1.6146 + Tcl_Channel chan; /* The channel whose buffer size
1.6147 + * to set. */
1.6148 + int sz; /* The size to set. */
1.6149 +{
1.6150 + ChannelState *statePtr; /* State of real channel structure. */
1.6151 +
1.6152 + /*
1.6153 + * If the buffer size is smaller than 1 byte or larger than one MByte,
1.6154 + * do not accept the requested size and leave the current buffer size.
1.6155 + */
1.6156 +
1.6157 + if (sz < 1) {
1.6158 + return;
1.6159 + }
1.6160 + if (sz > (1024 * 1024)) {
1.6161 + return;
1.6162 + }
1.6163 +
1.6164 + statePtr = ((Channel *) chan)->state;
1.6165 + statePtr->bufSize = sz;
1.6166 +
1.6167 + if (statePtr->outputStage != NULL) {
1.6168 + ckfree((char *) statePtr->outputStage);
1.6169 + statePtr->outputStage = NULL;
1.6170 + }
1.6171 + if ((statePtr->encoding != NULL) && (statePtr->flags & TCL_WRITABLE)) {
1.6172 + statePtr->outputStage = (char *)
1.6173 + ckalloc((unsigned) (statePtr->bufSize + 2));
1.6174 + }
1.6175 +}
1.6176 +
1.6177 +/*
1.6178 + *----------------------------------------------------------------------
1.6179 + *
1.6180 + * Tcl_GetChannelBufferSize --
1.6181 + *
1.6182 + * Retrieves the size of buffers to allocate for this channel.
1.6183 + *
1.6184 + * Results:
1.6185 + * The size.
1.6186 + *
1.6187 + * Side effects:
1.6188 + * None.
1.6189 + *
1.6190 + *----------------------------------------------------------------------
1.6191 + */
1.6192 +
1.6193 +EXPORT_C int
1.6194 +Tcl_GetChannelBufferSize(chan)
1.6195 + Tcl_Channel chan; /* The channel for which to find the
1.6196 + * buffer size. */
1.6197 +{
1.6198 + ChannelState *statePtr = ((Channel *) chan)->state;
1.6199 + /* State of real channel structure. */
1.6200 +
1.6201 + return statePtr->bufSize;
1.6202 +}
1.6203 +
1.6204 +/*
1.6205 + *----------------------------------------------------------------------
1.6206 + *
1.6207 + * Tcl_BadChannelOption --
1.6208 + *
1.6209 + * This procedure generates a "bad option" error message in an
1.6210 + * (optional) interpreter. It is used by channel drivers when
1.6211 + * a invalid Set/Get option is requested. Its purpose is to concatenate
1.6212 + * the generic options list to the specific ones and factorize
1.6213 + * the generic options error message string.
1.6214 + *
1.6215 + * Results:
1.6216 + * TCL_ERROR.
1.6217 + *
1.6218 + * Side effects:
1.6219 + * An error message is generated in interp's result object to
1.6220 + * indicate that a command was invoked with the a bad option
1.6221 + * The message has the form
1.6222 + * bad option "blah": should be one of
1.6223 + * <...generic options...>+<...specific options...>
1.6224 + * "blah" is the optionName argument and "<specific options>"
1.6225 + * is a space separated list of specific option words.
1.6226 + * The function takes good care of inserting minus signs before
1.6227 + * each option, commas after, and an "or" before the last option.
1.6228 + *
1.6229 + *----------------------------------------------------------------------
1.6230 + */
1.6231 +
1.6232 +EXPORT_C int
1.6233 +Tcl_BadChannelOption(interp, optionName, optionList)
1.6234 + Tcl_Interp *interp; /* Current interpreter. (can be NULL)*/
1.6235 + CONST char *optionName; /* 'bad option' name */
1.6236 + CONST char *optionList; /* Specific options list to append
1.6237 + * to the standard generic options.
1.6238 + * can be NULL for generic options
1.6239 + * only.
1.6240 + */
1.6241 +{
1.6242 + if (interp) {
1.6243 + CONST char *genericopt =
1.6244 + "blocking buffering buffersize encoding eofchar translation";
1.6245 + CONST char **argv;
1.6246 + int argc, i;
1.6247 + Tcl_DString ds;
1.6248 +
1.6249 + Tcl_DStringInit(&ds);
1.6250 + Tcl_DStringAppend(&ds, genericopt, -1);
1.6251 + if (optionList && (*optionList)) {
1.6252 + Tcl_DStringAppend(&ds, " ", 1);
1.6253 + Tcl_DStringAppend(&ds, optionList, -1);
1.6254 + }
1.6255 + if (Tcl_SplitList(interp, Tcl_DStringValue(&ds),
1.6256 + &argc, &argv) != TCL_OK) {
1.6257 + panic("malformed option list in channel driver");
1.6258 + }
1.6259 + Tcl_ResetResult(interp);
1.6260 + Tcl_AppendResult(interp, "bad option \"", optionName,
1.6261 + "\": should be one of ", (char *) NULL);
1.6262 + argc--;
1.6263 + for (i = 0; i < argc; i++) {
1.6264 + Tcl_AppendResult(interp, "-", argv[i], ", ", (char *) NULL);
1.6265 + }
1.6266 + Tcl_AppendResult(interp, "or -", argv[i], (char *) NULL);
1.6267 + Tcl_DStringFree(&ds);
1.6268 + ckfree((char *) argv);
1.6269 + }
1.6270 + Tcl_SetErrno(EINVAL);
1.6271 + return TCL_ERROR;
1.6272 +}
1.6273 +
1.6274 +/*
1.6275 + *----------------------------------------------------------------------
1.6276 + *
1.6277 + * Tcl_GetChannelOption --
1.6278 + *
1.6279 + * Gets a mode associated with an IO channel. If the optionName arg
1.6280 + * is non NULL, retrieves the value of that option. If the optionName
1.6281 + * arg is NULL, retrieves a list of alternating option names and
1.6282 + * values for the given channel.
1.6283 + *
1.6284 + * Results:
1.6285 + * A standard Tcl result. Also sets the supplied DString to the
1.6286 + * string value of the option(s) returned.
1.6287 + *
1.6288 + * Side effects:
1.6289 + * None.
1.6290 + *
1.6291 + *----------------------------------------------------------------------
1.6292 + */
1.6293 +
1.6294 +EXPORT_C int
1.6295 +Tcl_GetChannelOption(interp, chan, optionName, dsPtr)
1.6296 + Tcl_Interp *interp; /* For error reporting - can be NULL. */
1.6297 + Tcl_Channel chan; /* Channel on which to get option. */
1.6298 + CONST char *optionName; /* Option to get. */
1.6299 + Tcl_DString *dsPtr; /* Where to store value(s). */
1.6300 +{
1.6301 + size_t len; /* Length of optionName string. */
1.6302 + char optionVal[128]; /* Buffer for sprintf. */
1.6303 + Channel *chanPtr = (Channel *) chan;
1.6304 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.6305 + int flags;
1.6306 +
1.6307 + /*
1.6308 + * Disallow options on dead channels -- channels that have been closed but
1.6309 + * not yet been deallocated. Such channels can be found if the exit
1.6310 + * handler for channel cleanup has run but the channel is still
1.6311 + * registered in an interpreter.
1.6312 + */
1.6313 +
1.6314 + if (CheckForDeadChannel(interp, statePtr)) {
1.6315 + return TCL_ERROR;
1.6316 + }
1.6317 +
1.6318 + /*
1.6319 + * This operation should occur at the top of a channel stack.
1.6320 + */
1.6321 +
1.6322 + chanPtr = statePtr->topChanPtr;
1.6323 +
1.6324 + /*
1.6325 + * If we are in the middle of a background copy, use the saved flags.
1.6326 + */
1.6327 +
1.6328 + if (statePtr->csPtr) {
1.6329 + if (chanPtr == statePtr->csPtr->readPtr) {
1.6330 + flags = statePtr->csPtr->readFlags;
1.6331 + } else {
1.6332 + flags = statePtr->csPtr->writeFlags;
1.6333 + }
1.6334 + } else {
1.6335 + flags = statePtr->flags;
1.6336 + }
1.6337 +
1.6338 + /*
1.6339 + * If the optionName is NULL it means that we want a list of all
1.6340 + * options and values.
1.6341 + */
1.6342 +
1.6343 + if (optionName == (char *) NULL) {
1.6344 + len = 0;
1.6345 + } else {
1.6346 + len = strlen(optionName);
1.6347 + }
1.6348 +
1.6349 + if ((len == 0) || ((len > 2) && (optionName[1] == 'b') &&
1.6350 + (strncmp(optionName, "-blocking", len) == 0))) {
1.6351 + if (len == 0) {
1.6352 + Tcl_DStringAppendElement(dsPtr, "-blocking");
1.6353 + }
1.6354 + Tcl_DStringAppendElement(dsPtr,
1.6355 + (flags & CHANNEL_NONBLOCKING) ? "0" : "1");
1.6356 + if (len > 0) {
1.6357 + return TCL_OK;
1.6358 + }
1.6359 + }
1.6360 + if ((len == 0) || ((len > 7) && (optionName[1] == 'b') &&
1.6361 + (strncmp(optionName, "-buffering", len) == 0))) {
1.6362 + if (len == 0) {
1.6363 + Tcl_DStringAppendElement(dsPtr, "-buffering");
1.6364 + }
1.6365 + if (flags & CHANNEL_LINEBUFFERED) {
1.6366 + Tcl_DStringAppendElement(dsPtr, "line");
1.6367 + } else if (flags & CHANNEL_UNBUFFERED) {
1.6368 + Tcl_DStringAppendElement(dsPtr, "none");
1.6369 + } else {
1.6370 + Tcl_DStringAppendElement(dsPtr, "full");
1.6371 + }
1.6372 + if (len > 0) {
1.6373 + return TCL_OK;
1.6374 + }
1.6375 + }
1.6376 + if ((len == 0) || ((len > 7) && (optionName[1] == 'b') &&
1.6377 + (strncmp(optionName, "-buffersize", len) == 0))) {
1.6378 + if (len == 0) {
1.6379 + Tcl_DStringAppendElement(dsPtr, "-buffersize");
1.6380 + }
1.6381 + TclFormatInt(optionVal, statePtr->bufSize);
1.6382 + Tcl_DStringAppendElement(dsPtr, optionVal);
1.6383 + if (len > 0) {
1.6384 + return TCL_OK;
1.6385 + }
1.6386 + }
1.6387 + if ((len == 0) ||
1.6388 + ((len > 2) && (optionName[1] == 'e') &&
1.6389 + (strncmp(optionName, "-encoding", len) == 0))) {
1.6390 + if (len == 0) {
1.6391 + Tcl_DStringAppendElement(dsPtr, "-encoding");
1.6392 + }
1.6393 + if (statePtr->encoding == NULL) {
1.6394 + Tcl_DStringAppendElement(dsPtr, "binary");
1.6395 + } else {
1.6396 + Tcl_DStringAppendElement(dsPtr,
1.6397 + Tcl_GetEncodingName(statePtr->encoding));
1.6398 + }
1.6399 + if (len > 0) {
1.6400 + return TCL_OK;
1.6401 + }
1.6402 + }
1.6403 + if ((len == 0) ||
1.6404 + ((len > 2) && (optionName[1] == 'e') &&
1.6405 + (strncmp(optionName, "-eofchar", len) == 0))) {
1.6406 + if (len == 0) {
1.6407 + Tcl_DStringAppendElement(dsPtr, "-eofchar");
1.6408 + }
1.6409 + if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
1.6410 + (TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
1.6411 + Tcl_DStringStartSublist(dsPtr);
1.6412 + }
1.6413 + if (flags & TCL_READABLE) {
1.6414 + if (statePtr->inEofChar == 0) {
1.6415 + Tcl_DStringAppendElement(dsPtr, "");
1.6416 + } else {
1.6417 + char buf[4];
1.6418 +
1.6419 + sprintf(buf, "%c", statePtr->inEofChar);
1.6420 + Tcl_DStringAppendElement(dsPtr, buf);
1.6421 + }
1.6422 + }
1.6423 + if (flags & TCL_WRITABLE) {
1.6424 + if (statePtr->outEofChar == 0) {
1.6425 + Tcl_DStringAppendElement(dsPtr, "");
1.6426 + } else {
1.6427 + char buf[4];
1.6428 +
1.6429 + sprintf(buf, "%c", statePtr->outEofChar);
1.6430 + Tcl_DStringAppendElement(dsPtr, buf);
1.6431 + }
1.6432 + }
1.6433 + if ( !(flags & (TCL_READABLE|TCL_WRITABLE))) {
1.6434 + /* Not readable or writable (server socket) */
1.6435 + Tcl_DStringAppendElement(dsPtr, "");
1.6436 + }
1.6437 + if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
1.6438 + (TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
1.6439 + Tcl_DStringEndSublist(dsPtr);
1.6440 + }
1.6441 + if (len > 0) {
1.6442 + return TCL_OK;
1.6443 + }
1.6444 + }
1.6445 + if ((len == 0) ||
1.6446 + ((len > 1) && (optionName[1] == 't') &&
1.6447 + (strncmp(optionName, "-translation", len) == 0))) {
1.6448 + if (len == 0) {
1.6449 + Tcl_DStringAppendElement(dsPtr, "-translation");
1.6450 + }
1.6451 + if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
1.6452 + (TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
1.6453 + Tcl_DStringStartSublist(dsPtr);
1.6454 + }
1.6455 + if (flags & TCL_READABLE) {
1.6456 + if (statePtr->inputTranslation == TCL_TRANSLATE_AUTO) {
1.6457 + Tcl_DStringAppendElement(dsPtr, "auto");
1.6458 + } else if (statePtr->inputTranslation == TCL_TRANSLATE_CR) {
1.6459 + Tcl_DStringAppendElement(dsPtr, "cr");
1.6460 + } else if (statePtr->inputTranslation == TCL_TRANSLATE_CRLF) {
1.6461 + Tcl_DStringAppendElement(dsPtr, "crlf");
1.6462 + } else {
1.6463 + Tcl_DStringAppendElement(dsPtr, "lf");
1.6464 + }
1.6465 + }
1.6466 + if (flags & TCL_WRITABLE) {
1.6467 + if (statePtr->outputTranslation == TCL_TRANSLATE_AUTO) {
1.6468 + Tcl_DStringAppendElement(dsPtr, "auto");
1.6469 + } else if (statePtr->outputTranslation == TCL_TRANSLATE_CR) {
1.6470 + Tcl_DStringAppendElement(dsPtr, "cr");
1.6471 + } else if (statePtr->outputTranslation == TCL_TRANSLATE_CRLF) {
1.6472 + Tcl_DStringAppendElement(dsPtr, "crlf");
1.6473 + } else {
1.6474 + Tcl_DStringAppendElement(dsPtr, "lf");
1.6475 + }
1.6476 + }
1.6477 + if ( !(flags & (TCL_READABLE|TCL_WRITABLE))) {
1.6478 + /* Not readable or writable (server socket) */
1.6479 + Tcl_DStringAppendElement(dsPtr, "auto");
1.6480 + }
1.6481 + if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
1.6482 + (TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
1.6483 + Tcl_DStringEndSublist(dsPtr);
1.6484 + }
1.6485 + if (len > 0) {
1.6486 + return TCL_OK;
1.6487 + }
1.6488 + }
1.6489 + if (chanPtr->typePtr->getOptionProc != (Tcl_DriverGetOptionProc *) NULL) {
1.6490 + /*
1.6491 + * let the driver specific handle additional options
1.6492 + * and result code and message.
1.6493 + */
1.6494 +
1.6495 + return (chanPtr->typePtr->getOptionProc) (chanPtr->instanceData,
1.6496 + interp, optionName, dsPtr);
1.6497 + } else {
1.6498 + /*
1.6499 + * no driver specific options case.
1.6500 + */
1.6501 +
1.6502 + if (len == 0) {
1.6503 + return TCL_OK;
1.6504 + }
1.6505 + return Tcl_BadChannelOption(interp, optionName, NULL);
1.6506 + }
1.6507 +}
1.6508 +
1.6509 +/*
1.6510 + *---------------------------------------------------------------------------
1.6511 + *
1.6512 + * Tcl_SetChannelOption --
1.6513 + *
1.6514 + * Sets an option on a channel.
1.6515 + *
1.6516 + * Results:
1.6517 + * A standard Tcl result. On error, sets interp's result object
1.6518 + * if interp is not NULL.
1.6519 + *
1.6520 + * Side effects:
1.6521 + * May modify an option on a device.
1.6522 + *
1.6523 + *---------------------------------------------------------------------------
1.6524 + */
1.6525 +
1.6526 +EXPORT_C int
1.6527 +Tcl_SetChannelOption(interp, chan, optionName, newValue)
1.6528 + Tcl_Interp *interp; /* For error reporting - can be NULL. */
1.6529 + Tcl_Channel chan; /* Channel on which to set mode. */
1.6530 + CONST char *optionName; /* Which option to set? */
1.6531 + CONST char *newValue; /* New value for option. */
1.6532 +{
1.6533 + Channel *chanPtr = (Channel *) chan; /* The real IO channel. */
1.6534 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.6535 + size_t len; /* Length of optionName string. */
1.6536 + int argc;
1.6537 + CONST char **argv;
1.6538 +
1.6539 + /*
1.6540 + * If the channel is in the middle of a background copy, fail.
1.6541 + */
1.6542 +
1.6543 + if (statePtr->csPtr) {
1.6544 + if (interp) {
1.6545 + Tcl_AppendResult(interp,
1.6546 + "unable to set channel options: background copy in progress",
1.6547 + (char *) NULL);
1.6548 + }
1.6549 + return TCL_ERROR;
1.6550 + }
1.6551 +
1.6552 + /*
1.6553 + * Disallow options on dead channels -- channels that have been closed but
1.6554 + * not yet been deallocated. Such channels can be found if the exit
1.6555 + * handler for channel cleanup has run but the channel is still
1.6556 + * registered in an interpreter.
1.6557 + */
1.6558 +
1.6559 + if (CheckForDeadChannel(NULL, statePtr)) {
1.6560 + return TCL_ERROR;
1.6561 + }
1.6562 +
1.6563 + /*
1.6564 + * This operation should occur at the top of a channel stack.
1.6565 + */
1.6566 +
1.6567 + chanPtr = statePtr->topChanPtr;
1.6568 +
1.6569 + len = strlen(optionName);
1.6570 +
1.6571 + if ((len > 2) && (optionName[1] == 'b') &&
1.6572 + (strncmp(optionName, "-blocking", len) == 0)) {
1.6573 + int newMode;
1.6574 + if (Tcl_GetBoolean(interp, newValue, &newMode) == TCL_ERROR) {
1.6575 + return TCL_ERROR;
1.6576 + }
1.6577 + if (newMode) {
1.6578 + newMode = TCL_MODE_BLOCKING;
1.6579 + } else {
1.6580 + newMode = TCL_MODE_NONBLOCKING;
1.6581 + }
1.6582 + return SetBlockMode(interp, chanPtr, newMode);
1.6583 + } else if ((len > 7) && (optionName[1] == 'b') &&
1.6584 + (strncmp(optionName, "-buffering", len) == 0)) {
1.6585 + len = strlen(newValue);
1.6586 + if ((newValue[0] == 'f') && (strncmp(newValue, "full", len) == 0)) {
1.6587 + statePtr->flags &=
1.6588 + (~(CHANNEL_UNBUFFERED|CHANNEL_LINEBUFFERED));
1.6589 + } else if ((newValue[0] == 'l') &&
1.6590 + (strncmp(newValue, "line", len) == 0)) {
1.6591 + statePtr->flags &= (~(CHANNEL_UNBUFFERED));
1.6592 + statePtr->flags |= CHANNEL_LINEBUFFERED;
1.6593 + } else if ((newValue[0] == 'n') &&
1.6594 + (strncmp(newValue, "none", len) == 0)) {
1.6595 + statePtr->flags &= (~(CHANNEL_LINEBUFFERED));
1.6596 + statePtr->flags |= CHANNEL_UNBUFFERED;
1.6597 + } else {
1.6598 + if (interp) {
1.6599 + Tcl_AppendResult(interp, "bad value for -buffering: ",
1.6600 + "must be one of full, line, or none",
1.6601 + (char *) NULL);
1.6602 + return TCL_ERROR;
1.6603 + }
1.6604 + }
1.6605 + return TCL_OK;
1.6606 + } else if ((len > 7) && (optionName[1] == 'b') &&
1.6607 + (strncmp(optionName, "-buffersize", len) == 0)) {
1.6608 + int newBufferSize;
1.6609 + if (Tcl_GetInt(interp, newValue, &newBufferSize) == TCL_ERROR) {
1.6610 + return TCL_ERROR;
1.6611 + }
1.6612 + Tcl_SetChannelBufferSize(chan, newBufferSize);
1.6613 + } else if ((len > 2) && (optionName[1] == 'e') &&
1.6614 + (strncmp(optionName, "-encoding", len) == 0)) {
1.6615 + Tcl_Encoding encoding;
1.6616 +
1.6617 + if ((newValue[0] == '\0') || (strcmp(newValue, "binary") == 0)) {
1.6618 + encoding = NULL;
1.6619 + } else {
1.6620 + encoding = Tcl_GetEncoding(interp, newValue);
1.6621 + if (encoding == NULL) {
1.6622 + return TCL_ERROR;
1.6623 + }
1.6624 + }
1.6625 + /*
1.6626 + * When the channel has an escape sequence driven encoding such as
1.6627 + * iso2022, the terminated escape sequence must write to the buffer.
1.6628 + */
1.6629 + if ((statePtr->encoding != NULL) && (statePtr->curOutPtr != NULL)
1.6630 + && (CheckChannelErrors(statePtr, TCL_WRITABLE) == 0)) {
1.6631 + statePtr->outputEncodingFlags |= TCL_ENCODING_END;
1.6632 + WriteChars(chanPtr, "", 0);
1.6633 + }
1.6634 + Tcl_FreeEncoding(statePtr->encoding);
1.6635 + statePtr->encoding = encoding;
1.6636 + statePtr->inputEncodingState = NULL;
1.6637 + statePtr->inputEncodingFlags = TCL_ENCODING_START;
1.6638 + statePtr->outputEncodingState = NULL;
1.6639 + statePtr->outputEncodingFlags = TCL_ENCODING_START;
1.6640 + statePtr->flags &= ~CHANNEL_NEED_MORE_DATA;
1.6641 + UpdateInterest(chanPtr);
1.6642 + } else if ((len > 2) && (optionName[1] == 'e') &&
1.6643 + (strncmp(optionName, "-eofchar", len) == 0)) {
1.6644 + if (Tcl_SplitList(interp, newValue, &argc, &argv) == TCL_ERROR) {
1.6645 + return TCL_ERROR;
1.6646 + }
1.6647 + if (argc == 0) {
1.6648 + statePtr->inEofChar = 0;
1.6649 + statePtr->outEofChar = 0;
1.6650 + } else if (argc == 1) {
1.6651 + if (statePtr->flags & TCL_WRITABLE) {
1.6652 + statePtr->outEofChar = (int) argv[0][0];
1.6653 + }
1.6654 + if (statePtr->flags & TCL_READABLE) {
1.6655 + statePtr->inEofChar = (int) argv[0][0];
1.6656 + }
1.6657 + } else if (argc != 2) {
1.6658 + if (interp) {
1.6659 + Tcl_AppendResult(interp,
1.6660 + "bad value for -eofchar: should be a list of zero,",
1.6661 + " one, or two elements", (char *) NULL);
1.6662 + }
1.6663 + ckfree((char *) argv);
1.6664 + return TCL_ERROR;
1.6665 + } else {
1.6666 + if (statePtr->flags & TCL_READABLE) {
1.6667 + statePtr->inEofChar = (int) argv[0][0];
1.6668 + }
1.6669 + if (statePtr->flags & TCL_WRITABLE) {
1.6670 + statePtr->outEofChar = (int) argv[1][0];
1.6671 + }
1.6672 + }
1.6673 + if (argv != NULL) {
1.6674 + ckfree((char *) argv);
1.6675 + }
1.6676 +
1.6677 + /*
1.6678 + * [SF Tcl Bug 930851] Reset EOF and BLOCKED flags. Changing
1.6679 + * the character which signals eof can transform a current eof
1.6680 + * condition into a 'go ahead'. Ditto for blocked.
1.6681 + */
1.6682 +
1.6683 + statePtr->flags &= (~(CHANNEL_EOF | CHANNEL_STICKY_EOF | CHANNEL_BLOCKED));
1.6684 +
1.6685 + return TCL_OK;
1.6686 + } else if ((len > 1) && (optionName[1] == 't') &&
1.6687 + (strncmp(optionName, "-translation", len) == 0)) {
1.6688 + CONST char *readMode, *writeMode;
1.6689 +
1.6690 + if (Tcl_SplitList(interp, newValue, &argc, &argv) == TCL_ERROR) {
1.6691 + return TCL_ERROR;
1.6692 + }
1.6693 +
1.6694 + if (argc == 1) {
1.6695 + readMode = (statePtr->flags & TCL_READABLE) ? argv[0] : NULL;
1.6696 + writeMode = (statePtr->flags & TCL_WRITABLE) ? argv[0] : NULL;
1.6697 + } else if (argc == 2) {
1.6698 + readMode = (statePtr->flags & TCL_READABLE) ? argv[0] : NULL;
1.6699 + writeMode = (statePtr->flags & TCL_WRITABLE) ? argv[1] : NULL;
1.6700 + } else {
1.6701 + if (interp) {
1.6702 + Tcl_AppendResult(interp,
1.6703 + "bad value for -translation: must be a one or two",
1.6704 + " element list", (char *) NULL);
1.6705 + }
1.6706 + ckfree((char *) argv);
1.6707 + return TCL_ERROR;
1.6708 + }
1.6709 +
1.6710 + if (readMode) {
1.6711 + TclEolTranslation translation;
1.6712 + if (*readMode == '\0') {
1.6713 + translation = statePtr->inputTranslation;
1.6714 + } else if (strcmp(readMode, "auto") == 0) {
1.6715 + translation = TCL_TRANSLATE_AUTO;
1.6716 + } else if (strcmp(readMode, "binary") == 0) {
1.6717 + translation = TCL_TRANSLATE_LF;
1.6718 + statePtr->inEofChar = 0;
1.6719 + Tcl_FreeEncoding(statePtr->encoding);
1.6720 + statePtr->encoding = NULL;
1.6721 + } else if (strcmp(readMode, "lf") == 0) {
1.6722 + translation = TCL_TRANSLATE_LF;
1.6723 + } else if (strcmp(readMode, "cr") == 0) {
1.6724 + translation = TCL_TRANSLATE_CR;
1.6725 + } else if (strcmp(readMode, "crlf") == 0) {
1.6726 + translation = TCL_TRANSLATE_CRLF;
1.6727 + } else if (strcmp(readMode, "platform") == 0) {
1.6728 + translation = TCL_PLATFORM_TRANSLATION;
1.6729 + } else {
1.6730 + if (interp) {
1.6731 + Tcl_AppendResult(interp,
1.6732 + "bad value for -translation: ",
1.6733 + "must be one of auto, binary, cr, lf, crlf,",
1.6734 + " or platform", (char *) NULL);
1.6735 + }
1.6736 + ckfree((char *) argv);
1.6737 + return TCL_ERROR;
1.6738 + }
1.6739 +
1.6740 + /*
1.6741 + * Reset the EOL flags since we need to look at any buffered
1.6742 + * data to see if the new translation mode allows us to
1.6743 + * complete the line.
1.6744 + */
1.6745 +
1.6746 + if (translation != statePtr->inputTranslation) {
1.6747 + statePtr->inputTranslation = translation;
1.6748 + statePtr->flags &= ~(INPUT_SAW_CR);
1.6749 + statePtr->flags &= ~(CHANNEL_NEED_MORE_DATA);
1.6750 + UpdateInterest(chanPtr);
1.6751 + }
1.6752 + }
1.6753 + if (writeMode) {
1.6754 + if (*writeMode == '\0') {
1.6755 + /* Do nothing. */
1.6756 + } else if (strcmp(writeMode, "auto") == 0) {
1.6757 + /*
1.6758 + * This is a hack to get TCP sockets to produce output
1.6759 + * in CRLF mode if they are being set into AUTO mode.
1.6760 + * A better solution for achieving this effect will be
1.6761 + * coded later.
1.6762 + */
1.6763 +
1.6764 + if (strcmp(Tcl_ChannelName(chanPtr->typePtr), "tcp") == 0) {
1.6765 + statePtr->outputTranslation = TCL_TRANSLATE_CRLF;
1.6766 + } else {
1.6767 + statePtr->outputTranslation = TCL_PLATFORM_TRANSLATION;
1.6768 + }
1.6769 + } else if (strcmp(writeMode, "binary") == 0) {
1.6770 + statePtr->outEofChar = 0;
1.6771 + statePtr->outputTranslation = TCL_TRANSLATE_LF;
1.6772 + Tcl_FreeEncoding(statePtr->encoding);
1.6773 + statePtr->encoding = NULL;
1.6774 + } else if (strcmp(writeMode, "lf") == 0) {
1.6775 + statePtr->outputTranslation = TCL_TRANSLATE_LF;
1.6776 + } else if (strcmp(writeMode, "cr") == 0) {
1.6777 + statePtr->outputTranslation = TCL_TRANSLATE_CR;
1.6778 + } else if (strcmp(writeMode, "crlf") == 0) {
1.6779 + statePtr->outputTranslation = TCL_TRANSLATE_CRLF;
1.6780 + } else if (strcmp(writeMode, "platform") == 0) {
1.6781 + statePtr->outputTranslation = TCL_PLATFORM_TRANSLATION;
1.6782 + } else {
1.6783 + if (interp) {
1.6784 + Tcl_AppendResult(interp,
1.6785 + "bad value for -translation: ",
1.6786 + "must be one of auto, binary, cr, lf, crlf,",
1.6787 + " or platform", (char *) NULL);
1.6788 + }
1.6789 + ckfree((char *) argv);
1.6790 + return TCL_ERROR;
1.6791 + }
1.6792 + }
1.6793 + ckfree((char *) argv);
1.6794 + return TCL_OK;
1.6795 + } else if (chanPtr->typePtr->setOptionProc != NULL) {
1.6796 + return (*chanPtr->typePtr->setOptionProc)(chanPtr->instanceData,
1.6797 + interp, optionName, newValue);
1.6798 + } else {
1.6799 + return Tcl_BadChannelOption(interp, optionName, (char *) NULL);
1.6800 + }
1.6801 +
1.6802 + /*
1.6803 + * If bufsize changes, need to get rid of old utility buffer.
1.6804 + */
1.6805 +
1.6806 + if (statePtr->saveInBufPtr != NULL) {
1.6807 + RecycleBuffer(statePtr, statePtr->saveInBufPtr, 1);
1.6808 + statePtr->saveInBufPtr = NULL;
1.6809 + }
1.6810 + if (statePtr->inQueueHead != NULL) {
1.6811 + if ((statePtr->inQueueHead->nextPtr == NULL)
1.6812 + && (statePtr->inQueueHead->nextAdded ==
1.6813 + statePtr->inQueueHead->nextRemoved)) {
1.6814 + RecycleBuffer(statePtr, statePtr->inQueueHead, 1);
1.6815 + statePtr->inQueueHead = NULL;
1.6816 + statePtr->inQueueTail = NULL;
1.6817 + }
1.6818 + }
1.6819 +
1.6820 + /*
1.6821 + * If encoding or bufsize changes, need to update output staging buffer.
1.6822 + */
1.6823 +
1.6824 + if (statePtr->outputStage != NULL) {
1.6825 + ckfree((char *) statePtr->outputStage);
1.6826 + statePtr->outputStage = NULL;
1.6827 + }
1.6828 + if ((statePtr->encoding != NULL) && (statePtr->flags & TCL_WRITABLE)) {
1.6829 + statePtr->outputStage = (char *)
1.6830 + ckalloc((unsigned) (statePtr->bufSize + 2));
1.6831 + }
1.6832 + return TCL_OK;
1.6833 +}
1.6834 +
1.6835 +/*
1.6836 + *----------------------------------------------------------------------
1.6837 + *
1.6838 + * CleanupChannelHandlers --
1.6839 + *
1.6840 + * Removes channel handlers that refer to the supplied interpreter,
1.6841 + * so that if the actual channel is not closed now, these handlers
1.6842 + * will not run on subsequent events on the channel. This would be
1.6843 + * erroneous, because the interpreter no longer has a reference to
1.6844 + * this channel.
1.6845 + *
1.6846 + * Results:
1.6847 + * None.
1.6848 + *
1.6849 + * Side effects:
1.6850 + * Removes channel handlers.
1.6851 + *
1.6852 + *----------------------------------------------------------------------
1.6853 + */
1.6854 +
1.6855 +static void
1.6856 +CleanupChannelHandlers(interp, chanPtr)
1.6857 + Tcl_Interp *interp;
1.6858 + Channel *chanPtr;
1.6859 +{
1.6860 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.6861 + EventScriptRecord *sPtr, *prevPtr, *nextPtr;
1.6862 +
1.6863 + /*
1.6864 + * Remove fileevent records on this channel that refer to the
1.6865 + * given interpreter.
1.6866 + */
1.6867 +
1.6868 + for (sPtr = statePtr->scriptRecordPtr,
1.6869 + prevPtr = (EventScriptRecord *) NULL;
1.6870 + sPtr != (EventScriptRecord *) NULL;
1.6871 + sPtr = nextPtr) {
1.6872 + nextPtr = sPtr->nextPtr;
1.6873 + if (sPtr->interp == interp) {
1.6874 + if (prevPtr == (EventScriptRecord *) NULL) {
1.6875 + statePtr->scriptRecordPtr = nextPtr;
1.6876 + } else {
1.6877 + prevPtr->nextPtr = nextPtr;
1.6878 + }
1.6879 +
1.6880 + Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
1.6881 + TclChannelEventScriptInvoker, (ClientData) sPtr);
1.6882 +
1.6883 + Tcl_DecrRefCount(sPtr->scriptPtr);
1.6884 + ckfree((char *) sPtr);
1.6885 + } else {
1.6886 + prevPtr = sPtr;
1.6887 + }
1.6888 + }
1.6889 +}
1.6890 +
1.6891 +/*
1.6892 + *----------------------------------------------------------------------
1.6893 + *
1.6894 + * Tcl_NotifyChannel --
1.6895 + *
1.6896 + * This procedure is called by a channel driver when a driver
1.6897 + * detects an event on a channel. This procedure is responsible
1.6898 + * for actually handling the event by invoking any channel
1.6899 + * handler callbacks.
1.6900 + *
1.6901 + * Results:
1.6902 + * None.
1.6903 + *
1.6904 + * Side effects:
1.6905 + * Whatever the channel handler callback procedure does.
1.6906 + *
1.6907 + *----------------------------------------------------------------------
1.6908 + */
1.6909 +
1.6910 +EXPORT_C void
1.6911 +Tcl_NotifyChannel(channel, mask)
1.6912 + Tcl_Channel channel; /* Channel that detected an event. */
1.6913 + int mask; /* OR'ed combination of TCL_READABLE,
1.6914 + * TCL_WRITABLE, or TCL_EXCEPTION: indicates
1.6915 + * which events were detected. */
1.6916 +{
1.6917 + Channel *chanPtr = (Channel *) channel;
1.6918 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.6919 + ChannelHandler *chPtr;
1.6920 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.6921 + NextChannelHandler nh;
1.6922 + Channel* upChanPtr;
1.6923 + Tcl_ChannelType* upTypePtr;
1.6924 +
1.6925 +#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
1.6926 + /* [SF Tcl Bug 943274]
1.6927 + * For a non-blocking channel without blockmodeproc we keep track
1.6928 + * of actual input coming from the OS so that we can do a credible
1.6929 + * imitation of non-blocking behaviour.
1.6930 + */
1.6931 +
1.6932 + if ((mask & TCL_READABLE) &&
1.6933 + (statePtr->flags & CHANNEL_NONBLOCKING) &&
1.6934 + (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL) &&
1.6935 + !(statePtr->flags & CHANNEL_TIMER_FEV)) {
1.6936 +
1.6937 + statePtr->flags |= CHANNEL_HAS_MORE_DATA;
1.6938 + }
1.6939 +#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
1.6940 +
1.6941 + /*
1.6942 + * In contrast to the other API functions this procedure walks towards
1.6943 + * the top of a stack and not down from it.
1.6944 + *
1.6945 + * The channel calling this procedure is the one who generated the event,
1.6946 + * and thus does not take part in handling it. IOW, its HandlerProc is
1.6947 + * not called, instead we begin with the channel above it.
1.6948 + *
1.6949 + * This behaviour also allows the transformation channels to
1.6950 + * generate their own events and pass them upward.
1.6951 + */
1.6952 +
1.6953 + while (mask && (chanPtr->upChanPtr != ((Channel*) NULL))) {
1.6954 + Tcl_DriverHandlerProc* upHandlerProc;
1.6955 +
1.6956 + upChanPtr = chanPtr->upChanPtr;
1.6957 + upTypePtr = upChanPtr->typePtr;
1.6958 + upHandlerProc = Tcl_ChannelHandlerProc(upTypePtr);
1.6959 + if (upHandlerProc != NULL) {
1.6960 + mask = (*upHandlerProc) (upChanPtr->instanceData, mask);
1.6961 + }
1.6962 +
1.6963 + /* ELSE:
1.6964 + * Ignore transformations which are unable to handle the event
1.6965 + * coming from below. Assume that they don't change the mask and
1.6966 + * pass it on.
1.6967 + */
1.6968 +
1.6969 + chanPtr = upChanPtr;
1.6970 + }
1.6971 +
1.6972 + channel = (Tcl_Channel) chanPtr;
1.6973 +
1.6974 + /*
1.6975 + * Here we have either reached the top of the stack or the mask is
1.6976 + * empty. We break out of the procedure if it is the latter.
1.6977 + */
1.6978 +
1.6979 + if (!mask) {
1.6980 + return;
1.6981 + }
1.6982 +
1.6983 + /*
1.6984 + * We are now above the topmost channel in a stack and have events
1.6985 + * left. Now call the channel handlers as usual.
1.6986 + *
1.6987 + * Preserve the channel struct in case the script closes it.
1.6988 + */
1.6989 +
1.6990 + Tcl_Preserve((ClientData) channel);
1.6991 + Tcl_Preserve((ClientData) statePtr);
1.6992 +
1.6993 + /*
1.6994 + * If we are flushing in the background, be sure to call FlushChannel
1.6995 + * for writable events. Note that we have to discard the writable
1.6996 + * event so we don't call any write handlers before the flush is
1.6997 + * complete.
1.6998 + */
1.6999 +
1.7000 + if ((statePtr->flags & BG_FLUSH_SCHEDULED) && (mask & TCL_WRITABLE)) {
1.7001 + FlushChannel(NULL, chanPtr, 1);
1.7002 + mask &= ~TCL_WRITABLE;
1.7003 + }
1.7004 +
1.7005 + /*
1.7006 + * Add this invocation to the list of recursive invocations of
1.7007 + * ChannelHandlerEventProc.
1.7008 + */
1.7009 +
1.7010 + nh.nextHandlerPtr = (ChannelHandler *) NULL;
1.7011 + nh.nestedHandlerPtr = tsdPtr->nestedHandlerPtr;
1.7012 + tsdPtr->nestedHandlerPtr = &nh;
1.7013 +
1.7014 + for (chPtr = statePtr->chPtr; chPtr != (ChannelHandler *) NULL; ) {
1.7015 + /*
1.7016 + * If this channel handler is interested in any of the events that
1.7017 + * have occurred on the channel, invoke its procedure.
1.7018 + */
1.7019 +
1.7020 + if ((chPtr->mask & mask) != 0) {
1.7021 + nh.nextHandlerPtr = chPtr->nextPtr;
1.7022 + (*(chPtr->proc))(chPtr->clientData, mask);
1.7023 + chPtr = nh.nextHandlerPtr;
1.7024 + } else {
1.7025 + chPtr = chPtr->nextPtr;
1.7026 + }
1.7027 + }
1.7028 +
1.7029 + /*
1.7030 + * Update the notifier interest, since it may have changed after
1.7031 + * invoking event handlers. Skip that if the channel was deleted
1.7032 + * in the call to the channel handler.
1.7033 + */
1.7034 +
1.7035 + if (chanPtr->typePtr != NULL) {
1.7036 + UpdateInterest(chanPtr);
1.7037 + }
1.7038 +
1.7039 + Tcl_Release((ClientData) statePtr);
1.7040 + Tcl_Release((ClientData) channel);
1.7041 +
1.7042 + tsdPtr->nestedHandlerPtr = nh.nestedHandlerPtr;
1.7043 +}
1.7044 +
1.7045 +/*
1.7046 + *----------------------------------------------------------------------
1.7047 + *
1.7048 + * UpdateInterest --
1.7049 + *
1.7050 + * Arrange for the notifier to call us back at appropriate times
1.7051 + * based on the current state of the channel.
1.7052 + *
1.7053 + * Results:
1.7054 + * None.
1.7055 + *
1.7056 + * Side effects:
1.7057 + * May schedule a timer or driver handler.
1.7058 + *
1.7059 + *----------------------------------------------------------------------
1.7060 + */
1.7061 +
1.7062 +static void
1.7063 +UpdateInterest(chanPtr)
1.7064 + Channel *chanPtr; /* Channel to update. */
1.7065 +{
1.7066 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.7067 + int mask = statePtr->interestMask;
1.7068 +
1.7069 + /*
1.7070 + * If there are flushed buffers waiting to be written, then
1.7071 + * we need to watch for the channel to become writable.
1.7072 + */
1.7073 +
1.7074 + if (statePtr->flags & BG_FLUSH_SCHEDULED) {
1.7075 + mask |= TCL_WRITABLE;
1.7076 + }
1.7077 +
1.7078 + /*
1.7079 + * If there is data in the input queue, and we aren't waiting for more
1.7080 + * data, then we need to schedule a timer so we don't block in the
1.7081 + * notifier. Also, cancel the read interest so we don't get duplicate
1.7082 + * events.
1.7083 + */
1.7084 +
1.7085 + if (mask & TCL_READABLE) {
1.7086 + if (!(statePtr->flags & CHANNEL_NEED_MORE_DATA)
1.7087 + && (statePtr->inQueueHead != (ChannelBuffer *) NULL)
1.7088 + && (statePtr->inQueueHead->nextRemoved <
1.7089 + statePtr->inQueueHead->nextAdded)) {
1.7090 + mask &= ~TCL_READABLE;
1.7091 +
1.7092 + /*
1.7093 + * Andreas Kupries, April 11, 2003
1.7094 + *
1.7095 + * Some operating systems (Solaris 2.6 and higher (but not
1.7096 + * Solaris 2.5, go figure)) generate READABLE and
1.7097 + * EXCEPTION events when select()'ing [*] on a plain file,
1.7098 + * even if EOF was not yet reached. This is a problem in
1.7099 + * the following situation:
1.7100 + *
1.7101 + * - An extension asks to get both READABLE and EXCEPTION
1.7102 + * events.
1.7103 + * - It reads data into a buffer smaller than the buffer
1.7104 + * used by Tcl itself.
1.7105 + * - It does not process all events in the event queue, but
1.7106 + * only only one, at least in some situations.
1.7107 + *
1.7108 + * In that case we can get into a situation where
1.7109 + *
1.7110 + * - Tcl drops READABLE here, because it has data in its own
1.7111 + * buffers waiting to be read by the extension.
1.7112 + * - A READABLE event is syntesized via timer.
1.7113 + * - The OS still reports the EXCEPTION condition on the file.
1.7114 + * - And the extension gets the EXCPTION event first, and
1.7115 + * handles this as EOF.
1.7116 + *
1.7117 + * End result ==> Premature end of reading from a file.
1.7118 + *
1.7119 + * The concrete example is 'Expect', and its [expect]
1.7120 + * command (and at the C-level, deep in the bowels of
1.7121 + * Expect, 'exp_get_next_event'. See marker 'SunOS' for
1.7122 + * commentary in that function too).
1.7123 + *
1.7124 + * [*] As the Tcl notifier does. See also for marker
1.7125 + * 'SunOS' in file 'exp_event.c' of Expect.
1.7126 + *
1.7127 + * Our solution here is to drop the interest in the
1.7128 + * EXCEPTION events too. This compiles on all platforms,
1.7129 + * and also passes the testsuite on all of them.
1.7130 + */
1.7131 +
1.7132 + mask &= ~TCL_EXCEPTION;
1.7133 +
1.7134 + if (!statePtr->timer) {
1.7135 + statePtr->timer = Tcl_CreateTimerHandler(0, ChannelTimerProc,
1.7136 + (ClientData) chanPtr);
1.7137 + }
1.7138 + }
1.7139 + }
1.7140 + (chanPtr->typePtr->watchProc)(chanPtr->instanceData, mask);
1.7141 +}
1.7142 +
1.7143 +/*
1.7144 + *----------------------------------------------------------------------
1.7145 + *
1.7146 + * ChannelTimerProc --
1.7147 + *
1.7148 + * Timer handler scheduled by UpdateInterest to monitor the
1.7149 + * channel buffers until they are empty.
1.7150 + *
1.7151 + * Results:
1.7152 + * None.
1.7153 + *
1.7154 + * Side effects:
1.7155 + * May invoke channel handlers.
1.7156 + *
1.7157 + *----------------------------------------------------------------------
1.7158 + */
1.7159 +
1.7160 +static void
1.7161 +ChannelTimerProc(clientData)
1.7162 + ClientData clientData;
1.7163 +{
1.7164 + Channel *chanPtr = (Channel *) clientData;
1.7165 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.7166 +
1.7167 + if (!(statePtr->flags & CHANNEL_NEED_MORE_DATA)
1.7168 + && (statePtr->interestMask & TCL_READABLE)
1.7169 + && (statePtr->inQueueHead != (ChannelBuffer *) NULL)
1.7170 + && (statePtr->inQueueHead->nextRemoved <
1.7171 + statePtr->inQueueHead->nextAdded)) {
1.7172 + /*
1.7173 + * Restart the timer in case a channel handler reenters the
1.7174 + * event loop before UpdateInterest gets called by Tcl_NotifyChannel.
1.7175 + */
1.7176 +
1.7177 + statePtr->timer = Tcl_CreateTimerHandler(0, ChannelTimerProc,
1.7178 + (ClientData) chanPtr);
1.7179 +
1.7180 +#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
1.7181 + /* Set the TIMER flag to notify the higher levels that the
1.7182 + * driver might have no data for us. We do this only if we are
1.7183 + * in non-blocking mode and the driver has no BlockModeProc
1.7184 + * because only then we really don't know if the driver will
1.7185 + * block or not. A similar test is done in "PeekAhead".
1.7186 + */
1.7187 +
1.7188 + if ((statePtr->flags & CHANNEL_NONBLOCKING) &&
1.7189 + (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL)) {
1.7190 + statePtr->flags |= CHANNEL_TIMER_FEV;
1.7191 + }
1.7192 +#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
1.7193 +
1.7194 + Tcl_Preserve((ClientData) statePtr);
1.7195 + Tcl_NotifyChannel((Tcl_Channel)chanPtr, TCL_READABLE);
1.7196 +
1.7197 +#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
1.7198 + statePtr->flags &= ~CHANNEL_TIMER_FEV;
1.7199 +#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
1.7200 +
1.7201 + Tcl_Release((ClientData) statePtr);
1.7202 + } else {
1.7203 + statePtr->timer = NULL;
1.7204 + UpdateInterest(chanPtr);
1.7205 + }
1.7206 +}
1.7207 +
1.7208 +/*
1.7209 + *----------------------------------------------------------------------
1.7210 + *
1.7211 + * Tcl_CreateChannelHandler --
1.7212 + *
1.7213 + * Arrange for a given procedure to be invoked whenever the
1.7214 + * channel indicated by the chanPtr arg becomes readable or
1.7215 + * writable.
1.7216 + *
1.7217 + * Results:
1.7218 + * None.
1.7219 + *
1.7220 + * Side effects:
1.7221 + * From now on, whenever the I/O channel given by chanPtr becomes
1.7222 + * ready in the way indicated by mask, proc will be invoked.
1.7223 + * See the manual entry for details on the calling sequence
1.7224 + * to proc. If there is already an event handler for chan, proc
1.7225 + * and clientData, then the mask will be updated.
1.7226 + *
1.7227 + *----------------------------------------------------------------------
1.7228 + */
1.7229 +
1.7230 +EXPORT_C void
1.7231 +Tcl_CreateChannelHandler(chan, mask, proc, clientData)
1.7232 + Tcl_Channel chan; /* The channel to create the handler for. */
1.7233 + int mask; /* OR'ed combination of TCL_READABLE,
1.7234 + * TCL_WRITABLE, and TCL_EXCEPTION:
1.7235 + * indicates conditions under which
1.7236 + * proc should be called. Use 0 to
1.7237 + * disable a registered handler. */
1.7238 + Tcl_ChannelProc *proc; /* Procedure to call for each
1.7239 + * selected event. */
1.7240 + ClientData clientData; /* Arbitrary data to pass to proc. */
1.7241 +{
1.7242 + ChannelHandler *chPtr;
1.7243 + Channel *chanPtr = (Channel *) chan;
1.7244 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.7245 +
1.7246 + /*
1.7247 + * Check whether this channel handler is not already registered. If
1.7248 + * it is not, create a new record, else reuse existing record (smash
1.7249 + * current values).
1.7250 + */
1.7251 +
1.7252 + for (chPtr = statePtr->chPtr;
1.7253 + chPtr != (ChannelHandler *) NULL;
1.7254 + chPtr = chPtr->nextPtr) {
1.7255 + if ((chPtr->chanPtr == chanPtr) && (chPtr->proc == proc) &&
1.7256 + (chPtr->clientData == clientData)) {
1.7257 + break;
1.7258 + }
1.7259 + }
1.7260 + if (chPtr == (ChannelHandler *) NULL) {
1.7261 + chPtr = (ChannelHandler *) ckalloc((unsigned) sizeof(ChannelHandler));
1.7262 + chPtr->mask = 0;
1.7263 + chPtr->proc = proc;
1.7264 + chPtr->clientData = clientData;
1.7265 + chPtr->chanPtr = chanPtr;
1.7266 + chPtr->nextPtr = statePtr->chPtr;
1.7267 + statePtr->chPtr = chPtr;
1.7268 + }
1.7269 +
1.7270 + /*
1.7271 + * The remainder of the initialization below is done regardless of
1.7272 + * whether or not this is a new record or a modification of an old
1.7273 + * one.
1.7274 + */
1.7275 +
1.7276 + chPtr->mask = mask;
1.7277 +
1.7278 + /*
1.7279 + * Recompute the interest mask for the channel - this call may actually
1.7280 + * be disabling an existing handler.
1.7281 + */
1.7282 +
1.7283 + statePtr->interestMask = 0;
1.7284 + for (chPtr = statePtr->chPtr;
1.7285 + chPtr != (ChannelHandler *) NULL;
1.7286 + chPtr = chPtr->nextPtr) {
1.7287 + statePtr->interestMask |= chPtr->mask;
1.7288 + }
1.7289 +
1.7290 + UpdateInterest(statePtr->topChanPtr);
1.7291 +}
1.7292 +
1.7293 +/*
1.7294 + *----------------------------------------------------------------------
1.7295 + *
1.7296 + * Tcl_DeleteChannelHandler --
1.7297 + *
1.7298 + * Cancel a previously arranged callback arrangement for an IO
1.7299 + * channel.
1.7300 + *
1.7301 + * Results:
1.7302 + * None.
1.7303 + *
1.7304 + * Side effects:
1.7305 + * If a callback was previously registered for this chan, proc and
1.7306 + * clientData , it is removed and the callback will no longer be called
1.7307 + * when the channel becomes ready for IO.
1.7308 + *
1.7309 + *----------------------------------------------------------------------
1.7310 + */
1.7311 +
1.7312 +EXPORT_C void
1.7313 +Tcl_DeleteChannelHandler(chan, proc, clientData)
1.7314 + Tcl_Channel chan; /* The channel for which to remove the
1.7315 + * callback. */
1.7316 + Tcl_ChannelProc *proc; /* The procedure in the callback to delete. */
1.7317 + ClientData clientData; /* The client data in the callback
1.7318 + * to delete. */
1.7319 +
1.7320 +{
1.7321 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.7322 + ChannelHandler *chPtr, *prevChPtr;
1.7323 + Channel *chanPtr = (Channel *) chan;
1.7324 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.7325 + NextChannelHandler *nhPtr;
1.7326 +
1.7327 + /*
1.7328 + * Find the entry and the previous one in the list.
1.7329 + */
1.7330 +
1.7331 + for (prevChPtr = (ChannelHandler *) NULL, chPtr = statePtr->chPtr;
1.7332 + chPtr != (ChannelHandler *) NULL;
1.7333 + chPtr = chPtr->nextPtr) {
1.7334 + if ((chPtr->chanPtr == chanPtr) && (chPtr->clientData == clientData)
1.7335 + && (chPtr->proc == proc)) {
1.7336 + break;
1.7337 + }
1.7338 + prevChPtr = chPtr;
1.7339 + }
1.7340 +
1.7341 + /*
1.7342 + * If not found, return without doing anything.
1.7343 + */
1.7344 +
1.7345 + if (chPtr == (ChannelHandler *) NULL) {
1.7346 + return;
1.7347 + }
1.7348 +
1.7349 + /*
1.7350 + * If ChannelHandlerEventProc is about to process this handler, tell it to
1.7351 + * process the next one instead - we are going to delete *this* one.
1.7352 + */
1.7353 +
1.7354 + for (nhPtr = tsdPtr->nestedHandlerPtr;
1.7355 + nhPtr != (NextChannelHandler *) NULL;
1.7356 + nhPtr = nhPtr->nestedHandlerPtr) {
1.7357 + if (nhPtr->nextHandlerPtr == chPtr) {
1.7358 + nhPtr->nextHandlerPtr = chPtr->nextPtr;
1.7359 + }
1.7360 + }
1.7361 +
1.7362 + /*
1.7363 + * Splice it out of the list of channel handlers.
1.7364 + */
1.7365 +
1.7366 + if (prevChPtr == (ChannelHandler *) NULL) {
1.7367 + statePtr->chPtr = chPtr->nextPtr;
1.7368 + } else {
1.7369 + prevChPtr->nextPtr = chPtr->nextPtr;
1.7370 + }
1.7371 + ckfree((char *) chPtr);
1.7372 +
1.7373 + /*
1.7374 + * Recompute the interest list for the channel, so that infinite loops
1.7375 + * will not result if Tcl_DeleteChannelHandler is called inside an
1.7376 + * event.
1.7377 + */
1.7378 +
1.7379 + statePtr->interestMask = 0;
1.7380 + for (chPtr = statePtr->chPtr;
1.7381 + chPtr != (ChannelHandler *) NULL;
1.7382 + chPtr = chPtr->nextPtr) {
1.7383 + statePtr->interestMask |= chPtr->mask;
1.7384 + }
1.7385 +
1.7386 + UpdateInterest(statePtr->topChanPtr);
1.7387 +}
1.7388 +
1.7389 +/*
1.7390 + *----------------------------------------------------------------------
1.7391 + *
1.7392 + * DeleteScriptRecord --
1.7393 + *
1.7394 + * Delete a script record for this combination of channel, interp
1.7395 + * and mask.
1.7396 + *
1.7397 + * Results:
1.7398 + * None.
1.7399 + *
1.7400 + * Side effects:
1.7401 + * Deletes a script record and cancels a channel event handler.
1.7402 + *
1.7403 + *----------------------------------------------------------------------
1.7404 + */
1.7405 +
1.7406 +static void
1.7407 +DeleteScriptRecord(interp, chanPtr, mask)
1.7408 + Tcl_Interp *interp; /* Interpreter in which script was to be
1.7409 + * executed. */
1.7410 + Channel *chanPtr; /* The channel for which to delete the
1.7411 + * script record (if any). */
1.7412 + int mask; /* Events in mask must exactly match mask
1.7413 + * of script to delete. */
1.7414 +{
1.7415 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.7416 + EventScriptRecord *esPtr, *prevEsPtr;
1.7417 +
1.7418 + for (esPtr = statePtr->scriptRecordPtr,
1.7419 + prevEsPtr = (EventScriptRecord *) NULL;
1.7420 + esPtr != (EventScriptRecord *) NULL;
1.7421 + prevEsPtr = esPtr, esPtr = esPtr->nextPtr) {
1.7422 + if ((esPtr->interp == interp) && (esPtr->mask == mask)) {
1.7423 + if (esPtr == statePtr->scriptRecordPtr) {
1.7424 + statePtr->scriptRecordPtr = esPtr->nextPtr;
1.7425 + } else {
1.7426 + prevEsPtr->nextPtr = esPtr->nextPtr;
1.7427 + }
1.7428 +
1.7429 + Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
1.7430 + TclChannelEventScriptInvoker, (ClientData) esPtr);
1.7431 +
1.7432 + Tcl_DecrRefCount(esPtr->scriptPtr);
1.7433 + ckfree((char *) esPtr);
1.7434 +
1.7435 + break;
1.7436 + }
1.7437 + }
1.7438 +}
1.7439 +
1.7440 +/*
1.7441 + *----------------------------------------------------------------------
1.7442 + *
1.7443 + * CreateScriptRecord --
1.7444 + *
1.7445 + * Creates a record to store a script to be executed when a specific
1.7446 + * event fires on a specific channel.
1.7447 + *
1.7448 + * Results:
1.7449 + * None.
1.7450 + *
1.7451 + * Side effects:
1.7452 + * Causes the script to be stored for later execution.
1.7453 + *
1.7454 + *----------------------------------------------------------------------
1.7455 + */
1.7456 +
1.7457 +static void
1.7458 +CreateScriptRecord(interp, chanPtr, mask, scriptPtr)
1.7459 + Tcl_Interp *interp; /* Interpreter in which to execute
1.7460 + * the stored script. */
1.7461 + Channel *chanPtr; /* Channel for which script is to
1.7462 + * be stored. */
1.7463 + int mask; /* Set of events for which script
1.7464 + * will be invoked. */
1.7465 + Tcl_Obj *scriptPtr; /* Pointer to script object. */
1.7466 +{
1.7467 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.7468 + EventScriptRecord *esPtr;
1.7469 +
1.7470 + for (esPtr = statePtr->scriptRecordPtr;
1.7471 + esPtr != (EventScriptRecord *) NULL;
1.7472 + esPtr = esPtr->nextPtr) {
1.7473 + if ((esPtr->interp == interp) && (esPtr->mask == mask)) {
1.7474 + Tcl_DecrRefCount(esPtr->scriptPtr);
1.7475 + esPtr->scriptPtr = (Tcl_Obj *) NULL;
1.7476 + break;
1.7477 + }
1.7478 + }
1.7479 + if (esPtr == (EventScriptRecord *) NULL) {
1.7480 + esPtr = (EventScriptRecord *) ckalloc((unsigned)
1.7481 + sizeof(EventScriptRecord));
1.7482 + Tcl_CreateChannelHandler((Tcl_Channel) chanPtr, mask,
1.7483 + TclChannelEventScriptInvoker, (ClientData) esPtr);
1.7484 + esPtr->nextPtr = statePtr->scriptRecordPtr;
1.7485 + statePtr->scriptRecordPtr = esPtr;
1.7486 + }
1.7487 + esPtr->chanPtr = chanPtr;
1.7488 + esPtr->interp = interp;
1.7489 + esPtr->mask = mask;
1.7490 + Tcl_IncrRefCount(scriptPtr);
1.7491 + esPtr->scriptPtr = scriptPtr;
1.7492 +}
1.7493 +
1.7494 +/*
1.7495 + *----------------------------------------------------------------------
1.7496 + *
1.7497 + * TclChannelEventScriptInvoker --
1.7498 + *
1.7499 + * Invokes a script scheduled by "fileevent" for when the channel
1.7500 + * becomes ready for IO. This function is invoked by the channel
1.7501 + * handler which was created by the Tcl "fileevent" command.
1.7502 + *
1.7503 + * Results:
1.7504 + * None.
1.7505 + *
1.7506 + * Side effects:
1.7507 + * Whatever the script does.
1.7508 + *
1.7509 + *----------------------------------------------------------------------
1.7510 + */
1.7511 +
1.7512 +void
1.7513 +TclChannelEventScriptInvoker(clientData, mask)
1.7514 + ClientData clientData; /* The script+interp record. */
1.7515 + int mask; /* Not used. */
1.7516 +{
1.7517 + Tcl_Interp *interp; /* Interpreter in which to eval the script. */
1.7518 + Channel *chanPtr; /* The channel for which this handler is
1.7519 + * registered. */
1.7520 + EventScriptRecord *esPtr; /* The event script + interpreter to eval it
1.7521 + * in. */
1.7522 + int result; /* Result of call to eval script. */
1.7523 +
1.7524 + esPtr = (EventScriptRecord *) clientData;
1.7525 + chanPtr = esPtr->chanPtr;
1.7526 + mask = esPtr->mask;
1.7527 + interp = esPtr->interp;
1.7528 +
1.7529 + /*
1.7530 + * We must preserve the interpreter so we can report errors on it
1.7531 + * later. Note that we do not need to preserve the channel because
1.7532 + * that is done by Tcl_NotifyChannel before calling channel handlers.
1.7533 + */
1.7534 +
1.7535 + Tcl_Preserve((ClientData) interp);
1.7536 + result = Tcl_EvalObjEx(interp, esPtr->scriptPtr, TCL_EVAL_GLOBAL);
1.7537 +
1.7538 + /*
1.7539 + * On error, cause a background error and remove the channel handler
1.7540 + * and the script record.
1.7541 + *
1.7542 + * NOTE: Must delete channel handler before causing the background error
1.7543 + * because the background error may want to reinstall the handler.
1.7544 + */
1.7545 +
1.7546 + if (result != TCL_OK) {
1.7547 + if (chanPtr->typePtr != NULL) {
1.7548 + DeleteScriptRecord(interp, chanPtr, mask);
1.7549 + }
1.7550 + Tcl_BackgroundError(interp);
1.7551 + }
1.7552 + Tcl_Release((ClientData) interp);
1.7553 +}
1.7554 +
1.7555 +/*
1.7556 + *----------------------------------------------------------------------
1.7557 + *
1.7558 + * Tcl_FileEventObjCmd --
1.7559 + *
1.7560 + * This procedure implements the "fileevent" Tcl command. See the
1.7561 + * user documentation for details on what it does. This command is
1.7562 + * based on the Tk command "fileevent" which in turn is based on work
1.7563 + * contributed by Mark Diekhans.
1.7564 + *
1.7565 + * Results:
1.7566 + * A standard Tcl result.
1.7567 + *
1.7568 + * Side effects:
1.7569 + * May create a channel handler for the specified channel.
1.7570 + *
1.7571 + *----------------------------------------------------------------------
1.7572 + */
1.7573 +
1.7574 + /* ARGSUSED */
1.7575 +int
1.7576 +Tcl_FileEventObjCmd(clientData, interp, objc, objv)
1.7577 + ClientData clientData; /* Not used. */
1.7578 + Tcl_Interp *interp; /* Interpreter in which the channel
1.7579 + * for which to create the handler
1.7580 + * is found. */
1.7581 + int objc; /* Number of arguments. */
1.7582 + Tcl_Obj *CONST objv[]; /* Argument objects. */
1.7583 +{
1.7584 + Channel *chanPtr; /* The channel to create
1.7585 + * the handler for. */
1.7586 + ChannelState *statePtr; /* state info for channel */
1.7587 + Tcl_Channel chan; /* The opaque type for the channel. */
1.7588 + char *chanName;
1.7589 + int modeIndex; /* Index of mode argument. */
1.7590 + int mask;
1.7591 + static CONST char *modeOptions[] = {"readable", "writable", NULL};
1.7592 + static int maskArray[] = {TCL_READABLE, TCL_WRITABLE};
1.7593 +
1.7594 + if ((objc != 3) && (objc != 4)) {
1.7595 + Tcl_WrongNumArgs(interp, 1, objv, "channelId event ?script?");
1.7596 + return TCL_ERROR;
1.7597 + }
1.7598 + if (Tcl_GetIndexFromObj(interp, objv[2], modeOptions, "event name", 0,
1.7599 + &modeIndex) != TCL_OK) {
1.7600 + return TCL_ERROR;
1.7601 + }
1.7602 + mask = maskArray[modeIndex];
1.7603 +
1.7604 + chanName = Tcl_GetString(objv[1]);
1.7605 + chan = Tcl_GetChannel(interp, chanName, NULL);
1.7606 + if (chan == (Tcl_Channel) NULL) {
1.7607 + return TCL_ERROR;
1.7608 + }
1.7609 + chanPtr = (Channel *) chan;
1.7610 + statePtr = chanPtr->state;
1.7611 + if ((statePtr->flags & mask) == 0) {
1.7612 + Tcl_AppendResult(interp, "channel is not ",
1.7613 + (mask == TCL_READABLE) ? "readable" : "writable",
1.7614 + (char *) NULL);
1.7615 + return TCL_ERROR;
1.7616 + }
1.7617 +
1.7618 + /*
1.7619 + * If we are supposed to return the script, do so.
1.7620 + */
1.7621 +
1.7622 + if (objc == 3) {
1.7623 + EventScriptRecord *esPtr;
1.7624 + for (esPtr = statePtr->scriptRecordPtr;
1.7625 + esPtr != (EventScriptRecord *) NULL;
1.7626 + esPtr = esPtr->nextPtr) {
1.7627 + if ((esPtr->interp == interp) && (esPtr->mask == mask)) {
1.7628 + Tcl_SetObjResult(interp, esPtr->scriptPtr);
1.7629 + break;
1.7630 + }
1.7631 + }
1.7632 + return TCL_OK;
1.7633 + }
1.7634 +
1.7635 + /*
1.7636 + * If we are supposed to delete a stored script, do so.
1.7637 + */
1.7638 +
1.7639 + if (*(Tcl_GetString(objv[3])) == '\0') {
1.7640 + DeleteScriptRecord(interp, chanPtr, mask);
1.7641 + return TCL_OK;
1.7642 + }
1.7643 +
1.7644 + /*
1.7645 + * Make the script record that will link between the event and the
1.7646 + * script to invoke. This also creates a channel event handler which
1.7647 + * will evaluate the script in the supplied interpreter.
1.7648 + */
1.7649 +
1.7650 + CreateScriptRecord(interp, chanPtr, mask, objv[3]);
1.7651 +
1.7652 + return TCL_OK;
1.7653 +}
1.7654 +
1.7655 +/*
1.7656 + *----------------------------------------------------------------------
1.7657 + *
1.7658 + * TclCopyChannel --
1.7659 + *
1.7660 + * This routine copies data from one channel to another, either
1.7661 + * synchronously or asynchronously. If a command script is
1.7662 + * supplied, the operation runs in the background. The script
1.7663 + * is invoked when the copy completes. Otherwise the function
1.7664 + * waits until the copy is completed before returning.
1.7665 + *
1.7666 + * Results:
1.7667 + * A standard Tcl result.
1.7668 + *
1.7669 + * Side effects:
1.7670 + * May schedule a background copy operation that causes both
1.7671 + * channels to be marked busy.
1.7672 + *
1.7673 + *----------------------------------------------------------------------
1.7674 + */
1.7675 +
1.7676 +int
1.7677 +TclCopyChannel(interp, inChan, outChan, toRead, cmdPtr)
1.7678 + Tcl_Interp *interp; /* Current interpreter. */
1.7679 + Tcl_Channel inChan; /* Channel to read from. */
1.7680 + Tcl_Channel outChan; /* Channel to write to. */
1.7681 + int toRead; /* Amount of data to copy, or -1 for all. */
1.7682 + Tcl_Obj *cmdPtr; /* Pointer to script to execute or NULL. */
1.7683 +{
1.7684 + Channel *inPtr = (Channel *) inChan;
1.7685 + Channel *outPtr = (Channel *) outChan;
1.7686 + ChannelState *inStatePtr, *outStatePtr;
1.7687 + int readFlags, writeFlags;
1.7688 + CopyState *csPtr;
1.7689 + int nonBlocking = (cmdPtr) ? CHANNEL_NONBLOCKING : 0;
1.7690 +
1.7691 + inStatePtr = inPtr->state;
1.7692 + outStatePtr = outPtr->state;
1.7693 +
1.7694 + if (inStatePtr->csPtr) {
1.7695 + if (interp) {
1.7696 + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "channel \"",
1.7697 + Tcl_GetChannelName(inChan), "\" is busy", NULL);
1.7698 + }
1.7699 + return TCL_ERROR;
1.7700 + }
1.7701 + if (outStatePtr->csPtr) {
1.7702 + if (interp) {
1.7703 + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "channel \"",
1.7704 + Tcl_GetChannelName(outChan), "\" is busy", NULL);
1.7705 + }
1.7706 + return TCL_ERROR;
1.7707 + }
1.7708 +
1.7709 + readFlags = inStatePtr->flags;
1.7710 + writeFlags = outStatePtr->flags;
1.7711 +
1.7712 + /*
1.7713 + * Set up the blocking mode appropriately. Background copies need
1.7714 + * non-blocking channels. Foreground copies need blocking channels.
1.7715 + * If there is an error, restore the old blocking mode.
1.7716 + */
1.7717 +
1.7718 + if (nonBlocking != (readFlags & CHANNEL_NONBLOCKING)) {
1.7719 + if (SetBlockMode(interp, inPtr,
1.7720 + nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING)
1.7721 + != TCL_OK) {
1.7722 + return TCL_ERROR;
1.7723 + }
1.7724 + }
1.7725 + if (inPtr != outPtr) {
1.7726 + if (nonBlocking != (writeFlags & CHANNEL_NONBLOCKING)) {
1.7727 + if (SetBlockMode(NULL, outPtr,
1.7728 + nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING)
1.7729 + != TCL_OK) {
1.7730 + if (nonBlocking != (readFlags & CHANNEL_NONBLOCKING)) {
1.7731 + SetBlockMode(NULL, inPtr,
1.7732 + (readFlags & CHANNEL_NONBLOCKING)
1.7733 + ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING);
1.7734 + return TCL_ERROR;
1.7735 + }
1.7736 + }
1.7737 + }
1.7738 + }
1.7739 +
1.7740 + /*
1.7741 + * Make sure the output side is unbuffered.
1.7742 + */
1.7743 +
1.7744 + outStatePtr->flags = (outStatePtr->flags & ~(CHANNEL_LINEBUFFERED))
1.7745 + | CHANNEL_UNBUFFERED;
1.7746 +
1.7747 + /*
1.7748 + * Allocate a new CopyState to maintain info about the current copy in
1.7749 + * progress. This structure will be deallocated when the copy is
1.7750 + * completed.
1.7751 + */
1.7752 +
1.7753 + csPtr = (CopyState*) ckalloc(sizeof(CopyState) + inStatePtr->bufSize);
1.7754 + csPtr->bufSize = inStatePtr->bufSize;
1.7755 + csPtr->readPtr = inPtr;
1.7756 + csPtr->writePtr = outPtr;
1.7757 + csPtr->readFlags = readFlags;
1.7758 + csPtr->writeFlags = writeFlags;
1.7759 + csPtr->toRead = toRead;
1.7760 + csPtr->total = 0;
1.7761 + csPtr->interp = interp;
1.7762 + if (cmdPtr) {
1.7763 + Tcl_IncrRefCount(cmdPtr);
1.7764 + }
1.7765 + csPtr->cmdPtr = cmdPtr;
1.7766 + inStatePtr->csPtr = csPtr;
1.7767 + outStatePtr->csPtr = csPtr;
1.7768 +
1.7769 + /*
1.7770 + * Start copying data between the channels.
1.7771 + */
1.7772 +
1.7773 + return CopyData(csPtr, 0);
1.7774 +}
1.7775 +
1.7776 +/*
1.7777 + *----------------------------------------------------------------------
1.7778 + *
1.7779 + * CopyData --
1.7780 + *
1.7781 + * This function implements the lowest level of the copying
1.7782 + * mechanism for TclCopyChannel.
1.7783 + *
1.7784 + * Results:
1.7785 + * Returns TCL_OK on success, else TCL_ERROR.
1.7786 + *
1.7787 + * Side effects:
1.7788 + * Moves data between channels, may create channel handlers.
1.7789 + *
1.7790 + *----------------------------------------------------------------------
1.7791 + */
1.7792 +
1.7793 +static int
1.7794 +CopyData(csPtr, mask)
1.7795 + CopyState *csPtr; /* State of copy operation. */
1.7796 + int mask; /* Current channel event flags. */
1.7797 +{
1.7798 + Tcl_Interp *interp;
1.7799 + Tcl_Obj *cmdPtr, *errObj = NULL, *bufObj = NULL;
1.7800 + Tcl_Channel inChan, outChan;
1.7801 + ChannelState *inStatePtr, *outStatePtr;
1.7802 + int result = TCL_OK, size, total, sizeb;
1.7803 + char* buffer;
1.7804 +
1.7805 + int inBinary, outBinary, sameEncoding; /* Encoding control */
1.7806 + int underflow; /* input underflow */
1.7807 +
1.7808 + inChan = (Tcl_Channel) csPtr->readPtr;
1.7809 + outChan = (Tcl_Channel) csPtr->writePtr;
1.7810 + inStatePtr = csPtr->readPtr->state;
1.7811 + outStatePtr = csPtr->writePtr->state;
1.7812 + interp = csPtr->interp;
1.7813 + cmdPtr = csPtr->cmdPtr;
1.7814 +
1.7815 + /*
1.7816 + * Copy the data the slow way, using the translation mechanism.
1.7817 + *
1.7818 + * Note: We have make sure that we use the topmost channel in a stack
1.7819 + * for the copying. The caller uses Tcl_GetChannel to access it, and
1.7820 + * thus gets the bottom of the stack.
1.7821 + */
1.7822 +
1.7823 + inBinary = (inStatePtr->encoding == NULL);
1.7824 + outBinary = (outStatePtr->encoding == NULL);
1.7825 + sameEncoding = (inStatePtr->encoding == outStatePtr->encoding);
1.7826 +
1.7827 + if (!(inBinary || sameEncoding)) {
1.7828 + bufObj = Tcl_NewObj ();
1.7829 + Tcl_IncrRefCount (bufObj);
1.7830 + }
1.7831 +
1.7832 + while (csPtr->toRead != 0) {
1.7833 + /*
1.7834 + * Check for unreported background errors.
1.7835 + */
1.7836 +
1.7837 + if (inStatePtr->unreportedError != 0) {
1.7838 + Tcl_SetErrno(inStatePtr->unreportedError);
1.7839 + inStatePtr->unreportedError = 0;
1.7840 + goto readError;
1.7841 + }
1.7842 + if (outStatePtr->unreportedError != 0) {
1.7843 + Tcl_SetErrno(outStatePtr->unreportedError);
1.7844 + outStatePtr->unreportedError = 0;
1.7845 + goto writeError;
1.7846 + }
1.7847 +
1.7848 + /*
1.7849 + * Read up to bufSize bytes.
1.7850 + */
1.7851 +
1.7852 + if ((csPtr->toRead == -1) || (csPtr->toRead > csPtr->bufSize)) {
1.7853 + sizeb = csPtr->bufSize;
1.7854 + } else {
1.7855 + sizeb = csPtr->toRead;
1.7856 + }
1.7857 +
1.7858 + if (inBinary || sameEncoding) {
1.7859 + size = DoRead(inStatePtr->topChanPtr, csPtr->buffer, sizeb);
1.7860 + } else {
1.7861 + size = DoReadChars(inStatePtr->topChanPtr, bufObj, sizeb, 0 /* No append */);
1.7862 + }
1.7863 + underflow = (size >= 0) && (size < sizeb); /* input underflow */
1.7864 +
1.7865 + if (size < 0) {
1.7866 + readError:
1.7867 + errObj = Tcl_NewObj();
1.7868 + Tcl_AppendStringsToObj(errObj, "error reading \"",
1.7869 + Tcl_GetChannelName(inChan), "\": ",
1.7870 + Tcl_PosixError(interp), (char *) NULL);
1.7871 + break;
1.7872 + } else if (underflow) {
1.7873 + /*
1.7874 + * We had an underflow on the read side. If we are at EOF,
1.7875 + * then the copying is done, otherwise set up a channel
1.7876 + * handler to detect when the channel becomes readable again.
1.7877 + */
1.7878 +
1.7879 + if ((size == 0) && Tcl_Eof(inChan)) {
1.7880 + break;
1.7881 + }
1.7882 + if (! Tcl_Eof(inChan) && !(mask & TCL_READABLE)) {
1.7883 + if (mask & TCL_WRITABLE) {
1.7884 + Tcl_DeleteChannelHandler(outChan, CopyEventProc,
1.7885 + (ClientData) csPtr);
1.7886 + }
1.7887 + Tcl_CreateChannelHandler(inChan, TCL_READABLE,
1.7888 + CopyEventProc, (ClientData) csPtr);
1.7889 + }
1.7890 + if (size == 0) {
1.7891 + if (bufObj != (Tcl_Obj*) NULL) {
1.7892 + Tcl_DecrRefCount (bufObj);
1.7893 + bufObj = (Tcl_Obj*) NULL;
1.7894 + }
1.7895 + return TCL_OK;
1.7896 + }
1.7897 + }
1.7898 +
1.7899 + /*
1.7900 + * Now write the buffer out.
1.7901 + */
1.7902 +
1.7903 + if (inBinary || sameEncoding) {
1.7904 + buffer = csPtr->buffer;
1.7905 + sizeb = size;
1.7906 + } else {
1.7907 + buffer = Tcl_GetStringFromObj (bufObj, &sizeb);
1.7908 + }
1.7909 +
1.7910 + if (outBinary || sameEncoding) {
1.7911 + sizeb = DoWrite(outStatePtr->topChanPtr, buffer, sizeb);
1.7912 + } else {
1.7913 + sizeb = DoWriteChars(outStatePtr->topChanPtr, buffer, sizeb);
1.7914 + }
1.7915 +
1.7916 + if (inBinary || sameEncoding) {
1.7917 + /* Both read and write counted bytes */
1.7918 + size = sizeb;
1.7919 + } /* else : Read counted characters, write counted bytes, i.e. size != sizeb */
1.7920 +
1.7921 + if (sizeb < 0) {
1.7922 + writeError:
1.7923 + errObj = Tcl_NewObj();
1.7924 + Tcl_AppendStringsToObj(errObj, "error writing \"",
1.7925 + Tcl_GetChannelName(outChan), "\": ",
1.7926 + Tcl_PosixError(interp), (char *) NULL);
1.7927 + break;
1.7928 + }
1.7929 +
1.7930 + /*
1.7931 + * Update the current byte count. Do it now so the count is
1.7932 + * valid before a return or break takes us out of the loop.
1.7933 + * The invariant at the top of the loop should be that
1.7934 + * csPtr->toRead holds the number of bytes left to copy.
1.7935 + */
1.7936 +
1.7937 + if (csPtr->toRead != -1) {
1.7938 + csPtr->toRead -= size;
1.7939 + }
1.7940 + csPtr->total += size;
1.7941 +
1.7942 + /*
1.7943 + * Break loop if EOF && (size>0)
1.7944 + */
1.7945 +
1.7946 + if (Tcl_Eof(inChan)) {
1.7947 + break;
1.7948 + }
1.7949 +
1.7950 + /*
1.7951 + * Check to see if the write is happening in the background. If so,
1.7952 + * stop copying and wait for the channel to become writable again.
1.7953 + * After input underflow we already installed a readable handler
1.7954 + * therefore we don't need a writable handler.
1.7955 + */
1.7956 +
1.7957 + if ( ! underflow && (outStatePtr->flags & BG_FLUSH_SCHEDULED) ) {
1.7958 + if (!(mask & TCL_WRITABLE)) {
1.7959 + if (mask & TCL_READABLE) {
1.7960 + Tcl_DeleteChannelHandler(inChan, CopyEventProc,
1.7961 + (ClientData) csPtr);
1.7962 + }
1.7963 + Tcl_CreateChannelHandler(outChan, TCL_WRITABLE,
1.7964 + CopyEventProc, (ClientData) csPtr);
1.7965 + }
1.7966 + if (bufObj != (Tcl_Obj*) NULL) {
1.7967 + Tcl_DecrRefCount (bufObj);
1.7968 + bufObj = (Tcl_Obj*) NULL;
1.7969 + }
1.7970 + return TCL_OK;
1.7971 + }
1.7972 +
1.7973 + /*
1.7974 + * For background copies, we only do one buffer per invocation so
1.7975 + * we don't starve the rest of the system.
1.7976 + */
1.7977 +
1.7978 + if (cmdPtr) {
1.7979 + /*
1.7980 + * The first time we enter this code, there won't be a
1.7981 + * channel handler established yet, so do it here.
1.7982 + */
1.7983 +
1.7984 + if (mask == 0) {
1.7985 + Tcl_CreateChannelHandler(outChan, TCL_WRITABLE,
1.7986 + CopyEventProc, (ClientData) csPtr);
1.7987 + }
1.7988 + if (bufObj != (Tcl_Obj*) NULL) {
1.7989 + Tcl_DecrRefCount (bufObj);
1.7990 + bufObj = (Tcl_Obj*) NULL;
1.7991 + }
1.7992 + return TCL_OK;
1.7993 + }
1.7994 + } /* while */
1.7995 +
1.7996 + if (bufObj != (Tcl_Obj*) NULL) {
1.7997 + Tcl_DecrRefCount (bufObj);
1.7998 + bufObj = (Tcl_Obj*) NULL;
1.7999 + }
1.8000 +
1.8001 + /*
1.8002 + * Make the callback or return the number of bytes transferred.
1.8003 + * The local total is used because StopCopy frees csPtr.
1.8004 + */
1.8005 +
1.8006 + total = csPtr->total;
1.8007 + if (cmdPtr && interp) {
1.8008 + /*
1.8009 + * Get a private copy of the command so we can mutate it
1.8010 + * by adding arguments. Note that StopCopy frees our saved
1.8011 + * reference to the original command obj.
1.8012 + */
1.8013 +
1.8014 + cmdPtr = Tcl_DuplicateObj(cmdPtr);
1.8015 + Tcl_IncrRefCount(cmdPtr);
1.8016 + StopCopy(csPtr);
1.8017 + Tcl_Preserve((ClientData) interp);
1.8018 +
1.8019 + Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewIntObj(total));
1.8020 + if (errObj) {
1.8021 + Tcl_ListObjAppendElement(interp, cmdPtr, errObj);
1.8022 + }
1.8023 + if (Tcl_EvalObjEx(interp, cmdPtr, TCL_EVAL_GLOBAL) != TCL_OK) {
1.8024 + Tcl_BackgroundError(interp);
1.8025 + result = TCL_ERROR;
1.8026 + }
1.8027 + Tcl_DecrRefCount(cmdPtr);
1.8028 + Tcl_Release((ClientData) interp);
1.8029 + } else {
1.8030 + StopCopy(csPtr);
1.8031 + if (interp) {
1.8032 + if (errObj) {
1.8033 + Tcl_SetObjResult(interp, errObj);
1.8034 + result = TCL_ERROR;
1.8035 + } else {
1.8036 + Tcl_ResetResult(interp);
1.8037 + Tcl_SetIntObj(Tcl_GetObjResult(interp), total);
1.8038 + }
1.8039 + }
1.8040 + }
1.8041 + return result;
1.8042 +}
1.8043 +
1.8044 +/*
1.8045 + *----------------------------------------------------------------------
1.8046 + *
1.8047 + * DoRead --
1.8048 + *
1.8049 + * Reads a given number of bytes from a channel.
1.8050 + *
1.8051 + * No encoding conversions are applied to the bytes being read.
1.8052 + *
1.8053 + * Results:
1.8054 + * The number of characters read, or -1 on error. Use Tcl_GetErrno()
1.8055 + * to retrieve the error code for the error that occurred.
1.8056 + *
1.8057 + * Side effects:
1.8058 + * May cause input to be buffered.
1.8059 + *
1.8060 + *----------------------------------------------------------------------
1.8061 + */
1.8062 +
1.8063 +static int
1.8064 +DoRead(chanPtr, bufPtr, toRead)
1.8065 + Channel *chanPtr; /* The channel from which to read. */
1.8066 + char *bufPtr; /* Where to store input read. */
1.8067 + int toRead; /* Maximum number of bytes to read. */
1.8068 +{
1.8069 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.8070 + int copied; /* How many characters were copied into
1.8071 + * the result string? */
1.8072 + int copiedNow; /* How many characters were copied from
1.8073 + * the current input buffer? */
1.8074 + int result; /* Of calling GetInput. */
1.8075 +
1.8076 + /*
1.8077 + * If we have not encountered a sticky EOF, clear the EOF bit. Either
1.8078 + * way clear the BLOCKED bit. We want to discover these anew during
1.8079 + * each operation.
1.8080 + */
1.8081 +
1.8082 + if (!(statePtr->flags & CHANNEL_STICKY_EOF)) {
1.8083 + statePtr->flags &= ~CHANNEL_EOF;
1.8084 + }
1.8085 + statePtr->flags &= ~(CHANNEL_BLOCKED | CHANNEL_NEED_MORE_DATA);
1.8086 +
1.8087 + for (copied = 0; copied < toRead; copied += copiedNow) {
1.8088 + copiedNow = CopyAndTranslateBuffer(statePtr, bufPtr + copied,
1.8089 + toRead - copied);
1.8090 + if (copiedNow == 0) {
1.8091 + if (statePtr->flags & CHANNEL_EOF) {
1.8092 + goto done;
1.8093 + }
1.8094 + if (statePtr->flags & CHANNEL_BLOCKED) {
1.8095 + if (statePtr->flags & CHANNEL_NONBLOCKING) {
1.8096 + goto done;
1.8097 + }
1.8098 + statePtr->flags &= (~(CHANNEL_BLOCKED));
1.8099 + }
1.8100 + result = GetInput(chanPtr);
1.8101 + if (result != 0) {
1.8102 + if (result != EAGAIN) {
1.8103 + copied = -1;
1.8104 + }
1.8105 + goto done;
1.8106 + }
1.8107 + }
1.8108 + }
1.8109 +
1.8110 + statePtr->flags &= (~(CHANNEL_BLOCKED));
1.8111 +
1.8112 + done:
1.8113 + /*
1.8114 + * Update the notifier state so we don't block while there is still
1.8115 + * data in the buffers.
1.8116 + */
1.8117 +
1.8118 + UpdateInterest(chanPtr);
1.8119 + return copied;
1.8120 +}
1.8121 +
1.8122 +/*
1.8123 + *----------------------------------------------------------------------
1.8124 + *
1.8125 + * CopyAndTranslateBuffer --
1.8126 + *
1.8127 + * Copy at most one buffer of input to the result space, doing
1.8128 + * eol translations according to mode in effect currently.
1.8129 + *
1.8130 + * Results:
1.8131 + * Number of bytes stored in the result buffer (as opposed to the
1.8132 + * number of bytes read from the channel). May return
1.8133 + * zero if no input is available to be translated.
1.8134 + *
1.8135 + * Side effects:
1.8136 + * Consumes buffered input. May deallocate one buffer.
1.8137 + *
1.8138 + *----------------------------------------------------------------------
1.8139 + */
1.8140 +
1.8141 +static int
1.8142 +CopyAndTranslateBuffer(statePtr, result, space)
1.8143 + ChannelState *statePtr; /* Channel state from which to read input. */
1.8144 + char *result; /* Where to store the copied input. */
1.8145 + int space; /* How many bytes are available in result
1.8146 + * to store the copied input? */
1.8147 +{
1.8148 + ChannelBuffer *bufPtr; /* The buffer from which to copy bytes. */
1.8149 + int bytesInBuffer; /* How many bytes are available to be
1.8150 + * copied in the current input buffer? */
1.8151 + int copied; /* How many characters were already copied
1.8152 + * into the destination space? */
1.8153 + int i; /* Iterates over the copied input looking
1.8154 + * for the input eofChar. */
1.8155 +
1.8156 + /*
1.8157 + * If there is no input at all, return zero. The invariant is that either
1.8158 + * there is no buffer in the queue, or if the first buffer is empty, it
1.8159 + * is also the last buffer (and thus there is no input in the queue).
1.8160 + * Note also that if the buffer is empty, we leave it in the queue.
1.8161 + */
1.8162 +
1.8163 + if (statePtr->inQueueHead == (ChannelBuffer *) NULL) {
1.8164 + return 0;
1.8165 + }
1.8166 + bufPtr = statePtr->inQueueHead;
1.8167 + bytesInBuffer = bufPtr->nextAdded - bufPtr->nextRemoved;
1.8168 +
1.8169 + copied = 0;
1.8170 + switch (statePtr->inputTranslation) {
1.8171 + case TCL_TRANSLATE_LF: {
1.8172 + if (bytesInBuffer == 0) {
1.8173 + return 0;
1.8174 + }
1.8175 +
1.8176 + /*
1.8177 + * Copy the current chunk into the result buffer.
1.8178 + */
1.8179 +
1.8180 + if (bytesInBuffer < space) {
1.8181 + space = bytesInBuffer;
1.8182 + }
1.8183 + memcpy((VOID *) result,
1.8184 + (VOID *) (bufPtr->buf + bufPtr->nextRemoved),
1.8185 + (size_t) space);
1.8186 + bufPtr->nextRemoved += space;
1.8187 + copied = space;
1.8188 + break;
1.8189 + }
1.8190 + case TCL_TRANSLATE_CR: {
1.8191 + char *end;
1.8192 +
1.8193 + if (bytesInBuffer == 0) {
1.8194 + return 0;
1.8195 + }
1.8196 +
1.8197 + /*
1.8198 + * Copy the current chunk into the result buffer, then
1.8199 + * replace all \r with \n.
1.8200 + */
1.8201 +
1.8202 + if (bytesInBuffer < space) {
1.8203 + space = bytesInBuffer;
1.8204 + }
1.8205 + memcpy((VOID *) result,
1.8206 + (VOID *) (bufPtr->buf + bufPtr->nextRemoved),
1.8207 + (size_t) space);
1.8208 + bufPtr->nextRemoved += space;
1.8209 + copied = space;
1.8210 +
1.8211 + for (end = result + copied; result < end; result++) {
1.8212 + if (*result == '\r') {
1.8213 + *result = '\n';
1.8214 + }
1.8215 + }
1.8216 + break;
1.8217 + }
1.8218 + case TCL_TRANSLATE_CRLF: {
1.8219 + char *src, *end, *dst;
1.8220 + int curByte;
1.8221 +
1.8222 + /*
1.8223 + * If there is a held-back "\r" at EOF, produce it now.
1.8224 + */
1.8225 +
1.8226 + if (bytesInBuffer == 0) {
1.8227 + if ((statePtr->flags & (INPUT_SAW_CR | CHANNEL_EOF)) ==
1.8228 + (INPUT_SAW_CR | CHANNEL_EOF)) {
1.8229 + result[0] = '\r';
1.8230 + statePtr->flags &= ~INPUT_SAW_CR;
1.8231 + return 1;
1.8232 + }
1.8233 + return 0;
1.8234 + }
1.8235 +
1.8236 + /*
1.8237 + * Copy the current chunk and replace "\r\n" with "\n"
1.8238 + * (but not standalone "\r"!).
1.8239 + */
1.8240 +
1.8241 + if (bytesInBuffer < space) {
1.8242 + space = bytesInBuffer;
1.8243 + }
1.8244 + memcpy((VOID *) result,
1.8245 + (VOID *) (bufPtr->buf + bufPtr->nextRemoved),
1.8246 + (size_t) space);
1.8247 + bufPtr->nextRemoved += space;
1.8248 + copied = space;
1.8249 +
1.8250 + end = result + copied;
1.8251 + dst = result;
1.8252 + for (src = result; src < end; src++) {
1.8253 + curByte = *src;
1.8254 + if (curByte == '\n') {
1.8255 + statePtr->flags &= ~INPUT_SAW_CR;
1.8256 + } else if (statePtr->flags & INPUT_SAW_CR) {
1.8257 + statePtr->flags &= ~INPUT_SAW_CR;
1.8258 + *dst = '\r';
1.8259 + dst++;
1.8260 + }
1.8261 + if (curByte == '\r') {
1.8262 + statePtr->flags |= INPUT_SAW_CR;
1.8263 + } else {
1.8264 + *dst = (char) curByte;
1.8265 + dst++;
1.8266 + }
1.8267 + }
1.8268 + copied = dst - result;
1.8269 + break;
1.8270 + }
1.8271 + case TCL_TRANSLATE_AUTO: {
1.8272 + char *src, *end, *dst;
1.8273 + int curByte;
1.8274 +
1.8275 + if (bytesInBuffer == 0) {
1.8276 + return 0;
1.8277 + }
1.8278 +
1.8279 + /*
1.8280 + * Loop over the current buffer, converting "\r" and "\r\n"
1.8281 + * to "\n".
1.8282 + */
1.8283 +
1.8284 + if (bytesInBuffer < space) {
1.8285 + space = bytesInBuffer;
1.8286 + }
1.8287 + memcpy((VOID *) result,
1.8288 + (VOID *) (bufPtr->buf + bufPtr->nextRemoved),
1.8289 + (size_t) space);
1.8290 + bufPtr->nextRemoved += space;
1.8291 + copied = space;
1.8292 +
1.8293 + end = result + copied;
1.8294 + dst = result;
1.8295 + for (src = result; src < end; src++) {
1.8296 + curByte = *src;
1.8297 + if (curByte == '\r') {
1.8298 + statePtr->flags |= INPUT_SAW_CR;
1.8299 + *dst = '\n';
1.8300 + dst++;
1.8301 + } else {
1.8302 + if ((curByte != '\n') ||
1.8303 + !(statePtr->flags & INPUT_SAW_CR)) {
1.8304 + *dst = (char) curByte;
1.8305 + dst++;
1.8306 + }
1.8307 + statePtr->flags &= ~INPUT_SAW_CR;
1.8308 + }
1.8309 + }
1.8310 + copied = dst - result;
1.8311 + break;
1.8312 + }
1.8313 + default: {
1.8314 + panic("unknown eol translation mode");
1.8315 + }
1.8316 + }
1.8317 +
1.8318 + /*
1.8319 + * If an in-stream EOF character is set for this channel, check that
1.8320 + * the input we copied so far does not contain the EOF char. If it does,
1.8321 + * copy only up to and excluding that character.
1.8322 + */
1.8323 +
1.8324 + if (statePtr->inEofChar != 0) {
1.8325 + for (i = 0; i < copied; i++) {
1.8326 + if (result[i] == (char) statePtr->inEofChar) {
1.8327 + /*
1.8328 + * Set sticky EOF so that no further input is presented
1.8329 + * to the caller.
1.8330 + */
1.8331 +
1.8332 + statePtr->flags |= (CHANNEL_EOF | CHANNEL_STICKY_EOF);
1.8333 + statePtr->inputEncodingFlags |= TCL_ENCODING_END;
1.8334 + copied = i;
1.8335 + break;
1.8336 + }
1.8337 + }
1.8338 + }
1.8339 +
1.8340 + /*
1.8341 + * If the current buffer is empty recycle it.
1.8342 + */
1.8343 +
1.8344 + if (bufPtr->nextRemoved == bufPtr->nextAdded) {
1.8345 + statePtr->inQueueHead = bufPtr->nextPtr;
1.8346 + if (statePtr->inQueueHead == (ChannelBuffer *) NULL) {
1.8347 + statePtr->inQueueTail = (ChannelBuffer *) NULL;
1.8348 + }
1.8349 + RecycleBuffer(statePtr, bufPtr, 0);
1.8350 + }
1.8351 +
1.8352 + /*
1.8353 + * Return the number of characters copied into the result buffer.
1.8354 + * This may be different from the number of bytes consumed, because
1.8355 + * of EOL translations.
1.8356 + */
1.8357 +
1.8358 + return copied;
1.8359 +}
1.8360 +
1.8361 +/*
1.8362 + *----------------------------------------------------------------------
1.8363 + *
1.8364 + * CopyBuffer --
1.8365 + *
1.8366 + * Copy at most one buffer of input to the result space.
1.8367 + *
1.8368 + * Results:
1.8369 + * Number of bytes stored in the result buffer. May return
1.8370 + * zero if no input is available.
1.8371 + *
1.8372 + * Side effects:
1.8373 + * Consumes buffered input. May deallocate one buffer.
1.8374 + *
1.8375 + *----------------------------------------------------------------------
1.8376 + */
1.8377 +
1.8378 +static int
1.8379 +CopyBuffer(chanPtr, result, space)
1.8380 + Channel *chanPtr; /* Channel from which to read input. */
1.8381 + char *result; /* Where to store the copied input. */
1.8382 + int space; /* How many bytes are available in result
1.8383 + * to store the copied input? */
1.8384 +{
1.8385 + ChannelBuffer *bufPtr; /* The buffer from which to copy bytes. */
1.8386 + int bytesInBuffer; /* How many bytes are available to be
1.8387 + * copied in the current input buffer? */
1.8388 + int copied; /* How many characters were already copied
1.8389 + * into the destination space? */
1.8390 +
1.8391 + /*
1.8392 + * If there is no input at all, return zero. The invariant is that
1.8393 + * either there is no buffer in the queue, or if the first buffer
1.8394 + * is empty, it is also the last buffer (and thus there is no
1.8395 + * input in the queue). Note also that if the buffer is empty, we
1.8396 + * don't leave it in the queue, but recycle it.
1.8397 + */
1.8398 +
1.8399 + if (chanPtr->inQueueHead == (ChannelBuffer *) NULL) {
1.8400 + return 0;
1.8401 + }
1.8402 + bufPtr = chanPtr->inQueueHead;
1.8403 + bytesInBuffer = bufPtr->nextAdded - bufPtr->nextRemoved;
1.8404 +
1.8405 + copied = 0;
1.8406 +
1.8407 + if (bytesInBuffer == 0) {
1.8408 + RecycleBuffer(chanPtr->state, bufPtr, 0);
1.8409 + chanPtr->inQueueHead = (ChannelBuffer*) NULL;
1.8410 + chanPtr->inQueueTail = (ChannelBuffer*) NULL;
1.8411 + return 0;
1.8412 + }
1.8413 +
1.8414 + /*
1.8415 + * Copy the current chunk into the result buffer.
1.8416 + */
1.8417 +
1.8418 + if (bytesInBuffer < space) {
1.8419 + space = bytesInBuffer;
1.8420 + }
1.8421 +
1.8422 + memcpy((VOID *) result,
1.8423 + (VOID *) (bufPtr->buf + bufPtr->nextRemoved),
1.8424 + (size_t) space);
1.8425 + bufPtr->nextRemoved += space;
1.8426 + copied = space;
1.8427 +
1.8428 + /*
1.8429 + * We don't care about in-stream EOF characters here as the data
1.8430 + * read here may still flow through one or more transformations,
1.8431 + * i.e. is not in its final state yet.
1.8432 + */
1.8433 +
1.8434 + /*
1.8435 + * If the current buffer is empty recycle it.
1.8436 + */
1.8437 +
1.8438 + if (bufPtr->nextRemoved == bufPtr->nextAdded) {
1.8439 + chanPtr->inQueueHead = bufPtr->nextPtr;
1.8440 + if (chanPtr->inQueueHead == (ChannelBuffer *) NULL) {
1.8441 + chanPtr->inQueueTail = (ChannelBuffer *) NULL;
1.8442 + }
1.8443 + RecycleBuffer(chanPtr->state, bufPtr, 0);
1.8444 + }
1.8445 +
1.8446 + /*
1.8447 + * Return the number of characters copied into the result buffer.
1.8448 + */
1.8449 +
1.8450 + return copied;
1.8451 +}
1.8452 +
1.8453 +/*
1.8454 + *----------------------------------------------------------------------
1.8455 + *
1.8456 + * DoWrite --
1.8457 + *
1.8458 + * Puts a sequence of characters into an output buffer, may queue the
1.8459 + * buffer for output if it gets full, and also remembers whether the
1.8460 + * current buffer is ready e.g. if it contains a newline and we are in
1.8461 + * line buffering mode.
1.8462 + *
1.8463 + * Results:
1.8464 + * The number of bytes written or -1 in case of error. If -1,
1.8465 + * Tcl_GetErrno will return the error code.
1.8466 + *
1.8467 + * Side effects:
1.8468 + * May buffer up output and may cause output to be produced on the
1.8469 + * channel.
1.8470 + *
1.8471 + *----------------------------------------------------------------------
1.8472 + */
1.8473 +
1.8474 +static int
1.8475 +DoWrite(chanPtr, src, srcLen)
1.8476 + Channel *chanPtr; /* The channel to buffer output for. */
1.8477 + CONST char *src; /* Data to write. */
1.8478 + int srcLen; /* Number of bytes to write. */
1.8479 +{
1.8480 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.8481 + ChannelBuffer *outBufPtr; /* Current output buffer. */
1.8482 + int foundNewline; /* Did we find a newline in output? */
1.8483 + char *dPtr;
1.8484 + CONST char *sPtr; /* Search variables for newline. */
1.8485 + int crsent; /* In CRLF eol translation mode,
1.8486 + * remember the fact that a CR was
1.8487 + * output to the channel without
1.8488 + * its following NL. */
1.8489 + int i; /* Loop index for newline search. */
1.8490 + int destCopied; /* How many bytes were used in this
1.8491 + * destination buffer to hold the
1.8492 + * output? */
1.8493 + int totalDestCopied; /* How many bytes total were
1.8494 + * copied to the channel buffer? */
1.8495 + int srcCopied; /* How many bytes were copied from
1.8496 + * the source string? */
1.8497 + char *destPtr; /* Where in line to copy to? */
1.8498 +
1.8499 + /*
1.8500 + * If we are in network (or windows) translation mode, record the fact
1.8501 + * that we have not yet sent a CR to the channel.
1.8502 + */
1.8503 +
1.8504 + crsent = 0;
1.8505 +
1.8506 + /*
1.8507 + * Loop filling buffers and flushing them until all output has been
1.8508 + * consumed.
1.8509 + */
1.8510 +
1.8511 + srcCopied = 0;
1.8512 + totalDestCopied = 0;
1.8513 +
1.8514 + while (srcLen > 0) {
1.8515 +
1.8516 + /*
1.8517 + * Make sure there is a current output buffer to accept output.
1.8518 + */
1.8519 +
1.8520 + if (statePtr->curOutPtr == (ChannelBuffer *) NULL) {
1.8521 + statePtr->curOutPtr = AllocChannelBuffer(statePtr->bufSize);
1.8522 + }
1.8523 +
1.8524 + outBufPtr = statePtr->curOutPtr;
1.8525 +
1.8526 + destCopied = outBufPtr->bufLength - outBufPtr->nextAdded;
1.8527 + if (destCopied > srcLen) {
1.8528 + destCopied = srcLen;
1.8529 + }
1.8530 +
1.8531 + destPtr = outBufPtr->buf + outBufPtr->nextAdded;
1.8532 + switch (statePtr->outputTranslation) {
1.8533 + case TCL_TRANSLATE_LF:
1.8534 + srcCopied = destCopied;
1.8535 + memcpy((VOID *) destPtr, (VOID *) src, (size_t) destCopied);
1.8536 + break;
1.8537 + case TCL_TRANSLATE_CR:
1.8538 + srcCopied = destCopied;
1.8539 + memcpy((VOID *) destPtr, (VOID *) src, (size_t) destCopied);
1.8540 + for (dPtr = destPtr; dPtr < destPtr + destCopied; dPtr++) {
1.8541 + if (*dPtr == '\n') {
1.8542 + *dPtr = '\r';
1.8543 + }
1.8544 + }
1.8545 + break;
1.8546 + case TCL_TRANSLATE_CRLF:
1.8547 + for (srcCopied = 0, dPtr = destPtr, sPtr = src;
1.8548 + dPtr < destPtr + destCopied;
1.8549 + dPtr++, sPtr++, srcCopied++) {
1.8550 + if (*sPtr == '\n') {
1.8551 + if (crsent) {
1.8552 + *dPtr = '\n';
1.8553 + crsent = 0;
1.8554 + } else {
1.8555 + *dPtr = '\r';
1.8556 + crsent = 1;
1.8557 + sPtr--, srcCopied--;
1.8558 + }
1.8559 + } else {
1.8560 + *dPtr = *sPtr;
1.8561 + }
1.8562 + }
1.8563 + break;
1.8564 + case TCL_TRANSLATE_AUTO:
1.8565 + panic("Tcl_Write: AUTO output translation mode not supported");
1.8566 + default:
1.8567 + panic("Tcl_Write: unknown output translation mode");
1.8568 + }
1.8569 +
1.8570 + /*
1.8571 + * The current buffer is ready for output if it is full, or if it
1.8572 + * contains a newline and this channel is line-buffered, or if it
1.8573 + * contains any output and this channel is unbuffered.
1.8574 + */
1.8575 +
1.8576 + outBufPtr->nextAdded += destCopied;
1.8577 + if (!(statePtr->flags & BUFFER_READY)) {
1.8578 + if (outBufPtr->nextAdded == outBufPtr->bufLength) {
1.8579 + statePtr->flags |= BUFFER_READY;
1.8580 + } else if (statePtr->flags & CHANNEL_LINEBUFFERED) {
1.8581 + for (sPtr = src, i = 0, foundNewline = 0;
1.8582 + (i < srcCopied) && (!foundNewline);
1.8583 + i++, sPtr++) {
1.8584 + if (*sPtr == '\n') {
1.8585 + foundNewline = 1;
1.8586 + break;
1.8587 + }
1.8588 + }
1.8589 + if (foundNewline) {
1.8590 + statePtr->flags |= BUFFER_READY;
1.8591 + }
1.8592 + } else if (statePtr->flags & CHANNEL_UNBUFFERED) {
1.8593 + statePtr->flags |= BUFFER_READY;
1.8594 + }
1.8595 + }
1.8596 +
1.8597 + totalDestCopied += srcCopied;
1.8598 + src += srcCopied;
1.8599 + srcLen -= srcCopied;
1.8600 +
1.8601 + if (statePtr->flags & BUFFER_READY) {
1.8602 + if (FlushChannel(NULL, chanPtr, 0) != 0) {
1.8603 + return -1;
1.8604 + }
1.8605 + }
1.8606 + } /* Closes "while" */
1.8607 +
1.8608 + return totalDestCopied;
1.8609 +}
1.8610 +
1.8611 +/*
1.8612 + *----------------------------------------------------------------------
1.8613 + *
1.8614 + * CopyEventProc --
1.8615 + *
1.8616 + * This routine is invoked as a channel event handler for
1.8617 + * the background copy operation. It is just a trivial wrapper
1.8618 + * around the CopyData routine.
1.8619 + *
1.8620 + * Results:
1.8621 + * None.
1.8622 + *
1.8623 + * Side effects:
1.8624 + * None.
1.8625 + *
1.8626 + *----------------------------------------------------------------------
1.8627 + */
1.8628 +
1.8629 +static void
1.8630 +CopyEventProc(clientData, mask)
1.8631 + ClientData clientData;
1.8632 + int mask;
1.8633 +{
1.8634 + (void) CopyData((CopyState *)clientData, mask);
1.8635 +}
1.8636 +
1.8637 +/*
1.8638 + *----------------------------------------------------------------------
1.8639 + *
1.8640 + * StopCopy --
1.8641 + *
1.8642 + * This routine halts a copy that is in progress.
1.8643 + *
1.8644 + * Results:
1.8645 + * None.
1.8646 + *
1.8647 + * Side effects:
1.8648 + * Removes any pending channel handlers and restores the blocking
1.8649 + * and buffering modes of the channels. The CopyState is freed.
1.8650 + *
1.8651 + *----------------------------------------------------------------------
1.8652 + */
1.8653 +
1.8654 +static void
1.8655 +StopCopy(csPtr)
1.8656 + CopyState *csPtr; /* State for bg copy to stop . */
1.8657 +{
1.8658 + ChannelState *inStatePtr, *outStatePtr;
1.8659 + int nonBlocking;
1.8660 +
1.8661 + if (!csPtr) {
1.8662 + return;
1.8663 + }
1.8664 +
1.8665 + inStatePtr = csPtr->readPtr->state;
1.8666 + outStatePtr = csPtr->writePtr->state;
1.8667 +
1.8668 + /*
1.8669 + * Restore the old blocking mode and output buffering mode.
1.8670 + */
1.8671 +
1.8672 + nonBlocking = (csPtr->readFlags & CHANNEL_NONBLOCKING);
1.8673 + if (nonBlocking != (inStatePtr->flags & CHANNEL_NONBLOCKING)) {
1.8674 + SetBlockMode(NULL, csPtr->readPtr,
1.8675 + nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING);
1.8676 + }
1.8677 + if (csPtr->readPtr != csPtr->writePtr) {
1.8678 + nonBlocking = (csPtr->writeFlags & CHANNEL_NONBLOCKING);
1.8679 + if (nonBlocking != (outStatePtr->flags & CHANNEL_NONBLOCKING)) {
1.8680 + SetBlockMode(NULL, csPtr->writePtr,
1.8681 + nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING);
1.8682 + }
1.8683 + }
1.8684 + outStatePtr->flags &= ~(CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED);
1.8685 + outStatePtr->flags |=
1.8686 + csPtr->writeFlags & (CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED);
1.8687 +
1.8688 + if (csPtr->cmdPtr) {
1.8689 + Tcl_DeleteChannelHandler((Tcl_Channel)csPtr->readPtr, CopyEventProc,
1.8690 + (ClientData)csPtr);
1.8691 + if (csPtr->readPtr != csPtr->writePtr) {
1.8692 + Tcl_DeleteChannelHandler((Tcl_Channel)csPtr->writePtr,
1.8693 + CopyEventProc, (ClientData)csPtr);
1.8694 + }
1.8695 + Tcl_DecrRefCount(csPtr->cmdPtr);
1.8696 + }
1.8697 + inStatePtr->csPtr = NULL;
1.8698 + outStatePtr->csPtr = NULL;
1.8699 + ckfree((char*) csPtr);
1.8700 +}
1.8701 +
1.8702 +/*
1.8703 + *----------------------------------------------------------------------
1.8704 + *
1.8705 + * StackSetBlockMode --
1.8706 + *
1.8707 + * This function sets the blocking mode for a channel, iterating
1.8708 + * through each channel in a stack and updates the state flags.
1.8709 + *
1.8710 + * Results:
1.8711 + * 0 if OK, result code from failed blockModeProc otherwise.
1.8712 + *
1.8713 + * Side effects:
1.8714 + * Modifies the blocking mode of the channel and possibly generates
1.8715 + * an error.
1.8716 + *
1.8717 + *----------------------------------------------------------------------
1.8718 + */
1.8719 +
1.8720 +static int
1.8721 +StackSetBlockMode(chanPtr, mode)
1.8722 + Channel *chanPtr; /* Channel to modify. */
1.8723 + int mode; /* One of TCL_MODE_BLOCKING or
1.8724 + * TCL_MODE_NONBLOCKING. */
1.8725 +{
1.8726 + int result = 0;
1.8727 + Tcl_DriverBlockModeProc *blockModeProc;
1.8728 +
1.8729 + /*
1.8730 + * Start at the top of the channel stack
1.8731 + */
1.8732 +
1.8733 + chanPtr = chanPtr->state->topChanPtr;
1.8734 + while (chanPtr != (Channel *) NULL) {
1.8735 + blockModeProc = Tcl_ChannelBlockModeProc(chanPtr->typePtr);
1.8736 + if (blockModeProc != NULL) {
1.8737 + result = (*blockModeProc) (chanPtr->instanceData, mode);
1.8738 + if (result != 0) {
1.8739 + Tcl_SetErrno(result);
1.8740 + return result;
1.8741 + }
1.8742 + }
1.8743 + chanPtr = chanPtr->downChanPtr;
1.8744 + }
1.8745 + return 0;
1.8746 +}
1.8747 +
1.8748 +/*
1.8749 + *----------------------------------------------------------------------
1.8750 + *
1.8751 + * SetBlockMode --
1.8752 + *
1.8753 + * This function sets the blocking mode for a channel and updates
1.8754 + * the state flags.
1.8755 + *
1.8756 + * Results:
1.8757 + * A standard Tcl result.
1.8758 + *
1.8759 + * Side effects:
1.8760 + * Modifies the blocking mode of the channel and possibly generates
1.8761 + * an error.
1.8762 + *
1.8763 + *----------------------------------------------------------------------
1.8764 + */
1.8765 +
1.8766 +static int
1.8767 +SetBlockMode(interp, chanPtr, mode)
1.8768 + Tcl_Interp *interp; /* Interp for error reporting. */
1.8769 + Channel *chanPtr; /* Channel to modify. */
1.8770 + int mode; /* One of TCL_MODE_BLOCKING or
1.8771 + * TCL_MODE_NONBLOCKING. */
1.8772 +{
1.8773 + ChannelState *statePtr = chanPtr->state; /* state info for channel */
1.8774 + int result = 0;
1.8775 +
1.8776 + result = StackSetBlockMode(chanPtr, mode);
1.8777 + if (result != 0) {
1.8778 + if (interp != (Tcl_Interp *) NULL) {
1.8779 + Tcl_AppendResult(interp, "error setting blocking mode: ",
1.8780 + Tcl_PosixError(interp), (char *) NULL);
1.8781 + }
1.8782 + return TCL_ERROR;
1.8783 + }
1.8784 + if (mode == TCL_MODE_BLOCKING) {
1.8785 + statePtr->flags &= (~(CHANNEL_NONBLOCKING | BG_FLUSH_SCHEDULED));
1.8786 + } else {
1.8787 + statePtr->flags |= CHANNEL_NONBLOCKING;
1.8788 + }
1.8789 + return TCL_OK;
1.8790 +}
1.8791 +
1.8792 +/*
1.8793 + *----------------------------------------------------------------------
1.8794 + *
1.8795 + * Tcl_GetChannelNames --
1.8796 + *
1.8797 + * Return the names of all open channels in the interp.
1.8798 + *
1.8799 + * Results:
1.8800 + * TCL_OK or TCL_ERROR.
1.8801 + *
1.8802 + * Side effects:
1.8803 + * Interp result modified with list of channel names.
1.8804 + *
1.8805 + *----------------------------------------------------------------------
1.8806 + */
1.8807 +
1.8808 +EXPORT_C int
1.8809 +Tcl_GetChannelNames(interp)
1.8810 + Tcl_Interp *interp; /* Interp for error reporting. */
1.8811 +{
1.8812 + return Tcl_GetChannelNamesEx(interp, (char *) NULL);
1.8813 +}
1.8814 +
1.8815 +/*
1.8816 + *----------------------------------------------------------------------
1.8817 + *
1.8818 + * Tcl_GetChannelNamesEx --
1.8819 + *
1.8820 + * Return the names of open channels in the interp filtered
1.8821 + * filtered through a pattern. If pattern is NULL, it returns
1.8822 + * all the open channels.
1.8823 + *
1.8824 + * Results:
1.8825 + * TCL_OK or TCL_ERROR.
1.8826 + *
1.8827 + * Side effects:
1.8828 + * Interp result modified with list of channel names.
1.8829 + *
1.8830 + *----------------------------------------------------------------------
1.8831 + */
1.8832 +
1.8833 +EXPORT_C int
1.8834 +Tcl_GetChannelNamesEx(interp, pattern)
1.8835 + Tcl_Interp *interp; /* Interp for error reporting. */
1.8836 + CONST char *pattern; /* pattern to filter on. */
1.8837 +{
1.8838 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.8839 + ChannelState *statePtr;
1.8840 + CONST char *name; /* name for channel */
1.8841 + Tcl_Obj *resultPtr; /* pointer to result object */
1.8842 + Tcl_HashTable *hTblPtr; /* Hash table of channels. */
1.8843 + Tcl_HashEntry *hPtr; /* Search variable. */
1.8844 + Tcl_HashSearch hSearch; /* Search variable. */
1.8845 +
1.8846 + if (interp == (Tcl_Interp *) NULL) {
1.8847 + return TCL_OK;
1.8848 + }
1.8849 +
1.8850 + /*
1.8851 + * Get the channel table that stores the channels registered
1.8852 + * for this interpreter.
1.8853 + */
1.8854 + hTblPtr = GetChannelTable(interp);
1.8855 + resultPtr = Tcl_GetObjResult(interp);
1.8856 +
1.8857 + for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
1.8858 + hPtr != (Tcl_HashEntry *) NULL;
1.8859 + hPtr = Tcl_NextHashEntry(&hSearch)) {
1.8860 +
1.8861 + statePtr = ((Channel *) Tcl_GetHashValue(hPtr))->state;
1.8862 + if (statePtr->topChanPtr == (Channel *) tsdPtr->stdinChannel) {
1.8863 + name = "stdin";
1.8864 + } else if (statePtr->topChanPtr == (Channel *) tsdPtr->stdoutChannel) {
1.8865 + name = "stdout";
1.8866 + } else if (statePtr->topChanPtr == (Channel *) tsdPtr->stderrChannel) {
1.8867 + name = "stderr";
1.8868 + } else {
1.8869 + /*
1.8870 + * This is also stored in Tcl_GetHashKey(hTblPtr, hPtr),
1.8871 + * but it's simpler to just grab the name from the statePtr.
1.8872 + */
1.8873 + name = statePtr->channelName;
1.8874 + }
1.8875 +
1.8876 + if (((pattern == NULL) || Tcl_StringMatch(name, pattern)) &&
1.8877 + (Tcl_ListObjAppendElement(interp, resultPtr,
1.8878 + Tcl_NewStringObj(name, -1)) != TCL_OK)) {
1.8879 + return TCL_ERROR;
1.8880 + }
1.8881 + }
1.8882 + return TCL_OK;
1.8883 +}
1.8884 +
1.8885 +/*
1.8886 + *----------------------------------------------------------------------
1.8887 + *
1.8888 + * Tcl_IsChannelRegistered --
1.8889 + *
1.8890 + * Checks whether the channel is associated with the interp.
1.8891 + * See also Tcl_RegisterChannel and Tcl_UnregisterChannel.
1.8892 + *
1.8893 + * Results:
1.8894 + * 0 if the channel is not registered in the interpreter, 1 else.
1.8895 + *
1.8896 + * Side effects:
1.8897 + * None.
1.8898 + *
1.8899 + *----------------------------------------------------------------------
1.8900 + */
1.8901 +
1.8902 +EXPORT_C int
1.8903 +Tcl_IsChannelRegistered (interp, chan)
1.8904 + Tcl_Interp* interp; /* The interp to query of the channel */
1.8905 + Tcl_Channel chan; /* The channel to check */
1.8906 +{
1.8907 + Tcl_HashTable *hTblPtr; /* Hash table of channels. */
1.8908 + Tcl_HashEntry *hPtr; /* Search variable. */
1.8909 + Channel *chanPtr; /* The real IO channel. */
1.8910 + ChannelState *statePtr; /* State of the real channel. */
1.8911 +
1.8912 + /*
1.8913 + * Always check bottom-most channel in the stack. This is the one
1.8914 + * that gets registered.
1.8915 + */
1.8916 + chanPtr = ((Channel *) chan)->state->bottomChanPtr;
1.8917 + statePtr = chanPtr->state;
1.8918 +
1.8919 + hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
1.8920 + if (hTblPtr == (Tcl_HashTable *) NULL) {
1.8921 + return 0;
1.8922 + }
1.8923 + hPtr = Tcl_FindHashEntry(hTblPtr, statePtr->channelName);
1.8924 + if (hPtr == (Tcl_HashEntry *) NULL) {
1.8925 + return 0;
1.8926 + }
1.8927 + if ((Channel *) Tcl_GetHashValue(hPtr) != chanPtr) {
1.8928 + return 0;
1.8929 + }
1.8930 +
1.8931 + return 1;
1.8932 +}
1.8933 +
1.8934 +/*
1.8935 + *----------------------------------------------------------------------
1.8936 + *
1.8937 + * Tcl_IsChannelShared --
1.8938 + *
1.8939 + * Checks whether the channel is shared by multiple interpreters.
1.8940 + *
1.8941 + * Results:
1.8942 + * A boolean value (0 = Not shared, 1 = Shared).
1.8943 + *
1.8944 + * Side effects:
1.8945 + * None.
1.8946 + *
1.8947 + *----------------------------------------------------------------------
1.8948 + */
1.8949 +
1.8950 +EXPORT_C int
1.8951 +Tcl_IsChannelShared (chan)
1.8952 + Tcl_Channel chan; /* The channel to query */
1.8953 +{
1.8954 + ChannelState *statePtr = ((Channel *) chan)->state;
1.8955 + /* State of real channel structure. */
1.8956 +
1.8957 + return ((statePtr->refCount > 1) ? 1 : 0);
1.8958 +}
1.8959 +
1.8960 +/*
1.8961 + *----------------------------------------------------------------------
1.8962 + *
1.8963 + * Tcl_IsChannelExisting --
1.8964 + *
1.8965 + * Checks whether a channel of the given name exists in the
1.8966 + * (thread)-global list of all channels.
1.8967 + * See Tcl_GetChannelNamesEx for function exposed at the Tcl level.
1.8968 + *
1.8969 + * Results:
1.8970 + * A boolean value (0 = Does not exist, 1 = Does exist).
1.8971 + *
1.8972 + * Side effects:
1.8973 + * None.
1.8974 + *
1.8975 + *----------------------------------------------------------------------
1.8976 + */
1.8977 +
1.8978 +EXPORT_C int
1.8979 +Tcl_IsChannelExisting(chanName)
1.8980 + CONST char* chanName; /* The name of the channel to look for. */
1.8981 +{
1.8982 + ChannelState *statePtr;
1.8983 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.8984 + CONST char *name;
1.8985 + int chanNameLen;
1.8986 +
1.8987 + chanNameLen = strlen(chanName);
1.8988 + for (statePtr = tsdPtr->firstCSPtr;
1.8989 + statePtr != NULL;
1.8990 + statePtr = statePtr->nextCSPtr) {
1.8991 + if (statePtr->topChanPtr == (Channel *) tsdPtr->stdinChannel) {
1.8992 + name = "stdin";
1.8993 + } else if (statePtr->topChanPtr == (Channel *) tsdPtr->stdoutChannel) {
1.8994 + name = "stdout";
1.8995 + } else if (statePtr->topChanPtr == (Channel *) tsdPtr->stderrChannel) {
1.8996 + name = "stderr";
1.8997 + } else {
1.8998 + name = statePtr->channelName;
1.8999 + }
1.9000 +
1.9001 + if ((*chanName == *name) &&
1.9002 + (memcmp(name, chanName, (size_t) chanNameLen) == 0)) {
1.9003 + return 1;
1.9004 + }
1.9005 + }
1.9006 +
1.9007 + return 0;
1.9008 +}
1.9009 +
1.9010 +/*
1.9011 + *----------------------------------------------------------------------
1.9012 + *
1.9013 + * Tcl_ChannelName --
1.9014 + *
1.9015 + * Return the name of the channel type.
1.9016 + *
1.9017 + * Results:
1.9018 + * A pointer the name of the channel type.
1.9019 + *
1.9020 + * Side effects:
1.9021 + * None.
1.9022 + *
1.9023 + *----------------------------------------------------------------------
1.9024 + */
1.9025 +
1.9026 +EXPORT_C CONST char *
1.9027 +Tcl_ChannelName(chanTypePtr)
1.9028 + Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
1.9029 +{
1.9030 + return chanTypePtr->typeName;
1.9031 +}
1.9032 +
1.9033 +/*
1.9034 + *----------------------------------------------------------------------
1.9035 + *
1.9036 + * Tcl_ChannelVersion --
1.9037 + *
1.9038 + * Return the of version of the channel type.
1.9039 + *
1.9040 + * Results:
1.9041 + * One of the TCL_CHANNEL_VERSION_* constants from tcl.h
1.9042 + *
1.9043 + * Side effects:
1.9044 + * None.
1.9045 + *
1.9046 + *----------------------------------------------------------------------
1.9047 + */
1.9048 +
1.9049 +EXPORT_C Tcl_ChannelTypeVersion
1.9050 +Tcl_ChannelVersion(chanTypePtr)
1.9051 + Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
1.9052 +{
1.9053 + if (chanTypePtr->version == TCL_CHANNEL_VERSION_2) {
1.9054 + return TCL_CHANNEL_VERSION_2;
1.9055 + } else if (chanTypePtr->version == TCL_CHANNEL_VERSION_3) {
1.9056 + return TCL_CHANNEL_VERSION_3;
1.9057 + } else if (chanTypePtr->version == TCL_CHANNEL_VERSION_4) {
1.9058 + return TCL_CHANNEL_VERSION_4;
1.9059 + } else {
1.9060 + /*
1.9061 + * In <v2 channel versions, the version field is occupied
1.9062 + * by the Tcl_DriverBlockModeProc
1.9063 + */
1.9064 + return TCL_CHANNEL_VERSION_1;
1.9065 + }
1.9066 +}
1.9067 +
1.9068 +/*
1.9069 + *----------------------------------------------------------------------
1.9070 + *
1.9071 + * HaveVersion --
1.9072 + *
1.9073 + * Return whether a channel type is (at least) of a given version.
1.9074 + *
1.9075 + * Results:
1.9076 + * True if the minimum version is exceeded by the version actually
1.9077 + * present.
1.9078 + *
1.9079 + * Side effects:
1.9080 + * None.
1.9081 + *
1.9082 + *----------------------------------------------------------------------
1.9083 + */
1.9084 +
1.9085 +static int
1.9086 +HaveVersion(chanTypePtr, minimumVersion)
1.9087 + Tcl_ChannelType *chanTypePtr;
1.9088 + Tcl_ChannelTypeVersion minimumVersion;
1.9089 +{
1.9090 + Tcl_ChannelTypeVersion actualVersion = Tcl_ChannelVersion(chanTypePtr);
1.9091 +
1.9092 + return ((int)actualVersion) >= ((int)minimumVersion);
1.9093 +}
1.9094 +
1.9095 +/*
1.9096 + *----------------------------------------------------------------------
1.9097 + *
1.9098 + * Tcl_ChannelBlockModeProc --
1.9099 + *
1.9100 + * Return the Tcl_DriverBlockModeProc of the channel type.
1.9101 + *
1.9102 + * Results:
1.9103 + * A pointer to the proc.
1.9104 + *
1.9105 + * Side effects:
1.9106 + * None.
1.9107 + *
1.9108 + *---------------------------------------------------------------------- */
1.9109 +
1.9110 +EXPORT_C Tcl_DriverBlockModeProc *
1.9111 +Tcl_ChannelBlockModeProc(chanTypePtr)
1.9112 + Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
1.9113 +{
1.9114 + if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_2)) {
1.9115 + return chanTypePtr->blockModeProc;
1.9116 + } else {
1.9117 + /*
1.9118 + * The v1 structure had the blockModeProc in a different place.
1.9119 + */
1.9120 + return (Tcl_DriverBlockModeProc *) (chanTypePtr->version);
1.9121 + }
1.9122 +}
1.9123 +
1.9124 +/*
1.9125 + *----------------------------------------------------------------------
1.9126 + *
1.9127 + * Tcl_ChannelCloseProc --
1.9128 + *
1.9129 + * Return the Tcl_DriverCloseProc of the channel type.
1.9130 + *
1.9131 + * Results:
1.9132 + * A pointer to the proc.
1.9133 + *
1.9134 + * Side effects:
1.9135 + * None.
1.9136 + *
1.9137 + *----------------------------------------------------------------------
1.9138 + */
1.9139 +
1.9140 +EXPORT_C Tcl_DriverCloseProc *
1.9141 +Tcl_ChannelCloseProc(chanTypePtr)
1.9142 + Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
1.9143 +{
1.9144 + return chanTypePtr->closeProc;
1.9145 +}
1.9146 +
1.9147 +/*
1.9148 + *----------------------------------------------------------------------
1.9149 + *
1.9150 + * Tcl_ChannelClose2Proc --
1.9151 + *
1.9152 + * Return the Tcl_DriverClose2Proc of the channel type.
1.9153 + *
1.9154 + * Results:
1.9155 + * A pointer to the proc.
1.9156 + *
1.9157 + * Side effects:
1.9158 + * None.
1.9159 + *
1.9160 + *----------------------------------------------------------------------
1.9161 + */
1.9162 +
1.9163 +EXPORT_C Tcl_DriverClose2Proc *
1.9164 +Tcl_ChannelClose2Proc(chanTypePtr)
1.9165 + Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
1.9166 +{
1.9167 + return chanTypePtr->close2Proc;
1.9168 +}
1.9169 +
1.9170 +/*
1.9171 + *----------------------------------------------------------------------
1.9172 + *
1.9173 + * Tcl_ChannelInputProc --
1.9174 + *
1.9175 + * Return the Tcl_DriverInputProc of the channel type.
1.9176 + *
1.9177 + * Results:
1.9178 + * A pointer to the proc.
1.9179 + *
1.9180 + * Side effects:
1.9181 + * None.
1.9182 + *
1.9183 + *----------------------------------------------------------------------
1.9184 + */
1.9185 +
1.9186 +EXPORT_C Tcl_DriverInputProc *
1.9187 +Tcl_ChannelInputProc(chanTypePtr)
1.9188 + Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
1.9189 +{
1.9190 + return chanTypePtr->inputProc;
1.9191 +}
1.9192 +
1.9193 +/*
1.9194 + *----------------------------------------------------------------------
1.9195 + *
1.9196 + * Tcl_ChannelOutputProc --
1.9197 + *
1.9198 + * Return the Tcl_DriverOutputProc of the channel type.
1.9199 + *
1.9200 + * Results:
1.9201 + * A pointer to the proc.
1.9202 + *
1.9203 + * Side effects:
1.9204 + * None.
1.9205 + *
1.9206 + *----------------------------------------------------------------------
1.9207 + */
1.9208 +
1.9209 +EXPORT_C Tcl_DriverOutputProc *
1.9210 +Tcl_ChannelOutputProc(chanTypePtr)
1.9211 + Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
1.9212 +{
1.9213 + return chanTypePtr->outputProc;
1.9214 +}
1.9215 +
1.9216 +/*
1.9217 + *----------------------------------------------------------------------
1.9218 + *
1.9219 + * Tcl_ChannelSeekProc --
1.9220 + *
1.9221 + * Return the Tcl_DriverSeekProc of the channel type.
1.9222 + *
1.9223 + * Results:
1.9224 + * A pointer to the proc.
1.9225 + *
1.9226 + * Side effects:
1.9227 + * None.
1.9228 + *
1.9229 + *----------------------------------------------------------------------
1.9230 + */
1.9231 +
1.9232 +EXPORT_C Tcl_DriverSeekProc *
1.9233 +Tcl_ChannelSeekProc(chanTypePtr)
1.9234 + Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
1.9235 +{
1.9236 + return chanTypePtr->seekProc;
1.9237 +}
1.9238 +
1.9239 +/*
1.9240 + *----------------------------------------------------------------------
1.9241 + *
1.9242 + * Tcl_ChannelSetOptionProc --
1.9243 + *
1.9244 + * Return the Tcl_DriverSetOptionProc of the channel type.
1.9245 + *
1.9246 + * Results:
1.9247 + * A pointer to the proc.
1.9248 + *
1.9249 + * Side effects:
1.9250 + * None.
1.9251 + *
1.9252 + *----------------------------------------------------------------------
1.9253 + */
1.9254 +
1.9255 +EXPORT_C Tcl_DriverSetOptionProc *
1.9256 +Tcl_ChannelSetOptionProc(chanTypePtr)
1.9257 + Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
1.9258 +{
1.9259 + return chanTypePtr->setOptionProc;
1.9260 +}
1.9261 +
1.9262 +/*
1.9263 + *----------------------------------------------------------------------
1.9264 + *
1.9265 + * Tcl_ChannelGetOptionProc --
1.9266 + *
1.9267 + * Return the Tcl_DriverGetOptionProc of the channel type.
1.9268 + *
1.9269 + * Results:
1.9270 + * A pointer to the proc.
1.9271 + *
1.9272 + * Side effects:
1.9273 + * None.
1.9274 + *
1.9275 + *----------------------------------------------------------------------
1.9276 + */
1.9277 +
1.9278 +EXPORT_C Tcl_DriverGetOptionProc *
1.9279 +Tcl_ChannelGetOptionProc(chanTypePtr)
1.9280 + Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
1.9281 +{
1.9282 + return chanTypePtr->getOptionProc;
1.9283 +}
1.9284 +
1.9285 +/*
1.9286 + *----------------------------------------------------------------------
1.9287 + *
1.9288 + * Tcl_ChannelWatchProc --
1.9289 + *
1.9290 + * Return the Tcl_DriverWatchProc of the channel type.
1.9291 + *
1.9292 + * Results:
1.9293 + * A pointer to the proc.
1.9294 + *
1.9295 + * Side effects:
1.9296 + * None.
1.9297 + *
1.9298 + *----------------------------------------------------------------------
1.9299 + */
1.9300 +
1.9301 +EXPORT_C Tcl_DriverWatchProc *
1.9302 +Tcl_ChannelWatchProc(chanTypePtr)
1.9303 + Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
1.9304 +{
1.9305 + return chanTypePtr->watchProc;
1.9306 +}
1.9307 +
1.9308 +/*
1.9309 + *----------------------------------------------------------------------
1.9310 + *
1.9311 + * Tcl_ChannelGetHandleProc --
1.9312 + *
1.9313 + * Return the Tcl_DriverGetHandleProc of the channel type.
1.9314 + *
1.9315 + * Results:
1.9316 + * A pointer to the proc.
1.9317 + *
1.9318 + * Side effects:
1.9319 + * None.
1.9320 + *
1.9321 + *----------------------------------------------------------------------
1.9322 + */
1.9323 +
1.9324 +EXPORT_C Tcl_DriverGetHandleProc *
1.9325 +Tcl_ChannelGetHandleProc(chanTypePtr)
1.9326 + Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
1.9327 +{
1.9328 + return chanTypePtr->getHandleProc;
1.9329 +}
1.9330 +
1.9331 +/*
1.9332 + *----------------------------------------------------------------------
1.9333 + *
1.9334 + * Tcl_ChannelFlushProc --
1.9335 + *
1.9336 + * Return the Tcl_DriverFlushProc of the channel type.
1.9337 + *
1.9338 + * Results:
1.9339 + * A pointer to the proc.
1.9340 + *
1.9341 + * Side effects:
1.9342 + * None.
1.9343 + *
1.9344 + *----------------------------------------------------------------------
1.9345 + */
1.9346 +
1.9347 +EXPORT_C Tcl_DriverFlushProc *
1.9348 +Tcl_ChannelFlushProc(chanTypePtr)
1.9349 + Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
1.9350 +{
1.9351 + if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_2)) {
1.9352 + return chanTypePtr->flushProc;
1.9353 + } else {
1.9354 + return NULL;
1.9355 + }
1.9356 +}
1.9357 +
1.9358 +/*
1.9359 + *----------------------------------------------------------------------
1.9360 + *
1.9361 + * Tcl_ChannelHandlerProc --
1.9362 + *
1.9363 + * Return the Tcl_DriverHandlerProc of the channel type.
1.9364 + *
1.9365 + * Results:
1.9366 + * A pointer to the proc.
1.9367 + *
1.9368 + * Side effects:
1.9369 + * None.
1.9370 + *
1.9371 + *----------------------------------------------------------------------
1.9372 + */
1.9373 +
1.9374 +EXPORT_C Tcl_DriverHandlerProc *
1.9375 +Tcl_ChannelHandlerProc(chanTypePtr)
1.9376 + Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
1.9377 +{
1.9378 + if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_2)) {
1.9379 + return chanTypePtr->handlerProc;
1.9380 + } else {
1.9381 + return NULL;
1.9382 + }
1.9383 +}
1.9384 +
1.9385 +/*
1.9386 + *----------------------------------------------------------------------
1.9387 + *
1.9388 + * Tcl_ChannelWideSeekProc --
1.9389 + *
1.9390 + * Return the Tcl_DriverWideSeekProc of the channel type.
1.9391 + *
1.9392 + * Results:
1.9393 + * A pointer to the proc.
1.9394 + *
1.9395 + * Side effects:
1.9396 + * None.
1.9397 + *
1.9398 + *----------------------------------------------------------------------
1.9399 + */
1.9400 +
1.9401 +EXPORT_C Tcl_DriverWideSeekProc *
1.9402 +Tcl_ChannelWideSeekProc(chanTypePtr)
1.9403 + Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
1.9404 +{
1.9405 + if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_3)) {
1.9406 + return chanTypePtr->wideSeekProc;
1.9407 + } else {
1.9408 + return NULL;
1.9409 + }
1.9410 +}
1.9411 +
1.9412 +/*
1.9413 + *----------------------------------------------------------------------
1.9414 + *
1.9415 + * Tcl_ChannelThreadActionProc --
1.9416 + *
1.9417 + * Return the Tcl_DriverThreadActionProc of the channel type.
1.9418 + *
1.9419 + * Results:
1.9420 + * A pointer to the proc.
1.9421 + *
1.9422 + * Side effects:
1.9423 + * None.
1.9424 + *
1.9425 + *----------------------------------------------------------------------
1.9426 + */
1.9427 +
1.9428 +EXPORT_C Tcl_DriverThreadActionProc *
1.9429 +Tcl_ChannelThreadActionProc(chanTypePtr)
1.9430 + Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
1.9431 +{
1.9432 + if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_4)) {
1.9433 + return chanTypePtr->threadActionProc;
1.9434 + } else {
1.9435 + return NULL;
1.9436 + }
1.9437 +}
1.9438 +
1.9439 +#if 0
1.9440 +/* For future debugging work, a simple function to print the flags of
1.9441 + * a channel in semi-readable form.
1.9442 + */
1.9443 +
1.9444 +static int
1.9445 +DumpFlags (str, flags)
1.9446 + char* str;
1.9447 + int flags;
1.9448 +{
1.9449 + char buf [20];
1.9450 + int i = 0;
1.9451 +
1.9452 + if (flags & TCL_READABLE) {buf[i] = 'r';} else {buf [i]='_';}; i++;
1.9453 + if (flags & TCL_WRITABLE) {buf[i] = 'w';} else {buf [i]='_';}; i++;
1.9454 + if (flags & CHANNEL_NONBLOCKING) {buf[i] = 'n';} else {buf [i]='_';}; i++;
1.9455 + if (flags & CHANNEL_LINEBUFFERED) {buf[i] = 'l';} else {buf [i]='_';}; i++;
1.9456 + if (flags & CHANNEL_UNBUFFERED) {buf[i] = 'u';} else {buf [i]='_';}; i++;
1.9457 + if (flags & BUFFER_READY) {buf[i] = 'R';} else {buf [i]='_';}; i++;
1.9458 + if (flags & BG_FLUSH_SCHEDULED) {buf[i] = 'F';} else {buf [i]='_';}; i++;
1.9459 + if (flags & CHANNEL_CLOSED) {buf[i] = 'c';} else {buf [i]='_';}; i++;
1.9460 + if (flags & CHANNEL_EOF) {buf[i] = 'E';} else {buf [i]='_';}; i++;
1.9461 + if (flags & CHANNEL_STICKY_EOF) {buf[i] = 'S';} else {buf [i]='_';}; i++;
1.9462 + if (flags & CHANNEL_BLOCKED) {buf[i] = 'B';} else {buf [i]='_';}; i++;
1.9463 + if (flags & INPUT_SAW_CR) {buf[i] = '/';} else {buf [i]='_';}; i++;
1.9464 + if (flags & INPUT_NEED_NL) {buf[i] = '*';} else {buf [i]='_';}; i++;
1.9465 + if (flags & CHANNEL_DEAD) {buf[i] = 'D';} else {buf [i]='_';}; i++;
1.9466 + if (flags & CHANNEL_RAW_MODE) {buf[i] = 'R';} else {buf [i]='_';}; i++;
1.9467 +#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
1.9468 + if (flags & CHANNEL_TIMER_FEV) {buf[i] = 'T';} else {buf [i]='_';}; i++;
1.9469 + if (flags & CHANNEL_HAS_MORE_DATA) {buf[i] = 'H';} else {buf [i]='_';}; i++;
1.9470 +#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
1.9471 + if (flags & CHANNEL_INCLOSE) {buf[i] = 'x';} else {buf [i]='_';}; i++;
1.9472 + buf [i] ='\0';
1.9473 +
1.9474 + fprintf (stderr,"%s: %s\n", str, buf); fflush(stderr);
1.9475 + return 0;
1.9476 +}
1.9477 +#endif