os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinChan.c
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinChan.c Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,1433 @@
1.4 +/*
1.5 + * tclWinChan.c
1.6 + *
1.7 + * Channel drivers for Windows channels based on files, command
1.8 + * pipes and TCP sockets.
1.9 + *
1.10 + * Copyright (c) 1995-1997 Sun Microsystems, Inc.
1.11 + *
1.12 + * See the file "license.terms" for information on usage and redistribution
1.13 + * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
1.14 + *
1.15 + * RCS: @(#) $Id: tclWinChan.c,v 1.30.2.5 2006/08/30 17:53:28 hobbs Exp $
1.16 + */
1.17 +
1.18 +#include "tclWinInt.h"
1.19 +#include "tclIO.h"
1.20 +
1.21 +/*
1.22 + * State flags used in the info structures below.
1.23 + */
1.24 +
1.25 +#define FILE_PENDING (1<<0) /* Message is pending in the queue. */
1.26 +#define FILE_ASYNC (1<<1) /* Channel is non-blocking. */
1.27 +#define FILE_APPEND (1<<2) /* File is in append mode. */
1.28 +
1.29 +#define FILE_TYPE_SERIAL (FILE_TYPE_PIPE+1)
1.30 +#define FILE_TYPE_CONSOLE (FILE_TYPE_PIPE+2)
1.31 +
1.32 +/*
1.33 + * The following structure contains per-instance data for a file based channel.
1.34 + */
1.35 +
1.36 +typedef struct FileInfo {
1.37 + Tcl_Channel channel; /* Pointer to channel structure. */
1.38 + int validMask; /* OR'ed combination of TCL_READABLE,
1.39 + * TCL_WRITABLE, or TCL_EXCEPTION: indicates
1.40 + * which operations are valid on the file. */
1.41 + int watchMask; /* OR'ed combination of TCL_READABLE,
1.42 + * TCL_WRITABLE, or TCL_EXCEPTION: indicates
1.43 + * which events should be reported. */
1.44 + int flags; /* State flags, see above for a list. */
1.45 + HANDLE handle; /* Input/output file. */
1.46 + struct FileInfo *nextPtr; /* Pointer to next registered file. */
1.47 + int dirty; /* Boolean flag. Set if the OS may have data
1.48 + * pending on the channel */
1.49 +} FileInfo;
1.50 +
1.51 +typedef struct ThreadSpecificData {
1.52 + /*
1.53 + * List of all file channels currently open.
1.54 + */
1.55 +
1.56 + FileInfo *firstFilePtr;
1.57 +} ThreadSpecificData;
1.58 +
1.59 +static Tcl_ThreadDataKey dataKey;
1.60 +
1.61 +/*
1.62 + * The following structure is what is added to the Tcl event queue when
1.63 + * file events are generated.
1.64 + */
1.65 +
1.66 +typedef struct FileEvent {
1.67 + Tcl_Event header; /* Information that is standard for
1.68 + * all events. */
1.69 + FileInfo *infoPtr; /* Pointer to file info structure. Note
1.70 + * that we still have to verify that the
1.71 + * file exists before dereferencing this
1.72 + * pointer. */
1.73 +} FileEvent;
1.74 +
1.75 +/*
1.76 + * Static routines for this file:
1.77 + */
1.78 +
1.79 +static int FileBlockProc _ANSI_ARGS_((ClientData instanceData,
1.80 + int mode));
1.81 +static void FileChannelExitHandler _ANSI_ARGS_((
1.82 + ClientData clientData));
1.83 +static void FileCheckProc _ANSI_ARGS_((ClientData clientData,
1.84 + int flags));
1.85 +static int FileCloseProc _ANSI_ARGS_((ClientData instanceData,
1.86 + Tcl_Interp *interp));
1.87 +static int FileEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
1.88 + int flags));
1.89 +static int FileGetHandleProc _ANSI_ARGS_((ClientData instanceData,
1.90 + int direction, ClientData *handlePtr));
1.91 +static ThreadSpecificData *FileInit _ANSI_ARGS_((void));
1.92 +static int FileInputProc _ANSI_ARGS_((ClientData instanceData,
1.93 + char *buf, int toRead, int *errorCode));
1.94 +static int FileOutputProc _ANSI_ARGS_((ClientData instanceData,
1.95 + CONST char *buf, int toWrite, int *errorCode));
1.96 +static int FileSeekProc _ANSI_ARGS_((ClientData instanceData,
1.97 + long offset, int mode, int *errorCode));
1.98 +static Tcl_WideInt FileWideSeekProc _ANSI_ARGS_((ClientData instanceData,
1.99 + Tcl_WideInt offset, int mode, int *errorCode));
1.100 +static void FileSetupProc _ANSI_ARGS_((ClientData clientData,
1.101 + int flags));
1.102 +static void FileWatchProc _ANSI_ARGS_((ClientData instanceData,
1.103 + int mask));
1.104 +static void FileThreadActionProc _ANSI_ARGS_ ((
1.105 + ClientData instanceData, int action));
1.106 +static DWORD FileGetType _ANSI_ARGS_((HANDLE handle));
1.107 +
1.108 +/*
1.109 + * This structure describes the channel type structure for file based IO.
1.110 + */
1.111 +
1.112 +static Tcl_ChannelType fileChannelType = {
1.113 + "file", /* Type name. */
1.114 + TCL_CHANNEL_VERSION_4, /* v4 channel */
1.115 + FileCloseProc, /* Close proc. */
1.116 + FileInputProc, /* Input proc. */
1.117 + FileOutputProc, /* Output proc. */
1.118 + FileSeekProc, /* Seek proc. */
1.119 + NULL, /* Set option proc. */
1.120 + NULL, /* Get option proc. */
1.121 + FileWatchProc, /* Set up the notifier to watch the channel. */
1.122 + FileGetHandleProc, /* Get an OS handle from channel. */
1.123 + NULL, /* close2proc. */
1.124 + FileBlockProc, /* Set blocking or non-blocking mode.*/
1.125 + NULL, /* flush proc. */
1.126 + NULL, /* handler proc. */
1.127 + FileWideSeekProc, /* Wide seek proc. */
1.128 + FileThreadActionProc, /* Thread action proc. */
1.129 +};
1.130 +
1.131 +#ifdef HAVE_NO_SEH
1.132 +
1.133 +/*
1.134 + * Unlike Borland and Microsoft, we don't register exception handlers
1.135 + * by pushing registration records onto the runtime stack. Instead, we
1.136 + * register them by creating an EXCEPTION_REGISTRATION within the activation
1.137 + * record.
1.138 + */
1.139 +
1.140 +typedef struct EXCEPTION_REGISTRATION {
1.141 + struct EXCEPTION_REGISTRATION* link;
1.142 + EXCEPTION_DISPOSITION (*handler)( struct _EXCEPTION_RECORD*, void*,
1.143 + struct _CONTEXT*, void* );
1.144 + void* ebp;
1.145 + void* esp;
1.146 + int status;
1.147 +} EXCEPTION_REGISTRATION;
1.148 +
1.149 +#endif
1.150 +
1.151 +/*
1.152 + *----------------------------------------------------------------------
1.153 + *
1.154 + * FileInit --
1.155 + *
1.156 + * This function creates the window used to simulate file events.
1.157 + *
1.158 + * Results:
1.159 + * None.
1.160 + *
1.161 + * Side effects:
1.162 + * Creates a new window and creates an exit handler.
1.163 + *
1.164 + *----------------------------------------------------------------------
1.165 + */
1.166 +
1.167 +static ThreadSpecificData *
1.168 +FileInit()
1.169 +{
1.170 + ThreadSpecificData *tsdPtr =
1.171 + (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
1.172 + if (tsdPtr == NULL) {
1.173 + tsdPtr = TCL_TSD_INIT(&dataKey);
1.174 + tsdPtr->firstFilePtr = NULL;
1.175 + Tcl_CreateEventSource(FileSetupProc, FileCheckProc, NULL);
1.176 + Tcl_CreateThreadExitHandler(FileChannelExitHandler, NULL);
1.177 + }
1.178 + return tsdPtr;
1.179 +}
1.180 +
1.181 +/*
1.182 + *----------------------------------------------------------------------
1.183 + *
1.184 + * FileChannelExitHandler --
1.185 + *
1.186 + * This function is called to cleanup the channel driver before
1.187 + * Tcl is unloaded.
1.188 + *
1.189 + * Results:
1.190 + * None.
1.191 + *
1.192 + * Side effects:
1.193 + * Destroys the communication window.
1.194 + *
1.195 + *----------------------------------------------------------------------
1.196 + */
1.197 +
1.198 +static void
1.199 +FileChannelExitHandler(clientData)
1.200 + ClientData clientData; /* Old window proc */
1.201 +{
1.202 + Tcl_DeleteEventSource(FileSetupProc, FileCheckProc, NULL);
1.203 +}
1.204 +
1.205 +/*
1.206 + *----------------------------------------------------------------------
1.207 + *
1.208 + * FileSetupProc --
1.209 + *
1.210 + * This procedure is invoked before Tcl_DoOneEvent blocks waiting
1.211 + * for an event.
1.212 + *
1.213 + * Results:
1.214 + * None.
1.215 + *
1.216 + * Side effects:
1.217 + * Adjusts the block time if needed.
1.218 + *
1.219 + *----------------------------------------------------------------------
1.220 + */
1.221 +
1.222 +void
1.223 +FileSetupProc(data, flags)
1.224 + ClientData data; /* Not used. */
1.225 + int flags; /* Event flags as passed to Tcl_DoOneEvent. */
1.226 +{
1.227 + FileInfo *infoPtr;
1.228 + Tcl_Time blockTime = { 0, 0 };
1.229 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.230 +
1.231 + if (!(flags & TCL_FILE_EVENTS)) {
1.232 + return;
1.233 + }
1.234 +
1.235 + /*
1.236 + * Check to see if there is a ready file. If so, poll.
1.237 + */
1.238 +
1.239 + for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL;
1.240 + infoPtr = infoPtr->nextPtr) {
1.241 + if (infoPtr->watchMask) {
1.242 + Tcl_SetMaxBlockTime(&blockTime);
1.243 + break;
1.244 + }
1.245 + }
1.246 +}
1.247 +
1.248 +/*
1.249 + *----------------------------------------------------------------------
1.250 + *
1.251 + * FileCheckProc --
1.252 + *
1.253 + * This procedure is called by Tcl_DoOneEvent to check the file
1.254 + * event source for events.
1.255 + *
1.256 + * Results:
1.257 + * None.
1.258 + *
1.259 + * Side effects:
1.260 + * May queue an event.
1.261 + *
1.262 + *----------------------------------------------------------------------
1.263 + */
1.264 +
1.265 +static void
1.266 +FileCheckProc(data, flags)
1.267 + ClientData data; /* Not used. */
1.268 + int flags; /* Event flags as passed to Tcl_DoOneEvent. */
1.269 +{
1.270 + FileEvent *evPtr;
1.271 + FileInfo *infoPtr;
1.272 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.273 +
1.274 + if (!(flags & TCL_FILE_EVENTS)) {
1.275 + return;
1.276 + }
1.277 +
1.278 + /*
1.279 + * Queue events for any ready files that don't already have events
1.280 + * queued (caused by persistent states that won't generate WinSock
1.281 + * events).
1.282 + */
1.283 +
1.284 + for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL;
1.285 + infoPtr = infoPtr->nextPtr) {
1.286 + if (infoPtr->watchMask && !(infoPtr->flags & FILE_PENDING)) {
1.287 + infoPtr->flags |= FILE_PENDING;
1.288 + evPtr = (FileEvent *) ckalloc(sizeof(FileEvent));
1.289 + evPtr->header.proc = FileEventProc;
1.290 + evPtr->infoPtr = infoPtr;
1.291 + Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
1.292 + }
1.293 + }
1.294 +}
1.295 +
1.296 +/*----------------------------------------------------------------------
1.297 + *
1.298 + * FileEventProc --
1.299 + *
1.300 + * This function is invoked by Tcl_ServiceEvent when a file event
1.301 + * reaches the front of the event queue. This procedure invokes
1.302 + * Tcl_NotifyChannel on the file.
1.303 + *
1.304 + * Results:
1.305 + * Returns 1 if the event was handled, meaning it should be removed
1.306 + * from the queue. Returns 0 if the event was not handled, meaning
1.307 + * it should stay on the queue. The only time the event isn't
1.308 + * handled is if the TCL_FILE_EVENTS flag bit isn't set.
1.309 + *
1.310 + * Side effects:
1.311 + * Whatever the notifier callback does.
1.312 + *
1.313 + *----------------------------------------------------------------------
1.314 + */
1.315 +
1.316 +static int
1.317 +FileEventProc(evPtr, flags)
1.318 + Tcl_Event *evPtr; /* Event to service. */
1.319 + int flags; /* Flags that indicate what events to
1.320 + * handle, such as TCL_FILE_EVENTS. */
1.321 +{
1.322 + FileEvent *fileEvPtr = (FileEvent *)evPtr;
1.323 + FileInfo *infoPtr;
1.324 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.325 +
1.326 + if (!(flags & TCL_FILE_EVENTS)) {
1.327 + return 0;
1.328 + }
1.329 +
1.330 + /*
1.331 + * Search through the list of watched files for the one whose handle
1.332 + * matches the event. We do this rather than simply dereferencing
1.333 + * the handle in the event so that files can be deleted while the
1.334 + * event is in the queue.
1.335 + */
1.336 +
1.337 + for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL;
1.338 + infoPtr = infoPtr->nextPtr) {
1.339 + if (fileEvPtr->infoPtr == infoPtr) {
1.340 + infoPtr->flags &= ~(FILE_PENDING);
1.341 + Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask);
1.342 + break;
1.343 + }
1.344 + }
1.345 + return 1;
1.346 +}
1.347 +
1.348 +/*
1.349 + *----------------------------------------------------------------------
1.350 + *
1.351 + * FileBlockProc --
1.352 + *
1.353 + * Set blocking or non-blocking mode on channel.
1.354 + *
1.355 + * Results:
1.356 + * 0 if successful, errno when failed.
1.357 + *
1.358 + * Side effects:
1.359 + * Sets the device into blocking or non-blocking mode.
1.360 + *
1.361 + *----------------------------------------------------------------------
1.362 + */
1.363 +
1.364 +static int
1.365 +FileBlockProc(instanceData, mode)
1.366 + ClientData instanceData; /* Instance data for channel. */
1.367 + int mode; /* TCL_MODE_BLOCKING or
1.368 + * TCL_MODE_NONBLOCKING. */
1.369 +{
1.370 + FileInfo *infoPtr = (FileInfo *) instanceData;
1.371 +
1.372 + /*
1.373 + * Files on Windows can not be switched between blocking and nonblocking,
1.374 + * hence we have to emulate the behavior. This is done in the input
1.375 + * function by checking against a bit in the state. We set or unset the
1.376 + * bit here to cause the input function to emulate the correct behavior.
1.377 + */
1.378 +
1.379 + if (mode == TCL_MODE_NONBLOCKING) {
1.380 + infoPtr->flags |= FILE_ASYNC;
1.381 + } else {
1.382 + infoPtr->flags &= ~(FILE_ASYNC);
1.383 + }
1.384 + return 0;
1.385 +}
1.386 +
1.387 +/*
1.388 + *----------------------------------------------------------------------
1.389 + *
1.390 + * FileCloseProc --
1.391 + *
1.392 + * Closes the IO channel.
1.393 + *
1.394 + * Results:
1.395 + * 0 if successful, the value of errno if failed.
1.396 + *
1.397 + * Side effects:
1.398 + * Closes the physical channel
1.399 + *
1.400 + *----------------------------------------------------------------------
1.401 + */
1.402 +
1.403 +static int
1.404 +FileCloseProc(instanceData, interp)
1.405 + ClientData instanceData; /* Pointer to FileInfo structure. */
1.406 + Tcl_Interp *interp; /* Not used. */
1.407 +{
1.408 + FileInfo *fileInfoPtr = (FileInfo *) instanceData;
1.409 + FileInfo *infoPtr;
1.410 + ThreadSpecificData *tsdPtr;
1.411 + int errorCode = 0;
1.412 +
1.413 + /*
1.414 + * Remove the file from the watch list.
1.415 + */
1.416 +
1.417 + FileWatchProc(instanceData, 0);
1.418 +
1.419 + /*
1.420 + * Don't close the Win32 handle if the handle is a standard channel
1.421 + * during the thread exit process. Otherwise, one thread may kill
1.422 + * the stdio of another.
1.423 + */
1.424 +
1.425 + if (!TclInThreadExit()
1.426 + || ((GetStdHandle(STD_INPUT_HANDLE) != fileInfoPtr->handle)
1.427 + && (GetStdHandle(STD_OUTPUT_HANDLE) != fileInfoPtr->handle)
1.428 + && (GetStdHandle(STD_ERROR_HANDLE) != fileInfoPtr->handle))) {
1.429 + if (CloseHandle(fileInfoPtr->handle) == FALSE) {
1.430 + TclWinConvertError(GetLastError());
1.431 + errorCode = errno;
1.432 + }
1.433 + }
1.434 +
1.435 + /*
1.436 + * See if this FileInfo* is still on the thread local list.
1.437 + */
1.438 + tsdPtr = TCL_TSD_INIT(&dataKey);
1.439 + for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL;
1.440 + infoPtr = infoPtr->nextPtr) {
1.441 + if (infoPtr == fileInfoPtr) {
1.442 + /*
1.443 + * This channel exists on the thread local list. It should
1.444 + * have been removed by an earlier Thread Action call,
1.445 + * but do that now since just deallocating fileInfoPtr would
1.446 + * leave an deallocated pointer on the thread local list.
1.447 + */
1.448 + FileThreadActionProc(fileInfoPtr,TCL_CHANNEL_THREAD_REMOVE);
1.449 + break;
1.450 + }
1.451 + }
1.452 + ckfree((char *)fileInfoPtr);
1.453 + return errorCode;
1.454 +}
1.455 +
1.456 +/*
1.457 + *----------------------------------------------------------------------
1.458 + *
1.459 + * FileSeekProc --
1.460 + *
1.461 + * Seeks on a file-based channel. Returns the new position.
1.462 + *
1.463 + * Results:
1.464 + * -1 if failed, the new position if successful. If failed, it
1.465 + * also sets *errorCodePtr to the error code.
1.466 + *
1.467 + * Side effects:
1.468 + * Moves the location at which the channel will be accessed in
1.469 + * future operations.
1.470 + *
1.471 + *----------------------------------------------------------------------
1.472 + */
1.473 +
1.474 +static int
1.475 +FileSeekProc(instanceData, offset, mode, errorCodePtr)
1.476 + ClientData instanceData; /* File state. */
1.477 + long offset; /* Offset to seek to. */
1.478 + int mode; /* Relative to where should we seek? */
1.479 + int *errorCodePtr; /* To store error code. */
1.480 +{
1.481 + FileInfo *infoPtr = (FileInfo *) instanceData;
1.482 + DWORD moveMethod;
1.483 + DWORD newPos, newPosHigh;
1.484 + DWORD oldPos, oldPosHigh;
1.485 +
1.486 + *errorCodePtr = 0;
1.487 + if (mode == SEEK_SET) {
1.488 + moveMethod = FILE_BEGIN;
1.489 + } else if (mode == SEEK_CUR) {
1.490 + moveMethod = FILE_CURRENT;
1.491 + } else {
1.492 + moveMethod = FILE_END;
1.493 + }
1.494 +
1.495 + /*
1.496 + * Save our current place in case we need to roll-back the seek.
1.497 + */
1.498 + oldPosHigh = (DWORD)0;
1.499 + oldPos = SetFilePointer(infoPtr->handle, (LONG)0, &oldPosHigh,
1.500 + FILE_CURRENT);
1.501 + if (oldPos == INVALID_SET_FILE_POINTER) {
1.502 + DWORD winError = GetLastError();
1.503 + if (winError != NO_ERROR) {
1.504 + TclWinConvertError(winError);
1.505 + *errorCodePtr = errno;
1.506 + return -1;
1.507 + }
1.508 + }
1.509 +
1.510 + newPosHigh = (DWORD)(offset < 0 ? -1 : 0);
1.511 + newPos = SetFilePointer(infoPtr->handle, (LONG) offset, &newPosHigh,
1.512 + moveMethod);
1.513 + if (newPos == INVALID_SET_FILE_POINTER) {
1.514 + DWORD winError = GetLastError();
1.515 + if (winError != NO_ERROR) {
1.516 + TclWinConvertError(winError);
1.517 + *errorCodePtr = errno;
1.518 + return -1;
1.519 + }
1.520 + }
1.521 +
1.522 + /*
1.523 + * Check for expressability in our return type, and roll-back otherwise.
1.524 + */
1.525 + if (newPosHigh != 0) {
1.526 + *errorCodePtr = EOVERFLOW;
1.527 + SetFilePointer(infoPtr->handle, (LONG)oldPos, &oldPosHigh, FILE_BEGIN);
1.528 + return -1;
1.529 + }
1.530 + return (int) newPos;
1.531 +}
1.532 +
1.533 +/*
1.534 + *----------------------------------------------------------------------
1.535 + *
1.536 + * FileWideSeekProc --
1.537 + *
1.538 + * Seeks on a file-based channel. Returns the new position.
1.539 + *
1.540 + * Results:
1.541 + * -1 if failed, the new position if successful. If failed, it
1.542 + * also sets *errorCodePtr to the error code.
1.543 + *
1.544 + * Side effects:
1.545 + * Moves the location at which the channel will be accessed in
1.546 + * future operations.
1.547 + *
1.548 + *----------------------------------------------------------------------
1.549 + */
1.550 +
1.551 +static Tcl_WideInt
1.552 +FileWideSeekProc(instanceData, offset, mode, errorCodePtr)
1.553 + ClientData instanceData; /* File state. */
1.554 + Tcl_WideInt offset; /* Offset to seek to. */
1.555 + int mode; /* Relative to where should we seek? */
1.556 + int *errorCodePtr; /* To store error code. */
1.557 +{
1.558 + FileInfo *infoPtr = (FileInfo *) instanceData;
1.559 + DWORD moveMethod;
1.560 + DWORD newPos, newPosHigh;
1.561 +
1.562 + *errorCodePtr = 0;
1.563 + if (mode == SEEK_SET) {
1.564 + moveMethod = FILE_BEGIN;
1.565 + } else if (mode == SEEK_CUR) {
1.566 + moveMethod = FILE_CURRENT;
1.567 + } else {
1.568 + moveMethod = FILE_END;
1.569 + }
1.570 +
1.571 + newPosHigh = (DWORD)(offset >> 32);
1.572 + newPos = SetFilePointer(infoPtr->handle, (LONG) offset, &newPosHigh,
1.573 + moveMethod);
1.574 + if (newPos == INVALID_SET_FILE_POINTER) {
1.575 + DWORD winError = GetLastError();
1.576 + if (winError != NO_ERROR) {
1.577 + TclWinConvertError(winError);
1.578 + *errorCodePtr = errno;
1.579 + return -1;
1.580 + }
1.581 + }
1.582 + return ((Tcl_WideInt) newPos) | (((Tcl_WideInt) newPosHigh) << 32);
1.583 +}
1.584 +
1.585 +/*
1.586 + *----------------------------------------------------------------------
1.587 + *
1.588 + * FileInputProc --
1.589 + *
1.590 + * Reads input from the IO channel into the buffer given. Returns
1.591 + * count of how many bytes were actually read, and an error indication.
1.592 + *
1.593 + * Results:
1.594 + * A count of how many bytes were read is returned and an error
1.595 + * indication is returned in an output argument.
1.596 + *
1.597 + * Side effects:
1.598 + * Reads input from the actual channel.
1.599 + *
1.600 + *----------------------------------------------------------------------
1.601 + */
1.602 +
1.603 +static int
1.604 +FileInputProc(instanceData, buf, bufSize, errorCode)
1.605 + ClientData instanceData; /* File state. */
1.606 + char *buf; /* Where to store data read. */
1.607 + int bufSize; /* How much space is available
1.608 + * in the buffer? */
1.609 + int *errorCode; /* Where to store error code. */
1.610 +{
1.611 + FileInfo *infoPtr;
1.612 + DWORD bytesRead;
1.613 +
1.614 + *errorCode = 0;
1.615 + infoPtr = (FileInfo *) instanceData;
1.616 +
1.617 + /*
1.618 + * Note that we will block on reads from a console buffer until a
1.619 + * full line has been entered. The only way I know of to get
1.620 + * around this is to write a console driver. We should probably
1.621 + * do this at some point, but for now, we just block. The same
1.622 + * problem exists for files being read over the network.
1.623 + */
1.624 +
1.625 + if (ReadFile(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize, &bytesRead,
1.626 + (LPOVERLAPPED) NULL) != FALSE) {
1.627 + return bytesRead;
1.628 + }
1.629 +
1.630 + TclWinConvertError(GetLastError());
1.631 + *errorCode = errno;
1.632 + if (errno == EPIPE) {
1.633 + return 0;
1.634 + }
1.635 + return -1;
1.636 +}
1.637 +
1.638 +/*
1.639 + *----------------------------------------------------------------------
1.640 + *
1.641 + * FileOutputProc --
1.642 + *
1.643 + * Writes the given output on the IO channel. Returns count of how
1.644 + * many characters were actually written, and an error indication.
1.645 + *
1.646 + * Results:
1.647 + * A count of how many characters were written is returned and an
1.648 + * error indication is returned in an output argument.
1.649 + *
1.650 + * Side effects:
1.651 + * Writes output on the actual channel.
1.652 + *
1.653 + *----------------------------------------------------------------------
1.654 + */
1.655 +
1.656 +static int
1.657 +FileOutputProc(instanceData, buf, toWrite, errorCode)
1.658 + ClientData instanceData; /* File state. */
1.659 + CONST char *buf; /* The data buffer. */
1.660 + int toWrite; /* How many bytes to write? */
1.661 + int *errorCode; /* Where to store error code. */
1.662 +{
1.663 + FileInfo *infoPtr = (FileInfo *) instanceData;
1.664 + DWORD bytesWritten;
1.665 +
1.666 + *errorCode = 0;
1.667 +
1.668 + /*
1.669 + * If we are writing to a file that was opened with O_APPEND, we need to
1.670 + * seek to the end of the file before writing the current buffer.
1.671 + */
1.672 +
1.673 + if (infoPtr->flags & FILE_APPEND) {
1.674 + SetFilePointer(infoPtr->handle, 0, NULL, FILE_END);
1.675 + }
1.676 +
1.677 + if (WriteFile(infoPtr->handle, (LPVOID) buf, (DWORD) toWrite, &bytesWritten,
1.678 + (LPOVERLAPPED) NULL) == FALSE) {
1.679 + TclWinConvertError(GetLastError());
1.680 + *errorCode = errno;
1.681 + return -1;
1.682 + }
1.683 + infoPtr->dirty = 1;
1.684 + return bytesWritten;
1.685 +}
1.686 +
1.687 +/*
1.688 + *----------------------------------------------------------------------
1.689 + *
1.690 + * FileWatchProc --
1.691 + *
1.692 + * Called by the notifier to set up to watch for events on this
1.693 + * channel.
1.694 + *
1.695 + * Results:
1.696 + * None.
1.697 + *
1.698 + * Side effects:
1.699 + * None.
1.700 + *
1.701 + *----------------------------------------------------------------------
1.702 + */
1.703 +
1.704 +static void
1.705 +FileWatchProc(instanceData, mask)
1.706 + ClientData instanceData; /* File state. */
1.707 + int mask; /* What events to watch for; OR-ed
1.708 + * combination of TCL_READABLE,
1.709 + * TCL_WRITABLE and TCL_EXCEPTION. */
1.710 +{
1.711 + FileInfo *infoPtr = (FileInfo *) instanceData;
1.712 + Tcl_Time blockTime = { 0, 0 };
1.713 +
1.714 + /*
1.715 + * Since the file is always ready for events, we set the block time
1.716 + * to zero so we will poll.
1.717 + */
1.718 +
1.719 + infoPtr->watchMask = mask & infoPtr->validMask;
1.720 + if (infoPtr->watchMask) {
1.721 + Tcl_SetMaxBlockTime(&blockTime);
1.722 + }
1.723 +}
1.724 +
1.725 +/*
1.726 + *----------------------------------------------------------------------
1.727 + *
1.728 + * FileGetHandleProc --
1.729 + *
1.730 + * Called from Tcl_GetChannelHandle to retrieve OS handles from
1.731 + * a file based channel.
1.732 + *
1.733 + * Results:
1.734 + * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
1.735 + * there is no handle for the specified direction.
1.736 + *
1.737 + * Side effects:
1.738 + * None.
1.739 + *
1.740 + *----------------------------------------------------------------------
1.741 + */
1.742 +
1.743 +static int
1.744 +FileGetHandleProc(instanceData, direction, handlePtr)
1.745 + ClientData instanceData; /* The file state. */
1.746 + int direction; /* TCL_READABLE or TCL_WRITABLE */
1.747 + ClientData *handlePtr; /* Where to store the handle. */
1.748 +{
1.749 + FileInfo *infoPtr = (FileInfo *) instanceData;
1.750 +
1.751 + if (direction & infoPtr->validMask) {
1.752 + *handlePtr = (ClientData) infoPtr->handle;
1.753 + return TCL_OK;
1.754 + } else {
1.755 + return TCL_ERROR;
1.756 + }
1.757 +}
1.758 +
1.759 +
1.760 +/*
1.761 + *----------------------------------------------------------------------
1.762 + *
1.763 + * TclpOpenFileChannel --
1.764 + *
1.765 + * Open an File based channel on Unix systems.
1.766 + *
1.767 + * Results:
1.768 + * The new channel or NULL. If NULL, the output argument
1.769 + * errorCodePtr is set to a POSIX error.
1.770 + *
1.771 + * Side effects:
1.772 + * May open the channel and may cause creation of a file on the
1.773 + * file system.
1.774 + *
1.775 + *----------------------------------------------------------------------
1.776 + */
1.777 +
1.778 +Tcl_Channel
1.779 +TclpOpenFileChannel(interp, pathPtr, mode, permissions)
1.780 + Tcl_Interp *interp; /* Interpreter for error reporting;
1.781 + * can be NULL. */
1.782 + Tcl_Obj *pathPtr; /* Name of file to open. */
1.783 + int mode; /* POSIX mode. */
1.784 + int permissions; /* If the open involves creating a
1.785 + * file, with what modes to create
1.786 + * it? */
1.787 +{
1.788 + Tcl_Channel channel = 0;
1.789 + int channelPermissions;
1.790 + DWORD accessMode, createMode, shareMode, flags;
1.791 + CONST TCHAR *nativeName;
1.792 + HANDLE handle;
1.793 + char channelName[16 + TCL_INTEGER_SPACE];
1.794 + TclFile readFile = NULL;
1.795 + TclFile writeFile = NULL;
1.796 +
1.797 + nativeName = (TCHAR*) Tcl_FSGetNativePath(pathPtr);
1.798 + if (nativeName == NULL) {
1.799 + return NULL;
1.800 + }
1.801 +
1.802 + switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
1.803 + case O_RDONLY:
1.804 + accessMode = GENERIC_READ;
1.805 + channelPermissions = TCL_READABLE;
1.806 + break;
1.807 + case O_WRONLY:
1.808 + accessMode = GENERIC_WRITE;
1.809 + channelPermissions = TCL_WRITABLE;
1.810 + break;
1.811 + case O_RDWR:
1.812 + accessMode = (GENERIC_READ | GENERIC_WRITE);
1.813 + channelPermissions = (TCL_READABLE | TCL_WRITABLE);
1.814 + break;
1.815 + default:
1.816 + panic("TclpOpenFileChannel: invalid mode value");
1.817 + break;
1.818 + }
1.819 +
1.820 + /*
1.821 + * Map the creation flags to the NT create mode.
1.822 + */
1.823 +
1.824 + switch (mode & (O_CREAT | O_EXCL | O_TRUNC)) {
1.825 + case (O_CREAT | O_EXCL):
1.826 + case (O_CREAT | O_EXCL | O_TRUNC):
1.827 + createMode = CREATE_NEW;
1.828 + break;
1.829 + case (O_CREAT | O_TRUNC):
1.830 + createMode = CREATE_ALWAYS;
1.831 + break;
1.832 + case O_CREAT:
1.833 + createMode = OPEN_ALWAYS;
1.834 + break;
1.835 + case O_TRUNC:
1.836 + case (O_TRUNC | O_EXCL):
1.837 + createMode = TRUNCATE_EXISTING;
1.838 + break;
1.839 + default:
1.840 + createMode = OPEN_EXISTING;
1.841 + break;
1.842 + }
1.843 +
1.844 + /*
1.845 + * If the file is being created, get the file attributes from the
1.846 + * permissions argument, else use the existing file attributes.
1.847 + */
1.848 +
1.849 + if (mode & O_CREAT) {
1.850 + if (permissions & S_IWRITE) {
1.851 + flags = FILE_ATTRIBUTE_NORMAL;
1.852 + } else {
1.853 + flags = FILE_ATTRIBUTE_READONLY;
1.854 + }
1.855 + } else {
1.856 + flags = (*tclWinProcs->getFileAttributesProc)(nativeName);
1.857 + if (flags == 0xFFFFFFFF) {
1.858 + flags = 0;
1.859 + }
1.860 + }
1.861 +
1.862 + /*
1.863 + * Set up the file sharing mode. We want to allow simultaneous access.
1.864 + */
1.865 +
1.866 + shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
1.867 +
1.868 + /*
1.869 + * Now we get to create the file.
1.870 + */
1.871 +
1.872 + handle = (*tclWinProcs->createFileProc)(nativeName, accessMode,
1.873 + shareMode, NULL, createMode, flags, (HANDLE) NULL);
1.874 +
1.875 + if (handle == INVALID_HANDLE_VALUE) {
1.876 + DWORD err;
1.877 + err = GetLastError();
1.878 + if ((err & 0xffffL) == ERROR_OPEN_FAILED) {
1.879 + err = (mode & O_CREAT) ? ERROR_FILE_EXISTS : ERROR_FILE_NOT_FOUND;
1.880 + }
1.881 + TclWinConvertError(err);
1.882 + if (interp != (Tcl_Interp *) NULL) {
1.883 + Tcl_AppendResult(interp, "couldn't open \"",
1.884 + Tcl_GetString(pathPtr), "\": ",
1.885 + Tcl_PosixError(interp), (char *) NULL);
1.886 + }
1.887 + return NULL;
1.888 + }
1.889 +
1.890 + channel = NULL;
1.891 +
1.892 + switch ( FileGetType(handle) ) {
1.893 + case FILE_TYPE_SERIAL:
1.894 + /*
1.895 + * Reopen channel for OVERLAPPED operation
1.896 + * Normally this shouldn't fail, because the channel exists
1.897 + */
1.898 + handle = TclWinSerialReopen(handle, nativeName, accessMode);
1.899 + if (handle == INVALID_HANDLE_VALUE) {
1.900 + TclWinConvertError(GetLastError());
1.901 + if (interp != (Tcl_Interp *) NULL) {
1.902 + Tcl_AppendResult(interp, "couldn't reopen serial \"",
1.903 + Tcl_GetString(pathPtr), "\": ",
1.904 + Tcl_PosixError(interp), (char *) NULL);
1.905 + }
1.906 + return NULL;
1.907 + }
1.908 + channel = TclWinOpenSerialChannel(handle, channelName,
1.909 + channelPermissions);
1.910 + break;
1.911 + case FILE_TYPE_CONSOLE:
1.912 + channel = TclWinOpenConsoleChannel(handle, channelName,
1.913 + channelPermissions);
1.914 + break;
1.915 + case FILE_TYPE_PIPE:
1.916 + if (channelPermissions & TCL_READABLE) {
1.917 + readFile = TclWinMakeFile(handle);
1.918 + }
1.919 + if (channelPermissions & TCL_WRITABLE) {
1.920 + writeFile = TclWinMakeFile(handle);
1.921 + }
1.922 + channel = TclpCreateCommandChannel(readFile, writeFile, NULL, 0, NULL);
1.923 + break;
1.924 + case FILE_TYPE_CHAR:
1.925 + case FILE_TYPE_DISK:
1.926 + case FILE_TYPE_UNKNOWN:
1.927 + channel = TclWinOpenFileChannel(handle, channelName,
1.928 + channelPermissions,
1.929 + (mode & O_APPEND) ? FILE_APPEND : 0);
1.930 + break;
1.931 +
1.932 + default:
1.933 + /*
1.934 + * The handle is of an unknown type, probably /dev/nul equivalent
1.935 + * or possibly a closed handle.
1.936 + */
1.937 +
1.938 + channel = NULL;
1.939 + Tcl_AppendResult(interp, "couldn't open \"",
1.940 + Tcl_GetString(pathPtr), "\": ",
1.941 + "bad file type", (char *) NULL);
1.942 + break;
1.943 + }
1.944 +
1.945 + return channel;
1.946 +}
1.947 +
1.948 +/*
1.949 + *----------------------------------------------------------------------
1.950 + *
1.951 + * Tcl_MakeFileChannel --
1.952 + *
1.953 + * Creates a Tcl_Channel from an existing platform specific file
1.954 + * handle.
1.955 + *
1.956 + * Results:
1.957 + * The Tcl_Channel created around the preexisting file.
1.958 + *
1.959 + * Side effects:
1.960 + * None.
1.961 + *
1.962 + *----------------------------------------------------------------------
1.963 + */
1.964 +
1.965 +Tcl_Channel
1.966 +Tcl_MakeFileChannel(rawHandle, mode)
1.967 + ClientData rawHandle; /* OS level handle */
1.968 + int mode; /* ORed combination of TCL_READABLE and
1.969 + * TCL_WRITABLE to indicate file mode. */
1.970 +{
1.971 +#ifdef HAVE_NO_SEH
1.972 + EXCEPTION_REGISTRATION registration;
1.973 +#endif
1.974 + char channelName[16 + TCL_INTEGER_SPACE];
1.975 + Tcl_Channel channel = NULL;
1.976 + HANDLE handle = (HANDLE) rawHandle;
1.977 + HANDLE dupedHandle;
1.978 + TclFile readFile = NULL;
1.979 + TclFile writeFile = NULL;
1.980 + BOOL result;
1.981 +
1.982 + if (mode == 0) {
1.983 + return NULL;
1.984 + }
1.985 +
1.986 + switch (FileGetType(handle))
1.987 + {
1.988 + case FILE_TYPE_SERIAL:
1.989 + channel = TclWinOpenSerialChannel(handle, channelName, mode);
1.990 + break;
1.991 + case FILE_TYPE_CONSOLE:
1.992 + channel = TclWinOpenConsoleChannel(handle, channelName, mode);
1.993 + break;
1.994 + case FILE_TYPE_PIPE:
1.995 + if (mode & TCL_READABLE)
1.996 + {
1.997 + readFile = TclWinMakeFile(handle);
1.998 + }
1.999 + if (mode & TCL_WRITABLE)
1.1000 + {
1.1001 + writeFile = TclWinMakeFile(handle);
1.1002 + }
1.1003 + channel = TclpCreateCommandChannel(readFile, writeFile, NULL, 0, NULL);
1.1004 + break;
1.1005 +
1.1006 + case FILE_TYPE_DISK:
1.1007 + case FILE_TYPE_CHAR:
1.1008 + channel = TclWinOpenFileChannel(handle, channelName, mode, 0);
1.1009 + break;
1.1010 +
1.1011 + case FILE_TYPE_UNKNOWN:
1.1012 + default:
1.1013 + /*
1.1014 + * The handle is of an unknown type. Test the validity of this OS
1.1015 + * handle by duplicating it, then closing the dupe. The Win32 API
1.1016 + * doesn't provide an IsValidHandle() function, so we have to emulate
1.1017 + * it here. This test will not work on a console handle reliably,
1.1018 + * which is why we can't test every handle that comes into this
1.1019 + * function in this way.
1.1020 + */
1.1021 +
1.1022 + result = DuplicateHandle(GetCurrentProcess(), handle,
1.1023 + GetCurrentProcess(), &dupedHandle, 0, FALSE,
1.1024 + DUPLICATE_SAME_ACCESS);
1.1025 +
1.1026 + if (result == 0) {
1.1027 + /*
1.1028 + * Unable to make a duplicate. It's definately invalid at this
1.1029 + * point.
1.1030 + */
1.1031 +
1.1032 + return NULL;
1.1033 + }
1.1034 +
1.1035 + /*
1.1036 + * Use structured exception handling (Win32 SEH) to protect the close
1.1037 + * of this duped handle which might throw EXCEPTION_INVALID_HANDLE.
1.1038 + */
1.1039 +
1.1040 + result = 0;
1.1041 +#ifndef HAVE_NO_SEH
1.1042 + __try {
1.1043 + CloseHandle(dupedHandle);
1.1044 + result = 1;
1.1045 + } __except (EXCEPTION_EXECUTE_HANDLER) {}
1.1046 +#else
1.1047 + /*
1.1048 + * Don't have SEH available, do things the hard way.
1.1049 + * Note that this needs to be one block of asm, to avoid stack
1.1050 + * imbalance; also, it is illegal for one asm block to contain
1.1051 + * a jump to another.
1.1052 + */
1.1053 +
1.1054 + __asm__ __volatile__ (
1.1055 +
1.1056 + /*
1.1057 + * Pick up parameters before messing with the stack
1.1058 + */
1.1059 +
1.1060 + "movl %[dupedHandle], %%ebx" "\n\t"
1.1061 +
1.1062 + /*
1.1063 + * Construct an EXCEPTION_REGISTRATION to protect the
1.1064 + * call to CloseHandle
1.1065 + */
1.1066 + "leal %[registration], %%edx" "\n\t"
1.1067 + "movl %%fs:0, %%eax" "\n\t"
1.1068 + "movl %%eax, 0x0(%%edx)" "\n\t" /* link */
1.1069 + "leal 1f, %%eax" "\n\t"
1.1070 + "movl %%eax, 0x4(%%edx)" "\n\t" /* handler */
1.1071 + "movl %%ebp, 0x8(%%edx)" "\n\t" /* ebp */
1.1072 + "movl %%esp, 0xc(%%edx)" "\n\t" /* esp */
1.1073 + "movl $0, 0x10(%%edx)" "\n\t" /* status */
1.1074 +
1.1075 + /* Link the EXCEPTION_REGISTRATION on the chain */
1.1076 +
1.1077 + "movl %%edx, %%fs:0" "\n\t"
1.1078 +
1.1079 + /* Call CloseHandle( dupedHandle ) */
1.1080 +
1.1081 + "pushl %%ebx" "\n\t"
1.1082 + "call _CloseHandle@4" "\n\t"
1.1083 +
1.1084 + /*
1.1085 + * Come here on normal exit. Recover the EXCEPTION_REGISTRATION
1.1086 + * and put a TRUE status return into it.
1.1087 + */
1.1088 +
1.1089 + "movl %%fs:0, %%edx" "\n\t"
1.1090 + "movl $1, %%eax" "\n\t"
1.1091 + "movl %%eax, 0x10(%%edx)" "\n\t"
1.1092 + "jmp 2f" "\n"
1.1093 +
1.1094 + /*
1.1095 + * Come here on an exception. Recover the EXCEPTION_REGISTRATION
1.1096 + */
1.1097 +
1.1098 + "1:" "\t"
1.1099 + "movl %%fs:0, %%edx" "\n\t"
1.1100 + "movl 0x8(%%edx), %%edx" "\n\t"
1.1101 +
1.1102 + /*
1.1103 + * Come here however we exited. Restore context from the
1.1104 + * EXCEPTION_REGISTRATION in case the stack is unbalanced.
1.1105 + */
1.1106 +
1.1107 + "2:" "\t"
1.1108 + "movl 0xc(%%edx), %%esp" "\n\t"
1.1109 + "movl 0x8(%%edx), %%ebp" "\n\t"
1.1110 + "movl 0x0(%%edx), %%eax" "\n\t"
1.1111 + "movl %%eax, %%fs:0" "\n\t"
1.1112 +
1.1113 + :
1.1114 + /* No outputs */
1.1115 + :
1.1116 + [registration] "m" (registration),
1.1117 + [dupedHandle] "m" (dupedHandle)
1.1118 + :
1.1119 + "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"
1.1120 + );
1.1121 + result = registration.status;
1.1122 +
1.1123 +#endif
1.1124 + if (result == FALSE) {
1.1125 + return NULL;
1.1126 + }
1.1127 +
1.1128 + /* Fall through, the handle is valid. */
1.1129 +
1.1130 + /*
1.1131 + * Create the undefined channel, anyways, because we know the handle
1.1132 + * is valid to something.
1.1133 + */
1.1134 +
1.1135 + channel = TclWinOpenFileChannel(handle, channelName, mode, 0);
1.1136 + }
1.1137 +
1.1138 + return channel;
1.1139 +}
1.1140 +
1.1141 +/*
1.1142 + *----------------------------------------------------------------------
1.1143 + *
1.1144 + * TclpGetDefaultStdChannel --
1.1145 + *
1.1146 + * Constructs a channel for the specified standard OS handle.
1.1147 + *
1.1148 + * Results:
1.1149 + * Returns the specified default standard channel, or NULL.
1.1150 + *
1.1151 + * Side effects:
1.1152 + * May cause the creation of a standard channel and the underlying
1.1153 + * file.
1.1154 + *
1.1155 + *----------------------------------------------------------------------
1.1156 + */
1.1157 +
1.1158 +Tcl_Channel
1.1159 +TclpGetDefaultStdChannel(type)
1.1160 + int type; /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
1.1161 +{
1.1162 + Tcl_Channel channel;
1.1163 + HANDLE handle;
1.1164 + int mode;
1.1165 + char *bufMode;
1.1166 + DWORD handleId; /* Standard handle to retrieve. */
1.1167 +
1.1168 +
1.1169 + switch (type) {
1.1170 + case TCL_STDIN:
1.1171 + handleId = STD_INPUT_HANDLE;
1.1172 + mode = TCL_READABLE;
1.1173 + bufMode = "line";
1.1174 + break;
1.1175 + case TCL_STDOUT:
1.1176 + handleId = STD_OUTPUT_HANDLE;
1.1177 + mode = TCL_WRITABLE;
1.1178 + bufMode = "line";
1.1179 + break;
1.1180 + case TCL_STDERR:
1.1181 + handleId = STD_ERROR_HANDLE;
1.1182 + mode = TCL_WRITABLE;
1.1183 + bufMode = "none";
1.1184 + break;
1.1185 + default:
1.1186 + panic("TclGetDefaultStdChannel: Unexpected channel type");
1.1187 + break;
1.1188 + }
1.1189 +
1.1190 + handle = GetStdHandle(handleId);
1.1191 +
1.1192 + /*
1.1193 + * Note that we need to check for 0 because Windows may return 0 if this
1.1194 + * is not a console mode application, even though this is not a valid
1.1195 + * handle.
1.1196 + */
1.1197 +
1.1198 + if ((handle == INVALID_HANDLE_VALUE) || (handle == 0)) {
1.1199 + return (Tcl_Channel) NULL;
1.1200 + }
1.1201 +
1.1202 + channel = Tcl_MakeFileChannel(handle, mode);
1.1203 +
1.1204 + if (channel == NULL) {
1.1205 + return (Tcl_Channel) NULL;
1.1206 + }
1.1207 +
1.1208 + /*
1.1209 + * Set up the normal channel options for stdio handles.
1.1210 + */
1.1211 +
1.1212 + if ((Tcl_SetChannelOption((Tcl_Interp *) NULL, channel, "-translation",
1.1213 + "auto") == TCL_ERROR)
1.1214 + || (Tcl_SetChannelOption((Tcl_Interp *) NULL, channel, "-eofchar",
1.1215 + "\032 {}") == TCL_ERROR)
1.1216 + || (Tcl_SetChannelOption((Tcl_Interp *) NULL, channel,
1.1217 + "-buffering", bufMode) == TCL_ERROR)) {
1.1218 + Tcl_Close((Tcl_Interp *) NULL, channel);
1.1219 + return (Tcl_Channel) NULL;
1.1220 + }
1.1221 + return channel;
1.1222 +}
1.1223 +
1.1224 +
1.1225 +
1.1226 +/*
1.1227 + *----------------------------------------------------------------------
1.1228 + *
1.1229 + * TclWinOpenFileChannel --
1.1230 + *
1.1231 + * Constructs a File channel for the specified standard OS handle.
1.1232 + * This is a helper function to break up the construction of
1.1233 + * channels into File, Console, or Serial.
1.1234 + *
1.1235 + * Results:
1.1236 + * Returns the new channel, or NULL.
1.1237 + *
1.1238 + * Side effects:
1.1239 + * May open the channel and may cause creation of a file on the
1.1240 + * file system.
1.1241 + *
1.1242 + *----------------------------------------------------------------------
1.1243 + */
1.1244 +
1.1245 +Tcl_Channel
1.1246 +TclWinOpenFileChannel(handle, channelName, permissions, appendMode)
1.1247 + HANDLE handle;
1.1248 + char *channelName;
1.1249 + int permissions;
1.1250 + int appendMode;
1.1251 +{
1.1252 + FileInfo *infoPtr;
1.1253 + ThreadSpecificData *tsdPtr;
1.1254 +
1.1255 + tsdPtr = FileInit();
1.1256 +
1.1257 + /*
1.1258 + * See if a channel with this handle already exists.
1.1259 + */
1.1260 +
1.1261 + for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL;
1.1262 + infoPtr = infoPtr->nextPtr) {
1.1263 + if (infoPtr->handle == (HANDLE) handle) {
1.1264 + return (permissions == infoPtr->validMask) ? infoPtr->channel : NULL;
1.1265 + }
1.1266 + }
1.1267 +
1.1268 + infoPtr = (FileInfo *) ckalloc((unsigned) sizeof(FileInfo));
1.1269 + /* TIP #218. Removed the code inserting the new structure
1.1270 + * into the global list. This is now handled in the thread
1.1271 + * action callbacks, and only there.
1.1272 + */
1.1273 + infoPtr->nextPtr = NULL;
1.1274 + infoPtr->validMask = permissions;
1.1275 + infoPtr->watchMask = 0;
1.1276 + infoPtr->flags = appendMode;
1.1277 + infoPtr->handle = handle;
1.1278 + infoPtr->dirty = 0;
1.1279 + wsprintfA(channelName, "file%lx", (int) infoPtr);
1.1280 +
1.1281 + infoPtr->channel = Tcl_CreateChannel(&fileChannelType, channelName,
1.1282 + (ClientData) infoPtr, permissions);
1.1283 +
1.1284 + /*
1.1285 + * Files have default translation of AUTO and ^Z eof char, which
1.1286 + * means that a ^Z will be accepted as EOF when reading.
1.1287 + */
1.1288 +
1.1289 + Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
1.1290 + Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}");
1.1291 +
1.1292 + return infoPtr->channel;
1.1293 +}
1.1294 +
1.1295 +
1.1296 +/*
1.1297 + *----------------------------------------------------------------------
1.1298 + *
1.1299 + * TclWinFlushDirtyChannels --
1.1300 + *
1.1301 + * Flush all dirty channels to disk, so that requesting the
1.1302 + * size of any file returns the correct value.
1.1303 + *
1.1304 + * Results:
1.1305 + * None.
1.1306 + *
1.1307 + * Side effects:
1.1308 + * Information is actually written to disk now, rather than
1.1309 + * later. Don't call this too often, or there will be a
1.1310 + * performance hit (i.e. only call when we need to ask for
1.1311 + * the size of a file).
1.1312 + *
1.1313 + *----------------------------------------------------------------------
1.1314 + */
1.1315 +
1.1316 +void
1.1317 +TclWinFlushDirtyChannels ()
1.1318 +{
1.1319 + FileInfo *infoPtr;
1.1320 + ThreadSpecificData *tsdPtr;
1.1321 +
1.1322 + tsdPtr = FileInit();
1.1323 +
1.1324 + /*
1.1325 + * Flush all channels which are dirty, i.e. may have data pending
1.1326 + * in the OS
1.1327 + */
1.1328 +
1.1329 + for (infoPtr = tsdPtr->firstFilePtr;
1.1330 + infoPtr != NULL;
1.1331 + infoPtr = infoPtr->nextPtr) {
1.1332 + if (infoPtr->dirty) {
1.1333 + FlushFileBuffers(infoPtr->handle);
1.1334 + infoPtr->dirty = 0;
1.1335 + }
1.1336 + }
1.1337 +}
1.1338 +
1.1339 +/*
1.1340 + *----------------------------------------------------------------------
1.1341 + *
1.1342 + * FileThreadActionProc --
1.1343 + *
1.1344 + * Insert or remove any thread local refs to this channel.
1.1345 + *
1.1346 + * Results:
1.1347 + * None.
1.1348 + *
1.1349 + * Side effects:
1.1350 + * Changes thread local list of valid channels.
1.1351 + *
1.1352 + *----------------------------------------------------------------------
1.1353 + */
1.1354 +
1.1355 +static void
1.1356 +FileThreadActionProc (instanceData, action)
1.1357 + ClientData instanceData;
1.1358 + int action;
1.1359 +{
1.1360 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.1361 + FileInfo *infoPtr = (FileInfo *) instanceData;
1.1362 +
1.1363 + if (action == TCL_CHANNEL_THREAD_INSERT) {
1.1364 + infoPtr->nextPtr = tsdPtr->firstFilePtr;
1.1365 + tsdPtr->firstFilePtr = infoPtr;
1.1366 + } else {
1.1367 + FileInfo **nextPtrPtr;
1.1368 + int removed = 0;
1.1369 +
1.1370 + for (nextPtrPtr = &(tsdPtr->firstFilePtr); (*nextPtrPtr) != NULL;
1.1371 + nextPtrPtr = &((*nextPtrPtr)->nextPtr)) {
1.1372 + if ((*nextPtrPtr) == infoPtr) {
1.1373 + (*nextPtrPtr) = infoPtr->nextPtr;
1.1374 + removed = 1;
1.1375 + break;
1.1376 + }
1.1377 + }
1.1378 +
1.1379 + /*
1.1380 + * This could happen if the channel was created in one thread
1.1381 + * and then moved to another without updating the thread
1.1382 + * local data in each thread.
1.1383 + */
1.1384 +
1.1385 + if (!removed) {
1.1386 + panic("file info ptr not on thread channel list");
1.1387 + }
1.1388 + }
1.1389 +}
1.1390 +
1.1391 +
1.1392 +/*
1.1393 + *----------------------------------------------------------------------
1.1394 + *
1.1395 + * FileGetType --
1.1396 + *
1.1397 + * Given a file handle, return its type
1.1398 + *
1.1399 + * Results:
1.1400 + * None.
1.1401 + *
1.1402 + * Side effects:
1.1403 + * None.
1.1404 + *
1.1405 + *----------------------------------------------------------------------
1.1406 + */
1.1407 +
1.1408 +DWORD
1.1409 +FileGetType(handle)
1.1410 + HANDLE handle; /* Opened file handle */
1.1411 +{
1.1412 + DWORD type;
1.1413 + DWORD consoleParams;
1.1414 + DCB dcb;
1.1415 +
1.1416 + type = GetFileType(handle);
1.1417 +
1.1418 + /*
1.1419 + * If the file is a character device, we need to try to figure out
1.1420 + * whether it is a serial port, a console, or something else. We
1.1421 + * test for the console case first because this is more common.
1.1422 + */
1.1423 +
1.1424 + if (type == FILE_TYPE_CHAR || (type == FILE_TYPE_UNKNOWN && !GetLastError())) {
1.1425 + if (GetConsoleMode(handle, &consoleParams)) {
1.1426 + type = FILE_TYPE_CONSOLE;
1.1427 + } else {
1.1428 + dcb.DCBlength = sizeof( DCB ) ;
1.1429 + if (GetCommState(handle, &dcb)) {
1.1430 + type = FILE_TYPE_SERIAL;
1.1431 + }
1.1432 + }
1.1433 + }
1.1434 +
1.1435 + return type;
1.1436 +}