summaryrefslogtreecommitdiffstats
path: root/win
diff options
context:
space:
mode:
Diffstat (limited to 'win')
-rw-r--r--win/tclWinChan.c107
-rw-r--r--win/tclWinConsole.c7
-rw-r--r--win/tclWinFCmd.c12
-rw-r--r--win/tclWinFile.c27
-rw-r--r--win/tclWinInit.c20
-rw-r--r--win/tclWinInt.h2
-rw-r--r--win/tclWinPipe.c6
-rw-r--r--win/tclWinPort.h9
-rw-r--r--win/tclWinSerial.c25
-rw-r--r--win/tclWinSock.c120
10 files changed, 172 insertions, 163 deletions
diff --git a/win/tclWinChan.c b/win/tclWinChan.c
index 29717e0..241276a 100644
--- a/win/tclWinChan.c
+++ b/win/tclWinChan.c
@@ -95,7 +95,7 @@ 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.
*/
@@ -662,6 +662,10 @@ FileInputProc(
*errorCode = 0;
/*
+ * 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
@@ -885,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.
*/
@@ -935,11 +966,15 @@ 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) {
@@ -1479,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
diff --git a/win/tclWinConsole.c b/win/tclWinConsole.c
index bd7f0e3..b8c4782 100644
--- a/win/tclWinConsole.c
+++ b/win/tclWinConsole.c
@@ -756,6 +756,13 @@ ConsoleInputProc(
if (ReadConsoleBytes(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize,
&count) == TRUE) {
+ /*
+ * TODO: This potentially writes beyond the limits specified
+ * by the caller. In practice this is harmless, since all writes
+ * are into ChannelBuffers, and those have padding, but still
+ * ought to remove this, unless some Windows wizard can give
+ * a reason not to.
+ */
buf[count] = '\0';
return count;
}
diff --git a/win/tclWinFCmd.c b/win/tclWinFCmd.c
index 042fe67..e91f529 100644
--- a/win/tclWinFCmd.c
+++ b/win/tclWinFCmd.c
@@ -1108,7 +1108,12 @@ DoRemoveJustDirectory(
end:
if (errorPtr != NULL) {
+ char *p;
Tcl_WinTCharToUtf(nativePath, -1, errorPtr);
+ p = Tcl_DStringValue(errorPtr);
+ for (; *p; ++p) {
+ if (*p == '\\') *p = '/';
+ }
}
return TCL_ERROR;
@@ -1825,12 +1830,12 @@ SetWinFileAttributes(
Tcl_Obj *fileName, /* The name of the file. */
Tcl_Obj *attributePtr) /* The new value of the attribute. */
{
- DWORD fileAttributes;
+ DWORD fileAttributes, old;
int yesNo, result;
const TCHAR *nativeName;
nativeName = Tcl_FSGetNativePath(fileName);
- fileAttributes = GetFileAttributes(nativeName);
+ fileAttributes = old = GetFileAttributes(nativeName);
if (fileAttributes == 0xffffffff) {
StatError(interp, fileName);
@@ -1848,7 +1853,8 @@ SetWinFileAttributes(
fileAttributes &= ~(attributeArray[objIndex]);
}
- if (!SetFileAttributes(nativeName, fileAttributes)) {
+ if ((fileAttributes != old)
+ && !SetFileAttributes(nativeName, fileAttributes)) {
StatError(interp, fileName);
return TCL_ERROR;
}
diff --git a/win/tclWinFile.c b/win/tclWinFile.c
index fbc4a3a..3bfde57 100644
--- a/win/tclWinFile.c
+++ b/win/tclWinFile.c
@@ -1816,6 +1816,9 @@ TclpObjChdir(
nativePath = Tcl_FSGetNativePath(pathPtr);
+ if (!nativePath) {
+ return -1;
+ }
result = SetCurrentDirectory(nativePath);
if (result == 0) {
@@ -2897,7 +2900,8 @@ TclNativeCreateNativeRep(
char *nativePathPtr, *str;
Tcl_DString ds;
Tcl_Obj *validPathPtr;
- int len;
+ int len, i = 2;
+ WCHAR *wp;
if (TclFSCwdIsNative()) {
/*
@@ -2923,17 +2927,22 @@ TclNativeCreateNativeRep(
}
str = Tcl_GetStringFromObj(validPathPtr, &len);
- if (str[0] == '/' && str[1] == '/' && str[2] == '?' && str[3] == '/') {
- char *p;
-
- for (p = str; p && *p; ++p) {
- if (*p == '/') {
- *p = '\\';
+ Tcl_WinUtfToTChar(str, len, &ds);
+ len = Tcl_DStringLength(&ds) + sizeof(WCHAR);
+ wp = (WCHAR *) Tcl_DStringValue(&ds);
+ for (i=sizeof(WCHAR); i<len; ++wp,i+=sizeof(WCHAR)) {
+ if ( (*wp < ' ') || wcschr(L"\"*<>|", *wp) ){
+ if (!*wp){
+ /* See bug [3118489]: NUL in filenames */
+ Tcl_DecrRefCount(validPathPtr);
+ Tcl_DStringFree(&ds);
+ return NULL;
}
+ *wp |= 0xF000;
+ }else if (*wp=='/') {
+ *wp = '\\';
}
}
- Tcl_WinUtfToTChar(str, len, &ds);
- len = Tcl_DStringLength(&ds) + sizeof(WCHAR);
Tcl_DecrRefCount(validPathPtr);
nativePathPtr = ckalloc(len);
memcpy(nativePathPtr, Tcl_DStringValue(&ds), (size_t) len);
diff --git a/win/tclWinInit.c b/win/tclWinInit.c
index 2c21d38..8b600f6 100644
--- a/win/tclWinInit.c
+++ b/win/tclWinInit.c
@@ -113,8 +113,8 @@ static int ToUtf(const WCHAR *wSrc, char *dst);
*
* TclpInitPlatform --
*
- * Initialize all the platform-dependant things like signals and
- * floating-point error handling.
+ * Initialize all the platform-dependant things like signals,
+ * floating-point error handling and sockets.
*
* Called at process initialization time.
*
@@ -130,20 +130,16 @@ static int ToUtf(const WCHAR *wSrc, char *dst);
void
TclpInitPlatform(void)
{
+ WSADATA wsaData;
+ WORD wVersionRequested = MAKEWORD(2, 2);
+
tclPlatform = TCL_PLATFORM_WINDOWS;
/*
- * The following code stops Windows 3.X and Windows NT 3.51 from
- * automatically putting up Sharing Violation dialogs, e.g, when someone
- * tries to access a file that is locked or a drive with no disk in it.
- * Tcl already returns the appropriate error to the caller, and they can
- * decide to put up their own dialog in response to that failure.
- *
- * Under 95 and NT 4.0, this is a NOOP because the system doesn't
- * automatically put up dialogs when the above operations fail.
+ * Initialize the winsock library. On Windows XP and higher this
+ * can never fail.
*/
-
- SetErrorMode(SetErrorMode(0) | SEM_FAILCRITICALERRORS);
+ WSAStartup(wVersionRequested, &wsaData);
#ifdef STATIC_BUILD
/*
diff --git a/win/tclWinInt.h b/win/tclWinInt.h
index 87ccb1b..c7dbf39 100644
--- a/win/tclWinInt.h
+++ b/win/tclWinInt.h
@@ -80,7 +80,7 @@ MODULE_SCOPE Tcl_Channel TclWinOpenFileChannel(HANDLE handle, char *channelName,
int permissions, int appendMode);
MODULE_SCOPE Tcl_Channel TclWinOpenSerialChannel(HANDLE handle,
char *channelName, int permissions);
-MODULE_SCOPE HANDLE TclWinSerialReopen(HANDLE handle, const TCHAR *name,
+MODULE_SCOPE HANDLE TclWinSerialOpen(HANDLE handle, const TCHAR *name,
DWORD access);
MODULE_SCOPE int TclWinSymLinkCopyDirectory(const TCHAR *LinkOriginal,
const TCHAR *LinkCopy);
diff --git a/win/tclWinPipe.c b/win/tclWinPipe.c
index b0e830f..33493ae 100644
--- a/win/tclWinPipe.c
+++ b/win/tclWinPipe.c
@@ -82,6 +82,12 @@ static ProcInfo *procList;
#define PIPE_EXTRABYTE (1<<3) /* The reader thread has consumed one byte. */
/*
+ * TODO: It appears the whole EXTRABYTE machinery is in place to support
+ * outdated Win 95 systems. If this can be confirmed, much code can be
+ * deleted.
+ */
+
+/*
* This structure describes per-instance data for a pipe based channel.
*/
diff --git a/win/tclWinPort.h b/win/tclWinPort.h
index 6dbb12a..cb1f11b 100644
--- a/win/tclWinPort.h
+++ b/win/tclWinPort.h
@@ -525,15 +525,6 @@ typedef DWORD_PTR * PDWORD_PTR;
#define TclpSysRealloc(ptr, size) ((void*)HeapReAlloc(GetProcessHeap(), \
(DWORD)0, (LPVOID)ptr, (DWORD)size))
-/*
- * The following defines map from standard socket names to our internal
- * wrappers that redirect through the winSock function table (see the
- * file tclWinSock.c).
- */
-
-#define getservbyname TclWinGetServByName
-#define getsockopt TclWinGetSockOpt
-#define setsockopt TclWinSetSockOpt
/* This type is not defined in the Windows headers */
#define socklen_t int
diff --git a/win/tclWinSerial.c b/win/tclWinSerial.c
index 2d12fe1..aa16f67 100644
--- a/win/tclWinSerial.c
+++ b/win/tclWinSerial.c
@@ -1410,23 +1410,22 @@ SerialWriterThread(
/*
*----------------------------------------------------------------------
*
- * TclWinSerialReopen --
+ * TclWinSerialOpen --
*
- * Reopens the serial port with the OVERLAPPED FLAG set
+ * Opens or Reopens the serial port with the OVERLAPPED FLAG set
*
* Results:
- * Returns the new handle, or INVALID_HANDLE_VALUE. Normally there
- * shouldn't be any error, because the same channel has previously been
- * succeesfully opened.
+ * Returns the new handle, or INVALID_HANDLE_VALUE.
+ * If an existing channel is specified it is closed and reopened.
*
* Side effects:
- * May close the original handle
+ * May close/reopen the original handle
*
*----------------------------------------------------------------------
*/
HANDLE
-TclWinSerialReopen(
+TclWinSerialOpen(
HANDLE handle,
const TCHAR *name,
DWORD access)
@@ -1434,16 +1433,22 @@ TclWinSerialReopen(
SerialInit();
/*
+ * If an open channel is specified, close it
+ */
+
+ if ( handle != INVALID_HANDLE_VALUE && CloseHandle(handle) == FALSE) {
+ return INVALID_HANDLE_VALUE;
+ }
+
+ /*
* Multithreaded I/O needs the overlapped flag set otherwise
* ClearCommError blocks under Windows NT/2000 until serial output is
* finished
*/
- if (CloseHandle(handle) == FALSE) {
- return INVALID_HANDLE_VALUE;
- }
handle = CreateFile(name, access, 0, 0, OPEN_EXISTING,
FILE_FLAG_OVERLAPPED, 0);
+
return handle;
}
diff --git a/win/tclWinSock.c b/win/tclWinSock.c
index 4b50914..e4fc9a4 100644
--- a/win/tclWinSock.c
+++ b/win/tclWinSock.c
@@ -47,13 +47,6 @@
#include "tclWinInt.h"
-/*
- * Which version of the winsock API do we want?
- */
-
-#define WSA_VERSION_MAJOR 1
-#define WSA_VERSION_MINOR 1
-
#ifdef _MSC_VER
# pragma comment (lib, "ws2_32")
#endif
@@ -287,8 +280,7 @@ static const Tcl_ChannelType tcpChannelType = {
static void
InitSockets(void)
{
- DWORD id, err;
- WSADATA wsaData;
+ DWORD id;
ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);
if (!initialized) {
@@ -317,32 +309,6 @@ InitSockets(void)
TclWinConvertError(GetLastError());
goto initFailure;
}
-
- /*
- * Initialize the winsock library and check the interface version
- * actually loaded. We only ask for the 1.1 interface and do require
- * that it not be less than 1.1.
- */
-
- err = WSAStartup((WORD) MAKEWORD(WSA_VERSION_MAJOR,WSA_VERSION_MINOR),
- &wsaData);
- if (err != 0) {
- TclWinConvertError(err);
- goto initFailure;
- }
-
- /*
- * Note the byte positions ae swapped for the comparison, so that
- * 0x0002 (2.0, MAKEWORD(2,0)) doesn't look less than 0x0101 (1.1). We
- * want the comparison to be 0x0200 < 0x0101.
- */
-
- if (MAKEWORD(HIBYTE(wsaData.wVersion), LOBYTE(wsaData.wVersion))
- < MAKEWORD(WSA_VERSION_MINOR, WSA_VERSION_MAJOR)) {
- TclWinConvertError(WSAVERNOTSUPPORTED);
- WSACleanup();
- goto initFailure;
- }
}
/*
@@ -459,7 +425,6 @@ SocketExitHandler(
TclpFinalizeSockets();
UnregisterClass(classname, TclWinGetTclInstance());
- WSACleanup();
initialized = 0;
Tcl_MutexUnlock(&socketMutex);
}
@@ -2114,7 +2079,7 @@ TcpGetOptionProc(
int ret;
optlen = sizeof(int);
- ret = TclWinGetSockOpt(sock, SOL_SOCKET, SO_ERROR,
+ ret = getsockopt(sock, SOL_SOCKET, SO_ERROR,
(char *)&err, &optlen);
if (ret == SOCKET_ERROR) {
err = WSAGetLastError();
@@ -2449,7 +2414,7 @@ SocketThread(
*
* Side effects:
* The flags for the given socket are updated to reflect the event that
- * occured.
+ * occurred.
*
*----------------------------------------------------------------------
*/
@@ -2678,85 +2643,6 @@ InitializeHostName(
/*
*----------------------------------------------------------------------
*
- * TclWinGetSockOpt, et al. --
- *
- * These functions are wrappers that let us bind the WinSock API
- * dynamically so we can run on systems that don't have the wsock32.dll.
- * We need wrappers for these interfaces because they are called from the
- * generic Tcl code.
- *
- * Results:
- * As defined for each function.
- *
- * Side effects:
- * As defined for each function.
- *
- *----------------------------------------------------------------------
- */
-
-int
-TclWinGetSockOpt(
- SOCKET s,
- int level,
- int optname,
- char *optval,
- int *optlen)
-{
- /*
- * Check that WinSock is initialized; do not call it if not, to prevent
- * system crashes. This can happen at exit time if the exit handler for
- * WinSock ran before other exit handlers that want to use sockets.
- */
-
- if (!SocketsEnabled()) {
- return SOCKET_ERROR;
- }
-
- return getsockopt(s, level, optname, optval, optlen);
-}
-
-int
-TclWinSetSockOpt(
- SOCKET s,
- int level,
- int optname,
- const char *optval,
- int optlen)
-{
- /*
- * Check that WinSock is initialized; do not call it if not, to prevent
- * system crashes. This can happen at exit time if the exit handler for
- * WinSock ran before other exit handlers that want to use sockets.
- */
-
- if (!SocketsEnabled()) {
- return SOCKET_ERROR;
- }
-
- return setsockopt(s, level, optname, optval, optlen);
-}
-
-struct servent *
-TclWinGetServByName(
- const char *name,
- const char *proto)
-{
- /*
- * Check that WinSock is initialized; do not call it if not, to prevent
- * system crashes. This can happen at exit time if the exit handler for
- * WinSock ran before other exit handlers that want to use sockets.
- */
-
- if (!SocketsEnabled()) {
- return NULL;
- }
-
- return getservbyname(name, proto);
-}
-
-/*
- *----------------------------------------------------------------------
- *
* TcpThreadActionProc --
*
* Insert or remove any thread local refs to this channel.