summaryrefslogtreecommitdiffstats
path: root/win
diff options
context:
space:
mode:
authoroehhar <harald.oehlmann@elmicron.de>2015-05-31 16:20:06 (GMT)
committeroehhar <harald.oehlmann@elmicron.de>2015-05-31 16:20:06 (GMT)
commitf50357637950d7ee913d02d98cfa78ca49bd0e09 (patch)
treed53d085f4f5d210127092023f92633ef57a090f6 /win
parentf9c9b71cd327714fabe221f91e2f9af29fdd9b85 (diff)
parent32461a99d3dc5741caf2f1c282ca57fe06220b79 (diff)
downloadtcl-f50357637950d7ee913d02d98cfa78ca49bd0e09.zip
tcl-f50357637950d7ee913d02d98cfa78ca49bd0e09.tar.gz
tcl-f50357637950d7ee913d02d98cfa78ca49bd0e09.tar.bz2
merge trunk
Diffstat (limited to 'win')
-rwxr-xr-x[-rw-r--r--]win/Makefile.in83
-rw-r--r--win/README6
-rwxr-xr-xwin/configure70
-rw-r--r--win/configure.in31
-rw-r--r--win/makefile.bc41
-rw-r--r--win/makefile.vc25
-rw-r--r--win/tcl.m4278
-rw-r--r--win/tclAppInit.c64
-rw-r--r--win/tclConfig.sh.in4
-rw-r--r--win/tclWin32Dll.c67
-rw-r--r--win/tclWinChan.c133
-rw-r--r--win/tclWinConsole.c28
-rw-r--r--win/tclWinDde.c77
-rw-r--r--win/tclWinError.c2
-rw-r--r--win/tclWinFCmd.c102
-rw-r--r--win/tclWinFile.c593
-rw-r--r--win/tclWinInit.c112
-rw-r--r--win/tclWinInt.h25
-rw-r--r--win/tclWinLoad.c3
-rw-r--r--win/tclWinPipe.c27
-rw-r--r--win/tclWinPort.h35
-rw-r--r--win/tclWinReg.c6
-rw-r--r--win/tclWinSerial.c33
-rw-r--r--win/tclWinSock.c3508
-rw-r--r--win/tclWinTest.c51
-rw-r--r--win/tclWinThrd.c84
-rw-r--r--win/tclWinTime.c4
-rw-r--r--win/tclooConfig.sh4
-rw-r--r--win/tclsh.exe.manifest.in53
-rw-r--r--win/tclsh.rc13
30 files changed, 3207 insertions, 2355 deletions
diff --git a/win/Makefile.in b/win/Makefile.in
index 8ea4f0a..00c5cf2 100644..100755
--- a/win/Makefile.in
+++ b/win/Makefile.in
@@ -82,6 +82,11 @@ CFLAGS_OPTIMIZE = @CFLAGS_OPTIMIZE@
#CFLAGS = $(CFLAGS_DEBUG) $(CFLAGS_OPTIMIZE)
CFLAGS = @CFLAGS@ @CFLAGS_DEFAULT@ -DUNICODE -D_UNICODE
+# To compile without backward compatibility and deprecated code uncomment the
+# following
+NO_DEPRECATED_FLAGS =
+#NO_DEPRECATED_FLAGS = -DTCL_NO_DEPRECATED
+
# To enable compilation debugging reverse the comment characters on one of the
# following lines.
COMPILE_DEBUG_FLAGS =
@@ -90,7 +95,7 @@ COMPILE_DEBUG_FLAGS =
SRC_DIR = @srcdir@
ROOT_DIR = @srcdir@/..
-TOP_DIR = $(shell cd @srcdir@/..; pwd)
+TOP_DIR = $(shell cd @srcdir@/..; pwd -P)
GENERIC_DIR = $(TOP_DIR)/generic
TOMMATH_DIR = $(TOP_DIR)/libtommath
WIN_DIR = $(TOP_DIR)/win
@@ -105,6 +110,7 @@ GENERIC_DIR_NATIVE = $(shell $(CYGPATH) '$(GENERIC_DIR)' | sed 's!\\!/!g')
TOMMATH_DIR_NATIVE = $(shell $(CYGPATH) '$(TOMMATH_DIR)' | sed 's!\\!/!g')
WIN_DIR_NATIVE = $(shell $(CYGPATH) '$(WIN_DIR)' | sed 's!\\!/!g')
ROOT_DIR_NATIVE = $(shell $(CYGPATH) '$(ROOT_DIR)' | sed 's!\\!/!g')
+ZLIB_DIR_NATIVE = $(shell $(CYGPATH) '$(ZLIB_DIR)' | sed 's!\\!/!g')
#GENERIC_DIR_NATIVE = $(GENERIC_DIR)
#TOMMATH_DIR_NATIVE = $(TOMMATH_DIR)
#WIN_DIR_NATIVE = $(WIN_DIR)
@@ -112,7 +118,7 @@ ROOT_DIR_NATIVE = $(shell $(CYGPATH) '$(ROOT_DIR)' | sed 's!\\!/!g')
# Fully qualify library path so that `make test`
# does not depend on the current directory.
-LIBRARY_DIR1 = $(shell cd '$(ROOT_DIR_NATIVE)/library' ; pwd)
+LIBRARY_DIR1 = $(shell cd '$(ROOT_DIR_NATIVE)/library' ; pwd -P)
LIBRARY_DIR = $(shell $(CYGPATH) '$(LIBRARY_DIR1)' | sed 's!\\!/!g')
DLLSUFFIX = @DLLSUFFIX@
LIBSUFFIX = @LIBSUFFIX@
@@ -184,10 +190,10 @@ SHELL = @SHELL@
RM = rm -f
COPY = cp
-CC_SWITCHES = ${CFLAGS} ${CFLAGS_WARNING} ${TCL_SHLIB_CFLAGS} -I"${ZLIB_DIR}" \
--I"${GENERIC_DIR_NATIVE}" -DTCL_TOMMATH -DMP_PREC=4 -I"${TOMMATH_DIR_NATIVE}" \
--I"${WIN_DIR_NATIVE}" ${AC_FLAGS} \
-${COMPILE_DEBUG_FLAGS}
+CC_SWITCHES = ${CFLAGS} ${CFLAGS_WARNING} ${TCL_SHLIB_CFLAGS} \
+-I"${ZLIB_DIR_NATIVE}" -I"${GENERIC_DIR_NATIVE}" -DTCL_TOMMATH \
+-DMP_PREC=4 -I"${TOMMATH_DIR_NATIVE}" -I"${WIN_DIR_NATIVE}" \
+${AC_FLAGS} ${COMPILE_DEBUG_FLAGS} ${NO_DEPRECATED_FLAGS}
CC_OBJNAME = @CC_OBJNAME@
CC_EXENAME = @CC_EXENAME@
@@ -220,12 +226,14 @@ GENERIC_OBJS = \
tclCmdIL.$(OBJEXT) \
tclCmdMZ.$(OBJEXT) \
tclCompCmds.$(OBJEXT) \
+ tclCompCmdsGR.$(OBJEXT) \
tclCompCmdsSZ.$(OBJEXT) \
tclCompExpr.$(OBJEXT) \
tclCompile.$(OBJEXT) \
tclConfig.$(OBJEXT) \
tclDate.$(OBJEXT) \
tclDictObj.$(OBJEXT) \
+ tclDisassemble.$(OBJEXT) \
tclEncoding.$(OBJEXT) \
tclEnsemble.$(OBJEXT) \
tclEnv.$(OBJEXT) \
@@ -261,6 +269,7 @@ GENERIC_OBJS = \
tclOOMethod.$(OBJEXT) \
tclOOStubInit.$(OBJEXT) \
tclObj.$(OBJEXT) \
+ tclOptimize.$(OBJEXT) \
tclPanic.$(OBJEXT) \
tclParse.$(OBJEXT) \
tclPathObj.$(OBJEXT) \
@@ -426,7 +435,7 @@ $(CAT32): cat32.$(OBJEXT)
${TCL_STUB_LIB_FILE}: ${STUB_OBJS}
@$(RM) ${TCL_STUB_LIB_FILE}
- @MAKE_LIB@ ${STUB_OBJS}
+ @MAKE_STUB_LIB@ ${STUB_OBJS}
@POST_MAKE_LIB@
${TCL_DLL_FILE}: ${TCL_OBJS} tcl.$(RES) @ZLIB_DLL_FILE@
@@ -439,25 +448,19 @@ ${TCL_LIB_FILE}: ${TCL_OBJS} ${DDE_OBJS} ${REG_OBJS}
@MAKE_LIB@ ${TCL_OBJS} ${DDE_OBJS} ${REG_OBJS}
@POST_MAKE_LIB@
-# assume GNU make
-
-# To enable concurrent parallel make of tcl<x>.dll and tcl<x>.lib, the tcl<x>.dll
-# targets have to depend on tcl<x>.lib, this ensures that linking of tcl<x>.dll
-# does not execute concurrently with the renaming and recompiling of tcl<x>.lib
-
-${DDE_DLL_FILE}: ${DDE_OBJS} ${TCL_STUB_LIB_FILE}
+${DDE_DLL_FILE}: ${TCL_STUB_LIB_FILE} ${DDE_OBJS}
@MAKE_DLL@ ${DDE_OBJS} $(TCL_STUB_LIB_FILE) $(SHLIB_LD_LIBS)
-${REG_DLL_FILE}: ${REG_OBJS} ${TCL_STUB_LIB_FILE}
+${REG_DLL_FILE}: ${TCL_STUB_LIB_FILE} ${REG_OBJS}
@MAKE_DLL@ ${REG_OBJS} $(TCL_STUB_LIB_FILE) $(SHLIB_LD_LIBS)
-${TEST_DLL_FILE}: ${TCLTEST_OBJS} ${TCL_STUB_LIB_FILE}
+${TEST_DLL_FILE}: ${TCL_STUB_LIB_FILE} ${TCLTEST_OBJS}
@$(RM) ${TEST_DLL_FILE} ${TEST_LIB_FILE}
@MAKE_DLL@ ${TCLTEST_OBJS} $(TCL_STUB_LIB_FILE) $(SHLIB_LD_LIBS)
# use pre-built zlib1.dll
${ZLIB_DLL_FILE}: ${TCL_STUB_LIB_FILE}
- @if test "@ZLIB_LIBS@set" == "${ZLIB_DIR}/win64/zdll.libset" ; then \
+ @if test "@ZLIB_LIBS@set" != "${ZLIB_DIR}/win32/zdll.libset" ; then \
$(COPY) $(ZLIB_DIR)/win64/${ZLIB_DLL_FILE} ${ZLIB_DLL_FILE}; \
else \
$(COPY) $(ZLIB_DIR)/win32/${ZLIB_DLL_FILE} ${ZLIB_DLL_FILE}; \
@@ -544,9 +547,9 @@ gendate:
# run (and the results checked) after updating to a new release of libtommath.
gentommath_h:
- $(TCL_EXE) "$(ROOT_DIR_NATIVE)\tools\fix_tommath_h.tcl" \
- "$(TOMMATH_DIR_NATIVE)\tommath.h" \
- > "$(GENERIC_DIR_NATIVE)\tclTomMath.h"
+ $(TCL_EXE) "$(ROOT_DIR_NATIVE)/tools/fix_tommath_h.tcl" \
+ "$(TOMMATH_DIR_NATIVE)/tommath.h" \
+ > "$(GENERIC_DIR_NATIVE)/tclTomMath.h"
install: all install-binaries install-libraries install-doc install-packages
@@ -583,23 +586,23 @@ install-binaries: binaries
fi; \
done
@if [ -f $(DDE_DLL_FILE) ]; then \
- echo installing $(DDE_DLL_FILE); \
+ echo Installing $(DDE_DLL_FILE); \
$(COPY) $(DDE_DLL_FILE) $(LIB_INSTALL_DIR)/dde${DDEDOTVER}; \
$(COPY) $(ROOT_DIR)/library/dde/pkgIndex.tcl \
$(LIB_INSTALL_DIR)/dde${DDEDOTVER}; \
fi
@if [ -f $(DDE_LIB_FILE) ]; then \
- echo installing $(DDE_LIB_FILE); \
+ echo Installing $(DDE_LIB_FILE); \
$(COPY) $(DDE_LIB_FILE) $(LIB_INSTALL_DIR)/dde${DDEDOTVER}; \
fi
@if [ -f $(REG_DLL_FILE) ]; then \
- echo installing $(REG_DLL_FILE); \
+ echo Installing $(REG_DLL_FILE); \
$(COPY) $(REG_DLL_FILE) $(LIB_INSTALL_DIR)/reg${REGDOTVER}; \
$(COPY) $(ROOT_DIR)/library/reg/pkgIndex.tcl \
$(LIB_INSTALL_DIR)/reg${REGDOTVER}; \
fi
@if [ -f $(REG_LIB_FILE) ]; then \
- echo installing $(REG_LIB_FILE); \
+ echo Installing $(REG_LIB_FILE); \
$(COPY) $(REG_LIB_FILE) $(LIB_INSTALL_DIR)/reg${REGDOTVER}; \
fi
@@ -640,8 +643,8 @@ install-libraries: libraries install-tzdata install-msgs
do \
$(COPY) "$$j" "$(SCRIPT_INSTALL_DIR)/http1.0"; \
done;
- @echo "Installing package http 2.8.4 as a Tcl Module";
- @$(COPY) $(ROOT_DIR)/library/http/http.tcl $(SCRIPT_INSTALL_DIR)/../tcl8/8.6/http-2.8.4.tm;
+ @echo "Installing package http 2.8.9 as a Tcl Module";
+ @$(COPY) $(ROOT_DIR)/library/http/http.tcl $(SCRIPT_INSTALL_DIR)/../tcl8/8.6/http-2.8.9.tm;
@echo "Installing library opt0.4 directory";
@for j in $(ROOT_DIR)/library/opt/*.tcl; \
do \
@@ -649,10 +652,10 @@ install-libraries: libraries install-tzdata install-msgs
done;
@echo "Installing package msgcat 1.6.0 as a Tcl Module";
@$(COPY) $(ROOT_DIR)/library/msgcat/msgcat.tcl $(SCRIPT_INSTALL_DIR)/../tcl8/8.5/msgcat-1.6.0.tm;
- @echo "Installing package tcltest 2.3.4 as a Tcl Module";
- @$(COPY) $(ROOT_DIR)/library/tcltest/tcltest.tcl $(SCRIPT_INSTALL_DIR)/../tcl8/8.5/tcltest-2.3.4.tm;
- @echo "Installing package platform 1.0.10 as a Tcl Module";
- @$(COPY) $(ROOT_DIR)/library/platform/platform.tcl $(SCRIPT_INSTALL_DIR)/../tcl8/8.4/platform-1.0.10.tm;
+ @echo "Installing package tcltest 2.3.8 as a Tcl Module";
+ @$(COPY) $(ROOT_DIR)/library/tcltest/tcltest.tcl $(SCRIPT_INSTALL_DIR)/../tcl8/8.5/tcltest-2.3.8.tm;
+ @echo "Installing package platform 1.0.13 as a Tcl Module";
+ @$(COPY) $(ROOT_DIR)/library/platform/platform.tcl $(SCRIPT_INSTALL_DIR)/../tcl8/8.4/platform-1.0.13.tm;
@echo "Installing package platform::shell 1.1.4 as a Tcl Module";
@$(COPY) $(ROOT_DIR)/library/platform/shell.tcl $(SCRIPT_INSTALL_DIR)/../tcl8/8.4/platform/shell-1.1.4.tm;
@echo "Installing encodings";
@@ -701,14 +704,14 @@ test-tcl: binaries $(TCLSH) $(CAT32) $(TEST_DLL_FILE)
TCL_LIBRARY="$(LIBRARY_DIR)"; export TCL_LIBRARY; \
./$(TCLSH) "$(ROOT_DIR_NATIVE)/tests/all.tcl" $(TESTFLAGS) \
-load "package ifneeded Tcltest ${VERSION}@TCL_PATCH_LEVEL@ [list load [file normalize ${TEST_DLL_FILE}] Tcltest]; \
- package ifneeded dde 1.4.0b1 [list load [file normalize ${DDE_DLL_FILE}] dde]; \
+ package ifneeded dde 1.4.0 [list load [file normalize ${DDE_DLL_FILE}] dde]; \
package ifneeded registry 1.3.0 [list load [file normalize ${REG_DLL_FILE}] registry]" | ./$(CAT32)
# Useful target to launch a built tclsh with the proper path,...
runtest: binaries $(TCLSH) $(TEST_DLL_FILE)
@TCL_LIBRARY="$(LIBRARY_DIR)"; export TCL_LIBRARY; \
./$(TCLSH) $(TESTFLAGS) -load "package ifneeded Tcltest ${VERSION}@TCL_PATCH_LEVEL@ [list load [file normalize ${TEST_DLL_FILE}] Tcltest]; \
- package ifneeded dde 1.4.0b1 [list load [file normalize ${DDE_DLL_FILE}] dde]; \
+ package ifneeded dde 1.4.0 [list load [file normalize ${DDE_DLL_FILE}] dde]; \
package ifneeded registry 1.3.0 [list load [file normalize ${REG_DLL_FILE}] registry]" $(SCRIPT)
# This target can be used to run tclsh from the build directory via
@@ -748,7 +751,7 @@ PKG_CFG_ARGS = @PKG_CFG_ARGS@
PKG_DIR = ./pkgs
packages:
- @builddir=`pwd`; \
+ @builddir=`pwd -P`; \
for i in $(PKGS_DIR)/*; do \
if [ -d $$i ] ; then \
if [ -x $$i/configure ] ; then \
@@ -756,8 +759,8 @@ packages:
mkdir -p $(PKG_DIR)/$$pkg; \
if [ ! -f $(PKG_DIR)/$$pkg/Makefile ]; then \
( cd $(PKG_DIR)/$$pkg; \
- echo "Configuring package '$$i' wd = `pwd`"; \
- $$i/configure --with-tcl=$(PWD) --with-tclinclude=$(GENERIC_DIR) $(PKG_CFG_ARGS) --enable-shared --enable-threads; ) \
+ echo "Configuring package '$$i' wd = `pwd -P`"; \
+ $$i/configure --with-tcl=$$builddir --with-tclinclude=$(GENERIC_DIR) $(PKG_CFG_ARGS) --enable-shared --enable-threads; ) \
fi ; \
echo "Building package '$$pkg'"; \
( cd $(PKG_DIR)/$$pkg; $(MAKE); ) \
@@ -767,7 +770,7 @@ packages:
cd $$builddir
install-packages: packages
- @builddir=`pwd`; \
+ @builddir=`pwd -P`; \
for i in $(PKGS_DIR)/*; do \
if [ -d $$i ]; then \
pkg=`basename $$i`; \
@@ -780,7 +783,7 @@ install-packages: packages
cd $$builddir
test-packages: tcltest packages
- @builddir=`pwd`; \
+ @builddir=`pwd -P`; \
for i in $(PKGS_DIR)/*; do \
if [ -d $$i ]; then \
pkg=`basename $$i`; \
@@ -793,7 +796,7 @@ test-packages: tcltest packages
cd $$builddir
clean-packages:
- @builddir=`pwd`; \
+ @builddir=`pwd -P`; \
for i in $(PKGS_DIR)/*; do \
if [ -d $$i ]; then \
pkg=`basename $$i`; \
@@ -805,7 +808,7 @@ clean-packages:
cd $$builddir
distclean-packages:
- @builddir=`pwd`; \
+ @builddir=`pwd -P`; \
for i in $(PKGS_DIR)/*; do \
if [ -d $$i ]; then \
pkg=`basename $$i`; \
@@ -849,8 +852,10 @@ TOOL_DIR=$(ROOT_DIR)/tools
HTML_INSTALL_DIR=$(ROOT_DIR)/html
html:
$(MAKE) shell SCRIPT="$(TOOL_DIR)/tcltk-man2html.tcl --htmldir=$(HTML_INSTALL_DIR) --srcdir=$(ROOT_DIR)/.. $(BUILD_HTML_FLAGS)"
+
html-tcl: $(TCLSH)
$(MAKE) shell SCRIPT="$(TOOL_DIR)/tcltk-man2html.tcl --htmldir=$(HTML_INSTALL_DIR) --srcdir=$(ROOT_DIR)/.. $(BUILD_HTML_FLAGS) --tcl"
+
html-tk: $(TCLSH)
$(MAKE) shell SCRIPT="$(TOOL_DIR)/tcltk-man2html.tcl --htmldir=$(HTML_INSTALL_DIR) --srcdir=$(ROOT_DIR)/.. $(BUILD_HTML_FLAGS) --tk"
diff --git a/win/README b/win/README
index 8b257b1..5e060ef 100644
--- a/win/README
+++ b/win/README
@@ -79,7 +79,7 @@ Use the Makefile "install" target to install Tcl. It will install it
according to the prefix options you provided in the correct directory
structure.
-Note that in order to run tclsh85.exe, you must ensure that tcl85.dll is
+Note that in order to run tclsh86.exe, you must ensure that tcl86.dll is
on your path, in the system directory, or in the directory containing
tclsh86.exe.
@@ -91,9 +91,9 @@ Note: Tcl no longer provides support for Win32s.
This distribution contains an extensive test suite for Tcl. Some of the
tests are timing dependent and will fail from time to time. If a test is
failing consistently, please send us a bug report with as much detail as
-you can manage. Please use the online database at
+you can manage to our tracker:
- http://tcl.sourceforge.net/
+ http://core.tcl.tk/tcl/reportlist
In order to run the test suite, you build the "test" target using the
appropriate makefile for your compiler.
diff --git a/win/configure b/win/configure
index 521fc51..2401002 100755
--- a/win/configure
+++ b/win/configure
@@ -309,7 +309,7 @@ ac_includes_default="\
# include <unistd.h>
#endif"
-ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP EGREP AR ac_ct_AR RANLIB ac_ct_RANLIB RC ac_ct_RC SET_MAKE TCL_THREADS CYGPATH CELIB_DIR DL_LIBS CFLAGS_DEBUG CFLAGS_OPTIMIZE CFLAGS_WARNING ZLIB_DLL_FILE ZLIB_LIBS ZLIB_OBJS CFLAGS_DEFAULT LDFLAGS_DEFAULT VC_MANIFEST_EMBED_DLL VC_MANIFEST_EMBED_EXE TCL_VERSION TCL_MAJOR_VERSION TCL_MINOR_VERSION TCL_PATCH_LEVEL PKG_CFG_ARGS TCL_EXE TCL_LIB_FILE TCL_LIB_FLAG TCL_STATIC_LIB_FILE TCL_STATIC_LIB_FLAG TCL_IMPORT_LIB_FILE TCL_IMPORT_LIB_FLAG TCL_LIB_SPEC TCL_STUB_LIB_FILE TCL_STUB_LIB_FLAG TCL_STUB_LIB_SPEC TCL_STUB_LIB_PATH TCL_INCLUDE_SPEC TCL_BUILD_STUB_LIB_SPEC TCL_BUILD_STUB_LIB_PATH TCL_DLL_FILE TCL_SRC_DIR TCL_BIN_DIR TCL_DBGX CFG_TCL_SHARED_LIB_SUFFIX CFG_TCL_UNSHARED_LIB_SUFFIX CFG_TCL_EXPORT_FILE_SUFFIX EXTRA_CFLAGS DEPARG CC_OBJNAME CC_EXENAME LDFLAGS_DEBUG LDFLAGS_OPTIMIZE LDFLAGS_CONSOLE LDFLAGS_WINDOW STLIB_LD SHLIB_LD SHLIB_LD_LIBS SHLIB_CFLAGS SHLIB_SUFFIX TCL_SHARED_BUILD LIBS_GUI DLLSUFFIX LIBPREFIX LIBSUFFIX EXESUFFIX LIBRARIES MAKE_LIB POST_MAKE_LIB MAKE_DLL MAKE_EXE TCL_BUILD_LIB_SPEC TCL_LD_SEARCH_FLAGS TCL_NEEDS_EXP_FILE TCL_BUILD_EXP_FILE TCL_EXP_FILE TCL_LIB_VERSIONS_OK TCL_PACKAGE_PATH TCL_DDE_VERSION TCL_DDE_MAJOR_VERSION TCL_DDE_MINOR_VERSION TCL_REG_VERSION TCL_REG_MAJOR_VERSION TCL_REG_MINOR_VERSION RC_OUT RC_TYPE RC_INCLUDE RC_DEFINE RC_DEFINES RES LIBOBJS LTLIBOBJS'
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP EGREP AR ac_ct_AR RANLIB ac_ct_RANLIB RC ac_ct_RC SET_MAKE TCL_THREADS CYGPATH CELIB_DIR DL_LIBS CFLAGS_DEBUG CFLAGS_OPTIMIZE CFLAGS_WARNING ZLIB_DLL_FILE ZLIB_LIBS ZLIB_OBJS CFLAGS_DEFAULT LDFLAGS_DEFAULT VC_MANIFEST_EMBED_DLL VC_MANIFEST_EMBED_EXE TCL_WIN_VERSION MACHINE TCL_VERSION TCL_MAJOR_VERSION TCL_MINOR_VERSION TCL_PATCH_LEVEL PKG_CFG_ARGS TCL_EXE TCL_LIB_FILE TCL_LIB_FLAG TCL_STATIC_LIB_FILE TCL_STATIC_LIB_FLAG TCL_IMPORT_LIB_FILE TCL_IMPORT_LIB_FLAG TCL_LIB_SPEC TCL_STUB_LIB_FILE TCL_STUB_LIB_FLAG TCL_STUB_LIB_SPEC TCL_STUB_LIB_PATH TCL_INCLUDE_SPEC TCL_BUILD_STUB_LIB_SPEC TCL_BUILD_STUB_LIB_PATH TCL_DLL_FILE TCL_SRC_DIR TCL_BIN_DIR TCL_DBGX CFG_TCL_SHARED_LIB_SUFFIX CFG_TCL_UNSHARED_LIB_SUFFIX CFG_TCL_EXPORT_FILE_SUFFIX EXTRA_CFLAGS DEPARG CC_OBJNAME CC_EXENAME LDFLAGS_DEBUG LDFLAGS_OPTIMIZE LDFLAGS_CONSOLE LDFLAGS_WINDOW STLIB_LD SHLIB_LD SHLIB_LD_LIBS SHLIB_CFLAGS SHLIB_SUFFIX TCL_SHARED_BUILD LIBS_GUI DLLSUFFIX LIBPREFIX LIBSUFFIX EXESUFFIX LIBRARIES MAKE_LIB MAKE_STUB_LIB POST_MAKE_LIB MAKE_DLL MAKE_EXE TCL_BUILD_LIB_SPEC TCL_LD_SEARCH_FLAGS TCL_NEEDS_EXP_FILE TCL_BUILD_EXP_FILE TCL_EXP_FILE TCL_LIB_VERSIONS_OK TCL_PACKAGE_PATH TCL_DDE_VERSION TCL_DDE_MAJOR_VERSION TCL_DDE_MINOR_VERSION TCL_REG_VERSION TCL_REG_MAJOR_VERSION TCL_REG_MINOR_VERSION RC_OUT RC_TYPE RC_INCLUDE RC_DEFINE RC_DEFINES RES LIBOBJS LTLIBOBJS'
ac_subst_files=''
# Initialize some variables set by options.
@@ -1311,7 +1311,7 @@ SHELL=/bin/sh
TCL_VERSION=8.6
TCL_MAJOR_VERSION=8
TCL_MINOR_VERSION=6
-TCL_PATCH_LEVEL="b3"
+TCL_PATCH_LEVEL=".4"
VER=$TCL_MAJOR_VERSION$TCL_MINOR_VERSION
TCL_DDE_VERSION=1.4
@@ -3146,7 +3146,8 @@ echo "${ECHO_T}shared" >&6
echo "$as_me:$LINENO: result: static" >&5
echo "${ECHO_T}static" >&6
SHARED_BUILD=0
- cat >>confdefs.h <<\_ACEOF
+
+cat >>confdefs.h <<\_ACEOF
#define STATIC_BUILD 1
_ACEOF
@@ -3339,7 +3340,7 @@ cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
- #ifndef __WIN32__
+ #ifndef _WIN32
#error cross-compiler
#endif
@@ -3448,6 +3449,8 @@ echo "${ECHO_T}yes" >&6
# set various compiler flags depending on whether we are using gcc or cl
if test "${GCC}" = "yes" ; then
+ extra_cflags="-pipe"
+ extra_ldflags="-pipe -static-libgcc"
echo "$as_me:$LINENO: checking for mingw32 version of gcc" >&5
echo $ECHO_N "checking for mingw32 version of gcc... $ECHO_C" >&6
if test "${ac_cv_win32+set}" = set; then
@@ -3460,7 +3463,7 @@ cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
- #ifdef __WIN32__
+ #ifdef _WIN32
#error win32
#endif
@@ -3594,13 +3597,11 @@ echo $ECHO_N "checking compiler flags... $ECHO_C" >&6
RC_DEFINE=--define
RES=res.o
MAKE_LIB="\${STLIB_LD} \$@"
+ MAKE_STUB_LIB="\${STLIB_LD} \$@"
POST_MAKE_LIB="\${RANLIB} \$@"
MAKE_EXE="\${CC} -o \$@"
LIBPREFIX="lib"
- extra_cflags="$extra_cflags -pipe"
- extra_ldflags="$extra_ldflags -pipe"
-
if test "${SHARED_BUILD}" = "0" ; then
# static
echo "$as_me:$LINENO: result: using static flags" >&5
@@ -3629,9 +3630,8 @@ echo "$as_me: error: ${CC} does not support the -shared option.
LIBRARIES="\${SHARED_LIBRARIES}"
fi
# Link with gcc since ld does not link to default libs like
- # -luser32 and -lmsvcrt by default. Make sure CFLAGS is
- # included so -mno-cygwin passed the correct libs to the linker.
- SHLIB_LD='${CC} -shared ${CFLAGS}'
+ # -luser32 and -lmsvcrt by default.
+ SHLIB_LD='${CC} -shared'
SHLIB_LD_LIBS='${LIBS}'
MAKE_DLL="\${SHLIB_LD} \$(LDFLAGS) -o \$@ ${extra_ldflags} \
-Wl,--out-implib,\$(patsubst %.dll,lib%.a,\$@)"
@@ -4003,6 +4003,7 @@ _ACEOF
RC_DEFINE=-d
RES=res
MAKE_LIB="\${STLIB_LD} -out:\$@"
+ MAKE_STUB_LIB="\${STLIB_LD} -nodefaultlib -out:\$@"
POST_MAKE_LIB=
MAKE_EXE="\${CC} -Fe\$@"
LIBPREFIX=""
@@ -4344,12 +4345,6 @@ esac
# as we just assume that the platform hasn't got a usable z.lib
#------------------------------------------------------------------------
-if test "$do64bit" = "yes" && test "$GCC" != "yes"; then
-
- tcl_ok=no
-
-else
-
if test "${enable_shared+set}" = "set"; then
enableval="$enable_shared"
@@ -4361,21 +4356,20 @@ else
fi
-
-fi
-
if test "$tcl_ok" = "yes"; then
ZLIB_DLL_FILE=\${ZLIB_DLL_FILE}
if test "$do64bit" = "yes"; then
- ZLIB_LIBS=\${ZLIB_DIR}/win64/zdll.lib
+ if test "$GCC" == "yes"; then
+
+ ZLIB_LIBS=\${ZLIB_DIR_NATIVE}/win64/libz.dll.a
else
- ZLIB_LIBS=\${ZLIB_DIR}/win32/zdll.lib
+ ZLIB_LIBS=\${ZLIB_DIR_NATIVE}/win64/zdll.lib
fi
@@ -4383,11 +4377,15 @@ fi
else
- ZLIB_OBJS=\${ZLIB_OBJS}
+ ZLIB_LIBS=\${ZLIB_DIR_NATIVE}/win32/zdll.lib
- cat >>confdefs.h <<_ACEOF
-#define NO_VIZ 1
-_ACEOF
+
+fi
+
+
+else
+
+ ZLIB_OBJS=\${ZLIB_OBJS}
fi
@@ -5170,6 +5168,19 @@ else
TCL_PACKAGE_PATH="${prefix}/lib"
fi
+# The tclsh.exe.manifest requires these
+# TCL_WIN_VERSION is the 4 dotted pair Windows version format which needs
+# the release level, and must account for interim release versioning
+case "$TCL_PATCH_LEVEL" in
+ *a*) TCL_RELEASE_LEVEL=0 ;;
+ *b*) TCL_RELEASE_LEVEL=1 ;;
+ *) TCL_RELEASE_LEVEL=2 ;;
+esac
+TCL_WIN_VERSION="$TCL_VERSION.$TCL_RELEASE_LEVEL.`echo $TCL_PATCH_LEVEL | tr -d ab.`"
+
+# X86|AMD64|IA64 for manifest
+
+
@@ -5237,6 +5248,7 @@ fi
+
# empty on win, but needs sub'ing
@@ -5263,7 +5275,7 @@ fi
- ac_config_files="$ac_config_files Makefile tclConfig.sh tcl.hpj"
+ ac_config_files="$ac_config_files Makefile tclConfig.sh tcl.hpj tclsh.exe.manifest"
cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure
# tests run on this system so they can be shared between configure
@@ -5817,6 +5829,7 @@ do
"Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
"tclConfig.sh" ) CONFIG_FILES="$CONFIG_FILES tclConfig.sh" ;;
"tcl.hpj" ) CONFIG_FILES="$CONFIG_FILES tcl.hpj" ;;
+ "tclsh.exe.manifest" ) CONFIG_FILES="$CONFIG_FILES tclsh.exe.manifest" ;;
*) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
{ (exit 1); exit 1; }; };;
@@ -5930,6 +5943,8 @@ s,@CFLAGS_DEFAULT@,$CFLAGS_DEFAULT,;t t
s,@LDFLAGS_DEFAULT@,$LDFLAGS_DEFAULT,;t t
s,@VC_MANIFEST_EMBED_DLL@,$VC_MANIFEST_EMBED_DLL,;t t
s,@VC_MANIFEST_EMBED_EXE@,$VC_MANIFEST_EMBED_EXE,;t t
+s,@TCL_WIN_VERSION@,$TCL_WIN_VERSION,;t t
+s,@MACHINE@,$MACHINE,;t t
s,@TCL_VERSION@,$TCL_VERSION,;t t
s,@TCL_MAJOR_VERSION@,$TCL_MAJOR_VERSION,;t t
s,@TCL_MINOR_VERSION@,$TCL_MINOR_VERSION,;t t
@@ -5978,6 +5993,7 @@ s,@LIBSUFFIX@,$LIBSUFFIX,;t t
s,@EXESUFFIX@,$EXESUFFIX,;t t
s,@LIBRARIES@,$LIBRARIES,;t t
s,@MAKE_LIB@,$MAKE_LIB,;t t
+s,@MAKE_STUB_LIB@,$MAKE_STUB_LIB,;t t
s,@POST_MAKE_LIB@,$POST_MAKE_LIB,;t t
s,@MAKE_DLL@,$MAKE_DLL,;t t
s,@MAKE_EXE@,$MAKE_EXE,;t t
diff --git a/win/configure.in b/win/configure.in
index 9145ff3..fc487d0 100644
--- a/win/configure.in
+++ b/win/configure.in
@@ -14,7 +14,7 @@ SHELL=/bin/sh
TCL_VERSION=8.6
TCL_MAJOR_VERSION=8
TCL_MINOR_VERSION=6
-TCL_PATCH_LEVEL="b3"
+TCL_PATCH_LEVEL=".4"
VER=$TCL_MAJOR_VERSION$TCL_MINOR_VERSION
TCL_DDE_VERSION=1.4
@@ -120,26 +120,25 @@ esac
# as we just assume that the platform hasn't got a usable z.lib
#------------------------------------------------------------------------
-AS_IF([test "$do64bit" = "yes" && test "$GCC" != "yes"], [
- tcl_ok=no
-], [
AS_IF([test "${enable_shared+set}" = "set"], [
enableval="$enable_shared"
tcl_ok=$enableval
], [
tcl_ok=yes
])
-])
AS_IF([test "$tcl_ok" = "yes"], [
AC_SUBST(ZLIB_DLL_FILE,[\${ZLIB_DLL_FILE}])
AS_IF([test "$do64bit" = "yes"], [
- AC_SUBST(ZLIB_LIBS,[\${ZLIB_DIR}/win64/zdll.lib])
+ AS_IF([test "$GCC" == "yes"],[
+ AC_SUBST(ZLIB_LIBS,[\${ZLIB_DIR_NATIVE}/win64/libz.dll.a])
+ ], [
+ AC_SUBST(ZLIB_LIBS,[\${ZLIB_DIR_NATIVE}/win64/zdll.lib])
+ ])
], [
- AC_SUBST(ZLIB_LIBS,[\${ZLIB_DIR}/win32/zdll.lib])
+ AC_SUBST(ZLIB_LIBS,[\${ZLIB_DIR_NATIVE}/win32/zdll.lib])
])
], [
AC_SUBST(ZLIB_OBJS,[\${ZLIB_OBJS}])
- AC_DEFINE_UNQUOTED(NO_VIZ, 1)
])
AC_DEFINE(HAVE_ZLIB, 1, [Is there an installed zlib?])
@@ -351,6 +350,19 @@ else
TCL_PACKAGE_PATH="${prefix}/lib"
fi
+# The tclsh.exe.manifest requires these
+# TCL_WIN_VERSION is the 4 dotted pair Windows version format which needs
+# the release level, and must account for interim release versioning
+case "$TCL_PATCH_LEVEL" in
+ *a*) TCL_RELEASE_LEVEL=0 ;;
+ *b*) TCL_RELEASE_LEVEL=1 ;;
+ *) TCL_RELEASE_LEVEL=2 ;;
+esac
+TCL_WIN_VERSION="$TCL_VERSION.$TCL_RELEASE_LEVEL.`echo $TCL_PATCH_LEVEL | tr -d ab.`"
+AC_SUBST(TCL_WIN_VERSION)
+# X86|AMD64|IA64 for manifest
+AC_SUBST(MACHINE)
+
AC_SUBST(TCL_VERSION)
AC_SUBST(TCL_MAJOR_VERSION)
AC_SUBST(TCL_MINOR_VERSION)
@@ -414,6 +426,7 @@ AC_SUBST(LIBSUFFIX)
AC_SUBST(EXESUFFIX)
AC_SUBST(LIBRARIES)
AC_SUBST(MAKE_LIB)
+AC_SUBST(MAKE_STUB_LIB)
AC_SUBST(POST_MAKE_LIB)
AC_SUBST(MAKE_DLL)
AC_SUBST(MAKE_EXE)
@@ -444,7 +457,7 @@ AC_SUBST(RC_DEFINE)
AC_SUBST(RC_DEFINES)
AC_SUBST(RES)
-AC_OUTPUT(Makefile tclConfig.sh tcl.hpj)
+AC_OUTPUT(Makefile tclConfig.sh tcl.hpj tclsh.exe.manifest)
dnl Local Variables:
dnl mode: autoconf;
diff --git a/win/makefile.bc b/win/makefile.bc
index d17c624..f5196b6 100644
--- a/win/makefile.bc
+++ b/win/makefile.bc
@@ -200,12 +200,14 @@ TCLOBJS = \
$(TMPDIR)\tclCmdIL.obj \
$(TMPDIR)\tclCmdMZ.obj \
$(TMPDIR)\tclCompCmds.obj \
+ $(TMPDIR)\tclCompCmdsGR.obj \
$(TMPDIR)\tclCompCmdsSZ.obj \
$(TMPDIR)\tclCompExpr.obj \
$(TMPDIR)\tclCompile.obj \
$(TMPDIR)\tclConfig.obj \
$(TMPDIR)\tclDate.obj \
$(TMPDIR)\tclDictObj.obj \
+ $(TMPDIR)\tclDisassemble.obj \
$(TMPDIR)\tclEncoding.obj \
$(TMPDIR)\tclEnsemble.obj \
$(TMPDIR)\tclEnv.obj \
@@ -238,6 +240,7 @@ TCLOBJS = \
$(TMPDIR)\tclOOMethod.obj \
$(TMPDIR)\tclOOStubInit.obj \
$(TMPDIR)\tclObj.obj \
+ $(TMPDIR)\tclOptimize.obj \
$(TMPDIR)\tclPanic.obj \
$(TMPDIR)\tclParse.obj \
$(TMPDIR)\tclPipe.obj \
@@ -407,57 +410,57 @@ $(CAT32): $(WINDIR)\cat.c
install-binaries: $(TCLSH)
$(MKDIR) "$(BIN_INSTALL_DIR)"
$(MKDIR) "$(LIB_INSTALL_DIR)"
- @echo installing $(TCLDLLNAME)
+ @echo Installing $(TCLDLLNAME)
@copy "$(TCLDLL)" "$(BIN_INSTALL_DIR)"
@copy "$(TCLLIB)" "$(LIB_INSTALL_DIR)"
- @echo installing "$(TCLSH)"
+ @echo Installing "$(TCLSH)"
@copy "$(TCLSH)" "$(BIN_INSTALL_DIR)"
- @echo installing $(TCLSTUBLIBNAME)
+ @echo Installing $(TCLSTUBLIBNAME)
@copy "$(TCLSTUBLIB)" "$(LIB_INSTALL_DIR)"
- @echo installing $(WINDIR)\tclooConfig.sh
+ @echo Installing $(WINDIR)\tclooConfig.sh
@copy "$(WINDIR)\tclooConfig.sh" "$(LIB_INSTALL_DIR)"
install-libraries:
-@$(MKDIR) "$(LIB_INSTALL_DIR)"
-@$(MKDIR) "$(INCLUDE_INSTALL_DIR)"
-@$(MKDIR) "$(SCRIPT_INSTALL_DIR)"
- @echo installing http1.0
+ @echo Installing http1.0
-@$(MKDIR) "$(SCRIPT_INSTALL_DIR)\http1.0"
-@copy "$(ROOT)\library\http1.0\http.tcl" "$(SCRIPT_INSTALL_DIR)\http1.0"
-@copy "$(ROOT)\library\http1.0\pkgIndex.tcl" "$(SCRIPT_INSTALL_DIR)\http1.0"
- @echo installing http2.8
+ @echo Installing http2.8
-@$(MKDIR) "$(SCRIPT_INSTALL_DIR)\http2.8"
-@copy "$(ROOT)\library\http\http.tcl" "$(SCRIPT_INSTALL_DIR)\http2.8"
-@copy "$(ROOT)\library\http\pkgIndex.tcl" "$(SCRIPT_INSTALL_DIR)\http2.8"
- @echo installing opt0.4
+ @echo Installing opt0.4
-@$(MKDIR) "$(SCRIPT_INSTALL_DIR)\opt0.4"
-@copy "$(ROOT)\library\opt\optparse.tcl" "$(SCRIPT_INSTALL_DIR)\opt0.4"
-@copy "$(ROOT)\library\opt\pkgIndex.tcl" "$(SCRIPT_INSTALL_DIR)\opt0.4"
- @echo installing msgcat1.4
- -@$(MKDIR) "$(SCRIPT_INSTALL_DIR)\msgcat1.4"
- -@copy "$(ROOT)\library\msgcat\msgcat.tcl" "$(SCRIPT_INSTALL_DIR)\msgcat1.4"
- -@copy "$(ROOT)\library\msgcat\pkgIndex.tcl" "$(SCRIPT_INSTALL_DIR)\msgcat1.4"
- @echo installing tcltest2.3
+ @echo Installing msgcat1.5
+ -@$(MKDIR) "$(SCRIPT_INSTALL_DIR)\msgcat1.5"
+ -@copy "$(ROOT)\library\msgcat\msgcat.tcl" "$(SCRIPT_INSTALL_DIR)\msgcat1.5"
+ -@copy "$(ROOT)\library\msgcat\pkgIndex.tcl" "$(SCRIPT_INSTALL_DIR)\msgcat1.5"
+ @echo Installing tcltest2.3
-@$(MKDIR) "$(SCRIPT_INSTALL_DIR)\tcltest2.3"
-@copy "$(ROOT)\library\tcltest\tcltest.tcl" "$(SCRIPT_INSTALL_DIR)\tcltest2.3"
-@copy "$(ROOT)\library\tcltest\pkgIndex.tcl" "$(SCRIPT_INSTALL_DIR)\tcltest2.3"
- @echo installing platform1.0
+ @echo Installing platform1.0
-@$(MKDIR) "$(SCRIPT_INSTALL_DIR)\platform1.0"
-@copy "$(ROOT)\library\platform\platform.tcl" "$(SCRIPT_INSTALL_DIR)\platform1.0"
-@copy "$(ROOT)\library\platform\shell.tcl" "$(SCRIPT_INSTALL_DIR)\platform1.0"
-@copy "$(ROOT)\library\platform\pkgIndex.tcl" "$(SCRIPT_INSTALL_DIR)\platform1.0"
- @echo installing $(TCLDDEDLLNAME)
+ @echo Installing $(TCLDDEDLLNAME)
-@$(MKDIR) "$(SCRIPT_INSTALL_DIR)\dde1.3"
-@copy "$(TCLDDEDLL)" "$(SCRIPT_INSTALL_DIR)\dde1.3"
-@copy "$(ROOT)\library\dde\pkgIndex.tcl" "$(SCRIPT_INSTALL_DIR)\dde1.3"
- @echo installing $(TCLREGDLLNAME)
+ @echo Installing $(TCLREGDLLNAME)
-@$(MKDIR) "$(SCRIPT_INSTALL_DIR)\reg1.2"
-@copy "$(TCLREGDLL)" "$(SCRIPT_INSTALL_DIR)\reg1.3"
-@copy "$(ROOT)\library\reg\pkgIndex.tcl" "$(SCRIPT_INSTALL_DIR)\reg1.2"
- @echo installing encoding files
+ @echo Installing encoding files
-@$(MKDIR) "$(SCRIPT_INSTALL_DIR)\encoding"
-@copy "$(ROOT)\library\encoding\*.enc" "$(SCRIPT_INSTALL_DIR)\encoding"
- @echo installing library files
+ @echo Installing library files
-@copy "$(GENERICDIR)\tcl.h" "$(INCLUDE_INSTALL_DIR)"
-@copy "$(GENERICDIR)\tclDecls.h" "$(INCLUDE_INSTALL_DIR)"
-@copy "$(GENERICDIR)\tclOO.h" "$(INCLUDE_INSTALL_DIR)"
@@ -585,3 +588,7 @@ clean:
-@$(RM) $(TMPDIR)\*.exe
-@$(RMDIR) $(OUTDIR)
-@$(RMDIR) $(TMPDIR)
+
+# Local Variables:
+# mode: makefile
+# End:
diff --git a/win/makefile.vc b/win/makefile.vc
index ba5b710..8c65bd0 100644
--- a/win/makefile.vc
+++ b/win/makefile.vc
@@ -1,4 +1,4 @@
-#------------------------------------------------------------- -*- makefile -*-
+#-------------------------------------------------------------
# makefile.vc --
#
# Microsoft Visual C++ makefile for use with nmake.exe v1.62+ (VC++ 5.0+)
@@ -274,12 +274,14 @@ COREOBJS = \
$(TMP_DIR)\tclCmdIL.obj \
$(TMP_DIR)\tclCmdMZ.obj \
$(TMP_DIR)\tclCompCmds.obj \
+ $(TMP_DIR)\tclCompCmdsGR.obj \
$(TMP_DIR)\tclCompCmdsSZ.obj \
$(TMP_DIR)\tclCompExpr.obj \
$(TMP_DIR)\tclCompile.obj \
$(TMP_DIR)\tclConfig.obj \
$(TMP_DIR)\tclDate.obj \
$(TMP_DIR)\tclDictObj.obj \
+ $(TMP_DIR)\tclDisassemble.obj \
$(TMP_DIR)\tclEncoding.obj \
$(TMP_DIR)\tclEnsemble.obj \
$(TMP_DIR)\tclEnv.obj \
@@ -315,6 +317,7 @@ COREOBJS = \
$(TMP_DIR)\tclOOMethod.obj \
$(TMP_DIR)\tclOOStubInit.obj \
$(TMP_DIR)\tclObj.obj \
+ $(TMP_DIR)\tclOptimize.obj \
$(TMP_DIR)\tclPanic.obj \
$(TMP_DIR)\tclParse.obj \
$(TMP_DIR)\tclPathObj.obj \
@@ -578,13 +581,13 @@ test-core: setup $(TCLTEST) dlls $(CAT32)
set TCL_LIBRARY=$(ROOT:\=/)/library
!if "$(OS)" == "Windows_NT" || "$(MSVCDIR)" == "IDE"
$(DEBUGGER) $(TCLTEST) "$(ROOT:\=/)/tests/all.tcl" $(TESTFLAGS) -loadfile <<
- package ifneeded dde 1.4.0b1 [list load "$(TCLDDELIB:\=/)" dde]
+ package ifneeded dde 1.4.0 [list load "$(TCLDDELIB:\=/)" dde]
package ifneeded registry 1.3.0 [list load "$(TCLREGLIB:\=/)" registry]
<<
!else
@echo Please wait while the tests are collected...
$(TCLTEST) "$(ROOT:\=/)/tests/all.tcl" $(TESTFLAGS) -loadfile << > tests.log
- package ifneeded dde 1.4.0b1 "$(TCLDDELIB:\=/)" dde]
+ package ifneeded dde 1.4.0 "$(TCLDDELIB:\=/)" dde]
package ifneeded registry 1.3.0 "$(TCLREGLIB:\=/)" registry]
<<
type tests.log | more
@@ -620,7 +623,7 @@ $**
!endif
$(TCLSTUBLIB): $(TCLSTUBOBJS)
- $(lib32) -nologo $(LINKERFLAGS) -out:$@ $(TCLSTUBOBJS)
+ $(lib32) -nologo $(LINKERFLAGS) -nodefaultlib -out:$@ $(TCLSTUBOBJS)
$(TCLSH): $(TCLSHOBJS) $(TCLSTUBLIB) $(TCLIMPLIB)
$(link32) $(conlflags) -stack:2300000 -out:$@ $(baselibs) $**
@@ -983,6 +986,12 @@ $(TMP_DIR)\tclTomMathStubLib.obj: $(GENERICDIR)\tclTomMathStubLib.c
$(TMP_DIR)\tclOOStubLib.obj: $(GENERICDIR)\tclOOStubLib.c
$(cc32) $(STUB_CFLAGS) -Zl -DSTATIC_BUILD $(TCL_INCLUDES) -Fo$@ $?
+$(TMP_DIR)\tclsh.exe.manifest: $(WINDIR)\tclsh.exe.manifest.in
+ @nmakehlp -s << $** >$@
+@MACHINE@ $(MACHINE:IX86=X86)
+@TCL_WIN_VERSION@ $(DOTVERSION).0.0
+<<
+
#---------------------------------------------------------------------
# Generate the source dependencies. Having dependency rules will
# improve incremental build accuracy without having to resort to a
@@ -1049,12 +1058,14 @@ $<
<<
{$(WINDIR)}.rc{$(TMP_DIR)}.res:
- $(rc32) -fo $@ -r -i "$(GENERICDIR)" \
+ $(rc32) -fo $@ -r -i "$(GENERICDIR)" -i "$(TMP_DIR)" \
-d DEBUG=$(DEBUG) -d UNCHECKED=$(UNCHECKED) \
-d TCL_THREADS=$(TCL_THREADS) \
-d STATIC_BUILD=$(STATIC_BUILD) \
$<
+$(TMP_DIR)\tclsh.res: $(TMP_DIR)\tclsh.exe.manifest
+
.SUFFIXES:
.SUFFIXES:.c .rc
@@ -1219,3 +1230,7 @@ realclean: hose
hose:
@echo Hosing $(OUT_DIR)\* ...
@if exist $(OUT_DIR)\nul $(RMDIR) $(OUT_DIR)
+
+# Local Variables:
+# mode: makefile
+# End:
diff --git a/win/tcl.m4 b/win/tcl.m4
index 5e8e135..d12ae10 100644
--- a/win/tcl.m4
+++ b/win/tcl.m4
@@ -3,50 +3,124 @@
#
# Locate the tclConfig.sh file and perform a sanity check on
# the Tcl compile flags
-# Currently a no-op for Windows
#
# Arguments:
-# PATCH_LEVEL The patch level for Tcl if any.
+# none
#
# Results:
#
# Adds the following arguments to configure:
# --with-tcl=...
#
-# Sets the following vars:
-# TCL_BIN_DIR Full path to the tclConfig.sh file
+# Defines the following vars:
+# TCL_BIN_DIR Full path to the directory containing
+# the tclConfig.sh file
#------------------------------------------------------------------------
AC_DEFUN([SC_PATH_TCLCONFIG], [
- AC_MSG_CHECKING([the location of tclConfig.sh])
+ #
+ # Ok, lets find the tcl configuration
+ # First, look for one uninstalled.
+ # the alternative search directory is invoked by --with-tcl
+ #
- if test -d ../../tcl8.6$1/win; then
- TCL_BIN_DIR_DEFAULT=../../tcl8.6$1/win
- elif test -d ../../tcl8.6/win; then
- TCL_BIN_DIR_DEFAULT=../../tcl8.6/win
- else
- TCL_BIN_DIR_DEFAULT=../../tcl/win
- fi
+ if test x"${no_tcl}" = x ; then
+ # we reset no_tcl in case something fails here
+ no_tcl=true
+ AC_ARG_WITH(tcl,
+ AC_HELP_STRING([--with-tcl],
+ [directory containing tcl configuration (tclConfig.sh)]),
+ with_tclconfig="${withval}")
+ AC_MSG_CHECKING([for Tcl configuration])
+ AC_CACHE_VAL(ac_cv_c_tclconfig,[
+
+ # First check to see if --with-tcl was specified.
+ if test x"${with_tclconfig}" != x ; then
+ case "${with_tclconfig}" in
+ */tclConfig.sh )
+ if test -f "${with_tclconfig}"; then
+ AC_MSG_WARN([--with-tcl argument should refer to directory containing tclConfig.sh, not to tclConfig.sh itself])
+ with_tclconfig="`echo "${with_tclconfig}" | sed 's!/tclConfig\.sh$!!'`"
+ fi ;;
+ esac
+ if test -f "${with_tclconfig}/tclConfig.sh" ; then
+ ac_cv_c_tclconfig="`(cd "${with_tclconfig}"; pwd)`"
+ else
+ AC_MSG_ERROR([${with_tclconfig} directory doesn't contain tclConfig.sh])
+ fi
+ fi
- AC_ARG_WITH(tcl, [ --with-tcl=DIR use Tcl 8.6 binaries from DIR],
- TCL_BIN_DIR=$withval, TCL_BIN_DIR=`cd $TCL_BIN_DIR_DEFAULT; pwd`)
- if test ! -d $TCL_BIN_DIR; then
- AC_MSG_ERROR(Tcl directory $TCL_BIN_DIR does not exist)
- fi
- if test ! -f $TCL_BIN_DIR/tclConfig.sh; then
- if test ! -f $TCL_BIN_DIR/../unix/tclConfig.sh; then
- AC_MSG_ERROR(There is no tclConfig.sh in $TCL_BIN_DIR: perhaps you did not specify the Tcl *build* directory (not the toplevel Tcl directory) or you forgot to configure Tcl?)
+ # then check for a private Tcl installation
+ if test x"${ac_cv_c_tclconfig}" = x ; then
+ for i in \
+ ../tcl \
+ `ls -dr ../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+ `ls -dr ../tcl[[8-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ../tcl[[8-9]].[[0-9]]* 2>/dev/null` \
+ ../../tcl \
+ `ls -dr ../../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+ `ls -dr ../../tcl[[8-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ../../tcl[[8-9]].[[0-9]]* 2>/dev/null` \
+ ../../../tcl \
+ `ls -dr ../../../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+ `ls -dr ../../../tcl[[8-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ../../../tcl[[8-9]].[[0-9]]* 2>/dev/null` ; do
+ if test -f "$i/win/tclConfig.sh" ; then
+ ac_cv_c_tclconfig="`(cd $i/win; pwd)`"
+ break
+ fi
+ done
+ fi
+
+ # check in a few common install locations
+ if test x"${ac_cv_c_tclconfig}" = x ; then
+ for i in `ls -d ${libdir} 2>/dev/null` \
+ `ls -d ${exec_prefix}/lib 2>/dev/null` \
+ `ls -d ${prefix}/lib 2>/dev/null` \
+ `ls -d /cygdrive/c/Tcl/lib 2>/dev/null` \
+ `ls -d /cygdrive/c/Progra~1/Tcl/lib 2>/dev/null` \
+ `ls -d /c/Tcl/lib 2>/dev/null` \
+ `ls -d /c/Progra~1/Tcl/lib 2>/dev/null` \
+ `ls -d C:/Tcl/lib 2>/dev/null` \
+ `ls -d C:/Progra~1/Tcl/lib 2>/dev/null` \
+ ; do
+ if test -f "$i/tclConfig.sh" ; then
+ ac_cv_c_tclconfig="`(cd $i; pwd)`"
+ break
+ fi
+ done
+ fi
+
+ # check in a few other private locations
+ if test x"${ac_cv_c_tclconfig}" = x ; then
+ for i in \
+ ${srcdir}/../tcl \
+ `ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+ `ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]]* 2>/dev/null` ; do
+ if test -f "$i/win/tclConfig.sh" ; then
+ ac_cv_c_tclconfig="`(cd $i/win; pwd)`"
+ break
+ fi
+ done
+ fi
+ ])
+
+ if test x"${ac_cv_c_tclconfig}" = x ; then
+ TCL_BIN_DIR="# no Tcl configs found"
+ AC_MSG_ERROR([Can't find Tcl configuration definitions. Use --with-tcl to specify a directory containing tclConfig.sh])
+ else
+ no_tcl=
+ TCL_BIN_DIR="${ac_cv_c_tclconfig}"
+ AC_MSG_RESULT([found ${TCL_BIN_DIR}/tclConfig.sh])
fi
- TCL_BIN_DIR=`cd ${TCL_BIN_DIR}/../unix; pwd`
fi
- AC_MSG_RESULT($TCL_BIN_DIR/tclConfig.sh)
])
#------------------------------------------------------------------------
# SC_PATH_TKCONFIG --
#
# Locate the tkConfig.sh file
-# Currently a no-op for Windows
#
# Arguments:
# none
@@ -56,31 +130,109 @@ AC_DEFUN([SC_PATH_TCLCONFIG], [
# Adds the following arguments to configure:
# --with-tk=...
#
-# Sets the following vars:
-# TK_BIN_DIR Full path to the tkConfig.sh file
+# Defines the following vars:
+# TK_BIN_DIR Full path to the directory containing
+# the tkConfig.sh file
#------------------------------------------------------------------------
AC_DEFUN([SC_PATH_TKCONFIG], [
- AC_MSG_CHECKING([the location of tkConfig.sh])
+ #
+ # Ok, lets find the tk configuration
+ # First, look for one uninstalled.
+ # the alternative search directory is invoked by --with-tk
+ #
- if test -d ../../tk8.6$1/win; then
- TK_BIN_DIR_DEFAULT=../../tk8.6$1/win
- elif test -d ../../tk8.6/win; then
- TK_BIN_DIR_DEFAULT=../../tk8.6/win
- else
- TK_BIN_DIR_DEFAULT=../../tk/win
- fi
+ if test x"${no_tk}" = x ; then
+ # we reset no_tk in case something fails here
+ no_tk=true
+ AC_ARG_WITH(tk,
+ AC_HELP_STRING([--with-tk],
+ [directory containing tk configuration (tkConfig.sh)]),
+ with_tkconfig="${withval}")
+ AC_MSG_CHECKING([for Tk configuration])
+ AC_CACHE_VAL(ac_cv_c_tkconfig,[
+
+ # First check to see if --with-tkconfig was specified.
+ if test x"${with_tkconfig}" != x ; then
+ case "${with_tkconfig}" in
+ */tkConfig.sh )
+ if test -f "${with_tkconfig}"; then
+ AC_MSG_WARN([--with-tk argument should refer to directory containing tkConfig.sh, not to tkConfig.sh itself])
+ with_tkconfig="`echo "${with_tkconfig}" | sed 's!/tkConfig\.sh$!!'`"
+ fi ;;
+ esac
+ if test -f "${with_tkconfig}/tkConfig.sh" ; then
+ ac_cv_c_tkconfig="`(cd "${with_tkconfig}"; pwd)`"
+ else
+ AC_MSG_ERROR([${with_tkconfig} directory doesn't contain tkConfig.sh])
+ fi
+ fi
- AC_ARG_WITH(tk, [ --with-tk=DIR use Tk 8.6 binaries from DIR],
- TK_BIN_DIR=$withval, TK_BIN_DIR=`cd $TK_BIN_DIR_DEFAULT; pwd`)
- if test ! -d $TK_BIN_DIR; then
- AC_MSG_ERROR(Tk directory $TK_BIN_DIR does not exist)
- fi
- if test ! -f $TK_BIN_DIR/tkConfig.sh; then
- AC_MSG_ERROR(There is no tkConfig.sh in $TK_BIN_DIR: perhaps you did not specify the Tk *build* directory (not the toplevel Tk directory) or you forgot to configure Tk?)
- fi
+ # then check for a private Tk library
+ if test x"${ac_cv_c_tkconfig}" = x ; then
+ for i in \
+ ../tk \
+ `ls -dr ../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+ `ls -dr ../tk[[8-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ../tk[[8-9]].[[0-9]]* 2>/dev/null` \
+ ../../tk \
+ `ls -dr ../../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+ `ls -dr ../../tk[[8-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ../../tk[[8-9]].[[0-9]]* 2>/dev/null` \
+ ../../../tk \
+ `ls -dr ../../../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+ `ls -dr ../../../tk[[8-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ../../../tk[[8-9]].[[0-9]]* 2>/dev/null` ; do
+ if test -f "$i/win/tkConfig.sh" ; then
+ ac_cv_c_tkconfig="`(cd $i/win; pwd)`"
+ break
+ fi
+ done
+ fi
- AC_MSG_RESULT([$TK_BIN_DIR/tkConfig.sh])
+ # check in a few common install locations
+ if test x"${ac_cv_c_tkconfig}" = x ; then
+ for i in `ls -d ${libdir} 2>/dev/null` \
+ `ls -d ${exec_prefix}/lib 2>/dev/null` \
+ `ls -d ${prefix}/lib 2>/dev/null` \
+ `ls -d /cygdrive/c/Tcl/lib 2>/dev/null` \
+ `ls -d /cygdrive/c/Progra~1/Tcl/lib 2>/dev/null` \
+ `ls -d /c/Tcl/lib 2>/dev/null` \
+ `ls -d /c/Progra~1/Tcl/lib 2>/dev/null` \
+ `ls -d C:/Tcl/lib 2>/dev/null` \
+ `ls -d C:/Progra~1/Tcl/lib 2>/dev/null` \
+ ; do
+ if test -f "$i/tkConfig.sh" ; then
+ ac_cv_c_tkconfig="`(cd $i; pwd)`"
+ break
+ fi
+ done
+ fi
+
+ # check in a few other private locations
+ if test x"${ac_cv_c_tkconfig}" = x ; then
+ for i in \
+ ${srcdir}/../tk \
+ `ls -dr ${srcdir}/../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+ `ls -dr ${srcdir}/../tk[[8-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ${srcdir}/../tk[[8-9]].[[0-9]]* 2>/dev/null` ; do
+ if test -f "$i/win/tkConfig.sh" ; then
+ ac_cv_c_tkconfig="`(cd $i/win; pwd)`"
+ break
+ fi
+ done
+ fi
+ ])
+
+ if test x"${ac_cv_c_tkconfig}" = x ; then
+ TK_BIN_DIR="# no Tk configs found"
+ AC_MSG_ERROR([Can't find Tk configuration definitions. Use --with-tk to specify a directory containing tkConfig.sh])
+ else
+ no_tk=
+ TK_BIN_DIR="${ac_cv_c_tkconfig}"
+ AC_MSG_RESULT([found ${TK_BIN_DIR}/tkConfig.sh])
+ fi
+ fi
])
#------------------------------------------------------------------------
@@ -95,7 +247,7 @@ AC_DEFUN([SC_PATH_TKCONFIG], [
#
# Results:
#
-# Subst the following vars:
+# Substitutes the following vars:
# TCL_BIN_DIR
# TCL_SRC_DIR
# TCL_LIB_FILE
@@ -103,13 +255,13 @@ AC_DEFUN([SC_PATH_TKCONFIG], [
#------------------------------------------------------------------------
AC_DEFUN([SC_LOAD_TCLCONFIG], [
- AC_MSG_CHECKING([for existence of $TCL_BIN_DIR/tclConfig.sh])
+ AC_MSG_CHECKING([for existence of ${TCL_BIN_DIR}/tclConfig.sh])
- if test -f "$TCL_BIN_DIR/tclConfig.sh" ; then
+ if test -f "${TCL_BIN_DIR}/tclConfig.sh" ; then
AC_MSG_RESULT([loading])
- . $TCL_BIN_DIR/tclConfig.sh
+ . "${TCL_BIN_DIR}/tclConfig.sh"
else
- AC_MSG_RESULT([file not found])
+ AC_MSG_RESULT([could not find ${TCL_BIN_DIR}/tclConfig.sh])
fi
#
@@ -158,7 +310,6 @@ AC_DEFUN([SC_LOAD_TCLCONFIG], [
# SC_LOAD_TKCONFIG --
#
# Load the tkConfig.sh file
-# Currently a no-op for Windows
#
# Arguments:
#
@@ -172,13 +323,13 @@ AC_DEFUN([SC_LOAD_TCLCONFIG], [
#------------------------------------------------------------------------
AC_DEFUN([SC_LOAD_TKCONFIG], [
- AC_MSG_CHECKING([for existence of $TK_BIN_DIR/tkConfig.sh])
+ AC_MSG_CHECKING([for existence of ${TK_BIN_DIR}/tkConfig.sh])
- if test -f "$TK_BIN_DIR/tkConfig.sh" ; then
+ if test -f "${TK_BIN_DIR}/tkConfig.sh" ; then
AC_MSG_RESULT([loading])
- . $TK_BIN_DIR/tkConfig.sh
+ . "${TK_BIN_DIR}/tkConfig.sh"
else
- AC_MSG_RESULT([could not find $TK_BIN_DIR/tkConfig.sh])
+ AC_MSG_RESULT([could not find ${TK_BIN_DIR}/tkConfig.sh])
fi
@@ -212,7 +363,7 @@ AC_DEFUN([SC_ENABLE_SHARED], [
AC_MSG_CHECKING([how to build libraries])
AC_ARG_ENABLE(shared,
[ --enable-shared build and link with shared libraries (default: on)],
- [tcl_ok=$enableval], [tcl_ok=yes])
+ [tcl_ok=$enableval], [tcl_ok=yes])
if test "${enable_shared+set}" = set; then
enableval="$enable_shared"
@@ -227,7 +378,7 @@ AC_DEFUN([SC_ENABLE_SHARED], [
else
AC_MSG_RESULT([static])
SHARED_BUILD=0
- AC_DEFINE(STATIC_BUILD)
+ AC_DEFINE(STATIC_BUILD, 1, [Is this a static build?])
fi
])
@@ -270,7 +421,7 @@ AC_DEFUN([SC_ENABLE_THREADS], [
#------------------------------------------------------------------------
# SC_ENABLE_SYMBOLS --
#
-# Specify if debugging symbols should be used
+# Specify if debugging symbols should be used.
# Memory (TCL_MEM_DEBUG) and compile (TCL_COMPILE_DEBUG) debugging
# can also be enabled.
#
@@ -372,6 +523,7 @@ AC_DEFUN([SC_ENABLE_SYMBOLS], [
# RES
#
# MAKE_LIB
+# MAKE_STUB_LIB
# MAKE_EXE
# MAKE_DLL
#
@@ -420,7 +572,7 @@ AC_DEFUN([SC_CONFIG_CFLAGS], [
AC_CACHE_CHECK(for cross-compile version of gcc,
ac_cv_cross,
AC_TRY_COMPILE([
- #ifndef __WIN32__
+ #ifndef _WIN32
#error cross-compiler
#endif
], [],
@@ -482,10 +634,12 @@ AC_DEFUN([SC_CONFIG_CFLAGS], [
# set various compiler flags depending on whether we are using gcc or cl
if test "${GCC}" = "yes" ; then
+ extra_cflags="-pipe"
+ extra_ldflags="-pipe -static-libgcc"
AC_CACHE_CHECK(for mingw32 version of gcc,
ac_cv_win32,
AC_TRY_COMPILE([
- #ifdef __WIN32__
+ #ifdef _WIN32
#error win32
#endif
], [],
@@ -529,13 +683,11 @@ AC_DEFUN([SC_CONFIG_CFLAGS], [
RC_DEFINE=--define
RES=res.o
MAKE_LIB="\${STLIB_LD} \[$]@"
+ MAKE_STUB_LIB="\${STLIB_LD} \[$]@"
POST_MAKE_LIB="\${RANLIB} \[$]@"
MAKE_EXE="\${CC} -o \[$]@"
LIBPREFIX="lib"
- extra_cflags="$extra_cflags -pipe"
- extra_ldflags="$extra_ldflags -pipe"
-
if test "${SHARED_BUILD}" = "0" ; then
# static
AC_MSG_RESULT([using static flags])
@@ -559,9 +711,8 @@ AC_DEFUN([SC_CONFIG_CFLAGS], [
LIBRARIES="\${SHARED_LIBRARIES}"
fi
# Link with gcc since ld does not link to default libs like
- # -luser32 and -lmsvcrt by default. Make sure CFLAGS is
- # included so -mno-cygwin passed the correct libs to the linker.
- SHLIB_LD='${CC} -shared ${CFLAGS}'
+ # -luser32 and -lmsvcrt by default.
+ SHLIB_LD='${CC} -shared'
SHLIB_LD_LIBS='${LIBS}'
MAKE_DLL="\${SHLIB_LD} \$(LDFLAGS) -o \[$]@ ${extra_ldflags} \
-Wl,--out-implib,\$(patsubst %.dll,lib%.a,\[$]@)"
@@ -810,6 +961,7 @@ AC_DEFUN([SC_CONFIG_CFLAGS], [
RC_DEFINE=-d
RES=res
MAKE_LIB="\${STLIB_LD} -out:\[$]@"
+ MAKE_STUB_LIB="\${STLIB_LD} -nodefaultlib -out:\[$]@"
POST_MAKE_LIB=
MAKE_EXE="\${CC} -Fe\[$]@"
LIBPREFIX=""
diff --git a/win/tclAppInit.c b/win/tclAppInit.c
index d6da500..e06eaf5 100644
--- a/win/tclAppInit.c
+++ b/win/tclAppInit.c
@@ -3,7 +3,8 @@
*
* Provides a default version of the main program and Tcl_AppInit
* procedure for tclsh and other Tcl-based applications (without Tk).
- * Note that this program must be built in Win32 console mode to work properly.
+ * Note that this program must be built in Win32 console mode to work
+ * properly.
*
* Copyright (c) 1993 The Regents of the University of California.
* Copyright (c) 1994-1997 Sun Microsystems, Inc.
@@ -15,7 +16,9 @@
#include "tcl.h"
#define WIN32_LEAN_AND_MEAN
+#define STRICT /* See MSDN Article Q83456 */
#include <windows.h>
+#undef STRICT
#undef WIN32_LEAN_AND_MEAN
#include <locale.h>
#include <stdlib.h>
@@ -33,19 +36,23 @@ extern Tcl_PackageInitProc Dde_SafeInit;
#endif
#ifdef TCL_BROKEN_MAINARGS
+int _CRT_glob = 0;
static void setargv(int *argcPtr, TCHAR ***argvPtr);
-#endif
+#endif /* TCL_BROKEN_MAINARGS */
/*
* The following #if block allows you to change the AppInit function by using
* a #define of TCL_LOCAL_APPINIT instead of rewriting this entire file. The
- * #if checks for that #define and uses Tcl_AppInit if it doesn't exist.
+ * #if checks for that #define and uses Tcl_AppInit if it does not exist.
*/
#ifndef TCL_LOCAL_APPINIT
#define TCL_LOCAL_APPINIT Tcl_AppInit
#endif
-extern int TCL_LOCAL_APPINIT(Tcl_Interp *interp);
+#ifndef MODULE_SCOPE
+# define MODULE_SCOPE extern
+#endif
+MODULE_SCOPE int TCL_LOCAL_APPINIT(Tcl_Interp *);
/*
* The following #if block allows you to change how Tcl finds the startup
@@ -54,7 +61,7 @@ extern int TCL_LOCAL_APPINIT(Tcl_Interp *interp);
*/
#ifdef TCL_LOCAL_MAIN_HOOK
-extern int TCL_LOCAL_MAIN_HOOK(int *argc, TCHAR ***argv);
+MODULE_SCOPE int TCL_LOCAL_MAIN_HOOK(int *argc, TCHAR ***argv);
#endif
/*
@@ -77,15 +84,15 @@ extern int TCL_LOCAL_MAIN_HOOK(int *argc, TCHAR ***argv);
#ifdef TCL_BROKEN_MAINARGS
int
main(
- int argc,
- char *dummy[])
+ int argc, /* Number of command-line arguments. */
+ char *dummy[]) /* Not used. */
{
TCHAR **argv;
#else
int
_tmain(
- int argc,
- TCHAR *argv[])
+ int argc, /* Number of command-line arguments. */
+ TCHAR *argv[]) /* Values of command-line arguments. */
{
#endif
TCHAR *p;
@@ -99,7 +106,7 @@ _tmain(
#ifdef TCL_BROKEN_MAINARGS
/*
- * Get our args from the c-runtime. Ignore lpszCmdLine.
+ * Get our args from the c-runtime. Ignore command line.
*/
setargv(&argc, &argv);
@@ -109,9 +116,9 @@ _tmain(
* Forward slashes substituted for backslashes.
*/
- for (p = argv[0]; *p != TEXT('\0'); p++) {
- if (*p == TEXT('\\')) {
- *p = TEXT('/');
+ for (p = argv[0]; *p != '\0'; p++) {
+ if (*p == '\\') {
+ *p = '/';
}
}
@@ -189,11 +196,12 @@ Tcl_AppInit(
/*
* Specify a user-specific startup file to invoke if the application is
* run interactively. Typically the startup file is "~/.apprc" where "app"
- * is the name of the application. If this line is deleted then no user-
- * specific startup file will be run under any conditions.
+ * is the name of the application. If this line is deleted then no
+ * user-specific startup file will be run under any conditions.
*/
- (Tcl_SetVar)(interp, "tcl_rcFileName", "~/tclshrc.tcl", TCL_GLOBAL_ONLY);
+ (Tcl_ObjSetVar2)(interp, Tcl_NewStringObj("tcl_rcFileName", -1), NULL,
+ Tcl_NewStringObj("~/tclshrc.tcl", -1), TCL_GLOBAL_ONLY);
return TCL_OK;
}
@@ -242,13 +250,13 @@ setargv(
*/
size = 2;
- for (p = cmdLine; *p != TEXT('\0'); p++) {
- if ((*p == TEXT(' ')) || (*p == TEXT('\t'))) { /* INTL: ISO space. */
+ for (p = cmdLine; *p != '\0'; p++) {
+ if ((*p == ' ') || (*p == '\t')) { /* INTL: ISO space. */
size++;
- while ((*p == TEXT(' ')) || (*p == TEXT('\t'))) { /* INTL: ISO space. */
+ while ((*p == ' ') || (*p == '\t')) { /* INTL: ISO space. */
p++;
}
- if (*p == TEXT('\0')) {
+ if (*p == '\0') {
break;
}
}
@@ -267,10 +275,10 @@ setargv(
p = cmdLine;
for (argc = 0; argc < size; argc++) {
argv[argc] = arg = argSpace;
- while ((*p == TEXT(' ')) || (*p == TEXT('\t'))) { /* INTL: ISO space. */
+ while ((*p == ' ') || (*p == '\t')) { /* INTL: ISO space. */
p++;
}
- if (*p == TEXT('\0')) {
+ if (*p == '\0') {
break;
}
@@ -278,14 +286,14 @@ setargv(
slashes = 0;
while (1) {
copy = 1;
- while (*p == TEXT('\\')) {
+ while (*p == '\\') {
slashes++;
p++;
}
- if (*p == TEXT('"')) {
+ if (*p == '"') {
if ((slashes & 1) == 0) {
copy = 0;
- if ((inquote) && (p[1] == TEXT('"'))) {
+ if ((inquote) && (p[1] == '"')) {
p++;
copy = 1;
} else {
@@ -296,13 +304,13 @@ setargv(
}
while (slashes) {
- *arg = TEXT('\\');
+ *arg = '\\';
arg++;
slashes--;
}
- if ((*p == TEXT('\0')) || (!inquote &&
- ((*p == TEXT(' ')) || (*p == TEXT('\t'))))) { /* INTL: ISO space. */
+ if ((*p == '\0') || (!inquote &&
+ ((*p == ' ') || (*p == '\t')))) { /* INTL: ISO space. */
break;
}
if (copy != 0) {
diff --git a/win/tclConfig.sh.in b/win/tclConfig.sh.in
index 65bc5c5..75324b2 100644
--- a/win/tclConfig.sh.in
+++ b/win/tclConfig.sh.in
@@ -1,5 +1,5 @@
# tclConfig.sh --
-#
+#
# This shell script (for sh) is generated automatically by Tcl's
# configure script. It will create shell variables for most of
# the configuration options discovered by the configure script.
@@ -175,6 +175,6 @@ TCL_BUILD_STUB_LIB_PATH='@TCL_BUILD_STUB_LIB_PATH@'
# Path to the Tcl stub library in the install directory.
TCL_STUB_LIB_PATH='@TCL_STUB_LIB_PATH@'
-# Flag, 1: we built Tcl with threads enables, 0 we didn't
+# Flag, 1: we built Tcl with threads enabled, 0 we didn't
TCL_THREADS=@TCL_THREADS@
diff --git a/win/tclWin32Dll.c b/win/tclWin32Dll.c
index 019d76f..688fa8d 100644
--- a/win/tclWin32Dll.c
+++ b/win/tclWin32Dll.c
@@ -25,23 +25,6 @@
static HINSTANCE hInstance; /* HINSTANCE of this DLL. */
static int platformId; /* Running under NT, or 95/98? */
-#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
-
/*
* VC++ 5.x has no 'cpuid' assembler instruction, so we must emulate it
*/
@@ -86,7 +69,7 @@ TCL_DECLARE_MUTEX(mountPointMap)
* We will need this below.
*/
-#ifdef __WIN32__
+#ifdef _WIN32
#ifndef STATIC_BUILD
/*
@@ -154,7 +137,7 @@ DllMain(
return TRUE;
}
#endif /* !STATIC_BUILD */
-#endif /* __WIN32__ */
+#endif /* _WIN32 */
/*
*----------------------------------------------------------------------
@@ -198,11 +181,11 @@ void
TclWinInit(
HINSTANCE hInst) /* Library instance handle. */
{
- OSVERSIONINFO os;
+ OSVERSIONINFOW os;
hInstance = hInst;
- os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
- GetVersionEx(&os);
+ os.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
+ GetVersionExW(&os);
platformId = os.dwPlatformId;
/*
@@ -281,16 +264,9 @@ TclWinNoBackslash(
/*
*---------------------------------------------------------------------------
*
- * TclWinSetInterfaces --
+ * TclpSetInterfaces --
*
- * A helper proc that allows the test library to change the tclWinProcs
- * structure to dispatch to either the wide-character or multi-byte
- * versions of the operating system calls, depending on whether Unicode
- * is the system encoding.
- *
- * As well as this, we can also try to load in some additional procs
- * which may/may not be present depending on the current Windows version
- * (e.g. Win95 will not have the procs below).
+ * A helper proc that initializes winTCharEncoding.
*
* Results:
* None.
@@ -302,15 +278,10 @@ TclWinNoBackslash(
*/
void
-TclWinSetInterfaces(
- int wide) /* Non-zero to use wide interfaces, 0
- * otherwise. */
+TclpSetInterfaces(void)
{
- TclWinResetInterfaces();
-
- if (wide) {
- winTCharEncoding = Tcl_GetEncoding(NULL, "unicode");
- }
+ TclWinResetInterfaces();
+ winTCharEncoding = Tcl_GetEncoding(NULL, "unicode");
}
/*
@@ -318,9 +289,7 @@ TclWinSetInterfaces(
*
* TclWinEncodingsCleanup --
*
- * Called during finalization to free up any encodings we use. The
- * tclWinProcs-> look up table is still ok to use after this call,
- * provided no encoding conversion is required.
+ * Called during finalization to free up any encodings we use.
*
* We also clean up any memory allocated in our mount point map which is
* used to follow certain kinds of symlinks. That code should never be
@@ -363,8 +332,6 @@ TclWinEncodingsCleanup(void)
* TclWinResetInterfaces --
*
* Called during finalization to reset us to a safe state for reuse.
- * After this call, it is best not to use the tclWinProcs-> look up table
- * since it is likely to be different to what is expected.
*
* Results:
* None.
@@ -678,7 +645,7 @@ TclWinCPUID(
# else
- EXCEPTION_REGISTRATION registration;
+ TCLEXCEPTION_REGISTRATION registration;
/*
* Execute the CPUID instruction with the given index, and store results
@@ -687,7 +654,7 @@ TclWinCPUID(
__asm__ __volatile__(
/*
- * Construct an EXCEPTION_REGISTRATION to protect the CPUID
+ * Construct an TCLEXCEPTION_REGISTRATION to protect the CPUID
* instruction (early 486's don't have CPUID)
*/
@@ -701,7 +668,7 @@ TclWinCPUID(
"movl %[error], 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"
@@ -720,7 +687,7 @@ TclWinCPUID(
"movl %%edx, 0xc(%%edi)" "\n\t"
/*
- * Come here on a normal exit. Recover the EXCEPTION_REGISTRATION and
+ * Come here on a normal exit. Recover the TCLEXCEPTION_REGISTRATION and
* store a TCL_OK status.
*/
@@ -730,7 +697,7 @@ TclWinCPUID(
"jmp 2f" "\n"
/*
- * Come here on an exception. Get the EXCEPTION_REGISTRATION that we
+ * Come here on an exception. Get the TCLEXCEPTION_REGISTRATION that we
* previously put on the chain.
*/
@@ -740,7 +707,7 @@ TclWinCPUID(
/*
* 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"
diff --git a/win/tclWinChan.c b/win/tclWinChan.c
index 52b9e32..cca0dab 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.
*/
@@ -119,23 +119,6 @@ static const Tcl_ChannelType fileChannelType = {
FileThreadActionProc, /* Thread action 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
/*
*----------------------------------------------------------------------
@@ -679,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
@@ -856,6 +843,11 @@ TclpOpenFileChannel(
nativeName = Tcl_FSGetNativePath(pathPtr);
if (nativeName == NULL) {
+ if (interp != (Tcl_Interp *) NULL) {
+ Tcl_AppendResult(interp, "couldn't open \"",
+ TclGetString(pathPtr), "\": filename is invalid on this platform",
+ NULL);
+ }
return NULL;
}
@@ -902,6 +894,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.
*/
@@ -952,11 +971,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) {
@@ -1030,7 +1053,7 @@ Tcl_MakeFileChannel(
* TCL_WRITABLE to indicate file mode. */
{
#if defined(HAVE_NO_SEH) && !defined(_WIN64)
- EXCEPTION_REGISTRATION registration;
+ TCLEXCEPTION_REGISTRATION registration;
#endif
char channelName[16 + TCL_INTEGER_SPACE];
Tcl_Channel channel = NULL;
@@ -1111,7 +1134,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.
*/
@@ -1125,7 +1148,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"
@@ -1138,7 +1161,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.
*/
@@ -1148,7 +1171,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"
@@ -1157,7 +1180,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"
@@ -1496,6 +1519,66 @@ 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]+
+ *
+ * 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) && (_wcsnicmp(p, L"com", 3) == 0) ) {
+ /*
+ * The 4th character must be a digit 1..9
+ */
+
+ if ( (p[3] < L'1') || (p[3] > L'9') ) {
+ return 0;
+ }
+ return 1;
+ }
+
+ /*
+ * 2. Look for \\.\com[0-9]+
+ */
+
+ if ((len >= 8) && (_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 5aab255..7380003 100644
--- a/win/tclWinConsole.c
+++ b/win/tclWinConsole.c
@@ -11,7 +11,6 @@
*/
#include "tclWinInt.h"
-#include <sys/stat.h>
/*
* The following variable is used to tell whether this module has been
@@ -221,8 +220,20 @@ ReadConsoleBytes(
BOOL result;
int tcharsize = sizeof(TCHAR);
- result = ReadConsole(hConsole, lpBuffer, nbytes / tcharsize, &ntchars,
- NULL);
+ /*
+ * If user types a Ctrl-Break or Ctrl-C, ReadConsole will return
+ * success with ntchars == 0 and GetLastError() will be
+ * ERROR_OPERATION_ABORTED. We do not want to treat this case
+ * as EOF so we will loop around again. If no Ctrl signal handlers
+ * have been established, the default signal OS handler in a separate
+ * thread will terminate the program. If a Ctrl signal handler
+ * has been established (through an extension for example), it
+ * will run and take whatever action it deems appropriate.
+ */
+ do {
+ result = ReadConsole(hConsole, lpBuffer, nbytes / tcharsize, &ntchars,
+ NULL);
+ } while (result && ntchars == 0 && GetLastError() == ERROR_OPERATION_ABORTED);
if (nbytesread != NULL) {
*nbytesread = ntchars * tcharsize;
}
@@ -757,6 +768,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;
}
@@ -801,7 +819,7 @@ ConsoleOutputProc(
* the channel is in non-blocking mode.
*/
- errno = EAGAIN;
+ errno = EWOULDBLOCK;
goto error;
}
@@ -1080,7 +1098,7 @@ WaitForRead(
* is in non-blocking mode.
*/
- errno = EAGAIN;
+ errno = EWOULDBLOCK;
return -1;
}
diff --git a/win/tclWinDde.c b/win/tclWinDde.c
index 23b3a8e..ce0b413 100644
--- a/win/tclWinDde.c
+++ b/win/tclWinDde.c
@@ -11,19 +11,14 @@
*/
#undef STATIC_BUILD
-#undef USE_TCL_STUBS
-#define USE_TCL_STUBS
+#ifndef USE_TCL_STUBS
+# define USE_TCL_STUBS
+#endif
#include "tclInt.h"
#include <dde.h>
#include <ddeml.h>
-#ifdef UNICODE
-# if !defined(NDEBUG)
- /* test POKE server Implemented for UNICODE in debug mode only */
-# undef CBF_FAIL_POKES
-# define CBF_FAIL_POKES 0
-# endif
-#else
+#ifndef UNICODE
# undef CP_WINUNICODE
# define CP_WINUNICODE CP_WINANSI
# undef Tcl_WinTCharToUtf
@@ -32,6 +27,12 @@
# define Tcl_WinUtfToTChar(a,b,c) Tcl_UtfToExternalDString(NULL,a,b,c)
#endif
+#if !defined(NDEBUG)
+ /* test POKE server Implemented for debug mode only */
+# undef CBF_FAIL_POKES
+# define CBF_FAIL_POKES 0
+#endif
+
/*
* TCL_STORAGE_CLASS is set unconditionally to DLLEXPORT because the Dde_Init
* declaration is in the source file itself, which is only accessed when we
@@ -96,7 +97,7 @@ static DWORD ddeInstance; /* The application instance handle given to us
* by DdeInitialize. */
static int ddeIsServer = 0;
-#define TCL_DDE_VERSION "1.4.0b1"
+#define TCL_DDE_VERSION "1.4.0"
#define TCL_DDE_PACKAGE_NAME "dde"
#define TCL_DDE_SERVICE_NAME TEXT("TclEval")
#define TCL_DDE_EXECUTE_RESULT TEXT("$TCLEVAL$EXECUTE$RESULT")
@@ -385,7 +386,8 @@ DdeSetServerName(
Tcl_DStringSetLength(&dString, offset + sizeof(TCHAR) * TCL_INTEGER_SPACE);
actualName = (TCHAR *) Tcl_DStringValue(&dString);
}
- _stprintf((TCHAR *) (Tcl_DStringValue(&dString) + offset), TEXT("%d"), suffix);
+ _sntprintf((TCHAR *) (Tcl_DStringValue(&dString) + offset),
+ TCL_INTEGER_SPACE, TEXT("%d"), suffix);
}
/*
@@ -756,7 +758,7 @@ DdeServerProc(
} else {
returnString = (char *)
Tcl_GetUnicodeFromObj(convPtr->returnPackagePtr, &len);
- len = 2 * len + 1;
+ len = sizeof(TCHAR) * len + 1;
}
ddeReturn = DdeCreateDataHandle(ddeInstance, (BYTE *)returnString,
(DWORD) len+1, 0, ddeItem, uFmt, 0);
@@ -777,7 +779,7 @@ DdeServerProc(
} else {
returnString = (char *) Tcl_GetUnicodeFromObj(
variableObjPtr, &len);
- len = 2 * len + 1;
+ len = sizeof(TCHAR) * len + 1;
}
ddeReturn = DdeCreateDataHandle(ddeInstance,
(BYTE *)returnString, (DWORD) len+1, 0, ddeItem,
@@ -1494,15 +1496,15 @@ DdeObjCmd(
case DDE_EXECUTE: {
int dataLength;
- const char *dataString;
+ const Tcl_UniChar *dataString;
if (flags & DDE_FLAG_BINARY) {
- dataString = (const char *)
+ dataString = (const Tcl_UniChar *)
Tcl_GetByteArrayFromObj(objv[firstArg + 2], &dataLength);
} else {
dataString =
- Tcl_GetStringFromObj(objv[firstArg + 2], &dataLength);
- dataLength += 1;
+ Tcl_GetUnicodeFromObj(objv[firstArg + 2], &dataLength);
+ dataLength = (dataLength + 1) * sizeof(Tcl_UniChar);
}
if (dataLength <= 0) {
@@ -1523,15 +1525,15 @@ DdeObjCmd(
}
ddeData = DdeCreateDataHandle(ddeInstance, (BYTE *) dataString,
- (DWORD) dataLength, 0, 0, CF_TEXT, 0);
+ (DWORD) dataLength, 0, 0, (flags & DDE_FLAG_BINARY) ? CF_TEXT : CF_UNICODETEXT, 0);
if (ddeData != NULL) {
if (flags & DDE_FLAG_ASYNC) {
DdeClientTransaction((LPBYTE) ddeData, 0xFFFFFFFF, hConv, 0,
- CF_TEXT, XTYP_EXECUTE, TIMEOUT_ASYNC, &ddeResult);
+ (flags & DDE_FLAG_BINARY) ? CF_TEXT : CF_UNICODETEXT, XTYP_EXECUTE, TIMEOUT_ASYNC, &ddeResult);
DdeAbandonTransaction(ddeInstance, hConv, ddeResult);
} else {
ddeReturn = DdeClientTransaction((LPBYTE) ddeData, 0xFFFFFFFF,
- hConv, 0, CF_TEXT, XTYP_EXECUTE, 30000, NULL);
+ hConv, 0, (flags & DDE_FLAG_BINARY) ? CF_TEXT : CF_UNICODETEXT, XTYP_EXECUTE, 30000, NULL);
if (ddeReturn == 0) {
SetDdeError(interp);
result = TCL_ERROR;
@@ -1573,22 +1575,23 @@ DdeObjCmd(
CP_WINUNICODE);
if (ddeItem != NULL) {
ddeData = DdeClientTransaction(NULL, 0, hConv, ddeItem,
- CF_TEXT, XTYP_REQUEST, 5000, NULL);
+ (flags & DDE_FLAG_BINARY) ? CF_TEXT : CF_UNICODETEXT, XTYP_REQUEST, 5000, NULL);
if (ddeData == NULL) {
SetDdeError(interp);
result = TCL_ERROR;
} else {
DWORD tmp;
- const char *dataString = (const char *) DdeAccessData(ddeData, &tmp);
+ const Tcl_UniChar *dataString = (const Tcl_UniChar *) DdeAccessData(ddeData, &tmp);
if (flags & DDE_FLAG_BINARY) {
returnObjPtr =
Tcl_NewByteArrayObj((BYTE *) dataString, (int) tmp);
} else {
- if (tmp && !dataString[tmp-1]) {
+ tmp >>= 1;
+ if (tmp && !dataString[(tmp-1)]) {
--tmp;
}
- returnObjPtr = Tcl_NewStringObj(dataString,
+ returnObjPtr = Tcl_NewUnicodeObj(dataString,
(int) tmp);
}
DdeUnaccessData(ddeData);
@@ -1625,8 +1628,8 @@ DdeObjCmd(
Tcl_GetByteArrayFromObj(objv[firstArg + 3], &length);
} else {
dataString = (BYTE *)
- Tcl_GetStringFromObj(objv[firstArg + 3], &length);
- length += 1;
+ Tcl_GetUnicodeFromObj(objv[firstArg + 3], &length);
+ length = 2 * length + 1;
}
hConv = DdeConnect(ddeInstance, ddeService, ddeTopic, NULL);
@@ -1641,7 +1644,7 @@ DdeObjCmd(
CP_WINUNICODE);
if (ddeItem != NULL) {
ddeData = DdeClientTransaction(dataString, (DWORD) length,
- hConv, ddeItem, CF_TEXT, XTYP_POKE, 5000, NULL);
+ hConv, ddeItem, (flags & DDE_FLAG_BINARY) ? CF_TEXT : CF_UNICODETEXT, XTYP_POKE, 5000, NULL);
if (ddeData == NULL) {
SetDdeError(interp);
result = TCL_ERROR;
@@ -1784,24 +1787,24 @@ DdeObjCmd(
}
objPtr = Tcl_ConcatObj(objc, objv);
- string = Tcl_GetStringFromObj(objPtr, &length);
+ string = (const char *) Tcl_GetUnicodeFromObj(objPtr, &length);
ddeItemData = DdeCreateDataHandle(ddeInstance,
- (BYTE *) string, (DWORD) length+1, 0, 0, CF_TEXT, 0);
+ (BYTE *) string, (DWORD) 2*length+2, 0, 0, CF_UNICODETEXT, 0);
if (flags & DDE_FLAG_ASYNC) {
ddeData = DdeClientTransaction((LPBYTE) ddeItemData,
0xFFFFFFFF, hConv, 0,
- CF_TEXT, XTYP_EXECUTE, TIMEOUT_ASYNC, &ddeResult);
+ CF_UNICODETEXT, XTYP_EXECUTE, TIMEOUT_ASYNC, &ddeResult);
DdeAbandonTransaction(ddeInstance, hConv, ddeResult);
} else {
ddeData = DdeClientTransaction((LPBYTE) ddeItemData,
0xFFFFFFFF, hConv, 0,
- CF_TEXT, XTYP_EXECUTE, 30000, NULL);
+ CF_UNICODETEXT, XTYP_EXECUTE, 30000, NULL);
if (ddeData != 0) {
ddeCookie = DdeCreateStringHandle(ddeInstance,
TCL_DDE_EXECUTE_RESULT, CP_WINUNICODE);
ddeData = DdeClientTransaction(NULL, 0, hConv, ddeCookie,
- CF_TEXT, XTYP_REQUEST, 30000, NULL);
+ CF_UNICODETEXT, XTYP_REQUEST, 30000, NULL);
}
}
@@ -1815,6 +1818,7 @@ DdeObjCmd(
if (!(flags & DDE_FLAG_ASYNC)) {
Tcl_Obj *resultPtr;
+ Tcl_UniChar *ddeDataString;
/*
* The return handle has a two or four element list in it. The
@@ -1827,10 +1831,11 @@ DdeObjCmd(
resultPtr = Tcl_NewObj();
length = DdeGetData(ddeData, NULL, 0, 0);
- Tcl_SetObjLength(resultPtr, (length + 1) * sizeof(TCHAR) - 1);
- string = Tcl_GetString(resultPtr);
- DdeGetData(ddeData, (BYTE *) string, (DWORD) length, 0);
- Tcl_SetObjLength(resultPtr, (int) strlen(string));
+ ddeDataString = ckalloc(length);
+ DdeGetData(ddeData, (BYTE *) ddeDataString, (DWORD) length, 0);
+ length = (length >> 1) - 1;
+ resultPtr = Tcl_NewUnicodeObj(ddeDataString, length);
+ ckfree(ddeDataString);
if (Tcl_ListObjIndex(NULL, resultPtr, 0, &objPtr) != TCL_OK) {
Tcl_DecrRefCount(resultPtr);
diff --git a/win/tclWinError.c b/win/tclWinError.c
index 49eeed3..4d3250d 100644
--- a/win/tclWinError.c
+++ b/win/tclWinError.c
@@ -292,7 +292,7 @@ static const unsigned char errorTable[] = {
*/
static const unsigned char wsaErrorTable[] = {
- EAGAIN, /* WSAEWOULDBLOCK */
+ EWOULDBLOCK, /* WSAEWOULDBLOCK */
EINPROGRESS, /* WSAEINPROGRESS */
EALREADY, /* WSAEALREADY */
ENOTSOCK, /* WSAENOTSOCK */
diff --git a/win/tclWinFCmd.c b/win/tclWinFCmd.c
index 80fad3e..52ea8c6 100644
--- a/win/tclWinFCmd.c
+++ b/win/tclWinFCmd.c
@@ -67,25 +67,6 @@ const TclFileAttrProcs tclpFileAttrProcs[] = {
{GetWinFileShortName, CannotSetAttribute},
{GetWinFileAttributes, SetWinFileAttributes}};
-#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
-
/*
* Prototype for the TraverseWinTree callback function.
*/
@@ -176,7 +157,7 @@ DoRenameFile(
* (native). */
{
#if defined(HAVE_NO_SEH) && !defined(_WIN64)
- EXCEPTION_REGISTRATION registration;
+ TCLEXCEPTION_REGISTRATION registration;
#endif
DWORD srcAttr, dstAttr;
int retval = -1;
@@ -213,7 +194,7 @@ DoRenameFile(
"movl %[nativeSrc], %%ecx" "\n\t"
/*
- * Construct an EXCEPTION_REGISTRATION to protect the call to
+ * Construct an TCLEXCEPTION_REGISTRATION to protect the call to
* MoveFile.
*/
@@ -227,7 +208,7 @@ DoRenameFile(
"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"
@@ -242,7 +223,7 @@ DoRenameFile(
"call *%%eax" "\n\t"
/*
- * Come here on normal exit. Recover the EXCEPTION_REGISTRATION and
+ * Come here on normal exit. Recover the TCLEXCEPTION_REGISTRATION and
* put the status return from MoveFile into it.
*/
@@ -251,7 +232,7 @@ DoRenameFile(
"jmp 2f" "\n"
/*
- * Come here on an exception. Recover the EXCEPTION_REGISTRATION
+ * Come here on an exception. Recover the TCLEXCEPTION_REGISTRATION
*/
"1:" "\t"
@@ -260,7 +241,7 @@ DoRenameFile(
/*
* 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"
@@ -563,7 +544,7 @@ DoCopyFile(
const TCHAR *nativeDst) /* Pathname of file to copy to (native). */
{
#if defined(HAVE_NO_SEH) && !defined(_WIN64)
- EXCEPTION_REGISTRATION registration;
+ TCLEXCEPTION_REGISTRATION registration;
#endif
int retval = -1;
@@ -600,7 +581,7 @@ DoCopyFile(
"movl %[nativeSrc], %%ecx" "\n\t"
/*
- * Construct an EXCEPTION_REGISTRATION to protect the call to
+ * Construct an TCLEXCEPTION_REGISTRATION to protect the call to
* CopyFile.
*/
@@ -614,7 +595,7 @@ DoCopyFile(
"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"
@@ -630,7 +611,7 @@ DoCopyFile(
"call *%%eax" "\n\t"
/*
- * Come here on normal exit. Recover the EXCEPTION_REGISTRATION and
+ * Come here on normal exit. Recover the TCLEXCEPTION_REGISTRATION and
* put the status return from CopyFile into it.
*/
@@ -639,7 +620,7 @@ DoCopyFile(
"jmp 2f" "\n"
/*
- * Come here on an exception. Recover the EXCEPTION_REGISTRATION
+ * Come here on an exception. Recover the TCLEXCEPTION_REGISTRATION
*/
"1:" "\t"
@@ -648,7 +629,7 @@ DoCopyFile(
/*
* 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"
@@ -1105,49 +1086,6 @@ DoRemoveJustDirectory(
SetFileAttributes(nativePath,
attr | FILE_ATTRIBUTE_READONLY);
}
-
- /*
- * Windows 95 reports removing a non-empty directory as
- * EACCES, not EEXIST. If the directory is not empty, change errno
- * so caller knows what's going on.
- */
-
- if (TclWinGetPlatformId() == VER_PLATFORM_WIN32_WINDOWS) {
- const char *path, *find;
- HANDLE handle;
- WIN32_FIND_DATAA data;
- Tcl_DString buffer;
- int len;
-
- path = (const char *) nativePath;
-
- Tcl_DStringInit(&buffer);
- len = strlen(path);
- find = Tcl_DStringAppend(&buffer, path, len);
- if ((len > 0) && (find[len - 1] != '\\')) {
- TclDStringAppendLiteral(&buffer, "\\");
- }
- find = TclDStringAppendLiteral(&buffer, "*.*");
- handle = FindFirstFileA(find, &data);
- if (handle != INVALID_HANDLE_VALUE) {
- while (1) {
- if ((strcmp(data.cFileName, ".") != 0)
- && (strcmp(data.cFileName, "..") != 0)) {
- /*
- * Found something in this directory.
- */
-
- Tcl_SetErrno(EEXIST);
- break;
- }
- if (FindNextFileA(handle, &data) == FALSE) {
- break;
- }
- }
- FindClose(handle);
- }
- Tcl_DStringFree(&buffer);
- }
}
}
@@ -1170,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;
@@ -1738,11 +1681,11 @@ ConvertFileNameFormat(
}
nativeName = data.cAlternateFileName;
if (longShort) {
- if (data.cFileName[0] != TEXT('\0')) {
+ if (data.cFileName[0] != '\0') {
nativeName = data.cFileName;
}
} else {
- if (data.cAlternateFileName[0] == TEXT('\0')) {
+ if (data.cAlternateFileName[0] == '\0') {
nativeName = (TCHAR *) data.cFileName;
}
}
@@ -1887,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);
@@ -1910,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 a44a257..a5b14b4 100644
--- a/win/tclWinFile.c
+++ b/win/tclWinFile.c
@@ -15,7 +15,6 @@
#include "tclWinInt.h"
#include "tclFileSystem.h"
#include <winioctl.h>
-#include <sys/stat.h>
#include <shlobj.h>
#include <lm.h> /* For TclpGetUserHome(). */
@@ -160,7 +159,7 @@ static unsigned short NativeStatMode(DWORD attr, int checkLinks,
int isExec);
static int NativeIsExec(const TCHAR *path);
static int NativeReadReparse(const TCHAR *LinkDirectory,
- REPARSE_DATA_BUFFER *buffer);
+ REPARSE_DATA_BUFFER *buffer, DWORD desiredAccess);
static int NativeWriteReparse(const TCHAR *LinkDirectory,
REPARSE_DATA_BUFFER *buffer);
static int NativeMatchType(int isDrive, DWORD attr,
@@ -444,7 +443,7 @@ TclWinSymLinkCopyDirectory(
DUMMY_REPARSE_BUFFER dummy;
REPARSE_DATA_BUFFER *reparseBuffer = (REPARSE_DATA_BUFFER *) &dummy;
- if (NativeReadReparse(linkOrigPath, reparseBuffer)) {
+ if (NativeReadReparse(linkOrigPath, reparseBuffer, GENERIC_READ)) {
return -1;
}
return NativeWriteReparse(linkCopyPath, reparseBuffer);
@@ -542,7 +541,7 @@ WinReadLinkDirectory(
if (!(attr & FILE_ATTRIBUTE_REPARSE_POINT)) {
goto invalidError;
}
- if (NativeReadReparse(linkDirPath, reparseBuffer)) {
+ if (NativeReadReparse(linkDirPath, reparseBuffer, 0)) {
return NULL;
}
@@ -663,12 +662,13 @@ WinReadLinkDirectory(
static int
NativeReadReparse(
const TCHAR *linkDirPath, /* The junction to read */
- REPARSE_DATA_BUFFER *buffer)/* Pointer to buffer. Cannot be NULL */
+ REPARSE_DATA_BUFFER *buffer,/* Pointer to buffer. Cannot be NULL */
+ DWORD desiredAccess)
{
HANDLE hFile;
DWORD returnedLength;
- hFile = CreateFile(linkDirPath, GENERIC_READ, 0, NULL, OPEN_EXISTING,
+ hFile = CreateFile(linkDirPath, desiredAccess, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
@@ -799,7 +799,7 @@ tclWinDebugPanic(
WCHAR msgString[TCL_MAX_WARN_LEN];
va_start(argList, format);
- _vsnprintf(buf, sizeof(buf), format, argList);
+ vsnprintf(buf, sizeof(buf), format, argList);
msgString[TCL_MAX_WARN_LEN-1] = L'\0';
MultiByteToWideChar(CP_UTF8, 0, buf, -1, msgString, TCL_MAX_WARN_LEN);
@@ -1241,9 +1241,9 @@ WinIsReserved(
if ((path[0] == 'c' || path[0] == 'C')
&& (path[1] == 'o' || path[1] == 'O')) {
if ((path[2] == 'm' || path[2] == 'M')
- && path[3] >= '1' && path[3] <= '4') {
+ && path[3] >= '1' && path[3] <= '9') {
/*
- * May have match for 'com[1-4]:?', which is a serial port.
+ * May have match for 'com[1-9]:?', which is a serial port.
*/
if (path[4] == '\0') {
@@ -1262,9 +1262,9 @@ WinIsReserved(
} else if ((path[0] == 'l' || path[0] == 'L')
&& (path[1] == 'p' || path[1] == 'P')
&& (path[2] == 't' || path[2] == 'T')) {
- if (path[3] >= '1' && path[3] <= '3') {
+ if (path[3] >= '1' && path[3] <= '9') {
/*
- * May have match for 'lpt[1-3]:?'
+ * May have match for 'lpt[1-9]:?'
*/
if (path[4] == '\0') {
@@ -1537,14 +1537,9 @@ NativeAccess(
* File might not exist.
*/
- WIN32_FIND_DATA ffd;
- HANDLE hFind;
- hFind = FindFirstFile(nativePath, &ffd);
- if (hFind != INVALID_HANDLE_VALUE) {
- attr = ffd.dwFileAttributes;
- FindClose(hFind);
- } else {
- TclWinConvertError(GetLastError());
+ DWORD lasterror = GetLastError();
+ if (lasterror != ERROR_SHARING_VIOLATION) {
+ TclWinConvertError(lasterror);
return -1;
}
}
@@ -1784,7 +1779,7 @@ NativeIsExec(
return 0;
}
- if (path[len-4] != TEXT('.')) {
+ if (path[len-4] != '.') {
return 0;
}
@@ -1821,6 +1816,9 @@ TclpObjChdir(
nativePath = Tcl_FSGetNativePath(pathPtr);
+ if (!nativePath) {
+ return -1;
+ }
result = SetCurrentDirectory(nativePath);
if (result == 0) {
@@ -2002,15 +2000,17 @@ NativeStat(
if (GetFileAttributesEx(nativePath,
GetFileExInfoStandard, &data) != TRUE) {
- /*
- * We might have just been denied access
- */
-
+ HANDLE hFind;
WIN32_FIND_DATA ffd;
- HANDLE hFind = FindFirstFile(nativePath, &ffd);
+ DWORD lasterror = GetLastError();
+ if (lasterror != ERROR_SHARING_VIOLATION) {
+ TclWinConvertError(lasterror);
+ return -1;
+ }
+ hFind = FindFirstFile(nativePath, &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
- Tcl_SetErrno(ENOENT);
+ TclWinConvertError(GetLastError());
return -1;
}
memcpy(&data, &ffd, sizeof(data));
@@ -2418,361 +2418,213 @@ TclpObjNormalizePath(
Tcl_DStringInit(&dsNorm);
path = Tcl_GetString(pathPtr);
- if (TclWinGetPlatformId() == VER_PLATFORM_WIN32_WINDOWS) {
- /*
- * We're on Win95, 98 or ME. There are two assumptions in this block
- * of code. First that the native (NULL) encoding is basically ascii,
- * and second that symbolic links are not possible. Both of these
- * assumptions appear to be true of these operating systems.
- *
- * FIXME: This code branch may be derelict as those are not supported
- * platforms any more.
- */
-
- currentPathEndPosition = path + nextCheckpoint;
- if (*currentPathEndPosition == '/') {
- currentPathEndPosition++;
- }
-
- while (1) {
- char cur = *currentPathEndPosition;
+ currentPathEndPosition = path + nextCheckpoint;
+ if (*currentPathEndPosition == '/') {
+ currentPathEndPosition++;
+ }
+ while (1) {
+ char cur = *currentPathEndPosition;
- if ((cur=='/' || cur==0) && (path != currentPathEndPosition)) {
- /*
- * Reached directory separator, or end of string.
- */
+ if ((cur=='/' || cur==0) && (path != currentPathEndPosition)) {
+ /*
+ * Reached directory separator, or end of string.
+ */
- const char *nativePath = Tcl_UtfToExternalDString(NULL, path,
- currentPathEndPosition - path, &ds);
+ WIN32_FILE_ATTRIBUTE_DATA data;
+ const TCHAR *nativePath = Tcl_WinUtfToTChar(path,
+ currentPathEndPosition - path, &ds);
+ if (GetFileAttributesEx(nativePath,
+ GetFileExInfoStandard, &data) != TRUE) {
/*
- * Now we convert the tail of the current path to its 'long
- * form', and append it to 'dsNorm' which holds the current
- * normalized path, if the file exists.
+ * File doesn't exist.
*/
if (isDrive) {
- if (GetFileAttributesA(nativePath)
- == INVALID_FILE_ATTRIBUTES) {
- /*
- * File doesn't exist.
- */
-
- if (isDrive) {
- int len = WinIsReserved(path);
-
- if (len > 0) {
- /*
- * Actually it does exist - COM1, etc.
- */
-
- int i;
-
- for (i=0 ; i<len ; i++) {
- if (nativePath[i] >= 'a') {
- ((char *) nativePath)[i] -= ('a'-'A');
- }
- }
- Tcl_DStringAppend(&dsNorm, nativePath, len);
- lastValidPathEnd = currentPathEndPosition;
- }
- }
- Tcl_DStringFree(&ds);
- break;
- }
- if (nativePath[0] >= 'a') {
- ((char *) nativePath)[0] -= ('a' - 'A');
- }
- Tcl_DStringAppend(&dsNorm, nativePath,
- Tcl_DStringLength(&ds));
- } else {
- char *checkDots = NULL;
-
- if (lastValidPathEnd[1] == '.') {
- checkDots = lastValidPathEnd + 1;
- while (checkDots < currentPathEndPosition) {
- if (*checkDots != '.') {
- checkDots = NULL;
- break;
- }
- checkDots++;
- }
- }
- if (checkDots != NULL) {
- int dotLen = currentPathEndPosition-lastValidPathEnd;
+ int len = WinIsReserved(path);
+ if (len > 0) {
/*
- * Path is just dots. We shouldn't really ever see a
- * path like that. However, to be nice we at least
- * don't mangle the path - we just add the dots as a
- * path segment and continue
+ * Actually it does exist - COM1, etc.
*/
- Tcl_DStringAppend(&dsNorm, (const char *)
- (nativePath + Tcl_DStringLength(&ds)-dotLen),
- dotLen);
- } else {
- /*
- * Normal path.
- */
-
- WIN32_FIND_DATAA fData;
- HANDLE handle;
-
- handle = FindFirstFileA(nativePath, &fData);
- if (handle == INVALID_HANDLE_VALUE) {
- if (GetFileAttributesA(nativePath)
- == INVALID_FILE_ATTRIBUTES) {
- /*
- * File doesn't exist.
- */
+ int i;
- Tcl_DStringFree(&ds);
- break;
- }
-
- /*
- * This is usually the '/' in 'c:/' at end of
- * string.
- */
+ for (i=0 ; i<len ; i++) {
+ WCHAR wc = ((WCHAR *) nativePath)[i];
- TclDStringAppendLiteral(&dsNorm, "/");
- } else {
- char *nativeName;
-
- if (fData.cFileName[0] != '\0') {
- nativeName = fData.cFileName;
- } else {
- nativeName = fData.cAlternateFileName;
+ if (wc >= L'a') {
+ wc -= (L'a' - L'A');
+ ((WCHAR *) nativePath)[i] = wc;
}
- FindClose(handle);
- TclDStringAppendLiteral(&dsNorm, "/");
- Tcl_DStringAppend(&dsNorm, nativeName, -1);
}
+ Tcl_DStringAppend(&dsNorm,
+ (const char *)nativePath,
+ (int)(sizeof(WCHAR) * len));
+ lastValidPathEnd = currentPathEndPosition;
+ } else if (nextCheckpoint == 0) {
+ /* Path starts with a drive designation
+ * that's not actually on the system.
+ * We still must normalize up past the
+ * first separator. [Bug 3603434] */
+ currentPathEndPosition++;
}
}
Tcl_DStringFree(&ds);
- lastValidPathEnd = currentPathEndPosition;
- if (cur == 0) {
- break;
- }
-
- /*
- * If we get here, we've got past one directory delimiter, so
- * we know it is no longer a drive.
- */
-
- isDrive = 0;
+ break;
}
- currentPathEndPosition++;
- }
- } else {
- /*
- * We're on WinNT (or 2000 or XP; something with an NT core).
- */
- currentPathEndPosition = path + nextCheckpoint;
- if (*currentPathEndPosition == '/') {
- currentPathEndPosition++;
- }
- while (1) {
- char cur = *currentPathEndPosition;
+ /*
+ * File 'nativePath' does exist if we get here. We now want to
+ * check if it is a symlink and otherwise continue with the
+ * rest of the path.
+ */
- if ((cur=='/' || cur==0) && (path != currentPathEndPosition)) {
- /*
- * Reached directory separator, or end of string.
- */
+ /*
+ * Check for symlinks, except at last component of path (we
+ * don't follow final symlinks). Also a drive (C:/) for
+ * example, may sometimes have the reparse flag set for some
+ * reason I don't understand. We therefore don't perform this
+ * check for drives.
+ */
- WIN32_FILE_ATTRIBUTE_DATA data;
- const TCHAR *nativePath = Tcl_WinUtfToTChar(path,
- currentPathEndPosition - path, &ds);
+ if (cur != 0 && !isDrive &&
+ data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT){
+ Tcl_Obj *to = WinReadLinkDirectory(nativePath);
- if (GetFileAttributesEx(nativePath,
- GetFileExInfoStandard, &data) != TRUE) {
+ if (to != NULL) {
/*
- * File doesn't exist.
+ * Read the reparse point ok. Now, reparse points need
+ * not be normalized, otherwise we could use:
+ *
+ * Tcl_GetStringFromObj(to, &pathLen);
+ * nextCheckpoint = pathLen;
+ *
+ * So, instead we have to start from the beginning.
*/
- if (isDrive) {
- int len = WinIsReserved(path);
-
- if (len > 0) {
- /*
- * Actually it does exist - COM1, etc.
- */
+ nextCheckpoint = 0;
+ Tcl_AppendToObj(to, currentPathEndPosition, -1);
- int i;
-
- for (i=0 ; i<len ; i++) {
- WCHAR wc = ((WCHAR *) nativePath)[i];
+ /*
+ * Convert link to forward slashes.
+ */
- if (wc >= L'a') {
- wc -= (L'a' - L'A');
- ((WCHAR *) nativePath)[i] = wc;
- }
- }
- Tcl_DStringAppend(&dsNorm,
- (const char *)nativePath,
- (int)(sizeof(WCHAR) * len));
- lastValidPathEnd = currentPathEndPosition;
+ for (path = Tcl_GetString(to); *path != 0; path++) {
+ if (*path == '\\') {
+ *path = '/';
}
}
- Tcl_DStringFree(&ds);
- break;
- }
-
- /*
- * File 'nativePath' does exist if we get here. We now want to
- * check if it is a symlink and otherwise continue with the
- * rest of the path.
- */
-
- /*
- * Check for symlinks, except at last component of path (we
- * don't follow final symlinks). Also a drive (C:/) for
- * example, may sometimes have the reparse flag set for some
- * reason I don't understand. We therefore don't perform this
- * check for drives.
- */
+ path = Tcl_GetString(to);
+ currentPathEndPosition = path + nextCheckpoint;
+ if (temp != NULL) {
+ Tcl_DecrRefCount(temp);
+ }
+ temp = to;
- if (cur != 0 && !isDrive &&
- data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT){
- Tcl_Obj *to = WinReadLinkDirectory(nativePath);
+ /*
+ * Reset variables so we can restart normalization.
+ */
- if (to != NULL) {
- /*
- * Read the reparse point ok. Now, reparse points need
- * not be normalized, otherwise we could use:
- *
- * Tcl_GetStringFromObj(to, &pathLen);
- * nextCheckpoint = pathLen;
- *
- * So, instead we have to start from the beginning.
- */
+ isDrive = 1;
+ Tcl_DStringFree(&dsNorm);
+ Tcl_DStringFree(&ds);
+ continue;
+ }
+ }
- nextCheckpoint = 0;
- Tcl_AppendToObj(to, currentPathEndPosition, -1);
+#ifndef TclNORM_LONG_PATH
+ /*
+ * Now we convert the tail of the current path to its 'long
+ * form', and append it to 'dsNorm' which holds the current
+ * normalized path
+ */
- /*
- * Convert link to forward slashes.
- */
+ if (isDrive) {
+ WCHAR drive = ((WCHAR *) nativePath)[0];
- for (path = Tcl_GetString(to); *path != 0; path++) {
- if (*path == '\\') {
- *path = '/';
- }
- }
- path = Tcl_GetString(to);
- currentPathEndPosition = path + nextCheckpoint;
- if (temp != NULL) {
- Tcl_DecrRefCount(temp);
+ if (drive >= L'a') {
+ drive -= (L'a' - L'A');
+ ((WCHAR *) nativePath)[0] = drive;
+ }
+ Tcl_DStringAppend(&dsNorm, (const char *)nativePath,
+ Tcl_DStringLength(&ds));
+ } else {
+ char *checkDots = NULL;
+
+ if (lastValidPathEnd[1] == '.') {
+ checkDots = lastValidPathEnd + 1;
+ while (checkDots < currentPathEndPosition) {
+ if (*checkDots != '.') {
+ checkDots = NULL;
+ break;
}
- temp = to;
-
- /*
- * Reset variables so we can restart normalization.
- */
-
- isDrive = 1;
- Tcl_DStringFree(&dsNorm);
- Tcl_DStringFree(&ds);
- continue;
+ checkDots++;
}
}
+ if (checkDots != NULL) {
+ int dotLen = currentPathEndPosition-lastValidPathEnd;
-#ifndef TclNORM_LONG_PATH
- /*
- * Now we convert the tail of the current path to its 'long
- * form', and append it to 'dsNorm' which holds the current
- * normalized path
- */
-
- if (isDrive) {
- WCHAR drive = ((WCHAR *) nativePath)[0];
+ /*
+ * Path is just dots. We shouldn't really ever see a
+ * path like that. However, to be nice we at least
+ * don't mangle the path - we just add the dots as a
+ * path segment and continue.
+ */
- if (drive >= L'a') {
- drive -= (L'a' - L'A');
- ((WCHAR *) nativePath)[0] = drive;
- }
- Tcl_DStringAppend(&dsNorm, (const char *)nativePath,
- Tcl_DStringLength(&ds));
+ Tcl_DStringAppend(&dsNorm, ((const char *)nativePath)
+ + Tcl_DStringLength(&ds)
+ - (dotLen * sizeof(TCHAR)),
+ (int)(dotLen * sizeof(TCHAR)));
} else {
- char *checkDots = NULL;
-
- if (lastValidPathEnd[1] == '.') {
- checkDots = lastValidPathEnd + 1;
- while (checkDots < currentPathEndPosition) {
- if (*checkDots != '.') {
- checkDots = NULL;
- break;
- }
- checkDots++;
- }
- }
- if (checkDots != NULL) {
- int dotLen = currentPathEndPosition-lastValidPathEnd;
+ /*
+ * Normal path.
+ */
- /*
- * Path is just dots. We shouldn't really ever see a
- * path like that. However, to be nice we at least
- * don't mangle the path - we just add the dots as a
- * path segment and continue.
- */
+ WIN32_FIND_DATAW fData;
+ HANDLE handle;
- Tcl_DStringAppend(&dsNorm, ((const char *)nativePath)
- + Tcl_DStringLength(&ds)
- - (dotLen * sizeof(TCHAR)),
- (int)(dotLen * sizeof(TCHAR)));
- } else {
+ handle = FindFirstFileW((WCHAR *) nativePath, &fData);
+ if (handle == INVALID_HANDLE_VALUE) {
/*
- * Normal path.
+ * This is usually the '/' in 'c:/' at end of
+ * string.
*/
- WIN32_FIND_DATAW fData;
- HANDLE handle;
-
- handle = FindFirstFileW((WCHAR *) nativePath, &fData);
- if (handle == INVALID_HANDLE_VALUE) {
- /*
- * This is usually the '/' in 'c:/' at end of
- * string.
- */
+ Tcl_DStringAppend(&dsNorm, (const char *) L"/",
+ sizeof(WCHAR));
+ } else {
+ WCHAR *nativeName;
- Tcl_DStringAppend(&dsNorm, (const char *) L"/",
- sizeof(WCHAR));
+ if (fData.cFileName[0] != '\0') {
+ nativeName = fData.cFileName;
} else {
- WCHAR *nativeName;
-
- if (fData.cFileName[0] != '\0') {
- nativeName = fData.cFileName;
- } else {
- nativeName = fData.cAlternateFileName;
- }
- FindClose(handle);
- Tcl_DStringAppend(&dsNorm, (const char *) L"/",
- sizeof(WCHAR));
- Tcl_DStringAppend(&dsNorm,
- (const char *) nativeName,
- (int) (wcslen(nativeName)*sizeof(WCHAR)));
+ nativeName = fData.cAlternateFileName;
}
+ FindClose(handle);
+ Tcl_DStringAppend(&dsNorm, (const char *) L"/",
+ sizeof(WCHAR));
+ Tcl_DStringAppend(&dsNorm,
+ (const char *) nativeName,
+ (int) (wcslen(nativeName)*sizeof(WCHAR)));
}
}
+ }
#endif /* !TclNORM_LONG_PATH */
- Tcl_DStringFree(&ds);
- lastValidPathEnd = currentPathEndPosition;
- if (cur == 0) {
- break;
- }
+ Tcl_DStringFree(&ds);
+ lastValidPathEnd = currentPathEndPosition;
+ if (cur == 0) {
+ break;
+ }
- /*
- * If we get here, we've got past one directory delimiter, so
- * we know it is no longer a drive.
- */
+ /*
+ * If we get here, we've got past one directory delimiter, so
+ * we know it is no longer a drive.
+ */
- isDrive = 0;
- }
- currentPathEndPosition++;
+ isDrive = 0;
}
+ currentPathEndPosition++;
#ifdef TclNORM_LONG_PATH
/*
@@ -3045,10 +2897,11 @@ ClientData
TclNativeCreateNativeRep(
Tcl_Obj *pathPtr)
{
- char *nativePathPtr, *str;
- Tcl_DString ds;
+ WCHAR *nativePathPtr;
+ const char *str;
Tcl_Obj *validPathPtr;
- int len;
+ size_t len;
+ WCHAR *wp;
if (TclFSCwdIsNative()) {
/*
@@ -3073,23 +2926,77 @@ TclNativeCreateNativeRep(
Tcl_IncrRefCount(validPathPtr);
}
- str = Tcl_GetStringFromObj(validPathPtr, &len);
- if (str[0] == '/' && str[1] == '/' && str[2] == '?' && str[3] == '/') {
- char *p;
+ str = Tcl_GetString(validPathPtr);
+ len = validPathPtr->length;
- for (p = str; p && *p; ++p) {
- if (*p == '/') {
- *p = '\\';
- }
+ if (strlen(str)!=(unsigned int)len) {
+ /* String contains NUL-bytes. This is invalid. */
+ return 0;
+ }
+ /* For a reserved device, strip a possible postfix ':' */
+ len = WinIsReserved(str);
+ if (len == 0) {
+ /* Let MultiByteToWideChar check for other invalid sequences, like
+ * 0xC0 0x80 (== overlong NUL). See bug [3118489]: NUL in filenames */
+ len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str, -1, 0, 0);
+ if (len==0) {
+ return 0;
}
}
- 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);
-
- Tcl_DStringFree(&ds);
+ /* Overallocate 6 chars, making some room for extended paths */
+ wp = nativePathPtr = ckalloc( (len+6) * sizeof(WCHAR) );
+ if (nativePathPtr==0) {
+ return 0;
+ }
+ MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str, -1, nativePathPtr, len+1);
+ /*
+ ** If path starts with "//?/" or "\\?\" (extended path), translate
+ ** any slashes to backslashes but leave the '?' intact
+ */
+ if ((str[0]=='\\' || str[0]=='/') && (str[1]=='\\' || str[1]=='/')
+ && str[2]=='?' && (str[3]=='\\' || str[3]=='/')) {
+ wp[0] = wp[1] = wp[3] = '\\';
+ str += 4;
+ wp += 4;
+ }
+ /*
+ ** If there is no "\\?\" prefix but there is a drive or UNC
+ ** path prefix and the path is larger than MAX_PATH chars,
+ ** no Win32 API function can handle that unless it is
+ ** prefixed with the extended path prefix. See:
+ ** <http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath>
+ **/
+ if (((str[0]>='A'&&str[0]<='Z') || (str[0]>='a'&&str[0]<='z'))
+ && str[1]==':') {
+ if (wp==nativePathPtr && len>MAX_PATH && (str[2]=='\\' || str[2]=='/')) {
+ memmove(wp+4, wp, len*sizeof(WCHAR));
+ memcpy(wp, L"\\\\?\\", 4*sizeof(WCHAR));
+ wp += 4;
+ }
+ /*
+ ** If (remainder of) path starts with "<drive>:",
+ ** leave the ':' intact.
+ */
+ wp += 2;
+ } else if (wp==nativePathPtr && len>MAX_PATH
+ && (str[0]=='\\' || str[0]=='/')
+ && (str[1]=='\\' || str[1]=='/') && str[2]!='?') {
+ memmove(wp+6, wp, len*sizeof(WCHAR));
+ memcpy(wp, L"\\\\?\\UNC", 7*sizeof(WCHAR));
+ wp += 7;
+ }
+ /*
+ ** In the remainder of the path, translate invalid characters to
+ ** characters in the Unicode private use area.
+ */
+ while (*wp != '\0') {
+ if ((*wp < ' ') || wcschr(L"\"*:<>?|", *wp)) {
+ *wp |= 0xF000;
+ } else if (*wp == '/') {
+ *wp = '\\';
+ }
+ ++wp;
+ }
return nativePathPtr;
}
diff --git a/win/tclWinInit.c b/win/tclWinInit.c
index d89c98e..8b600f6 100644
--- a/win/tclWinInit.c
+++ b/win/tclWinInit.c
@@ -101,6 +101,10 @@ static TclInitProcessGlobalValueProc InitializeDefaultLibraryDir;
static ProcessGlobalValue defaultLibraryDir =
{0, 0, NULL, NULL, InitializeDefaultLibraryDir, NULL, NULL};
+static TclInitProcessGlobalValueProc InitializeSourceLibraryDir;
+static ProcessGlobalValue sourceLibraryDir =
+ {0, 0, NULL, NULL, InitializeSourceLibraryDir, NULL, NULL};
+
static void AppendEnvironment(Tcl_Obj *listPtr, const char *lib);
static int ToUtf(const WCHAR *wSrc, char *dst);
@@ -109,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.
*
@@ -126,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
/*
@@ -175,7 +175,7 @@ TclpInitLibraryPath(
int *lengthPtr,
Tcl_Encoding *encodingPtr)
{
-#define LIBRARY_SIZE 32
+#define LIBRARY_SIZE 64
Tcl_Obj *pathPtr;
char installLib[LIBRARY_SIZE];
const char *bytes;
@@ -206,6 +206,13 @@ TclpInitLibraryPath(
Tcl_ListObjAppendElement(NULL, pathPtr,
TclGetProcessGlobalValue(&defaultLibraryDir));
+ /*
+ * Look for the library in its source checkout location.
+ */
+
+ Tcl_ListObjAppendElement(NULL, pathPtr,
+ TclGetProcessGlobalValue(&sourceLibraryDir));
+
*encodingPtr = NULL;
bytes = Tcl_GetStringFromObj(pathPtr, lengthPtr);
*valuePtr = ckalloc((*lengthPtr) + 1);
@@ -360,6 +367,57 @@ InitializeDefaultLibraryDir(
/*
*---------------------------------------------------------------------------
*
+ * InitializeSourceLibraryDir --
+ *
+ * Locate the Tcl script library default location relative to the
+ * location of the Tcl DLL as it exists in the build output directory
+ * associated with the source checkout.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static void
+InitializeSourceLibraryDir(
+ char **valuePtr,
+ int *lengthPtr,
+ Tcl_Encoding *encodingPtr)
+{
+ HMODULE hModule = TclWinGetTclInstance();
+ WCHAR wName[MAX_PATH + LIBRARY_SIZE];
+ char name[(MAX_PATH + LIBRARY_SIZE) * TCL_UTF_MAX];
+ char *end, *p;
+
+ if (GetModuleFileNameW(hModule, wName, MAX_PATH) == 0) {
+ GetModuleFileNameA(hModule, name, MAX_PATH);
+ } else {
+ ToUtf(wName, name);
+ }
+
+ end = strrchr(name, '\\');
+ *end = '\0';
+ p = strrchr(name, '\\');
+ if (p != NULL) {
+ end = p;
+ }
+ *end = '\\';
+
+ TclWinNoBackslash(name);
+ sprintf(end + 1, "../library");
+ *lengthPtr = strlen(name);
+ *valuePtr = ckalloc(*lengthPtr + 1);
+ *encodingPtr = NULL;
+ memcpy(*valuePtr, name, (size_t) *lengthPtr + 1);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
* ToUtf --
*
* Convert a char string to a UTF string.
@@ -424,13 +482,10 @@ TclpSetInitialEncodings(void)
Tcl_DStringFree(&encodingName);
}
-void
-TclpSetInterfaces(void)
+void TclWinSetInterfaces(
+ int dummy) /* Not used. */
{
- int useWide;
-
- useWide = (TclWinGetPlatformId() != VER_PLATFORM_WIN32_WINDOWS);
- TclWinSetInterfaces(useWide);
+ TclpSetInterfaces();
}
const char *
@@ -471,7 +526,8 @@ TclpSetVariables(
SYSTEM_INFO info;
OemId oemId;
} sys;
- OSVERSIONINFOA osInfo;
+ static OSVERSIONINFOW osInfo;
+ static int osInfoInitialized = 0;
Tcl_DString ds;
TCHAR szUserName[UNLEN+1];
DWORD cchUserNameLen = UNLEN;
@@ -479,9 +535,19 @@ TclpSetVariables(
Tcl_SetVar2Ex(interp, "tclDefaultLibrary", NULL,
TclGetProcessGlobalValue(&defaultLibraryDir), TCL_GLOBAL_ONLY);
- osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
- GetVersionExA(&osInfo);
-
+ if (!osInfoInitialized) {
+ HANDLE handle = LoadLibraryW(L"NTDLL");
+ int(__stdcall *getversion)(void *) =
+ (int(__stdcall *)(void *)) GetProcAddress(handle, "RtlGetVersion");
+ osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
+ if (!getversion || getversion(&osInfo)) {
+ GetVersionExW(&osInfo);
+ }
+ if (handle) {
+ FreeLibrary(handle);
+ }
+ osInfoInitialized = 1;
+ }
GetSystemInfo(&sys.info);
/*
diff --git a/win/tclWinInt.h b/win/tclWinInt.h
index 22ad8e9..9df424f 100644
--- a/win/tclWinInt.h
+++ b/win/tclWinInt.h
@@ -14,6 +14,23 @@
#include "tclInt.h"
+#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 TCLEXCEPTION_REGISTRATION within the activation record.
+ */
+
+typedef struct TCLEXCEPTION_REGISTRATION {
+ struct TCLEXCEPTION_REGISTRATION *link;
+ EXCEPTION_DISPOSITION (*handler)(
+ struct _EXCEPTION_RECORD*, void*, struct _CONTEXT*, void*);
+ void *ebp;
+ void *esp;
+ int status;
+} TCLEXCEPTION_REGISTRATION;
+#endif
+
/*
* Some versions of Borland C have a define for the OSVERSIONINFO for
* Win32s and for NT, but not for Windows 95.
@@ -33,12 +50,6 @@
# define TCL_I_MODIFIER ""
#endif
-#ifdef _WIN64
-# define TCL_I_MODIFIER "I"
-#else
-# define TCL_I_MODIFIER ""
-#endif
-
/*
* Declarations of functions that are not accessible by way of the
* stubs table.
@@ -55,7 +66,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/tclWinLoad.c b/win/tclWinLoad.c
index 6294086..3e11224 100644
--- a/win/tclWinLoad.c
+++ b/win/tclWinLoad.c
@@ -57,10 +57,11 @@ TclpDlopen(
Tcl_LoadHandle *loadHandle, /* Filled with token for dynamically loaded
* file which will be passed back to
* (*unloadProcPtr)() to unload the file. */
- Tcl_FSUnloadFileProc **unloadProcPtr)
+ Tcl_FSUnloadFileProc **unloadProcPtr,
/* Filled with address of Tcl_FSUnloadFileProc
* function which should be used for this
* file. */
+ int flags)
{
HINSTANCE hInstance;
const TCHAR *nativeName;
diff --git a/win/tclWinPipe.c b/win/tclWinPipe.c
index 36ae58a..a9eec6d 100644
--- a/win/tclWinPipe.c
+++ b/win/tclWinPipe.c
@@ -12,8 +12,6 @@
#include "tclWinInt.h"
-#include <sys/stat.h>
-
/*
* The following variable is used to tell whether this module has been
* initialized.
@@ -84,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.
*/
@@ -1051,15 +1055,8 @@ TclpCreateProcess(
* sink.
*/
- if ((TclWinGetPlatformId() == VER_PLATFORM_WIN32_WINDOWS)
- && (applType == APPL_DOS)) {
- if (CreatePipe(&h, &startInfo.hStdOutput, &secAtts, 0) != FALSE) {
- CloseHandle(h);
- }
- } else {
- startInfo.hStdOutput = CreateFileA("NUL:", GENERIC_WRITE, 0,
- &secAtts, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
- }
+ startInfo.hStdOutput = CreateFile(TEXT("NUL:"), GENERIC_WRITE, 0,
+ &secAtts, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
} else {
DuplicateHandle(hProcess, outputHandle, hProcess,
&startInfo.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS);
@@ -1078,7 +1075,7 @@ TclpCreateProcess(
* sink.
*/
- startInfo.hStdError = CreateFileA("NUL:", GENERIC_WRITE, 0,
+ startInfo.hStdError = CreateFile(TEXT("NUL:"), GENERIC_WRITE, 0,
&secAtts, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
} else {
DuplicateHandle(hProcess, errorHandle, hProcess, &startInfo.hStdError,
@@ -1893,7 +1890,7 @@ PipeClose2Proc(
SetEvent(pipePtr->stopWriter);
if (WaitForSingleObject(pipePtr->writable, 0) == WAIT_TIMEOUT) {
- return EAGAIN;
+ return EWOULDBLOCK;
}
} else {
@@ -2170,7 +2167,7 @@ PipeOutputProc(
* the channel is in non-blocking mode.
*/
- errno = EAGAIN;
+ errno = EWOULDBLOCK;
goto error;
}
@@ -2721,7 +2718,7 @@ WaitForRead(
* is in non-blocking mode.
*/
- errno = EAGAIN;
+ errno = EWOULDBLOCK;
return -1;
}
diff --git a/win/tclWinPort.h b/win/tclWinPort.h
index 48f7894..ca6b2bf 100644
--- a/win/tclWinPort.h
+++ b/win/tclWinPort.h
@@ -82,6 +82,7 @@ typedef DWORD_PTR * PDWORD_PTR;
*---------------------------------------------------------------------------
*/
+#include <time.h>
#include <wchar.h>
#include <io.h>
#include <errno.h>
@@ -92,11 +93,9 @@ typedef DWORD_PTR * PDWORD_PTR;
#include <signal.h>
#include <limits.h>
-#ifndef strncasecmp
-# define strncasecmp strnicmp
-#endif
-#ifndef strcasecmp
-# define strcasecmp stricmp
+#ifndef __GNUC__
+# define strncasecmp _strnicmp
+# define strcasecmp _stricmp
#endif
/*
@@ -114,8 +113,6 @@ typedef DWORD_PTR * PDWORD_PTR;
# endif /* __BORLANDC__ */
#endif /* __MWERKS__ */
-#include <time.h>
-
/*
* The following defines redefine the Windows Socket errors as
* BSD errors so Tcl_PosixError can do the right thing.
@@ -436,17 +433,17 @@ typedef DWORD_PTR * PDWORD_PTR;
* EDEADLK as the same value, which confuses Tcl_ErrnoId().
*/
-#if defined(_MSC_VER) || defined(__MINGW32__)
+#if defined(_MSC_VER) || defined(__MSVCRT__)
# define environ _environ
# if defined(_MSC_VER) && (_MSC_VER < 1600)
# define hypot _hypot
# endif
# define exception _exception
# undef EDEADLOCK
-# if defined(__MINGW32__) && !defined(__MSVCRT__)
+# if defined(_MSC_VER) && (_MSC_VER >= 1700)
# define timezone _timezone
# endif
-#endif /* _MSC_VER || __MINGW32__ */
+#endif /* _MSC_VER || __MSVCRT__ */
/*
* Borland's timezone and environ functions.
@@ -469,11 +466,12 @@ typedef DWORD_PTR * PDWORD_PTR;
* including the *printf family and others. Tell it to shut up.
* (_MSC_VER is 1200 for VC6, 1300 or 1310 for vc7.net, 1400 for 8.0)
*/
-#if _MSC_VER >= 1400
-#pragma warning(disable:4996)
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)
+# pragma warning(disable:4244)
+# pragma warning(disable:4267)
+# pragma warning(disable:4996)
#endif
-
/*
*---------------------------------------------------------------------------
* The following macros and declarations represent the interface between
@@ -507,7 +505,7 @@ typedef DWORD_PTR * PDWORD_PTR;
* Msvcrt's putenv() copies the string rather than takes ownership of it.
*/
-#if defined(_MSC_VER) || defined(__MINGW32__)
+#if defined(_MSC_VER) || defined(__MSVCRT__)
# define HAVE_PUTENV_THAT_COPIES 1
#endif
@@ -530,15 +528,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/tclWinReg.c b/win/tclWinReg.c
index 6ac5caf..327e4a3 100644
--- a/win/tclWinReg.c
+++ b/win/tclWinReg.c
@@ -13,9 +13,9 @@
*/
#undef STATIC_BUILD
-#undef USE_TCL_STUBS
-#define USE_TCL_STUBS
-
+#ifndef USE_TCL_STUBS
+# define USE_TCL_STUBS
+#endif
#include "tclInt.h"
#ifdef _MSC_VER
# pragma comment (lib, "advapi32.lib")
diff --git a/win/tclWinSerial.c b/win/tclWinSerial.c
index 9e9d1af..0730a46 100644
--- a/win/tclWinSerial.c
+++ b/win/tclWinSerial.c
@@ -14,8 +14,6 @@
#include "tclWinInt.h"
-#include <sys/stat.h>
-
/*
* The following variable is used to tell whether this module has been
* initialized.
@@ -376,7 +374,7 @@ SerialGetMilliseconds(void)
{
Tcl_Time time;
- TclpGetTime(&time);
+ Tcl_GetTime(&time);
return (time.sec * 1000 + time.usec / 1000);
}
@@ -934,7 +932,7 @@ SerialInputProc(
bufSize = cStat.cbInQue;
}
} else {
- errno = *errorCode = EAGAIN;
+ errno = *errorCode = EWOULDBLOCK;
return -1;
}
} else {
@@ -1036,7 +1034,7 @@ SerialOutputProc(
* the channel is in non-blocking mode.
*/
- errno = EAGAIN;
+ errno = EWOULDBLOCK;
goto error1;
}
@@ -1412,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)
@@ -1436,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 1a74354..a022ed5 100644
--- a/win/tclWinSock.c
+++ b/win/tclWinSock.c
@@ -9,6 +9,9 @@
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* -----------------------------------------------------------------------
+ * The order and naming of functions in this file should minimize
+ * the file diff to tclUnixSock.c.
+ * -----------------------------------------------------------------------
*
* General information on how this module works.
*
@@ -47,13 +50,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
@@ -75,6 +71,19 @@
#undef setsockopt
/*
+ * Helper macros to make parts of this file clearer. The macros do exactly
+ * what they say on the tin. :-) They also only ever refer to their arguments
+ * once, and so can be used without regard to side effects.
+ */
+
+#define SET_BITS(var, bits) ((var) |= (bits))
+#define CLEAR_BITS(var, bits) ((var) &= ~(bits))
+
+/* "sock" + a pointer in hex + \0 */
+#define SOCK_CHAN_LENGTH (4 + sizeof(void *) * 2 + 1)
+#define SOCK_TEMPLATE "sock%p"
+
+/*
* The following variable is used to tell whether this module has been
* initialized. If 1, initialization of sockets was successful, if -1 then
* socket initialization failed (WSAStartup failed).
@@ -85,15 +94,6 @@ static const TCHAR classname[] = TEXT("TclSocket");
TCL_DECLARE_MUTEX(socketMutex)
/*
- * The following variable holds the network name of this host.
- */
-
-static TclInitProcessGlobalValueProc InitializeHostName;
-static ProcessGlobalValue hostName = {
- 0, 0, NULL, NULL, InitializeHostName, NULL, NULL
-};
-
-/*
* The following defines declare the messages used on socket windows.
*/
@@ -119,20 +119,19 @@ typedef union {
#define IN6_ARE_ADDR_EQUAL IN6_ADDR_EQUAL
#endif
-typedef struct SocketInfo SocketInfo;
+/*
+ * This structure describes per-instance state of a tcp based channel.
+ */
+
+typedef struct TcpState TcpState;
typedef struct TcpFdList {
- SocketInfo *infoPtr;
+ TcpState *statePtr;
SOCKET fd;
struct TcpFdList *next;
} TcpFdList;
-/*
- * The following structure is used to store the data associated with each
- * socket.
- */
-
-struct SocketInfo {
+struct TcpState {
Tcl_Channel channel; /* Channel associated with this socket. */
struct TcpFdList *sockets; /* Windows SOCKET handle. */
int flags; /* Bit field comprised of the flags described
@@ -140,24 +139,60 @@ struct SocketInfo {
int watchEvents; /* OR'ed combination of FD_READ, FD_WRITE,
* FD_CLOSE, FD_ACCEPT and FD_CONNECT that
* indicate which events are interesting. */
- int readyEvents; /* OR'ed combination of FD_READ, FD_WRITE,
+ volatile int readyEvents; /* OR'ed combination of FD_READ, FD_WRITE,
* FD_CLOSE, FD_ACCEPT and FD_CONNECT that
- * indicate which events have occurred. */
+ * indicate which events have occurred.
+ * Set by notifier thread, access must be
+ * protected by semaphore */
int selectEvents; /* OR'ed combination of FD_READ, FD_WRITE,
* FD_CLOSE, FD_ACCEPT and FD_CONNECT that
* indicate which events are currently being
* selected. */
- int acceptEventCount; /* Count of the current number of FD_ACCEPTs
- * that have arrived and not yet processed. */
+ volatile int acceptEventCount;
+ /* Count of the current number of FD_ACCEPTs
+ * that have arrived and not yet processed.
+ * Set by notifier thread, access must be
+ * protected by semaphore */
Tcl_TcpAcceptProc *acceptProc;
/* Proc to call on accept. */
ClientData acceptProcData; /* The data for the accept proc. */
- int lastError; /* Error code from last message. */
- struct SocketInfo *nextPtr; /* The next socket on the per-thread socket
+
+ /*
+ * Only needed for client sockets
+ */
+
+ struct addrinfo *addrlist; /* Addresses to connect to. */
+ struct addrinfo *addr; /* Iterator over addrlist. */
+ struct addrinfo *myaddrlist;/* Local address. */
+ struct addrinfo *myaddr; /* Iterator over myaddrlist. */
+ int connectError; /* Cache status of async socket. */
+ int cachedBlocking; /* Cache blocking mode of async socket. */
+ volatile int notifierConnectError;
+ /* Async connect error set by notifier thread.
+ * This error is still a windows error code.
+ * Access must be protected by semaphore */
+ struct TcpState *nextPtr; /* The next socket on the per-thread socket
* list. */
};
/*
+ * These bits may be ORed together into the "flags" field of a TcpState
+ * structure.
+ */
+
+#define TCP_NONBLOCKING (1<<0) /* Socket with non-blocking I/O */
+#define TCP_ASYNC_CONNECT (1<<1) /* Async connect in progress. */
+#define SOCKET_EOF (1<<2) /* A zero read happened on the
+ * socket. */
+#define SOCKET_PENDING (1<<3) /* A message has been sent for this
+ * socket */
+#define TCP_ASYNC_PENDING (1<<4) /* TcpConnect was called to
+ * process an async connect. This
+ * flag indicates that reentry is
+ * still pending */
+#define TCP_ASYNC_FAILED (1<<5) /* An async connect finally failed */
+
+/*
* The following structure is what is added to the Tcl event queue when a
* socket event occurs.
*/
@@ -166,8 +201,8 @@ typedef struct {
Tcl_Event header; /* Information that is standard for all
* events. */
SOCKET socket; /* Socket descriptor that is ready. Used to
- * find the SocketInfo structure for the file
- * (can't point directly to the SocketInfo
+ * find the TcpState structure for the file
+ * (can't point directly to the TcpState
* structure because it could go away while
* the event is queued). */
} SocketEvent;
@@ -178,17 +213,6 @@ typedef struct {
#define TCP_BUFFER_SIZE 4096
-/*
- * The following macros may be used to set the flags field of a SocketInfo
- * structure.
- */
-
-#define SOCKET_ASYNC (1<<0) /* The socket is in blocking mode. */
-#define SOCKET_EOF (1<<1) /* A zero read happened on the
- * socket. */
-#define SOCKET_ASYNC_CONNECT (1<<2) /* This socket uses async connect. */
-#define SOCKET_PENDING (1<<3) /* A message has been sent for this
- * socket */
typedef struct {
HWND hwnd; /* Handle to window for socket messages. */
@@ -199,7 +223,11 @@ typedef struct {
* socketThread has been initialized and has
* started. */
HANDLE socketListLock; /* Win32 Event to lock the socketList */
- SocketInfo *socketList; /* Every open socket in this thread has an
+ TcpState *pendingTcpState;
+ /* This socket is opened but not jet in the
+ * list. This value is also checked by
+ * the event structure. */
+ TcpState *socketList; /* Every open socket in this thread has an
* entry on this list. */
} ThreadSpecificData;
@@ -207,21 +235,23 @@ static Tcl_ThreadDataKey dataKey;
static WNDCLASS windowClass;
/*
- * Static functions defined in this file.
+ * Static routines for this file:
*/
-static SocketInfo * CreateSocket(Tcl_Interp *interp, int port,
- const char *host, int server, const char *myaddr,
- int myport, int async);
+static int TcpConnect(Tcl_Interp *interp,
+ TcpState *state);
static void InitSockets(void);
-static SocketInfo * NewSocketInfo(SOCKET socket);
+static TcpState * NewSocketInfo(SOCKET socket);
static void SocketExitHandler(ClientData clientData);
static LRESULT CALLBACK SocketProc(HWND hwnd, UINT message, WPARAM wParam,
LPARAM lParam);
static int SocketsEnabled(void);
static void TcpAccept(TcpFdList *fds, SOCKET newSocket, address addr);
-static int WaitForSocketEvent(SocketInfo *infoPtr, int events,
+static int WaitForConnect(TcpState *statePtr, int *errorCodePtr);
+static int WaitForSocketEvent(TcpState *statePtr, int events,
int *errorCodePtr);
+static void AddSocketInfoFd(TcpState *statePtr, SOCKET socket);
+static int FindFDInList(TcpState *statePtr, SOCKET socket);
static DWORD WINAPI SocketThread(LPVOID arg);
static void TcpThreadActionProc(ClientData instanceData,
int action);
@@ -229,7 +259,7 @@ static void TcpThreadActionProc(ClientData instanceData,
static Tcl_EventCheckProc SocketCheckProc;
static Tcl_EventProc SocketEventProc;
static Tcl_EventSetupProc SocketSetupProc;
-static Tcl_DriverBlockModeProc TcpBlockProc;
+static Tcl_DriverBlockModeProc TcpBlockModeProc;
static Tcl_DriverCloseProc TcpCloseProc;
static Tcl_DriverClose2Proc TcpClose2Proc;
static Tcl_DriverSetOptionProc TcpSetOptionProc;
@@ -241,227 +271,180 @@ static Tcl_DriverGetHandleProc TcpGetHandleProc;
/*
* This structure describes the channel type structure for TCP socket
- * based IO.
+ * based IO:
*/
static const Tcl_ChannelType tcpChannelType = {
- "tcp", /* Type name. */
- TCL_CHANNEL_VERSION_5, /* v5 channel */
- TcpCloseProc, /* Close proc. */
- TcpInputProc, /* Input proc. */
- TcpOutputProc, /* Output proc. */
- NULL, /* Seek proc. */
- TcpSetOptionProc, /* Set option proc. */
- TcpGetOptionProc, /* Get option proc. */
- TcpWatchProc, /* Set up notifier to watch this channel. */
- TcpGetHandleProc, /* Get an OS handle from channel. */
- TcpClose2Proc, /* Close2proc. */
- TcpBlockProc, /* Set socket into (non-)blocking mode. */
- NULL, /* flush proc. */
- NULL, /* handler proc. */
- NULL, /* wide seek proc */
- TcpThreadActionProc, /* thread action proc */
- NULL /* truncate */
+ "tcp", /* Type name. */
+ TCL_CHANNEL_VERSION_5, /* v5 channel */
+ TcpCloseProc, /* Close proc. */
+ TcpInputProc, /* Input proc. */
+ TcpOutputProc, /* Output proc. */
+ NULL, /* Seek proc. */
+ TcpSetOptionProc, /* Set option proc. */
+ TcpGetOptionProc, /* Get option proc. */
+ TcpWatchProc, /* Initialize notifier. */
+ TcpGetHandleProc, /* Get OS handles out of channel. */
+ TcpClose2Proc, /* Close2 proc. */
+ TcpBlockModeProc, /* Set blocking or non-blocking mode.*/
+ NULL, /* flush proc. */
+ NULL, /* handler proc. */
+ NULL, /* wide seek proc. */
+ TcpThreadActionProc, /* thread action proc. */
+ NULL /* truncate proc. */
};
+
+/*
+ * The following variable holds the network name of this host.
+ */
+
+static TclInitProcessGlobalValueProc InitializeHostName;
+static ProcessGlobalValue hostName =
+ {0, 0, NULL, NULL, InitializeHostName, NULL, NULL};
+
+/*
+ * Address print debug functions
+ */
+#if 0
+void printaddrinfo(struct addrinfo *ai, char *prefix)
+{
+ char host[NI_MAXHOST], port[NI_MAXSERV];
+ getnameinfo(ai->ai_addr, ai->ai_addrlen,
+ host, sizeof(host),
+ port, sizeof(port),
+ NI_NUMERICHOST|NI_NUMERICSERV);
+}
+void printaddrinfolist(struct addrinfo *addrlist, char *prefix)
+{
+ struct addrinfo *ai;
+ for (ai = addrlist; ai != NULL; ai = ai->ai_next) {
+ printaddrinfo(ai, prefix);
+ }
+}
+#endif
/*
*----------------------------------------------------------------------
*
- * InitSockets --
- *
- * Initialize the socket module. If winsock startup is successful,
- * registers the event window for the socket notifier code.
+ * InitializeHostName --
*
- * Assumes socketMutex is held.
+ * This routine sets the process global value of the name of the local
+ * host on which the process is running.
*
* Results:
* None.
*
- * Side effects:
- * Initializes winsock, registers a new window class and creates a
- * window for use in asynchronous socket notification.
- *
*----------------------------------------------------------------------
*/
-static void
-InitSockets(void)
+void
+InitializeHostName(
+ char **valuePtr,
+ int *lengthPtr,
+ Tcl_Encoding *encodingPtr)
{
- DWORD id, err;
- WSADATA wsaData;
- ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);
-
- if (!initialized) {
- initialized = 1;
- TclCreateLateExitHandler(SocketExitHandler, NULL);
+ TCHAR tbuf[MAX_COMPUTERNAME_LENGTH + 1];
+ DWORD length = MAX_COMPUTERNAME_LENGTH + 1;
+ Tcl_DString ds;
+ if (GetComputerName(tbuf, &length) != 0) {
/*
- * Create the async notification window with a new class. We must
- * create a new class to avoid a Windows 95 bug that causes us to get
- * the wrong message number for socket events if the message window is
- * a subclass of a static control.
+ * Convert string from native to UTF then change to lowercase.
*/
- windowClass.style = 0;
- windowClass.cbClsExtra = 0;
- windowClass.cbWndExtra = 0;
- windowClass.hInstance = TclWinGetTclInstance();
- windowClass.hbrBackground = NULL;
- windowClass.lpszMenuName = NULL;
- windowClass.lpszClassName = classname;
- windowClass.lpfnWndProc = SocketProc;
- windowClass.hIcon = NULL;
- windowClass.hCursor = NULL;
-
- if (!RegisterClass(&windowClass)) {
- 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.
- */
+ Tcl_UtfToLower(Tcl_WinTCharToUtf(tbuf, -1, &ds));
- err = WSAStartup((WORD) MAKEWORD(WSA_VERSION_MAJOR,WSA_VERSION_MINOR),
- &wsaData);
- if (err != 0) {
- TclWinConvertError(err);
- goto initFailure;
- }
+ } else {
+ Tcl_DStringInit(&ds);
+ if (TclpHasSockets(NULL) == TCL_OK) {
+ /*
+ * The buffer size of 256 is recommended by the MSDN page that
+ * documents gethostname() as being always adequate.
+ */
- /*
- * 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.
- */
+ Tcl_DString inDs;
- if (MAKEWORD(HIBYTE(wsaData.wVersion), LOBYTE(wsaData.wVersion))
- < MAKEWORD(WSA_VERSION_MINOR, WSA_VERSION_MAJOR)) {
- TclWinConvertError(WSAVERNOTSUPPORTED);
- WSACleanup();
- goto initFailure;
+ Tcl_DStringInit(&inDs);
+ Tcl_DStringSetLength(&inDs, 256);
+ if (gethostname(Tcl_DStringValue(&inDs),
+ Tcl_DStringLength(&inDs)) == 0) {
+ Tcl_ExternalToUtfDString(NULL, Tcl_DStringValue(&inDs), -1,
+ &ds);
+ }
+ Tcl_DStringFree(&inDs);
}
}
- /*
- * Check for per-thread initialization.
- */
-
- if (tsdPtr != NULL) {
- return;
- }
-
- /*
- * OK, this thread has never done anything with sockets before. Construct
- * a worker thread to handle asynchronous events related to sockets
- * assigned to _this_ thread.
- */
-
- tsdPtr = TCL_TSD_INIT(&dataKey);
- tsdPtr->socketList = NULL;
- tsdPtr->hwnd = NULL;
- tsdPtr->threadId = Tcl_GetCurrentThread();
- tsdPtr->readyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- if (tsdPtr->readyEvent == NULL) {
- goto initFailure;
- }
- tsdPtr->socketListLock = CreateEvent(NULL, FALSE, TRUE, NULL);
- if (tsdPtr->socketListLock == NULL) {
- goto initFailure;
- }
- tsdPtr->socketThread = CreateThread(NULL, 256, SocketThread, tsdPtr, 0,
- &id);
- if (tsdPtr->socketThread == NULL) {
- goto initFailure;
- }
-
- SetThreadPriority(tsdPtr->socketThread, THREAD_PRIORITY_HIGHEST);
-
- /*
- * Wait for the thread to signal when the window has been created and if
- * it is ready to go.
- */
-
- WaitForSingleObject(tsdPtr->readyEvent, INFINITE);
-
- if (tsdPtr->hwnd == NULL) {
- goto initFailure; /* Trouble creating the window. */
- }
-
- Tcl_CreateEventSource(SocketSetupProc, SocketCheckProc, NULL);
- return;
-
- initFailure:
- TclpFinalizeSockets();
- initialized = -1;
- return;
+ *encodingPtr = Tcl_GetEncoding(NULL, "utf-8");
+ *lengthPtr = Tcl_DStringLength(&ds);
+ *valuePtr = ckalloc((*lengthPtr) + 1);
+ memcpy(*valuePtr, Tcl_DStringValue(&ds), (size_t)(*lengthPtr)+1);
+ Tcl_DStringFree(&ds);
}
/*
*----------------------------------------------------------------------
*
- * SocketsEnabled --
+ * Tcl_GetHostName --
*
- * Check that the WinSock was successfully initialized.
+ * Returns the name of the local host.
*
* Results:
- * 1 if it is.
+ * A string containing the network name for this machine, or an empty
+ * string if we can't figure out the name. The caller must not modify or
+ * free this string.
*
* Side effects:
- * None.
+ * Caches the name to return for future calls.
*
*----------------------------------------------------------------------
*/
- /* ARGSUSED */
-static int
-SocketsEnabled(void)
+const char *
+Tcl_GetHostName(void)
{
- int enabled;
-
- Tcl_MutexLock(&socketMutex);
- enabled = (initialized == 1);
- Tcl_MutexUnlock(&socketMutex);
- return enabled;
+ return Tcl_GetString(TclGetProcessGlobalValue(&hostName));
}
-
/*
*----------------------------------------------------------------------
*
- * SocketExitHandler --
+ * TclpHasSockets --
*
- * Callback invoked during exit clean up to delete the socket
- * communication window and to release the WinSock DLL.
+ * This function determines whether sockets are available on the current
+ * system and returns an error in interp if they are not. Note that
+ * interp may be NULL.
*
* Results:
- * None.
+ * Returns TCL_OK if the system supports sockets, or TCL_ERROR with an
+ * error in interp (if non-NULL).
*
* Side effects:
- * None.
+ * If not already prepared, initializes the TSD structure and socket
+ * message handling thread associated to the calling thread for the
+ * subsystem of the driver.
*
*----------------------------------------------------------------------
*/
- /* ARGSUSED */
-static void
-SocketExitHandler(
- ClientData clientData) /* Not used. */
+int
+TclpHasSockets(
+ Tcl_Interp *interp) /* Where to write an error message if sockets
+ * are not present, or NULL if no such message
+ * is to be written. */
{
Tcl_MutexLock(&socketMutex);
-
- /*
- * Make sure the socket event handling window is cleaned-up for, at
- * most, this thread.
- */
-
- TclpFinalizeSockets();
- UnregisterClass(classname, TclWinGetTclInstance());
- WSACleanup();
- initialized = 0;
+ InitSockets();
Tcl_MutexUnlock(&socketMutex);
+
+ if (SocketsEnabled()) {
+ return TCL_OK;
+ }
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "sockets are not available on this system", -1));
+ }
+ return TCL_ERROR;
}
/*
@@ -525,365 +508,451 @@ TclpFinalizeSockets(void)
/*
*----------------------------------------------------------------------
*
- * TclpHasSockets --
+ * TcpBlockModeProc --
*
- * This function determines whether sockets are available on the current
- * system and returns an error in interp if they are not. Note that
- * interp may be NULL.
+ * This function is invoked by the generic IO level to set blocking and
+ * nonblocking mode on a TCP socket based channel.
*
* Results:
- * Returns TCL_OK if the system supports sockets, or TCL_ERROR with an
- * error in interp (if non-NULL).
+ * 0 if successful, errno when failed.
*
* Side effects:
- * If not already prepared, initializes the TSD structure and socket
- * message handling thread associated to the calling thread for the
- * subsystem of the driver.
+ * Sets the device into blocking or nonblocking mode.
*
*----------------------------------------------------------------------
*/
-int
-TclpHasSockets(
- Tcl_Interp *interp) /* Where to write an error message if sockets
- * are not present, or NULL if no such message
- * is to be written. */
+ /* ARGSUSED */
+static int
+TcpBlockModeProc(
+ ClientData instanceData, /* Socket state. */
+ int mode) /* The mode to set. Can be one of
+ * TCL_MODE_BLOCKING or
+ * TCL_MODE_NONBLOCKING. */
{
- Tcl_MutexLock(&socketMutex);
- InitSockets();
- Tcl_MutexUnlock(&socketMutex);
+ TcpState *statePtr = instanceData;
- if (SocketsEnabled()) {
- return TCL_OK;
- }
- if (interp != NULL) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "sockets are not available on this system", -1));
+ if (mode == TCL_MODE_NONBLOCKING) {
+ statePtr->flags |= TCP_NONBLOCKING;
+ } else {
+ statePtr->flags &= ~(TCP_NONBLOCKING);
}
- return TCL_ERROR;
+ return 0;
}
/*
*----------------------------------------------------------------------
*
- * SocketSetupProc --
+ * WaitForConnect --
*
- * This function is invoked before Tcl_DoOneEvent blocks waiting for an
- * event.
+ * Check the state of an async connect process. If a connection
+ * attempt terminated, process it, which may finalize it or may
+ * start the next attempt. If a connect error occures, it is saved
+ * in statePtr->connectError to be reported by 'fconfigure -error'.
+ *
+ * There are two modes of operation, defined by errorCodePtr:
+ * * non-NULL: Called by explicite read/write command. block if
+ * socket is blocking.
+ * May return two error codes:
+ * * EWOULDBLOCK: if connect is still in progress
+ * * ENOTCONN: if connect failed. This would be the error
+ * message of a rect or sendto syscall so this is
+ * emulated here.
+ * * Null: Called by a backround operation. Do not block and
+ * don't return any error code.
*
* Results:
- * None.
+ * 0 if the connection has completed, -1 if still in progress
+ * or there is an error.
*
* Side effects:
- * Adjusts the block time if needed.
+ * Processes socket events off the system queue.
+ * May process asynchroneous connect.
*
*----------------------------------------------------------------------
*/
-void
-SocketSetupProc(
- ClientData data, /* Not used. */
- int flags) /* Event flags as passed to Tcl_DoOneEvent. */
+static int
+WaitForConnect(
+ TcpState *statePtr, /* State of the socket. */
+ int *errorCodePtr) /* Where to store errors?
+ * A passed null-pointer activates background mode.
+ */
{
- SocketInfo *infoPtr;
- Tcl_Time blockTime = { 0, 0 };
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ int result;
+ int oldMode;
+ ThreadSpecificData *tsdPtr;
- if (!(flags & TCL_FILE_EVENTS)) {
- return;
+ /*
+ * Check if an async connect failed already and error reporting is demanded,
+ * return the error ENOTCONN
+ */
+
+ if (errorCodePtr != NULL && (statePtr->flags & TCP_ASYNC_FAILED)) {
+ *errorCodePtr = ENOTCONN;
+ return -1;
}
/*
- * Check to see if there is a ready socket. If so, poll.
+ * Check if an async connect is running. If not return ok
*/
- WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
- for (infoPtr = tsdPtr->socketList; infoPtr != NULL;
- infoPtr = infoPtr->nextPtr) {
- if (infoPtr->readyEvents & infoPtr->watchEvents) {
- Tcl_SetMaxBlockTime(&blockTime);
- break;
- }
+ if (!(statePtr->flags & TCP_ASYNC_CONNECT)) {
+ return 0;
}
- SetEvent(tsdPtr->socketListLock);
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * SocketCheckProc --
- *
- * This function is called by Tcl_DoOneEvent to check the socket event
- * source for events.
- *
- * Results:
- * None.
- *
- * Side effects:
- * May queue an event.
- *
- *----------------------------------------------------------------------
- */
-static void
-SocketCheckProc(
- ClientData data, /* Not used. */
- int flags) /* Event flags as passed to Tcl_DoOneEvent. */
-{
- SocketInfo *infoPtr;
- SocketEvent *evPtr;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ /*
+ * Be sure to disable event servicing so we are truly modal.
+ */
- if (!(flags & TCL_FILE_EVENTS)) {
- return;
- }
+ oldMode = Tcl_SetServiceMode(TCL_SERVICE_NONE);
/*
- * Queue events for any ready sockets that don't already have events
- * queued (caused by persistent states that won't generate WinSock
- * events).
+ * Loop in the blocking case until the connect signal is present
*/
- WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
- for (infoPtr = tsdPtr->socketList; infoPtr != NULL;
- infoPtr = infoPtr->nextPtr) {
- if ((infoPtr->readyEvents & infoPtr->watchEvents)
- && !(infoPtr->flags & SOCKET_PENDING)) {
- infoPtr->flags |= SOCKET_PENDING;
- evPtr = ckalloc(sizeof(SocketEvent));
- evPtr->header.proc = SocketEventProc;
- evPtr->socket = infoPtr->sockets->fd;
- Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
+ while (1) {
+
+ /* get statePtr lock */
+ tsdPtr = TclThreadDataKeyGet(&dataKey);
+ WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
+
+ /* Check for connect event */
+ if (statePtr->readyEvents & FD_CONNECT) {
+
+ /* Consume the connect event */
+ statePtr->readyEvents &= ~(FD_CONNECT);
+
+ /*
+ * For blocking sockets and foreground processing
+ * disable async connect as we continue now synchoneously
+ */
+ if ( errorCodePtr != NULL &&
+ ! (statePtr->flags & TCP_NONBLOCKING) ) {
+ CLEAR_BITS(statePtr->flags, TCP_ASYNC_CONNECT);
+ }
+
+ /* Free list lock */
+ SetEvent(tsdPtr->socketListLock);
+
+ /*
+ * Continue connect.
+ * If switched to synchroneous connect, the connect is terminated.
+ */
+ result = TcpConnect(NULL, statePtr);
+
+ /* Restore event service mode */
+ (void) Tcl_SetServiceMode(oldMode);
+
+ /*
+ * Check for Succesfull connect or async connect restart
+ */
+
+ if (result == TCL_OK) {
+ /*
+ * Check for async connect restart
+ * (not possible for foreground blocking operation)
+ */
+ if ( statePtr->flags & TCP_ASYNC_PENDING ) {
+ if (errorCodePtr != NULL) {
+ *errorCodePtr = EWOULDBLOCK;
+ }
+ return -1;
+ }
+ return 0;
+ }
+
+ /*
+ * Connect finally failed.
+ * For foreground operation return ENOTCONN.
+ */
+
+ if (errorCodePtr != NULL) {
+ *errorCodePtr = ENOTCONN;
+ }
+ return -1;
}
+
+ /* Free list lock */
+ SetEvent(tsdPtr->socketListLock);
+
+ /*
+ * Background operation returns with no action as there was no connect
+ * event
+ */
+
+ if ( errorCodePtr == NULL ) {
+ return -1;
+ }
+
+ /*
+ * A non blocking socket waiting for an asyncronous connect
+ * returns directly the error EWOULDBLOCK
+ */
+
+ if (statePtr->flags & TCP_NONBLOCKING) {
+ *errorCodePtr = EWOULDBLOCK;
+ return -1;
+ }
+
+ /*
+ * Wait until something happens.
+ */
+
+ WaitForSingleObject(tsdPtr->readyEvent, INFINITE);
}
- SetEvent(tsdPtr->socketListLock);
}
/*
*----------------------------------------------------------------------
*
- * SocketEventProc --
+ * TcpInputProc --
*
- * This function is called by Tcl_ServiceEvent when a socket event
- * reaches the front of the event queue. This function is responsible for
- * notifying the generic channel code.
+ * This function is invoked by the generic IO level to read input from a
+ * TCP socket based channel.
*
* Results:
- * Returns 1 if the event was handled, meaning it should be removed from
- * the queue. Returns 0 if the event was not handled, meaning it should
- * stay on the queue. The only time the event isn't handled is if the
- * TCL_FILE_EVENTS flag bit isn't set.
+ * The number of bytes read is returned or -1 on error. An output
+ * argument contains the POSIX error code on error, or zero if no error
+ * occurred.
*
* Side effects:
- * Whatever the channel callback functions do.
+ * Reads input from the input device of the channel.
*
*----------------------------------------------------------------------
*/
+ /* ARGSUSED */
static int
-SocketEventProc(
- Tcl_Event *evPtr, /* Event to service. */
- int flags) /* Flags that indicate what events to handle,
- * such as TCL_FILE_EVENTS. */
+TcpInputProc(
+ ClientData instanceData, /* Socket state. */
+ char *buf, /* Where to store data read. */
+ int bufSize, /* How much space is available in the
+ * buffer? */
+ int *errorCodePtr) /* Where to store error code. */
{
- SocketInfo *infoPtr;
- SocketEvent *eventPtr = (SocketEvent *) evPtr;
- int mask = 0, events;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- TcpFdList *fds;
- SOCKET newSocket;
- address addr;
- int len;
+ TcpState *statePtr = instanceData;
+ int bytesRead;
+ DWORD error;
+ ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);
- if (!(flags & TCL_FILE_EVENTS)) {
- return 0;
- }
+ *errorCodePtr = 0;
/*
- * Find the specified socket on the socket list.
+ * 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.
*/
- WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
- for (infoPtr = tsdPtr->socketList; infoPtr != NULL;
- infoPtr = infoPtr->nextPtr) {
- if (infoPtr->sockets->fd == eventPtr->socket) {
- break;
- }
+ if (!SocketsEnabled()) {
+ *errorCodePtr = EFAULT;
+ return -1;
}
/*
- * Discard events that have gone stale.
+ * First check to see if EOF was already detected, to prevent calling the
+ * socket stack after the first time EOF is detected.
*/
- if (!infoPtr) {
- SetEvent(tsdPtr->socketListLock);
- return 1;
+ if (statePtr->flags & SOCKET_EOF) {
+ return 0;
}
- infoPtr->flags &= ~SOCKET_PENDING;
-
/*
- * Handle connection requests directly.
+ * Check if there is an async connect running.
+ * For blocking sockets terminate connect, otherwise do one step.
+ * For a non blocking socket return EWOULDBLOCK if connect not terminated
*/
- if (infoPtr->readyEvents & FD_ACCEPT) {
- for (fds = infoPtr->sockets; fds != NULL; fds = fds->next) {
-
- /*
- * Accept the incoming connection request.
- */
- len = sizeof(address);
-
- newSocket = accept(fds->fd, &(addr.sa), &len);
-
- /* On Tcl server sockets with multiple OS fds we loop over the fds trying
- * an accept() on each, so we expect INVALID_SOCKET. There are also other
- * network stack conditions that can result in FD_ACCEPT but a subsequent
- * failure on accept() by the time we get around to it.
- * Access to sockets (acceptEventCount, readyEvents) in socketList
- * is still protected by the lock (prevents reintroduction of
- * SF Tcl Bug 3056775.
- */
-
- if (newSocket == INVALID_SOCKET) {
- /* int err = WSAGetLastError(); */
- continue;
- }
+ if (WaitForConnect(statePtr, errorCodePtr) != 0) {
+ return -1;
+ }
- /*
- * It is possible that more than one FD_ACCEPT has been sent, so an extra
- * count must be kept. Decrement the count, and reset the readyEvent bit
- * if the count is no longer > 0.
- */
- infoPtr->acceptEventCount--;
+ /*
+ * No EOF, and it is connected, so try to read more from the socket. Note
+ * that we clear the FD_READ bit because read events are level triggered
+ * so a new event will be generated if there is still data available to be
+ * read. We have to simulate blocking behavior here since we are always
+ * using non-blocking sockets.
+ */
- if (infoPtr->acceptEventCount <= 0) {
- infoPtr->readyEvents &= ~(FD_ACCEPT);
- }
+ while (1) {
+ SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
+ (WPARAM) UNSELECT, (LPARAM) statePtr);
+ /* single fd operation: this proc is only called for a connected socket. */
+ bytesRead = recv(statePtr->sockets->fd, buf, bufSize, 0);
+ statePtr->readyEvents &= ~(FD_READ);
- SetEvent(tsdPtr->socketListLock);
+ /*
+ * Check for end-of-file condition or successful read.
+ */
- /* Caution: TcpAccept() has the side-effect of evaluating the server
- * accept script (via AcceptCallbackProc() in tclIOCmd.c), which can
- * close the server socket and invalidate infoPtr and fds.
- * If TcpAccept() accepts a socket we must return immediately and let
- * SocketCheckProc queue additional FD_ACCEPT events.
- */
- TcpAccept(fds, newSocket, addr);
- return 1;
+ if (bytesRead == 0) {
+ statePtr->flags |= SOCKET_EOF;
+ }
+ if (bytesRead != SOCKET_ERROR) {
+ break;
}
- /* Loop terminated with no sockets accepted; clear the ready mask so
- * we can detect the next connection request. Note that connection
- * requests are level triggered, so if there is a request already
- * pending, a new event will be generated.
+ /*
+ * If an error occurs after the FD_CLOSE has arrived, then ignore the
+ * error and report an EOF.
*/
- infoPtr->acceptEventCount = 0;
- infoPtr->readyEvents &= ~(FD_ACCEPT);
-
- SetEvent(tsdPtr->socketListLock);
- return 1;
- }
-
- SetEvent(tsdPtr->socketListLock);
- /*
- * Mask off unwanted events and compute the read/write mask so we can
- * notify the channel.
- */
+ if (statePtr->readyEvents & FD_CLOSE) {
+ statePtr->flags |= SOCKET_EOF;
+ bytesRead = 0;
+ break;
+ }
- events = infoPtr->readyEvents & infoPtr->watchEvents;
+ error = WSAGetLastError();
- if (events & FD_CLOSE) {
/*
- * If the socket was closed and the channel is still interested in
- * read events, then we need to ensure that we keep polling for this
- * event until someone does something with the channel. Note that we
- * do this before calling Tcl_NotifyChannel so we don't have to watch
- * out for the channel being deleted out from under us. This may cause
- * a redundant trip through the event loop, but it's simpler than
- * trying to do unwind protection.
+ * If an RST comes, then ignore the error and report an EOF just like
+ * on unix.
*/
- Tcl_Time blockTime = { 0, 0 };
-
- Tcl_SetMaxBlockTime(&blockTime);
- mask |= TCL_READABLE|TCL_WRITABLE;
- } else if (events & FD_READ) {
- fd_set readFds;
- struct timeval timeout;
+ if (error == WSAECONNRESET) {
+ statePtr->flags |= SOCKET_EOF;
+ bytesRead = 0;
+ break;
+ }
/*
- * We must check to see if data is really available, since someone
- * could have consumed the data in the meantime. Turn off async
- * notification so select will work correctly. If the socket is still
- * readable, notify the channel driver, otherwise reset the async
- * select handler and keep waiting.
+ * Check for error condition or underflow in non-blocking case.
*/
- SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
- (WPARAM) UNSELECT, (LPARAM) infoPtr);
+ if ((statePtr->flags & TCP_NONBLOCKING) || (error != WSAEWOULDBLOCK)) {
+ TclWinConvertError(error);
+ *errorCodePtr = Tcl_GetErrno();
+ bytesRead = -1;
+ break;
+ }
- FD_ZERO(&readFds);
- FD_SET(infoPtr->sockets->fd, &readFds);
- timeout.tv_usec = 0;
- timeout.tv_sec = 0;
+ /*
+ * In the blocking case, wait until the file becomes readable or
+ * closed and try again.
+ */
- if (select(0, &readFds, NULL, NULL, &timeout) != 0) {
- mask |= TCL_READABLE;
- } else {
- infoPtr->readyEvents &= ~(FD_READ);
- SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
- (WPARAM) SELECT, (LPARAM) infoPtr);
+ if (!WaitForSocketEvent(statePtr, FD_READ|FD_CLOSE, errorCodePtr)) {
+ bytesRead = -1;
+ break;
}
}
- if (events & (FD_WRITE | FD_CONNECT)) {
- mask |= TCL_WRITABLE;
- if (events & FD_CONNECT && infoPtr->lastError != NO_ERROR) {
- /*
- * Connect errors should also fire the readable handler.
- */
- mask |= TCL_READABLE;
- }
- }
+ SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM)SELECT, (LPARAM)statePtr);
- if (mask) {
- Tcl_NotifyChannel(infoPtr->channel, mask);
- }
- return 1;
+ return bytesRead;
}
/*
*----------------------------------------------------------------------
*
- * TcpBlockProc --
+ * TcpOutputProc --
*
- * Sets a socket into blocking or non-blocking mode.
+ * This function is called by the generic IO level to write data to a
+ * socket based channel.
*
* Results:
- * 0 if successful, errno if there was an error.
+ * The number of bytes written or -1 on failure.
*
* Side effects:
- * None.
+ * Produces output on the socket.
*
*----------------------------------------------------------------------
*/
static int
-TcpBlockProc(
- ClientData instanceData, /* The socket to block/un-block. */
- int mode) /* TCL_MODE_BLOCKING or
- * TCL_MODE_NONBLOCKING. */
+TcpOutputProc(
+ ClientData instanceData, /* Socket state. */
+ const char *buf, /* The data buffer. */
+ int toWrite, /* How many bytes to write? */
+ int *errorCodePtr) /* Where to store error code. */
{
- SocketInfo *infoPtr = instanceData;
+ TcpState *statePtr = instanceData;
+ int written;
+ DWORD error;
+ ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);
- if (mode == TCL_MODE_NONBLOCKING) {
- infoPtr->flags |= SOCKET_ASYNC;
- } else {
- infoPtr->flags &= ~(SOCKET_ASYNC);
+ *errorCodePtr = 0;
+
+ /*
+ * 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()) {
+ *errorCodePtr = EFAULT;
+ return -1;
}
- return 0;
+
+ /*
+ * Check if there is an async connect running.
+ * For blocking sockets terminate connect, otherwise do one step.
+ * For a non blocking socket return EWOULDBLOCK if connect not terminated
+ */
+
+ if (WaitForConnect(statePtr, errorCodePtr) != 0) {
+ return -1;
+ }
+
+ while (1) {
+ SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
+ (WPARAM) UNSELECT, (LPARAM) statePtr);
+
+ /* single fd operation: this proc is only called for a connected socket. */
+ written = send(statePtr->sockets->fd, buf, toWrite, 0);
+ if (written != SOCKET_ERROR) {
+ /*
+ * Since Windows won't generate a new write event until we hit an
+ * overflow condition, we need to force the event loop to poll
+ * until the condition changes.
+ */
+
+ if (statePtr->watchEvents & FD_WRITE) {
+ Tcl_Time blockTime = { 0, 0 };
+ Tcl_SetMaxBlockTime(&blockTime);
+ }
+ break;
+ }
+
+ /*
+ * Check for error condition or overflow. In the event of overflow, we
+ * need to clear the FD_WRITE flag so we can detect the next writable
+ * event. Note that Windows only sends a new writable event after a
+ * send fails with WSAEWOULDBLOCK.
+ */
+
+ error = WSAGetLastError();
+ if (error == WSAEWOULDBLOCK) {
+ statePtr->readyEvents &= ~(FD_WRITE);
+ if (statePtr->flags & TCP_NONBLOCKING) {
+ *errorCodePtr = EWOULDBLOCK;
+ written = -1;
+ break;
+ }
+ } else {
+ TclWinConvertError(error);
+ *errorCodePtr = Tcl_GetErrno();
+ written = -1;
+ break;
+ }
+
+ /*
+ * In the blocking case, wait until the file becomes writable or
+ * closed and try again.
+ */
+
+ if (!WaitForSocketEvent(statePtr, FD_WRITE|FD_CLOSE, errorCodePtr)) {
+ written = -1;
+ break;
+ }
+ }
+
+ SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM)SELECT, (LPARAM)statePtr);
+
+ return written;
}
/*
@@ -910,10 +979,10 @@ TcpCloseProc(
ClientData instanceData, /* The socket to close. */
Tcl_Interp *interp) /* Unused. */
{
- SocketInfo *infoPtr = instanceData;
+ TcpState *statePtr = instanceData;
/* TIP #218 */
int errorCode = 0;
- /* ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); */
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
/*
* Check that WinSock is initialized; do not call it if not, to prevent
@@ -928,9 +997,9 @@ TcpCloseProc(
* background.
*/
- while ( infoPtr->sockets != NULL ) {
- TcpFdList *thisfd = infoPtr->sockets;
- infoPtr->sockets = thisfd->next;
+ while ( statePtr->sockets != NULL ) {
+ TcpFdList *thisfd = statePtr->sockets;
+ statePtr->sockets = thisfd->next;
if (closesocket(thisfd->fd) == SOCKET_ERROR) {
TclWinConvertError((DWORD) WSAGetLastError());
@@ -940,6 +1009,30 @@ TcpCloseProc(
}
}
+ if (statePtr->addrlist != NULL) {
+ freeaddrinfo(statePtr->addrlist);
+ }
+ if (statePtr->myaddrlist != NULL) {
+ freeaddrinfo(statePtr->myaddrlist);
+ }
+
+ /*
+ * Clear an eventual tsd info list pointer.
+ * This may be called, if an async socket connect fails or is closed
+ * between connect and thread action callback.
+ */
+ if (tsdPtr->pendingTcpState != NULL
+ && tsdPtr->pendingTcpState == statePtr) {
+
+ /* get infoPtr lock, because this concerns the notifier thread */
+ WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
+
+ tsdPtr->pendingTcpState = NULL;
+
+ /* Free list lock */
+ SetEvent(tsdPtr->socketListLock);
+ }
+
/*
* TIP #218. Removed the code removing the structure from the global
* socket list. This is now done by the thread action callbacks, and only
@@ -947,7 +1040,7 @@ TcpCloseProc(
* fear of damaging the list.
*/
- ckfree(infoPtr);
+ ckfree(statePtr);
return errorCode;
}
@@ -974,14 +1067,15 @@ TcpClose2Proc(
Tcl_Interp *interp, /* For error reporting. */
int flags) /* Flags that indicate which side to close. */
{
- SocketInfo *infoPtr = instanceData;
- int errorCode = 0, sd;
+ TcpState *statePtr = instanceData;
+ int errorCode = 0;
+ int sd;
/*
* Shutdown the OS socket handle.
*/
- switch (flags) {
+ switch(flags) {
case TCL_CLOSE_READ:
sd = SD_RECEIVE;
break;
@@ -991,14 +1085,14 @@ TcpClose2Proc(
default:
if (interp) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "Socket close2proc called bidirectionally", -1));
+ "socket close2proc called bidirectionally", -1));
}
return TCL_ERROR;
}
/* single fd operation: Tcl_OpenTcpServer() does not set TCL_READABLE or
* TCL_WRITABLE so this should never be called for a server socket. */
- if (shutdown(infoPtr->sockets->fd, sd) == SOCKET_ERROR) {
+ if (shutdown(statePtr->sockets->fd, sd) == SOCKET_ERROR) {
TclWinConvertError((DWORD) WSAGetLastError());
errorCode = Tcl_GetErrno();
}
@@ -1009,136 +1103,138 @@ TcpClose2Proc(
/*
*----------------------------------------------------------------------
*
- * AddSocketInfoFd --
+ * TcpSetOptionProc --
*
- * This function adds a SOCKET file descriptor to the 'sockets' linked
- * list of a SocketInfo structure.
+ * Sets Tcp channel specific options.
*
* Results:
- * None.
+ * None, unless an error happens.
*
* Side effects:
- * None, except for allocation of memory.
+ * Changes attributes of the socket at the system level.
*
*----------------------------------------------------------------------
*/
-static void
-AddSocketInfoFd(
- SocketInfo *infoPtr,
- SOCKET socket)
+static int
+TcpSetOptionProc(
+ ClientData instanceData, /* Socket state. */
+ Tcl_Interp *interp, /* For error reporting - can be NULL. */
+ const char *optionName, /* Name of the option to set. */
+ const char *value) /* New value for option. */
{
- TcpFdList *fds = infoPtr->sockets;
+#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
+ TcpState *statePtr = instanceData;
+ SOCKET sock;
+#endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/
- if ( fds == NULL ) {
- /* Add the first FD */
- infoPtr->sockets = ckalloc(sizeof(TcpFdList));
- fds = infoPtr->sockets;
- } else {
- /* Find end of list and append FD */
- while ( fds->next != NULL ) {
- fds = fds->next;
+ /*
+ * 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()) {
+ if (interp) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "winsock is not initialized", -1));
}
-
- fds->next = ckalloc(sizeof(TcpFdList));
- fds = fds->next;
+ return TCL_ERROR;
}
- /* Populate new FD */
- fds->fd = socket;
- fds->infoPtr = infoPtr;
- fds->next = NULL;
-}
-
-
-/*
- *----------------------------------------------------------------------
- *
- * NewSocketInfo --
- *
- * This function allocates and initializes a new SocketInfo structure.
- *
- * Results:
- * Returns a newly allocated SocketInfo.
- *
- * Side effects:
- * None, except for allocation of memory.
- *
- *----------------------------------------------------------------------
- */
-
-static SocketInfo *
-NewSocketInfo(
- SOCKET socket)
-{
- SocketInfo *infoPtr = ckalloc(sizeof(SocketInfo));
-
- /* ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); */
- infoPtr->channel = 0;
- infoPtr->sockets = NULL;
- infoPtr->flags = 0;
- infoPtr->watchEvents = 0;
- infoPtr->readyEvents = 0;
- infoPtr->selectEvents = 0;
- infoPtr->acceptEventCount = 0;
- infoPtr->acceptProc = NULL;
- infoPtr->acceptProcData = NULL;
- infoPtr->lastError = 0;
+#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
+ #error "TCL_FEATURE_KEEPALIVE_NAGLE not reviewed for whether to treat statePtr->sockets as single fd or list"
+ sock = statePtr->sockets->fd;
- /*
- * TIP #218. Removed the code inserting the new structure into the global
- * list. This is now handled in the thread action callbacks, and only
- * there.
- */
+ if (!strcasecmp(optionName, "-keepalive")) {
+ BOOL val = FALSE;
+ int boolVar, rtn;
- infoPtr->nextPtr = NULL;
+ if (Tcl_GetBoolean(interp, value, &boolVar) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (boolVar) {
+ val = TRUE;
+ }
+ rtn = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
+ (const char *) &val, sizeof(BOOL));
+ if (rtn != 0) {
+ TclWinConvertError(WSAGetLastError());
+ if (interp) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "couldn't set socket option: %s",
+ Tcl_PosixError(interp)));
+ }
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+ } else if (!strcasecmp(optionName, "-nagle")) {
+ BOOL val = FALSE;
+ int boolVar, rtn;
- AddSocketInfoFd(infoPtr, socket);
+ if (Tcl_GetBoolean(interp, value, &boolVar) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (!boolVar) {
+ val = TRUE;
+ }
+ rtn = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+ (const char *) &val, sizeof(BOOL));
+ if (rtn != 0) {
+ TclWinConvertError(WSAGetLastError());
+ if (interp) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "couldn't set socket option: %s",
+ Tcl_PosixError(interp)));
+ }
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+ }
- return infoPtr;
+ return Tcl_BadChannelOption(interp, optionName, "keepalive nagle");
+#else
+ return Tcl_BadChannelOption(interp, optionName, "");
+#endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/
}
/*
*----------------------------------------------------------------------
*
- * CreateSocket --
+ * TcpGetOptionProc --
+ *
+ * Computes an option value for a TCP socket based channel, or a list of
+ * all options and their values.
*
- * This function opens a new socket and initializes the SocketInfo
- * structure.
+ * Note: This code is based on code contributed by John Haxby.
*
* Results:
- * Returns a new SocketInfo, or NULL with an error in interp.
+ * A standard Tcl result. The value of the specified option or a list of
+ * all options and their values is returned in the supplied DString. Sets
+ * Error message if needed.
*
* Side effects:
- * None, except for allocation of memory.
+ * None.
*
*----------------------------------------------------------------------
*/
-static SocketInfo *
-CreateSocket(
- Tcl_Interp *interp, /* For error reporting; can be NULL. */
- int port, /* Port number to open. */
- const char *host, /* Name of host on which to open port. */
- int server, /* 1 if socket should be a server socket, else
- * 0 for a client socket. */
- const char *myaddr, /* Optional client-side address */
- int myport, /* Optional client-side port */
- int async) /* If nonzero, connect client socket
- * asynchronously. */
+static int
+TcpGetOptionProc(
+ ClientData instanceData, /* Socket state. */
+ Tcl_Interp *interp, /* For error reporting - can be NULL. */
+ const char *optionName, /* Name of the option to retrieve the value
+ * for, or NULL to get all options and their
+ * values. */
+ Tcl_DString *dsPtr) /* Where to store the computed value;
+ * initialized by caller. */
{
- u_long flag = 1; /* Indicates nonblocking mode. */
- int asyncConnect = 0; /* Will be 1 if async connect is in
- * progress. */
- unsigned short chosenport = 0;
- struct addrinfo *addrlist = NULL, *addrPtr;
- /* Socket address to connect to. */
- struct addrinfo *myaddrlist = NULL, *myaddrPtr;
- /* Socket address for our side. */
- const char *errorMsg = NULL;
- SOCKET sock = INVALID_SOCKET;
- SocketInfo *infoPtr = NULL; /* The returned value. */
- ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);
+ TcpState *statePtr = instanceData;
+ char host[NI_MAXHOST], port[NI_MAXSERV];
+ SOCKET sock;
+ size_t len = 0;
+ int reverseDNS = 0;
+#define SUPPRESS_RDNS_VAR "::tcl::unsupported::noReverseDNS"
/*
* Check that WinSock is initialized; do not call it if not, to prevent
@@ -1147,313 +1243,640 @@ CreateSocket(
*/
if (!SocketsEnabled()) {
- return NULL;
+ if (interp) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "winsock is not initialized", -1));
+ }
+ return TCL_ERROR;
}
/*
- * Construct the addresses for each end of the socket.
+ * Go one step in async connect
+ * If any error is thrown save it as backround error to report eventually below
*/
+ WaitForConnect(statePtr, NULL);
- if (!TclCreateSocketAddress(interp, &addrlist, host, port, server,
- &errorMsg)) {
- goto error;
- }
- if (!TclCreateSocketAddress(interp, &myaddrlist, myaddr, myport, 1,
- &errorMsg)) {
- goto error;
+ sock = statePtr->sockets->fd;
+ if (optionName != NULL) {
+ len = strlen(optionName);
}
- if (server) {
-
- for (addrPtr = addrlist; addrPtr != NULL; addrPtr = addrPtr->ai_next) {
- sock = socket(addrPtr->ai_family, SOCK_STREAM, 0);
- if (sock == INVALID_SOCKET) {
- TclWinConvertError((DWORD) WSAGetLastError());
- continue;
- }
-
- /*
- * Win-NT has a misfeature that sockets are inherited in child
- * processes by default. Turn off the inherit bit.
- */
-
- SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0);
-
- /*
- * Set kernel space buffering
- */
-
- TclSockMinimumBuffers((void *)sock, TCP_BUFFER_SIZE);
-
- /*
- * Make sure we use the same port when opening two server sockets
- * for IPv4 and IPv6.
- *
- * As sockaddr_in6 uses the same offset and size for the port
- * member as sockaddr_in, we can handle both through the IPv4 API.
- */
+ if ((len > 1) && (optionName[1] == 'e') &&
+ (strncmp(optionName, "-error", len) == 0)) {
- if (port == 0 && chosenport != 0) {
- ((struct sockaddr_in *) addrPtr->ai_addr)->sin_port =
- htons(chosenport);
- }
+ /*
+ * Do not return any errors if async connect is running
+ */
+ if ( ! (statePtr->flags & TCP_ASYNC_PENDING) ) {
- /*
- * Bind to the specified port. Note that we must not call
- * setsockopt with SO_REUSEADDR because Microsoft allows addresses
- * to be reused even if they are still in use.
- *
- * Bind should not be affected by the socket having already been
- * set into nonblocking mode. If there is trouble, this is one
- * place to look for bugs.
- */
- if (bind(sock, addrPtr->ai_addr, addrPtr->ai_addrlen)
- == SOCKET_ERROR) {
- TclWinConvertError((DWORD) WSAGetLastError());
- closesocket(sock);
- continue;
- }
- if (port == 0 && chosenport == 0) {
- address sockname;
- socklen_t namelen = sizeof(sockname);
+ if ( statePtr->flags & TCP_ASYNC_FAILED ) {
/*
- * Synchronize port numbers when binding to port 0 of multiple
- * addresses.
+ * In case of a failed async connect, eventually report the
+ * connect error only once.
+ * Do not report the system error, as this comes again and again.
*/
- if (getsockname(sock, &sockname.sa, &namelen) >= 0) {
- chosenport = ntohs(sockname.sa4.sin_port);
+ if ( statePtr->connectError != 0 ) {
+ Tcl_DStringAppend(dsPtr,
+ Tcl_ErrnoMsg(statePtr->connectError), -1);
+ statePtr->connectError = 0;
}
- }
-
- /*
- * Set the maximum number of pending connect requests to the max
- * value allowed on each platform (Win32 and Win32s may be
- * different, and there may be differences between TCP/IP stacks).
- */
- if (listen(sock, SOMAXCONN) == SOCKET_ERROR) {
- TclWinConvertError((DWORD) WSAGetLastError());
- closesocket(sock);
- continue;
- }
+ } else {
- if (infoPtr == NULL) {
/*
- * Add this socket to the global list of sockets.
+ * Report an eventual last error of the socket system
*/
- infoPtr = NewSocketInfo(sock);
+ int optlen;
+ int ret;
+ DWORD err;
/*
- * Set up the select mask for connection request events.
+ * Populater the err Variable with a possix error
*/
-
- infoPtr->selectEvents = FD_ACCEPT;
- infoPtr->watchEvents |= FD_ACCEPT;
-
- } else {
- AddSocketInfoFd( infoPtr, sock );
- }
- }
- } else {
- for (addrPtr = addrlist; addrPtr != NULL;
- addrPtr = addrPtr->ai_next) {
- for (myaddrPtr = myaddrlist; myaddrPtr != NULL;
- myaddrPtr = myaddrPtr->ai_next) {
+ optlen = sizeof(int);
+ ret = getsockopt(sock, SOL_SOCKET, SO_ERROR,
+ (char *)&err, &optlen);
/*
- * No need to try combinations of local and remote addresses
- * of different families.
+ * The error was not returned directly but should be
+ * taken from WSA
*/
-
- if (myaddrPtr->ai_family != addrPtr->ai_family) {
- continue;
+ if (ret == SOCKET_ERROR) {
+ err = WSAGetLastError();
}
-
- sock = socket(myaddrPtr->ai_family, SOCK_STREAM, 0);
- if (sock == INVALID_SOCKET) {
- TclWinConvertError((DWORD) WSAGetLastError());
- continue;
- }
-
/*
- * Win-NT has a misfeature that sockets are inherited in child
- * processes by default. Turn off the inherit bit.
+ * Return error message
*/
+ if (err) {
+ TclWinConvertError(err);
+ Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(Tcl_GetErrno()), -1);
+ }
+ }
+ }
+ return TCL_OK;
+ }
- SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0);
+ if ((len > 1) && (optionName[1] == 'c') &&
+ (strncmp(optionName, "-connecting", len) == 0)) {
- /*
- * Set kernel space buffering
- */
+ Tcl_DStringAppend(dsPtr,
+ (statePtr->flags & TCP_ASYNC_PENDING)
+ ? "1" : "0", -1);
+ return TCL_OK;
+ }
- TclSockMinimumBuffers((void *) sock, TCP_BUFFER_SIZE);
+ if (interp != NULL && Tcl_GetVar(interp, SUPPRESS_RDNS_VAR, 0) != NULL) {
+ reverseDNS = NI_NUMERICHOST;
+ }
- /*
- * Try to bind to a local port.
- */
+ if ((len == 0) || ((len > 1) && (optionName[1] == 'p') &&
+ (strncmp(optionName, "-peername", len) == 0))) {
+ address peername;
+ socklen_t size = sizeof(peername);
- if (bind(sock, myaddrPtr->ai_addr, myaddrPtr->ai_addrlen)
- == SOCKET_ERROR) {
- TclWinConvertError((DWORD) WSAGetLastError());
- goto looperror;
- }
- /*
- * Set the socket into nonblocking mode if the connect should
- * be done in the background.
- */
- if (async && ioctlsocket(sock, (long) FIONBIO, &flag)
- == SOCKET_ERROR) {
- TclWinConvertError((DWORD) WSAGetLastError());
- goto looperror;
+ if ( (statePtr->flags & TCP_ASYNC_PENDING) ) {
+ /*
+ * In async connect output an empty string
+ */
+ if (len == 0) {
+ Tcl_DStringAppendElement(dsPtr, "-peername");
+ Tcl_DStringAppendElement(dsPtr, "");
+ } else {
+ return TCL_OK;
+ }
+ } else if ( getpeername(sock, (LPSOCKADDR) &(peername.sa), &size) == 0) {
+ /*
+ * Peername fetch succeeded - output list
+ */
+ if (len == 0) {
+ Tcl_DStringAppendElement(dsPtr, "-peername");
+ Tcl_DStringStartSublist(dsPtr);
+ }
+
+ getnameinfo(&(peername.sa), size, host, sizeof(host),
+ NULL, 0, NI_NUMERICHOST);
+ Tcl_DStringAppendElement(dsPtr, host);
+ getnameinfo(&(peername.sa), size, host, sizeof(host),
+ port, sizeof(port), reverseDNS | NI_NUMERICSERV);
+ Tcl_DStringAppendElement(dsPtr, host);
+ Tcl_DStringAppendElement(dsPtr, port);
+ if (len == 0) {
+ Tcl_DStringEndSublist(dsPtr);
+ } else {
+ return TCL_OK;
+ }
+ } else {
+ /*
+ * getpeername failed - but if we were asked for all the options
+ * (len==0), don't flag an error at that point because it could be
+ * an fconfigure request on a server socket (such sockets have no
+ * peer). {Copied from unix/tclUnixChan.c}
+ */
+
+ if (len) {
+ TclWinConvertError((DWORD) WSAGetLastError());
+ if (interp) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "can't get peername: %s",
+ Tcl_PosixError(interp)));
}
+ return TCL_ERROR;
+ }
+ }
+ }
- /*
- * Attempt to connect to the remote socket.
- */
+ if ((len == 0) || ((len > 1) && (optionName[1] == 's') &&
+ (strncmp(optionName, "-sockname", len) == 0))) {
+ TcpFdList *fds;
+ address sockname;
+ socklen_t size;
+ int found = 0;
- if (connect(sock, addrPtr->ai_addr, addrPtr->ai_addrlen)
- == SOCKET_ERROR) {
- TclWinConvertError((DWORD) WSAGetLastError());
- if (Tcl_GetErrno() != EAGAIN) {
- goto looperror;
- }
+ if (len == 0) {
+ Tcl_DStringAppendElement(dsPtr, "-sockname");
+ Tcl_DStringStartSublist(dsPtr);
+ }
+ if ( (statePtr->flags & TCP_ASYNC_PENDING ) ) {
+ /*
+ * In async connect output an empty string
+ */
+ found = 1;
+ } else {
+ for (fds = statePtr->sockets; fds != NULL; fds = fds->next) {
+ sock = fds->fd;
+ size = sizeof(sockname);
+ if (getsockname(sock, &(sockname.sa), &size) >= 0) {
+ int flags = reverseDNS;
+
+ found = 1;
+ getnameinfo(&sockname.sa, size, host, sizeof(host),
+ NULL, 0, NI_NUMERICHOST);
+ Tcl_DStringAppendElement(dsPtr, host);
/*
- * The connection is progressing in the background.
+ * We don't want to resolve INADDR_ANY and sin6addr_any; they
+ * can sometimes cause problems (and never have a name).
*/
-
- asyncConnect = 1;
- }
- goto connected;
-
- looperror:
- if (sock != INVALID_SOCKET) {
- closesocket(sock);
- sock = INVALID_SOCKET;
+ flags |= NI_NUMERICSERV;
+ if (sockname.sa.sa_family == AF_INET) {
+ if (sockname.sa4.sin_addr.s_addr == INADDR_ANY) {
+ flags |= NI_NUMERICHOST;
+ }
+ } else if (sockname.sa.sa_family == AF_INET6) {
+ if ((IN6_ARE_ADDR_EQUAL(&sockname.sa6.sin6_addr,
+ &in6addr_any)) ||
+ (IN6_IS_ADDR_V4MAPPED(&sockname.sa6.sin6_addr)
+ && sockname.sa6.sin6_addr.s6_addr[12] == 0
+ && sockname.sa6.sin6_addr.s6_addr[13] == 0
+ && sockname.sa6.sin6_addr.s6_addr[14] == 0
+ && sockname.sa6.sin6_addr.s6_addr[15] == 0)) {
+ flags |= NI_NUMERICHOST;
+ }
+ }
+ getnameinfo(&sockname.sa, size, host, sizeof(host),
+ port, sizeof(port), flags);
+ Tcl_DStringAppendElement(dsPtr, host);
+ Tcl_DStringAppendElement(dsPtr, port);
}
}
}
- goto error;
+ if (found) {
+ if (len) {
+ return TCL_OK;
+ }
+ Tcl_DStringEndSublist(dsPtr);
+ } else {
+ if (interp) {
+ TclWinConvertError((DWORD) WSAGetLastError());
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "can't get sockname: %s", Tcl_PosixError(interp)));
+ }
+ return TCL_ERROR;
+ }
+ }
- connected:
- /*
- * Add this socket to the global list of sockets.
- */
+#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
+ if (len == 0 || !strncmp(optionName, "-keepalive", len)) {
+ int optlen;
+ BOOL opt = FALSE;
- infoPtr = NewSocketInfo(sock);
+ if (len == 0) {
+ Tcl_DStringAppendElement(dsPtr, "-keepalive");
+ }
+ optlen = sizeof(BOOL);
+ getsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&opt, &optlen);
+ if (opt) {
+ Tcl_DStringAppendElement(dsPtr, "1");
+ } else {
+ Tcl_DStringAppendElement(dsPtr, "0");
+ }
+ if (len > 0) {
+ return TCL_OK;
+ }
+ }
- /*
- * Set up the select mask for read/write events. If the connect
- * attempt has not completed, include connect events.
- */
+ if (len == 0 || !strncmp(optionName, "-nagle", len)) {
+ int optlen;
+ BOOL opt = FALSE;
- infoPtr->selectEvents = FD_READ | FD_WRITE | FD_CLOSE;
- if (asyncConnect) {
- infoPtr->flags |= SOCKET_ASYNC_CONNECT;
- infoPtr->selectEvents |= FD_CONNECT;
+ if (len == 0) {
+ Tcl_DStringAppendElement(dsPtr, "-nagle");
+ }
+ optlen = sizeof(BOOL);
+ getsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&opt, &optlen);
+ if (opt) {
+ Tcl_DStringAppendElement(dsPtr, "0");
+ } else {
+ Tcl_DStringAppendElement(dsPtr, "1");
+ }
+ if (len > 0) {
+ return TCL_OK;
}
}
+#endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/
- error:
- if (addrlist == NULL) {
- freeaddrinfo(addrlist);
- }
- if (myaddrlist == NULL) {
- freeaddrinfo(myaddrlist);
+ if (len > 0) {
+#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
+ return Tcl_BadChannelOption(interp, optionName,
+ "connecting peername sockname keepalive nagle");
+#else
+ return Tcl_BadChannelOption(interp, optionName, "connecting peername sockname");
+#endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/
}
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TcpWatchProc --
+ *
+ * Informs the channel driver of the events that the generic channel code
+ * wishes to receive on this socket.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * May cause the notifier to poll if any of the specified conditions are
+ * already true.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+TcpWatchProc(
+ ClientData instanceData, /* The socket state. */
+ int mask) /* Events of interest; an OR-ed combination of
+ * TCL_READABLE, TCL_WRITABLE and
+ * TCL_EXCEPTION. */
+{
+ TcpState *statePtr = instanceData;
+
/*
- * Register for interest in events in the select mask. Note that this
- * automatically places the socket into non-blocking mode.
+ * Update the watch events mask. Only if the socket is not a server
+ * socket. [Bug 557878]
*/
- if (infoPtr != NULL) {
- ioctlsocket(sock, (long) FIONBIO, &flag);
- SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT,
- (LPARAM) infoPtr);
+ if (!statePtr->acceptProc) {
+ statePtr->watchEvents = 0;
+ if (mask & TCL_READABLE) {
+ statePtr->watchEvents |= (FD_READ|FD_CLOSE);
+ }
+ if (mask & TCL_WRITABLE) {
+ statePtr->watchEvents |= (FD_WRITE|FD_CLOSE);
+ }
- return infoPtr;
- }
+ /*
+ * If there are any conditions already set, then tell the notifier to
+ * poll rather than block.
+ */
- if (interp != NULL) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "couldn't open socket: %s",
- (errorMsg ? errorMsg : Tcl_PosixError(interp))));
- }
+ if (statePtr->readyEvents & statePtr->watchEvents) {
+ Tcl_Time blockTime = { 0, 0 };
- if (sock != INVALID_SOCKET) {
- closesocket(sock);
+ Tcl_SetMaxBlockTime(&blockTime);
+ }
}
- return NULL;
}
/*
*----------------------------------------------------------------------
*
- * WaitForSocketEvent --
+ * TcpGetHandleProc --
*
- * Waits until one of the specified events occurs on a socket.
+ * Called from Tcl_GetChannelHandle to retrieve OS handles from inside a
+ * TCP socket based channel.
*
* Results:
- * Returns 1 on success or 0 on failure, with an error code in
- * errorCodePtr.
+ * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if there is no
+ * handle for the specified direction.
*
* Side effects:
- * Processes socket events off the system queue.
+ * None.
*
*----------------------------------------------------------------------
*/
+ /* ARGSUSED */
static int
-WaitForSocketEvent(
- SocketInfo *infoPtr, /* Information about this socket. */
- int events, /* Events to look for. */
- int *errorCodePtr) /* Where to store errors? */
+TcpGetHandleProc(
+ ClientData instanceData, /* The socket state. */
+ int direction, /* Not used. */
+ ClientData *handlePtr) /* Where to store the handle. */
{
- int result = 1;
- int oldMode;
- ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);
+ TcpState *statePtr = instanceData;
+ *handlePtr = INT2PTR(statePtr->sockets->fd);
+ return TCL_OK;
+}
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TcpConnect --
+ *
+ * This function opens a new socket in client mode.
+ *
+ * This might be called in 3 circumstances:
+ * - By a regular socket command
+ * - By the event handler to continue an asynchroneous connect
+ * - By a blocking socket function (gets/puts) to terminate the
+ * connect synchroneously
+ *
+ * Results:
+ * TCL_OK, if the socket was successfully connected or an asynchronous
+ * connection is in progress. If an error occurs, TCL_ERROR is returned
+ * and an error message is left in interp.
+ *
+ * Side effects:
+ * Opens a socket.
+ *
+ * Remarks:
+ * A single host name may resolve to more than one IP address, e.g. for
+ * an IPv4/IPv6 dual stack host. For handling asyncronously connecting
+ * sockets in the background for such hosts, this function can act as a
+ * coroutine. On the first call, it sets up the control variables for the
+ * two nested loops over the local and remote addresses. Once the first
+ * connection attempt is in progress, it sets up itself as a writable
+ * event handler for that socket, and returns. When the callback occurs,
+ * control is transferred to the "reenter" label, right after the initial
+ * return and the loops resume as if they had never been interrupted.
+ * For syncronously connecting sockets, the loops work the usual way.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TcpConnect(
+ Tcl_Interp *interp, /* For error reporting; can be NULL. */
+ TcpState *statePtr)
+{
+ DWORD error;
/*
- * Be sure to disable event servicing so we are truly modal.
+ * We are started with async connect and the connect notification
+ * was not jet received
*/
+ int async_connect = statePtr->flags & TCP_ASYNC_CONNECT;
+ /* We were called by the event procedure and continue our loop */
+ int async_callback = statePtr->flags & TCP_ASYNC_PENDING;
+ ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);
- oldMode = Tcl_SetServiceMode(TCL_SERVICE_NONE);
+ if (async_callback) {
+ goto reenter;
+ }
+
+ for (statePtr->addr = statePtr->addrlist; statePtr->addr != NULL;
+ statePtr->addr = statePtr->addr->ai_next) {
+
+ for (statePtr->myaddr = statePtr->myaddrlist; statePtr->myaddr != NULL;
+ statePtr->myaddr = statePtr->myaddr->ai_next) {
+
+ /*
+ * No need to try combinations of local and remote addresses
+ * of different families.
+ */
+
+ if (statePtr->myaddr->ai_family != statePtr->addr->ai_family) {
+ continue;
+ }
+
+ /*
+ * Close the socket if it is still open from the last unsuccessful
+ * iteration.
+ */
+ if (statePtr->sockets->fd != INVALID_SOCKET) {
+ closesocket(statePtr->sockets->fd);
+ }
+
+ /* get statePtr lock */
+ WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
+
+ /*
+ * Reset last error from last try
+ */
+ statePtr->notifierConnectError = 0;
+ Tcl_SetErrno(0);
+
+ statePtr->sockets->fd = socket(statePtr->myaddr->ai_family, SOCK_STREAM, 0);
+
+ /* Free list lock */
+ SetEvent(tsdPtr->socketListLock);
+
+ /* continue on socket creation error */
+ if (statePtr->sockets->fd == INVALID_SOCKET) {
+ TclWinConvertError((DWORD) WSAGetLastError());
+ continue;
+ }
+
+ /*
+ * Win-NT has a misfeature that sockets are inherited in child
+ * processes by default. Turn off the inherit bit.
+ */
+
+ SetHandleInformation((HANDLE) statePtr->sockets->fd, HANDLE_FLAG_INHERIT, 0);
+
+ /*
+ * Set kernel space buffering
+ */
+
+ TclSockMinimumBuffers((void *) statePtr->sockets->fd, TCP_BUFFER_SIZE);
+
+ /*
+ * Try to bind to a local port.
+ */
+
+ if (bind(statePtr->sockets->fd, statePtr->myaddr->ai_addr,
+ statePtr->myaddr->ai_addrlen) == SOCKET_ERROR) {
+ TclWinConvertError((DWORD) WSAGetLastError());
+ continue;
+ }
+ /*
+ * For asyncroneous connect set the socket in nonblocking mode
+ * and activate connect notification
+ */
+ if (async_connect) {
+ TcpState *statePtr2;
+ int in_socket_list = 0;
+ /* get statePtr lock */
+ WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
+
+ /*
+ * Bugfig for 336441ed59 to not ignore notifications until the
+ * infoPtr is in the list.
+ * Check if my statePtr is already in the tsdPtr->socketList
+ * It is set after this call by TcpThreadActionProc and is set
+ * on a second round.
+ *
+ * If not, we buffer my statePtr in the tsd memory so it is not
+ * lost by the event procedure
+ */
+
+ for (statePtr2 = tsdPtr->socketList; statePtr2 != NULL;
+ statePtr2 = statePtr2->nextPtr) {
+ if (statePtr2 == statePtr) {
+ in_socket_list = 1;
+ break;
+ }
+ }
+ if (!in_socket_list) {
+ tsdPtr->pendingTcpState = statePtr;
+ }
+ /*
+ * Set connect mask to connect events
+ * This is activated by a SOCKET_SELECT message to the notifier
+ * thread.
+ */
+ statePtr->selectEvents |= FD_CONNECT;
+
+ /*
+ * Free list lock
+ */
+ SetEvent(tsdPtr->socketListLock);
+
+ /* activate accept notification */
+ SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT,
+ (LPARAM) statePtr);
+ }
+
+ /*
+ * Attempt to connect to the remote socket.
+ */
+
+ connect(statePtr->sockets->fd, statePtr->addr->ai_addr,
+ statePtr->addr->ai_addrlen);
+
+ error = WSAGetLastError();
+ TclWinConvertError(error);
+
+ if (async_connect && error == WSAEWOULDBLOCK) {
+ /*
+ * Asynchroneous connect
+ */
+
+ /*
+ * Remember that we jump back behind this next round
+ */
+ statePtr->flags |= TCP_ASYNC_PENDING;
+ return TCL_OK;
+
+ reenter:
+ /*
+ * Re-entry point for async connect after connect event or
+ * blocking operation
+ *
+ * Clear the reenter flag
+ */
+ statePtr->flags &= ~(TCP_ASYNC_PENDING);
+ /* get statePtr lock */
+ WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
+ /* Get signaled connect error */
+ TclWinConvertError((DWORD) statePtr->notifierConnectError);
+ /* Clear eventual connect flag */
+ statePtr->selectEvents &= ~(FD_CONNECT);
+ /* Free list lock */
+ SetEvent(tsdPtr->socketListLock);
+ }
+
+ /*
+ * Clear the tsd socket list pointer if we did not wait for
+ * the FD_CONNECT asyncroneously
+ */
+ tsdPtr->pendingTcpState = NULL;
+ if (Tcl_GetErrno() == 0) {
+ goto out;
+ }
+ }
+ }
+
+out:
/*
- * Reset WSAAsyncSelect so we have a fresh set of events pending.
+ * Socket connected or connection failed
*/
- SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) UNSELECT,
- (LPARAM) infoPtr);
- SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT,
- (LPARAM) infoPtr);
+ /*
+ * Async connect terminated
+ */
- while (1) {
- if (infoPtr->lastError) {
- *errorCodePtr = infoPtr->lastError;
- result = 0;
- break;
- } else if (infoPtr->readyEvents & events) {
- break;
- } else if (infoPtr->flags & SOCKET_ASYNC) {
- *errorCodePtr = EAGAIN;
- result = 0;
- break;
- }
+ CLEAR_BITS(statePtr->flags, TCP_ASYNC_CONNECT);
+ if ( Tcl_GetErrno() == 0 ) {
/*
- * Wait until something happens.
+ * Succesfully connected
*/
+ /*
+ * Set up the select mask for read/write events.
+ */
+ statePtr->selectEvents = FD_READ | FD_WRITE | FD_CLOSE;
- WaitForSingleObject(tsdPtr->readyEvent, INFINITE);
- }
+ /*
+ * Register for interest in events in the select mask. Note that this
+ * automatically places the socket into non-blocking mode.
+ */
- (void) Tcl_SetServiceMode(oldMode);
- return result;
+ SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT,
+ (LPARAM) statePtr);
+ } else {
+ /*
+ * Connect failed
+ */
+
+ /*
+ * For async connect schedule a writable event to report the fail.
+ */
+ if (async_callback) {
+ /*
+ * Set up the select mask for read/write events.
+ */
+ statePtr->selectEvents = FD_WRITE|FD_READ;
+ /* get statePtr lock */
+ WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
+ /* Signal ready readable and writable events */
+ statePtr->readyEvents |= FD_WRITE | FD_READ;
+ /* Flag error to event routine */
+ statePtr->flags |= TCP_ASYNC_FAILED;
+ /* Save connect error to be reported by 'fconfigure -error' */
+ statePtr->connectError = Tcl_GetErrno();
+ /* Free list lock */
+ SetEvent(tsdPtr->socketListLock);
+ }
+ /*
+ * Error message on syncroneous connect
+ */
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "couldn't open socket: %s", Tcl_PosixError(interp)));
+ }
+ return TCL_ERROR;
+ }
+ return TCL_OK;
}
/*
@@ -1480,39 +1903,75 @@ Tcl_OpenTcpClient(
const char *host, /* Host on which to open port. */
const char *myaddr, /* Client-side address */
int myport, /* Client-side port */
- int async) /* If nonzero, should connect client socket
- * asynchronously. */
+ int async) /* If nonzero, attempt to do an asynchronous
+ * connect. Otherwise we do a blocking
+ * connect. */
{
- SocketInfo *infoPtr;
- char channelName[16 + TCL_INTEGER_SPACE];
+ TcpState *statePtr;
+ const char *errorMsg = NULL;
+ struct addrinfo *addrlist = NULL, *myaddrlist = NULL;
+ char channelName[SOCK_CHAN_LENGTH];
if (TclpHasSockets(interp) != TCL_OK) {
return NULL;
}
/*
- * Create a new client socket and wrap it in a channel.
+ * 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.
*/
- infoPtr = CreateSocket(interp, port, host, 0, myaddr, myport, async);
- if (infoPtr == NULL) {
+ if (!SocketsEnabled()) {
return NULL;
}
- sprintf(channelName, "sock%" TCL_I_MODIFIER "u", (size_t) infoPtr->sockets->fd);
+ /*
+ * Do the name lookups for the local and remote addresses.
+ */
- infoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
- infoPtr, (TCL_READABLE | TCL_WRITABLE));
- if (TCL_ERROR == Tcl_SetChannelOption(NULL, infoPtr->channel,
+ if (!TclCreateSocketAddress(interp, &addrlist, host, port, 0, &errorMsg)
+ || !TclCreateSocketAddress(interp, &myaddrlist, myaddr, myport, 1,
+ &errorMsg)) {
+ if (addrlist != NULL) {
+ freeaddrinfo(addrlist);
+ }
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "couldn't open socket: %s", errorMsg));
+ }
+ return NULL;
+ }
+
+ statePtr = NewSocketInfo(INVALID_SOCKET);
+ statePtr->addrlist = addrlist;
+ statePtr->myaddrlist = myaddrlist;
+ if (async) {
+ statePtr->flags |= TCP_ASYNC_CONNECT;
+ }
+
+ /*
+ * Create a new client socket and wrap it in a channel.
+ */
+ if (TcpConnect(interp, statePtr) != TCL_OK) {
+ TcpCloseProc(statePtr, NULL);
+ return NULL;
+ }
+
+ sprintf(channelName, SOCK_TEMPLATE, statePtr);
+
+ statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
+ statePtr, (TCL_READABLE | TCL_WRITABLE));
+ if (TCL_ERROR == Tcl_SetChannelOption(NULL, statePtr->channel,
"-translation", "auto crlf")) {
- Tcl_Close(NULL, infoPtr->channel);
+ Tcl_Close(NULL, statePtr->channel);
return NULL;
- } else if (TCL_ERROR == Tcl_SetChannelOption(NULL, infoPtr->channel,
+ } else if (TCL_ERROR == Tcl_SetChannelOption(NULL, statePtr->channel,
"-eofchar", "")) {
- Tcl_Close(NULL, infoPtr->channel);
+ Tcl_Close(NULL, statePtr->channel);
return NULL;
}
- return infoPtr->channel;
+ return statePtr->channel;
}
/*
@@ -1528,8 +1987,6 @@ Tcl_OpenTcpClient(
* Side effects:
* None.
*
- * NOTE: Code contributed by Mark Diekhans (markd@grizzly.com)
- *
*----------------------------------------------------------------------
*/
@@ -1537,8 +1994,8 @@ Tcl_Channel
Tcl_MakeTcpClientChannel(
ClientData sock) /* The socket to wrap up into a channel. */
{
- SocketInfo *infoPtr;
- char channelName[16 + TCL_INTEGER_SPACE];
+ TcpState *statePtr;
+ char channelName[SOCK_CHAN_LENGTH];
ThreadSpecificData *tsdPtr;
if (TclpHasSockets(NULL) != TCL_OK) {
@@ -1553,20 +2010,20 @@ Tcl_MakeTcpClientChannel(
TclSockMinimumBuffers(sock, TCP_BUFFER_SIZE);
- infoPtr = NewSocketInfo((SOCKET) sock);
+ statePtr = NewSocketInfo((SOCKET) sock);
/*
* Start watching for read/write events on the socket.
*/
- infoPtr->selectEvents = FD_READ | FD_CLOSE | FD_WRITE;
- SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM)SELECT, (LPARAM)infoPtr);
+ statePtr->selectEvents = FD_READ | FD_CLOSE | FD_WRITE;
+ SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM)SELECT, (LPARAM)statePtr);
- sprintf(channelName, "sock%" TCL_I_MODIFIER "u", (size_t) infoPtr->sockets->fd);
- infoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
- infoPtr, (TCL_READABLE | TCL_WRITABLE));
- Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto crlf");
- return infoPtr->channel;
+ sprintf(channelName, SOCK_TEMPLATE, statePtr);
+ statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
+ statePtr, (TCL_READABLE | TCL_WRITABLE));
+ Tcl_SetChannelOption(NULL, statePtr->channel, "-translation", "auto crlf");
+ return statePtr->channel;
}
/*
@@ -1577,8 +2034,8 @@ Tcl_MakeTcpClientChannel(
* Opens a TCP server socket and creates a channel around it.
*
* Results:
- * The channel or NULL if failed. An error message is returned in the
- * interpreter on failure.
+ * The channel or NULL if failed. If an error occurred, an error message
+ * is left in the interp's result if interp is not NULL.
*
* Side effects:
* Opens a server socket and creates a new channel.
@@ -1590,72 +2047,203 @@ Tcl_Channel
Tcl_OpenTcpServer(
Tcl_Interp *interp, /* For error reporting - may be NULL. */
int port, /* Port number to open. */
- const char *host, /* Name of local host. */
+ const char *myHost, /* Name of local host. */
Tcl_TcpAcceptProc *acceptProc,
/* Callback for accepting connections from new
* clients. */
ClientData acceptProcData) /* Data for the callback. */
{
- SocketInfo *infoPtr;
- char channelName[16 + TCL_INTEGER_SPACE];
+ SOCKET sock = INVALID_SOCKET;
+ unsigned short chosenport = 0;
+ struct addrinfo *addrlist = NULL;
+ struct addrinfo *addrPtr; /* Socket address to listen on. */
+ TcpState *statePtr = NULL; /* The returned value. */
+ char channelName[SOCK_CHAN_LENGTH];
+ u_long flag = 1; /* Indicates nonblocking mode. */
+ const char *errorMsg = NULL;
+ ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);
if (TclpHasSockets(interp) != TCL_OK) {
return NULL;
}
/*
- * Create a new client socket and wrap it in a channel.
+ * 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.
*/
- infoPtr = CreateSocket(interp, port, host, 1, NULL, 0, 0);
- if (infoPtr == NULL) {
+ if (!SocketsEnabled()) {
return NULL;
}
- infoPtr->acceptProc = acceptProc;
- infoPtr->acceptProcData = acceptProcData;
+ /*
+ * Construct the addresses for each end of the socket.
+ */
+
+ if (!TclCreateSocketAddress(interp, &addrlist, myHost, port, 1, &errorMsg)) {
+ goto error;
+ }
+
+ for (addrPtr = addrlist; addrPtr != NULL; addrPtr = addrPtr->ai_next) {
+ sock = socket(addrPtr->ai_family, addrPtr->ai_socktype,
+ addrPtr->ai_protocol);
+ if (sock == INVALID_SOCKET) {
+ TclWinConvertError((DWORD) WSAGetLastError());
+ continue;
+ }
+
+ /*
+ * Win-NT has a misfeature that sockets are inherited in child
+ * processes by default. Turn off the inherit bit.
+ */
- sprintf(channelName, "sock%" TCL_I_MODIFIER "u", (size_t) infoPtr->sockets->fd);
+ SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0);
- infoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
- infoPtr, 0);
- if (Tcl_SetChannelOption(interp, infoPtr->channel, "-eofchar", "")
+ /*
+ * Set kernel space buffering
+ */
+
+ TclSockMinimumBuffers((void *)sock, TCP_BUFFER_SIZE);
+
+ /*
+ * Make sure we use the same port when opening two server sockets
+ * for IPv4 and IPv6.
+ *
+ * As sockaddr_in6 uses the same offset and size for the port
+ * member as sockaddr_in, we can handle both through the IPv4 API.
+ */
+
+ if (port == 0 && chosenport != 0) {
+ ((struct sockaddr_in *) addrPtr->ai_addr)->sin_port =
+ htons(chosenport);
+ }
+
+ /*
+ * Bind to the specified port. Note that we must not call
+ * setsockopt with SO_REUSEADDR because Microsoft allows addresses
+ * to be reused even if they are still in use.
+ *
+ * Bind should not be affected by the socket having already been
+ * set into nonblocking mode. If there is trouble, this is one
+ * place to look for bugs.
+ */
+
+ if (bind(sock, addrPtr->ai_addr, addrPtr->ai_addrlen)
+ == SOCKET_ERROR) {
+ TclWinConvertError((DWORD) WSAGetLastError());
+ closesocket(sock);
+ continue;
+ }
+ if (port == 0 && chosenport == 0) {
+ address sockname;
+ socklen_t namelen = sizeof(sockname);
+
+ /*
+ * Synchronize port numbers when binding to port 0 of multiple
+ * addresses.
+ */
+
+ if (getsockname(sock, &sockname.sa, &namelen) >= 0) {
+ chosenport = ntohs(sockname.sa4.sin_port);
+ }
+ }
+
+ /*
+ * Set the maximum number of pending connect requests to the max
+ * value allowed on each platform (Win32 and Win32s may be
+ * different, and there may be differences between TCP/IP stacks).
+ */
+
+ if (listen(sock, SOMAXCONN) == SOCKET_ERROR) {
+ TclWinConvertError((DWORD) WSAGetLastError());
+ closesocket(sock);
+ continue;
+ }
+
+ if (statePtr == NULL) {
+ /*
+ * Add this socket to the global list of sockets.
+ */
+ statePtr = NewSocketInfo(sock);
+ } else {
+ AddSocketInfoFd( statePtr, sock );
+ }
+ }
+
+error:
+ if (addrlist != NULL) {
+ freeaddrinfo(addrlist);
+ }
+
+ if (statePtr != NULL) {
+
+ statePtr->acceptProc = acceptProc;
+ statePtr->acceptProcData = acceptProcData;
+ sprintf(channelName, SOCK_TEMPLATE, statePtr);
+ statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
+ statePtr, 0);
+ /*
+ * Set up the select mask for connection request events.
+ */
+
+ statePtr->selectEvents = FD_ACCEPT;
+
+ /*
+ * Register for interest in events in the select mask. Note that this
+ * automatically places the socket into non-blocking mode.
+ */
+
+ ioctlsocket(sock, (long) FIONBIO, &flag);
+ SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT,
+ (LPARAM) statePtr);
+ if (Tcl_SetChannelOption(interp, statePtr->channel, "-eofchar", "")
== TCL_ERROR) {
- Tcl_Close(NULL, infoPtr->channel);
- return NULL;
+ Tcl_Close(NULL, statePtr->channel);
+ return NULL;
+ }
+ return statePtr->channel;
+ }
+
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "couldn't open socket: %s",
+ (errorMsg ? errorMsg : Tcl_PosixError(interp))));
}
- return infoPtr->channel;
+ if (sock != INVALID_SOCKET) {
+ closesocket(sock);
+ }
+ return NULL;
}
/*
*----------------------------------------------------------------------
*
* TcpAccept --
- *
- * Creates a channel for a newly accepted socket connection. This is
- * called by SocketEventProc and it in turns calls the registered
- * accept function.
+ * Accept a TCP socket connection. This is called by the event loop.
*
* Results:
* None.
*
* Side effects:
- * Invokes the accept proc which may invoke arbitrary Tcl code.
+ * Creates a new connection socket. Calls the registered callback for the
+ * connection acceptance mechanism.
*
*----------------------------------------------------------------------
*/
+ /* ARGSUSED */
static void
TcpAccept(
TcpFdList *fds, /* Server socket that accepted newSocket. */
SOCKET newSocket, /* Newly accepted socket. */
address addr) /* Address of new socket. */
{
- SocketInfo *newInfoPtr;
- SocketInfo *infoPtr = fds->infoPtr;
+ TcpState *newInfoPtr;
+ TcpState *statePtr = fds->statePtr;
int len = sizeof(addr);
- char channelName[16 + TCL_INTEGER_SPACE];
+ char channelName[SOCK_CHAN_LENGTH];
char host[NI_MAXHOST], port[NI_MAXSERV];
ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);
@@ -1680,7 +2268,7 @@ TcpAccept(
SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT,
(LPARAM) newInfoPtr);
- sprintf(channelName, "sock%" TCL_I_MODIFIER "u", (size_t) newInfoPtr->sockets->fd);
+ sprintf(channelName, SOCK_TEMPLATE, newInfoPtr);
newInfoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
newInfoPtr, (TCL_READABLE | TCL_WRITABLE));
if (Tcl_SetChannelOption(NULL, newInfoPtr->channel, "-translation",
@@ -1698,10 +2286,10 @@ TcpAccept(
* Invoke the accept callback function.
*/
- if (infoPtr->acceptProc != NULL) {
+ if (statePtr->acceptProc != NULL) {
getnameinfo(&(addr.sa), len, host, sizeof(host), port, sizeof(port),
NI_NUMERICHOST|NI_NUMERICSERV);
- infoPtr->acceptProc(infoPtr->acceptProcData, newInfoPtr->channel,
+ statePtr->acceptProc(statePtr->acceptProcData, newInfoPtr->channel,
host, atoi(port));
}
}
@@ -1709,666 +2297,672 @@ TcpAccept(
/*
*----------------------------------------------------------------------
*
- * TcpInputProc --
+ * InitSockets --
*
- * This function is called by the generic IO level to read data from a
- * socket based channel.
+ * Registers the event window for the socket notifier code.
+ *
+ * Assumes socketMutex is held.
*
* Results:
- * The number of bytes read or -1 on error.
+ * None.
*
* Side effects:
- * Consumes input from the socket.
+ * Register a new window class and creates a
+ * window for use in asynchronous socket notification.
*
*----------------------------------------------------------------------
*/
-static int
-TcpInputProc(
- ClientData instanceData, /* The socket state. */
- char *buf, /* Where to store data. */
- int toRead, /* Maximum number of bytes to read. */
- int *errorCodePtr) /* Where to store error codes. */
+static void
+InitSockets(void)
{
- SocketInfo *infoPtr = instanceData;
- int bytesRead;
- DWORD error;
+ DWORD id;
ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);
- *errorCodePtr = 0;
+ if (!initialized) {
+ initialized = 1;
+ TclCreateLateExitHandler(SocketExitHandler, NULL);
- /*
- * 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.
- */
+ /*
+ * Create the async notification window with a new class. We must
+ * create a new class to avoid a Windows 95 bug that causes us to get
+ * the wrong message number for socket events if the message window is
+ * a subclass of a static control.
+ */
- if (!SocketsEnabled()) {
- *errorCodePtr = EFAULT;
- return -1;
+ windowClass.style = 0;
+ windowClass.cbClsExtra = 0;
+ windowClass.cbWndExtra = 0;
+ windowClass.hInstance = TclWinGetTclInstance();
+ windowClass.hbrBackground = NULL;
+ windowClass.lpszMenuName = NULL;
+ windowClass.lpszClassName = classname;
+ windowClass.lpfnWndProc = SocketProc;
+ windowClass.hIcon = NULL;
+ windowClass.hCursor = NULL;
+
+ if (!RegisterClass(&windowClass)) {
+ TclWinConvertError(GetLastError());
+ goto initFailure;
+ }
}
/*
- * First check to see if EOF was already detected, to prevent calling the
- * socket stack after the first time EOF is detected.
+ * Check for per-thread initialization.
*/
- if (infoPtr->flags & SOCKET_EOF) {
- return 0;
+ if (tsdPtr != NULL) {
+ return;
}
/*
- * Check to see if the socket is connected before trying to read.
+ * OK, this thread has never done anything with sockets before. Construct
+ * a worker thread to handle asynchronous events related to sockets
+ * assigned to _this_ thread.
*/
- if ((infoPtr->flags & SOCKET_ASYNC_CONNECT)
- && !WaitForSocketEvent(infoPtr, FD_CONNECT, errorCodePtr)) {
- return -1;
+ tsdPtr = TCL_TSD_INIT(&dataKey);
+ tsdPtr->pendingTcpState = NULL;
+ tsdPtr->socketList = NULL;
+ tsdPtr->hwnd = NULL;
+ tsdPtr->threadId = Tcl_GetCurrentThread();
+ tsdPtr->readyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (tsdPtr->readyEvent == NULL) {
+ goto initFailure;
+ }
+ tsdPtr->socketListLock = CreateEvent(NULL, FALSE, TRUE, NULL);
+ if (tsdPtr->socketListLock == NULL) {
+ goto initFailure;
+ }
+ tsdPtr->socketThread = CreateThread(NULL, 256, SocketThread, tsdPtr, 0,
+ &id);
+ if (tsdPtr->socketThread == NULL) {
+ goto initFailure;
}
+ SetThreadPriority(tsdPtr->socketThread, THREAD_PRIORITY_HIGHEST);
+
/*
- * No EOF, and it is connected, so try to read more from the socket. Note
- * that we clear the FD_READ bit because read events are level triggered
- * so a new event will be generated if there is still data available to be
- * read. We have to simulate blocking behavior here since we are always
- * using non-blocking sockets.
+ * Wait for the thread to signal when the window has been created and if
+ * it is ready to go.
*/
- while (1) {
- SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
- (WPARAM) UNSELECT, (LPARAM) infoPtr);
- /* single fd operation: this proc is only called for a connected socket. */
- bytesRead = recv(infoPtr->sockets->fd, buf, toRead, 0);
- infoPtr->readyEvents &= ~(FD_READ);
-
- /*
- * Check for end-of-file condition or successful read.
- */
-
- if (bytesRead == 0) {
- infoPtr->flags |= SOCKET_EOF;
- }
- if (bytesRead != SOCKET_ERROR) {
- break;
- }
-
- /*
- * If an error occurs after the FD_CLOSE has arrived, then ignore the
- * error and report an EOF.
- */
-
- if (infoPtr->readyEvents & FD_CLOSE) {
- infoPtr->flags |= SOCKET_EOF;
- bytesRead = 0;
- break;
- }
-
- error = WSAGetLastError();
-
- /*
- * If an RST comes, then ignore the error and report an EOF just like
- * on unix.
- */
-
- if (error == WSAECONNRESET) {
- infoPtr->flags |= SOCKET_EOF;
- bytesRead = 0;
- break;
- }
-
- /*
- * Check for error condition or underflow in non-blocking case.
- */
-
- if ((infoPtr->flags & SOCKET_ASYNC) || (error != WSAEWOULDBLOCK)) {
- TclWinConvertError(error);
- *errorCodePtr = Tcl_GetErrno();
- bytesRead = -1;
- break;
- }
-
- /*
- * In the blocking case, wait until the file becomes readable or
- * closed and try again.
- */
+ WaitForSingleObject(tsdPtr->readyEvent, INFINITE);
- if (!WaitForSocketEvent(infoPtr, FD_READ|FD_CLOSE, errorCodePtr)) {
- bytesRead = -1;
- break;
- }
+ if (tsdPtr->hwnd == NULL) {
+ goto initFailure; /* Trouble creating the window. */
}
- SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM)SELECT, (LPARAM)infoPtr);
+ Tcl_CreateEventSource(SocketSetupProc, SocketCheckProc, NULL);
+ return;
- return bytesRead;
+ initFailure:
+ TclpFinalizeSockets();
+ initialized = -1;
+ return;
}
/*
*----------------------------------------------------------------------
*
- * TcpOutputProc --
+ * SocketsEnabled --
*
- * This function is called by the generic IO level to write data to a
- * socket based channel.
+ * Check that the WinSock was successfully initialized.
+ *
+ * Warning:
+ * This check was useful in times of Windows98 where WinSock may
+ * not be available. This is not the case any more.
+ * This function may be removed with TCL 9.0
*
* Results:
- * The number of bytes written or -1 on failure.
+ * 1 if it is.
*
* Side effects:
- * Produces output on the socket.
+ * None.
*
*----------------------------------------------------------------------
*/
+ /* ARGSUSED */
static int
-TcpOutputProc(
- ClientData instanceData, /* The socket state. */
- const char *buf, /* Where to get data. */
- int toWrite, /* Maximum number of bytes to write. */
- int *errorCodePtr) /* Where to store error codes. */
+SocketsEnabled(void)
{
- SocketInfo *infoPtr = instanceData;
- int bytesWritten;
- DWORD error;
- ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);
+ int enabled;
- *errorCodePtr = 0;
+ Tcl_MutexLock(&socketMutex);
+ enabled = (initialized == 1);
+ Tcl_MutexUnlock(&socketMutex);
+ return enabled;
+}
- /*
- * 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.
- */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SocketExitHandler --
+ *
+ * Callback invoked during exit clean up to delete the socket
+ * communication window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
- if (!SocketsEnabled()) {
- *errorCodePtr = EFAULT;
- return -1;
- }
+ /* ARGSUSED */
+static void
+SocketExitHandler(
+ ClientData clientData) /* Not used. */
+{
+ Tcl_MutexLock(&socketMutex);
/*
- * Check to see if the socket is connected before trying to write.
+ * Make sure the socket event handling window is cleaned-up for, at
+ * most, this thread.
*/
- if ((infoPtr->flags & SOCKET_ASYNC_CONNECT)
- && !WaitForSocketEvent(infoPtr, FD_CONNECT, errorCodePtr)) {
- return -1;
- }
-
- while (1) {
- SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
- (WPARAM) UNSELECT, (LPARAM) infoPtr);
-
- /* single fd operation: this proc is only called for a connected socket. */
- bytesWritten = send(infoPtr->sockets->fd, buf, toWrite, 0);
- if (bytesWritten != SOCKET_ERROR) {
- /*
- * Since Windows won't generate a new write event until we hit an
- * overflow condition, we need to force the event loop to poll
- * until the condition changes.
- */
-
- if (infoPtr->watchEvents & FD_WRITE) {
- Tcl_Time blockTime = { 0, 0 };
- Tcl_SetMaxBlockTime(&blockTime);
- }
- break;
- }
-
- /*
- * Check for error condition or overflow. In the event of overflow, we
- * need to clear the FD_WRITE flag so we can detect the next writable
- * event. Note that Windows only sends a new writable event after a
- * send fails with WSAEWOULDBLOCK.
- */
+ TclpFinalizeSockets();
+ UnregisterClass(classname, TclWinGetTclInstance());
+ initialized = 0;
+ Tcl_MutexUnlock(&socketMutex);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SocketSetupProc --
+ *
+ * This function is invoked before Tcl_DoOneEvent blocks waiting for an
+ * event.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Adjusts the block time if needed.
+ *
+ *----------------------------------------------------------------------
+ */
- error = WSAGetLastError();
- if (error == WSAEWOULDBLOCK) {
- infoPtr->readyEvents &= ~(FD_WRITE);
- if (infoPtr->flags & SOCKET_ASYNC) {
- *errorCodePtr = EAGAIN;
- bytesWritten = -1;
- break;
- }
- } else {
- TclWinConvertError(error);
- *errorCodePtr = Tcl_GetErrno();
- bytesWritten = -1;
- break;
- }
+void
+SocketSetupProc(
+ ClientData data, /* Not used. */
+ int flags) /* Event flags as passed to Tcl_DoOneEvent. */
+{
+ TcpState *statePtr;
+ Tcl_Time blockTime = { 0, 0 };
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- /*
- * In the blocking case, wait until the file becomes writable or
- * closed and try again.
- */
+ if (!(flags & TCL_FILE_EVENTS)) {
+ return;
+ }
- if (!WaitForSocketEvent(infoPtr, FD_WRITE|FD_CLOSE, errorCodePtr)) {
- bytesWritten = -1;
+ /*
+ * Check to see if there is a ready socket. If so, poll.
+ */
+ WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
+ for (statePtr = tsdPtr->socketList; statePtr != NULL;
+ statePtr = statePtr->nextPtr) {
+ if (statePtr->readyEvents &
+ (statePtr->watchEvents | FD_CONNECT | FD_ACCEPT)
+ ) {
+ Tcl_SetMaxBlockTime(&blockTime);
break;
}
}
-
- SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM)SELECT, (LPARAM)infoPtr);
-
- return bytesWritten;
+ SetEvent(tsdPtr->socketListLock);
}
/*
*----------------------------------------------------------------------
*
- * TcpSetOptionProc --
+ * SocketCheckProc --
*
- * Sets Tcp channel specific options.
+ * This function is called by Tcl_DoOneEvent to check the socket event
+ * source for events.
*
* Results:
- * None, unless an error happens.
+ * None.
*
* Side effects:
- * Changes attributes of the socket at the system level.
+ * May queue an event.
*
*----------------------------------------------------------------------
*/
-static int
-TcpSetOptionProc(
- ClientData instanceData, /* Socket state. */
- Tcl_Interp *interp, /* For error reporting - can be NULL. */
- const char *optionName, /* Name of the option to set. */
- const char *value) /* New value for option. */
+static void
+SocketCheckProc(
+ ClientData data, /* Not used. */
+ int flags) /* Event flags as passed to Tcl_DoOneEvent. */
{
-#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
- SocketInfo *infoPtr = instanceData;
- SOCKET sock;
-#endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/
-
- /*
- * 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.
- */
+ TcpState *statePtr;
+ SocketEvent *evPtr;
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- if (!SocketsEnabled()) {
- if (interp) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "winsock is not initialized", -1));
- }
- return TCL_ERROR;
+ if (!(flags & TCL_FILE_EVENTS)) {
+ return;
}
-#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
- #error "TCL_FEATURE_KEEPALIVE_NAGLE not reviewed for whether to treat infoPtr->sockets as single fd or list"
- sock = infoPtr->sockets->fd;
-
- if (!strcasecmp(optionName, "-keepalive")) {
- BOOL val = FALSE;
- int boolVar, rtn;
-
- if (Tcl_GetBoolean(interp, value, &boolVar) != TCL_OK) {
- return TCL_ERROR;
- }
- if (boolVar) {
- val = TRUE;
- }
- rtn = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
- (const char *) &val, sizeof(BOOL));
- if (rtn != 0) {
- TclWinConvertError(WSAGetLastError());
- if (interp) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "couldn't set socket option: %s",
- Tcl_PosixError(interp)));
- }
- return TCL_ERROR;
- }
- return TCL_OK;
- } else if (!strcasecmp(optionName, "-nagle")) {
- BOOL val = FALSE;
- int boolVar, rtn;
+ /*
+ * Queue events for any ready sockets that don't already have events
+ * queued (caused by persistent states that won't generate WinSock
+ * events).
+ */
- if (Tcl_GetBoolean(interp, value, &boolVar) != TCL_OK) {
- return TCL_ERROR;
- }
- if (!boolVar) {
- val = TRUE;
- }
- rtn = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
- (const char *) &val, sizeof(BOOL));
- if (rtn != 0) {
- TclWinConvertError(WSAGetLastError());
- if (interp) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "couldn't set socket option: %s",
- Tcl_PosixError(interp)));
- }
- return TCL_ERROR;
+ WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
+ for (statePtr = tsdPtr->socketList; statePtr != NULL;
+ statePtr = statePtr->nextPtr) {
+ if ((statePtr->readyEvents &
+ (statePtr->watchEvents | FD_CONNECT | FD_ACCEPT))
+ && !(statePtr->flags & SOCKET_PENDING)
+ ) {
+ statePtr->flags |= SOCKET_PENDING;
+ evPtr = ckalloc(sizeof(SocketEvent));
+ evPtr->header.proc = SocketEventProc;
+ evPtr->socket = statePtr->sockets->fd;
+ Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
}
- return TCL_OK;
}
-
- return Tcl_BadChannelOption(interp, optionName, "keepalive nagle");
-#else
- return Tcl_BadChannelOption(interp, optionName, "");
-#endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/
+ SetEvent(tsdPtr->socketListLock);
}
/*
*----------------------------------------------------------------------
*
- * TcpGetOptionProc --
- *
- * Computes an option value for a TCP socket based channel, or a list of
- * all options and their values.
+ * SocketEventProc --
*
- * Note: This code is based on code contributed by John Haxby.
+ * This function is called by Tcl_ServiceEvent when a socket event
+ * reaches the front of the event queue. This function is responsible for
+ * notifying the generic channel code.
*
* Results:
- * A standard Tcl result. The value of the specified option or a list of
- * all options and their values is returned in the supplied DString.
+ * Returns 1 if the event was handled, meaning it should be removed from
+ * the queue. Returns 0 if the event was not handled, meaning it should
+ * stay on the queue. The only time the event isn't handled is if the
+ * TCL_FILE_EVENTS flag bit isn't set.
*
* Side effects:
- * None.
+ * Whatever the channel callback functions do.
*
*----------------------------------------------------------------------
*/
static int
-TcpGetOptionProc(
- ClientData instanceData, /* Socket state. */
- Tcl_Interp *interp, /* For error reporting - can be NULL */
- const char *optionName, /* Name of the option to retrieve the value
- * for, or NULL to get all options and their
- * values. */
- Tcl_DString *dsPtr) /* Where to store the computed value;
- * initialized by caller. */
+SocketEventProc(
+ Tcl_Event *evPtr, /* Event to service. */
+ int flags) /* Flags that indicate what events to handle,
+ * such as TCL_FILE_EVENTS. */
{
- SocketInfo *infoPtr = instanceData;
- char host[NI_MAXHOST], port[NI_MAXSERV];
- SOCKET sock;
- size_t len = 0;
- int reverseDNS = 0;
-#define SUPPRESS_RDNS_VAR "::tcl::unsupported::noReverseDNS"
+ TcpState *statePtr;
+ SocketEvent *eventPtr = (SocketEvent *) evPtr;
+ int mask = 0, events;
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ TcpFdList *fds;
+ SOCKET newSocket;
+ address addr;
+ int len;
+
+ if (!(flags & TCL_FILE_EVENTS)) {
+ return 0;
+ }
/*
- * 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.
+ * Find the specified socket on the socket list.
*/
- if (!SocketsEnabled()) {
- if (interp) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "winsock is not initialized", -1));
+ WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
+ for (statePtr = tsdPtr->socketList; statePtr != NULL;
+ statePtr = statePtr->nextPtr) {
+ if (statePtr->sockets->fd == eventPtr->socket) {
+ break;
}
- return TCL_ERROR;
}
- sock = infoPtr->sockets->fd;
- if (optionName != NULL) {
- len = strlen(optionName);
+ /*
+ * Discard events that have gone stale.
+ */
+
+ if (!statePtr) {
+ SetEvent(tsdPtr->socketListLock);
+ return 1;
}
- if ((len > 1) && (optionName[1] == 'e') &&
- (strncmp(optionName, "-error", len) == 0)) {
- int optlen;
- DWORD err;
- int ret;
+ /*
+ * Clear flag that (this) event is pending
+ */
- optlen = sizeof(int);
- ret = TclWinGetSockOpt(sock, SOL_SOCKET, SO_ERROR,
- (char *)&err, &optlen);
- if (ret == SOCKET_ERROR) {
- err = WSAGetLastError();
- }
- if (err) {
- TclWinConvertError(err);
- Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(Tcl_GetErrno()), -1);
- }
- return TCL_OK;
- }
+ statePtr->flags &= ~SOCKET_PENDING;
- if (interp != NULL && Tcl_GetVar(interp, SUPPRESS_RDNS_VAR, 0) != NULL) {
- reverseDNS = NI_NUMERICHOST;
- }
+ /*
+ * Continue async connect if pending and ready
+ */
- if ((len == 0) || ((len > 1) && (optionName[1] == 'p') &&
- (strncmp(optionName, "-peername", len) == 0))) {
- address peername;
- socklen_t size = sizeof(peername);
+ if ( statePtr->readyEvents & FD_CONNECT ) {
+ if ( statePtr->flags & TCP_ASYNC_PENDING ) {
- if (getpeername(sock, (LPSOCKADDR) &(peername.sa), &size) == 0) {
- if (len == 0) {
- Tcl_DStringAppendElement(dsPtr, "-peername");
- Tcl_DStringStartSublist(dsPtr);
- }
+ /*
+ * Do one step and save eventual connect error
+ */
+
+ SetEvent(tsdPtr->socketListLock);
+ WaitForConnect(statePtr,NULL);
- getnameinfo(&(peername.sa), size, host, sizeof(host),
- NULL, 0, NI_NUMERICHOST);
- Tcl_DStringAppendElement(dsPtr, host);
- getnameinfo(&(peername.sa), size, host, sizeof(host),
- port, sizeof(port), reverseDNS | NI_NUMERICSERV);
- Tcl_DStringAppendElement(dsPtr, host);
- Tcl_DStringAppendElement(dsPtr, port);
- if (len == 0) {
- Tcl_DStringEndSublist(dsPtr);
- } else {
- return TCL_OK;
- }
} else {
+
/*
- * getpeername failed - but if we were asked for all the options
- * (len==0), don't flag an error at that point because it could be
- * an fconfigure request on a server socket (such sockets have no
- * peer). {Copied from unix/tclUnixChan.c}
+ * No async connect reenter pending. Just clear event.
*/
- if (len) {
- TclWinConvertError((DWORD) WSAGetLastError());
- if (interp) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "can't get peername: %s",
- Tcl_PosixError(interp)));
- }
- return TCL_ERROR;
- }
+ statePtr->readyEvents &= ~(FD_CONNECT);
+ SetEvent(tsdPtr->socketListLock);
}
+ return 1;
}
- if ((len == 0) || ((len > 1) && (optionName[1] == 's') &&
- (strncmp(optionName, "-sockname", len) == 0))) {
- TcpFdList *fds;
- address sockname;
- socklen_t size;
- int found = 0;
+ /*
+ * Handle connection requests directly.
+ */
+ if (statePtr->readyEvents & FD_ACCEPT) {
+ for (fds = statePtr->sockets; fds != NULL; fds = fds->next) {
- if (len == 0) {
- Tcl_DStringAppendElement(dsPtr, "-sockname");
- Tcl_DStringStartSublist(dsPtr);
- }
- for (fds = infoPtr->sockets; fds != NULL; fds = fds->next) {
- sock = fds->fd;
- size = sizeof(sockname);
- if (getsockname(sock, &(sockname.sa), &size) >= 0) {
- int flags = reverseDNS;
+ /*
+ * Accept the incoming connection request.
+ */
+ len = sizeof(address);
- found = 1;
- getnameinfo(&sockname.sa, size, host, sizeof(host),
- NULL, 0, NI_NUMERICHOST);
- Tcl_DStringAppendElement(dsPtr, host);
+ newSocket = accept(fds->fd, &(addr.sa), &len);
- /*
- * We don't want to resolve INADDR_ANY and sin6addr_any; they
- * can sometimes cause problems (and never have a name).
- */
- flags |= NI_NUMERICSERV;
- if (sockname.sa.sa_family == AF_INET) {
- if (sockname.sa4.sin_addr.s_addr == INADDR_ANY) {
- flags |= NI_NUMERICHOST;
- }
- } else if (sockname.sa.sa_family == AF_INET6) {
- if ((IN6_ARE_ADDR_EQUAL(&sockname.sa6.sin6_addr,
- &in6addr_any)) ||
- (IN6_IS_ADDR_V4MAPPED(&sockname.sa6.sin6_addr)
- && sockname.sa6.sin6_addr.s6_addr[12] == 0
- && sockname.sa6.sin6_addr.s6_addr[13] == 0
- && sockname.sa6.sin6_addr.s6_addr[14] == 0
- && sockname.sa6.sin6_addr.s6_addr[15] == 0)) {
- flags |= NI_NUMERICHOST;
- }
- }
- getnameinfo(&sockname.sa, size, host, sizeof(host),
- port, sizeof(port), flags);
- Tcl_DStringAppendElement(dsPtr, host);
- Tcl_DStringAppendElement(dsPtr, port);
- }
- }
- if (found) {
- if (len == 0) {
- Tcl_DStringEndSublist(dsPtr);
- } else {
- return TCL_OK;
+ /* On Tcl server sockets with multiple OS fds we loop over the fds trying
+ * an accept() on each, so we expect INVALID_SOCKET. There are also other
+ * network stack conditions that can result in FD_ACCEPT but a subsequent
+ * failure on accept() by the time we get around to it.
+ * Access to sockets (acceptEventCount, readyEvents) in socketList
+ * is still protected by the lock (prevents reintroduction of
+ * SF Tcl Bug 3056775.
+ */
+
+ if (newSocket == INVALID_SOCKET) {
+ /* int err = WSAGetLastError(); */
+ continue;
}
- } else {
- if (interp) {
- TclWinConvertError((DWORD) WSAGetLastError());
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "can't get sockname: %s", Tcl_PosixError(interp)));
+
+ /*
+ * It is possible that more than one FD_ACCEPT has been sent, so an extra
+ * count must be kept. Decrement the count, and reset the readyEvent bit
+ * if the count is no longer > 0.
+ */
+ statePtr->acceptEventCount--;
+
+ if (statePtr->acceptEventCount <= 0) {
+ statePtr->readyEvents &= ~(FD_ACCEPT);
}
- return TCL_ERROR;
- }
- }
-#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
- if (len == 0 || !strncmp(optionName, "-keepalive", len)) {
- int optlen;
- BOOL opt = FALSE;
+ SetEvent(tsdPtr->socketListLock);
- if (len == 0) {
- Tcl_DStringAppendElement(dsPtr, "-keepalive");
- }
- optlen = sizeof(BOOL);
- getsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&opt, &optlen);
- if (opt) {
- Tcl_DStringAppendElement(dsPtr, "1");
- } else {
- Tcl_DStringAppendElement(dsPtr, "0");
- }
- if (len > 0) {
- return TCL_OK;
+ /* Caution: TcpAccept() has the side-effect of evaluating the server
+ * accept script (via AcceptCallbackProc() in tclIOCmd.c), which can
+ * close the server socket and invalidate statePtr and fds.
+ * If TcpAccept() accepts a socket we must return immediately and let
+ * SocketCheckProc queue additional FD_ACCEPT events.
+ */
+ TcpAccept(fds, newSocket, addr);
+ return 1;
}
+
+ /* Loop terminated with no sockets accepted; clear the ready mask so
+ * we can detect the next connection request. Note that connection
+ * requests are level triggered, so if there is a request already
+ * pending, a new event will be generated.
+ */
+ statePtr->acceptEventCount = 0;
+ statePtr->readyEvents &= ~(FD_ACCEPT);
+
+ SetEvent(tsdPtr->socketListLock);
+ return 1;
}
- if (len == 0 || !strncmp(optionName, "-nagle", len)) {
- int optlen;
- BOOL opt = FALSE;
+ SetEvent(tsdPtr->socketListLock);
+
+ /*
+ * Mask off unwanted events and compute the read/write mask so we can
+ * notify the channel.
+ */
+
+ events = statePtr->readyEvents & statePtr->watchEvents;
+
+ if (events & FD_CLOSE) {
+ /*
+ * If the socket was closed and the channel is still interested in
+ * read events, then we need to ensure that we keep polling for this
+ * event until someone does something with the channel. Note that we
+ * do this before calling Tcl_NotifyChannel so we don't have to watch
+ * out for the channel being deleted out from under us. This may cause
+ * a redundant trip through the event loop, but it's simpler than
+ * trying to do unwind protection.
+ */
+
+ Tcl_Time blockTime = { 0, 0 };
+
+ Tcl_SetMaxBlockTime(&blockTime);
+ mask |= TCL_READABLE|TCL_WRITABLE;
+ } else if (events & FD_READ) {
+
+ /*
+ * Throw the readable event if an async connect failed.
+ */
+
+ if ( statePtr->flags & TCP_ASYNC_FAILED ) {
+
+ mask |= TCL_READABLE;
- if (len == 0) {
- Tcl_DStringAppendElement(dsPtr, "-nagle");
- }
- optlen = sizeof(BOOL);
- getsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&opt, &optlen);
- if (opt) {
- Tcl_DStringAppendElement(dsPtr, "0");
} else {
- Tcl_DStringAppendElement(dsPtr, "1");
- }
- if (len > 0) {
- return TCL_OK;
+ fd_set readFds;
+ struct timeval timeout;
+
+ /*
+ * We must check to see if data is really available, since someone
+ * could have consumed the data in the meantime. Turn off async
+ * notification so select will work correctly. If the socket is
+ * still readable, notify the channel driver, otherwise reset the
+ * async select handler and keep waiting.
+ */
+
+ SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
+ (WPARAM) UNSELECT, (LPARAM) statePtr);
+
+ FD_ZERO(&readFds);
+ FD_SET(statePtr->sockets->fd, &readFds);
+ timeout.tv_usec = 0;
+ timeout.tv_sec = 0;
+
+ if (select(0, &readFds, NULL, NULL, &timeout) != 0) {
+ mask |= TCL_READABLE;
+ } else {
+ statePtr->readyEvents &= ~(FD_READ);
+ SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
+ (WPARAM) SELECT, (LPARAM) statePtr);
+ }
}
}
-#endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/
- if (len > 0) {
-#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
- return Tcl_BadChannelOption(interp, optionName,
- "peername sockname keepalive nagle");
-#else
- return Tcl_BadChannelOption(interp, optionName, "peername sockname");
-#endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/
+ /*
+ * writable event
+ */
+
+ if (events & FD_WRITE) {
+ mask |= TCL_WRITABLE;
}
- return TCL_OK;
+ /*
+ * Call registered event procedures
+ */
+
+ if (mask) {
+ Tcl_NotifyChannel(statePtr->channel, mask);
+ }
+ return 1;
}
/*
*----------------------------------------------------------------------
*
- * TcpWatchProc --
+ * AddSocketInfoFd --
*
- * Informs the channel driver of the events that the generic channel code
- * wishes to receive on this socket.
+ * This function adds a SOCKET file descriptor to the 'sockets' linked
+ * list of a TcpState structure.
*
* Results:
* None.
*
* Side effects:
- * May cause the notifier to poll if any of the specified conditions are
- * already true.
+ * None, except for allocation of memory.
*
*----------------------------------------------------------------------
*/
static void
-TcpWatchProc(
- ClientData instanceData, /* The socket state. */
- int mask) /* Events of interest; an OR-ed combination of
- * TCL_READABLE, TCL_WRITABLE and
- * TCL_EXCEPTION. */
+AddSocketInfoFd(
+ TcpState *statePtr,
+ SOCKET socket)
{
- SocketInfo *infoPtr = instanceData;
+ TcpFdList *fds = statePtr->sockets;
- /*
- * Update the watch events mask. Only if the socket is not a server
- * socket. [Bug 557878]
- */
-
- if (!infoPtr->acceptProc) {
- infoPtr->watchEvents = 0;
- if (mask & TCL_READABLE) {
- infoPtr->watchEvents |= (FD_READ|FD_CLOSE|FD_ACCEPT);
- }
- if (mask & TCL_WRITABLE) {
- infoPtr->watchEvents |= (FD_WRITE|FD_CLOSE|FD_CONNECT);
+ if ( fds == NULL ) {
+ /* Add the first FD */
+ statePtr->sockets = ckalloc(sizeof(TcpFdList));
+ fds = statePtr->sockets;
+ } else {
+ /* Find end of list and append FD */
+ while ( fds->next != NULL ) {
+ fds = fds->next;
}
- /*
- * If there are any conditions already set, then tell the notifier to
- * poll rather than block.
- */
+ fds->next = ckalloc(sizeof(TcpFdList));
+ fds = fds->next;
+ }
- if (infoPtr->readyEvents & infoPtr->watchEvents) {
- Tcl_Time blockTime = { 0, 0 };
+ /* Populate new FD */
+ fds->fd = socket;
+ fds->statePtr = statePtr;
+ fds->next = NULL;
+}
- Tcl_SetMaxBlockTime(&blockTime);
- }
- }
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NewSocketInfo --
+ *
+ * This function allocates and initializes a new TcpState structure.
+ *
+ * Results:
+ * Returns a newly allocated TcpState.
+ *
+ * Side effects:
+ * None, except for allocation of memory.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static TcpState *
+NewSocketInfo(SOCKET socket)
+{
+ TcpState *statePtr = ckalloc(sizeof(TcpState));
+
+ memset(statePtr, 0, sizeof(TcpState));
+
+ /*
+ * TIP #218. Removed the code inserting the new structure into the global
+ * list. This is now handled in the thread action callbacks, and only
+ * there.
+ */
+
+ AddSocketInfoFd(statePtr, socket);
+
+ return statePtr;
}
/*
*----------------------------------------------------------------------
*
- * TcpGetProc --
+ * WaitForSocketEvent --
*
- * Called from Tcl_GetChannelHandle to retrieve an OS handle from inside
- * a TCP socket based channel.
+ * Waits until one of the specified events occurs on a socket.
+ * For event FD_CONNECT use WaitForConnect.
*
* Results:
- * Returns TCL_OK with the socket in handlePtr.
+ * Returns 1 on success or 0 on failure, with an error code in
+ * errorCodePtr.
*
* Side effects:
- * None.
+ * Processes socket events off the system queue.
*
*----------------------------------------------------------------------
*/
static int
-TcpGetHandleProc(
- ClientData instanceData, /* The socket state. */
- int direction, /* Not used. */
- ClientData *handlePtr) /* Where to store the handle. */
+WaitForSocketEvent(
+ TcpState *statePtr, /* Information about this socket. */
+ int events, /* Events to look for. May be one of
+ * FD_READ or FD_WRITE.
+ */
+ int *errorCodePtr) /* Where to store errors? */
{
- SocketInfo *statePtr = instanceData;
+ int result = 1;
+ int oldMode;
+ ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);
+ /*
+ * Be sure to disable event servicing so we are truly modal.
+ */
- *handlePtr = INT2PTR(statePtr->sockets->fd);
- return TCL_OK;
+ oldMode = Tcl_SetServiceMode(TCL_SERVICE_NONE);
+
+ /*
+ * Reset WSAAsyncSelect so we have a fresh set of events pending.
+ */
+
+ SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) UNSELECT,
+ (LPARAM) statePtr);
+ SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT,
+ (LPARAM) statePtr);
+
+ while (1) {
+ int event_found;
+
+ /* get statePtr lock */
+ WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
+
+ /* Check if event occured */
+ event_found = (statePtr->readyEvents & events);
+
+ /* Free list lock */
+ SetEvent(tsdPtr->socketListLock);
+
+ /* exit loop if event occured */
+ if (event_found) {
+ break;
+ }
+
+ /* Exit loop if event did not occur but this is a non-blocking channel */
+ if (statePtr->flags & TCP_NONBLOCKING) {
+ *errorCodePtr = EWOULDBLOCK;
+ result = 0;
+ break;
+ }
+
+ /*
+ * Wait until something happens.
+ */
+
+ WaitForSingleObject(tsdPtr->readyEvent, INFINITE);
+ }
+
+ (void) Tcl_SetServiceMode(oldMode);
+ return result;
}
/*
@@ -2462,7 +3056,8 @@ SocketProc(
{
int event, error;
SOCKET socket;
- SocketInfo *infoPtr;
+ TcpState *statePtr;
+ int info_found = 0;
TcpFdList *fds = NULL;
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
#ifdef _WIN64
@@ -2500,80 +3095,76 @@ SocketProc(
error = WSAGETSELECTERROR(lParam);
socket = (SOCKET) wParam;
+ WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
+
/*
* Find the specified socket on the socket list and update its
* eventState flag.
*/
- WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
- for (infoPtr = tsdPtr->socketList; infoPtr != NULL;
- infoPtr = infoPtr->nextPtr) {
- for (fds = infoPtr->sockets; fds != NULL; fds = fds->next) {
- if (fds->fd == socket) {
- /*
- * Update the socket state.
- *
- * A count of FD_ACCEPTS is stored, so if an FD_CLOSE event
- * happens, then clear the FD_ACCEPT count. Otherwise,
- * increment the count if the current event is an FD_ACCEPT.
- */
-
- if (event & FD_CLOSE) {
- infoPtr->acceptEventCount = 0;
- infoPtr->readyEvents &= ~(FD_WRITE|FD_ACCEPT);
- } else if (event & FD_ACCEPT) {
- infoPtr->acceptEventCount++;
- }
-
- if (event & FD_CONNECT) {
- /*
- * The socket is now connected, clear the async connect
- * flag.
- */
-
- infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT);
-
- /*
- * Remember any error that occurred so we can report
- * connection failures.
- */
-
- if (error != ERROR_SUCCESS) {
- TclWinConvertError((DWORD) error);
- infoPtr->lastError = Tcl_GetErrno();
- }
- }
+ for (statePtr = tsdPtr->socketList; statePtr != NULL;
+ statePtr = statePtr->nextPtr) {
+ if ( FindFDInList(statePtr,socket) ) {
+ info_found = 1;
+ break;
+ }
+ }
+ /*
+ * Check if there is a pending info structure not jet in the
+ * list
+ */
+ if ( !info_found
+ && tsdPtr->pendingTcpState != NULL
+ && FindFDInList(tsdPtr->pendingTcpState,socket) ) {
+ statePtr = tsdPtr->pendingTcpState;
+ info_found = 1;
+ }
+ if (info_found) {
- if (infoPtr->flags & SOCKET_ASYNC_CONNECT) {
- infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT);
- if (error != ERROR_SUCCESS) {
- TclWinConvertError((DWORD) error);
- infoPtr->lastError = Tcl_GetErrno();
- }
- infoPtr->readyEvents |= FD_WRITE;
- }
- infoPtr->readyEvents |= event;
+ /*
+ * Update the socket state.
+ *
+ * A count of FD_ACCEPTS is stored, so if an FD_CLOSE event
+ * happens, then clear the FD_ACCEPT count. Otherwise,
+ * increment the count if the current event is an FD_ACCEPT.
+ */
- /*
- * Wake up the Main Thread.
- */
+ if (event & FD_CLOSE) {
+ statePtr->acceptEventCount = 0;
+ statePtr->readyEvents &= ~(FD_WRITE|FD_ACCEPT);
+ } else if (event & FD_ACCEPT) {
+ statePtr->acceptEventCount++;
+ }
- SetEvent(tsdPtr->readyEvent);
- Tcl_ThreadAlert(tsdPtr->threadId);
- break;
+ if (event & FD_CONNECT) {
+ /*
+ * Remember any error that occurred so we can report
+ * connection failures.
+ */
+ if (error != ERROR_SUCCESS) {
+ statePtr->notifierConnectError = error;
}
}
+ /*
+ * Inform main thread about signaled events
+ */
+ statePtr->readyEvents |= event;
+
+ /*
+ * Wake up the Main Thread.
+ */
+ SetEvent(tsdPtr->readyEvent);
+ Tcl_ThreadAlert(tsdPtr->threadId);
}
SetEvent(tsdPtr->socketListLock);
break;
case SOCKET_SELECT:
- infoPtr = (SocketInfo *) lParam;
- for (fds = infoPtr->sockets; fds != NULL; fds = fds->next) {
- infoPtr = (SocketInfo *) lParam;
+ statePtr = (TcpState *) lParam;
+ for (fds = statePtr->sockets; fds != NULL; fds = fds->next) {
if (wParam == SELECT) {
WSAAsyncSelect(fds->fd, hwnd,
- SOCKET_MESSAGE, infoPtr->selectEvents);
+ SOCKET_MESSAGE, statePtr->selectEvents);
} else {
/*
* Clear the selection mask
@@ -2595,83 +3186,31 @@ SocketProc(
/*
*----------------------------------------------------------------------
*
- * Tcl_GetHostName --
+ * FindFDInList --
*
- * Returns the name of the local host.
+ * Return true, if the given file descriptior is contained in the
+ * file descriptor list.
*
* Results:
- * A string containing the network name for this machine. The caller must
- * not modify or free this string.
+ * true if found.
*
* Side effects:
- * Caches the name to return for future calls.
*
*----------------------------------------------------------------------
*/
-const char *
-Tcl_GetHostName(void)
-{
- return Tcl_GetString(TclGetProcessGlobalValue(&hostName));
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * InitializeHostName --
- *
- * This routine sets the process global value of the name of the local
- * host on which the process is running.
- *
- * Results:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
-void
-InitializeHostName(
- char **valuePtr,
- int *lengthPtr,
- Tcl_Encoding *encodingPtr)
+static int
+FindFDInList(
+ TcpState *statePtr,
+ SOCKET socket)
{
- TCHAR tbuf[MAX_COMPUTERNAME_LENGTH + 1];
- DWORD length = MAX_COMPUTERNAME_LENGTH + 1;
- Tcl_DString ds;
-
- if (GetComputerName(tbuf, &length) != 0) {
- /*
- * Convert string from native to UTF then change to lowercase.
- */
-
- Tcl_UtfToLower(Tcl_WinTCharToUtf(tbuf, -1, &ds));
-
- } else {
- Tcl_DStringInit(&ds);
- if (TclpHasSockets(NULL) == TCL_OK) {
- /*
- * The buffer size of 256 is recommended by the MSDN page that
- * documents gethostname() as being always adequate.
- */
-
- Tcl_DString inDs;
-
- Tcl_DStringInit(&inDs);
- Tcl_DStringSetLength(&inDs, 256);
- if (gethostname(Tcl_DStringValue(&inDs),
- Tcl_DStringLength(&inDs)) == 0) {
- Tcl_ExternalToUtfDString(NULL, Tcl_DStringValue(&inDs), -1,
- &ds);
- }
- Tcl_DStringFree(&inDs);
+ TcpFdList *fds;
+ for (fds = statePtr->sockets; fds != NULL; fds = fds->next) {
+ if (fds->fd == socket) {
+ return 1;
}
}
-
- *encodingPtr = Tcl_GetEncoding(NULL, "utf-8");
- *lengthPtr = Tcl_DStringLength(&ds);
- *valuePtr = ckalloc((*lengthPtr) + 1);
- memcpy(*valuePtr, Tcl_DStringValue(&ds), (size_t)(*lengthPtr)+1);
- Tcl_DStringFree(&ds);
+ return 0;
}
/*
@@ -2679,10 +3218,11 @@ 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.
+ * Those functions are historically exported by the stubs table and
+ * just use the original system calls now.
+ *
+ * Warning:
+ * Those functions are depreciated and will be removed with TCL 9.0.
*
* Results:
* As defined for each function.
@@ -2693,6 +3233,7 @@ InitializeHostName(
*----------------------------------------------------------------------
*/
+#undef TclWinGetSockOpt
int
TclWinGetSockOpt(
SOCKET s,
@@ -2701,19 +3242,10 @@ TclWinGetSockOpt(
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);
}
-
+#undef TclWinSetSockOpt
int
TclWinSetSockOpt(
SOCKET s,
@@ -2722,51 +3254,22 @@ TclWinSetSockOpt(
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);
}
+#undef TclpInetNtoa
char *
TclpInetNtoa(
struct in_addr addr)
{
- /*
- * 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 inet_ntoa(addr);
}
-
+#undef TclWinGetServByName
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);
}
@@ -2792,7 +3295,7 @@ TcpThreadActionProc(
int action)
{
ThreadSpecificData *tsdPtr;
- SocketInfo *infoPtr = instanceData;
+ TcpState *statePtr = instanceData;
int notifyCmd;
if (action == TCL_CHANNEL_THREAD_INSERT) {
@@ -2808,13 +3311,18 @@ TcpThreadActionProc(
tsdPtr = TCL_TSD_INIT(&dataKey);
WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
- infoPtr->nextPtr = tsdPtr->socketList;
- tsdPtr->socketList = infoPtr;
+ statePtr->nextPtr = tsdPtr->socketList;
+ tsdPtr->socketList = statePtr;
+
+ if (statePtr == tsdPtr->pendingTcpState) {
+ tsdPtr->pendingTcpState = NULL;
+ }
+
SetEvent(tsdPtr->socketListLock);
notifyCmd = SELECT;
} else {
- SocketInfo **nextPtrPtr;
+ TcpState **nextPtrPtr;
int removed = 0;
tsdPtr = TCL_TSD_INIT(&dataKey);
@@ -2827,8 +3335,8 @@ TcpThreadActionProc(
WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
for (nextPtrPtr = &(tsdPtr->socketList); (*nextPtrPtr) != NULL;
nextPtrPtr = &((*nextPtrPtr)->nextPtr)) {
- if ((*nextPtrPtr) == infoPtr) {
- (*nextPtrPtr) = infoPtr->nextPtr;
+ if ((*nextPtrPtr) == statePtr) {
+ (*nextPtrPtr) = statePtr->nextPtr;
removed = 1;
break;
}
@@ -2854,7 +3362,7 @@ TcpThreadActionProc(
*/
SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
- (WPARAM) notifyCmd, (LPARAM) infoPtr);
+ (WPARAM) notifyCmd, (LPARAM) statePtr);
}
/*
@@ -2862,5 +3370,7 @@ TcpThreadActionProc(
* mode: c
* c-basic-offset: 4
* fill-column: 78
+ * tab-width: 8
+ * indent-tabs-mode: nil
* End:
*/
diff --git a/win/tclWinTest.c b/win/tclWinTest.c
index 136c4db..b3ad626 100644
--- a/win/tclWinTest.c
+++ b/win/tclWinTest.c
@@ -17,7 +17,7 @@
/*
* For TestplatformChmod on Windows
*/
-#ifdef __WIN32__
+#ifdef _WIN32
#include <aclapi.h>
#endif
@@ -32,8 +32,8 @@
* Forward declarations of functions defined later in this file:
*/
-static int TesteventloopCmd(ClientData dummy, Tcl_Interp *interp,
- int argc, const char **argv);
+static int TesteventloopCmd(ClientData dummy, Tcl_Interp* interp,
+ int objc, Tcl_Obj *const objv[]);
static int TestvolumetypeCmd(ClientData dummy,
Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[]);
@@ -43,8 +43,8 @@ static int TestwinsleepCmd(ClientData dummy, Tcl_Interp* interp,
int objc, Tcl_Obj *const objv[]);
static Tcl_ObjCmdProc TestExceptionCmd;
static int TestplatformChmod(const char *nativePath, int pmode);
-static int TestchmodCmd(ClientData dummy,
- Tcl_Interp *interp, int argc, const char **argv);
+static int TestchmodCmd(ClientData dummy, Tcl_Interp* interp,
+ int objc, Tcl_Obj *const objv[]);
/*
*----------------------------------------------------------------------
@@ -71,8 +71,8 @@ TclplatformtestInit(
* Add commands for platform specific tests for Windows here.
*/
- Tcl_CreateCommand(interp, "testchmod", TestchmodCmd, NULL, NULL);
- Tcl_CreateCommand(interp, "testeventloop", TesteventloopCmd, NULL, NULL);
+ Tcl_CreateObjCommand(interp, "testchmod", TestchmodCmd, NULL, NULL);
+ Tcl_CreateObjCommand(interp, "testeventloop", TesteventloopCmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "testvolumetype", TestvolumetypeCmd,
NULL, NULL);
Tcl_CreateObjCommand(interp, "testwinclock", TestwinclockCmd, NULL, NULL);
@@ -103,21 +103,20 @@ static int
TesteventloopCmd(
ClientData clientData, /* Not used. */
Tcl_Interp *interp, /* Current interpreter. */
- int argc, /* Number of arguments. */
- const char **argv) /* Argument strings. */
+ int objc, /* Number of arguments. */
+ Tcl_Obj *const objv[]) /* Argument objects. */
{
static int *framePtr = NULL;/* Pointer to integer on stack frame of
* innermost invocation of the "wait"
* subcommand. */
- if (argc < 2) {
- Tcl_AppendResult(interp, "wrong # arguments: should be \"", argv[0],
- " option ... \"", NULL);
+ if (objc < 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "option ...");
return TCL_ERROR;
}
- if (strcmp(argv[1], "done") == 0) {
+ if (strcmp(Tcl_GetString(objv[1]), "done") == 0) {
*framePtr = 1;
- } else if (strcmp(argv[1], "wait") == 0) {
+ } else if (strcmp(Tcl_GetString(objv[1]), "wait") == 0) {
int *oldFramePtr, done;
int oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL);
@@ -152,7 +151,7 @@ TesteventloopCmd(
(void) Tcl_SetServiceMode(oldMode);
framePtr = oldFramePtr;
} else {
- Tcl_AppendResult(interp, "bad option \"", argv[1],
+ Tcl_AppendResult(interp, "bad option \"", Tcl_GetString(objv[1]),
"\": must be done or wait", NULL);
return TCL_ERROR;
}
@@ -211,7 +210,7 @@ TestvolumetypeCmd(
TclWinConvertError(GetLastError());
return TCL_ERROR;
}
- Tcl_SetResult(interp, volType, TCL_VOLATILE);
+ Tcl_AppendResult(interp, volType, NULL);
return TCL_OK;
#undef VOL_BUF_SIZE
}
@@ -623,29 +622,25 @@ static int
TestchmodCmd(
ClientData dummy, /* Not used. */
Tcl_Interp *interp, /* Current interpreter. */
- int argc, /* Number of arguments. */
- const char **argv) /* Argument strings. */
+ int objc, /* Parameter count */
+ Tcl_Obj *const * objv) /* Parameter vector */
{
int i, mode;
- char *rest;
- if (argc < 2) {
- usage:
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " mode file ?file ...?", NULL);
+ if (objc < 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "mode file ?file ...?");
return TCL_ERROR;
}
- mode = (int) strtol(argv[1], &rest, 8);
- if ((rest == argv[1]) || (*rest != '\0')) {
- goto usage;
+ if (Tcl_GetIntFromObj(interp, objv[1], &mode) != TCL_OK) {
+ return TCL_ERROR;
}
- for (i = 2; i < argc; i++) {
+ for (i = 2; i < objc; i++) {
Tcl_DString buffer;
const char *translated;
- translated = Tcl_TranslateFileName(interp, argv[i], &buffer);
+ translated = Tcl_TranslateFileName(interp, Tcl_GetString(objv[i]), &buffer);
if (translated == NULL) {
return TCL_ERROR;
}
diff --git a/win/tclWinThrd.c b/win/tclWinThrd.c
index 102fd40..1c9d483 100644
--- a/win/tclWinThrd.c
+++ b/win/tclWinThrd.c
@@ -13,7 +13,15 @@
#include "tclWinInt.h"
-#include <sys/stat.h>
+#include <float.h>
+
+/* Workaround for mingw versions which don't provide this in float.h */
+#ifndef _MCW_EM
+# define _MCW_EM 0x0008001F /* Error masks */
+# define _MCW_RC 0x00000300 /* Rounding */
+# define _MCW_PC 0x00030000 /* Precision */
+_CRTIMP unsigned int __cdecl _controlfp (unsigned int unNew, unsigned int unMask);
+#endif
/*
* This is the master lock used to serialize access to other serialization
@@ -124,6 +132,66 @@ typedef struct allocMutex {
#endif /* USE_THREAD_ALLOC */
/*
+ * The per thread data passed from TclpThreadCreate
+ * to TclWinThreadStart.
+ */
+
+typedef struct WinThread {
+ LPTHREAD_START_ROUTINE lpStartAddress; /* Original startup routine */
+ LPVOID lpParameter; /* Original startup data */
+ unsigned int fpControl; /* Floating point control word from the
+ * main thread */
+} WinThread;
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclWinThreadStart --
+ *
+ * This procedure is the entry point for all new threads created
+ * by Tcl on Windows.
+ *
+ * Results:
+ * Various, depending on the result of the wrapped thread start
+ * routine.
+ *
+ * Side effects:
+ * Arbitrary, since user code is executed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static DWORD WINAPI
+TclWinThreadStart(
+ LPVOID lpParameter) /* The WinThread structure pointer passed
+ * from TclpThreadCreate */
+{
+ WinThread *winThreadPtr = (WinThread *) lpParameter;
+ unsigned int fpmask;
+ LPTHREAD_START_ROUTINE lpOrigStartAddress;
+ LPVOID lpOrigParameter;
+
+ if (!winThreadPtr) {
+ return TCL_ERROR;
+ }
+
+ fpmask = _MCW_EM | _MCW_RC | _MCW_PC;
+
+#if defined(_MSC_VER) && _MSC_VER >= 1200
+ fpmask |= _MCW_DN;
+#endif
+
+ _controlfp(winThreadPtr->fpControl, fpmask);
+
+ lpOrigStartAddress = winThreadPtr->lpStartAddress;
+ lpOrigParameter = winThreadPtr->lpParameter;
+
+ ckfree((char *)winThreadPtr);
+ return lpOrigStartAddress(lpOrigParameter);
+}
+
+/*
*----------------------------------------------------------------------
*
* TclpThreadCreate --
@@ -149,8 +217,14 @@ TclpThreadCreate(
int flags) /* Flags controlling behaviour of the new
* thread. */
{
+ WinThread *winThreadPtr; /* Per-thread startup info */
HANDLE tHandle;
+ winThreadPtr = (WinThread *)ckalloc(sizeof(WinThread));
+ winThreadPtr->lpStartAddress = (LPTHREAD_START_ROUTINE) proc;
+ winThreadPtr->lpParameter = clientData;
+ winThreadPtr->fpControl = _controlfp(0, 0);
+
EnterCriticalSection(&joinLock);
*idPtr = 0; /* must initialize as Tcl_Thread is a pointer and
@@ -158,12 +232,12 @@ TclpThreadCreate(
*/
#if defined(_MSC_VER) || defined(__MSVCRT__) || defined(__BORLANDC__)
- tHandle = (HANDLE) _beginthreadex(NULL, (unsigned) stackSize, proc,
- clientData, 0, (unsigned *)idPtr);
+ tHandle = (HANDLE) _beginthreadex(NULL, (unsigned) stackSize,
+ (Tcl_ThreadCreateProc*) TclWinThreadStart, winThreadPtr,
+ 0, (unsigned *)idPtr);
#else
tHandle = CreateThread(NULL, (DWORD) stackSize,
- (LPTHREAD_START_ROUTINE) proc, (LPVOID) clientData,
- (DWORD) 0, (LPDWORD)idPtr);
+ TclWinThreadStart, winThreadPtr, 0, (LPDWORD)idPtr);
#endif
if (tHandle == NULL) {
diff --git a/win/tclWinTime.c b/win/tclWinTime.c
index daa229d..7045c72 100644
--- a/win/tclWinTime.c
+++ b/win/tclWinTime.c
@@ -280,7 +280,7 @@ NativeGetTime(
Tcl_Time *timePtr,
ClientData clientData)
{
- struct timeb t;
+ struct _timeb t;
int useFtime = 1; /* Flag == TRUE if we need to fall back on
* ftime rather than using the perf counter. */
@@ -446,7 +446,7 @@ NativeGetTime(
* High resolution timer is not available. Just use ftime.
*/
- ftime(&t);
+ _ftime(&t);
timePtr->sec = (long)t.time;
timePtr->usec = t.millitm * 1000;
}
diff --git a/win/tclooConfig.sh b/win/tclooConfig.sh
index dce540a..55fe75f 100644
--- a/win/tclooConfig.sh
+++ b/win/tclooConfig.sh
@@ -15,5 +15,5 @@ TCLOO_LIB_SPEC=""
TCLOO_STUB_LIB_SPEC=""
TCLOO_INCLUDE_SPEC=""
TCLOO_PRIVATE_INCLUDE_SPEC=""
-TCLOO_CFLAGS=-DUSE_TCLOO_STUBS
-TCLOO_VERSION=0.7
+TCLOO_CFLAGS=""
+TCLOO_VERSION=1.0.3
diff --git a/win/tclsh.exe.manifest.in b/win/tclsh.exe.manifest.in
new file mode 100644
index 0000000..8b06fce
--- /dev/null
+++ b/win/tclsh.exe.manifest.in
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"
+ xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
+ <assemblyIdentity
+ version="@TCL_WIN_VERSION@"
+ processorArchitecture="@MACHINE@"
+ name="Tcl.tclsh"
+ type="win32"
+ />
+ <description>Tcl command line shell (tclsh)</description>
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel
+ level="asInvoker"
+ uiAccess="false"
+ />
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!-- Windows 10 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+ <!-- Windows 8.1 -->
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+ <!-- Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ <!-- Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+ <!-- Windows Vista -->
+ <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
+ </application>
+ </compatibility>
+ <asmv3:application>
+ <asmv3:windowsSettings
+ xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
+ <dpiAware>true</dpiAware>
+ </asmv3:windowsSettings>
+ </asmv3:application>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity
+ type="win32"
+ name="Microsoft.Windows.Common-Controls"
+ version="6.0.0.0"
+ processorArchitecture="@MACHINE@"
+ publicKeyToken="6595b64144ccf1df"
+ language="*"
+ />
+ </dependentAssembly>
+ </dependency>
+</assembly>
diff --git a/win/tclsh.rc b/win/tclsh.rc
index 16eaf83..161da50 100644
--- a/win/tclsh.rc
+++ b/win/tclsh.rc
@@ -1,3 +1,4 @@
+//
// Version Resource Script
//
@@ -67,3 +68,15 @@ END
//
tclsh ICON DISCARDABLE "tclsh.ico"
+
+//
+// This is needed for Windows 8.1 onwards.
+//
+
+#ifndef RT_MANIFEST
+#define RT_MANIFEST 24
+#endif
+#ifndef CREATEPROCESS_MANIFEST_RESOURCE_ID
+#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
+#endif
+CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "tclsh.exe.manifest"