summaryrefslogtreecommitdiffstats
path: root/win/tclWinChan.c
diff options
context:
space:
mode:
Diffstat (limited to 'win/tclWinChan.c')
-rw-r--r--win/tclWinChan.c238
1 files changed, 164 insertions, 74 deletions
diff --git a/win/tclWinChan.c b/win/tclWinChan.c
index b4dbbd9..48acacb 100644
--- a/win/tclWinChan.c
+++ b/win/tclWinChan.c
@@ -8,8 +8,6 @@
*
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
- *
- * RCS: @(#) $Id: tclWinChan.c,v 1.49.4.1 2008/05/23 21:10:45 andreas_kupries Exp $
*/
#include "tclWinInt.h"
@@ -85,7 +83,7 @@ static ThreadSpecificData *FileInit(void);
static int FileInputProc(ClientData instanceData, char *buf,
int toRead, int *errorCode);
static int FileOutputProc(ClientData instanceData,
- CONST char *buf, int toWrite, int *errorCode);
+ const char *buf, int toWrite, int *errorCode);
static int FileSeekProc(ClientData instanceData, long offset,
int mode, int *errorCode);
static Tcl_WideInt FileWideSeekProc(ClientData instanceData,
@@ -97,12 +95,12 @@ static void FileThreadActionProc(ClientData instanceData,
static int FileTruncateProc(ClientData instanceData,
Tcl_WideInt length);
static DWORD FileGetType(HANDLE handle);
-
+static int NativeIsComPort(CONST TCHAR *nativeName);
/*
* This structure describes the channel type structure for file based IO.
*/
-static Tcl_ChannelType fileChannelType = {
+static const Tcl_ChannelType fileChannelType = {
"file", /* Type name. */
TCL_CHANNEL_VERSION_5, /* v5 channel */
FileCloseProc, /* Close proc. */
@@ -119,25 +117,8 @@ static Tcl_ChannelType fileChannelType = {
NULL, /* handler proc. */
FileWideSeekProc, /* Wide seek proc. */
FileThreadActionProc, /* Thread action proc. */
- FileTruncateProc, /* Truncate proc. */
+ FileTruncateProc /* Truncate proc. */
};
-
-#ifdef HAVE_NO_SEH
-/*
- * Unlike Borland and Microsoft, we don't register exception handlers by
- * pushing registration records onto the runtime stack. Instead, we register
- * them by creating an EXCEPTION_REGISTRATION within the activation record.
- */
-
-typedef struct EXCEPTION_REGISTRATION {
- struct EXCEPTION_REGISTRATION* link;
- EXCEPTION_DISPOSITION (*handler)(
- struct _EXCEPTION_RECORD*, void*, struct _CONTEXT*, void*);
- void* ebp;
- void* esp;
- int status;
-} EXCEPTION_REGISTRATION;
-#endif
/*
*----------------------------------------------------------------------
@@ -276,7 +257,7 @@ FileCheckProc(
infoPtr = infoPtr->nextPtr) {
if (infoPtr->watchMask && !(infoPtr->flags & FILE_PENDING)) {
infoPtr->flags |= FILE_PENDING;
- evPtr = (FileEvent *) ckalloc(sizeof(FileEvent));
+ evPtr = ckalloc(sizeof(FileEvent));
evPtr->header.proc = FileEventProc;
evPtr->infoPtr = infoPtr;
Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
@@ -359,7 +340,7 @@ FileBlockProc(
int mode) /* TCL_MODE_BLOCKING or
* TCL_MODE_NONBLOCKING. */
{
- FileInfo *infoPtr = (FileInfo *) instanceData;
+ FileInfo *infoPtr = instanceData;
/*
* Files on Windows can not be switched between blocking and nonblocking,
@@ -397,7 +378,7 @@ FileCloseProc(
ClientData instanceData, /* Pointer to FileInfo structure. */
Tcl_Interp *interp) /* Not used. */
{
- FileInfo *fileInfoPtr = (FileInfo *) instanceData;
+ FileInfo *fileInfoPtr = instanceData;
FileInfo *infoPtr;
ThreadSpecificData *tsdPtr;
int errorCode = 0;
@@ -443,7 +424,7 @@ FileCloseProc(
break;
}
}
- ckfree((char *)fileInfoPtr);
+ ckfree(fileInfoPtr);
return errorCode;
}
@@ -472,7 +453,7 @@ FileSeekProc(
int mode, /* Relative to where should we seek? */
int *errorCodePtr) /* To store error code. */
{
- FileInfo *infoPtr = (FileInfo *) instanceData;
+ FileInfo *infoPtr = instanceData;
LONG newPos, newPosHigh, oldPos, oldPosHigh;
DWORD moveMethod;
@@ -491,7 +472,7 @@ FileSeekProc(
oldPosHigh = 0;
oldPos = SetFilePointer(infoPtr->handle, 0, &oldPosHigh, FILE_CURRENT);
- if (oldPos == INVALID_SET_FILE_POINTER) {
+ if (oldPos == (LONG)INVALID_SET_FILE_POINTER) {
DWORD winError = GetLastError();
if (winError != NO_ERROR) {
@@ -503,7 +484,7 @@ FileSeekProc(
newPosHigh = (offset < 0 ? -1 : 0);
newPos = SetFilePointer(infoPtr->handle, offset, &newPosHigh, moveMethod);
- if (newPos == INVALID_SET_FILE_POINTER) {
+ if (newPos == (LONG)INVALID_SET_FILE_POINTER) {
DWORD winError = GetLastError();
if (winError != NO_ERROR) {
@@ -550,7 +531,7 @@ FileWideSeekProc(
int mode, /* Relative to where should we seek? */
int *errorCodePtr) /* To store error code. */
{
- FileInfo *infoPtr = (FileInfo *) instanceData;
+ FileInfo *infoPtr = instanceData;
DWORD moveMethod;
LONG newPos, newPosHigh;
@@ -566,7 +547,7 @@ FileWideSeekProc(
newPosHigh = Tcl_WideAsLong(offset >> 32);
newPos = SetFilePointer(infoPtr->handle, Tcl_WideAsLong(offset),
&newPosHigh, moveMethod);
- if (newPos == INVALID_SET_FILE_POINTER) {
+ if (newPos == (LONG)INVALID_SET_FILE_POINTER) {
DWORD winError = GetLastError();
if (winError != NO_ERROR) {
@@ -599,7 +580,7 @@ FileTruncateProc(
ClientData instanceData, /* File state. */
Tcl_WideInt length) /* Length to truncate at. */
{
- FileInfo *infoPtr = (FileInfo *) instanceData;
+ FileInfo *infoPtr = instanceData;
LONG newPos, newPosHigh, oldPos, oldPosHigh;
/*
@@ -608,7 +589,7 @@ FileTruncateProc(
oldPosHigh = 0;
oldPos = SetFilePointer(infoPtr->handle, 0, &oldPosHigh, FILE_CURRENT);
- if (oldPos == INVALID_SET_FILE_POINTER) {
+ if (oldPos == (LONG)INVALID_SET_FILE_POINTER) {
DWORD winError = GetLastError();
if (winError != NO_ERROR) {
TclWinConvertError(winError);
@@ -623,7 +604,7 @@ FileTruncateProc(
newPosHigh = Tcl_WideAsLong(length >> 32);
newPos = SetFilePointer(infoPtr->handle, Tcl_WideAsLong(length),
&newPosHigh, FILE_BEGIN);
- if (newPos == INVALID_SET_FILE_POINTER) {
+ if (newPos == (LONG)INVALID_SET_FILE_POINTER) {
DWORD winError = GetLastError();
if (winError != NO_ERROR) {
TclWinConvertError(winError);
@@ -675,13 +656,16 @@ FileInputProc(
int bufSize, /* Num bytes available in buffer. */
int *errorCode) /* Where to store error code. */
{
- FileInfo *infoPtr;
+ FileInfo *infoPtr = instanceData;
DWORD bytesRead;
*errorCode = 0;
- infoPtr = (FileInfo *) instanceData;
/*
+ * TODO: This comment appears to be out of date. We *do* have a
+ * console driver, over in tclWinConsole.c. After some Windows
+ * developer confirms, this comment should be revised.
+ *
* 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
@@ -723,11 +707,11 @@ FileInputProc(
static int
FileOutputProc(
ClientData instanceData, /* File state. */
- CONST char *buf, /* The data buffer. */
+ const char *buf, /* The data buffer. */
int toWrite, /* How many bytes to write? */
int *errorCode) /* Where to store error code. */
{
- FileInfo *infoPtr = (FileInfo *) instanceData;
+ FileInfo *infoPtr = instanceData;
DWORD bytesWritten;
*errorCode = 0;
@@ -774,7 +758,7 @@ FileWatchProc(
* of TCL_READABLE, TCL_WRITABLE and
* TCL_EXCEPTION. */
{
- FileInfo *infoPtr = (FileInfo *) instanceData;
+ FileInfo *infoPtr = instanceData;
Tcl_Time blockTime = { 0, 0 };
/*
@@ -812,7 +796,7 @@ FileGetHandleProc(
int direction, /* TCL_READABLE or TCL_WRITABLE */
ClientData *handlePtr) /* Where to store the handle. */
{
- FileInfo *infoPtr = (FileInfo *) instanceData;
+ FileInfo *infoPtr = instanceData;
if (direction & infoPtr->validMask) {
*handlePtr = (ClientData) infoPtr->handle;
@@ -852,12 +836,12 @@ TclpOpenFileChannel(
Tcl_Channel channel = 0;
int channelPermissions = 0;
DWORD accessMode = 0, createMode, shareMode, flags;
- CONST TCHAR *nativeName;
+ const TCHAR *nativeName;
HANDLE handle;
char channelName[16 + TCL_INTEGER_SPACE];
TclFile readFile = NULL, writeFile = NULL;
- nativeName = (TCHAR*) Tcl_FSGetNativePath(pathPtr);
+ nativeName = Tcl_FSGetNativePath(pathPtr);
if (nativeName == NULL) {
return NULL;
}
@@ -905,6 +889,33 @@ TclpOpenFileChannel(
}
/*
+ * [2413550] Avoid double-open of serial ports on Windows
+ * Special handling for Windows serial ports by a "name-hint"
+ * to directly open it with the OVERLAPPED flag set.
+ */
+
+ if( NativeIsComPort(nativeName) ) {
+
+ handle = TclWinSerialOpen(INVALID_HANDLE_VALUE, nativeName, accessMode);
+ if (handle == INVALID_HANDLE_VALUE) {
+ TclWinConvertError(GetLastError());
+ if (interp != (Tcl_Interp *) NULL) {
+ Tcl_AppendResult(interp, "couldn't open serial \"",
+ TclGetString(pathPtr), "\": ",
+ Tcl_PosixError(interp), NULL);
+ }
+ return NULL;
+ }
+
+ /*
+ * For natively named Windows serial ports we are done.
+ */
+ channel = TclWinOpenSerialChannel(handle, channelName,
+ channelPermissions);
+
+ return channel;
+ }
+ /*
* If the file is being created, get the file attributes from the
* permissions argument, else use the existing file attributes.
*/
@@ -916,7 +927,7 @@ TclpOpenFileChannel(
flags = FILE_ATTRIBUTE_READONLY;
}
} else {
- flags = (*tclWinProcs->getFileAttributesProc)(nativeName);
+ flags = GetFileAttributes(nativeName);
if (flags == 0xFFFFFFFF) {
flags = 0;
}
@@ -932,8 +943,8 @@ TclpOpenFileChannel(
* Now we get to create the file.
*/
- handle = (*tclWinProcs->createFileProc)(nativeName, accessMode,
- shareMode, NULL, createMode, flags, (HANDLE) NULL);
+ handle = CreateFile(nativeName, accessMode, shareMode,
+ NULL, createMode, flags, (HANDLE) NULL);
if (handle == INVALID_HANDLE_VALUE) {
DWORD err = GetLastError();
@@ -943,8 +954,9 @@ TclpOpenFileChannel(
}
TclWinConvertError(err);
if (interp != (Tcl_Interp *) NULL) {
- Tcl_AppendResult(interp, "couldn't open \"", TclGetString(pathPtr),
- "\": ", Tcl_PosixError(interp), NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "couldn't open \"%s\": %s",
+ TclGetString(pathPtr), Tcl_PosixError(interp)));
}
return NULL;
}
@@ -954,17 +966,21 @@ TclpOpenFileChannel(
switch (FileGetType(handle)) {
case FILE_TYPE_SERIAL:
/*
+ * Natively named serial ports "com1-9", "\\\\.\\comXX" are
+ * already done with the code above.
+ * Here we handle all other serial port names.
+ *
* Reopen channel for OVERLAPPED operation. Normally this shouldn't
* fail, because the channel exists.
*/
- handle = TclWinSerialReopen(handle, nativeName, accessMode);
+ handle = TclWinSerialOpen(handle, nativeName, accessMode);
if (handle == INVALID_HANDLE_VALUE) {
TclWinConvertError(GetLastError());
if (interp != (Tcl_Interp *) NULL) {
- Tcl_AppendResult(interp, "couldn't reopen serial \"",
- TclGetString(pathPtr), "\": ",
- Tcl_PosixError(interp), NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "couldn't reopen serial \"%s\": %s",
+ TclGetString(pathPtr), Tcl_PosixError(interp)));
}
return NULL;
}
@@ -998,8 +1014,11 @@ TclpOpenFileChannel(
*/
channel = NULL;
- Tcl_AppendResult(interp, "couldn't open \"", TclGetString(pathPtr),
- "\": bad file type", NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "couldn't open \"%s\": bad file type",
+ TclGetString(pathPtr)));
+ Tcl_SetErrorCode(interp, "TCL", "VALUE", "CHANNEL", "BAD_TYPE",
+ NULL);
break;
}
@@ -1028,8 +1047,8 @@ Tcl_MakeFileChannel(
int mode) /* ORed combination of TCL_READABLE and
* TCL_WRITABLE to indicate file mode. */
{
-#ifdef HAVE_NO_SEH
- EXCEPTION_REGISTRATION registration;
+#if defined(HAVE_NO_SEH) && !defined(_WIN64)
+ TCLEXCEPTION_REGISTRATION registration;
#endif
char channelName[16 + TCL_INTEGER_SPACE];
Tcl_Channel channel = NULL;
@@ -1094,12 +1113,7 @@ Tcl_MakeFileChannel(
*/
result = 0;
-#ifndef HAVE_NO_SEH
- __try {
- CloseHandle(dupedHandle);
- result = 1;
- } __except (EXCEPTION_EXECUTE_HANDLER) {}
-#else
+#if defined(HAVE_NO_SEH) && !defined(_WIN64)
/*
* Don't have SEH available, do things the hard way. Note that this
* needs to be one block of asm, to avoid stack imbalance; also, it is
@@ -1115,7 +1129,7 @@ Tcl_MakeFileChannel(
"movl %[dupedHandle], %%ebx" "\n\t"
/*
- * Construct an EXCEPTION_REGISTRATION to protect the call to
+ * Construct an TCLEXCEPTION_REGISTRATION to protect the call to
* CloseHandle.
*/
@@ -1129,7 +1143,7 @@ Tcl_MakeFileChannel(
"movl $0, 0x10(%%edx)" "\n\t" /* status */
/*
- * Link the EXCEPTION_REGISTRATION on the chain.
+ * Link the TCLEXCEPTION_REGISTRATION on the chain.
*/
"movl %%edx, %%fs:0" "\n\t"
@@ -1142,7 +1156,7 @@ Tcl_MakeFileChannel(
"call _CloseHandle@4" "\n\t"
/*
- * Come here on normal exit. Recover the EXCEPTION_REGISTRATION
+ * Come here on normal exit. Recover the TCLEXCEPTION_REGISTRATION
* and put a TRUE status return into it.
*/
@@ -1152,7 +1166,7 @@ Tcl_MakeFileChannel(
"jmp 2f" "\n"
/*
- * Come here on an exception. Recover the EXCEPTION_REGISTRATION
+ * Come here on an exception. Recover the TCLEXCEPTION_REGISTRATION
*/
"1:" "\t"
@@ -1161,7 +1175,7 @@ Tcl_MakeFileChannel(
/*
* Come here however we exited. Restore context from the
- * EXCEPTION_REGISTRATION in case the stack is unbalanced.
+ * TCLEXCEPTION_REGISTRATION in case the stack is unbalanced.
*/
"2:" "\t"
@@ -1179,7 +1193,15 @@ Tcl_MakeFileChannel(
"%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"
);
result = registration.status;
-
+#else
+#ifndef HAVE_NO_SEH
+ __try {
+#endif
+ CloseHandle(dupedHandle);
+ result = 1;
+#ifndef HAVE_NO_SEH
+ } __except (EXCEPTION_EXECUTE_HANDLER) {}
+#endif
#endif
if (result == FALSE) {
return NULL;
@@ -1222,8 +1244,8 @@ TclpGetDefaultStdChannel(
Tcl_Channel channel;
HANDLE handle;
int mode = -1;
- char *bufMode = NULL;
- DWORD handleId = (DWORD)INVALID_HANDLE_VALUE;
+ const char *bufMode = NULL;
+ DWORD handleId = (DWORD) -1;
/* Standard handle to retrieve. */
switch (type) {
@@ -1322,7 +1344,7 @@ TclWinOpenFileChannel(
}
}
- infoPtr = (FileInfo *) ckalloc((unsigned) sizeof(FileInfo));
+ infoPtr = ckalloc(sizeof(FileInfo));
/*
* TIP #218. Removed the code inserting the new structure into the global
@@ -1336,10 +1358,10 @@ TclWinOpenFileChannel(
infoPtr->flags = appendMode;
infoPtr->handle = handle;
infoPtr->dirty = 0;
- wsprintfA(channelName, "file%lx", (int) infoPtr);
+ sprintf(channelName, "file%" TCL_I_MODIFIER "x", (size_t) infoPtr);
infoPtr->channel = Tcl_CreateChannel(&fileChannelType, channelName,
- (ClientData) infoPtr, permissions);
+ infoPtr, permissions);
/*
* Files have default translation of AUTO and ^Z eof char, which means
@@ -1413,7 +1435,7 @@ FileThreadActionProc(
int action)
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- FileInfo *infoPtr = (FileInfo *) instanceData;
+ FileInfo *infoPtr = instanceData;
if (action == TCL_CHANNEL_THREAD_INSERT) {
infoPtr->nextPtr = tsdPtr->firstFilePtr;
@@ -1492,6 +1514,74 @@ FileGetType(
return type;
}
+ /*
+ *----------------------------------------------------------------------
+ *
+ * NativeIsComPort --
+ *
+ * Determines if a path refers to a Windows serial port.
+ * A simple and efficient solution is to use a "name hint" to detect
+ * COM ports by their filename instead of resorting to a syscall
+ * to detect serialness after the fact.
+ * The following patterns cover common serial port names:
+ * COM[1-9]:?
+ * //./COM[0-9]+
+ * \\.\COM[0-9]+
+ *
+ * Results:
+ * 1 = serial port, 0 = not.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+NativeIsComPort(
+ const TCHAR *nativePath) /* Path of file to access, native encoding. */
+{
+ const WCHAR *p = (const WCHAR *) nativePath;
+ int i, len = wcslen(p);
+
+ /*
+ * 1. Look for com[1-9]:?
+ */
+
+ if ( (len >= 4) && (len <= 5)
+ && (_wcsnicmp(p, L"com", 3) == 0) ) {
+ /*
+ * The 4th character must be a digit 1..9 optionally followed by a ":"
+ */
+
+ if ( (p[3] < L'1') || (p[3] > L'9') ) {
+ return 0;
+ }
+ if ( (len == 5) && (p[4] != L':') ) {
+ return 0;
+ }
+ return 1;
+ }
+
+ /*
+ * 2. Look for //./com[0-9]+ or \\.\com[0-9]+
+ */
+
+ if ( (len >= 8) && (
+ (_wcsnicmp(p, L"//./com", 7) == 0)
+ || (_wcsnicmp(p, L"\\\\.\\com", 7) == 0) ) )
+ {
+ /*
+ * Charaters 8..end must be a digits 0..9
+ */
+
+ for ( i=7; i<len; i++ ) {
+ if ( (p[i] < '0') || (p[i] > '9') ) {
+ return 0;
+ }
+ }
+ return 1;
+ }
+ return 0;
+}
+
/*
* Local Variables:
* mode: c