diff -urN /home/chaffee/tcl/tcl8.0p2-orig/generic/tclAsync.c tcl8.0/generic/tclAsync.c --- /home/chaffee/tcl/tcl8.0p2-orig/generic/tclAsync.c Wed Oct 23 09:01:34 1996 +++ tcl8.0/generic/tclAsync.c Tue Aug 26 11:59:04 1997 @@ -126,6 +126,7 @@ ((AsyncHandler *) async)->ready = 1; if (!asyncActive) { asyncReady = 1; + TclPlatformAsyncMark(async); } } diff -urN /home/chaffee/tcl/tcl8.0p2-orig/generic/tclIO.c tcl8.0/generic/tclIO.c --- /home/chaffee/tcl/tcl8.0p2-orig/generic/tclIO.c Fri Sep 4 04:15:16 1998 +++ tcl8.0/generic/tclIO.c Fri Aug 28 10:47:34 1998 @@ -161,6 +161,7 @@ * for this channel. */ int interestMask; /* Mask of all events this channel has * handlers for. */ + struct Channel *prevChanPtr;/* Prev in list of channels currently open. */ struct Channel *nextChanPtr;/* Next in list of channels currently open. */ EventScriptRecord *scriptRecordPtr; /* Chain of all scripts registered for @@ -342,7 +343,7 @@ Channel *chanPtr, int calledFromAsyncFlush)); static Tcl_HashTable *GetChannelTable _ANSI_ARGS_((Tcl_Interp *interp)); static int GetEOL _ANSI_ARGS_((Channel *chanPtr)); -static int GetInput _ANSI_ARGS_((Channel *chanPtr)); +static int GetInput _ANSI_ARGS_((Channel *chanPtr, int nwanted)); static void RecycleBuffer _ANSI_ARGS_((Channel *chanPtr, ChannelBuffer *bufPtr, int mustDiscard)); static int ScanBufferForEOL _ANSI_ARGS_((Channel *chanPtr, @@ -651,12 +652,12 @@ ClientData clientData; /* NULL - unused. */ { Channel *chanPtr; /* Iterates over open channels. */ - Channel *nextChanPtr; /* Iterates over open channels. */ + Channel *nextChanPtr, *prevChanPtr; /* Iterates over open channels. */ for (chanPtr = firstChanPtr; chanPtr != (Channel *) NULL; - chanPtr = nextChanPtr) { - nextChanPtr = chanPtr->nextChanPtr; + chanPtr = nextChanPtr) { + prevChanPtr = chanPtr->prevChanPtr; /* * Set the channel back into blocking mode to ensure that we wait @@ -714,6 +715,14 @@ chanPtr->instanceData = (ClientData) NULL; chanPtr->flags |= CHANNEL_DEAD; } + if (prevChanPtr) { + nextChanPtr = prevChanPtr->nextChanPtr; + } else { + nextChanPtr = firstChanPtr; + } + if (nextChanPtr == chanPtr) { + nextChanPtr = chanPtr->nextChanPtr; + } } /* @@ -1223,7 +1232,11 @@ * in the list on exit. */ + chanPtr->prevChanPtr = NULL; chanPtr->nextChanPtr = firstChanPtr; + if (firstChanPtr) { + firstChanPtr->prevChanPtr = chanPtr; + } firstChanPtr = chanPtr; if (!channelExitHandlerCreated) { @@ -1826,6 +1839,9 @@ if (chanPtr == firstChanPtr) { firstChanPtr = chanPtr->nextChanPtr; + if (firstChanPtr) { + firstChanPtr->prevChanPtr = NULL; + } } else { for (prevChanPtr = firstChanPtr; (prevChanPtr != (Channel *) NULL) && @@ -1837,6 +1853,9 @@ panic("FlushChannel: damaged channel list"); } prevChanPtr->nextChanPtr = chanPtr->nextChanPtr; + if (chanPtr->nextChanPtr) { + chanPtr->nextChanPtr->prevChanPtr = prevChanPtr; + } } /* @@ -2396,8 +2415,9 @@ */ static int -GetInput(chanPtr) +GetInput(chanPtr, nwanted) Channel *chanPtr; /* Channel to read input from. */ + int nwanted; { int toRead; /* How much to read? */ int result; /* Of calling driver. */ @@ -2443,6 +2463,9 @@ chanPtr->inQueueTail = bufPtr; bufPtr->nextPtr = (ChannelBuffer *) NULL; } + if ((nwanted != -1) && (toRead > nwanted)) { + toRead = nwanted; + } /* * If EOF is set, we should avoid calling the driver because on some @@ -3015,7 +3038,7 @@ } chanPtr->flags &= (~(CHANNEL_BLOCKED)); } - if (GetInput(chanPtr) != 0) { + if (GetInput(chanPtr, -1) != 0) { goto blocked; } } @@ -3148,7 +3171,7 @@ } chanPtr->flags &= (~(CHANNEL_BLOCKED)); } - result = GetInput(chanPtr); + result = GetInput(chanPtr, toRead - copied); if (result != 0) { if (result == EAGAIN) { return copied; @@ -4761,7 +4784,7 @@ chPtr != (ChannelHandler *) NULL; chPtr = chPtr->nextPtr) { if ((chPtr->chanPtr == chanPtr) && (chPtr->clientData == clientData) - && (chPtr->proc == proc)) { + && (chPtr->proc == proc)) { break; } prevChPtr = chPtr; diff -urN /home/chaffee/tcl/tcl8.0p2-orig/generic/tclInt.h tcl8.0/generic/tclInt.h --- /home/chaffee/tcl/tcl8.0p2-orig/generic/tclInt.h Tue Aug 12 17:07:02 1997 +++ tcl8.0/generic/tclInt.h Tue Aug 26 12:00:12 1997 @@ -1446,6 +1446,8 @@ EXTERN int TclParseQuotes _ANSI_ARGS_((Tcl_Interp *interp, char *string, int termChar, int flags, char **termPtr, ParseValue *pvPtr)); +EXTERN void TclPlatformAsyncMark _ANSI_ARGS_(( + Tcl_AsyncHandler async)); EXTERN void TclPlatformExit _ANSI_ARGS_((int status)); EXTERN void TclPlatformInit _ANSI_ARGS_((Tcl_Interp *interp)); EXTERN char * TclPrecTraceProc _ANSI_ARGS_((ClientData clientData, diff -urN /home/chaffee/tcl/tcl8.0p2-orig/mac/tclMacPort.h tcl8.0/mac/tclMacPort.h --- /home/chaffee/tcl/tcl8.0p2-orig/mac/tclMacPort.h Mon Aug 11 16:37:15 1997 +++ tcl8.0/mac/tclMacPort.h Tue Aug 26 11:57:56 1997 @@ -225,6 +225,7 @@ */ #define TclCreateCommandChannel(out, in, err, num, pidPtr) NULL #define TclClosePipeFile(x) +#define TclPlatformAsyncMark(async) /* * These definitions force putenv & company to use the version diff -urN /home/chaffee/tcl/tcl8.0p2-orig/unix/tclUnixPort.h tcl8.0/unix/tclUnixPort.h --- /home/chaffee/tcl/tcl8.0p2-orig/unix/tclUnixPort.h Fri Aug 1 10:49:30 1997 +++ tcl8.0/unix/tclUnixPort.h Tue Aug 26 11:58:16 1997 @@ -434,6 +434,11 @@ #define TclPlatformExit(status) exit(status) /* + * Doesn't do anything on Unix systems + */ +#define TclPlatformAsyncMark(async) + +/* * The following functions always succeeds under Unix. */ diff -urN /home/chaffee/tcl/tcl8.0p2-orig/win/makefile.vc tcl8.0/win/makefile.vc --- /home/chaffee/tcl/tcl8.0p2-orig/win/makefile.vc Fri Sep 4 04:17:57 1998 +++ tcl8.0/win/makefile.vc Fri Aug 28 10:48:56 1998 @@ -34,8 +34,13 @@ ROOT = .. TMPDIR = . + +!IFNDEF TOOLS32 TOOLS32 = c:\msdev +!ENDIF +!IFNDEF TOOLS16 TOOLS16 = c:\msvc +!ENDIF # Set this to the appropriate value of /MACHINE: for your platform MACHINE = IX86 @@ -71,7 +76,8 @@ CAT32 = cat32.exe TCLSHOBJS = \ - $(TMPDIR)\tclAppInit.obj + $(TMPDIR)\tclAppInit.obj \ + $(TMPDIR)\msjexhnd.obj TCLTESTOBJS = \ $(TMPDIR)\tclTest.obj \ @@ -136,6 +142,7 @@ $(TMPDIR)\tclWinMtherr.obj \ $(TMPDIR)\tclWinNotify.obj \ $(TMPDIR)\tclWinPipe.obj \ + $(TMPDIR)\tclWinReader.obj \ $(TMPDIR)\tclWinSock.obj \ $(TMPDIR)\tclWinTime.obj @@ -149,6 +156,8 @@ rc16 = $(TOOLS16)\bin\rc.exe include16 = -I$(TOOLS16)\include +MACDIR = $(ROOT)\mac +UNIXDIR = $(ROOT)\unix WINDIR = $(ROOT)\win GENERICDIR = $(ROOT)\generic @@ -191,7 +200,12 @@ libcdll = crtdll.lib !ELSE libc = libc.lib oldnames.lib +!IFDEF NODEBUG libcdll = msvcrt.lib oldnames.lib +!ELSE +#libcdll = msvcrtd.lib oldnames.lib +libcdll = msvcrt.lib oldnames.lib +!ENDIF !ENDIF baselibs = kernel32.lib $(optlibs) advapi32.lib user32.lib @@ -357,6 +371,9 @@ {$(WINDIR)}.c{$(TMPDIR)}.obj: $(cc32) $(TCL_CFLAGS) -Fo$(TMPDIR)\ $< +{$(WINDIR)}.cpp{$(TMPDIR)}.obj: + $(cc32) $(TCL_CFLAGS) -Fo$(TMPDIR)\ $< + {$(GENERICDIR)}.c{$(TMPDIR)}.obj: $(cc32) $(TCL_CFLAGS) -Fo$(TMPDIR)\ $< @@ -375,3 +392,14 @@ -@del $(TMPDIR)\*.obj -@del $(TMPDIR)\*.res -@del $(TMPDIR)\*.def + -@del $(TMPDIR)\*.pch + -@del $(TMPDIR)\*.pdb + -@del $(WINDIR)\*~ + -@del $(MACDIR)\*~ + -@del $(UNIXDIR)\*~ + -@del $(GENERICDIR)\*~ + +distclean: clean + -@del $(TMPDIR)\*.opt + -@del $(TMPDIR)\*.mdp + -@del $(TMPDIR)\*pure.ini diff -urN /home/chaffee/tcl/tcl8.0p2-orig/win/msjexhnd.cpp tcl8.0/win/msjexhnd.cpp --- /home/chaffee/tcl/tcl8.0p2-orig/win/msjexhnd.cpp Wed Dec 31 16:00:00 1969 +++ tcl8.0/win/msjexhnd.cpp Tue Aug 25 05:03:37 1998 @@ -0,0 +1,456 @@ +//========================================== +// Matt Pietrek +// Microsoft Systems Journal, May 1997 +// FILE: MSJEXHND.CPP +//========================================== +#include +#include +#include +#include "msjexhnd.h" + +//============================== Global Variables ============================= + +// +// Declare the static variables of the MSJExceptionHandler class +// +TCHAR MSJExceptionHandler::m_szLogFileName[MAX_PATH]; +LPTOP_LEVEL_EXCEPTION_FILTER MSJExceptionHandler::m_previousFilter; +HANDLE MSJExceptionHandler::m_hReportFile; + +MSJExceptionHandler::SYMINITIALIZEPROC MSJExceptionHandler::_SymInitialize = 0; +MSJExceptionHandler::SYMCLEANUPPROC MSJExceptionHandler::_SymCleanup = 0; +MSJExceptionHandler::STACKWALKPROC MSJExceptionHandler::_StackWalk = 0; + +MSJExceptionHandler::SYMFUNCTIONTABLEACCESSPROC + MSJExceptionHandler::_SymFunctionTableAccess = 0; + +MSJExceptionHandler::SYMGETMODULEBASEPROC + MSJExceptionHandler::_SymGetModuleBase = 0; + +MSJExceptionHandler::SYMGETSYMFROMADDRPROC + MSJExceptionHandler::_SymGetSymFromAddr = 0; + +MSJExceptionHandler g_MSJExceptionHandler; // Declare global instance of class + +//============================== Class Methods ============================= + +//============= +// Constructor +//============= +MSJExceptionHandler::MSJExceptionHandler( ) +{ + // Install the unhandled exception filter function + m_previousFilter = SetUnhandledExceptionFilter(MSJUnhandledExceptionFilter); + + // Figure out what the report file will be named, and store it away + GetModuleFileName( 0, m_szLogFileName, MAX_PATH ); + + // Look for the '.' before the "EXE" extension. Replace the extension + // with "RPT" + PTSTR pszDot = _tcsrchr( m_szLogFileName, _T('.') ); + if ( pszDot ) + { + pszDot++; // Advance past the '.' + if ( _tcslen(pszDot) >= 3 ) + _tcscpy( pszDot, _T("RPT") ); // "RPT" -> "Report" + } +} + +//============ +// Destructor +//============ +MSJExceptionHandler::~MSJExceptionHandler( ) +{ + SetUnhandledExceptionFilter( m_previousFilter ); +} + +//============================================================== +// Lets user change the name of the report file to be generated +//============================================================== +void MSJExceptionHandler::SetLogFileName( PTSTR pszLogFileName ) +{ + _tcscpy( m_szLogFileName, pszLogFileName ); +} + +//=========================================================== +// Entry point where control comes on an unhandled exception +//=========================================================== +LONG WINAPI MSJExceptionHandler::MSJUnhandledExceptionFilter( + PEXCEPTION_POINTERS pExceptionInfo ) +{ + m_hReportFile = CreateFile( m_szLogFileName, + GENERIC_WRITE, + 0, + 0, + OPEN_ALWAYS, + FILE_FLAG_WRITE_THROUGH, + 0 ); + + if ( m_hReportFile ) + { + SetFilePointer( m_hReportFile, 0, 0, FILE_END ); + + GenerateExceptionReport( pExceptionInfo ); + + CloseHandle( m_hReportFile ); + m_hReportFile = 0; + } + + if ( m_previousFilter ) + return m_previousFilter( pExceptionInfo ); + else + return EXCEPTION_CONTINUE_SEARCH; +} + +//=========================================================================== +// Open the report file, and write the desired information to it. Called by +// MSJUnhandledExceptionFilter +//=========================================================================== +void MSJExceptionHandler::GenerateExceptionReport( + PEXCEPTION_POINTERS pExceptionInfo ) +{ + // Start out with a banner + _tprintf( _T("//=====================================================\n") ); + + PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord; + + // First print information about the type of fault + _tprintf( _T("Exception code: %08X %s\n"), + pExceptionRecord->ExceptionCode, + GetExceptionString(pExceptionRecord->ExceptionCode) ); + + // Now print information about where the fault occured + TCHAR szFaultingModule[MAX_PATH]; + DWORD section, offset; + GetLogicalAddress( pExceptionRecord->ExceptionAddress, + szFaultingModule, + sizeof( szFaultingModule ), + section, offset ); + + _tprintf( _T("Fault address: %08X %02X:%08X %s\n"), + pExceptionRecord->ExceptionAddress, + section, offset, szFaultingModule ); + + PCONTEXT pCtx = pExceptionInfo->ContextRecord; + + // Show the registers + #ifdef _M_IX86 // Intel Only! + _tprintf( _T("\nRegisters:\n") ); + + _tprintf(_T("EAX:%08X\nEBX:%08X\nECX:%08X\nEDX:%08X\nESI:%08X\nEDI:%08X\n"), + pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx, pCtx->Esi, pCtx->Edi ); + + _tprintf( _T("CS:EIP:%04X:%08X\n"), pCtx->SegCs, pCtx->Eip ); + _tprintf( _T("SS:ESP:%04X:%08X EBP:%08X\n"), + pCtx->SegSs, pCtx->Esp, pCtx->Ebp ); + _tprintf( _T("DS:%04X ES:%04X FS:%04X GS:%04X\n"), + pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs ); + _tprintf( _T("Flags:%08X\n"), pCtx->EFlags ); + + #endif + + if ( !InitImagehlpFunctions() ) + { + OutputDebugString(_T("IMAGEHLP.DLL or its exported procs not found")); + + #ifdef _M_IX86 // Intel Only! + // Walk the stack using x86 specific code + IntelStackWalk( pCtx ); + #endif + + return; + } + + ImagehlpStackWalk( pCtx ); + + _SymCleanup( GetCurrentProcess() ); + + _tprintf( _T("\n") ); +} + +//====================================================================== +// Given an exception code, returns a pointer to a static string with a +// description of the exception +//====================================================================== +LPTSTR MSJExceptionHandler::GetExceptionString( DWORD dwCode ) +{ + #define EXCEPTION( x ) case EXCEPTION_##x: return _T(#x); + + switch ( dwCode ) + { + EXCEPTION( ACCESS_VIOLATION ) + EXCEPTION( DATATYPE_MISALIGNMENT ) + EXCEPTION( BREAKPOINT ) + EXCEPTION( SINGLE_STEP ) + EXCEPTION( ARRAY_BOUNDS_EXCEEDED ) + EXCEPTION( FLT_DENORMAL_OPERAND ) + EXCEPTION( FLT_DIVIDE_BY_ZERO ) + EXCEPTION( FLT_INEXACT_RESULT ) + EXCEPTION( FLT_INVALID_OPERATION ) + EXCEPTION( FLT_OVERFLOW ) + EXCEPTION( FLT_STACK_CHECK ) + EXCEPTION( FLT_UNDERFLOW ) + EXCEPTION( INT_DIVIDE_BY_ZERO ) + EXCEPTION( INT_OVERFLOW ) + EXCEPTION( PRIV_INSTRUCTION ) + EXCEPTION( IN_PAGE_ERROR ) + EXCEPTION( ILLEGAL_INSTRUCTION ) + EXCEPTION( NONCONTINUABLE_EXCEPTION ) + EXCEPTION( STACK_OVERFLOW ) + EXCEPTION( INVALID_DISPOSITION ) + EXCEPTION( GUARD_PAGE ) + EXCEPTION( INVALID_HANDLE ) + } + + // If not one of the "known" exceptions, try to get the string + // from NTDLL.DLL's message table. + + static TCHAR szBuffer[512] = { 0 }; + + FormatMessage( FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE, + GetModuleHandle( _T("NTDLL.DLL") ), + dwCode, 0, szBuffer, sizeof( szBuffer ), 0 ); + + return szBuffer; +} + +//============================================================================== +// Given a linear address, locates the module, section, and offset containing +// that address. +// +// Note: the szModule paramater buffer is an output buffer of length specified +// by the len parameter (in characters!) +//============================================================================== +BOOL MSJExceptionHandler::GetLogicalAddress( + PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD& offset ) +{ + MEMORY_BASIC_INFORMATION mbi; + + if ( !VirtualQuery( addr, &mbi, sizeof(mbi) ) ) + return FALSE; + + DWORD hMod = (DWORD)mbi.AllocationBase; + + if ( !GetModuleFileName( (HMODULE)hMod, szModule, len ) ) + return FALSE; + + // Point to the DOS header in memory + PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod; + + // From the DOS header, find the NT (PE) header + PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + pDosHdr->e_lfanew); + + PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION( pNtHdr ); + + DWORD rva = (DWORD)addr - hMod; // RVA is offset from module load address + + // Iterate through the section table, looking for the one that encompasses + // the linear address. + for ( unsigned i = 0; + i < pNtHdr->FileHeader.NumberOfSections; + i++, pSection++ ) + { + DWORD sectionStart = pSection->VirtualAddress; + DWORD sectionEnd = sectionStart + + max(pSection->SizeOfRawData, pSection->Misc.VirtualSize); + + // Is the address in this section??? + if ( (rva >= sectionStart) && (rva <= sectionEnd) ) + { + // Yes, address is in the section. Calculate section and offset, + // and store in the "section" & "offset" params, which were + // passed by reference. + section = i+1; + offset = rva - sectionStart; + return TRUE; + } + } + + return FALSE; // Should never get here! +} + +//============================================================ +// Walks the stack, and writes the results to the report file +//============================================================ +void MSJExceptionHandler::IntelStackWalk( PCONTEXT pContext ) +{ + _tprintf( _T("\nCall stack:\n") ); + + _tprintf( _T("Address Frame Logical addr Module\n") ); + + DWORD pc = pContext->Eip; + PDWORD pFrame, pPrevFrame; + + pFrame = (PDWORD)pContext->Ebp; + + do + { + TCHAR szModule[MAX_PATH] = _T(""); + DWORD section = 0, offset = 0; + + GetLogicalAddress((PVOID)pc, szModule,sizeof(szModule),section,offset ); + + _tprintf( _T("%08X %08X %04X:%08X %s\n"), + pc, pFrame, section, offset, szModule ); + + pc = pFrame[1]; + + pPrevFrame = pFrame; + + pFrame = (PDWORD)pFrame[0]; // proceed to next higher frame on stack + + if ( (DWORD)pFrame & 3 ) // Frame pointer must be aligned on a + break; // DWORD boundary. Bail if not so. + + if ( pFrame <= pPrevFrame ) + break; + + // Can two DWORDs be read from the supposed frame address? + if ( IsBadWritePtr(pFrame, sizeof(PVOID)*2) ) + break; + + } while ( 1 ); +} + +//============================================================ +// Walks the stack, and writes the results to the report file +//============================================================ +void MSJExceptionHandler::ImagehlpStackWalk( PCONTEXT pContext ) +{ + _tprintf( _T("\nCall stack:\n") ); + + _tprintf( _T("Address Frame\n") ); + + // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag + + STACKFRAME sf; + memset( &sf, 0, sizeof(sf) ); + + // Initialize the STACKFRAME structure for the first call. This is only + // necessary for Intel CPUs, and isn't mentioned in the documentation. + sf.AddrPC.Offset = pContext->Eip; + sf.AddrPC.Mode = AddrModeFlat; + sf.AddrStack.Offset = pContext->Esp; + sf.AddrStack.Mode = AddrModeFlat; + sf.AddrFrame.Offset = pContext->Ebp; + sf.AddrFrame.Mode = AddrModeFlat; + + while ( 1 ) + { + if ( ! _StackWalk( IMAGE_FILE_MACHINE_I386, + GetCurrentProcess(), + GetCurrentThread(), + &sf, + pContext, + 0, + _SymFunctionTableAccess, + _SymGetModuleBase, + 0 ) ) + break; + + if ( 0 == sf.AddrFrame.Offset ) // Basic sanity check to make sure + break; // the frame is OK. Bail if not. + + _tprintf( _T("%08X %08X "), sf.AddrPC.Offset, sf.AddrFrame.Offset ); + + // IMAGEHLP is wacky, and requires you to pass in a pointer to an + // IMAGEHLP_SYMBOL structure. The problem is that this structure is + // variable length. That is, you determine how big the structure is + // at runtime. This means that you can't use sizeof(struct). + // So...make a buffer that's big enough, and make a pointer + // to the buffer. We also need to initialize not one, but TWO + // members of the structure before it can be used. + + BYTE symbolBuffer[ sizeof(IMAGEHLP_SYMBOL) + 512 ]; + PIMAGEHLP_SYMBOL pSymbol = (PIMAGEHLP_SYMBOL)symbolBuffer; + pSymbol->SizeOfStruct = sizeof(symbolBuffer); + pSymbol->MaxNameLength = 512; + + DWORD symDisplacement = 0; // Displacement of the input address, + // relative to the start of the symbol + + if ( _SymGetSymFromAddr(GetCurrentProcess(), sf.AddrPC.Offset, + &symDisplacement, pSymbol) ) + { + _tprintf( _T("%hs+%X\n"), pSymbol->Name, symDisplacement ); + + } + else // No symbol found. Print out the logical address instead. + { + TCHAR szModule[MAX_PATH] = _T(""); + DWORD section = 0, offset = 0; + + GetLogicalAddress( (PVOID)sf.AddrPC.Offset, + szModule, sizeof(szModule), section, offset ); + + _tprintf( _T("%04X:%08X %s\n"), + section, offset, szModule ); + } + } + +} + +//============================================================================ +// Helper function that writes to the report file, and allows the user to use +// printf style formating +//============================================================================ +int __cdecl MSJExceptionHandler::_tprintf(const TCHAR * format, ...) +{ + TCHAR szBuff[1024]; + int retValue; + DWORD cbWritten; + va_list argptr; + + va_start( argptr, format ); + retValue = wvsprintf( szBuff, format, argptr ); + va_end( argptr ); + + WriteFile( m_hReportFile, szBuff, retValue * sizeof(TCHAR), &cbWritten, 0 ); + + return retValue; +} + + +//========================================================================= +// Load IMAGEHLP.DLL and get the address of functions in it that we'll use +//========================================================================= +BOOL MSJExceptionHandler::InitImagehlpFunctions( void ) +{ + HMODULE hModImagehlp = LoadLibrary( _T("IMAGEHLP.DLL") ); + if ( !hModImagehlp ) + return FALSE; + + _SymInitialize = (SYMINITIALIZEPROC)GetProcAddress( hModImagehlp, + "SymInitialize" ); + if ( !_SymInitialize ) + return FALSE; + + _SymCleanup = (SYMCLEANUPPROC)GetProcAddress( hModImagehlp, "SymCleanup" ); + if ( !_SymCleanup ) + return FALSE; + + _StackWalk = (STACKWALKPROC)GetProcAddress( hModImagehlp, "StackWalk" ); + if ( !_StackWalk ) + return FALSE; + + _SymFunctionTableAccess = (SYMFUNCTIONTABLEACCESSPROC) + GetProcAddress( hModImagehlp, "SymFunctionTableAccess" ); + + if ( !_SymFunctionTableAccess ) + return FALSE; + + _SymGetModuleBase=(SYMGETMODULEBASEPROC)GetProcAddress( hModImagehlp, + "SymGetModuleBase"); + if ( !_SymGetModuleBase ) + return FALSE; + + _SymGetSymFromAddr=(SYMGETSYMFROMADDRPROC)GetProcAddress( hModImagehlp, + "SymGetSymFromAddr" ); + if ( !_SymGetSymFromAddr ) + return FALSE; + + if ( !_SymInitialize( GetCurrentProcess(), 0, TRUE ) ) + return FALSE; + + return TRUE; +} diff -urN /home/chaffee/tcl/tcl8.0p2-orig/win/msjexhnd.h tcl8.0/win/msjexhnd.h --- /home/chaffee/tcl/tcl8.0p2-orig/win/msjexhnd.h Wed Dec 31 16:00:00 1969 +++ tcl8.0/win/msjexhnd.h Tue Aug 25 05:05:21 1998 @@ -0,0 +1,76 @@ +/* + * msjexhnd.h + */ + +#ifndef __MSJEXHND_H__ +#define __MSJEXHND_H__ + +class MSJExceptionHandler +{ + public: + + MSJExceptionHandler( ); + ~MSJExceptionHandler( ); + + void SetLogFileName( PTSTR pszLogFileName ); + + private: + + // entry point where control comes on an unhandled exception + static LONG WINAPI MSJUnhandledExceptionFilter( + PEXCEPTION_POINTERS pExceptionInfo ); + + // where report info is extracted and generated + static void GenerateExceptionReport( PEXCEPTION_POINTERS pExceptionInfo ); + + // Helper functions + static LPTSTR GetExceptionString( DWORD dwCode ); + static BOOL GetLogicalAddress(PVOID addr, PTSTR szModule, DWORD len, + DWORD& section, DWORD& offset ); + static void IntelStackWalk( PCONTEXT pContext ); + #if 1 + static void ImagehlpStackWalk( PCONTEXT pContext ); + #endif + static int __cdecl _tprintf(const TCHAR * format, ...); + + #if 1 + static BOOL InitImagehlpFunctions( void ); + #endif + + // Variables used by the class + static TCHAR m_szLogFileName[MAX_PATH]; + static LPTOP_LEVEL_EXCEPTION_FILTER m_previousFilter; + static HANDLE m_hReportFile; + + #if 1 + // Make typedefs for some IMAGEHLP.DLL functions so that we can use them + // with GetProcAddress + typedef BOOL (__stdcall * SYMINITIALIZEPROC)( HANDLE, LPSTR, BOOL ); + typedef BOOL (__stdcall *SYMCLEANUPPROC)( HANDLE ); + + typedef BOOL (__stdcall * STACKWALKPROC) + ( DWORD, HANDLE, HANDLE, LPSTACKFRAME, LPVOID, + PREAD_PROCESS_MEMORY_ROUTINE,PFUNCTION_TABLE_ACCESS_ROUTINE, + PGET_MODULE_BASE_ROUTINE, PTRANSLATE_ADDRESS_ROUTINE ); + + typedef LPVOID (__stdcall *SYMFUNCTIONTABLEACCESSPROC)( HANDLE, DWORD ); + + typedef DWORD (__stdcall *SYMGETMODULEBASEPROC)( HANDLE, DWORD ); + + typedef BOOL (__stdcall *SYMGETSYMFROMADDRPROC) + ( HANDLE, DWORD, PDWORD, PIMAGEHLP_SYMBOL ); + + static SYMINITIALIZEPROC _SymInitialize; + static SYMCLEANUPPROC _SymCleanup; + static STACKWALKPROC _StackWalk; + static SYMFUNCTIONTABLEACCESSPROC _SymFunctionTableAccess; + static SYMGETMODULEBASEPROC _SymGetModuleBase; + static SYMGETSYMFROMADDRPROC _SymGetSymFromAddr; + + #endif + +}; + +extern MSJExceptionHandler g_MSJExceptionHandler; // global instance of class + +#endif diff -urN /home/chaffee/tcl/tcl8.0p2-orig/win/tclWinChan.c tcl8.0/win/tclWinChan.c --- /home/chaffee/tcl/tcl8.0p2-orig/win/tclWinChan.c Fri Sep 4 04:15:18 1998 +++ tcl8.0/win/tclWinChan.c Fri Aug 28 10:47:56 1998 @@ -29,6 +29,13 @@ static int initialized = 0; /* + * The following define declares a new user message for use on the + * pipe window. + */ + +#define FILE_MESSAGE WM_USER+20 + +/* * State flags used in the info structures below. */ @@ -50,6 +57,9 @@ * which events should be reported. */ int flags; /* State flags, see above for a list. */ HANDLE handle; /* Input/output file. */ + OVERLAPPED wOverResult; /* Overlapped write result */ + TclWinReaderInfo *readerInfo; /* File reader data */ + struct FileInfo *nextLPtr; /* Pointer to next created file */ struct FileInfo *nextPtr; /* Pointer to next registered file. */ } FileInfo; @@ -74,6 +84,19 @@ } FileEvent; /* + * File notification window. This window is used to receive file + * notification events. + */ + +static HWND fileWindow = NULL; + +/* + * Window class for creating the file notification window. + */ + +static ATOM fileClass; + +/* * Static routines for this file: */ @@ -82,6 +105,9 @@ Tcl_DString *dsPtr)); static int ComInputProc _ANSI_ARGS_((ClientData instanceData, char *buf, int toRead, int *errorCode)); +static int ComOutputProc _ANSI_ARGS_((ClientData instanceData, + char *buf, int toWrite, int *errorCode)); +static DWORD WINAPI ComWatchThread (LPVOID *arg); static int ComSetOptionProc _ANSI_ARGS_((ClientData instanceData, Tcl_Interp *interp, char *optionName, char *value)); @@ -102,6 +128,8 @@ char *buf, int toRead, int *errorCode)); static int FileOutputProc _ANSI_ARGS_((ClientData instanceData, char *buf, int toWrite, int *errorCode)); +static LRESULT CALLBACK FileProc (HWND hwnd, UINT message, WPARAM wParam, + LPARAM lParam); static int FileSeekProc _ANSI_ARGS_((ClientData instanceData, long offset, int mode, int *errorCode)); static void FileSetupProc _ANSI_ARGS_((ClientData clientData, @@ -132,7 +160,7 @@ FileBlockProc, /* Set blocking or non-blocking mode.*/ FileCloseProc, /* Close proc. */ ComInputProc, /* Input proc. */ - FileOutputProc, /* Output proc. */ + ComOutputProc, /* Output proc. */ NULL, /* Seek proc. */ ComSetOptionProc, /* Set option proc. */ ComGetOptionProc, /* Get option proc. */ @@ -159,8 +187,43 @@ static void FileInit() { + WNDCLASS class; + + if (initialized) { + return; + } + initialized = 1; firstFilePtr = NULL; + + /* + * Register the async notification window class and window. + */ + + class.style = 0; + class.cbClsExtra = 0; + class.cbWndExtra = 0; + class.hInstance = TclWinGetTclInstance(); + class.hbrBackground = NULL; + class.lpszMenuName = NULL; + class.lpszClassName = "TclFile"; + class.lpfnWndProc = FileProc; + class.hIcon = NULL; + class.hCursor = NULL; + + fileClass = RegisterClass(&class); + if (!fileClass) { + return; + } + fileWindow = CreateWindowEx(0, (LPCTSTR)fileClass, "TclFile", + WS_OVERLAPPED, 0, 0, 0, 0, NULL, NULL, + TclWinGetTclInstance(), NULL); + if (fileWindow == NULL) { + TclWinConvertError(GetLastError()); + UnregisterClass((LPCTSTR)fileClass, TclWinGetTclInstance()); + return; + } + Tcl_CreateEventSource(FileSetupProc, FileCheckProc, NULL); Tcl_CreateExitHandler(FileChannelExitHandler, NULL); } @@ -168,6 +231,57 @@ /* *---------------------------------------------------------------------- * + * FileProc -- + * + * This function is called when WSAAsyncSelect has been used + * to register interest in a file event, and the event has + * occurred. + * + * Results: + * 0 on success. + * + * Side effects: + * The flags for the given file are updated to reflect the + * event that occured. + * + *---------------------------------------------------------------------- + */ + +static LRESULT CALLBACK +FileProc(hwnd, message, wParam, lParam) + HWND hwnd; + UINT message; + WPARAM wParam; + LPARAM lParam; +{ + int event; + HANDLE file; + FileInfo *filePtr; + + if ((hwnd != fileWindow) || (message != FILE_MESSAGE)) { + return DefWindowProc(hwnd, message, wParam, lParam); + } + + file = (HANDLE) wParam; + event = lParam; + + /* + * Find the specified file on the file list and update its + * check flags. + */ + + for (filePtr = firstFilePtr; filePtr != NULL; filePtr = filePtr->nextPtr) { + if (filePtr->readerInfo->rHandle == file) { + filePtr->readerInfo->readyMask |= event; + break; + } + } + return 0; +} + +/* + *---------------------------------------------------------------------- + * * FileChannelExitHandler -- * * This function is called to cleanup the channel driver before @@ -186,6 +300,8 @@ FileChannelExitHandler(clientData) ClientData clientData; /* Old window proc */ { + DestroyWindow(fileWindow); + UnregisterClass((LPCTSTR)fileClass, TclWinGetTclInstance()); Tcl_DeleteEventSource(FileSetupProc, FileCheckProc, NULL); initialized = 0; } @@ -224,7 +340,21 @@ */ for (infoPtr = firstFilePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) { - if (infoPtr->watchMask) { + if (infoPtr->watchMask & TCL_READABLE) { + if (infoPtr->readerInfo->rType == FILE_TYPE_CONSOLE) { + if (infoPtr->readerInfo->rThread == NULL) { + TclWinReaderStart(infoPtr->readerInfo, TclWinReaderThread); + } + SetEvent(infoPtr->readerInfo->rGoEvent); + } else if (infoPtr->readerInfo->rType == FILE_TYPE_COM) { + if (infoPtr->readerInfo->rThread == NULL) { + TclWinReaderStart(infoPtr->readerInfo, ComWatchThread); + } + SetEvent(infoPtr->readerInfo->rGoEvent); + } + } + + if (infoPtr->readerInfo->readyMask & infoPtr->watchMask) { Tcl_SetMaxBlockTime(&blockTime); break; } @@ -267,7 +397,31 @@ */ for (infoPtr = firstFilePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) { - if (infoPtr->watchMask && !(infoPtr->flags & FILE_PENDING)) { + if (infoPtr->watchMask & TCL_READABLE) { + if (infoPtr->readerInfo->rThread == NULL) { + if (infoPtr->readerInfo->rType == FILE_TYPE_CONSOLE) { + TclWinReaderStart(infoPtr->readerInfo, TclWinReaderThread); + } else if (infoPtr->readerInfo->rType == FILE_TYPE_COM) { + TclWinReaderStart(infoPtr->readerInfo, ComWatchThread); + } + SetEvent(infoPtr->readerInfo->rGoEvent); + } + } + if ((infoPtr->readerInfo->readyMask & infoPtr->watchMask) + && !(infoPtr->flags & FILE_PENDING)) + { + if (infoPtr->readerInfo->rType == FILE_TYPE_COM) { + COMSTAT cs; + DWORD dw; + + if (ClearCommError(infoPtr->readerInfo->rHandle, &dw, &cs)) { + if (dw != 0 || cs.cbInQue == 0) { + infoPtr->readerInfo->readyMask &= ~TCL_READABLE; + return; + } + } + + } infoPtr->flags |= FILE_PENDING; evPtr = (FileEvent *) ckalloc(sizeof(FileEvent)); evPtr->header.proc = FileEventProc; @@ -360,8 +514,10 @@ if (mode == TCL_MODE_NONBLOCKING) { infoPtr->flags |= FILE_ASYNC; + infoPtr->readerInfo->async = 1; } else { infoPtr->flags &= ~(FILE_ASYNC); + infoPtr->readerInfo->async = 0; } return 0; } @@ -397,10 +553,6 @@ FileWatchProc(instanceData, 0); - if (CloseHandle(fileInfoPtr->handle) == FALSE) { - TclWinConvertError(GetLastError()); - errorCode = errno; - } for (nextPtrPtr = &firstFilePtr; (*nextPtrPtr) != NULL; nextPtrPtr = &((*nextPtrPtr)->nextPtr)) { if ((*nextPtrPtr) == fileInfoPtr) { @@ -408,6 +560,15 @@ break; } } + + if (CloseHandle(fileInfoPtr->handle) == FALSE) { + TclWinConvertError(GetLastError()); + errorCode = errno; + } + TclWinReaderFree(fileInfoPtr->readerInfo); + if (fileInfoPtr->wOverResult.hEvent) { + CloseHandle(fileInfoPtr->wOverResult.hEvent); + } ckfree((char *)fileInfoPtr); return errorCode; } @@ -499,17 +660,22 @@ * problem exists for files being read over the network. */ - if (ReadFile(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize, &bytesRead, - (LPOVERLAPPED) NULL) != FALSE) { - return bytesRead; - } + if (infoPtr->readerInfo->rType != FILE_TYPE_CONSOLE && + infoPtr->readerInfo->rType != FILE_TYPE_COM) + { + if (ReadFile(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize, + &bytesRead, (LPOVERLAPPED) NULL) != FALSE) { + return bytesRead; + } - TclWinConvertError(GetLastError()); - *errorCode = errno; - if (errno == EPIPE) { - return 0; + TclWinConvertError(GetLastError()); + *errorCode = errno; + if (errno == EPIPE) { + return 0; + } + return -1; } - return -1; + return TclWinReaderInput(infoPtr->readerInfo, buf, bufSize, errorCode); } /* @@ -594,7 +760,7 @@ */ infoPtr->watchMask = mask & infoPtr->validMask; - if (infoPtr->watchMask) { + if (infoPtr->readerInfo->readyMask & infoPtr->watchMask) { Tcl_SetMaxBlockTime(&blockTime); } } @@ -636,6 +802,80 @@ /* *---------------------------------------------------------------------- * + * ComWatchThread -- + * + * This routine just waits on the a COM port for a character to + * be placed on the input queue. At that point, it posts a + * message saying that something might want to try and read the + * serial port. + * + * Results: + * This thread exits when the child is done. + * + * Side Effects: + * This buffer should not be changed until the data has been copied. + * + *---------------------------------------------------------------------- + */ +static DWORD WINAPI +ComWatchThread(arg) + LPVOID *arg; +{ + TclWinReaderInfo *infoPtr = (TclWinReaderInfo *) arg; + HANDLE handle = infoPtr->rHandle; + BOOL b; + DWORD mask; + DWORD err; + DWORD bytesWritten; + OVERLAPPED overlapped; + + overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + SetCommMask(handle, EV_RXCHAR); + while (1) { + if (infoPtr->rHandleClosed) { + break; + } + + /* + * Post a new message whenever we know there is something + * that can be read + */ + + WaitForSingleObject(infoPtr->rGoEvent, INFINITE); + b = WaitCommEvent(handle, &mask, &overlapped); + if (b == FALSE) { + err = GetLastError(); + if (err == ERROR_IO_PENDING) { + if (GetOverlappedResult(handle, &overlapped, + &bytesWritten, TRUE) == FALSE) { + err = GetLastError(); + break; + } + } else { + break; + } + } + if (mask & EV_RXCHAR) { + ResetEvent(infoPtr->rGoEvent); + PostMessage(infoPtr->postWin, infoPtr->postMsg, + (UINT) handle, TCL_READABLE); + } + } + CloseHandle(overlapped.hEvent); + + infoPtr->eof = 1; + PostMessage(infoPtr->postWin, infoPtr->postMsg, (UINT) handle, TCL_READABLE); + + WaitForSingleObject(infoPtr->rSemaphore, INFINITE); + + TclWinReaderFree(infoPtr); + ExitThread(0); + return 0; +} + +/* + *---------------------------------------------------------------------- + * * ComInputProc -- * * Reads input from the IO channel into the buffer given. Returns @@ -663,10 +903,13 @@ DWORD bytesRead; DWORD dw; COMSTAT cs; + DWORD err; *errorCode = 0; infoPtr = (FileInfo *) instanceData; + infoPtr->readerInfo->readyMask &= ~TCL_READABLE; + if (ClearCommError(infoPtr->handle, &dw, &cs)) { if (dw != 0) { *errorCode = EIO; @@ -687,18 +930,78 @@ } if (ReadFile(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize, &bytesRead, - (LPOVERLAPPED) NULL) == FALSE) { - TclWinConvertError(GetLastError()); - *errorCode = errno; - return -1; + &infoPtr->readerInfo->rOverResult) == FALSE) { + err = GetLastError(); + if (err == ERROR_IO_PENDING) { + if (GetOverlappedResult(infoPtr->handle, + &infoPtr->readerInfo->rOverResult, + &bytesRead, TRUE) == FALSE) + { + err = GetLastError(); + } + } + if (err != ERROR_IO_PENDING) { + TclWinConvertError(err); + *errorCode = errno; + return -1; + } } - + return bytesRead; } /* *---------------------------------------------------------------------- * + * ComOutputProc -- + * + * Writes the given output on the IO channel. Returns count of how + * many characters were actually written, and an error indication. + * + * Results: + * A count of how many characters were written is returned and an + * error indication is returned in an output argument. + * + * Side effects: + * Writes output on the actual channel. + * + *---------------------------------------------------------------------- + */ + +static int +ComOutputProc(instanceData, buf, toWrite, errorCode) + ClientData instanceData; /* File state. */ + char *buf; /* The data buffer. */ + int toWrite; /* How many bytes to write? */ + int *errorCode; /* Where to store error code. */ +{ + FileInfo *infoPtr = (FileInfo *) instanceData; + DWORD bytesWritten; + DWORD err; + + *errorCode = 0; + + if (WriteFile(infoPtr->handle, (LPVOID) buf, (DWORD) toWrite, + &bytesWritten, &infoPtr->wOverResult) == FALSE) { + err = GetLastError(); + if (err == ERROR_IO_PENDING) { + if (GetOverlappedResult(infoPtr->handle, &infoPtr->wOverResult, + &bytesWritten, TRUE) == FALSE) { + err = GetLastError(); + } + } + if (err != ERROR_IO_PENDING) { + TclWinConvertError(GetLastError()); + *errorCode = errno; + return -1; + } + } + return bytesWritten; +} + +/* + *---------------------------------------------------------------------- + * * ComSetOptionProc -- * * Sets an option on a channel. @@ -965,8 +1268,8 @@ return NULL; } + dcb.DCBlength = sizeof( DCB ) ; if (GetFileType(handle) == FILE_TYPE_CHAR) { - dcb.DCBlength = sizeof( DCB ) ; if (GetCommState(handle, &dcb)) { /* * This is a com port. Reopen it with the correct modes. @@ -976,7 +1279,7 @@ CloseHandle(handle); handle = CreateFile(nativeName, accessMode, 0, NULL, OPEN_EXISTING, - flags, NULL); + flags | FILE_FLAG_OVERLAPPED, NULL); if (handle == INVALID_HANDLE_VALUE) { goto openerr; } @@ -1014,6 +1317,13 @@ infoPtr->watchMask = 0; infoPtr->flags = (mode & O_APPEND) ? FILE_APPEND : 0; infoPtr->handle = handle; + infoPtr->readerInfo = TclWinReaderNew(fileWindow, FILE_MESSAGE, handle); + memset(&infoPtr->wOverResult, 0, sizeof(infoPtr->wOverResult)); + if (channelTypePtr == &comChannelType) { + infoPtr->wOverResult.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + } else { + infoPtr->wOverResult.hEvent = NULL; + } sprintf(channelName, "file%d", (int) handle); @@ -1067,6 +1377,8 @@ { char channelName[20]; FileInfo *infoPtr; + DCB dcb; + Tcl_ChannelType *channelTypePtr; if (!initialized) { FileInit(); @@ -1088,6 +1400,18 @@ } } + /* + * See if the handle is a serial device handle as those need to be + * handled differently. + */ + + dcb.DCBlength = sizeof( DCB ) ; + if (GetCommState(handle, &dcb)) { + channelTypePtr = &comChannelType; + } else { + channelTypePtr = &fileChannelType; + } + infoPtr = (FileInfo *) ckalloc((unsigned) sizeof(FileInfo)); infoPtr->nextPtr = firstFilePtr; firstFilePtr = infoPtr; @@ -1095,8 +1419,10 @@ infoPtr->watchMask = 0; infoPtr->flags = 0; infoPtr->handle = (HANDLE) handle; - infoPtr->channel = Tcl_CreateChannel(&fileChannelType, channelName, + infoPtr->channel = Tcl_CreateChannel(channelTypePtr, channelName, (ClientData) infoPtr, mode); + infoPtr->readerInfo = TclWinReaderNew(fileWindow, FILE_MESSAGE, handle); + memset(&infoPtr->wOverResult, 0, sizeof(infoPtr->wOverResult)); /* * Windows files have AUTO translation mode and ^Z eof char on input. @@ -1131,30 +1457,52 @@ Tcl_Channel channel; HANDLE handle; int mode; + int modeEquiv; char *bufMode; DWORD handleId; /* Standard handle to retrieve. */ + DWORD handleIdEquiv; /* Check if this handle is equivalent */ + handleIdEquiv = 0xFFFFFFFF; switch (type) { case TCL_STDIN: handleId = STD_INPUT_HANDLE; mode = TCL_READABLE; + handleIdEquiv = STD_OUTPUT_HANDLE; + modeEquiv = TCL_WRITABLE; bufMode = "line"; break; case TCL_STDOUT: handleId = STD_OUTPUT_HANDLE; mode = TCL_WRITABLE; + handleIdEquiv = STD_INPUT_HANDLE; + modeEquiv = TCL_READABLE; bufMode = "line"; break; case TCL_STDERR: handleId = STD_ERROR_HANDLE; mode = TCL_WRITABLE; + handleIdEquiv = STD_INPUT_HANDLE; + modeEquiv = TCL_READABLE; bufMode = "none"; break; default: panic("TclGetDefaultStdChannel: Unexpected channel type"); break; } + + /* + * If two or three of the standard handles are same, but sure to + * open the handle with all necessary permissions so it won't crash + * later. This happens when stdin and stdout have been redirected + * to the same handle. + */ + handle = GetStdHandle(handleId); + if (handleIdEquiv != 0xFFFFFFFF) { + if (GetStdHandle(handleIdEquiv) == handle) { + mode |= modeEquiv; + } + } /* * Note that we need to check for 0 because Windows will return 0 if this diff -urN /home/chaffee/tcl/tcl8.0p2-orig/win/tclWinInit.c tcl8.0/win/tclWinInit.c --- /home/chaffee/tcl/tcl8.0p2-orig/win/tclWinInit.c Wed Jun 25 10:40:33 1997 +++ tcl8.0/win/tclWinInit.c Thu Nov 13 04:18:26 1997 @@ -65,6 +65,8 @@ "intel", "mips", "alpha", "ppc" }; +DWORD TclWinThreadId; + /* * The following string is the startup script executed in new * interpreters. It looks on disk in several different directories @@ -113,11 +115,13 @@ }\n\ lappend dirs $tcl_library\n\ lappend dirs [file join [file dirname [file dirname [info nameofexecutable]]] lib/tcl$tcl_version]\n\ + lappend dirs [file join [file dirname [file dirname [info nameofexecutable]]] library]\n\ if [string match {*[ab]*} $tcl_patchLevel] {\n\ set lib tcl$tcl_patchLevel\n\ } else {\n\ set lib tcl$tcl_version\n\ }\n\ + lappend dirs [file join [file dirname [file dirname [file dirname [pwd]]]] $lib/library]\n\ lappend dirs [file join [file dirname [file dirname [pwd]]] $lib/library]\n\ lappend dirs [file join [file dirname [pwd]] library]\n\ foreach i $dirs {\n\ @@ -279,6 +283,7 @@ } Tcl_DStringFree(&ds); + TclWinThreadId = GetCurrentThreadId(); } /* @@ -391,4 +396,29 @@ } Tcl_DStringFree(&temp); } +} + +/* + *---------------------------------------------------------------------- + * + * TclPlatformAsyncMark -- + * + * Process asynchronous event on Windows + * + * Results: + * None + * + *---------------------------------------------------------------------- + */ + +void +TclPlatformAsyncMark(async) + Tcl_AsyncHandler async; /* Token for handler. */ +{ + /* + * Need a way to kick + * the Windows event loop and tell it to go look at asynchronous + * events. + */ + PostThreadMessage(TclWinThreadId, WM_USER, 0, 0); } diff -urN /home/chaffee/tcl/tcl8.0p2-orig/win/tclWinInt.h tcl8.0/win/tclWinInt.h --- /home/chaffee/tcl/tcl8.0p2-orig/win/tclWinInt.h Sat Jul 19 15:56:50 1997 +++ tcl8.0/win/tclWinInt.h Tue Aug 26 11:54:51 1997 @@ -30,6 +30,7 @@ #define VER_PLATFORM_WIN32_WINDOWS 1 #endif +EXTERN TclFile TclWinMakeFile _ANSI_ARGS_((HANDLE hFile)); EXTERN int TclWinSynchSpawn(void *args, int type, void **trans, Tcl_Pid *pidPtr); EXTERN int TclWinGetPlatformId(void); diff -urN /home/chaffee/tcl/tcl8.0p2-orig/win/tclWinPipe.c tcl8.0/win/tclWinPipe.c --- /home/chaffee/tcl/tcl8.0p2-orig/win/tclWinPipe.c Fri Sep 4 04:15:18 1998 +++ tcl8.0/win/tclWinPipe.c Fri Sep 4 03:32:20 1998 @@ -27,6 +27,13 @@ static int initialized = 0; /* + * The following define declares a new user message for use on the + * pipe window. + */ + +#define PIPE_MESSAGE WM_USER+20 + +/* * The following defines identify the various types of applications that * run under windows. There is special case code for the various types. */ @@ -95,7 +102,7 @@ struct ProcInfo *nextPtr; } ProcInfo; -static ProcInfo *procList; +static ProcInfo *procList = NULL; /* * State flags used in the PipeInfo structure below. @@ -122,6 +129,10 @@ TclFile errorFile; /* Error output from pipe. */ int numPids; /* Number of processes attached to pipe. */ Tcl_Pid *pidPtr; /* Pids of attached processes. */ + TclWinReaderInfo *readerInfo; /* File reader state */ + int refCount; /* Reference count for free routine */ + + struct PipeInfo *nextLPtr; /* Pointer to next created pipe */ struct PipeInfo *nextPtr; /* Pointer to next registered pipe. */ } PipeInfo; @@ -130,7 +141,13 @@ * that are being watched for file events. */ -static PipeInfo *firstPipePtr; +static PipeInfo *firstPipePtr = NULL; + +/* + * Every open pipe has an entry on the following list. + */ + +static PipeInfo *pipeList = NULL; /* * The following structure is what is added to the Tcl event queue when @@ -147,11 +164,24 @@ } PipeEvent; /* + * Pipe notification window. This window is used to receive pipe + * notification events. + */ + +static HWND pipeWindow = NULL; + +/* + * Window class for creating the pipe notification window. + */ + +static ATOM pipeClass; + +/* * Declarations for functions used only in this file. */ static int ApplicationType(Tcl_Interp *interp, const char *fileName, - char *fullName); + char *fullName, char *imagePath); static void BuildCommandLine(int argc, char **argv, Tcl_DString *linePtr); static void CopyChannel(HANDLE dst, HANDLE src); static BOOL HasConsole(void); @@ -163,6 +193,7 @@ static int PipeCloseProc(ClientData instanceData, Tcl_Interp *interp); static int PipeEventProc(Tcl_Event *evPtr, int flags); static void PipeExitHandler(ClientData clientData); +static void PipeFreeProc(ClientData clientData); static int PipeGetHandleProc(ClientData instanceData, int direction, ClientData *handlePtr); static void PipeInit(void); @@ -170,9 +201,12 @@ int *errorCode); static int PipeOutputProc(ClientData instanceData, char *buf, int toWrite, int *errorCode); +static LRESULT CALLBACK PipeProc (HWND hwnd, UINT message, WPARAM wParam, + LPARAM lParam); static void PipeWatchProc(ClientData instanceData, int mask); -static void PipeSetupProc _ANSI_ARGS_((ClientData clientData, - int flags)); +static int PipeSetOptionProc (ClientData instanceData, Tcl_Interp *interp, + char *optionName, char *value); +static void PipeSetupProc(ClientData clientData, int flags); static int TempFileName(char name[MAX_PATH]); /* @@ -187,7 +221,7 @@ PipeInputProc, /* Input proc. */ PipeOutputProc, /* Output proc. */ NULL, /* Seek proc. */ - NULL, /* Set option proc. */ + PipeSetOptionProc, /* Set option proc. */ NULL, /* Get option proc. */ PipeWatchProc, /* Set up notifier to watch the channel. */ PipeGetHandleProc, /* Get an OS handle from channel. */ @@ -212,9 +246,42 @@ static void PipeInit() { + WNDCLASS class; + + if (initialized) { + return; + } + initialized = 1; - firstPipePtr = NULL; - procList = NULL; + + /* + * Register the async notification window class and window. + */ + + class.style = 0; + class.cbClsExtra = 0; + class.cbWndExtra = 0; + class.hInstance = TclWinGetTclInstance(); + class.hbrBackground = NULL; + class.lpszMenuName = NULL; + class.lpszClassName = "TclPipe"; + class.lpfnWndProc = PipeProc; + class.hIcon = NULL; + class.hCursor = NULL; + + pipeClass = RegisterClass(&class); + if (!pipeClass) { + return; + } + pipeWindow = CreateWindowEx(0, (LPCTSTR)pipeClass, "TclPipe", + WS_OVERLAPPED, 0, 0, 0, 0, NULL, NULL, + TclWinGetTclInstance(), NULL); + if (pipeWindow == NULL) { + TclWinConvertError(GetLastError()); + UnregisterClass((LPCTSTR)pipeClass, TclWinGetTclInstance()); + return; + } + Tcl_CreateEventSource(PipeSetupProc, PipeCheckProc, NULL); Tcl_CreateExitHandler(PipeExitHandler, NULL); } @@ -222,6 +289,57 @@ /* *---------------------------------------------------------------------- * + * PipeProc -- + * + * This function is called when WSAAsyncSelect has been used + * to register interest in a pipe event, and the event has + * occurred. + * + * Results: + * 0 on success. + * + * Side effects: + * The flags for the given pipe are updated to reflect the + * event that occured. + * + *---------------------------------------------------------------------- + */ + +static LRESULT CALLBACK +PipeProc(hwnd, message, wParam, lParam) + HWND hwnd; + UINT message; + WPARAM wParam; + LPARAM lParam; +{ + int event; + HANDLE pipe; + PipeInfo *pipePtr; + + if ((hwnd != pipeWindow) || (message != PIPE_MESSAGE)) { + return DefWindowProc(hwnd, message, wParam, lParam); + } + + pipe = (HANDLE) wParam; + event = lParam; + + /* + * Find the specified pipe on the pipe list and update its + * check flags. + */ + + for (pipePtr = pipeList; pipePtr != NULL; pipePtr = pipePtr->nextLPtr) { + if (pipePtr->readerInfo->rHandle == pipe) { + pipePtr->readerInfo->readyMask |= event; + break; + } + } + return 0; +} + +/* + *---------------------------------------------------------------------- + * * PipeExitHandler -- * * This function is called to cleanup the pipe module before @@ -240,6 +358,8 @@ PipeExitHandler(clientData) ClientData clientData; /* Old window proc */ { + DestroyWindow(pipeWindow); + UnregisterClass((LPCTSTR)pipeClass, TclWinGetTclInstance()); Tcl_DeleteEventSource(PipeSetupProc, PipeCheckProc, NULL); initialized = 0; } @@ -274,11 +394,22 @@ } /* - * Check to see if there is a watched pipe. If so, poll. + * Check to see if there is a watched pipe. If so, poll on Win32s. */ for (infoPtr = firstPipePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) { - if (infoPtr->watchMask) { + if ((infoPtr->watchMask & TCL_READABLE) && + (infoPtr->readerInfo->rType != FILE_TYPE_DISK)) + { + if (infoPtr->readerInfo->rThread == NULL) { + TclWinReaderStart(infoPtr->readerInfo, TclWinReaderThread); + } + } + if ((infoPtr->watchMask & TCL_WRITABLE) || + ((infoPtr->readerInfo->readyMask & infoPtr->watchMask) + && ((infoPtr->readerInfo->rDataLen > infoPtr->readerInfo->rDataPos) + || infoPtr->readerInfo->eof))) + { Tcl_SetMaxBlockTime(&blockTime); break; } @@ -320,7 +451,19 @@ */ for (infoPtr = firstPipePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) { - if (infoPtr->watchMask && !(infoPtr->flags & PIPE_PENDING)) { + if ((infoPtr->watchMask & TCL_READABLE) && + (infoPtr->readerInfo->rType != FILE_TYPE_DISK)) + { + if (infoPtr->readerInfo->rThread == NULL) { + TclWinReaderStart(infoPtr->readerInfo, TclWinReaderThread); + } + } + if ((infoPtr->watchMask & TCL_WRITABLE) || + (((infoPtr->readerInfo->readyMask) & infoPtr->watchMask) + && (infoPtr->readerInfo->eof || + ((infoPtr->readerInfo->rDataLen > infoPtr->readerInfo->rDataPos) + && !(infoPtr->flags & PIPE_PENDING))))) + { infoPtr->flags |= PIPE_PENDING; evPtr = (PipeEvent *) ckalloc(sizeof(PipeEvent)); evPtr->header.proc = PipeEventProc; @@ -363,6 +506,28 @@ /* *---------------------------------------------------------------------- * + * TclWinMakeFile -- + * + * Exported version of MakeFile(). + * + * Results: + * Returns a newly allocated WinFile as a TclFile. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +TclFile +TclWinMakeFile(handle) + HANDLE handle; /* Type-specific data. */ +{ + return MakeFile(handle); +} + +/* + *---------------------------------------------------------------------- + * * TclpMakeFile -- * * Make a TclFile from a channel. @@ -871,6 +1036,7 @@ SECURITY_ATTRIBUTES secAtts; HANDLE hProcess, h, inputHandle, outputHandle, errorHandle; char execPath[MAX_PATH]; + char image[MAX_PATH]; char *originalName; WinFile *filePtr; @@ -878,7 +1044,7 @@ PipeInit(); } - applType = ApplicationType(interp, argv[0], execPath); + applType = ApplicationType(interp, argv[0], execPath, &image[0]); if (applType == APPL_NONE) { return TCL_ERROR; } @@ -1275,6 +1441,10 @@ * using ab~1.def instead of "a b.default"). */ + if (image[0] != '\0') { + Tcl_DStringAppend(&cmdLine, image, strlen(image)); + Tcl_DStringAppend(&cmdLine, " ", 1); + } BuildCommandLine(argc, argv, &cmdLine); if (!CreateProcess(NULL, Tcl_DStringValue(&cmdLine), NULL, NULL, TRUE, @@ -1394,11 +1564,12 @@ */ static int -ApplicationType(interp, originalName, fullPath) +ApplicationType(interp, originalName, fullPath, imagePath) Tcl_Interp *interp; /* Interp, for error message. */ const char *originalName; /* Name of the application to find. */ char fullPath[MAX_PATH]; /* Filled with complete path to * application. */ + char *imagePath; { int applType, i; HANDLE hFile; @@ -1421,6 +1592,9 @@ * the extensions, looking for a match. */ + if (imagePath) { + imagePath[0] = '\0'; + } applType = APPL_NONE; for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) { lstrcpyn(fullPath, originalName, MAX_PATH - 5); @@ -1437,6 +1611,28 @@ continue; } + /* + * MKS shell needs to be handled like a DOS program, otherwise + * it start acting wacky... Basically, it doesn't like being + * detached. + * XXX: We need to have a better way of determining that this + * is an MKS shell such as looking inside the resources. + * XXX: This needs to be case insensitive. + */ + if (strstr(fullPath, "mks") || strstr(fullPath, "MKS")) { + char *p = strrchr(fullPath, '\\'); + if (!p) { + p = strrchr(fullPath, '/'); + } + if (p) { + p++; + if (stricmp(p, "sh.exe")) { + applType = APPL_DOS; + break; + } + } + } + ext = strrchr(fullPath, '.'); if ((ext != NULL) && (strcmpi(ext, ".bat") == 0)) { applType = APPL_DOS; @@ -1451,6 +1647,66 @@ header.e_magic = 0; ReadFile(hFile, (void *) &header, sizeof(header), &read, NULL); + + /* + * A bit of a hack. Look for a '#!' at the start of the file. If we + * see it, it indicates the presence of a shell script or something + * along those lines (i.e. perl, tcl, etc). We extract the name of the + * image that it is looking for, and we try and turn it into the name + * of the Win32 image that needs to be run. + */ + if (header.e_magic == 0x2123 ) { /* #! */ + char *cpnt; + char *dpnt; + char scriptName[MAX_PATH]; + char shellPath[MAX_PATH]; + char tmpBuffer[MAX_PATH]; + Tcl_DString execBuffer; + + buf[0] = '\0'; + SetFilePointer(hFile, 0, NULL, FILE_BEGIN); + ReadFile(hFile, (void *) tmpBuffer, MAX_PATH, &read, NULL); + /* + * Extract the name of the script interpreter. + */ + cpnt = tmpBuffer + 2; + while (isspace(*cpnt) && *cpnt != '\n') { + cpnt++; + } + dpnt = scriptName; + + while (*cpnt && !isspace(*cpnt)) { + *dpnt++ = *cpnt++; + } + *dpnt = '\0'; + + /* + * First try and normalize the name to a Win32 name. + */ + Tcl_DStringInit(&execBuffer); + Tcl_TranslateFileName(interp, scriptName, &execBuffer); + applType = ApplicationType(interp, scriptName, shellPath, NULL); + + if (applType == APPL_NONE) { + if (strcmp(scriptName, "/bin/sh") == 0) { + cpnt = getenv("SHELL"); + if (cpnt == NULL) { + continue; + } + cpnt = Tcl_TranslateFileName(interp, cpnt, &execBuffer); + strcpy(scriptName, cpnt); + applType = ApplicationType(interp, scriptName, shellPath, NULL); + } + } + if (applType != APPL_NONE && imagePath != NULL) { + strcpy(imagePath, shellPath); + } + + CloseHandle(hFile); + Tcl_DStringFree(&execBuffer); + return applType; + } + if (header.e_magic != IMAGE_DOS_SIGNATURE) { /* * Doesn't have the magic number for relocatable executables. If @@ -1507,9 +1763,16 @@ } if (applType == APPL_NONE) { - TclWinConvertError(GetLastError()); - Tcl_AppendResult(interp, "couldn't execute \"", originalName, - "\": ", Tcl_PosixError(interp), (char *) NULL); + /* + * If we have a NULL imagePath, it means we are searching for the command + * interpreter. We don't record the errors here, since we may + * have to look at several possibilities. + */ + if (imagePath != NULL) { + TclWinConvertError(GetLastError()); + Tcl_AppendResult(interp, "couldn't execute \"", originalName, + "\": ", Tcl_PosixError(interp), (char *) NULL); + } return APPL_NONE; } @@ -1726,6 +1989,17 @@ infoPtr->errorFile = errorFile; infoPtr->numPids = numPids; infoPtr->pidPtr = pidPtr; + infoPtr->refCount = 1; + + if (!initialized) { + PipeInit(); + } + if (readFile) { + infoPtr->readerInfo = TclWinReaderNew(pipeWindow, PIPE_MESSAGE, + ((WinFile*)infoPtr->readFile)->handle); + } else { + infoPtr->readerInfo = NULL; + } /* * Use one of the fds associated with the channel as the @@ -1776,6 +2050,21 @@ "-translation", "auto"); Tcl_SetChannelOption((Tcl_Interp *) NULL, infoPtr->channel, "-eofchar", "\032 {}"); + + if (readFile) { + infoPtr->refCount++; + } + if (writeFile) { + infoPtr->refCount++; + } + if (errorFile) { + infoPtr->refCount++; + } + + infoPtr->nextPtr = NULL; + infoPtr->nextLPtr = pipeList; + pipeList = infoPtr; + return infoPtr->channel; } @@ -1856,12 +2145,19 @@ * hence we have to emulate the behavior. This is done in the input * function by checking against a bit in the state. We set or unset the * bit here to cause the input function to emulate the correct behavior. + * If readerInfo is not set, the pipe was only opened for writing */ if (mode == TCL_MODE_NONBLOCKING) { infoPtr->flags |= PIPE_ASYNC; + if (infoPtr->readerInfo) { + infoPtr->readerInfo->async = 1; + } } else { infoPtr->flags &= ~(PIPE_ASYNC); + if (infoPtr->readerInfo) { + infoPtr->readerInfo->async = 0; + } } return 0; } @@ -1891,9 +2187,10 @@ Tcl_Channel errChan; int errorCode, result; PipeInfo *infoPtr, **nextPtrPtr; + HANDLE rSemaphore = NULL; /* - * Remove the file from the list of watched files. + * Remove the pipe from the list of watched pipes. */ for (nextPtrPtr = &firstPipePtr, infoPtr = *nextPtrPtr; infoPtr != NULL; @@ -1904,20 +2201,18 @@ } } - errorCode = 0; - if (pipePtr->readFile != NULL) { - if (TclpCloseFile(pipePtr->readFile) != 0) { - errorCode = errno; - } - } - if (pipePtr->writeFile != NULL) { - if (TclpCloseFile(pipePtr->writeFile) != 0) { - if (errorCode == 0) { - errorCode = errno; - } + /* + * Remove the pipe from the list of all pipes. + */ + + for (nextPtrPtr = &pipeList, infoPtr = *nextPtrPtr; infoPtr != NULL; + nextPtrPtr = &infoPtr->nextLPtr, infoPtr = *nextPtrPtr) { + if (infoPtr == (PipeInfo *)pipePtr) { + *nextPtrPtr = infoPtr->nextLPtr; + break; } } - + /* * Wrap the error file into a channel and give it to the cleanup * routine. If we are running in Win32s, just delete the error file @@ -1938,21 +2233,134 @@ errChan = Tcl_MakeFileChannel((ClientData) filePtr->handle, TCL_READABLE); } + pipePtr->refCount--; } else { errChan = NULL; } + errorCode = 0; + if (pipePtr->readFile != NULL) { + if (TclpCloseFile(pipePtr->readFile) != 0) { + errorCode = errno; + } + pipePtr->readerInfo->rHandleClosed = TRUE; + pipePtr->refCount--; + } + + if (pipePtr->writeFile != NULL) { + if (!pipePtr->readFile || + (((WinFile *) pipePtr->writeFile)->handle != + ((WinFile *) pipePtr->readFile)->handle)) { + if (TclpCloseFile(pipePtr->writeFile) != 0) { + if (errorCode == 0) { + errorCode = errno; + } + } + } + pipePtr->refCount--; + } result = TclCleanupChildren(interp, pipePtr->numPids, pipePtr->pidPtr, errChan); - if (pipePtr->numPids > 0) { - ckfree((char *) pipePtr->pidPtr); - } - ckfree((char*) pipePtr); + + pipePtr->refCount--; + PipeFreeProc((ClientData) pipePtr); if (errorCode == 0) { return result; } return errorCode; } +/* + *---------------------------------------------------------------------- + * + * PipeFreeProc -- + * + * This callback is invoked by Tcl_FreeFile in order to delete + * the notifier data associated with a file handle. + * + * Results: + * None. + * + * Side effects: + * Removes the PipeInfo from the global pipe list. + * + *---------------------------------------------------------------------- + */ + +static void +PipeFreeProc(clientData) + ClientData clientData; +{ + PipeInfo *pipePtr = (PipeInfo *) clientData; + + if (--pipePtr->refCount > 0) { + return; + } + if (pipePtr->readerInfo) { + TclWinReaderFree(pipePtr->readerInfo); + } + if (pipePtr->numPids > 0) { + ckfree((char *) pipePtr->pidPtr); + } + ckfree((char *) pipePtr); +} + +/* + *---------------------------------------------------------------------- + * + * PipeSetOptionProc -- + * + * Sets an option on a channel. Used internally to set + * + * + * Results: + * A standard Tcl result. Also sets interp->result on error if + * interp is not NULL. + * + * Side effects: + * May modify an option on a device. + * + *---------------------------------------------------------------------- + */ + +static int +PipeSetOptionProc(instanceData, interp, optionName, value) + ClientData instanceData; /* File state. */ + Tcl_Interp *interp; /* For error reporting - can be NULL. */ + char *optionName; /* Which option to set? */ + char *value; /* New value for option. */ +{ + PipeInfo *pipePtr; + HANDLE handle; + int len; + + pipePtr = (PipeInfo *) instanceData; + handle = (HANDLE) ((WinFile*)pipePtr->readFile)->handle; + + if (handle == NULL) { + Tcl_AppendResult(interp, "not opened for reading", NULL); + return TCL_ERROR; + } + + len = strlen(optionName); + if ((len > 1) && (strncmp(optionName, "-iomode", len) == 0)) { + len = strlen(value); + if (len > 1) { + if (strncmp(value, "overlapped", len) == 0) { + pipePtr->readerInfo->overlappedIO = 1; + } else if (strncmp(value, "synchronous", len) == 0) { + pipePtr->readerInfo->overlappedIO = 0; + } else { + if (interp != NULL) { + Tcl_AppendResult(interp, + "bad value for -iomode: should be one of ", + "overlapped or synchronous", NULL); + return TCL_ERROR; + } + } + } + } + return TCL_OK; +} /* *---------------------------------------------------------------------- @@ -1982,7 +2390,6 @@ { PipeInfo *infoPtr = (PipeInfo *) instanceData; WinFile *filePtr = (WinFile*) infoPtr->readFile; - DWORD count; DWORD bytesRead; *errorCode = 0; @@ -1996,58 +2403,23 @@ NULL); } if (filePtr->handle == INVALID_HANDLE_VALUE) { - goto error; - } - } else { - /* - * Pipes will block until the requested number of bytes has been - * read. To avoid blocking unnecessarily, we look ahead and only - * read as much as is available. - */ - - if (PeekNamedPipe(filePtr->handle, (LPVOID) NULL, (DWORD) 0, - (LPDWORD) NULL, &count, (LPDWORD) NULL) == TRUE) { - if ((count != 0) && ((DWORD) bufSize > count)) { - bufSize = (int) count; - - /* - * This code is commented out because on Win95 we don't get - * notifier of eof on a pipe unless we try to read it. - * The correct solution is to move to threads. - */ - -/* } else if ((count == 0) && (infoPtr->flags & PIPE_ASYNC)) { */ -/* errno = *errorCode = EAGAIN; */ -/* return -1; */ - } else if ((count == 0) && !(infoPtr->flags & PIPE_ASYNC)) { - bufSize = 1; - } - } else { - goto error; + goto win32s_error; } - } - - /* - * Note that we will block on reads from a console buffer until a - * full line has been entered. The only way I know of to get - * around this is to write a console driver. We should probably - * do this at some point, but for now, we just block. - */ - - if (ReadFile(filePtr->handle, (LPVOID) buf, (DWORD) bufSize, &bytesRead, + if (ReadFile(filePtr->handle, (LPVOID) buf, (DWORD) bufSize, &bytesRead, (LPOVERLAPPED) NULL) == FALSE) { - goto error; - } + goto win32s_error; + } - return bytesRead; - - error: - TclWinConvertError(GetLastError()); - if (errno == EPIPE) { - return 0; + return bytesRead; + win32s_error: + TclWinConvertError(GetLastError()); + if (errno == EPIPE) { + return 0; + } + *errorCode = errno; + return -1; } - *errorCode = errno; - return -1; + return TclWinReaderInput(infoPtr->readerInfo, buf, bufSize, errorCode); } /* @@ -2083,9 +2455,6 @@ if (WriteFile(filePtr->handle, (LPVOID) buf, (DWORD) toWrite, &bytesWritten, (LPOVERLAPPED) NULL) == FALSE) { TclWinConvertError(GetLastError()); - if (errno == EPIPE) { - return 0; - } *errorCode = errno; return -1; } @@ -2405,6 +2774,32 @@ ckfree((char*)infoPtr); return result; +} + +/* + *---------------------------------------------------------------------- + * + * TclWinAddProcess -- + * + * Add a process to the process list so that we can use + * Tcl_WaitPid on the process. + * + * Results: + * None + * + *---------------------------------------------------------------------- + */ + +void +TclWinAddProcess(hProcess, id) + HANDLE hProcess; /* Handle to process */ + DWORD id; /* Global process identifier */ +{ + ProcInfo *procPtr = (ProcInfo *) ckalloc(sizeof(ProcInfo)); + procPtr->hProcess = hProcess; + procPtr->dwProcessId = id; + procPtr->nextPtr = procList; + procList = procPtr; } /* diff -urN /home/chaffee/tcl/tcl8.0p2-orig/win/tclWinPort.h tcl8.0/win/tclWinPort.h --- /home/chaffee/tcl/tcl8.0p2-orig/win/tclWinPort.h Fri Aug 1 10:48:50 1997 +++ tcl8.0/win/tclWinPort.h Sun Oct 19 03:10:18 1997 @@ -383,6 +383,8 @@ * Declarations for Windows specific functions. */ +EXTERN void TclWinAddProcess _ANSI_ARGS_((HANDLE hProcess, + DWORD id)); EXTERN void TclWinConvertError _ANSI_ARGS_((DWORD errCode)); EXTERN void TclWinConvertWSAError _ANSI_ARGS_((DWORD errCode)); EXTERN struct servent * PASCAL FAR @@ -396,4 +398,49 @@ TclWinNToHS _ANSI_ARGS_((u_short ns)); EXTERN int PASCAL FAR TclWinSetSockOpt _ANSI_ARGS_((SOCKET s, int level, int optname, const char FAR * optval, int optlen)); + +/* + * Used by the rType field below + */ + +#define FILE_TYPE_COM 0x1006 +#define FILE_TYPE_CONSOLE 0x1007 + +/* + * This structure describes per-instance data for a file reader thread + */ + +typedef struct TclWinReaderInfo { + HANDLE postWin; /* Window to post to when data is available */ + DWORD postMsg; /* Message to post when data is available */ + int readyMask; /* Events that need to be checked on */ + int eof; /* Are we at the end of file? */ + int async; /* Asynchronous or synchronous? */ + int overlappedIO; /* Use overlapped or non-overlapped IO */ + OVERLAPPED rOverResult; /* Overlapped read result */ + HANDLE rSemaphore; /* Semaphore for read data availability */ + HANDLE rEvent; /* Event for read data availability */ + HANDLE rGoEvent; /* Event used to allow console input to + * go forward. By holding off, an extension + * like expect can set the console mode. + * This is useful for disabling echo */ + HANDLE rHandle; /* Handle for reading pipe */ + DWORD rType; /* Win32 type for the handle */ + int rHandleClosed; /* Set if the handle was closed */ + HANDLE rThread; /* Reader thread handle if running */ + char *rData; /* Buffer with currently available data */ + int rDataLen; /* Length of currently available data */ + int rDataPos; /* Main thread reader position */ + int refCount; +} TclWinReaderInfo; + +EXTERN TclWinReaderInfo * TclWinReaderNew(HANDLE postWin, DWORD postMsg, + HANDLE file); +EXTERN void TclWinReaderFree(TclWinReaderInfo *infoPtr); +EXTERN int TclWinReaderInput(TclWinReaderInfo *infoPtr, char *buf, + int bufSize, int *errorCode); +EXTERN int TclWinReaderStart(TclWinReaderInfo *infoPtr, + LPTHREAD_START_ROUTINE threadProc); +EXTERN DWORD WINAPI TclWinReaderThread (LPVOID *arg); + #endif /* _TCLWINPORT */ diff -urN /home/chaffee/tcl/tcl8.0p2-orig/win/tclWinReader.c tcl8.0/win/tclWinReader.c --- /home/chaffee/tcl/tcl8.0p2-orig/win/tclWinReader.c Wed Dec 31 16:00:00 1969 +++ tcl8.0/win/tclWinReader.c Tue Aug 25 05:01:17 1998 @@ -0,0 +1,470 @@ +/* + * tclWinReader.c -- + * + * This file implements the Windows-specific functionality of + * reading blocking files on threads so event driven I/O can + * be done. + * + * Copyright (c) 1997 by Mitel, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + */ + +#include "tclWinInt.h" + +/* + * The following define declares a new user message for use on the + * reader window. + */ + +#define READER_MESSAGE WM_USER+20 + +/* + * This defines the buffersize we use for reading readers + */ + +#define READER_BUFFER_SIZE 8192 + +/* + * Declarations for functions used only in this file. + */ + + +/* + *---------------------------------------------------------------------- + * + * TclWinReaderNew -- + * + * Allocates a new TclWinReaderInfo structure + * + * Results: + * A TclWinReaderInfo structure + * + * Side Effects: + * None + * + *---------------------------------------------------------------------- + */ + +TclWinReaderInfo * +TclWinReaderNew(postWin, postMsg, file) + HANDLE postWin; + DWORD postMsg; + HANDLE file; +{ + TclWinReaderInfo *infoPtr; + DCB dcb; + DWORD dw; + + infoPtr = (TclWinReaderInfo *) ckalloc(sizeof(TclWinReaderInfo)); + + infoPtr->postWin = postWin; + infoPtr->postMsg = postMsg; + infoPtr->rHandle = file; + if (TclWinGetPlatformId() == VER_PLATFORM_WIN32s) { + infoPtr->rType = FILE_TYPE_DISK; + } else { + dcb.DCBlength = sizeof( DCB ) ; + if (GetCommState(file, &dcb)) { + infoPtr->rType = FILE_TYPE_COM; + } else { + infoPtr->rType = GetFileType(file); + if (infoPtr->rType == FILE_TYPE_CHAR) { + if (GetConsoleMode(file, &dw) == TRUE) { + infoPtr->rType = FILE_TYPE_CONSOLE; + } else { + dw = GetLastError(); + } + } + } + } + + /* + * overlappedIO flag is set via the SetOption interface. Ugly, + * but it works. + */ + infoPtr->overlappedIO = 0; + infoPtr->eof = 0; + infoPtr->async = 0; + infoPtr->readyMask = + (infoPtr->rType == FILE_TYPE_DISK) ? TCL_READABLE|TCL_WRITABLE : 0; + + ZeroMemory(&infoPtr->rOverResult, sizeof(infoPtr->rOverResult)); + infoPtr->rSemaphore = NULL; + infoPtr->rData = NULL; + infoPtr->rDataLen = 0; + infoPtr->rDataPos = 0; + infoPtr->rThread = NULL; + infoPtr->rEvent = NULL; + infoPtr->rGoEvent = NULL; + infoPtr->rHandleClosed = FALSE; + + infoPtr->refCount = 1; + + return infoPtr; +} + +/* + *---------------------------------------------------------------------- + * + * TclWinReaderFree -- + * + * Free a reader structure. If a thread is active, it will instead + * be freed by the thread. + * + * Results: + * None + * + * Side Effects. + * A semaphore may be released. + * + *---------------------------------------------------------------------- + */ + +void +TclWinReaderFree(infoPtr) + TclWinReaderInfo *infoPtr; +{ + if (--infoPtr->refCount <= 0) { + CloseHandle(infoPtr->rSemaphore); + CloseHandle(infoPtr->rOverResult.hEvent); + CloseHandle(infoPtr->rEvent); + CloseHandle(infoPtr->rGoEvent); + CloseHandle(infoPtr->rThread); + + if (infoPtr->rData) { + ckfree(infoPtr->rData); + } + ckfree((char *) infoPtr); + } +} + +/* + *---------------------------------------------------------------------- + * + * TclWinReaderStart -- + * + * Create a reader thread. + * + * Results: + * 1 on success, 0 on failure + * + * Side Effects: + * A thread is created and synchronization objects are created + * + *---------------------------------------------------------------------- + */ +int +TclWinReaderStart(infoPtr, threadProc) + TclWinReaderInfo *infoPtr; + LPTHREAD_START_ROUTINE threadProc; +{ + DWORD threadId; + + if (infoPtr->overlappedIO) { + infoPtr->rOverResult.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (infoPtr->rOverResult.hEvent == NULL) { + goto error; + } + } + infoPtr->rSemaphore = CreateSemaphore(NULL, 0, 1, NULL); + if (infoPtr->rSemaphore == NULL) { + goto error; + } + infoPtr->rEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (infoPtr->rEvent == NULL) { + goto error; + } + infoPtr->rGoEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (infoPtr->rGoEvent == NULL) { + goto error; + } + infoPtr->rData = ckalloc(READER_BUFFER_SIZE); + + infoPtr->refCount++; + infoPtr->rThread = CreateThread(NULL, 0, threadProc, infoPtr, 0, + &threadId); + if (infoPtr->rThread == NULL) { + --infoPtr->refCount; + goto error; + } + + return 1; + + error: + if (infoPtr->rSemaphore) { + CloseHandle(infoPtr->rSemaphore); + infoPtr->rSemaphore = NULL; + } + if (infoPtr->rOverResult.hEvent) { + CloseHandle(infoPtr->rOverResult.hEvent); + infoPtr->rOverResult.hEvent = NULL; + } + if (infoPtr->rEvent) { + CloseHandle(infoPtr->rEvent); + infoPtr->rEvent = NULL; + } + if (infoPtr->rGoEvent) { + CloseHandle(infoPtr->rGoEvent); + infoPtr->rGoEvent = NULL; + } + if (infoPtr->rData) { + ckfree(infoPtr->rData); + infoPtr->rData = NULL; + } + + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * TclWinReaderThread -- + * + * This routine reads from a file and places data into a buffer + * for the main thread to read from. + * + * Results: + * This thread exits when the child is done. + * + * Side Effects: + * This buffer should not be changed until the data has been copied. + * + *---------------------------------------------------------------------- + */ +DWORD WINAPI +TclWinReaderThread(arg) + LPVOID *arg; +{ + TclWinReaderInfo *infoPtr = (TclWinReaderInfo *) arg; + DWORD nread; + int n, cnt, nloopcnt; + DWORD nmax; + HANDLE handle; + LPOVERLAPPED overResult; + BOOL b; + DWORD err; + + handle = infoPtr->rHandle; + overResult = infoPtr->overlappedIO ? &infoPtr->rOverResult: NULL; + + n = 0; + while (1) { + nloopcnt = 0; + if (infoPtr->rHandleClosed) break; + do { + if (infoPtr->rType == FILE_TYPE_PIPE) { + nread = 0; + PeekNamedPipe(handle, NULL, 0, NULL, &nread, NULL); + if (nread == 0) { + nread = 1; + } + } else if (infoPtr->rType == FILE_TYPE_CONSOLE) { + nread = READER_BUFFER_SIZE; + } else { + nread = 1; + } + nmax = READER_BUFFER_SIZE - n; + nread = min(nread, nmax); + if (overResult) { + ResetEvent(overResult->hEvent); + } + if (infoPtr->rHandleClosed) goto done; + b = ReadFile(handle, &infoPtr->rData[n], nread, + &cnt, overResult); + if (b == FALSE) { + err = GetLastError(); + if (err == ERROR_IO_PENDING) { + if (infoPtr->rHandleClosed) goto done; + b = GetOverlappedResult(handle, overResult, + &cnt, TRUE); + if (b == FALSE) { + err = GetLastError(); + if (n == 0) { + goto done; + } + } + } else { + if (nloopcnt == 0) { + goto done; + } + } + } + if (cnt > 0) { + n += cnt; + nloopcnt += cnt; + if ((n >= READER_BUFFER_SIZE) || + (infoPtr->rType == FILE_TYPE_CONSOLE)) + { + break; + } + } else { + break; + } + if (infoPtr->rHandleClosed) goto done; + if (infoPtr->rType == FILE_TYPE_PIPE) { + PeekNamedPipe(handle, NULL, 0, NULL, &nread, NULL); + /* + * Allow subprocess to do something if we get all the + * processing time + */ + if (cnt == 1 && nread == 0) { + Sleep(40); + PeekNamedPipe(handle, NULL, 0, NULL, &nread, NULL); + } + } + } while (nread > 0); + + infoPtr->rData[n] = 0; + infoPtr->rDataLen = n; + if (nloopcnt > 0) { + if (infoPtr->rType == FILE_TYPE_CONSOLE) { + ResetEvent(infoPtr->rGoEvent); + } + SetEvent(infoPtr->rEvent); + PostMessage(infoPtr->postWin, infoPtr->postMsg, (UINT) handle, TCL_READABLE); + if (n == READER_BUFFER_SIZE || infoPtr->rType == FILE_TYPE_CONSOLE) { + WaitForSingleObject(infoPtr->rSemaphore, INFINITE); + n = 0; + if (infoPtr->rType == FILE_TYPE_CONSOLE) { + /* + * For reading from the console, do not actually start + * reading until input has been requested. The only + * reason this is necessary is that if a an extension + * (i.e. Expect) needs to set the console mode, it only + * take affect if ReadFile has not yet been called for + * console input or it will wait until the next line. + */ + WaitForSingleObject(infoPtr->rGoEvent, INFINITE); + } + } + nloopcnt = 0; + } else if (cnt <= 0 && nloopcnt <= 0) { + infoPtr->rDataLen = 0; + infoPtr->rData[0] = 0; + break; + } + } + + done: + infoPtr->eof = 1; + PostMessage(infoPtr->postWin, READER_MESSAGE, (UINT) handle, TCL_READABLE); + SetEvent(infoPtr->rEvent); + + WaitForSingleObject(infoPtr->rSemaphore, INFINITE); + + TclWinReaderFree(infoPtr); + ExitThread(0); + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * TclWinReaderInput -- + * + * Reads input from the IO channel into the buffer given. Returns + * count of how many bytes were actually read, and an error indication. + * + * Results: + * A count of how many bytes were read is returned and an error + * indication is returned in an output argument. + * + * Side effects: + * Reads input from the actual channel. + * + *---------------------------------------------------------------------- + */ + +int +TclWinReaderInput(infoPtr, buf, bufSize, errorCode) + TclWinReaderInfo *infoPtr; /* Reader state. */ + char *buf; /* Where to store data read. */ + int bufSize; /* How much space is available + * in the buffer? */ + int *errorCode; /* Where to store error code. */ +{ + int n, pos, len; + + /* + * Readers will block until the requested number of bytes has been + * read. To avoid blocking unnecessarily, we look ahead and only + * read as much as is available. + */ + + if (infoPtr->eof) { + if (infoPtr->rDataLen - infoPtr->rDataPos == 0) { + ReleaseSemaphore(infoPtr->rSemaphore, 1, NULL); + return 0; + } + } + if (infoPtr->rThread == NULL) { + TclWinReaderStart(infoPtr, TclWinReaderThread); + } + if (infoPtr->rDataLen - infoPtr->rDataPos == 0) { + if (infoPtr->rType == FILE_TYPE_CONSOLE) { + /* + * For reading from the console, do not actually start + * reading until input has been requested. The only reason + * this is necessary is that if a an extension (i.e. Expect) + * needs to set the console mode, it only take affect if + * ReadFile has not yet been called for console input + */ + SetEvent(infoPtr->rGoEvent); + } + if (infoPtr->async) { + infoPtr->readyMask &= ~TCL_READABLE; + errno = *errorCode = EAGAIN; + return -1; + } + while (1) { + if (WaitForSingleObject(infoPtr->rEvent, INFINITE) != WAIT_OBJECT_0) { + goto error; + } + if (infoPtr->rDataLen - infoPtr->rDataPos > 0) { + break; + } + if (infoPtr->eof == 1) { + break; + } + ResetEvent(infoPtr->rEvent); + } + } + if (infoPtr->rDataLen - infoPtr->rDataPos > 0) { + len = infoPtr->rDataLen; + pos = infoPtr->rDataPos; + n = min(bufSize, len - pos); + memcpy(buf, infoPtr->rData + pos, n); + infoPtr->rDataPos += n; + if (infoPtr->rDataPos == infoPtr->rDataLen) { + infoPtr->readyMask &= ~TCL_READABLE; + } + /* + * Console input gets read a line at a time and then waits. + * Pipe input gets read until the entire buffer has been + * filled and then it waits. + */ + if (infoPtr->rDataPos == READER_BUFFER_SIZE || + infoPtr->rType == FILE_TYPE_CONSOLE) + { + infoPtr->rDataPos = 0; + infoPtr->rDataLen = 0; + ResetEvent(infoPtr->rEvent); + ReleaseSemaphore(infoPtr->rSemaphore, 1, NULL); + } + return n; + } + ReleaseSemaphore(infoPtr->rSemaphore, 1, NULL); + return 0; + + error: + TclWinConvertError(GetLastError()); + if (errno == EPIPE) { + ReleaseSemaphore(infoPtr->rSemaphore, 1, NULL); + return 0; + } + *errorCode = errno; + return -1; +}