os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinChan.c
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/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 +}