summaryrefslogtreecommitdiffstats
path: root/win
diff options
context:
space:
mode:
authorsergey.brester <sergey.brester>2019-02-07 15:45:42 (GMT)
committersergey.brester <sergey.brester>2019-02-07 15:45:42 (GMT)
commit0a876f8e0063525f271dbf8a958fd4c092f655b5 (patch)
tree6396e2e88472fc9b9e0ce83583a1409ecedba739 /win
parentf9abf9b060c15ad2d4b00f99f7814a388875c642 (diff)
parent04ed7f99daa6ac1ca3e5a2903fc7c9325cd4d581 (diff)
downloadtcl-0a876f8e0063525f271dbf8a958fd4c092f655b5.zip
tcl-0a876f8e0063525f271dbf8a958fd4c092f655b5.tar.gz
tcl-0a876f8e0063525f271dbf8a958fd4c092f655b5.tar.bz2
merge 8.6(.9), conflicts resolved
Diffstat (limited to 'win')
-rw-r--r--win/Makefile.in66
-rwxr-xr-xwin/configure5
-rw-r--r--win/configure.in4
-rw-r--r--win/makefile.vc618
-rw-r--r--win/nmakehlp.c140
-rw-r--r--win/rules-ext.vc118
-rw-r--r--win/rules.vc1746
-rw-r--r--win/targets.vc98
-rw-r--r--win/tclAppInit.c4
-rw-r--r--win/tclWin32Dll.c179
-rw-r--r--win/tclWinDde.c319
-rw-r--r--win/tclWinError.c2
-rw-r--r--win/tclWinFCmd.c7
-rwxr-xr-x[-rw-r--r--]win/tclWinFile.c138
-rw-r--r--win/tclWinInit.c53
-rw-r--r--win/tclWinInt.h2
-rw-r--r--win/tclWinLoad.c38
-rw-r--r--win/tclWinPipe.c219
-rw-r--r--win/tclWinPort.h3
-rw-r--r--win/tclWinReg.c87
-rw-r--r--win/tclWinSock.c16
-rw-r--r--win/tclWinTest.c10
-rw-r--r--win/tclWinThrd.c8
-rw-r--r--win/tclWinTime.c9
-rw-r--r--win/tclooConfig.sh2
25 files changed, 2686 insertions, 1205 deletions
diff --git a/win/Makefile.in b/win/Makefile.in
index 71df19a..e6b9801 100644
--- a/win/Makefile.in
+++ b/win/Makefile.in
@@ -116,6 +116,7 @@ GENERIC_DIR_NATIVE = $(shell $(CYGPATH) '$(GENERIC_DIR)')
TOMMATH_DIR_NATIVE = $(shell $(CYGPATH) '$(TOMMATH_DIR)')
WIN_DIR_NATIVE = $(shell $(CYGPATH) '$(WIN_DIR)')
ROOT_DIR_NATIVE = $(shell $(CYGPATH) '$(ROOT_DIR)')
+ROOT_DIR_WIN_NATIVE = $(shell cd '$(ROOT_DIR)' ; pwd -W || pwd -P)
ZLIB_DIR_NATIVE = $(shell $(CYGPATH) '$(ZLIB_DIR)')
#GENERIC_DIR_NATIVE = $(GENERIC_DIR)
#TOMMATH_DIR_NATIVE = $(TOMMATH_DIR)
@@ -145,7 +146,12 @@ DDE_LIB_FILE = @LIBPREFIX@tcldde$(DDEVER)${LIBSUFFIX}
REG_DLL_FILE = tclreg$(REGVER)${DLLSUFFIX}
REG_LIB_FILE = @LIBPREFIX@tclreg$(REGVER)${LIBSUFFIX}
TEST_DLL_FILE = tcltest$(VER)${DLLSUFFIX}
+TEST_EXE_FILE = tcltest${EXESUFFIX}
TEST_LIB_FILE = @LIBPREFIX@tcltest$(VER)${LIBSUFFIX}
+TEST_LOAD_PRMS = package ifneeded dde 1.4.1 [list load [file normalize ${DDE_DLL_FILE}] dde];\
+ package ifneeded registry 1.3.3 [list load [file normalize ${REG_DLL_FILE}] registry]
+TEST_LOAD_FACILITIES = package ifneeded Tcltest ${VERSION}@TCL_PATCH_LEVEL@ [list load [file normalize ${TEST_DLL_FILE}] Tcltest];\
+ $(TEST_LOAD_PRMS)
ZLIB_DLL_FILE = zlib1.dll
SHARED_LIBRARIES = $(TCL_DLL_FILE) @ZLIB_DLL_FILE@
@@ -328,6 +334,7 @@ TOMMATH_OBJS = \
bn_mp_div_3.${OBJEXT} \
bn_mp_exch.${OBJEXT} \
bn_mp_expt_d.${OBJEXT} \
+ bn_mp_expt_d_ex.${OBJEXT} \
bn_mp_grow.${OBJEXT} \
bn_mp_init.${OBJEXT} \
bn_mp_init_copy.${OBJEXT} \
@@ -417,7 +424,34 @@ TCL_DOCS = "$(ROOT_DIR_NATIVE)"/doc/*.[13n]
all: binaries libraries doc packages
-tcltest: $(TCLSH) $(TEST_DLL_FILE)
+# Test-suite helper (can be used to test Tcl from build directory with all expected modules).
+# To start from windows shell use:
+# > tcltest.cmd -verbose bps -file fileName.test
+# or from mingw/msys shell:
+# $ ./tcltest -verbose bps -file fileName.test
+
+tcltest.cmd:
+ @echo 'Create tcltest.cmd helpers';
+ @(\
+ echo '@echo off'; \
+ echo 'rem set LANG=en_US'; \
+ echo 'set BDP=%~dp0'; \
+ echo 'set OWD=%CD%'; \
+ echo 'cd /d %TEMP%'; \
+ echo 'rem "%BDP%\$(TCLSH)" "$(ROOT_DIR_WIN_NATIVE)/tests/all.tcl" $(TESTFLAGS) -load "$(TEST_LOAD_FACILITIES)" %*'; \
+ echo '"%BDP%\$(TEST_EXE_FILE)" "$(ROOT_DIR_WIN_NATIVE)/tests/all.tcl" $(TESTFLAGS) -load "$(TEST_LOAD_PRMS)" %*'; \
+ echo 'cd /d %OWD%'; \
+ ) > tcltest.cmd;
+ @(\
+ echo '#!/bin/sh'; \
+ echo '#LANG=en_US'; \
+ echo 'BDP=$$(dirname $$(readlink -f %0))'; \
+ echo 'cd /tmp'; \
+ echo '#"$$BDP/$(TCLSH)" "$(ROOT_DIR_WIN_NATIVE)/tests/all.tcl" $(TESTFLAGS) -load "$(TEST_LOAD_FACILITIES)" "$$@"'; \
+ echo '"$$BDP/$(TEST_EXE_FILE)" "$(ROOT_DIR_WIN_NATIVE)/tests/all.tcl" $(TESTFLAGS) -load "$(TEST_LOAD_PRMS)" "$$@"'; \
+ ) > tcltest;
+
+tcltest: $(TCLSH) $(TEST_EXE_FILE) $(TEST_DLL_FILE) tcltest.cmd
binaries: $(TCL_STUB_LIB_FILE) @LIBRARIES@ winextensions $(TCLSH)
@@ -466,6 +500,11 @@ ${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)
+${TEST_EXE_FILE}: ${TCL_STUB_LIB_FILE} ${TCLTEST_OBJS} tclTestMain.${OBJEXT}
+ @$(RM) ${TEST_EXE_FILE}
+ $(CC) $(CFLAGS) $(TCLTEST_OBJS) tclTestMain.$(OBJEXT) $(TCL_LIB_FILE) $(TCL_STUB_LIB_FILE) $(LIBS) \
+ tclsh.$(RES) $(CC_EXENAME) $(LDFLAGS_CONSOLE)
+
# use pre-built zlib1.dll
${ZLIB_DLL_FILE}: ${TCL_STUB_LIB_FILE}
@if test "@ZLIB_LIBS@set" != "${ZLIB_DIR_NATIVE}/win32/zdll.libset" ; then \
@@ -483,6 +522,9 @@ ${ZLIB_DLL_FILE}: ${TCL_STUB_LIB_FILE}
# Special case object targets
+tclTestMain.${OBJEXT}: tclAppInit.c
+ $(CC) -c $(CC_SWITCHES) -DTCL_TEST -DBUILD_tcl $(EXTFLAGS) $(CC_OBJNAME) $(WIN_DIR)/tclAppInit.c
+
tclWinInit.${OBJEXT}: tclWinInit.c
$(CC) -c $(CC_SWITCHES) -DBUILD_tcl $(EXTFLAGS) @DEPARG@ $(CC_OBJNAME)
@@ -651,17 +693,17 @@ install-libraries: libraries install-tzdata install-msgs
do \
$(COPY) "$$j" "$(SCRIPT_INSTALL_DIR)/http1.0"; \
done;
- @echo "Installing package http 2.8.11 as a Tcl Module";
- @$(COPY) $(ROOT_DIR)/library/http/http.tcl $(SCRIPT_INSTALL_DIR)/../tcl8/8.6/http-2.8.11.tm;
+ @echo "Installing package http 2.9.0 as a Tcl Module";
+ @$(COPY) $(ROOT_DIR)/library/http/http.tcl $(SCRIPT_INSTALL_DIR)/../tcl8/8.6/http-2.9.0.tm;
@echo "Installing library opt0.4 directory";
@for j in $(ROOT_DIR)/library/opt/*.tcl; \
do \
$(COPY) "$$j" "$(SCRIPT_INSTALL_DIR)/opt0.4"; \
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.4.0 as a Tcl Module";
- @$(COPY) $(ROOT_DIR)/library/tcltest/tcltest.tcl $(SCRIPT_INSTALL_DIR)/../tcl8/8.5/tcltest-2.4.0.tm;
+ @echo "Installing package msgcat 1.6.1 as a Tcl Module";
+ @$(COPY) $(ROOT_DIR)/library/msgcat/msgcat.tcl $(SCRIPT_INSTALL_DIR)/../tcl8/8.5/msgcat-1.6.1.tm;
+ @echo "Installing package tcltest 2.5.0 as a Tcl Module";
+ @$(COPY) $(ROOT_DIR)/library/tcltest/tcltest.tcl $(SCRIPT_INSTALL_DIR)/../tcl8/8.5/tcltest-2.5.0.tm;
@echo "Installing package platform 1.0.14 as a Tcl Module";
@$(COPY) $(ROOT_DIR)/library/platform/platform.tcl $(SCRIPT_INSTALL_DIR)/../tcl8/8.4/platform-1.0.14.tm;
@echo "Installing package platform::shell 1.1.4 as a Tcl Module";
@@ -711,16 +753,12 @@ test: test-tcl test-packages
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.0 [list load [file normalize ${DDE_DLL_FILE}] dde]; \
- package ifneeded registry 1.3.2 [list load [file normalize ${REG_DLL_FILE}] registry]" | ./$(CAT32)
+ -load "$(TEST_LOAD_FACILITIES)" | ./$(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.0 [list load [file normalize ${DDE_DLL_FILE}] dde]; \
- package ifneeded registry 1.3.2 [list load [file normalize ${REG_DLL_FILE}] registry]" $(SCRIPT)
+ ./$(TCLSH) $(TESTFLAGS) -load "$(TEST_LOAD_FACILITIES)" $(SCRIPT)
# This target can be used to run tclsh from the build directory via
# `make shell SCRIPT=foo.tcl`
@@ -744,7 +782,7 @@ cleanhelp:
clean: cleanhelp clean-packages
$(RM) *.lib *.a *.exp *.dll *.$(RES) *.${OBJEXT} *~ \#* TAGS a.out
- $(RM) $(TCLSH) $(CAT32)
+ $(RM) $(TCLSH) $(CAT32) $(TEST_EXE_FILE) $(TEST_DLL_FILE) tcltest.cmd tcltest
$(RM) *.pch *.ilk *.pdb
distclean: distclean-packages clean
diff --git a/win/configure b/win/configure
index 85dc0ba..3024594 100755
--- a/win/configure
+++ b/win/configure
@@ -1311,7 +1311,7 @@ SHELL=/bin/sh
TCL_VERSION=8.6
TCL_MAJOR_VERSION=8
TCL_MINOR_VERSION=6
-TCL_PATCH_LEVEL=".6"
+TCL_PATCH_LEVEL=".9"
VER=$TCL_MAJOR_VERSION$TCL_MINOR_VERSION
TCL_DDE_VERSION=1.4
@@ -4372,7 +4372,7 @@ if test "$tcl_ok" = "yes"; then
ZLIB_DLL_FILE=\${ZLIB_DLL_FILE}
- if test "$do64bit" = "yes"; then
+ if test "$do64bit" != "no"; then
if test "$GCC" == "yes"; then
@@ -5271,6 +5271,7 @@ TCL_WIN_VERSION="$TCL_VERSION.$TCL_RELEASE_LEVEL.`echo $TCL_PATCH_LEVEL | tr -d
+
# win only
diff --git a/win/configure.in b/win/configure.in
index b478d1e..511cb39 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=".6"
+TCL_PATCH_LEVEL=".9"
VER=$TCL_MAJOR_VERSION$TCL_MINOR_VERSION
TCL_DDE_VERSION=1.4
@@ -128,7 +128,7 @@ AS_IF([test "${enable_shared+set}" = "set"], [
])
AS_IF([test "$tcl_ok" = "yes"], [
AC_SUBST(ZLIB_DLL_FILE,[\${ZLIB_DLL_FILE}])
- AS_IF([test "$do64bit" = "yes"], [
+ AS_IF([test "$do64bit" != "no"], [
AS_IF([test "$GCC" == "yes"],[
AC_SUBST(ZLIB_LIBS,[\${ZLIB_DIR_NATIVE}/win64/libz.dll.a])
], [
diff --git a/win/makefile.vc b/win/makefile.vc
index ada08cc..a6709d1 100644
--- a/win/makefile.vc
+++ b/win/makefile.vc
@@ -1,7 +1,6 @@
-#-------------------------------------------------------------
-# makefile.vc --
+#------------------------------------------------------------- -*- makefile -*-
#
-# Microsoft Visual C++ makefile for use with nmake.exe v1.62+ (VC++ 5.0+)
+# Microsoft Visual C++ makefile for building Tcl with nmake
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -11,37 +10,16 @@
# Copyright (c) 2001-2005 ActiveState Corporation.
# Copyright (c) 2001-2004 David Gravereaux.
# Copyright (c) 2003-2008 Pat Thoyts.
+# Copyright (c) 2017 Ashok P. Nadkarni
#------------------------------------------------------------------------------
-# Check to see we are configured to build with MSVC (MSDEVDIR, MSVCDIR or
-# VCINSTALLDIR) or with the MS Platform SDK (MSSDK or WindowsSDKDir)
-!if !defined(MSDEVDIR) && !defined(MSVCDIR) && !defined(VCINSTALLDIR) && !defined(MSSDK) && !defined(WINDOWSSDKDIR)
-MSG = ^
-You need to run vcvars32.bat from Developer Studio or setenv.bat from the^
-Platform SDK first to setup the environment. Jump to this line to read^
-the build instructions.
-!error $(MSG)
-!endif
-
-#------------------------------------------------------------------------------
-# HOW TO USE this makefile:
-#
-# 1) It is now necessary to have MSVCDir, MSDevDir or MSSDK set in the
-# environment. This is used as a check to see if vcvars32.bat had been
-# run prior to running nmake or during the installation of Microsoft
-# Visual C++, MSVCDir had been set globally and the PATH adjusted.
-# Either way is valid.
+# General usage:
+# nmake [-nologo] -f makefile.vc [TARGET|MACRODEF [TARGET|MACRODEF] [...]]
#
-# You'll need to run vcvars32.bat contained in the MsDev's vc(98)/bin
-# directory to setup the proper environment, if needed, for your
-# current setup. This is a needed bootstrap requirement and allows the
-# swapping of different environments to be easier.
+# For MACRODEF, see TIP 477 (https://core.tcl.tk/tips/doc/trunk/tip/477.md)
+# or examine Sections 6-8 in rules.vc.
#
-# 2) To use the Platform SDK (not expressly needed), run setenv.bat after
-# vcvars32.bat according to the instructions for it. This can also
-# turn on the 64-bit compiler, if your SDK has it.
-#
-# 3) Targets are:
+# Possible values of TARGET are:
# release -- Builds the core, the shell and the dlls. (default)
# dlls -- Just builds the windows extensions
# shell -- Just builds the shell and the core.
@@ -61,154 +39,83 @@ the build instructions.
# troff manual pages found in $(ROOT)\doc. You need to
# have installed the HTML Help Compiler package from Microsoft
# to produce the .chm file.
-# winhelp -- (deprecated) Builds the windows .hlp file for Tcl from
-# the troff man files found in $(ROOT)\doc. This type of
-# help file is deprecated by Microsoft in favour of html
-# help files (.chm)
-#
-# 4) Macros usable on the commandline:
-# INSTALLDIR=<path>
-# Sets where to install Tcl from the built binaries.
-# C:\Progra~1\Tcl is assumed when not specified.
-#
-# OPTS=loimpact,msvcrt,nothreads,pdbs,profile,static,staticpkg,symbols,thrdalloc,tclalloc,unchecked,none
-# Sets special options for the core. The default is for none.
-# Any combination of the above may be used (comma separated).
-# 'none' will over-ride everything to nothing.
-#
-# loimpact = Adds a flag for how NT treats the heap to keep memory
-# in use, low. This is said to impact alloc performance.
-# msvcrt = Affects the static option only to switch it from
-# using libcmt(d) as the C runtime [by default] to
-# msvcrt(d). This is useful for static embedding
-# support.
-# nothreads= Turns off full multithreading support.
-# pdbs = Build detached symbols for release builds.
-# profile = Adds profiling hooks. Map file is assumed.
-# static = Builds a static library of the core instead of a
-# dll. The static library will contain the dde and reg
-# extensions. External applications who want to use
-# this, need to link with the stub library as well as
-# the static Tcl library.The shell will be static (and
-# large), as well.
-# staticpkg = Affects the static option only to switch
-# tclshXX.exe to have the dde and reg extension linked
-# inside it.
-# symbols = Debug build. Links to the debug C runtime, disables
-# optimizations and creates pdb symbols files.
-# thrdalloc = Use the thread allocator (shared global free pool)
-# This is the default on threaded builds.
-# tclalloc = Use the old non-thread allocator
-# unchecked= Allows a symbols build to not use the debug
-# enabled runtime (msvcrt.dll not msvcrtd.dll
-# or libcmt.lib not libcmtd.lib).
-#
-# STATS=compdbg,memdbg,none
-# Sets optional memory and bytecode compiler debugging code added
-# to the core. The default is for none. Any combination of the
-# above may be used (comma separated). 'none' will over-ride
-# everything to nothing.
-#
-# compdbg = Enables byte compilation logging.
-# memdbg = Enables the debugging memory allocator.
-#
-# CHECKS=64bit,fullwarn,nodep,none
-# Sets special macros for checking compatibility.
-#
-# 64bit = Enable 64bit portability warnings (if available)
-# fullwarn = Builds with full compiler and link warnings enabled.
-# Very verbose.
-# nodep = Turns off compatibility macros to ensure the core
-# isn't being built with deprecated functions.
-#
-# MACHINE=(ALPHA|AMD64|IA64|IX86)
-# Set the machine type used for the compiler, linker, and
-# resource compiler. This hook is needed to tell the tools
-# when alternate platforms are requested. IX86 is the default
-# when not specified. If the CPU environment variable has been
-# set (ie: recent Platform SDK) then MACHINE is set from CPU.
-#
-# TMP_DIR=<path>
-# OUT_DIR=<path>
-# Hooks to allow the intermediate and output directories to be
-# changed. $(OUT_DIR) is assumed to be
-# $(BINROOT)\(Release|Debug) based on if symbols are requested.
-# $(TMP_DIR) will de $(OUT_DIR)\<buildtype> by default.
-#
-# TESTPAT=<file>
-# Reads the tests requested to be run from this file.
-#
-# CFG_ENCODING=encoding
-# name of encoding for configuration information. Defaults
-# to cp1252
#
-# 5) Examples:
+# The steps to setup a Visual C++ environment depend on which
+# version of Visual Studio and/or the Windows SDK you are building
+# against and are not described here. The simplest method is generally
+# to start a command shell using one of the short cuts installed by
+# Visual Studio/Windows SDK for the appropriate target architecture.
#
-# Basic syntax of calling nmake looks like this:
-# nmake [-nologo] -f makefile.vc [target|macrodef [target|macrodef] [...]]
+# NOTE: For older (Visual C++ 6 or the 2003 SDK), to use the Platform
+# SDK (not expressly needed), run setenv.bat after
+# vcvars32.bat according to the instructions for it. This can also
+# turn on the 64-bit compiler, if your SDK has it.
#
-# Standard (no frills)
-# c:\tcl_src\win\>c:\progra~1\micros~1\vc98\bin\vcvars32.bat
-# Setting environment for using Microsoft Visual C++ tools.
+# Examples:
# c:\tcl_src\win\>nmake -f makefile.vc release
+# c:\tcl_src\win\>nmake -f makefile.vc test
# c:\tcl_src\win\>nmake -f makefile.vc install INSTALLDIR=c:\progra~1\tcl
+# c:\tcl_src\win\>nmake -f makefile.vc release OPTS=pdbs
+# c:\tcl_src\win\>nmake -f makefile.vc release OPTS=symbols
#
-# Building for Win64
-# c:\tcl_src\win\>c:\progra~1\micros~1\vc98\bin\vcvars32.bat
-# Setting environment for using Microsoft Visual C++ tools.
-# c:\tcl_src\win\>c:\progra~1\platfo~1\setenv.bat /pre64 /RETAIL
-# Targeting Windows pre64 RETAIL
-# c:\tcl_src\win\>nmake -f makefile.vc MACHINE=IA64
-#
-#------------------------------------------------------------------------------
-#==============================================================================
-###############################################################################
+# NOTE:
+# Before modifying this file, check whether the modification is applicable
+# to building extensions as well and if so, modify rules.vc instead.
-# //==================================================================\\
-# >>[ -> Do not modify below this line. <- ]<<
-# >>[ Please, use the commandline macros to modify how Tcl is built. ]<<
-# >>[ If you need more features, send us a patch for more macros. ]<<
-# \\==================================================================//
-
+# The PROJECT macro is used by rules.vc for generating appropriate
+# macros and rules.
+PROJECT = tcl
-###############################################################################
-#==============================================================================
-#------------------------------------------------------------------------------
+# Default target to build if no target is specified. If unspecified, the
+# rules.vc file will set up "all" as the target.
+DEFAULT_BUILD_TARGET = release
-!if !exist("makefile.vc")
-MSG = ^
-You must run this makefile only from the directory it is in.^
-Please `cd` to its location first.
-!error $(MSG)
-!endif
+# We want to use our own resource file, not the standard template one.
+RCFILE = tcl.rc
-PROJECT = tcl
+# The rules.vc file does most of the hard work in terms of defining
+# the build configuration, macros, output directories etc.
!include "rules.vc"
-STUBPREFIX = $(PROJECT)stub
+# Tcl version info based on macros set up by rules.vc
DOTVERSION = $(TCL_MAJOR_VERSION).$(TCL_MINOR_VERSION)
VERSION = $(TCL_MAJOR_VERSION)$(TCL_MINOR_VERSION)
+# We need versions of various core packages to generate appropriate
+# file names during installation.
+!if [echo REM = This file is generated from makefile.vc > versions.vc]
+!endif
+!if [echo PKG_HTTP_VER = \>> versions.vc] \
+ && [nmakehlp -V ..\library\http\pkgIndex.tcl http >> versions.vc]
+!endif
+!if [echo PKG_TCLTEST_VER = \>> versions.vc] \
+ && [nmakehlp -V ..\library\tcltest\pkgIndex.tcl tcltest >> versions.vc]
+!endif
+!if [echo PKG_MSGCAT_VER = \>> versions.vc] \
+ && [nmakehlp -V ..\library\msgcat\pkgIndex.tcl msgcat >> versions.vc]
+!endif
+!if [echo PKG_PLATFORM_VER = \>> versions.vc] \
+ && [nmakehlp -V ..\library\platform\pkgIndex.tcl "platform " >> versions.vc]
+!endif
+!if [echo PKG_SHELL_VER = \>> versions.vc] \
+ && [nmakehlp -V ..\library\platform\pkgIndex.tcl "platform::shell" >> versions.vc]
+!endif
+!if [echo PKG_DDE_VER = \>> versions.vc] \
+ && [nmakehlp -V ..\library\dde\pkgIndex.tcl "dde " >> versions.vc]
+!endif
+!if [echo PKG_REG_VER =\>> versions.vc] \
+ && [nmakehlp -V ..\library\reg\pkgIndex.tcl registry >> versions.vc]
+!endif
+
+!include versions.vc
+
DDEDOTVERSION = 1.4
DDEVERSION = $(DDEDOTVERSION:.=)
REGDOTVERSION = 1.3
REGVERSION = $(REGDOTVERSION:.=)
-BINROOT = $(MAKEDIR) # originally .
-ROOT = $(MAKEDIR)\.. # originally ..
-
-TCLIMPLIB = $(OUT_DIR)\$(PROJECT)$(VERSION)$(SUFX).lib
-TCLLIBNAME = $(PROJECT)$(VERSION)$(SUFX).$(EXT)
-TCLLIB = $(OUT_DIR)\$(TCLLIBNAME)
-
-TCLSTUBLIBNAME = $(STUBPREFIX)$(VERSION).lib
-TCLSTUBLIB = $(OUT_DIR)\$(TCLSTUBLIBNAME)
-
-TCLSHNAME = $(PROJECT)sh$(VERSION)$(SUFX).exe
-TCLSH = $(OUT_DIR)\$(TCLSHNAME)
-
TCLREGLIBNAME = $(PROJECT)reg$(REGVERSION)$(SUFX:t=).$(EXT)
TCLREGLIB = $(OUT_DIR)\$(TCLREGLIBNAME)
@@ -218,22 +125,6 @@ TCLDDELIB = $(OUT_DIR)\$(TCLDDELIBNAME)
TCLTEST = $(OUT_DIR)\$(PROJECT)test.exe
CAT32 = $(OUT_DIR)\cat32.exe
-# Can we run what we build? IX86 runs on all architectures.
-!ifndef TCLSH_NATIVE
-!if "$(MACHINE)" == "IX86" || "$(MACHINE)" == "$(NATIVE_ARCH)"
-TCLSH_NATIVE = $(TCLSH)
-!else
-!error You must explicitly set TCLSH_NATIVE for cross-compilation
-!endif
-!endif
-
-### Make sure we use backslash only.
-LIB_INSTALL_DIR = $(_INSTALLDIR)\lib
-BIN_INSTALL_DIR = $(_INSTALLDIR)\bin
-DOC_INSTALL_DIR = $(_INSTALLDIR)\doc
-SCRIPT_INSTALL_DIR = $(_INSTALLDIR)\lib\tcl$(DOTVERSION)
-INCLUDE_INSTALL_DIR = $(_INSTALLDIR)\include
-
TCLSHOBJS = \
$(TMP_DIR)\tclAppInit.obj \
!if !$(STATIC_BUILD)
@@ -383,6 +274,7 @@ TOMMATHOBJS = \
$(TMP_DIR)\bn_mp_div_3.obj \
$(TMP_DIR)\bn_mp_exch.obj \
$(TMP_DIR)\bn_mp_expt_d.obj \
+ $(TMP_DIR)\bn_mp_expt_d_ex.obj \
$(TMP_DIR)\bn_mp_grow.obj \
$(TMP_DIR)\bn_mp_init.obj \
$(TMP_DIR)\bn_mp_init_copy.obj \
@@ -454,113 +346,18 @@ TCLSTUBOBJS = \
$(TMP_DIR)\tclTomMathStubLib.obj \
$(TMP_DIR)\tclOOStubLib.obj
-### The following paths CANNOT have spaces in them.
-COMPATDIR = $(ROOT)\compat
-DOCDIR = $(ROOT)\doc
-GENERICDIR = $(ROOT)\generic
+### The following paths CANNOT have spaces in them as they appear on
+### the left side of implicit rules.
TOMMATHDIR = $(ROOT)\libtommath
-TOOLSDIR = $(ROOT)\tools
-WINDIR = $(ROOT)\win
PKGSDIR = $(ROOT)\pkgs
-#---------------------------------------------------------------------
-# Compile flags
-#---------------------------------------------------------------------
-
-!if !$(DEBUG)
-!if $(OPTIMIZING)
-### This cranks the optimization level to maximize speed
-cdebug = -O2 $(OPTIMIZATIONS)
-!else
-cdebug =
-!endif
-!if $(SYMBOLS)
-cdebug = $(cdebug) -Zi
-!endif
-!else if "$(MACHINE)" == "IA64" || "$(MACHINE)" == "AMD64"
-### Warnings are too many, can't support warnings into errors.
-cdebug = -Zi -Od $(DEBUGFLAGS)
-!else
-cdebug = -Zi -WX $(DEBUGFLAGS)
-!endif
-
-### Declarations common to all compiler options
-cwarn = $(WARNINGS) -D _CRT_SECURE_NO_DEPRECATE -D _CRT_NONSTDC_NO_DEPRECATE
-cflags = -nologo -c $(COMPILERFLAGS) $(cwarn) -Fp$(TMP_DIR)^\
-
-!if $(MSVCRT)
-!if $(DEBUG) && !$(UNCHECKED)
-crt = -MDd
-!else
-crt = -MD
-!endif
-!else
-!if $(DEBUG) && !$(UNCHECKED)
-crt = -MTd
-!else
-crt = -MT
-!endif
-!endif
-
-TCL_INCLUDES = -I"$(WINDIR)" -I"$(GENERICDIR)" -I"$(TOMMATHDIR)"
-TCL_DEFINES = -DTCL_TOMMATH -DMP_PREC=4 -Dinline=__inline -DHAVE_ZLIB=1
-BASE_CFLAGS = $(cflags) $(cdebug) $(crt) $(TCL_INCLUDES) $(TCL_DEFINES)
-CON_CFLAGS = $(cflags) $(cdebug) $(crt) -DCONSOLE
-TCL_CFLAGS = $(BASE_CFLAGS) $(OPTDEFINES)
-STUB_CFLAGS = $(cflags) $(cdebug) $(OPTDEFINES)
-
-
-#---------------------------------------------------------------------
-# Link flags
-#---------------------------------------------------------------------
-
-!if $(DEBUG)
-ldebug = -debug -debugtype:cv
-!else
-ldebug = -release -opt:ref -opt:icf,3
-!if $(SYMBOLS)
-ldebug = $(ldebug) -debug -debugtype:cv
-!endif
-!endif
-
-### Declarations common to all linker options
-lflags = -nologo -machine:$(MACHINE) $(LINKERFLAGS) $(ldebug)
-
-!if $(PROFILE)
-lflags = $(lflags) -profile
-!endif
-
-!if $(MSVCRT) && !($(DEBUG) && !$(UNCHECKED)) && $(VCVERSION) >= 1900
-lflags = $(lflags) -nodefaultlib:libucrt.lib
-!endif
+# Additional include and C macro definitions for the implicit rules
+# defined in rules.vc
+PRJ_INCLUDES = -I"$(TOMMATHDIR)"
+PRJ_DEFINES = -DTCL_TOMMATH -DMP_PREC=4 -Dinline=__inline -DHAVE_ZLIB=1 -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE
-!if $(ALIGN98_HACK) && !$(STATIC_BUILD)
-### Align sections for PE size savings.
-lflags = $(lflags) -opt:nowin98
-!else if !$(ALIGN98_HACK) && $(STATIC_BUILD)
-### Align sections for speed in loading by choosing the virtual page size.
-lflags = $(lflags) -align:4096
-!endif
-
-!if $(LOIMPACT)
-lflags = $(lflags) -ws:aggressive
-!endif
-
-dlllflags = $(lflags) -dll
-conlflags = $(lflags) -subsystem:console
-guilflags = $(lflags) -subsystem:windows
-
-baselibs = netapi32.lib kernel32.lib user32.lib advapi32.lib userenv.lib ws2_32.lib
-# Avoid 'unresolved external symbol __security_cookie' errors.
-# c.f. http://support.microsoft.com/?id=894573
-!if "$(MACHINE)" == "IA64" || "$(MACHINE)" == "AMD64"
-!if $(VCVERSION) > 1399 && $(VCVERSION) < 1500
-baselibs = $(baselibs) bufferoverflowU.lib
-!endif
-!endif
-!if $(MSVCRT) && !($(DEBUG) && !$(UNCHECKED)) && $(VCVERSION) >= 1900
-baselibs = $(baselibs) ucrt.lib
-!endif
+# Additional Link libraries needed beyond those in rules.vc
+PRJ_LIBS = netapi32.lib user32.lib userenv.lib ws2_32.lib
#---------------------------------------------------------------------
# TclTest flags
@@ -582,23 +379,15 @@ dlls: setup $(TCLREGLIB) $(TCLDDELIB)
all: setup $(TCLSH) $(TCLSTUBLIB) dlls $(CAT32) pkgs
tcltest: setup $(TCLTEST) dlls $(CAT32)
install: install-binaries install-libraries install-docs install-pkgs
+setup: default-setup
test: test-core test-pkgs
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.0 [list load "$(TCLDDELIB:\=/)" dde]
- package ifneeded registry 1.3.2 [list load "$(TCLREGLIB:\=/)" registry]
+ package ifneeded dde 1.4.1 [list load "$(TCLDDELIB:\=/)" dde]
+ package ifneeded registry 1.3.3 [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.0 "$(TCLDDELIB:\=/)" dde]
- package ifneeded registry 1.3.2 "$(TCLREGLIB:\=/)" registry]
-<<
- type tests.log | more
-!endif
runtest: setup $(TCLTEST) dlls $(CAT32)
set TCL_LIBRARY=$(ROOT:\=/)/library
@@ -608,55 +397,51 @@ runshell: setup $(TCLSH) dlls
set TCL_LIBRARY=$(ROOT:\=/)/library
$(DEBUGGER) $(TCLSH) $(SCRIPT)
-setup:
- @if not exist $(OUT_DIR)\nul mkdir $(OUT_DIR)
- @if not exist $(TMP_DIR)\nul mkdir $(TMP_DIR)
-
-!if !$(STATIC_BUILD)
-$(TCLIMPLIB): $(TCLLIB)
-!endif
+!if $(STATIC_BUILD)
$(TCLLIB): $(TCLOBJS)
-!if $(STATIC_BUILD)
- $(lib32) -nologo $(LINKERFLAGS) -out:$@ @<<
+ $(LIBCMD) @<<
$**
<<
+
!else
- $(link32) $(dlllflags) -base:@$(WINDIR)\coffbase.txt,tcl -out:$@ \
- $(baselibs) @<<
+
+$(TCLLIB): $(TCLOBJS)
+ $(DLLCMD) @<<
$**
<<
$(_VC_MANIFEST_EMBED_DLL)
-!endif
+$(TCLIMPLIB): $(TCLLIB)
+
+!endif # $(STATIC_BUILD)
+
$(TCLSTUBLIB): $(TCLSTUBOBJS)
- $(lib32) -nologo $(LINKERFLAGS) -nodefaultlib -out:$@ $(TCLSTUBOBJS)
+ $(LIBCMD) -nodefaultlib $(TCLSTUBOBJS)
$(TCLSH): $(TCLSHOBJS) $(TCLSTUBLIB) $(TCLIMPLIB)
- $(link32) $(conlflags) -stack:2300000 -out:$@ $(baselibs) $**
+ $(CONEXECMD) -stack:2300000 $**
$(_VC_MANIFEST_EMBED_EXE)
$(TCLTEST): $(TCLTESTOBJS) $(TCLSTUBLIB) $(TCLIMPLIB)
- $(link32) $(conlflags) -stack:2300000 -out:$@ $(baselibs) $**
+ $(CONEXECMD) -stack:2300000 $**
$(_VC_MANIFEST_EMBED_EXE)
!if $(STATIC_BUILD)
$(TCLDDELIB): $(TMP_DIR)\tclWinDde.obj
- $(lib32) -nologo $(LINKERFLAGS) -out:$@ $**
+ $(LIBCMD) $**
!else
$(TCLDDELIB): $(TMP_DIR)\tclWinDde.obj $(TCLSTUBLIB)
- $(link32) $(dlllflags) -base:@$(WINDIR)\coffbase.txt,tcldde -out:$@ \
- $** $(baselibs)
+ $(DLLCMD) $**
$(_VC_MANIFEST_EMBED_DLL)
!endif
!if $(STATIC_BUILD)
$(TCLREGLIB): $(TMP_DIR)\tclWinReg.obj
- $(lib32) -nologo $(LINKERFLAGS) -out:$@ $**
+ $(LIBCMD) $**
!else
$(TCLREGLIB): $(TMP_DIR)\tclWinReg.obj $(TCLSTUBLIB)
- $(link32) $(dlllflags) -base:@$(WINDIR)\coffbase.txt,tclreg -out:$@ \
- $** $(baselibs)
+ $(DLLCMD) $**
$(_VC_MANIFEST_EMBED_DLL)
!endif
@@ -693,9 +478,8 @@ clean-pkgs:
)
$(CAT32): $(WINDIR)\cat.c
- $(cc32) $(CON_CFLAGS) -Fo$(TMP_DIR)\ $?
- $(link32) $(conlflags) -out:$@ -stack:16384 $(TMP_DIR)\cat.obj \
- $(baselibs)
+ $(cc32) $(cflags) $(crt) -D_CRT_NONSTDC_NO_DEPRECATE -DCONSOLE -Fo$(TMP_DIR)\ $?
+ $(CONEXECMD) -stack:16384 $(TMP_DIR)\cat.obj
$(_VC_MANIFEST_EMBED_EXE)
#---------------------------------------------------------------------
@@ -733,9 +517,12 @@ gentommath_h:
# Build the Windows HTML help file.
#---------------------------------------------------------------------
-# NOTE: you can define HHC on the command-line to override this
-!ifndef HHC
-HHC=""%ProgramFiles%\HTML Help Workshop\hhc.exe""
+# NOTE: you can define HHC on the command-line to override this.
+# nmake does not set macro values if already set on the command line.
+!if defined(PROCESSOR_ARCHITECTURE) && "$(PROCESSOR_ARCHITECTURE)" == "AMD64"
+HHC="%ProgramFiles(x86)%\HTML Help Workshop\hhc.exe"
+!else
+HHC="%ProgramFiles%\HTML Help Workshop\hhc.exe"
!endif
HTMLDIR=$(OUT_DIR)\html
HTMLBASE=TclTk$(VERSION)
@@ -747,7 +534,7 @@ htmlhelp: chmsetup $(CHMFILE)
$(CHMFILE): $(DOCDIR)\*
@$(TCLSH) $(TOOLSDIR)\tcltk-man2html.tcl "--htmldir=$(HTMLDIR)"
@echo Compiling HTML help project
- -$(HHC) <<$(HHPFILE) >NUL
+ -"$(HHC)" <<$(HHPFILE) >NUL
[OPTIONS]
Compatibility=1.1 or later
Compiled file=$(HTMLBASE).chm
@@ -756,7 +543,7 @@ Display compile progress=no
Error log file=$(HTMLBASE).log
Full-text search=Yes
Language=0x409 English (United States)
-Title=Tcl/Tk $(DOT_VERSION) Help
+Title=Tcl/Tk $(DOTVERSION) Help
[FILES]
contents.htm
docs.css
@@ -771,76 +558,27 @@ UserCmd\*.htm
chmsetup:
@if not exist $(HTMLDIR)\nul mkdir $(HTMLDIR)
-#-------------------------------------------------------------------------
-# Build the old-style Windows .hlp file
-#-------------------------------------------------------------------------
-
-TCLHLPBASE = $(PROJECT)$(VERSION)
-HELPFILE = $(OUT_DIR)\$(TCLHLPBASE).hlp
-HELPCNT = $(OUT_DIR)\$(TCLHLPBASE).cnt
-DOCTMP_DIR = $(OUT_DIR)\$(PROJECT)_docs
-HELPRTF = $(DOCTMP_DIR)\$(PROJECT).rtf
-MAN2HELP = $(DOCTMP_DIR)\man2help.tcl
-MAN2HELP2 = $(DOCTMP_DIR)\man2help2.tcl
-INDEX = $(DOCTMP_DIR)\index.tcl
-BMP = $(DOCTMP_DIR)\feather.bmp
-BMP_NOPATH = feather.bmp
-MAN2TCL = $(DOCTMP_DIR)\man2tcl.exe
-
-winhelp: docsetup $(HELPFILE)
-
-docsetup:
- @if not exist $(DOCTMP_DIR)\nul mkdir $(DOCTMP_DIR)
-
-$(MAN2HELP) $(MAN2HELP2) $(INDEX) $(BMP): $(TOOLSDIR)\$$(@F)
- @$(CPY) $(TOOLSDIR)\$(@F) $(@D)
-
-$(HELPFILE): $(HELPRTF) $(BMP)
- cd $(DOCTMP_DIR)
- start /wait hcrtf.exe -x <<$(PROJECT).hpj
-[OPTIONS]
-COMPRESS=12 Hall Zeck
-LCID=0x409 0x0 0x0 ; English (United States)
-TITLE=Tcl/Tk Reference Manual
-BMROOT=.
-CNT=$(@B).cnt
-HLP=$(@B).hlp
-
-[FILES]
-$(PROJECT).rtf
-
-[WINDOWS]
-main="Tcl/Tk Reference Manual",,27648,(r15263976),(r65535)
-
-[CONFIG]
-BrowseButtons()
-CreateButton(1, "Web", ExecFile("http://www.tcl.tk"))
-CreateButton(2, "SF", ExecFile("http://sf.net/projects/tcl"))
-CreateButton(3, "Wiki", ExecFile("http://wiki.tcl.tk"))
-CreateButton(4, "FAQ", ExecFile("http://www.purl.org/NET/Tcl-FAQ/"))
-<<
- cd $(MAKEDIR)
- @$(CPY) "$(DOCTMP_DIR)\$(@B).hlp" "$(OUT_DIR)"
- @$(CPY) "$(DOCTMP_DIR)\$(@B).cnt" "$(OUT_DIR)"
-
-$(MAN2TCL): $(TOOLSDIR)\$$(@B).c
- $(cc32) $(TCL_CFLAGS) -Fo$(@D)\ $(TOOLSDIR)\$(@B).c
- $(link32) $(conlflags) -out:$@ -stack:16384 $(@D)\man2tcl.obj
- $(_VC_MANIFEST_EMBED_EXE)
-
-$(HELPRTF): $(MAN2TCL) $(MAN2HELP) $(MAN2HELP2) $(INDEX) $(DOCDIR)\*
- $(TCLSH) $(MAN2HELP) -bitmap $(BMP_NOPATH) $(PROJECT) $(VERSION) $(DOCDIR:\=/)
-
install-docs:
!if exist("$(CHMFILE)")
@echo Installing compiled HTML help
@$(CPY) "$(CHMFILE)" "$(DOC_INSTALL_DIR)\"
!endif
-!if exist("$(HELPFILE)")
- @echo Installing Windows help
- @$(CPY) "$(HELPFILE)" "$(DOC_INSTALL_DIR)\"
- @$(CPY) "$(HELPCNT)" "$(DOC_INSTALL_DIR)\"
-!endif
+
+# "emacs font-lock highlighting fix
+
+#---------------------------------------------------------------------
+# Generate the tcl.nmake file which contains the options used to build
+# Tcl itself. This is used when building extensions.
+#---------------------------------------------------------------------
+tcl-nmake: $(OUT_DIR)\tcl.nmake
+$(OUT_DIR)\tcl.nmake:
+ @type << >$@
+CORE_MACHINE = $(MACHINE)
+CORE_DEBUG = $(DEBUG)
+CORE_TCL_THREADS = $(TCL_THREADS)
+CORE_USE_THREAD_ALLOC = $(USE_THREAD_ALLOC)
+CORE_USE_WIDECHAR_API = $(USE_WIDECHAR_API)
+<<
#---------------------------------------------------------------------
# Build tclConfig.sh for the TEA build system.
@@ -848,6 +586,7 @@ install-docs:
tclConfig: $(OUT_DIR)\tclConfig.sh
+# TBD - is this tclConfig.sh file ever used? The values are incorrect!
$(OUT_DIR)\tclConfig.sh: $(WINDIR)\tclConfig.sh.in
@echo Creating tclConfig.sh
@nmakehlp -s << $** >$@
@@ -857,7 +596,7 @@ $(OUT_DIR)\tclConfig.sh: $(WINDIR)\tclConfig.sh.in
@TCL_MINOR_VERSION@ $(TCL_MINOR_VERSION)
@TCL_PATCH_LEVEL@ $(TCL_PATCH_LEVEL)
@CC@ $(CC)
-@DEFS@ $(TCL_CFLAGS)
+@DEFS@ $(pkgcflags)
@CFLAGS_DEBUG@ -nologo -c -W3 -YX -Fp$(TMP_DIR)\ -MDd
@CFLAGS_OPTIMIZE@ -nologo -c -W3 -YX -Fp$(TMP_DIR)\ -MD
@LDFLAGS_DEBUG@ -nologo -machine:$(MACHINE) -debug -debugtype:cv
@@ -865,7 +604,7 @@ $(OUT_DIR)\tclConfig.sh: $(WINDIR)\tclConfig.sh.in
@TCL_DBGX@ $(SUFX)
@TCL_LIB_FILE@ $(PROJECT)$(VERSION)$(SUFX).lib
@TCL_NEEDS_EXP_FILE@
-@LIBS@ $(baselibs)
+@LIBS@ $(baselibs) $(PRJ_LIBS)
@prefix@ $(_INSTALLDIR)
@exec_prefix@ $(BIN_INSTALL_DIR)
@SHLIB_CFLAGS@
@@ -874,7 +613,7 @@ $(OUT_DIR)\tclConfig.sh: $(WINDIR)\tclConfig.sh.in
@EXTRA_CFLAGS@ -YX
@SHLIB_LD@ $(link32) $(dlllflags)
@STLIB_LD@ $(lib32) -nologo
-@SHLIB_LD_LIBS@ $(baselibs)
+@SHLIB_LD_LIBS@ $(baselibs) $(PRJ_LIBS)
@SHLIB_SUFFIX@ .dll
@DL_LIBS@
@LDFLAGS@
@@ -925,28 +664,28 @@ gendate:
#---------------------------------------------------------------------
$(TMP_DIR)\testMain.obj: $(WINDIR)\tclAppInit.c
- $(cc32) $(TCL_CFLAGS) -DTCL_TEST \
+ $(cc32) $(appcflags) -DTCL_TEST \
-DTCL_USE_STATIC_PACKAGES=$(TCL_USE_STATIC_PACKAGES) \
-Fo$@ $?
$(TMP_DIR)\tclMain2.obj: $(GENERICDIR)\tclMain.c
- $(cc32) $(TCL_CFLAGS) -DBUILD_tcl -DTCL_ASCII_MAIN \
+ $(cc32) $(pkgcflags) -DTCL_ASCII_MAIN \
-Fo$@ $?
$(TMP_DIR)\tclTest.obj: $(GENERICDIR)\tclTest.c
- $(cc32) $(TCL_CFLAGS) -Fo$@ $?
+ $(cc32) $(appcflags) -Fo$@ $?
$(TMP_DIR)\tclTestObj.obj: $(GENERICDIR)\tclTestObj.c
- $(cc32) $(TCL_CFLAGS) -Fo$@ $?
+ $(cc32) $(appcflags) -Fo$@ $?
$(TMP_DIR)\tclWinTest.obj: $(WINDIR)\tclWinTest.c
- $(cc32) $(TCL_CFLAGS) -Fo$@ $?
+ $(CCAPPCMD) $?
$(TMP_DIR)\tclZlib.obj: $(GENERICDIR)\tclZlib.c
- $(cc32) $(TCL_CFLAGS) -I$(COMPATDIR)\zlib -DBUILD_tcl -Fo$@ $?
+ $(cc32) $(pkgcflags) -I$(COMPATDIR)\zlib -Fo$@ $?
$(TMP_DIR)\tclPkgConfig.obj: $(GENERICDIR)\tclPkgConfig.c
- $(cc32) -DBUILD_tcl $(TCL_CFLAGS) \
+ $(cc32) $(pkgcflags) \
-DCFG_INSTALL_LIBDIR="\"$(LIB_INSTALL_DIR:\=\\)\"" \
-DCFG_INSTALL_BINDIR="\"$(BIN_INSTALL_DIR:\=\\)\"" \
-DCFG_INSTALL_SCRDIR="\"$(SCRIPT_INSTALL_DIR:\=\\)\"" \
@@ -960,7 +699,7 @@ $(TMP_DIR)\tclPkgConfig.obj: $(GENERICDIR)\tclPkgConfig.c
-Fo$@ $?
$(TMP_DIR)\tclAppInit.obj: $(WINDIR)\tclAppInit.c
- $(cc32) $(TCL_CFLAGS) \
+ $(cc32) $(appcflags) \
-DTCL_USE_STATIC_PACKAGES=$(TCL_USE_STATIC_PACKAGES) \
-Fo$@ $?
@@ -969,17 +708,17 @@ $(TMP_DIR)\tclAppInit.obj: $(WINDIR)\tclAppInit.c
$(TMP_DIR)\tclWinReg.obj: $(WINDIR)\tclWinReg.c
!if $(STATIC_BUILD)
- $(cc32) $(TCL_CFLAGS) -DTCL_THREADS=1 -DSTATIC_BUILD -Fo$@ $?
+ $(cc32) $(appcflags) -DSTATIC_BUILD -Fo$@ $?
!else
- $(cc32) $(TCL_CFLAGS) -DTCL_THREADS=1 -DUSE_TCL_STUBS -Fo$@ $?
+ $(cc32) $(appcflags) -DUSE_TCL_STUBS -Fo$@ $?
!endif
$(TMP_DIR)\tclWinDde.obj: $(WINDIR)\tclWinDde.c
!if $(STATIC_BUILD)
- $(cc32) $(TCL_CFLAGS) -DTCL_THREADS=1 -DSTATIC_BUILD -Fo$@ $?
+ $(cc32) $(appcflags) -DSTATIC_BUILD -Fo$@ $?
!else
- $(cc32) $(TCL_CFLAGS) -DTCL_THREADS=1 -DUSE_TCL_STUBS -Fo$@ $?
+ $(cc32) $(appcflags) -DUSE_TCL_STUBS -Fo$@ $?
!endif
@@ -988,13 +727,13 @@ $(TMP_DIR)\tclWinDde.obj: $(WINDIR)\tclWinDde.c
### specific C run-time.
$(TMP_DIR)\tclStubLib.obj: $(GENERICDIR)\tclStubLib.c
- $(cc32) $(STUB_CFLAGS) -Zl -DSTATIC_BUILD $(TCL_INCLUDES) -Fo$@ $?
+ $(cc32) $(stubscflags) -Fo$@ $?
$(TMP_DIR)\tclTomMathStubLib.obj: $(GENERICDIR)\tclTomMathStubLib.c
- $(cc32) $(STUB_CFLAGS) -Zl -DSTATIC_BUILD $(TCL_INCLUDES) -Fo$@ $?
+ $(cc32) $(stubscflags) -Fo$@ $?
$(TMP_DIR)\tclOOStubLib.obj: $(GENERICDIR)\tclOOStubLib.c
- $(cc32) $(STUB_CFLAGS) -Zl -DSTATIC_BUILD $(TCL_INCLUDES) -Fo$@ $?
+ $(cc32) $(stubscflags) -Fo$@ $?
$(TMP_DIR)\tclsh.exe.manifest: $(WINDIR)\tclsh.exe.manifest.in
@nmakehlp -s << $** >$@
@@ -1015,7 +754,7 @@ depend:
@echo Build tclsh first!
!else
$(TCLSH) $(TOOLSDIR:\=/)/mkdepend.tcl -vc32 -out:"$(OUT_DIR)\depend.mk" \
- -passthru:"-DBUILD_tcl $(TCL_INCLUDES)" $(GENERICDIR),$$(GENERICDIR) \
+ -passthru:"-DBUILD_tcl $(TCL_INCLUDES) $(PRJ_INCLUDES)" $(GENERICDIR),$$(GENERICDIR) \
$(COMPATDIR),$$(COMPATDIR) $(TOMMATHDIR),$$(TOMMATHDIR) $(WINDIR),$$(WINDIR) @<<
$(TCLOBJS)
<<
@@ -1037,47 +776,23 @@ $(TCLOBJS)
#---------------------------------------------------------------------
-# Implicit rules. A limitation exists with nmake that requires that
+# Implicit rules that are not covered by the common ones defined in
+# rules.vc. A limitation exists with nmake that requires that
# source directory can not contain spaces in the path. This an
# absolute.
#---------------------------------------------------------------------
-{$(WINDIR)}.c{$(TMP_DIR)}.obj::
- $(cc32) $(TCL_CFLAGS) -DBUILD_tcl -Fo$(TMP_DIR)\ @<<
-$<
-<<
-
{$(TOMMATHDIR)}.c{$(TMP_DIR)}.obj::
- $(cc32) $(TCL_CFLAGS) -DBUILD_tcl -Fo$(TMP_DIR)\ @<<
-$<
-<<
-
-{$(GENERICDIR)}.c{$(TMP_DIR)}.obj::
- $(cc32) $(TCL_CFLAGS) -DBUILD_tcl -Fo$(TMP_DIR)\ @<<
-$<
-<<
-
-{$(COMPATDIR)}.c{$(TMP_DIR)}.obj::
- $(cc32) $(TCL_CFLAGS) -DBUILD_tcl -Fo$(TMP_DIR)\ @<<
+ $(cc32) $(pkgcflags) -Fo$(TMP_DIR)\ @<<
$<
<<
{$(COMPATDIR)\zlib}.c{$(TMP_DIR)}.obj::
- $(cc32) $(TCL_CFLAGS) -DBUILD_tcl -Fo$(TMP_DIR)\ @<<
+ $(cc32) $(pkgcflags) -Fo$(TMP_DIR)\ @<<
$<
<<
-{$(WINDIR)}.rc{$(TMP_DIR)}.res:
- $(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
+$(TMP_DIR)\tclsh.res: $(TMP_DIR)\tclsh.exe.manifest $(WINDIR)\tclsh.rc
#---------------------------------------------------------------------
@@ -1098,21 +813,21 @@ install-binaries:
@echo Installing $(TCLSTUBLIBNAME)
@$(CPY) "$(TCLSTUBLIB)" "$(LIB_INSTALL_DIR)\"
-#" emacs fix
-
-install-libraries: tclConfig install-msgs install-tzdata
- @if not exist "$(SCRIPT_INSTALL_DIR)$(NULL)" \
+install-libraries: tclConfig tcl-nmake install-msgs install-tzdata
+ @if not exist "$(SCRIPT_INSTALL_DIR)" \
$(MKDIR) "$(SCRIPT_INSTALL_DIR)"
- @if not exist "$(SCRIPT_INSTALL_DIR)\..\tcl8$(NULL)" \
+ @if not exist "$(SCRIPT_INSTALL_DIR)\..\tcl8" \
$(MKDIR) "$(SCRIPT_INSTALL_DIR)\..\tcl8"
- @if not exist "$(SCRIPT_INSTALL_DIR)\..\tcl8\8.4$(NULL)" \
+ @if not exist "$(SCRIPT_INSTALL_DIR)\..\tcl8\8.4" \
$(MKDIR) "$(SCRIPT_INSTALL_DIR)\..\tcl8\8.4"
- @if not exist "$(SCRIPT_INSTALL_DIR)\..\tcl8\8.4\platform$(NULL)" \
+ @if not exist "$(SCRIPT_INSTALL_DIR)\..\tcl8\8.4\platform" \
$(MKDIR) "$(SCRIPT_INSTALL_DIR)\..\tcl8\8.4\platform"
- @if not exist "$(SCRIPT_INSTALL_DIR)\..\tcl8\8.5$(NULL)" \
+ @if not exist "$(SCRIPT_INSTALL_DIR)\..\tcl8\8.5" \
$(MKDIR) "$(SCRIPT_INSTALL_DIR)\..\tcl8\8.5"
- @if not exist "$(SCRIPT_INSTALL_DIR)\..\tcl8\8.6$(NULL)" \
+ @if not exist "$(SCRIPT_INSTALL_DIR)\..\tcl8\8.6" \
$(MKDIR) "$(SCRIPT_INSTALL_DIR)\..\tcl8\8.6"
+ @if not exist "$(LIB_INSTALL_DIR)\nmake" \
+ $(MKDIR) "$(LIB_INSTALL_DIR)\nmake"
@echo Installing header files
@$(CPY) "$(GENERICDIR)\tcl.h" "$(INCLUDE_INSTALL_DIR)\"
@$(CPY) "$(GENERICDIR)\tclDecls.h" "$(INCLUDE_INSTALL_DIR)\"
@@ -1136,6 +851,10 @@ install-libraries: tclConfig install-msgs install-tzdata
@$(CPY) "$(ROOT)\library\auto.tcl" "$(SCRIPT_INSTALL_DIR)\"
@$(CPY) "$(OUT_DIR)\tclConfig.sh" "$(LIB_INSTALL_DIR)\"
@$(CPY) "$(WINDIR)\tclooConfig.sh" "$(LIB_INSTALL_DIR)\"
+ @$(CPY) "$(WINDIR)\rules.vc" "$(LIB_INSTALL_DIR)\nmake\"
+ @$(CPY) "$(WINDIR)\targets.vc" "$(LIB_INSTALL_DIR)\nmake\"
+ @$(CPY) "$(WINDIR)\nmakehlp.c" "$(LIB_INSTALL_DIR)\nmake\"
+ @$(CPY) "$(OUT_DIR)\tcl.nmake" "$(LIB_INSTALL_DIR)\nmake\"
@echo Installing library http1.0 directory
@$(CPY) "$(ROOT)\library\http1.0\*.tcl" \
"$(SCRIPT_INSTALL_DIR)\http1.0\"
@@ -1180,8 +899,7 @@ install-libraries: tclConfig install-msgs install-tzdata
@echo Installing encodings
@$(CPY) "$(ROOT)\library\encoding\*.enc" \
"$(SCRIPT_INSTALL_DIR)\encoding\"
-
-#" emacs fix
+# "emacs font-lock highlighting fix
install-tzdata:
@echo Installing time zone data
@@ -1215,28 +933,10 @@ tidy:
@echo Removing $(TCLREGLIB) ...
@if exist $(TCLREGLIB) del $(TCLREGLIB)
-clean: clean-pkgs
- @echo Cleaning $(TMP_DIR)\* ...
- @if exist $(TMP_DIR)\nul $(RMDIR) $(TMP_DIR)
- @echo Cleaning $(WINDIR)\nmakehlp.obj ...
- @if exist $(WINDIR)\nmakehlp.obj del $(WINDIR)\nmakehlp.obj
- @echo Cleaning $(WINDIR)\nmakehlp.exe ...
- @if exist $(WINDIR)\nmakehlp.exe del $(WINDIR)\nmakehlp.exe
- @echo Cleaning $(WINDIR)\_junk.pch ...
- @if exist $(WINDIR)\_junk.pch del $(WINDIR)\_junk.pch
- @echo Cleaning $(WINDIR)\vercl.x ...
- @if exist $(WINDIR)\vercl.x del $(WINDIR)\vercl.x
- @echo Cleaning $(WINDIR)\vercl.i ...
- @if exist $(WINDIR)\vercl.i del $(WINDIR)\vercl.i
- @echo Cleaning $(WINDIR)\versions.vc ...
- @if exist $(WINDIR)\versions.vc del $(WINDIR)\versions.vc
-
+clean: default-clean clean-pkgs
+hose: default-hose
realclean: hose
-hose:
- @echo Hosing $(OUT_DIR)\* ...
- @if exist $(OUT_DIR)\nul $(RMDIR) $(OUT_DIR)
-
# Local Variables:
# mode: makefile
# End:
diff --git a/win/nmakehlp.c b/win/nmakehlp.c
index 22b7b06..b759020 100644
--- a/win/nmakehlp.c
+++ b/win/nmakehlp.c
@@ -14,13 +14,8 @@
#define _CRT_SECURE_NO_DEPRECATE
#include <windows.h>
-#define NO_SHLWAPI_GDI
-#define NO_SHLWAPI_STREAM
-#define NO_SHLWAPI_REG
-#include <shlwapi.h>
#pragma comment (lib, "user32.lib")
#pragma comment (lib, "kernel32.lib")
-#pragma comment (lib, "shlwapi.lib")
#include <stdio.h>
#include <math.h>
@@ -39,7 +34,6 @@
#endif
-
/* protos */
static int CheckForCompilerFeature(const char *option);
@@ -47,6 +41,7 @@ static int CheckForLinkerFeature(const char **options, int count);
static int IsIn(const char *string, const char *substring);
static int SubstituteFile(const char *substs, const char *filename);
static int QualifyPath(const char *path);
+static int LocateDependency(const char *keyfile);
static const char *GetVersionFromFile(const char *filename, const char *match, int numdots);
static DWORD WINAPI ReadFromPipe(LPVOID args);
@@ -74,6 +69,7 @@ main(
char msg[300];
DWORD dwWritten;
int chars;
+ const char *s;
/*
* Make sure children (cl.exe and link.exe) are kept quiet.
@@ -153,8 +149,13 @@ main(
&dwWritten, NULL);
return 0;
}
- printf("%s\n", GetVersionFromFile(argv[2], argv[3], *(argv[1]+2) - '0'));
- return 0;
+ s = GetVersionFromFile(argv[2], argv[3], *(argv[1]+2) - '0');
+ if (s && *s) {
+ printf("%s\n", s);
+ return 0;
+ } else
+ return 1; /* Version not found. Return non-0 exit code */
+
case 'Q':
if (argc != 3) {
chars = snprintf(msg, sizeof(msg) - 1,
@@ -166,6 +167,18 @@ main(
return 2;
}
return QualifyPath(argv[2]);
+
+ case 'L':
+ if (argc != 3) {
+ chars = snprintf(msg, sizeof(msg) - 1,
+ "usage: %s -L keypath\n"
+ "Emit the fully qualified path of directory containing keypath\n"
+ "exitcodes: 0 == success, 1 == not found, 2 == error\n", argv[0]);
+ WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
+ &dwWritten, NULL);
+ return 2;
+ }
+ return LocateDependency(argv[2]);
}
}
chars = snprintf(msg, sizeof(msg) - 1,
@@ -670,6 +683,17 @@ SubstituteFile(
return 0;
}
+BOOL FileExists(LPCTSTR szPath)
+{
+#ifndef INVALID_FILE_ATTRIBUTES
+ #define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
+#endif
+ DWORD pathAttr = GetFileAttributes(szPath);
+ return (pathAttr != INVALID_FILE_ATTRIBUTES &&
+ !(pathAttr & FILE_ATTRIBUTE_DIRECTORY));
+}
+
+
/*
* QualifyPath --
*
@@ -683,18 +707,104 @@ QualifyPath(
const char *szPath)
{
char szCwd[MAX_PATH + 1];
- char szTmp[MAX_PATH + 1];
- char *p;
- GetCurrentDirectory(MAX_PATH, szCwd);
- while ((p = strchr(szPath, '/')) && *p)
- *p = '\\';
- PathCombine(szTmp, szCwd, szPath);
- PathCanonicalize(szCwd, szTmp);
+
+ GetFullPathName(szPath, sizeof(szCwd)-1, szCwd, NULL);
printf("%s\n", szCwd);
return 0;
}
/*
+ * Implements LocateDependency for a single directory. See that command
+ * for an explanation.
+ * Returns 0 if found after printing the directory.
+ * Returns 1 if not found but no errors.
+ * Returns 2 on any kind of error
+ * Basically, these are used as exit codes for the process.
+ */
+static int LocateDependencyHelper(const char *dir, const char *keypath)
+{
+ HANDLE hSearch;
+ char path[MAX_PATH+1];
+ int dirlen, keylen, ret;
+ WIN32_FIND_DATA finfo;
+
+ if (dir == NULL || keypath == NULL)
+ return 2; /* Have no real error reporting mechanism into nmake */
+ dirlen = strlen(dir);
+ if ((dirlen + 3) > sizeof(path))
+ return 2;
+ strncpy(path, dir, dirlen);
+ strncpy(path+dirlen, "\\*", 3); /* Including terminating \0 */
+ keylen = strlen(keypath);
+
+#if 0 /* This function is not available in Visual C++ 6 */
+ /*
+ * Use numerics 0 -> FindExInfoStandard,
+ * 1 -> FindExSearchLimitToDirectories,
+ * as these are not defined in Visual C++ 6
+ */
+ hSearch = FindFirstFileEx(path, 0, &finfo, 1, NULL, 0);
+#else
+ hSearch = FindFirstFile(path, &finfo);
+#endif
+ if (hSearch == INVALID_HANDLE_VALUE)
+ return 1; /* Not found */
+
+ /* Loop through all subdirs checking if the keypath is under there */
+ ret = 1; /* Assume not found */
+ do {
+ int sublen;
+ /*
+ * We need to check it is a directory despite the
+ * FindExSearchLimitToDirectories in the above call. See SDK docs
+ */
+ if ((finfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+ continue;
+ sublen = strlen(finfo.cFileName);
+ if ((dirlen+1+sublen+1+keylen+1) > sizeof(path))
+ continue; /* Path does not fit, assume not matched */
+ strncpy(path+dirlen+1, finfo.cFileName, sublen);
+ path[dirlen+1+sublen] = '\\';
+ strncpy(path+dirlen+1+sublen+1, keypath, keylen+1);
+ if (FileExists(path)) {
+ /* Found a match, print to stdout */
+ path[dirlen+1+sublen] = '\0';
+ QualifyPath(path);
+ ret = 0;
+ break;
+ }
+ } while (FindNextFile(hSearch, &finfo));
+ FindClose(hSearch);
+ return ret;
+}
+
+/*
+ * LocateDependency --
+ *
+ * Locates a dependency for a package.
+ * keypath - a relative path within the package directory
+ * that is used to confirm it is the correct directory.
+ * The search path for the package directory is currently only
+ * the parent and grandparent of the current working directory.
+ * If found, the command prints
+ * name_DIRPATH=<full path of located directory>
+ * and returns 0. If not found, does not print anything and returns 1.
+ */
+static int LocateDependency(const char *keypath)
+{
+ int i, ret;
+ static char *paths[] = {"..", "..\\..", "..\\..\\.."};
+
+ for (i = 0; i < (sizeof(paths)/sizeof(paths[0])); ++i) {
+ ret = LocateDependencyHelper(paths[i], keypath);
+ if (ret == 0)
+ return ret;
+ }
+ return ret;
+}
+
+
+/*
* Local variables:
* mode: c
* c-basic-offset: 4
diff --git a/win/rules-ext.vc b/win/rules-ext.vc
new file mode 100644
index 0000000..531e070
--- /dev/null
+++ b/win/rules-ext.vc
@@ -0,0 +1,118 @@
+# This file should only be included in makefiles for Tcl extensions,
+# NOT in the makefile for Tcl itself.
+
+!ifndef _RULES_EXT_VC
+
+# We need to run from the directory the parent makefile is located in.
+# nmake does not tell us what makefile was used to invoke it so parent
+# makefile has to set the MAKEFILEVC macro or we just make a guess and
+# warn if we think that is not the case.
+!if "$(MAKEFILEVC)" == ""
+
+!if exist("$(PROJECT).vc")
+MAKEFILEVC = $(PROJECT).vc
+!elseif exist("makefile.vc")
+MAKEFILEVC = makefile.vc
+!endif
+!endif # "$(MAKEFILEVC)" == ""
+
+!if !exist("$(MAKEFILEVC)")
+MSG = ^
+You must run nmake from the directory containing the project makefile.^
+If you are doing that and getting this message, set the MAKEFILEVC^
+macro to the name of the project makefile.
+!message WARNING: $(MSG)
+!endif
+
+!if "$(PROJECT)" == "tcl"
+!error The rules-ext.vc file is not intended for Tcl itself.
+!endif
+
+# We extract version numbers using the nmakehlp program. For now use
+# the local copy of nmakehlp. Once we locate Tcl, we will use that
+# one if it is newer.
+!if [$(CC) -nologo "nmakehlp.c" -link -subsystem:console > nul]
+!endif
+
+# First locate the Tcl directory that we are working with.
+!if "$(TCLDIR)" != ""
+
+_RULESDIR = $(TCLDIR:/=\)
+
+!else
+
+# If an installation path is specified, that is also the Tcl directory.
+# Also Tk never builds against an installed Tcl, it needs Tcl sources
+!if defined(INSTALLDIR) && "$(PROJECT)" != "tk"
+_RULESDIR=$(INSTALLDIR:/=\)
+!else
+# Locate Tcl sources
+!if [echo _RULESDIR = \> nmakehlp.out] \
+ || [nmakehlp -L generic\tcl.h >> nmakehlp.out]
+_RULESDIR = ..\..\tcl
+!else
+!include nmakehlp.out
+!endif
+
+!endif # defined(INSTALLDIR)....
+
+!endif # ifndef TCLDIR
+
+# Now look for the targets.vc file under the Tcl root. Note we check this
+# file and not rules.vc because the latter also exists on older systems.
+!if exist("$(_RULESDIR)\lib\nmake\targets.vc") # Building against installed Tcl
+_RULESDIR = $(_RULESDIR)\lib\nmake
+!elseif exist("$(_RULESDIR)\win\targets.vc") # Building against Tcl sources
+_RULESDIR = $(_RULESDIR)\win
+!else
+# If we have not located Tcl's targets file, most likely we are compiling
+# against an older version of Tcl and so must use our own support files.
+_RULESDIR = .
+!endif
+
+!if "$(_RULESDIR)" != "."
+# Potentially using Tcl's support files. If this extension has its own
+# nmake support files, need to compare the versions and pick newer.
+
+!if exist("rules.vc") # The extension has its own copy
+
+!if [echo TCL_RULES_MAJOR = \> versions.vc] \
+ && [nmakehlp -V "$(_RULESDIR)\rules.vc" RULES_VERSION_MAJOR >> versions.vc]
+!endif
+!if [echo TCL_RULES_MINOR = \>> versions.vc] \
+ && [nmakehlp -V "$(_RULESDIR)\rules.vc" RULES_VERSION_MINOR >> versions.vc]
+!endif
+
+!if [echo OUR_RULES_MAJOR = \>> versions.vc] \
+ && [nmakehlp -V "rules.vc" RULES_VERSION_MAJOR >> versions.vc]
+!endif
+!if [echo OUR_RULES_MINOR = \>> versions.vc] \
+ && [nmakehlp -V "rules.vc" RULES_VERSION_MINOR >> versions.vc]
+!endif
+!include versions.vc
+# We have a newer version of the support files, use them
+!if ($(TCL_RULES_MAJOR) != $(OUR_RULES_MAJOR)) || ($(TCL_RULES_MINOR) < $(OUR_RULES_MINOR))
+_RULESDIR = .
+!endif
+
+!endif # if exist("rules.vc")
+
+!endif # if $(_RULESDIR) != "."
+
+# Let rules.vc know what copy of nmakehlp.c to use.
+NMAKEHLPC = $(_RULESDIR)\nmakehlp.c
+
+# Get rid of our internal defines before calling rules.vc
+!undef TCL_RULES_MAJOR
+!undef TCL_RULES_MINOR
+!undef OUR_RULES_MAJOR
+!undef OUR_RULES_MINOR
+
+!if exist("$(_RULESDIR)\rules.vc")
+!message *** Using $(_RULESDIR)\rules.vc
+!include "$(_RULESDIR)\rules.vc"
+!else
+!error *** Could not locate rules.vc in $(_RULESDIR)
+!endif
+
+!endif # _RULES_EXT_VC \ No newline at end of file
diff --git a/win/rules.vc b/win/rules.vc
index 4a3ae26..8db4752 100644
--- a/win/rules.vc
+++ b/win/rules.vc
@@ -1,60 +1,435 @@
-#------------------------------------------------------------------------------
+#------------------------------------------------------------- -*- makefile -*-
# rules.vc --
#
-# Microsoft Visual C++ makefile include for decoding the commandline
-# macros. This file does not need editing to build Tcl.
+# Part of the nmake based build system for Tcl and its extensions.
+# This file does all the hard work in terms of parsing build options,
+# compiler switches, defining common targets and macros. The Tcl makefile
+# directly includes this. Extensions include it via "rules-ext.vc".
+#
+# See TIP 477 (https://core.tcl.tk/tips/doc/trunk/tip/477.md) for
+# detailed documentation.
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
# Copyright (c) 2001-2003 David Gravereaux.
# Copyright (c) 2003-2008 Patrick Thoyts
+# Copyright (c) 2017 Ashok P. Nadkarni
#------------------------------------------------------------------------------
!ifndef _RULES_VC
_RULES_VC = 1
-cc32 = $(CC) # built-in default.
-link32 = link
-lib32 = lib
-rc32 = $(RC) # built-in default.
+# The following macros define the version of the rules.vc nmake build system
+# For modifications that are not backward-compatible, you *must* change
+# the major version.
+RULES_VERSION_MAJOR = 1
+RULES_VERSION_MINOR = 2
-!ifndef INSTALLDIR
-### Assume the normal default.
-_INSTALLDIR = C:\Program Files\Tcl
+# The PROJECT macro must be defined by parent makefile.
+!if "$(PROJECT)" == ""
+!error *** Error: Macro PROJECT not defined! Please define it before including rules.vc
+!endif
+
+!if "$(PRJ_PACKAGE_TCLNAME)" == ""
+PRJ_PACKAGE_TCLNAME = $(PROJECT)
+!endif
+
+# Also special case Tcl and Tk to save some typing later
+DOING_TCL = 0
+DOING_TK = 0
+!if "$(PROJECT)" == "tcl"
+DOING_TCL = 1
+!elseif "$(PROJECT)" == "tk"
+DOING_TK = 1
+!endif
+
+!ifndef NEED_TK
+# Backwards compatibility
+!ifdef PROJECT_REQUIRES_TK
+NEED_TK = $(PROJECT_REQUIRES_TK)
!else
-### Fix the path separators.
-_INSTALLDIR = $(INSTALLDIR:/=\)
+NEED_TK = 0
+!endif
+!endif
+
+!ifndef NEED_TCL_SOURCE
+NEED_TCL_SOURCE = 0
+!endif
+
+!ifdef NEED_TK_SOURCE
+!if $(NEED_TK_SOURCE)
+NEED_TK = 1
+!endif
+!else
+NEED_TK_SOURCE = 0
!endif
+################################################################
+# Nmake is a pretty weak environment in syntax and capabilities
+# so this file is necessarily verbose. It's broken down into
+# the following parts.
+#
+# 0. Sanity check that compiler environment is set up and initialize
+# any built-in settings from the parent makefile
+# 1. First define the external tools used for compiling, copying etc.
+# as this is independent of everything else.
+# 2. Figure out our build structure in terms of the directory, whether
+# we are building Tcl or an extension, etc.
+# 3. Determine the compiler and linker versions
+# 4. Build the nmakehlp helper application
+# 5. Determine the supported compiler options and features
+# 6. Parse the OPTS macro value for user-specified build configuration
+# 7. Parse the STATS macro value for statistics instrumentation
+# 8. Parse the CHECKS macro for additional compilation checks
+# 9. Extract Tcl, and possibly Tk, version numbers from the headers
+# 10. Based on this selected configuration, construct the output
+# directory and file paths
+# 11. Construct the paths where the package is to be installed
+# 12. Set up the actual options passed to compiler and linker based
+# on the information gathered above.
+# 13. Define some standard build targets and implicit rules. These may
+# be optionally disabled by the parent makefile.
+# 14. (For extensions only.) Compare the configuration of the target
+# Tcl and the extensions and warn against discrepancies.
+#
+# One final note about the macro names used. They are as they are
+# for historical reasons. We would like legacy extensions to
+# continue to work with this make include file so be wary of
+# changing them for consistency or clarity.
+
+# 0. Sanity check compiler environment
+
+# Check to see we are configured to build with MSVC (MSDEVDIR, MSVCDIR or
+# VCINSTALLDIR) or with the MS Platform SDK (MSSDK or WindowsSDKDir)
+
+!if !defined(MSDEVDIR) && !defined(MSVCDIR) && !defined(VCINSTALLDIR) && !defined(MSSDK) && !defined(WINDOWSSDKDIR)
+MSG = ^
+Visual C++ compiler environment not initialized.
+!error $(MSG)
+!endif
+
+# We need to run from the directory the parent makefile is located in.
+# nmake does not tell us what makefile was used to invoke it so parent
+# makefile has to set the MAKEFILEVC macro or we just make a guess and
+# warn if we think that is not the case.
+!if "$(MAKEFILEVC)" == ""
+
+!if exist("$(PROJECT).vc")
+MAKEFILEVC = $(PROJECT).vc
+!elseif exist("makefile.vc")
+MAKEFILEVC = makefile.vc
+!endif
+!endif # "$(MAKEFILEVC)" == ""
+
+!if !exist("$(MAKEFILEVC)")
+MSG = ^
+You must run nmake from the directory containing the project makefile.^
+If you are doing that and getting this message, set the MAKEFILEVC^
+macro to the name of the project makefile.
+!message WARNING: $(MSG)
+!endif
+
+
+################################################################
+# 1. Define external programs being used
+
#----------------------------------------------------------
# Set the proper copy method to avoid overwrite questions
# to the user when copying files and selecting the right
# "delete all" method.
#----------------------------------------------------------
-!if "$(OS)" == "Windows_NT"
RMDIR = rmdir /S /Q
-ERRNULL = 2>NUL
-!if ![ver | find "4.0" > nul]
-CPY = echo y | xcopy /i >NUL
-COPY = copy >NUL
-!else
CPY = xcopy /i /y >NUL
+CPYDIR = xcopy /e /i /y >NUL
COPY = copy /y >NUL
+MKDIR = mkdir
+
+######################################################################
+# 2. Figure out our build environment in terms of what we're building.
+#
+# (a) Tcl itself
+# (b) Tk
+# (c) a Tcl extension using libraries/includes from an *installed* Tcl
+# (d) a Tcl extension using libraries/includes from Tcl source directory
+#
+# This last is needed because some extensions still need
+# some Tcl interfaces that are not publicly exposed.
+#
+# The fragment will set the following macros:
+# ROOT - root of this module sources
+# COMPATDIR - source directory that holds compatibility sources
+# DOCDIR - source directory containing documentation files
+# GENERICDIR - platform-independent source directory
+# WINDIR - Windows-specific source directory
+# TESTDIR - directory containing test files
+# TOOLSDIR - directory containing build tools
+# _TCLDIR - root of the Tcl installation OR the Tcl sources. Not set
+# when building Tcl itself.
+# _INSTALLDIR - native form of the installation path. For Tcl
+# this will be the root of the Tcl installation. For extensions
+# this will be the lib directory under the root.
+# TCLINSTALL - set to 1 if _TCLDIR refers to
+# headers and libraries from an installed Tcl, and 0 if built against
+# Tcl sources. Not set when building Tcl itself. Yes, not very well
+# named.
+# _TCL_H - native path to the tcl.h file
+#
+# If Tk is involved, also sets the following
+# _TKDIR - native form Tk installation OR Tk source. Not set if building
+# Tk itself.
+# TKINSTALL - set 1 if _TKDIR refers to installed Tk and 0 if Tk sources
+# _TK_H - native path to the tk.h file
+
+# Root directory for sources and assumed subdirectories
+ROOT = $(MAKEDIR)\..
+# The following paths CANNOT have spaces in them as they appear on the
+# left side of implicit rules.
+!ifndef COMPATDIR
+COMPATDIR = $(ROOT)\compat
+!endif
+!ifndef DOCDIR
+DOCDIR = $(ROOT)\doc
+!endif
+!ifndef GENERICDIR
+GENERICDIR = $(ROOT)\generic
+!endif
+!ifndef TOOLSDIR
+TOOLSDIR = $(ROOT)\tools
+!endif
+!ifndef TESTDIR
+TESTDIR = $(ROOT)\tests
+!endif
+!ifndef LIBDIR
+!if exist("$(ROOT)\library")
+LIBDIR = $(ROOT)\library
+!else
+LIBDIR = $(ROOT)\lib
!endif
-!else # "$(OS)" != "Windows_NT"
-CPY = xcopy /i >_JUNK.OUT # On Win98 NUL does not work here.
-COPY = copy >_JUNK.OUT # On Win98 NUL does not work here.
-RMDIR = deltree /Y
-NULL = \NUL # Used in testing directory existence
-ERRNULL = >NUL # Win9x shell cannot redirect stderr
!endif
-MKDIR = mkdir
+!ifndef DEMODIR
+!if exist("$(LIBDIR)\demos")
+DEMODIR = $(LIBDIR)\demos
+!else
+DEMODIR = $(ROOT)\demos
+!endif
+!endif # ifndef DEMODIR
+# Do NOT enclose WINDIR in a !ifndef because Windows always defines
+# WINDIR env var to point to c:\windows!
+# TBD - This is a potentially dangerous conflict, rename WINDIR to
+# something else
+WINDIR = $(ROOT)\win
+
+!ifndef RCDIR
+!if exist("$(WINDIR)\rc")
+RCDIR = $(WINDIR)\rc
+!else
+RCDIR = $(WINDIR)
+!endif
+!endif
+RCDIR = $(RCDIR:/=\)
-#------------------------------------------------------------------------------
-# Determine the host and target architectures and compiler version.
-#------------------------------------------------------------------------------
+# The target directory where the built packages and binaries will be installed.
+# INSTALLDIR is the (optional) path specified by the user.
+# _INSTALLDIR is INSTALLDIR using the backslash separator syntax
+!ifdef INSTALLDIR
+### Fix the path separators.
+_INSTALLDIR = $(INSTALLDIR:/=\)
+!else
+### Assume the normal default.
+_INSTALLDIR = $(HOMEDRIVE)\Tcl
+!endif
+
+!if $(DOING_TCL)
+
+# BEGIN Case 2(a) - Building Tcl itself
+
+# Only need to define _TCL_H
+_TCL_H = ..\generic\tcl.h
+
+# END Case 2(a) - Building Tcl itself
+
+!elseif $(DOING_TK)
+
+# BEGIN Case 2(b) - Building Tk
+
+TCLINSTALL = 0 # Tk always builds against Tcl source, not an installed Tcl
+!if "$(TCLDIR)" == ""
+!if [echo TCLDIR = \> nmakehlp.out] \
+ || [nmakehlp -L generic\tcl.h >> nmakehlp.out]
+!error *** Could not locate Tcl source directory.
+!endif
+!include nmakehlp.out
+!endif # TCLDIR == ""
+
+_TCLDIR = $(TCLDIR:/=\)
+_TCL_H = $(_TCLDIR)\generic\tcl.h
+!if !exist("$(_TCL_H)")
+!error Could not locate tcl.h. Please set the TCLDIR macro to point to the Tcl *source* directory.
+!endif
+
+_TK_H = ..\generic\tk.h
+
+# END Case 2(b) - Building Tk
+
+!else
+
+# BEGIN Case 2(c) or (d) - Building an extension other than Tk
+
+# If command line has specified Tcl location through TCLDIR, use it
+# else default to the INSTALLDIR setting
+!if "$(TCLDIR)" != ""
+
+_TCLDIR = $(TCLDIR:/=\)
+!if exist("$(_TCLDIR)\include\tcl.h") # Case 2(c) with TCLDIR defined
+TCLINSTALL = 1
+_TCL_H = $(_TCLDIR)\include\tcl.h
+!elseif exist("$(_TCLDIR)\generic\tcl.h") # Case 2(d) with TCLDIR defined
+TCLINSTALL = 0
+_TCL_H = $(_TCLDIR)\generic\tcl.h
+!endif
+
+!else # # Case 2(c) for extensions with TCLDIR undefined
+
+# Need to locate Tcl depending on whether it needs Tcl source or not.
+# If we don't, check the INSTALLDIR for an installed Tcl first
+
+!if exist("$(_INSTALLDIR)\include\tcl.h") && !$(NEED_TCL_SOURCE)
+
+TCLINSTALL = 1
+TCLDIR = $(_INSTALLDIR)\..
+# NOTE: we will be resetting _INSTALLDIR to _INSTALLDIR/lib for extensions
+# later so the \.. accounts for the /lib
+_TCLDIR = $(_INSTALLDIR)\..
+_TCL_H = $(_TCLDIR)\include\tcl.h
+
+!else # exist(...) && ! $(NEED_TCL_SOURCE)
+
+!if [echo _TCLDIR = \> nmakehlp.out] \
+ || [nmakehlp -L generic\tcl.h >> nmakehlp.out]
+!error *** Could not locate Tcl source directory.
+!endif
+!include nmakehlp.out
+TCLINSTALL = 0
+TCLDIR = $(_TCLDIR)
+_TCL_H = $(_TCLDIR)\generic\tcl.h
+
+!endif # exist(...) && ! $(NEED_TCL_SOURCE)
+
+!endif # TCLDIR
+
+!ifndef _TCL_H
+MSG =^
+Failed to find tcl.h. The TCLDIR macro is set incorrectly or is not set and default path does not contain tcl.h.
+!error $(MSG)
+!endif
+
+# Now do the same to locate Tk headers and libs if project requires Tk
+!if $(NEED_TK)
+
+!if "$(TKDIR)" != ""
+
+_TKDIR = $(TKDIR:/=\)
+!if exist("$(_TKDIR)\include\tk.h")
+TKINSTALL = 1
+_TK_H = $(_TKDIR)\include\tk.h
+!elseif exist("$(_TKDIR)\generic\tk.h")
+TKINSTALL = 0
+_TK_H = $(_TKDIR)\generic\tk.h
+!endif
+
+!else # TKDIR not defined
+
+# Need to locate Tcl depending on whether it needs Tcl source or not.
+# If we don't, check the INSTALLDIR for an installed Tcl first
+
+!if exist("$(_INSTALLDIR)\include\tk.h") && !$(NEED_TK_SOURCE)
+
+TKINSTALL = 1
+# NOTE: we will be resetting _INSTALLDIR to _INSTALLDIR/lib for extensions
+# later so the \.. accounts for the /lib
+_TKDIR = $(_INSTALLDIR)\..
+_TK_H = $(_TKDIR)\include\tk.h
+TKDIR = $(_TKDIR)
+
+!else # exist("$(_INSTALLDIR)\include\tk.h") && !$(NEED_TK_SOURCE)
+
+!if [echo _TKDIR = \> nmakehlp.out] \
+ || [nmakehlp -L generic\tk.h >> nmakehlp.out]
+!error *** Could not locate Tk source directory.
+!endif
+!include nmakehlp.out
+TKINSTALL = 0
+TKDIR = $(_TKDIR)
+_TK_H = $(_TKDIR)\generic\tk.h
+
+!endif # exist("$(_INSTALLDIR)\include\tk.h") && !$(NEED_TK_SOURCE)
+
+!endif # TKDIR
+
+!ifndef _TK_H
+MSG =^
+Failed to find tk.h. The TKDIR macro is set incorrectly or is not set and default path does not contain tk.h.
+!error $(MSG)
+!endif
+
+!endif # NEED_TK
+
+!if $(NEED_TCL_SOURCE) && $(TCLINSTALL)
+MSG = ^
+*** Warning: This extension requires the source distribution of Tcl.^
+*** Please set the TCLDIR macro to point to the Tcl sources.
+!error $(MSG)
+!endif
+
+!if $(NEED_TK_SOURCE)
+!if $(TKINSTALL)
+MSG = ^
+*** Warning: This extension requires the source distribution of Tk.^
+*** Please set the TKDIR macro to point to the Tk sources.
+!error $(MSG)
+!endif
+!endif
+
+
+# If INSTALLDIR set to Tcl installation root dir then reset to the
+# lib dir for installing extensions
+!if exist("$(_INSTALLDIR)\include\tcl.h")
+_INSTALLDIR=$(_INSTALLDIR)\lib
+!endif
+
+# END Case 2(c) or (d) - Building an extension
+!endif # if $(DOING_TCL)
+
+################################################################
+# 3. Determine compiler version and architecture
+# In this section, we figure out the compiler version and the
+# architecture for which we are building. This sets the
+# following macros:
+# VCVERSION - the internal compiler version as 1200, 1400, 1910 etc.
+# This is also printed by the compiler in dotted form 19.10 etc.
+# VCVER - the "marketing version", for example Visual C++ 6 for internal
+# compiler version 1200. This is kept only for legacy reasons as it
+# does not make sense for recent Microsoft compilers. Only used for
+# output directory names.
+# ARCH - set to IX86 or AMD64 depending on 32- or 64-bit target
+# NATIVE_ARCH - set to IX86 or AMD64 for the host machine
+# MACHINE - same as $(ARCH) - legacy
+# _VC_MANIFEST_EMBED_{DLL,EXE} - commands for embedding a manifest if needed
+# CFG_ENCODING - set to an character encoding.
+# TBD - this is passed to compiler as TCL_CFGVAL_ENCODING but can't
+# see where it is used
+
+cc32 = $(CC) # built-in default.
+link32 = link
+lib32 = lib
+rc32 = $(RC) # built-in default.
+
+#----------------------------------------------------------------
+# Figure out the compiler architecture and version by writing
+# the C macros to a file, preprocessing them with the C
+# preprocessor and reading back the created file
_HASH=^#
_VC_MANIFEST_EMBED_EXE=
@@ -66,16 +441,43 @@ VCVER=0
&& ![echo $(_HASH)elif defined(_M_AMD64) >> vercl.x] \
&& ![echo ARCH=AMD64 >> vercl.x] \
&& ![echo $(_HASH)endif >> vercl.x] \
- && ![cl -nologo -TC -P vercl.x $(ERRNULL)]
+ && ![$(cc32) -nologo -TC -P vercl.x 2>NUL]
!include vercl.i
+!if $(VCVERSION) < 1900
!if ![echo VCVER= ^\> vercl.vc] \
&& ![set /a $(VCVERSION) / 100 - 6 >> vercl.vc]
!include vercl.vc
!endif
+!else
+# The simple calculation above does not apply to new Visual Studio releases
+# Keep the compiler version in its native form.
+VCVER = $(VCVERSION)
!endif
-!if ![del $(ERRNUL) /q/f vercl.x vercl.i vercl.vc]
!endif
+!if ![del 2>NUL /q/f vercl.x vercl.i vercl.vc]
+!endif
+
+#----------------------------------------------------------------
+# The MACHINE macro is used by legacy makefiles so set it as well
+!ifdef MACHINE
+!if "$(MACHINE)" == "x86"
+!undef MACHINE
+MACHINE = IX86
+!elseif "$(MACHINE)" == "x64"
+!undef MACHINE
+MACHINE = AMD64
+!endif
+!if "$(MACHINE)" != "$(ARCH)"
+!error Specified MACHINE macro $(MACHINE) does not match detected target architecture $(ARCH).
+!endif
+!else
+MACHINE=$(ARCH)
+!endif
+
+#------------------------------------------------------------
+# Figure out the *host* architecture by reading the registry
+
!if ![reg query HKLM\Hardware\Description\System\CentralProcessor\0 /v Identifier | findstr /i x86]
NATIVE_ARCH=IX86
!else
@@ -88,135 +490,190 @@ _VC_MANIFEST_EMBED_EXE=if exist $@.manifest mt -nologo -manifest $@.manifest -ou
_VC_MANIFEST_EMBED_DLL=if exist $@.manifest mt -nologo -manifest $@.manifest -outputresource:$@;2
!endif
-!ifndef MACHINE
-MACHINE=$(ARCH)
-!endif
-
!ifndef CFG_ENCODING
CFG_ENCODING = \"cp1252\"
!endif
-!message ===============================================================================
+################################################################
+# 4. Build the nmakehlp program
+# This is a helper app we need to overcome nmake's limiting
+# environment. We will call out to it to get various bits of
+# information about supported compiler options etc.
+#
+# Tcl itself will always use the nmakehlp.c program which is
+# in its own source. This is the "master" copy and kept updated.
+#
+# Extensions built against an installed Tcl will use the installed
+# copy of Tcl's nmakehlp.c if there is one and their own version
+# otherwise. In the latter case, they would also be using their own
+# rules.vc. Note that older versions of Tcl do not install nmakehlp.c
+# or rules.vc.
+#
+# Extensions built against Tcl sources will use the one from the Tcl source.
+#
+# When building an extension using a sufficiently new version of Tcl,
+# rules-ext.vc will define NMAKEHLPC appropriately to point to the
+# copy of nmakehlp.c to be used.
-#----------------------------------------------------------
-# build the helper app we need to overcome nmake's limiting
-# environment.
-#----------------------------------------------------------
+!ifndef NMAKEHLPC
+# Default to the one in the current directory (the extension's own nmakehlp.c)
+NMAKEHLPC = nmakehlp.c
-!if !exist(nmakehlp.exe)
-!if [$(cc32) -nologo nmakehlp.c -link -subsystem:console > nul]
+!if !$(DOING_TCL)
+!if $(TCLINSTALL)
+!if exist("$(_TCLDIR)\lib\nmake\nmakehlp.c")
+NMAKEHLPC = $(_TCLDIR)\lib\nmake\nmakehlp.c
!endif
+!else # ! $(TCLINSTALL)
+!if exist("$(_TCLDIR)\win\nmakehlp.c")
+NMAKEHLPC = $(_TCLDIR)\win\nmakehlp.c
!endif
+!endif # $(TCLINSTALL)
+!endif # !$(DOING_TCL)
-#----------------------------------------------------------
-# Test for compiler features
-#----------------------------------------------------------
+!endif # NMAKEHLPC
-### test for optimizations
-!if [nmakehlp -c -Ot]
-!message *** Compiler has 'Optimizations'
-OPTIMIZING = 1
-!else
-!message *** Compiler does not have 'Optimizations'
-OPTIMIZING = 0
+# We always build nmakehlp even if it exists since we do not know
+# what source it was built from.
+!if [$(cc32) -nologo "$(NMAKEHLPC)" -link -subsystem:console > nul]
!endif
-OPTIMIZATIONS =
+################################################################
+# 5. Test for compiler features
+# Visual C++ compiler options have changed over the years. Check
+# which options are supported by the compiler in use.
+#
+# The following macros are set:
+# OPTIMIZATIONS - the compiler flags to be used for optimized builds
+# DEBUGFLAGS - the compiler flags to be used for debug builds
+# LINKERFLAGS - Flags passed to the linker
+#
+# Note that these are the compiler settings *available*, not those
+# that will be *used*. The latter depends on the OPTS macro settings
+# which we have not yet parsed.
+#
+# Also note that some of the flags in OPTIMIZATIONS are not really
+# related to optimization. They are placed there only for legacy reasons
+# as some extensions expect them to be included in that macro.
-!if [nmakehlp -c -Ot]
-OPTIMIZATIONS = $(OPTIMIZATIONS) -Ot
+# -Op improves float consistency. Note only needed for older compilers
+# Newer compilers do not need or support this option.
+!if [nmakehlp -c -Op]
+FPOPTS = -Op
!endif
-!if [nmakehlp -c -Oi]
-OPTIMIZATIONS = $(OPTIMIZATIONS) -Oi
+# Strict floating point semantics - present in newer compilers in lieu of -Op
+!if [nmakehlp -c -fp:strict]
+FPOPTS = $(FPOPTS) -fp:strict
!endif
-!if [nmakehlp -c -Op]
-OPTIMIZATIONS = $(OPTIMIZATIONS) -Op
+!if "$(MACHINE)" == "IX86"
+### test for pentium errata
+!if [nmakehlp -c -QI0f]
+!message *** Compiler has 'Pentium 0x0f fix'
+FPOPTS = $(FPOPTS) -QI0f
+!else
+!message *** Compiler does not have 'Pentium 0x0f fix'
!endif
-
-!if [nmakehlp -c -fp:strict]
-OPTIMIZATIONS = $(OPTIMIZATIONS) -fp:strict
!endif
-!if [nmakehlp -c -Gs]
-OPTIMIZATIONS = $(OPTIMIZATIONS) -Gs
+### test for optimizations
+# /O2 optimization includes /Og /Oi /Ot /Oy /Ob2 /Gs /GF /Gy as per
+# documentation. Note we do NOT want /Gs as that inserts a _chkstk
+# stack probe at *every* function entry, not just those with more than
+# a page of stack allocation resulting in a performance hit. However,
+# /O2 documentation is misleading as its stack probes are simply the
+# default page size locals allocation probes and not what is implied
+# by an explicit /Gs option.
+
+OPTIMIZATIONS = $(FPOPTS)
+
+!if [nmakehlp -c -O2]
+OPTIMIZING = 1
+OPTIMIZATIONS = $(OPTIMIZATIONS) -O2
+!else
+# Legacy, really. All modern compilers support this
+!message *** Compiler does not have 'Optimizations'
+OPTIMIZING = 0
!endif
+# Checks for buffer overflows in local arrays
!if [nmakehlp -c -GS]
OPTIMIZATIONS = $(OPTIMIZATIONS) -GS
!endif
+# Link time optimization. Note that this option (potentially) makes
+# generated libraries only usable by the specific VC++ version that
+# created it. Requires /LTCG linker option
!if [nmakehlp -c -GL]
OPTIMIZATIONS = $(OPTIMIZATIONS) -GL
+CC_GL_OPT_ENABLED = 1
+!else
+# In newer compilers -GL and -YX are incompatible.
+!if [nmakehlp -c -YX]
+OPTIMIZATIONS = $(OPTIMIZATIONS) -YX
!endif
+!endif # [nmakehlp -c -GL]
-DEBUGFLAGS =
+DEBUGFLAGS = $(FPOPTS)
+# Run time error checks. Not available or valid in a release, non-debug build
+# RTC is for modern compilers, -GZ is legacy
!if [nmakehlp -c -RTC1]
DEBUGFLAGS = $(DEBUGFLAGS) -RTC1
!elseif [nmakehlp -c -GZ]
DEBUGFLAGS = $(DEBUGFLAGS) -GZ
!endif
-COMPILERFLAGS =-W3 /DUNICODE /D_UNICODE /D_ATL_XP_TARGETING
+#----------------------------------------------------------------
+# Linker flags
-# In v13 -GL and -YX are incompatible.
-!if [nmakehlp -c -YX]
-!if ![nmakehlp -c -GL]
-OPTIMIZATIONS = $(OPTIMIZATIONS) -YX
-!endif
-!endif
-
-!if "$(MACHINE)" == "IX86"
-### test for pentium errata
-!if [nmakehlp -c -QI0f]
-!message *** Compiler has 'Pentium 0x0f fix'
-COMPILERFLAGS = $(COMPILERFLAGS) -QI0f
-!else
-!message *** Compiler does not have 'Pentium 0x0f fix'
-!endif
-!endif
-
-!if "$(MACHINE)" == "IA64"
-### test for Itanium errata
-!if [nmakehlp -c -QIA64_Bx]
-!message *** Compiler has 'B-stepping errata workarounds'
-COMPILERFLAGS = $(COMPILERFLAGS) -QIA64_Bx
-!else
-!message *** Compiler does not have 'B-stepping errata workarounds'
-!endif
-!endif
-
-# Prevents "LNK1561: entry point must be defined" error compiling from VS-IDE:
+# LINKER_TESTFLAGS are for internal use when we call nmakehlp to test
+# if the linker supports a specific option. Without these flags link will
+# return "LNK1561: entry point must be defined" error compiling from VS-IDE:
+# They are not passed through to the actual application / extension
+# link rules.
!ifndef LINKER_TESTFLAGS
-LINKER_TESTFLAGS = /DLL /NOENTRY /OUT:nmhlp-out.txt
-!endif
-
-!if "$(MACHINE)" == "IX86"
-### test for -align:4096, when align:512 will do.
-!if [nmakehlp -l -opt:nowin98 $(LINKER_TESTFLAGS)]
-!message *** Linker has 'Win98 alignment problem'
-ALIGN98_HACK = 1
-!else
-!message *** Linker does not have 'Win98 alignment problem'
-ALIGN98_HACK = 0
-!endif
-!else
-ALIGN98_HACK = 0
+LINKER_TESTFLAGS = /DLL /NOENTRY /OUT:nmakehlp.out
!endif
LINKERFLAGS =
+# If compiler has enabled link time optimization, linker must too with -ltcg
+!ifdef CC_GL_OPT_ENABLED
!if [nmakehlp -l -ltcg $(LINKER_TESTFLAGS)]
-LINKERFLAGS =-ltcg
-!endif
-
-#----------------------------------------------------------
-# Decode the options requested.
-#----------------------------------------------------------
-
-!if "$(OPTS)" == "" || [nmakehlp -f "$(OPTS)" "none"]
+LINKERFLAGS = $(LINKERFLAGS) -ltcg
+!endif
+!endif
+
+########################################################################
+# 6. Parse the OPTS macro to work out the requested build configuration.
+# Based on this, we will construct the actual switches to be passed to the
+# compiler and linker using the macros defined in the previous section.
+# The following macros are defined by this section based on OPTS
+# STATIC_BUILD - 0 -> Tcl is to be built as a shared library
+# 1 -> build as a static library and shell
+# TCL_THREADS - legacy but always 1 on Windows since winsock requires it.
+# DEBUG - 1 -> debug build, 0 -> release builds
+# SYMBOLS - 1 -> generate PDB's, 0 -> no PDB's
+# PROFILE - 1 -> generate profiling info, 0 -> no profiling
+# PGO - 1 -> profile based optimization, 0 -> no
+# MSVCRT - 1 -> link to dynamic C runtime even when building static Tcl build
+# 0 -> link to static C runtime for static Tcl build.
+# Does not impact shared Tcl builds (STATIC_BUILD == 0)
+# TCL_USE_STATIC_PACKAGES - 1 -> statically link the registry and dde extensions
+# in the Tcl shell. 0 -> keep them as shared libraries
+# Does not impact shared Tcl builds.
+# USE_THREAD_ALLOC - 1 -> Use a shared global free pool for allocation.
+# 0 -> Use the non-thread allocator.
+# UNCHECKED - 1 -> when doing a debug build with symbols, use the release
+# C runtime, 0 -> use the debug C runtime.
+# USE_STUBS - 1 -> compile to use stubs interfaces, 0 -> direct linking
+# CONFIG_CHECK - 1 -> check current build configuration against Tcl
+# configuration (ignored for Tcl itself)
+# Further, LINKERFLAGS are modified based on above.
+
+# Default values for all the above
STATIC_BUILD = 0
TCL_THREADS = 1
DEBUG = 0
@@ -224,17 +681,32 @@ SYMBOLS = 0
PROFILE = 0
PGO = 0
MSVCRT = 1
-LOIMPACT = 0
TCL_USE_STATIC_PACKAGES = 0
USE_THREAD_ALLOC = 1
UNCHECKED = 0
+CONFIG_CHECK = 1
+!if $(DOING_TCL)
+USE_STUBS = 0
!else
+USE_STUBS = 1
+!endif
+
+# If OPTS is not empty AND does not contain "none" which turns off all OPTS
+# set the above macros based on OPTS content
+!if "$(OPTS)" != "" && ![nmakehlp -f "$(OPTS)" "none"]
+
+# OPTS are specified, parse them
+
!if [nmakehlp -f $(OPTS) "static"]
!message *** Doing static
STATIC_BUILD = 1
-!else
-STATIC_BUILD = 0
!endif
+
+!if [nmakehlp -f $(OPTS) "nostubs"]
+!message *** Not using stubs
+USE_STUBS = 0
+!endif
+
!if [nmakehlp -f $(OPTS) "nomsvcrt"]
!message *** Doing nomsvcrt
MSVCRT = 0
@@ -249,13 +721,15 @@ MSVCRT = 1
MSVCRT = 0
!endif
!endif
-!endif
+!endif # [nmakehlp -f $(OPTS) "nomsvcrt"]
+
!if [nmakehlp -f $(OPTS) "staticpkg"] && $(STATIC_BUILD)
!message *** Doing staticpkg
TCL_USE_STATIC_PACKAGES = 1
!else
TCL_USE_STATIC_PACKAGES = 0
!endif
+
!if [nmakehlp -f $(OPTS) "nothreads"]
!message *** Compile explicitly for non-threaded tcl
TCL_THREADS = 0
@@ -264,24 +738,28 @@ USE_THREAD_ALLOC= 0
TCL_THREADS = 1
USE_THREAD_ALLOC= 1
!endif
+
!if [nmakehlp -f $(OPTS) "symbols"]
!message *** Doing symbols
DEBUG = 1
!else
DEBUG = 0
!endif
+
!if [nmakehlp -f $(OPTS) "pdbs"]
!message *** Doing pdbs
SYMBOLS = 1
!else
SYMBOLS = 0
!endif
+
!if [nmakehlp -f $(OPTS) "profile"]
!message *** Doing profile
PROFILE = 1
!else
PROFILE = 0
!endif
+
!if [nmakehlp -f $(OPTS) "pgi"]
!message *** Doing profile guided optimization instrumentation
PGO = 1
@@ -291,44 +769,229 @@ PGO = 2
!else
PGO = 0
!endif
+
!if [nmakehlp -f $(OPTS) "loimpact"]
-!message *** Doing loimpact
-LOIMPACT = 1
-!else
-LOIMPACT = 0
+!message *** Warning: ignoring option "loimpact" - deprecated on modern Windows.
!endif
+
+# TBD - should get rid of this option
!if [nmakehlp -f $(OPTS) "thrdalloc"]
!message *** Doing thrdalloc
USE_THREAD_ALLOC = 1
!endif
+
!if [nmakehlp -f $(OPTS) "tclalloc"]
-!message *** Doing tclalloc
USE_THREAD_ALLOC = 0
!endif
+
!if [nmakehlp -f $(OPTS) "unchecked"]
!message *** Doing unchecked
UNCHECKED = 1
!else
UNCHECKED = 0
!endif
+
+!if [nmakehlp -f $(OPTS) "noconfigcheck"]
+CONFIG_CHECK = 1
+!else
+CONFIG_CHECK = 0
!endif
-#----------------------------------------------------------
-# Figure-out how to name our intermediate and output directories.
-# We wouldn't want different builds to use the same .obj files
-# by accident.
-#----------------------------------------------------------
+!endif # "$(OPTS)" != "" && ... parsing of OPTS
+
+# Set linker flags based on above
+
+!if $(PGO) > 1
+!if [nmakehlp -l -ltcg:pgoptimize $(LINKER_TESTFLAGS)]
+LINKERFLAGS = $(LINKERFLAGS:-ltcg=) -ltcg:pgoptimize
+!else
+MSG=^
+This compiler does not support profile guided optimization.
+!error $(MSG)
+!endif
+!elseif $(PGO) > 0
+!if [nmakehlp -l -ltcg:pginstrument $(LINKER_TESTFLAGS)]
+LINKERFLAGS = $(LINKERFLAGS:-ltcg=) -ltcg:pginstrument
+!else
+MSG=^
+This compiler does not support profile guided optimization.
+!error $(MSG)
+!endif
+!endif
+
+################################################################
+# 7. Parse the STATS macro to configure code instrumentation
+# The following macros are set by this section:
+# TCL_MEM_DEBUG - 1 -> enables memory allocation instrumentation
+# 0 -> disables
+# TCL_COMPILE_DEBUG - 1 -> enables byte compiler logging
+# 0 -> disables
+
+# Default both are off
+TCL_MEM_DEBUG = 0
+TCL_COMPILE_DEBUG = 0
+
+!if "$(STATS)" != "" && ![nmakehlp -f "$(STATS)" "none"]
+
+!if [nmakehlp -f $(STATS) "memdbg"]
+!message *** Doing memdbg
+TCL_MEM_DEBUG = 1
+!else
+TCL_MEM_DEBUG = 0
+!endif
+
+!if [nmakehlp -f $(STATS) "compdbg"]
+!message *** Doing compdbg
+TCL_COMPILE_DEBUG = 1
+!else
+TCL_COMPILE_DEBUG = 0
+!endif
+
+!endif
+
+####################################################################
+# 8. Parse the CHECKS macro to configure additional compiler checks
+# The following macros are set by this section:
+# WARNINGS - compiler switches that control the warnings level
+# TCL_NO_DEPRECATED - 1 -> disable support for deprecated functions
+# 0 -> enable deprecated functions
+
+# Defaults - Permit deprecated functions and warning level 3
+TCL_NO_DEPRECATED = 0
+WARNINGS = -W3
+
+!if "$(CHECKS)" != "" && ![nmakehlp -f "$(CHECKS)" "none"]
+
+!if [nmakehlp -f $(CHECKS) "nodep"]
+!message *** Doing nodep check
+TCL_NO_DEPRECATED = 1
+!endif
+
+!if [nmakehlp -f $(CHECKS) "fullwarn"]
+!message *** Doing full warnings check
+WARNINGS = -W4
+!if [nmakehlp -l -warn:3 $(LINKER_TESTFLAGS)]
+LINKERFLAGS = $(LINKERFLAGS) -warn:3
+!endif
+!endif
+
+!if [nmakehlp -f $(CHECKS) "64bit"] && [nmakehlp -c -Wp64]
+!message *** Doing 64bit portability warnings
+WARNINGS = $(WARNINGS) -Wp64
+!endif
+
+!endif
+
+################################################################
+# 9. Extract various version numbers
+# For Tcl and Tk, version numbers are extracted from tcl.h and tk.h
+# respectively. For extensions, versions are extracted from the
+# configure.in or configure.ac from the TEA configuration if it
+# exists, and unset otherwise.
+# Sets the following macros:
+# TCL_MAJOR_VERSION
+# TCL_MINOR_VERSION
+# TCL_PATCH_LEVEL
+# TCL_VERSION
+# TK_MAJOR_VERSION
+# TK_MINOR_VERSION
+# TK_PATCH_LEVEL
+# TK_VERSION
+# DOTVERSION - set as (for example) 2.5
+# VERSION - set as (for example 25)
+#--------------------------------------------------------------
+
+!if [echo REM = This file is generated from rules.vc > versions.vc]
+!endif
+!if [echo TCL_MAJOR_VERSION = \>> versions.vc] \
+ && [nmakehlp -V "$(_TCL_H)" TCL_MAJOR_VERSION >> versions.vc]
+!endif
+!if [echo TCL_MINOR_VERSION = \>> versions.vc] \
+ && [nmakehlp -V "$(_TCL_H)" TCL_MINOR_VERSION >> versions.vc]
+!endif
+!if [echo TCL_PATCH_LEVEL = \>> versions.vc] \
+ && [nmakehlp -V "$(_TCL_H)" TCL_PATCH_LEVEL >> versions.vc]
+!endif
+
+!if defined(_TK_H)
+!if [echo TK_MAJOR_VERSION = \>> versions.vc] \
+ && [nmakehlp -V $(_TK_H) TK_MAJOR_VERSION >> versions.vc]
+!endif
+!if [echo TK_MINOR_VERSION = \>> versions.vc] \
+ && [nmakehlp -V $(_TK_H) TK_MINOR_VERSION >> versions.vc]
+!endif
+!if [echo TK_PATCH_LEVEL = \>> versions.vc] \
+ && [nmakehlp -V $(_TK_H) TK_PATCH_LEVEL >> versions.vc]
+!endif
+!endif # _TK_H
+
+!include versions.vc
+
+TCL_VERSION = $(TCL_MAJOR_VERSION)$(TCL_MINOR_VERSION)
+TCL_DOTVERSION = $(TCL_MAJOR_VERSION).$(TCL_MINOR_VERSION)
+!if defined(_TK_H)
+TK_VERSION = $(TK_MAJOR_VERSION)$(TK_MINOR_VERSION)
+TK_DOTVERSION = $(TK_MAJOR_VERSION).$(TK_MINOR_VERSION)
+!endif
+
+# Set DOTVERSION and VERSION
+!if $(DOING_TCL)
+
+DOTVERSION = $(TCL_MAJOR_VERSION).$(TCL_MINOR_VERSION)
+VERSION = $(TCL_VERSION)
+
+!elseif $(DOING_TK)
+
+DOTVERSION = $(TK_DOTVERSION)
+VERSION = $(TK_VERSION)
+
+!else # Doing a non-Tk extension
+
+# If parent makefile has not defined DOTVERSION, try to get it from TEA
+# first from a configure.in file, and then from configure.ac
+!ifndef DOTVERSION
+!if [echo DOTVERSION = \> versions.vc] \
+ || [nmakehlp -V $(ROOT)\configure.in ^[$(PROJECT)^] >> versions.vc]
+!if [echo DOTVERSION = \> versions.vc] \
+ || [nmakehlp -V $(ROOT)\configure.ac ^[$(PROJECT)^] >> versions.vc]
+!error *** Could not figure out extension version. Please define DOTVERSION in parent makefile before including rules.vc.
+!endif
+!endif
+!include versions.vc
+!endif # DOTVERSION
+VERSION = $(DOTVERSION:.=)
+
+!endif # $(DOING_TCL) ... etc.
-#----------------------------------------
-# Naming convention:
+################################################################
+# 10. Construct output directory and file paths
+# Figure-out how to name our intermediate and output directories.
+# In order to avoid inadvertent mixing of object files built using
+# different compilers, build configurations etc.,
+#
+# Naming convention (suffixes):
# t = full thread support.
-# s = static library (as opposed to an
-# import library)
-# g = linked to the debug enabled C
-# run-time.
-# x = special static build when it
-# links to the dynamic C run-time.
-#----------------------------------------
+# s = static library (as opposed to an import library)
+# g = linked to the debug enabled C run-time.
+# x = special static build when it links to the dynamic C run-time.
+#
+# The following macros are set in this section:
+# SUFX - the suffix to use for binaries based on above naming convention
+# BUILDDIRTOP - the toplevel default output directory
+# is of the form {Release,Debug}[_AMD64][_COMPILERVERSION]
+# TMP_DIR - directory where object files are created
+# OUT_DIR - directory where output executables are created
+# Both TMP_DIR and OUT_DIR are defaulted only if not defined by the
+# parent makefile (or command line). The default values are
+# based on BUILDDIRTOP.
+# STUBPREFIX - name of the stubs library for this project
+# PRJIMPLIB - output path of the generated project import library
+# PRJLIBNAME - name of generated project library
+# PRJLIB - output path of generated project library
+# PRJSTUBLIBNAME - name of the generated project stubs library
+# PRJSTUBLIB - output path of the generated project stubs library
+# RESFILE - output resource file (only if not static build)
+
SUFX = tsgx
!if $(DEBUG)
@@ -381,80 +1044,217 @@ OUT_DIR = $(TMP_DIR)
!endif
!endif
-
-#----------------------------------------------------------
-# Decode the statistics requested.
-#----------------------------------------------------------
-
-!if "$(STATS)" == "" || [nmakehlp -f "$(STATS)" "none"]
-TCL_MEM_DEBUG = 0
-TCL_COMPILE_DEBUG = 0
-!else
-!if [nmakehlp -f $(STATS) "memdbg"]
-!message *** Doing memdbg
-TCL_MEM_DEBUG = 1
-!else
-TCL_MEM_DEBUG = 0
-!endif
-!if [nmakehlp -f $(STATS) "compdbg"]
-!message *** Doing compdbg
-TCL_COMPILE_DEBUG = 1
-!else
-TCL_COMPILE_DEBUG = 0
+# Relative paths -> absolute
+!if [echo OUT_DIR = \> nmakehlp.out] \
+ || [nmakehlp -Q "$(OUT_DIR)" >> nmakehlp.out]
+!error *** Could not fully qualify path OUT_DIR=$(OUT_DIR)
!endif
+!if [echo TMP_DIR = \>> nmakehlp.out] \
+ || [nmakehlp -Q "$(TMP_DIR)" >> nmakehlp.out]
+!error *** Could not fully qualify path TMP_DIR=$(TMP_DIR)
!endif
+!include nmakehlp.out
+# The name of the stubs library for the project being built
+STUBPREFIX = $(PROJECT)stub
-#----------------------------------------------------------
-# Decode the checks requested.
-#----------------------------------------------------------
+# Set up paths to various Tcl executables and libraries needed by extensions
+!if $(DOING_TCL)
-!if "$(CHECKS)" == "" || [nmakehlp -f "$(CHECKS)" "none"]
-TCL_NO_DEPRECATED = 0
-WARNINGS = -W3
-!else
-!if [nmakehlp -f $(CHECKS) "nodep"]
-!message *** Doing nodep check
-TCL_NO_DEPRECATED = 1
-!else
-TCL_NO_DEPRECATED = 0
+TCLSHNAME = $(PROJECT)sh$(TCL_VERSION)$(SUFX).exe
+TCLSH = $(OUT_DIR)\$(TCLSHNAME)
+TCLIMPLIB = $(OUT_DIR)\$(PROJECT)$(VERSION)$(SUFX).lib
+TCLLIBNAME = $(PROJECT)$(VERSION)$(SUFX).$(EXT)
+TCLLIB = $(OUT_DIR)\$(TCLLIBNAME)
+
+TCLSTUBLIBNAME = $(STUBPREFIX)$(VERSION).lib
+TCLSTUBLIB = $(OUT_DIR)\$(TCLSTUBLIBNAME)
+TCL_INCLUDES = -I"$(WINDIR)" -I"$(GENERICDIR)"
+
+!else # ! $(DOING_TCL)
+
+!if $(TCLINSTALL) # Building against an installed Tcl
+
+# When building extensions, we need to locate tclsh. Depending on version
+# of Tcl we are building against, this may or may not have a "t" suffix.
+# Try various possibilities in turn.
+TCLSH = $(_TCLDIR)\bin\tclsh$(TCL_VERSION)$(SUFX).exe
+!if !exist("$(TCLSH)") && $(TCL_THREADS)
+TCLSH = $(_TCLDIR)\bin\tclsh$(TCL_VERSION)t$(SUFX).exe
!endif
-!if [nmakehlp -f $(CHECKS) "fullwarn"]
-!message *** Doing full warnings check
-WARNINGS = -W4
-!if [nmakehlp -l -warn:3 $(LINKER_TESTFLAGS)]
-LINKERFLAGS = $(LINKERFLAGS) -warn:3
+!if !exist("$(TCLSH)")
+TCLSH = $(_TCLDIR)\bin\tclsh$(TCL_VERSION)$(SUFX:t=).exe
!endif
-!else
-WARNINGS = -W3
+
+TCLSTUBLIB = $(_TCLDIR)\lib\tclstub$(TCL_VERSION).lib
+TCLIMPLIB = $(_TCLDIR)\lib\tcl$(TCL_VERSION)$(SUFX).lib
+# When building extensions, may be linking against Tcl that does not add
+# "t" suffix (e.g. 8.5 or 8.7). If lib not found check for that possibility.
+!if !exist("$(TCLIMPLIB)")
+TCLIMPLIB = $(_TCLDIR)\lib\tcl$(TCL_VERSION)$(SUFX:t=).lib
!endif
-!if [nmakehlp -f $(CHECKS) "64bit"] && [nmakehlp -c -Wp64]
-!message *** Doing 64bit portability warnings
-WARNINGS = $(WARNINGS) -Wp64
+TCL_LIBRARY = $(_TCLDIR)\lib
+TCLREGLIB = $(_TCLDIR)\lib\tclreg13$(SUFX:t=).lib
+TCLDDELIB = $(_TCLDIR)\lib\tcldde14$(SUFX:t=).lib
+TCLTOOLSDIR = \must\have\tcl\sources\to\build\this\target
+TCL_INCLUDES = -I"$(_TCLDIR)\include"
+
+!else # Building against Tcl sources
+
+TCLSH = $(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)$(SUFX).exe
+!if !exist($(TCLSH)) && $(TCL_THREADS)
+TCLSH = $(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)t$(SUFX).exe
!endif
+!if !exist($(TCLSH))
+TCLSH = $(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)$(SUFX:t=).exe
!endif
+TCLSTUBLIB = $(_TCLDIR)\win\$(BUILDDIRTOP)\tclstub$(TCL_VERSION).lib
+TCLIMPLIB = $(_TCLDIR)\win\$(BUILDDIRTOP)\tcl$(TCL_VERSION)$(SUFX).lib
+# When building extensions, may be linking against Tcl that does not add
+# "t" suffix (e.g. 8.5 or 8.7). If lib not found check for that possibility.
+!if !exist("$(TCLIMPLIB)")
+TCLIMPLIB = $(_TCLDIR)\win\$(BUILDDIRTOP)\tcl$(TCL_VERSION)$(SUFX:t=).lib
+!endif
+TCL_LIBRARY = $(_TCLDIR)\library
+TCLREGLIB = $(_TCLDIR)\win\$(BUILDDIRTOP)\tclreg13$(SUFX:t=).lib
+TCLDDELIB = $(_TCLDIR)\win\$(BUILDDIRTOP)\tcldde14$(SUFX:t=).lib
+TCLTOOLSDIR = $(_TCLDIR)\tools
+TCL_INCLUDES = -I"$(_TCLDIR)\generic" -I"$(_TCLDIR)\win"
-!if $(PGO) > 1
-!if [nmakehlp -l -ltcg:pgoptimize $(LINKER_TESTFLAGS)]
-LINKERFLAGS = $(LINKERFLAGS:-ltcg=) -ltcg:pgoptimize
+!endif # TCLINSTALL
+
+tcllibs = "$(TCLSTUBLIB)" "$(TCLIMPLIB)"
+
+!endif # $(DOING_TCL)
+
+# We need a tclsh that will run on the host machine as part of the build.
+# IX86 runs on all architectures.
+!ifndef TCLSH_NATIVE
+!if "$(MACHINE)" == "IX86" || "$(MACHINE)" == "$(NATIVE_ARCH)"
+TCLSH_NATIVE = $(TCLSH)
!else
-MSG=^
-This compiler does not support profile guided optimization.
-!error $(MSG)
+!error You must explicitly set TCLSH_NATIVE for cross-compilation
!endif
-!elseif $(PGO) > 0
-!if [nmakehlp -l -ltcg:pginstrument $(LINKER_TESTFLAGS)]
-LINKERFLAGS = $(LINKERFLAGS:-ltcg=) -ltcg:pginstrument
-!else
-MSG=^
-This compiler does not support profile guided optimization.
-!error $(MSG)
!endif
+
+# Do the same for Tk and Tk extensions that require the Tk libraries
+!if $(DOING_TK) || $(NEED_TK)
+WISHNAMEPREFIX = wish
+WISHNAME = $(WISHNAMEPREFIX)$(TK_VERSION)$(SUFX).exe
+TKLIBNAME = $(PROJECT)$(TK_VERSION)$(SUFX).$(EXT)
+TKSTUBLIBNAME = tkstub$(TK_VERSION).lib
+TKIMPLIBNAME = tk$(TK_VERSION)$(SUFX).lib
+
+!if $(DOING_TK)
+WISH = $(OUT_DIR)\$(WISHNAME)
+TKSTUBLIB = $(OUT_DIR)\$(TKSTUBLIBNAME)
+TKIMPLIB = $(OUT_DIR)\$(TKIMPLIBNAME)
+TKLIB = $(OUT_DIR)\$(TKLIBNAME)
+TK_INCLUDES = -I"$(WINDIR)" -I"$(GENERICDIR)"
+
+!else # effectively NEED_TK
+
+!if $(TKINSTALL) # Building against installed Tk
+WISH = $(_TKDIR)\bin\$(WISHNAME)
+TKSTUBLIB = $(_TKDIR)\lib\$(TKSTUBLIBNAME)
+TKIMPLIB = $(_TKDIR)\lib\$(TKIMPLIBNAME)
+# When building extensions, may be linking against Tk that does not add
+# "t" suffix (e.g. 8.5 or 8.7). If lib not found check for that possibility.
+!if !exist("$(TKIMPLIB)")
+TKIMPLIBNAME = tk$(TK_VERSION)$(SUFX:t=).lib
+TKIMPLIB = $(_TKDIR)\lib\$(TKIMPLIBNAME)
+!endif
+TK_INCLUDES = -I"$(_TKDIR)\include"
+!else # Building against Tk sources
+WISH = $(_TKDIR)\win\$(BUILDDIRTOP)\$(WISHNAME)
+TKSTUBLIB = $(_TKDIR)\win\$(BUILDDIRTOP)\$(TKSTUBLIBNAME)
+TKIMPLIB = $(_TKDIR)\win\$(BUILDDIRTOP)\$(TKIMPLIBNAME)
+# When building extensions, may be linking against Tk that does not add
+# "t" suffix (e.g. 8.5 or 8.7). If lib not found check for that possibility.
+!if !exist("$(TKIMPLIB)")
+TKIMPLIBNAME = tk$(TK_VERSION)$(SUFX:t=).lib
+TKIMPLIB = $(_TKDIR)\win\$(BUILDDIRTOP)\$(TKIMPLIBNAME)
!endif
+TK_INCLUDES = -I"$(_TKDIR)\generic" -I"$(_TKDIR)\win" -I"$(_TKDIR)\xlib"
+!endif # TKINSTALL
+tklibs = "$(TKSTUBLIB)" "$(TKIMPLIB)"
-#----------------------------------------------------------
-# Set our defines now armed with our options.
-#----------------------------------------------------------
+!endif # $(DOING_TK)
+!endif # $(DOING_TK) || $(NEED_TK)
+
+# Various output paths
+PRJIMPLIB = $(OUT_DIR)\$(PROJECT)$(VERSION)$(SUFX).lib
+PRJLIBNAME = $(PROJECT)$(VERSION)$(SUFX).$(EXT)
+PRJLIB = $(OUT_DIR)\$(PRJLIBNAME)
+
+PRJSTUBLIBNAME = $(STUBPREFIX)$(VERSION).lib
+PRJSTUBLIB = $(OUT_DIR)\$(PRJSTUBLIBNAME)
+
+# If extension parent makefile has not defined a resource definition file,
+# we will generate one from standard template.
+!if !$(DOING_TCL) && !$(DOING_TK) && !$(STATIC_BUILD)
+!ifdef RCFILE
+RESFILE = $(TMP_DIR)\$(RCFILE:.rc=.res)
+!else
+RESFILE = $(TMP_DIR)\$(PROJECT).res
+!endif
+!endif
+
+###################################################################
+# 11. Construct the paths for the installation directories
+# The following macros get defined in this section:
+# LIB_INSTALL_DIR - where libraries should be installed
+# BIN_INSTALL_DIR - where the executables should be installed
+# DOC_INSTALL_DIR - where documentation should be installed
+# SCRIPT_INSTALL_DIR - where scripts should be installed
+# INCLUDE_INSTALL_DIR - where C include files should be installed
+# DEMO_INSTALL_DIR - where demos should be installed
+# PRJ_INSTALL_DIR - where package will be installed (not set for Tcl and Tk)
+
+!if $(DOING_TCL) || $(DOING_TK)
+LIB_INSTALL_DIR = $(_INSTALLDIR)\lib
+BIN_INSTALL_DIR = $(_INSTALLDIR)\bin
+DOC_INSTALL_DIR = $(_INSTALLDIR)\doc
+!if $(DOING_TCL)
+SCRIPT_INSTALL_DIR = $(_INSTALLDIR)\lib\$(PROJECT)$(TCL_MAJOR_VERSION).$(TCL_MINOR_VERSION)
+!else # DOING_TK
+SCRIPT_INSTALL_DIR = $(_INSTALLDIR)\lib\$(PROJECT)$(TK_MAJOR_VERSION).$(TK_MINOR_VERSION)
+!endif
+DEMO_INSTALL_DIR = $(SCRIPT_INSTALL_DIR)\demos
+INCLUDE_INSTALL_DIR = $(_INSTALLDIR)\include
+
+!else # extension other than Tk
+
+PRJ_INSTALL_DIR = $(_INSTALLDIR)\$(PROJECT)$(DOTVERSION)
+LIB_INSTALL_DIR = $(PRJ_INSTALL_DIR)
+BIN_INSTALL_DIR = $(PRJ_INSTALL_DIR)
+DOC_INSTALL_DIR = $(PRJ_INSTALL_DIR)
+SCRIPT_INSTALL_DIR = $(PRJ_INSTALL_DIR)
+DEMO_INSTALL_DIR = $(PRJ_INSTALL_DIR)\demos
+INCLUDE_INSTALL_DIR = $(_INSTALLDIR)\..\include
+
+!endif
+
+###################################################################
+# 12. Set up actual options to be passed to the compiler and linker
+# Now we have all the information we need, set up the actual flags and
+# options that we will pass to the compiler and linker. The main
+# makefile should use these in combination with whatever other flags
+# and switches are specific to it.
+# The following macros are defined, names are for historical compatibility:
+# OPTDEFINES - /Dxxx C macro flags based on user-specified OPTS
+# COMPILERFLAGS - /Dxxx C macro flags independent of any configuration opttions
+# crt - Compiler switch that selects the appropriate C runtime
+# cdebug - Compiler switches related to debug AND optimizations
+# cwarn - Compiler switches that set warning levels
+# cflags - complete compiler switches (subsumes cdebug and cwarn)
+# ldebug - Linker switches controlling debug information and optimization
+# lflags - complete linker switches (subsumes ldebug) except subsystem type
+# dlllflags - complete linker switches to build DLLs (subsumes lflags)
+# conlflags - complete linker switches for console program (subsumes lflags)
+# guilflags - complete linker switches for GUI program (subsumes lflags)
+# baselibs - minimum Windows libraries required. Parent makefile can
+# define PRJ_LIBS before including rules.rc if additional libs are needed
OPTDEFINES = -DTCL_CFGVAL_ENCODING=$(CFG_ENCODING) -DSTDC_HEADERS
@@ -477,6 +1277,17 @@ OPTDEFINES = $(OPTDEFINES) -DSTATIC_BUILD
OPTDEFINES = $(OPTDEFINES) -DTCL_NO_DEPRECATED
!endif
+!if $(USE_STUBS)
+# Note we do not define USE_TCL_STUBS even when building tk since some
+# test targets in tk do not use stubs
+!if ! $(DOING_TCL)
+USE_STUBS_DEFS = -DUSE_TCL_STUBS -DUSE_TCLOO_STUBS
+!if $(NEED_TK)
+USE_STUBS_DEFS = $(USE_STUBS_DEFS) -DUSE_TK_STUBS
+!endif
+!endif
+!endif # USE_STUBS
+
!if !$(DEBUG)
OPTDEFINES = $(OPTDEFINES) -DNDEBUG
!if $(OPTIMIZING)
@@ -486,223 +1297,456 @@ OPTDEFINES = $(OPTDEFINES) -DTCL_CFG_OPTIMIZED
!if $(PROFILE)
OPTDEFINES = $(OPTDEFINES) -DTCL_CFG_PROFILED
!endif
-!if "$(MACHINE)" == "IA64" || "$(MACHINE)" == "AMD64"
+!if "$(MACHINE)" == "AMD64"
OPTDEFINES = $(OPTDEFINES) -DTCL_CFG_DO64BIT
!endif
!if $(VCVERSION) < 1300
OPTDEFINES = $(OPTDEFINES) -DNO_STRTOI64
!endif
-#----------------------------------------------------------
-# Locate the Tcl headers to build against
-#----------------------------------------------------------
-
-!if "$(PROJECT)" == "tcl"
-
-_TCL_H = ..\generic\tcl.h
+# _ATL_XP_TARGETING - Newer SDK's need this to build for XP
+COMPILERFLAGS = /D_ATL_XP_TARGETING
+# Following is primarily for the benefit of extensions. Tcl 8.5 builds
+# Tcl without /DUNICODE, while 8.6 builds with it defined. When building
+# an extension, it is advisable (but not mandated) to use the same Windows
+# API as the Tcl build. This is accordingly defaulted below. A particular
+# extension can override this by pre-definining USE_WIDECHAR_API.
+!ifndef USE_WIDECHAR_API
+!if $(TCL_VERSION) > 85
+USE_WIDECHAR_API = 1
!else
+USE_WIDECHAR_API = 0
+!endif
+!endif
-# If INSTALLDIR set to tcl root dir then reset to the lib dir.
-!if exist("$(_INSTALLDIR)\include\tcl.h")
-_INSTALLDIR=$(_INSTALLDIR)\lib
+!if $(USE_WIDECHAR_API)
+COMPILERFLAGS = $(COMPILERFLAGS) /DUNICODE /D_UNICODE
!endif
-!if !defined(TCLDIR)
-!if exist("$(_INSTALLDIR)\..\include\tcl.h")
-TCLINSTALL = 1
-_TCLDIR = $(_INSTALLDIR)\..
-_TCL_H = $(_INSTALLDIR)\..\include\tcl.h
-TCLDIR = $(_INSTALLDIR)\..
+# Like the TEA system only set this non empty for non-Tk extensions
+# Note: some extensions use PACKAGE_NAME and others use PACKAGE_TCLNAME
+# so we pass both
+!if !$(DOING_TCL) && !$(DOING_TK)
+PKGNAMEFLAGS = -DPACKAGE_NAME="\"$(PRJ_PACKAGE_TCLNAME)\"" \
+ -DPACKAGE_TCLNAME="\"$(PRJ_PACKAGE_TCLNAME)\"" \
+ -DPACKAGE_VERSION="\"$(DOTVERSION)\"" \
+ -DMODULE_SCOPE=extern
+!endif
+
+# crt picks the C run time based on selected OPTS
+!if $(MSVCRT)
+!if $(DEBUG) && !$(UNCHECKED)
+crt = -MDd
!else
-MSG=^
-Failed to find tcl.h. Set the TCLDIR macro.
-!error $(MSG)
+crt = -MD
!endif
!else
-_TCLDIR = $(TCLDIR:/=\)
-!if exist("$(_TCLDIR)\include\tcl.h")
-TCLINSTALL = 1
-_TCL_H = $(_TCLDIR)\include\tcl.h
-!elseif exist("$(_TCLDIR)\generic\tcl.h")
-TCLINSTALL = 0
-_TCL_H = $(_TCLDIR)\generic\tcl.h
+!if $(DEBUG) && !$(UNCHECKED)
+crt = -MTd
!else
-MSG =^
-Failed to find tcl.h. The TCLDIR macro does not appear correct.
-!error $(MSG)
-!endif
+crt = -MT
!endif
!endif
-#--------------------------------------------------------------
-# Extract various version numbers from tcl headers
-# The generated file is then included in the makefile.
-#--------------------------------------------------------------
+# cdebug includes compiler options for debugging as well as optimization.
+!if $(DEBUG)
-!if [echo REM = This file is generated from rules.vc > versions.vc]
-!endif
-!if [echo TCL_MAJOR_VERSION = \>> versions.vc] \
- && [nmakehlp -V "$(_TCL_H)" TCL_MAJOR_VERSION >> versions.vc]
-!endif
-!if [echo TCL_MINOR_VERSION = \>> versions.vc] \
- && [nmakehlp -V "$(_TCL_H)" TCL_MINOR_VERSION >> versions.vc]
-!endif
-!if [echo TCL_PATCH_LEVEL = \>> versions.vc] \
- && [nmakehlp -V "$(_TCL_H)" TCL_PATCH_LEVEL >> versions.vc]
+# In debugging mode, optimizations need to be disabled
+cdebug = -Zi -Od $(DEBUGFLAGS)
+
+!else
+
+cdebug = $(OPTIMIZATIONS)
+!if $(SYMBOLS)
+cdebug = $(cdebug) -Zi
!endif
-# If building the tcl core then we need additional package versions
-!if "$(PROJECT)" == "tcl"
-!if [echo PKG_HTTP_VER = \>> versions.vc] \
- && [nmakehlp -V ..\library\http\pkgIndex.tcl http >> versions.vc]
+!endif # $(DEBUG)
+
+# cwarn includes default warning levels.
+cwarn = $(WARNINGS)
+
+!if "$(MACHINE)" == "AMD64"
+# Disable pointer<->int warnings related to cast between different sizes
+# There are a gadzillion of these due to use of ClientData and
+# clutter up compiler
+# output increasing chance of a real warning getting lost. So disable them.
+# Eventually some day, Tcl will be 64-bit clean.
+cwarn = $(cwarn) -wd4311 -wd4312
!endif
-!if [echo PKG_TCLTEST_VER = \>> versions.vc] \
- && [nmakehlp -V ..\library\tcltest\pkgIndex.tcl tcltest >> versions.vc]
+
+### Common compiler options that are architecture specific
+!if "$(MACHINE)" == "ARM"
+carch = -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE
+!else
+carch =
!endif
-!if [echo PKG_MSGCAT_VER = \>> versions.vc] \
- && [nmakehlp -V ..\library\msgcat\pkgIndex.tcl msgcat >> versions.vc]
+
+!if $(DEBUG)
+# Turn warnings into errors
+cwarn = $(cwarn) -WX
!endif
-!if [echo PKG_PLATFORM_VER = \>> versions.vc] \
- && [nmakehlp -V ..\library\platform\pkgIndex.tcl "platform " >> versions.vc]
+
+INCLUDES = $(TCL_INCLUDES) $(TK_INCLUDES) $(PRJ_INCLUDES)
+!if !$(DOING_TCL) && !$(DOING_TK)
+INCLUDES = $(INCLUDES) -I"$(GENERICDIR)" -I"$(WINDIR)" -I"$(COMPATDIR)"
!endif
-!if [echo PKG_SHELL_VER = \>> versions.vc] \
- && [nmakehlp -V ..\library\platform\pkgIndex.tcl "platform::shell" >> versions.vc]
+
+# These flags are defined roughly in the order of the pre-reform
+# rules.vc/makefile.vc to help visually compare that the pre- and
+# post-reform build logs
+
+# cflags contains generic flags used for building practically all object files
+cflags = -nologo -c $(COMPILERFLAGS) $(carch) $(cwarn) -Fp$(TMP_DIR)^\ $(cdebug)
+
+# appcflags contains $(cflags) and flags for building the application
+# object files (e.g. tclsh, or wish) pkgcflags contains $(cflags) plus
+# flags used for building shared object files The two differ in the
+# BUILD_$(PROJECT) macro which should be defined only for the shared
+# library *implementation* and not for its caller interface
+
+appcflags = $(cflags) $(crt) $(INCLUDES) $(TCL_DEFINES) $(PRJ_DEFINES) $(OPTDEFINES) $(USE_STUBS_DEFS)
+appcflags_nostubs = $(cflags) $(crt) $(INCLUDES) $(TCL_DEFINES) $(PRJ_DEFINES) $(OPTDEFINES)
+pkgcflags = $(appcflags) $(PKGNAMEFLAGS) -DBUILD_$(PROJECT)
+pkgcflags_nostubs = $(appcflags_nostubs) $(PKGNAMEFLAGS) -DBUILD_$(PROJECT)
+
+# stubscflags contains $(cflags) plus flags used for building a stubs
+# library for the package. Note: -DSTATIC_BUILD is defined in
+# $(OPTDEFINES) only if the OPTS configuration indicates a static
+# library. However the stubs library is ALWAYS static hence included
+# here irrespective of the OPTS setting.
+#
+# TBD - tclvfs has a comment that stubs libs should not be compiled with -GL
+# without stating why. Tcl itself compiled stubs libs with this flag.
+# so we do not remove it from cflags. -GL may prevent extensions
+# compiled with one VC version to fail to link against stubs library
+# compiled with another VC version. Check for this and fix accordingly.
+stubscflags = $(cflags) $(PKGNAMEFLAGS) $(PRJ_DEFINES) $(OPTDEFINES) -Zl -DSTATIC_BUILD $(INCLUDES)
+
+# Link flags
+
+!if $(DEBUG)
+ldebug = -debug -debugtype:cv
+!else
+ldebug = -release -opt:ref -opt:icf,3
+!if $(SYMBOLS)
+ldebug = $(ldebug) -debug -debugtype:cv
!endif
-!if [echo PKG_DDE_VER = \>> versions.vc] \
- && [nmakehlp -V ..\library\dde\pkgIndex.tcl "dde " >> versions.vc]
!endif
-!if [echo PKG_REG_VER =\>> versions.vc] \
- && [nmakehlp -V ..\library\reg\pkgIndex.tcl registry >> versions.vc]
+
+# Note: Profiling is currently only possible with the Visual Studio Enterprise
+!if $(PROFILE)
+ldebug= $(ldebug) -profile
!endif
+
+### Declarations common to all linker versions
+lflags = -nologo -machine:$(MACHINE) $(LINKERFLAGS) $(ldebug)
+
+!if $(MSVCRT) && !($(DEBUG) && !$(UNCHECKED)) && $(VCVERSION) >= 1900
+lflags = $(lflags) -nodefaultlib:libucrt.lib
!endif
-!include versions.vc
+# Old linkers (Visual C++ 6 in particular) will link for fast loading
+# on Win98. Since we do not support Win98 any more, we specify nowin98
+# as recommended for NT and later. However, this is only required by
+# IX86 on older compilers and only needed if we are not doing a static build.
-#--------------------------------------------------------------
-# Setup tcl version dependent stuff headers
-#--------------------------------------------------------------
+!if "$(MACHINE)" == "IX86" && !$(STATIC_BUILD)
+!if [nmakehlp -l -opt:nowin98 $(LINKER_TESTFLAGS)]
+# Align sections for PE size savings.
+lflags = $(lflags) -opt:nowin98
+!endif
+!endif
-!if "$(PROJECT)" != "tcl"
+dlllflags = $(lflags) -dll
+conlflags = $(lflags) -subsystem:console
+guilflags = $(lflags) -subsystem:windows
-TCL_VERSION = $(TCL_MAJOR_VERSION)$(TCL_MINOR_VERSION)
+# Libraries that are required for every image.
+# Extensions should define any additional libraries with $(PRJ_LIBS)
+winlibs = kernel32.lib advapi32.lib
-!if $(TCLINSTALL)
-TCLSH = "$(_TCLDIR)\bin\tclsh$(TCL_VERSION)$(SUFX).exe"
-!if !exist($(TCLSH)) && $(TCL_THREADS)
-TCLSH = "$(_TCLDIR)\bin\tclsh$(TCL_VERSION)t$(SUFX).exe"
+!if $(NEED_TK)
+winlibs = $(winlibs) gdi32.lib user32.lib uxtheme.lib
!endif
-TCLSTUBLIB = "$(_TCLDIR)\lib\tclstub$(TCL_VERSION).lib"
-TCLIMPLIB = "$(_TCLDIR)\lib\tcl$(TCL_VERSION)$(SUFX).lib"
-TCL_LIBRARY = $(_TCLDIR)\lib
-TCLREGLIB = "$(_TCLDIR)\lib\tclreg13$(SUFX:t=).lib"
-TCLDDELIB = "$(_TCLDIR)\lib\tcldde14$(SUFX:t=).lib"
-COFFBASE = \must\have\tcl\sources\to\build\this\target
-TCLTOOLSDIR = \must\have\tcl\sources\to\build\this\target
-TCL_INCLUDES = -I"$(_TCLDIR)\include"
-!else
-TCLSH = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)$(SUFX).exe"
-!if !exist($(TCLSH)) && $(TCL_THREADS)
-TCLSH = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)t$(SUFX).exe"
+
+# Avoid 'unresolved external symbol __security_cookie' errors.
+# c.f. http://support.microsoft.com/?id=894573
+!if "$(MACHINE)" == "AMD64"
+!if $(VCVERSION) > 1399 && $(VCVERSION) < 1500
+winlibs = $(winlibs) bufferoverflowU.lib
!endif
-TCLSTUBLIB = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tclstub$(TCL_VERSION).lib"
-TCLIMPLIB = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tcl$(TCL_VERSION)$(SUFX).lib"
-TCL_LIBRARY = $(_TCLDIR)\library
-TCLREGLIB = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tclreg13$(SUFX:t=).lib"
-TCLDDELIB = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tcldde14$(SUFX:t=).lib"
-COFFBASE = "$(_TCLDIR)\win\coffbase.txt"
-TCLTOOLSDIR = $(_TCLDIR)\tools
-TCL_INCLUDES = -I"$(_TCLDIR)\generic" -I"$(_TCLDIR)\win"
!endif
+baselibs = $(winlibs) $(PRJ_LIBS)
+
+!if $(MSVCRT) && !($(DEBUG) && !$(UNCHECKED)) && $(VCVERSION) >= 1900
+baselibs = $(baselibs) ucrt.lib
!endif
-#-------------------------------------------------------------------------
-# Locate the Tk headers to build against
-#-------------------------------------------------------------------------
+################################################################
+# 13. Define standard commands, common make targets and implicit rules
-!if "$(PROJECT)" == "tk"
-_TK_H = ..\generic\tk.h
-_INSTALLDIR = $(_INSTALLDIR)\..
-!endif
+CCPKGCMD = $(cc32) $(pkgcflags) -Fo$(TMP_DIR)^\
+CCAPPCMD = $(cc32) $(appcflags) -Fo$(TMP_DIR)^\
+CCSTUBSCMD = $(cc32) $(stubscflags) -Fo$(TMP_DIR)^\
-!ifdef PROJECT_REQUIRES_TK
-!if !defined(TKDIR)
-!if exist("$(_INSTALLDIR)\..\include\tk.h")
-TKINSTALL = 1
-_TKDIR = $(_INSTALLDIR)\..
-_TK_H = $(_TKDIR)\include\tk.h
-TKDIR = $(_TKDIR)
-!elseif exist("$(_TCLDIR)\include\tk.h")
-TKINSTALL = 1
-_TKDIR = $(_TCLDIR)
-_TK_H = $(_TKDIR)\include\tk.h
-TKDIR = $(_TKDIR)
+LIBCMD = $(lib32) -nologo $(LINKERFLAGS) -out:$@
+DLLCMD = $(link32) $(dlllflags) -out:$@ $(baselibs) $(tcllibs) $(tklibs)
+
+CONEXECMD = $(link32) $(conlflags) -out:$@ $(baselibs) $(tcllibs) $(tklibs)
+GUIEXECMD = $(link32) $(guilflags) -out:$@ $(baselibs) $(tcllibs) $(tklibs)
+RESCMD = $(rc32) -fo $@ -r -i "$(GENERICDIR)" -i "$(TMP_DIR)" \
+ $(TCL_INCLUDES) \
+ -DDEBUG=$(DEBUG) -d UNCHECKED=$(UNCHECKED) \
+ -DCOMMAVERSION=$(DOTVERSION:.=,),0 \
+ -DDOTVERSION=\"$(DOTVERSION)\" \
+ -DVERSION=\"$(VERSION)\" \
+ -DSUFX=\"$(SUFX)\" \
+ -DPROJECT=\"$(PROJECT)\" \
+ -DPRJLIBNAME=\"$(PRJLIBNAME)\"
+
+!ifndef DEFAULT_BUILD_TARGET
+DEFAULT_BUILD_TARGET = $(PROJECT)
!endif
+
+default-target: $(DEFAULT_BUILD_TARGET)
+
+default-pkgindex:
+ @echo package ifneeded $(PRJ_PACKAGE_TCLNAME) $(DOTVERSION) \
+ [list load [file join $$dir $(PRJLIBNAME)]] > $(OUT_DIR)\pkgIndex.tcl
+
+default-pkgindex-tea:
+ @if exist $(ROOT)\pkgIndex.tcl.in nmakehlp -s << $(ROOT)\pkgIndex.tcl.in > $(OUT_DIR)\pkgIndex.tcl
+@PACKAGE_VERSION@ $(DOTVERSION)
+@PACKAGE_NAME@ $(PRJ_PACKAGE_TCLNAME)
+@PACKAGE_TCLNAME@ $(PRJ_PACKAGE_TCLNAME)
+@PKG_LIB_FILE@ $(PRJLIBNAME)
+<<
+
+
+default-install: default-install-binaries default-install-libraries
+
+default-install-binaries: $(PRJLIB)
+ @echo Installing binaries to '$(SCRIPT_INSTALL_DIR)'
+ @if not exist "$(SCRIPT_INSTALL_DIR)" mkdir "$(SCRIPT_INSTALL_DIR)"
+ @$(CPY) $(PRJLIB) "$(SCRIPT_INSTALL_DIR)" >NUL
+
+default-install-libraries: $(OUT_DIR)\pkgIndex.tcl
+ @echo Installing libraries to '$(SCRIPT_INSTALL_DIR)'
+ @if exist $(LIBDIR) $(CPY) $(LIBDIR)\*.tcl "$(SCRIPT_INSTALL_DIR)"
+ @echo Installing package index in '$(SCRIPT_INSTALL_DIR)'
+ @$(CPY) $(OUT_DIR)\pkgIndex.tcl $(SCRIPT_INSTALL_DIR)
+
+default-install-stubs:
+ @echo Installing stubs library to '$(SCRIPT_INSTALL_DIR)'
+ @if not exist "$(SCRIPT_INSTALL_DIR)" mkdir "$(SCRIPT_INSTALL_DIR)"
+ @$(CPY) $(PRJSTUBLIB) "$(SCRIPT_INSTALL_DIR)" >NUL
+
+default-install-docs-html:
+ @echo Installing documentation files to '$(DOC_INSTALL_DIR)'
+ @if not exist "$(DOC_INSTALL_DIR)" mkdir "$(DOC_INSTALL_DIR)"
+ @if exist $(DOCDIR) for %f in ("$(DOCDIR)\*.html" "$(DOCDIR)\*.css" "$(DOCDIR)\*.png") do @$(COPY) %f "$(DOC_INSTALL_DIR)"
+
+default-install-docs-n:
+ @echo Installing documentation files to '$(DOC_INSTALL_DIR)'
+ @if not exist "$(DOC_INSTALL_DIR)" mkdir "$(DOC_INSTALL_DIR)"
+ @if exist $(DOCDIR) for %f in ("$(DOCDIR)\*.n") do @$(COPY) %f "$(DOC_INSTALL_DIR)"
+
+default-install-demos:
+ @echo Installing demos to '$(DEMO_INSTALL_DIR)'
+ @if not exist "$(DEMO_INSTALL_DIR)" mkdir "$(DEMO_INSTALL_DIR)"
+ @if exist $(DEMODIR) $(CPYDIR) "$(DEMODIR)" "$(DEMO_INSTALL_DIR)"
+
+default-clean:
+ @echo Cleaning $(TMP_DIR)\* ...
+ @if exist $(TMP_DIR)\nul $(RMDIR) $(TMP_DIR)
+ @echo Cleaning $(WINDIR)\nmakehlp.obj, nmakehlp.exe ...
+ @if exist $(WINDIR)\nmakehlp.obj del $(WINDIR)\nmakehlp.obj
+ @if exist $(WINDIR)\nmakehlp.exe del $(WINDIR)\nmakehlp.exe
+ @if exist $(WINDIR)\nmakehlp.out del $(WINDIR)\nmakehlp.out
+ @echo Cleaning $(WINDIR)\nmhlp-out.txt ...
+ @if exist $(WINDIR)\nmhlp-out.txt del $(WINDIR)\nmhlp-out.txt
+ @echo Cleaning $(WINDIR)\_junk.pch ...
+ @if exist $(WINDIR)\_junk.pch del $(WINDIR)\_junk.pch
+ @echo Cleaning $(WINDIR)\vercl.x, vercl.i ...
+ @if exist $(WINDIR)\vercl.x del $(WINDIR)\vercl.x
+ @if exist $(WINDIR)\vercl.i del $(WINDIR)\vercl.i
+ @echo Cleaning $(WINDIR)\versions.vc, version.vc ...
+ @if exist $(WINDIR)\versions.vc del $(WINDIR)\versions.vc
+ @if exist $(WINDIR)\version.vc del $(WINDIR)\version.vc
+
+default-hose: default-clean
+ @echo Hosing $(OUT_DIR)\* ...
+ @if exist $(OUT_DIR)\nul $(RMDIR) $(OUT_DIR)
+
+# Only for backward compatibility
+default-distclean: default-hose
+
+default-setup:
+ @if not exist $(OUT_DIR)\nul mkdir $(OUT_DIR)
+ @if not exist $(TMP_DIR)\nul mkdir $(TMP_DIR)
+
+!if "$(TESTPAT)" != ""
+TESTFLAGS = $(TESTFLAGS) -file $(TESTPAT)
+!endif
+
+default-test: default-setup $(PROJECT)
+ @set TCLLIBPATH=$(OUT_DIR:\=/)
+ @if exist $(LIBDIR) for %f in ("$(LIBDIR)\*.tcl") do @$(COPY) %f "$(OUT_DIR)"
+ cd "$(TESTDIR)" && $(DEBUGGER) $(TCLSH) all.tcl $(TESTFLAGS)
+
+default-shell: default-setup $(PROJECT)
+ @set TCLLIBPATH=$(OUT_DIR:\=/)
+ @if exist $(LIBDIR) for %f in ("$(LIBDIR)\*.tcl") do @$(COPY) %f "$(OUT_DIR)"
+ $(DEBUGGER) $(TCLSH)
+
+# Generation of Windows version resource
+!ifdef RCFILE
+
+# Note: don't use $** in below rule because there may be other dependencies
+# and only the "master" rc must be passed to the resource compiler
+$(TMP_DIR)\$(PROJECT).res: $(RCDIR)\$(PROJECT).rc
+ $(RESCMD) $(RCDIR)\$(PROJECT).rc
+
!else
-_TKDIR = $(TKDIR:/=\)
-!if exist("$(_TKDIR)\include\tk.h")
-TKINSTALL = 1
-_TK_H = $(_TKDIR)\include\tk.h
-!elseif exist("$(_TKDIR)\generic\tk.h")
-TKINSTALL = 0
-_TK_H = $(_TKDIR)\generic\tk.h
-!else
-MSG =^
-Failed to find tk.h. The TKDIR macro does not appear correct.
-!error $(MSG)
-!endif
-!endif
+
+# If parent makefile has not defined a resource definition file,
+# we will generate one from standard template.
+$(TMP_DIR)\$(PROJECT).res: $(TMP_DIR)\$(PROJECT).rc
+
+$(TMP_DIR)\$(PROJECT).rc:
+ @$(COPY) << $(TMP_DIR)\$(PROJECT).rc
+#include <winver.h>
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION COMMAVERSION
+ PRODUCTVERSION COMMAVERSION
+ FILEFLAGSMASK 0x3fL
+#ifdef DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS_NT_WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "FileDescription", "Tcl extension " PROJECT
+ VALUE "OriginalFilename", PRJLIBNAME
+ VALUE "FileVersion", DOTVERSION
+ VALUE "ProductName", "Package " PROJECT " for Tcl"
+ VALUE "ProductVersion", DOTVERSION
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+<<
+
+!endif # ifdef RCFILE
+
+!ifndef DISABLE_IMPLICIT_RULES
+DISABLE_IMPLICIT_RULES = 0
!endif
-#-------------------------------------------------------------------------
-# Extract Tk version numbers
-#-------------------------------------------------------------------------
+!if !$(DISABLE_IMPLICIT_RULES)
+# Implicit rule definitions - only for building library objects. For stubs and
+# main application, the master makefile should define explicit rules.
-!if defined(PROJECT_REQUIRES_TK) || "$(PROJECT)" == "tk"
+{$(ROOT)}.c{$(TMP_DIR)}.obj::
+ $(CCPKGCMD) @<<
+$<
+<<
+
+{$(WINDIR)}.c{$(TMP_DIR)}.obj::
+ $(CCPKGCMD) @<<
+$<
+<<
+
+{$(GENERICDIR)}.c{$(TMP_DIR)}.obj::
+ $(CCPKGCMD) @<<
+$<
+<<
+
+{$(COMPATDIR)}.c{$(TMP_DIR)}.obj::
+ $(CCPKGCMD) @<<
+$<
+<<
+
+{$(RCDIR)}.rc{$(TMP_DIR)}.res:
+ $(RESCMD) $<
+
+{$(WINDIR)}.rc{$(TMP_DIR)}.res:
+ $(RESCMD) $<
+
+{$(TMP_DIR)}.rc{$(TMP_DIR)}.res:
+ $(RESCMD) $<
+
+.SUFFIXES:
+.SUFFIXES:.c .rc
-!if [echo TK_MAJOR_VERSION = \>> versions.vc] \
- && [nmakehlp -V $(_TK_H) TK_MAJOR_VERSION >> versions.vc]
!endif
-!if [echo TK_MINOR_VERSION = \>> versions.vc] \
- && [nmakehlp -V $(_TK_H) TK_MINOR_VERSION >> versions.vc]
+
+################################################################
+# 14. Sanity check selected options against Tcl build options
+# When building an extension, certain configuration options should
+# match the ones used when Tcl was built. Here we check and
+# warn on a mismatch.
+!if ! $(DOING_TCL)
+
+!if $(TCLINSTALL) # Building against an installed Tcl
+!if exist("$(_TCLDIR)\lib\nmake\tcl.nmake")
+TCLNMAKECONFIG = "$(_TCLDIR)\lib\nmake\tcl.nmake"
!endif
-!if [echo TK_PATCH_LEVEL = \>> versions.vc] \
- && [nmakehlp -V $(_TK_H) TK_PATCH_LEVEL >> versions.vc]
+!else # ! $(TCLINSTALL) - building against Tcl source
+!if exist("$(OUT_DIR)\tcl.nmake")
+TCLNMAKECONFIG = "$(OUT_DIR)\tcl.nmake"
!endif
+!endif # TCLINSTALL
-!include versions.vc
+!if $(CONFIG_CHECK)
+!ifdef TCLNMAKECONFIG
+!include $(TCLNMAKECONFIG)
-TK_DOTVERSION = $(TK_MAJOR_VERSION).$(TK_MINOR_VERSION)
-TK_VERSION = $(TK_MAJOR_VERSION)$(TK_MINOR_VERSION)
-
-!if "$(PROJECT)" != "tk"
-!if $(TKINSTALL)
-WISH = "$(_TKDIR)\bin\wish$(TK_VERSION)$(SUFX).exe"
-TKSTUBLIB = "$(_TKDIR)\lib\tkstub$(TK_VERSION).lib"
-TKIMPLIB = "$(_TKDIR)\lib\tk$(TK_VERSION)$(SUFX).lib"
-TK_INCLUDES = -I"$(_TKDIR)\include"
-!else
-WISH = "$(_TKDIR)\win\$(BUILDDIRTOP)\wish$(TCL_VERSION)$(SUFX).exe"
-TKSTUBLIB = "$(_TKDIR)\win\$(BUILDDIRTOP)\tkstub$(TCL_VERSION).lib"
-TKIMPLIB = "$(_TKDIR)\win\$(BUILDDIRTOP)\tk$(TCL_VERSION)$(SUFX).lib"
-TK_INCLUDES = -I"$(_TKDIR)\generic" -I"$(_TKDIR)\win" -I"$(_TKDIR)\xlib"
+!if defined(CORE_MACHINE) && "$(CORE_MACHINE)" != "$(MACHINE)"
+!error ERROR: Build target ($(MACHINE)) does not match the Tcl library architecture ($(CORE_MACHINE)).
!endif
+!if defined(CORE_USE_THREAD_ALLOC) && $(CORE_USE_THREAD_ALLOC) != $(USE_THREAD_ALLOC)
+!message WARNING: Value of USE_THREAD_ALLOC ($(USE_THREAD_ALLOC)) does not match its Tcl core value ($(CORE_USE_THREAD_ALLOC)).
+!endif
+!if defined(CORE_DEBUG) && $(CORE_DEBUG) != $(DEBUG)
+!message WARNING: Value of DEBUG ($(DEBUG)) does not match its Tcl library configuration ($(DEBUG)).
!endif
-
!endif
+!endif # TCLNMAKECONFIG
+
+!endif # ! $(DOING_TCL)
+
+
#----------------------------------------------------------
# Display stats being used.
#----------------------------------------------------------
+!if !$(DOING_TCL)
+!message *** Building against Tcl at '$(_TCLDIR)'
+!endif
+!if !$(DOING_TK) && $(NEED_TK)
+!message *** Building against Tk at '$(_TKDIR)'
+!endif
!message *** Intermediate directory will be '$(TMP_DIR)'
!message *** Output directory will be '$(OUT_DIR)'
+!message *** Installation, if selected, will be in '$(_INSTALLDIR)'
!message *** Suffix for binaries will be '$(SUFX)'
-!message *** Optional defines are '$(OPTDEFINES)'
-!message *** Compiler version $(VCVER). Target machine is $(MACHINE)
-!message *** Host architecture is $(NATIVE_ARCH)
-!message *** Compiler options '$(COMPILERFLAGS) $(OPTIMIZATIONS) $(DEBUGFLAGS) $(WARNINGS)'
-!message *** Link options '$(LINKERFLAGS)'
+!message *** Compiler version $(VCVER). Target $(MACHINE), host $(NATIVE_ARCH).
-!endif
+!endif # ifdef _RULES_VC
diff --git a/win/targets.vc b/win/targets.vc
new file mode 100644
index 0000000..7f1d388
--- /dev/null
+++ b/win/targets.vc
@@ -0,0 +1,98 @@
+#------------------------------------------------------------- -*- makefile -*-
+# targets.vc --
+#
+# Part of the nmake based build system for Tcl and its extensions.
+# This file defines some standard targets for the convenience of extensions
+# and can be optionally included by the extension makefile.
+# See TIP 477 (https://core.tcl.tk/tips/doc/trunk/tip/477.md) for docs.
+
+$(PROJECT): setup pkgindex $(PRJLIB)
+
+!ifdef PRJ_STUBOBJS
+$(PROJECT): $(PRJSTUBLIB)
+$(PRJSTUBLIB): $(PRJ_STUBOBJS)
+ $(LIBCMD) $**
+
+$(PRJ_STUBOBJS):
+ $(CCSTUBSCMD) %s
+!endif # PRJ_STUBOBJS
+
+!ifdef PRJ_MANIFEST
+$(PROJECT): $(PRJLIB).manifest
+$(PRJLIB).manifest: $(PRJ_MANIFEST)
+ @nmakehlp -s << $** >$@
+@MACHINE@ $(MACHINE:IX86=X86)
+<<
+!endif
+
+!if "$(PROJECT)" != "tcl" && "$(PROJECT)" != "tk"
+$(PRJLIB): $(PRJ_OBJS) $(RESFILE)
+!if $(STATIC_BUILD)
+ $(LIBCMD) $**
+!else
+ $(DLLCMD) $**
+ $(_VC_MANIFEST_EMBED_DLL)
+!endif
+ -@del $*.exp
+!endif
+
+!if "$(PRJ_HEADERS)" != "" && "$(PRJ_OBJS)" != ""
+$(PRJ_OBJS): $(PRJ_HEADERS)
+!endif
+
+# If parent makefile has defined stub objects, add their installation
+# to the default install
+!if "$(PRJ_STUBOBJS)" != ""
+default-install: default-install-stubs
+!endif
+
+# Unlike the other default targets, these cannot be in rules.vc because
+# the executed command depends on existence of macro PRJ_HEADERS_PUBLIC
+# that the parent makefile will not define until after including rules-ext.vc
+!if "$(PRJ_HEADERS_PUBLIC)" != ""
+default-install: default-install-headers
+default-install-headers:
+ @echo Installing headers to '$(INCLUDE_INSTALL_DIR)'
+ @for %f in ($(PRJ_HEADERS_PUBLIC)) do @$(COPY) %f "$(INCLUDE_INSTALL_DIR)"
+!endif
+
+!if "$(DISABLE_STANDARD_TARGETS)" == ""
+DISABLE_STANDARD_TARGETS = 0
+!endif
+
+!if "$(DISABLE_TARGET_setup)" == ""
+DISABLE_TARGET_setup = 0
+!endif
+!if "$(DISABLE_TARGET_install)" == ""
+DISABLE_TARGET_install = 0
+!endif
+!if "$(DISABLE_TARGET_clean)" == ""
+DISABLE_TARGET_clean = 0
+!endif
+!if "$(DISABLE_TARGET_test)" == ""
+DISABLE_TARGET_test = 0
+!endif
+!if "$(DISABLE_TARGET_shell)" == ""
+DISABLE_TARGET_shell = 0
+!endif
+
+!if !$(DISABLE_STANDARD_TARGETS)
+!if !$(DISABLE_TARGET_setup)
+setup: default-setup
+!endif
+!if !$(DISABLE_TARGET_install)
+install: default-install
+!endif
+!if !$(DISABLE_TARGET_clean)
+clean: default-clean
+realclean: hose
+hose: default-hose
+distclean: realclean default-distclean
+!endif
+!if !$(DISABLE_TARGET_test)
+test: default-test
+!endif
+!if !$(DISABLE_TARGET_shell)
+shell: default-shell
+!endif
+!endif # DISABLE_STANDARD_TARGETS
diff --git a/win/tclAppInit.c b/win/tclAppInit.c
index e06eaf5..2236da3 100644
--- a/win/tclAppInit.c
+++ b/win/tclAppInit.c
@@ -35,8 +35,10 @@ extern Tcl_PackageInitProc Dde_Init;
extern Tcl_PackageInitProc Dde_SafeInit;
#endif
-#ifdef TCL_BROKEN_MAINARGS
+#if defined(__GNUC__) || defined(TCL_BROKEN_MAINARGS)
int _CRT_glob = 0;
+#endif /* __GNUC__ || TCL_BROKEN_MAINARGS */
+#ifdef TCL_BROKEN_MAINARGS
static void setargv(int *argcPtr, TCHAR ***argvPtr);
#endif /* TCL_BROKEN_MAINARGS */
diff --git a/win/tclWin32Dll.c b/win/tclWin32Dll.c
index 688fa8d..3fb8796 100644
--- a/win/tclWin32Dll.c
+++ b/win/tclWin32Dll.c
@@ -29,12 +29,10 @@ static int platformId; /* Running under NT, or 95/98? */
* VC++ 5.x has no 'cpuid' assembler instruction, so we must emulate it
*/
-#if defined(_MSC_VER) && (_MSC_VER <= 1100)
+#if defined(_MSC_VER) && (_MSC_VER <= 1100) && defined (_M_IX86)
#define cpuid __asm __emit 0fh __asm __emit 0a2h
#endif
-static Tcl_Encoding winTCharEncoding = NULL;
-
/*
* The following declaration is for the VC++ DLL entry point.
*/
@@ -49,7 +47,7 @@ BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD reason,
*/
typedef struct MountPointMap {
- const TCHAR *volumeName; /* Native wide string volume name. */
+ TCHAR *volumeName; /* Native wide string volume name. */
TCHAR driveLetter; /* Drive letter corresponding to the volume
* name. */
struct MountPointMap *nextPtr;
@@ -199,8 +197,6 @@ TclWinInit(
if (platformId == VER_PLATFORM_WIN32_WINDOWS) {
Tcl_Panic("Windows 9x is not a supported platform");
}
-
- TclWinResetInterfaces();
}
/*
@@ -266,7 +262,7 @@ TclWinNoBackslash(
*
* TclpSetInterfaces --
*
- * A helper proc that initializes winTCharEncoding.
+ * A helper proc.
*
* Results:
* None.
@@ -280,8 +276,6 @@ TclWinNoBackslash(
void
TclpSetInterfaces(void)
{
- TclWinResetInterfaces();
- winTCharEncoding = Tcl_GetEncoding(NULL, "unicode");
}
/*
@@ -309,8 +303,6 @@ TclWinEncodingsCleanup(void)
{
MountPointMap *dlIter, *dlIter2;
- TclWinResetInterfaces();
-
/*
* Clean up the mount point map.
*/
@@ -344,10 +336,6 @@ TclWinEncodingsCleanup(void)
void
TclWinResetInterfaces(void)
{
- if (winTCharEncoding != NULL) {
- Tcl_FreeEncoding(winTCharEncoding);
- winTCharEncoding = NULL;
- }
}
/*
@@ -513,39 +501,32 @@ TclWinDriveLetterForVolMountPoint(
*
* Tcl_WinUtfToTChar, Tcl_WinTCharToUtf --
*
- * Convert between UTF-8 and Unicode when running Windows NT or the
- * current ANSI code page when running Windows 95.
+ * Convert between UTF-8 and Unicode when running Windows.
*
- * On Mac, Unix, and Windows 95, all strings exchanged between Tcl and
- * the OS are "char" oriented. We need only one Tcl_Encoding to convert
- * between UTF-8 and the system's native encoding. We use NULL to
- * represent that encoding.
+ * On Mac and Unix, all strings exchanged between Tcl and the OS are
+ * "char" oriented. We need only one Tcl_Encoding to convert between
+ * UTF-8 and the system's native encoding. We use NULL to represent
+ * that encoding.
*
- * On NT, some strings exchanged between Tcl and the OS are "char"
+ * On Windows, some strings exchanged between Tcl and the OS are "char"
* oriented, while others are in Unicode. We need two Tcl_Encoding APIs
* depending on whether we are targeting a "char" or Unicode interface.
*
- * Calling Tcl_UtfToExternal() or Tcl_ExternalToUtf() with an encoding of
- * NULL should always used to convert between UTF-8 and the system's
+ * Calling Tcl_UtfToExternal() or Tcl_ExternalToUtf() with an encoding
+ * of NULL should always used to convert between UTF-8 and the system's
* "char" oriented encoding. The following two functions are used in
- * Windows-specific code to convert between UTF-8 and Unicode strings
- * (NT) or "char" strings(95). This saves you the trouble of writing the
+ * Windows-specific code to convert between UTF-8 and Unicode strings.
+ * This saves you the trouble of writing the
* following type of fragment over and over:
*
- * if (running NT) {
- * encoding <- Tcl_GetEncoding("unicode");
- * nativeBuffer <- UtfToExternal(encoding, utfBuffer);
- * Tcl_FreeEncoding(encoding);
- * } else {
- * nativeBuffer <- UtfToExternal(NULL, utfBuffer);
- * }
- *
- * By convention, in Windows a TCHAR is a character in the ANSI code page
- * on Windows 95, a Unicode character on Windows NT. If you plan on
- * targeting a Unicode interfaces when running on NT and a "char"
- * oriented interface while running on 95, these functions should be
- * used. If you plan on targetting the same "char" oriented function on
- * both 95 and NT, use Tcl_UtfToExternal() with an encoding of NULL.
+ * encoding <- Tcl_GetEncoding("unicode");
+ * nativeBuffer <- UtfToExternal(encoding, utfBuffer);
+ * Tcl_FreeEncoding(encoding);
+ *
+ * By convention, in Windows a TCHAR is a Unicode character. If you plan
+ * on targeting a Unicode interface when running on Windows, these
+ * functions should be used. If you plan on targetting a "char" oriented
+ * function on Windows, use Tcl_UtfToExternal() with an encoding of NULL.
*
* Results:
* The result is a pointer to the string in the desired target encoding.
@@ -561,26 +542,124 @@ TclWinDriveLetterForVolMountPoint(
TCHAR *
Tcl_WinUtfToTChar(
const char *string, /* Source string in UTF-8. */
- int len, /* Source string length in bytes, or < 0 for
+ int len, /* Source string length in bytes, or -1 for
* strlen(). */
Tcl_DString *dsPtr) /* Uninitialized or free DString in which the
* converted string is stored. */
{
- return (TCHAR *) Tcl_UtfToExternalDString(winTCharEncoding,
- string, len, dsPtr);
+#if TCL_UTF_MAX > 4
+ Tcl_UniChar ch = 0;
+ TCHAR *w, *wString;
+ const char *p, *end;
+ int oldLength;
+#endif
+
+ Tcl_DStringInit(dsPtr);
+ if (!string) {
+ return NULL;
+ }
+#if TCL_UTF_MAX > 4
+
+ if (len < 0) {
+ len = strlen(string);
+ }
+
+ /*
+ * Unicode string length in Tcl_UniChars will be <= UTF-8 string length in
+ * bytes.
+ */
+
+ oldLength = Tcl_DStringLength(dsPtr);
+
+ Tcl_DStringSetLength(dsPtr,
+ oldLength + (int) ((len + 1) * sizeof(TCHAR)));
+ wString = (TCHAR *) (Tcl_DStringValue(dsPtr) + oldLength);
+
+ w = wString;
+ p = string;
+ end = string + len - 4;
+ while (p < end) {
+ p += TclUtfToUniChar(p, &ch);
+ if (ch > 0xFFFF) {
+ *w++ = (wchar_t) (0xD800 + ((ch -= 0x10000) >> 10));
+ *w++ = (wchar_t) (0xDC00 | (ch & 0x3FF));
+ } else {
+ *w++ = ch;
+ }
+ }
+ end += 4;
+ while (p < end) {
+ if (Tcl_UtfCharComplete(p, end-p)) {
+ p += TclUtfToUniChar(p, &ch);
+ } else {
+ ch = UCHAR(*p++);
+ }
+ if (ch > 0xFFFF) {
+ *w++ = (wchar_t) (0xD800 + ((ch -= 0x10000) >> 10));
+ *w++ = (wchar_t) (0xDC00 | (ch & 0x3FF));
+ } else {
+ *w++ = ch;
+ }
+ }
+ *w = '\0';
+ Tcl_DStringSetLength(dsPtr,
+ oldLength + ((char *) w - (char *) wString));
+
+ return wString;
+#else
+ return Tcl_UtfToUniCharDString(string, len, dsPtr);
+#endif
}
char *
Tcl_WinTCharToUtf(
- const TCHAR *string, /* Source string in Unicode when running NT,
- * ANSI when running 95. */
- int len, /* Source string length in bytes, or < 0 for
+ const TCHAR *string, /* Source string in Unicode. */
+ int len, /* Source string length in bytes, or -1 for
* platform-specific string length. */
Tcl_DString *dsPtr) /* Uninitialized or free DString in which the
* converted string is stored. */
{
- return Tcl_ExternalToUtfDString(winTCharEncoding,
- (const char *) string, len, dsPtr);
+#if TCL_UTF_MAX > 4
+ const TCHAR *w, *wEnd;
+ char *p, *result;
+ int oldLength, blen = 1;
+#endif
+
+ Tcl_DStringInit(dsPtr);
+ if (!string) {
+ return NULL;
+ }
+ if (len < 0) {
+ len = wcslen((TCHAR *)string);
+ } else {
+ len /= 2;
+ }
+#if TCL_UTF_MAX > 4
+ oldLength = Tcl_DStringLength(dsPtr);
+ Tcl_DStringSetLength(dsPtr, oldLength + (len + 1) * 4);
+ result = Tcl_DStringValue(dsPtr) + oldLength;
+
+ p = result;
+ wEnd = (TCHAR *)string + len;
+ for (w = (TCHAR *)string; w < wEnd; ) {
+ if (!blen && ((*w & 0xFC00) != 0xDC00)) {
+ /* Special case for handling upper surrogates. */
+ p += Tcl_UniCharToUtf(-1, p);
+ }
+ blen = Tcl_UniCharToUtf(*w, p);
+ p += blen;
+ w++;
+ }
+ if (!blen) {
+ /* Special case for handling upper surrogates. */
+ p += Tcl_UniCharToUtf(-1, p);
+ }
+ Tcl_DStringSetLength(dsPtr, oldLength + (p - result));
+
+ return result;
+#else
+ return Tcl_UniCharToUtfDString((Tcl_UniChar *)string, len, dsPtr);
+#endif
}
/*
@@ -610,7 +689,7 @@ TclWinCPUID(
#if defined(HAVE_INTRIN_H) && defined(_WIN64)
- __cpuid(regsPtr, index);
+ __cpuid((int *)regsPtr, index);
status = TCL_OK;
#elif defined(__GNUC__)
@@ -735,7 +814,7 @@ TclWinCPUID(
__cpuid(regsPtr, index);
status = TCL_OK;
-# else
+# elif defined (_M_IX86)
/*
* Define a structure in the stack frame to hold the registers.
*/
diff --git a/win/tclWinDde.c b/win/tclWinDde.c
index ce0b413..27ddfc8 100644
--- a/win/tclWinDde.c
+++ b/win/tclWinDde.c
@@ -17,15 +17,7 @@
#include "tclInt.h"
#include <dde.h>
#include <ddeml.h>
-
-#ifndef UNICODE
-# undef CP_WINUNICODE
-# define CP_WINUNICODE CP_WINANSI
-# undef Tcl_WinTCharToUtf
-# define Tcl_WinTCharToUtf(a,b,c) Tcl_ExternalToUtfDString(NULL,a,b,c)
-# undef Tcl_WinUtfToTChar
-# define Tcl_WinUtfToTChar(a,b,c) Tcl_UtfToExternalDString(NULL,a,b,c)
-#endif
+#include <tchar.h>
#if !defined(NDEBUG)
/* test POKE server Implemented for debug mode only */
@@ -34,16 +26,6 @@
#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
- * are building a library. DO NOT MOVE BEFORE ANY #include LINES. ONLY USE
- * EXTERN TO INDICATE EXPORTED FUNCTIONS FROM NOW ON.
- */
-
-#undef TCL_STORAGE_CLASS
-#define TCL_STORAGE_CLASS DLLEXPORT
-
-/*
* The following structure is used to keep track of the interpreters
* registered by this process.
*/
@@ -69,7 +51,7 @@ typedef struct Conversation {
Tcl_Obj *returnPackagePtr; /* The result package for this conversation. */
} Conversation;
-typedef struct DdeEnumServices {
+typedef struct {
Tcl_Interp *interp;
int result;
ATOM service;
@@ -77,7 +59,7 @@ typedef struct DdeEnumServices {
HWND hwnd;
} DdeEnumServices;
-typedef struct ThreadSpecificData {
+typedef struct {
Conversation *currentConversations;
/* A list of conversations currently being
* processed. */
@@ -97,7 +79,7 @@ static DWORD ddeInstance; /* The application instance handle given to us
* by DdeInitialize. */
static int ddeIsServer = 0;
-#define TCL_DDE_VERSION "1.4.0"
+#define TCL_DDE_VERSION "1.4.1"
#define TCL_DDE_PACKAGE_NAME "dde"
#define TCL_DDE_SERVICE_NAME TEXT("TclEval")
#define TCL_DDE_EXECUTE_RESULT TEXT("$TCLEVAL$EXECUTE$RESULT")
@@ -114,7 +96,7 @@ TCL_DECLARE_MUTEX(ddeMutex)
static LRESULT CALLBACK DdeClientWindowProc(HWND hwnd, UINT uMsg,
WPARAM wParam, LPARAM lParam);
-static int DdeCreateClient(struct DdeEnumServices *es);
+static int DdeCreateClient(DdeEnumServices *es);
static BOOL CALLBACK DdeEnumWindowsCallback(HWND hwndTarget,
LPARAM lParam);
static void DdeExitProc(ClientData clientData);
@@ -135,8 +117,27 @@ static int DdeObjCmd(ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[]);
-EXTERN int Dde_Init(Tcl_Interp *interp);
-EXTERN int Dde_SafeInit(Tcl_Interp *interp);
+static unsigned char *
+getByteArrayFromObj(
+ Tcl_Obj *objPtr,
+ size_t *lengthPtr
+) {
+ int length;
+
+ unsigned char *result = Tcl_GetByteArrayFromObj(objPtr, &length);
+#if TCL_MAJOR_VERSION > 8
+ if (sizeof(TCL_HASH_TYPE) > sizeof(int)) {
+ /* 64-bit and TIP #494 situation: */
+ *lengthPtr = *(TCL_HASH_TYPE *) objPtr->internalRep.twoPtrValue.ptr1;
+ } else
+#endif
+ /* 32-bit or without TIP #494 */
+ *lengthPtr = (size_t) (unsigned) length;
+ return result;
+}
+
+DLLEXPORT int Dde_Init(Tcl_Interp *interp);
+DLLEXPORT int Dde_SafeInit(Tcl_Interp *interp);
/*
*----------------------------------------------------------------------
@@ -162,13 +163,6 @@ Dde_Init(
return TCL_ERROR;
}
-#ifdef UNICODE
- if (TclWinGetPlatformId() < VER_PLATFORM_WIN32_NT) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "Win32s and Windows 9x are not supported platforms", -1));
- return TCL_ERROR;
- }
-#endif
Tcl_CreateObjCommand(interp, "dde", DdeObjCmd, NULL, NULL);
Tcl_CreateExitHandler(DdeExitProc, NULL);
return Tcl_PkgProvide(interp, TCL_DDE_PACKAGE_NAME, TCL_DDE_VERSION);
@@ -399,7 +393,7 @@ DdeSetServerName(
Tcl_DString ds;
Tcl_ListObjIndex(interp, srvPtrPtr[n], 1, &namePtr);
- Tcl_WinUtfToTChar(Tcl_GetString(namePtr), -1, &ds);
+ Tcl_WinUtfToTChar(Tcl_GetString(namePtr), -1, &ds);
if (_tcscmp(actualName, (TCHAR *)Tcl_DStringValue(&ds)) == 0) {
suffix++;
Tcl_DStringFree(&ds);
@@ -414,9 +408,9 @@ DdeSetServerName(
* We have found a unique name. Now add it to the registry.
*/
- riPtr = ckalloc(sizeof(RegisteredInterp));
+ riPtr = (RegisteredInterp *) Tcl_Alloc(sizeof(RegisteredInterp));
riPtr->interp = interp;
- riPtr->name = ckalloc((_tcslen(actualName) + 1) * sizeof(TCHAR));
+ riPtr->name = (TCHAR *) Tcl_Alloc((_tcslen(actualName) + 1) * sizeof(TCHAR));
riPtr->nextPtr = tsdPtr->interpListPtr;
riPtr->handlerPtr = handlerPtr;
if (riPtr->handlerPtr != NULL) {
@@ -517,7 +511,7 @@ DeleteProc(
prevPtr->nextPtr = searchPtr->nextPtr;
}
}
- ckfree(riPtr->name);
+ Tcl_Free((char *) riPtr->name);
if (riPtr->handlerPtr) {
Tcl_DecrRefCount(riPtr->handlerPtr);
}
@@ -555,7 +549,7 @@ ExecuteRemoteObject(
Tcl_Obj *returnPackagePtr;
int result = TCL_OK;
- if (riPtr->handlerPtr == NULL && Tcl_IsSafe(riPtr->interp)) {
+ if ((riPtr->handlerPtr == NULL) && Tcl_IsSafe(riPtr->interp)) {
Tcl_SetObjResult(riPtr->interp, Tcl_NewStringObj("permission denied: "
"a handler procedure must be defined for use in a safe "
"interp", -1));
@@ -637,7 +631,7 @@ DdeServerProc(
/* Transaction-dependent data. */
{
Tcl_DString dString;
- int len;
+ size_t len;
DWORD dlen;
TCHAR *utilString;
Tcl_Obj *ddeObjectPtr;
@@ -687,7 +681,7 @@ DdeServerProc(
for (riPtr = tsdPtr->interpListPtr; riPtr != NULL;
riPtr = riPtr->nextPtr) {
if (_tcsicmp(riPtr->name, utilString) == 0) {
- convPtr = ckalloc(sizeof(Conversation));
+ convPtr = (Conversation *) Tcl_Alloc(sizeof(Conversation));
convPtr->nextPtr = tsdPtr->currentConversations;
convPtr->returnPackagePtr = NULL;
convPtr->hConv = hConv;
@@ -717,7 +711,7 @@ DdeServerProc(
if (convPtr->returnPackagePtr != NULL) {
Tcl_DecrRefCount(convPtr->returnPackagePtr);
}
- ckfree(convPtr);
+ Tcl_Free((char *) convPtr);
break;
}
}
@@ -743,22 +737,24 @@ DdeServerProc(
}
if (convPtr != NULL) {
+ Tcl_DString dsBuf;
char *returnString;
len = DdeQueryString(ddeInstance, ddeItem, NULL, 0, CP_WINUNICODE);
Tcl_DStringInit(&dString);
+ Tcl_DStringInit(&dsBuf);
Tcl_DStringSetLength(&dString, (len + 1) * sizeof(TCHAR) - 1);
utilString = (TCHAR *) Tcl_DStringValue(&dString);
DdeQueryString(ddeInstance, ddeItem, utilString, (DWORD) len + 1,
CP_WINUNICODE);
if (_tcsicmp(utilString, TCL_DDE_EXECUTE_RESULT) == 0) {
- if (uFmt == CF_TEXT) {
- returnString =
- Tcl_GetStringFromObj(convPtr->returnPackagePtr, &len);
- } else {
- returnString = (char *)
- Tcl_GetUnicodeFromObj(convPtr->returnPackagePtr, &len);
- len = sizeof(TCHAR) * len + 1;
+ returnString =
+ Tcl_GetString(convPtr->returnPackagePtr);
+ len = convPtr->returnPackagePtr->length;
+ if (uFmt != CF_TEXT) {
+ Tcl_WinUtfToTChar(returnString, len, &dsBuf);
+ returnString = Tcl_DStringValue(&dsBuf);
+ len = Tcl_DStringLength(&dsBuf) + sizeof(TCHAR) - 1;
}
ddeReturn = DdeCreateDataHandle(ddeInstance, (BYTE *)returnString,
(DWORD) len+1, 0, ddeItem, uFmt, 0);
@@ -768,18 +764,18 @@ DdeServerProc(
} else {
Tcl_DString ds;
Tcl_Obj *variableObjPtr;
+
Tcl_WinTCharToUtf(utilString, -1, &ds);
variableObjPtr = Tcl_GetVar2Ex(
convPtr->riPtr->interp, Tcl_DStringValue(&ds), NULL,
TCL_GLOBAL_ONLY);
if (variableObjPtr != NULL) {
- if (uFmt == CF_TEXT) {
- returnString = Tcl_GetStringFromObj(
- variableObjPtr, &len);
- } else {
- returnString = (char *) Tcl_GetUnicodeFromObj(
- variableObjPtr, &len);
- len = sizeof(TCHAR) * len + 1;
+ returnString = Tcl_GetString(variableObjPtr);
+ len = variableObjPtr->length;
+ if (uFmt != CF_TEXT) {
+ Tcl_WinUtfToTChar(returnString, len, &dsBuf);
+ returnString = Tcl_DStringValue(&dsBuf);
+ len = Tcl_DStringLength(&dsBuf) + sizeof(TCHAR) - 1;
}
ddeReturn = DdeCreateDataHandle(ddeInstance,
(BYTE *)returnString, (DWORD) len+1, 0, ddeItem,
@@ -790,6 +786,7 @@ DdeServerProc(
Tcl_DStringFree(&ds);
}
}
+ Tcl_DStringFree(&dsBuf);
Tcl_DStringFree(&dString);
}
return ddeReturn;
@@ -814,26 +811,30 @@ DdeServerProc(
}
if (convPtr && !Tcl_IsSafe(convPtr->riPtr->interp)) {
- Tcl_DString ds;
+ Tcl_DString ds, ds2;
Tcl_Obj *variableObjPtr;
+ DWORD len2;
- len = DdeQueryString(ddeInstance, ddeItem, NULL, 0, CP_WINUNICODE);
Tcl_DStringInit(&dString);
+ Tcl_DStringInit(&ds2);
+ len = DdeQueryString(ddeInstance, ddeItem, NULL, 0, CP_WINUNICODE);
Tcl_DStringSetLength(&dString, (len + 1) * sizeof(TCHAR) - 1);
utilString = (TCHAR *) Tcl_DStringValue(&dString);
DdeQueryString(ddeInstance, ddeItem, utilString, (DWORD) len + 1,
CP_WINUNICODE);
Tcl_WinTCharToUtf(utilString, -1, &ds);
- utilString = (TCHAR *) DdeAccessData(hData, &dlen);
- if (uFmt == CF_TEXT) {
- variableObjPtr = Tcl_NewStringObj((char *)utilString, -1);
- } else {
- variableObjPtr = Tcl_NewUnicodeObj(utilString, -1);
+ utilString = (TCHAR *) DdeAccessData(hData, &len2);
+ len = len2;
+ if (uFmt != CF_TEXT) {
+ Tcl_WinTCharToUtf(utilString, -1, &ds2);
+ utilString = (TCHAR *) Tcl_DStringValue(&ds2);
}
+ variableObjPtr = Tcl_NewStringObj((char *)utilString, -1);
Tcl_SetVar2Ex(convPtr->riPtr->interp, Tcl_DStringValue(&ds), NULL,
variableObjPtr, TCL_GLOBAL_ONLY);
+ Tcl_DStringFree(&ds2);
Tcl_DStringFree(&ds);
Tcl_DStringFree(&dString);
ddeReturn = (HDDEDATA) DDE_FACK;
@@ -874,8 +875,12 @@ DdeServerProc(
ddeObjectPtr = Tcl_NewStringObj(string, dlen);
} else {
/* unicode */
- dlen >>= 1;
- ddeObjectPtr = Tcl_NewUnicodeObj((Tcl_UniChar *)utilString, dlen - 1);
+ Tcl_DString dsBuf;
+
+ Tcl_WinTCharToUtf(utilString, dlen - sizeof(TCHAR), &dsBuf);
+ ddeObjectPtr = Tcl_NewStringObj(Tcl_DStringValue(&dsBuf),
+ Tcl_DStringLength(&dsBuf));
+ Tcl_DStringFree(&dsBuf);
}
Tcl_IncrRefCount(ddeObjectPtr);
DdeUnaccessData(hData);
@@ -1040,7 +1045,7 @@ MakeDdeConnection(
static int
DdeCreateClient(
- struct DdeEnumServices *es)
+ DdeEnumServices *es)
{
WNDCLASSEX wc;
static const TCHAR *szDdeClientClassName = TEXT("TclEval client class");
@@ -1050,7 +1055,7 @@ DdeCreateClient(
wc.cbSize = sizeof(wc);
wc.lpfnWndProc = DdeClientWindowProc;
wc.lpszClassName = szDdeClientClassName;
- wc.cbWndExtra = sizeof(struct DdeEnumServices *);
+ wc.cbWndExtra = sizeof(DdeEnumServices *);
/*
* Register and create the callback window.
@@ -1072,8 +1077,8 @@ DdeClientWindowProc(
switch (uMsg) {
case WM_CREATE: {
LPCREATESTRUCT lpcs = (LPCREATESTRUCT) lParam;
- struct DdeEnumServices *es =
- (struct DdeEnumServices *) lpcs->lpCreateParams;
+ DdeEnumServices *es =
+ (DdeEnumServices *) lpcs->lpCreateParams;
#ifdef _WIN64
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) es);
@@ -1098,18 +1103,18 @@ DdeServicesOnAck(
HWND hwndRemote = (HWND)wParam;
ATOM service = (ATOM)LOWORD(lParam);
ATOM topic = (ATOM)HIWORD(lParam);
- struct DdeEnumServices *es;
+ DdeEnumServices *es;
TCHAR sz[255];
Tcl_DString dString;
#ifdef _WIN64
- es = (struct DdeEnumServices *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ es = (DdeEnumServices *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
#else
- es = (struct DdeEnumServices *) GetWindowLong(hwnd, GWL_USERDATA);
+ es = (DdeEnumServices *) GetWindowLong(hwnd, GWL_USERDATA);
#endif
- if ((es->service == (ATOM)0 || es->service == service)
- && (es->topic == (ATOM)0 || es->topic == topic)) {
+ if (((es->service == (ATOM)0) || (es->service == service))
+ && ((es->topic == (ATOM)0) || (es->topic == topic))) {
Tcl_Obj *matchPtr = Tcl_NewListObj(0, NULL);
Tcl_Obj *resultPtr = Tcl_GetObjResult(es->interp);
@@ -1156,7 +1161,7 @@ DdeEnumWindowsCallback(
LPARAM lParam)
{
DWORD_PTR dwResult = 0;
- struct DdeEnumServices *es = (struct DdeEnumServices *) lParam;
+ DdeEnumServices *es = (DdeEnumServices *) lParam;
SendMessageTimeout(hwndTarget, WM_DDE_INITIATE, (WPARAM)es->hwnd,
MAKELONG(es->service, es->topic), SMTO_ABORTIFHUNG, 1000,
@@ -1170,7 +1175,7 @@ DdeGetServicesList(
const TCHAR *serviceName,
const TCHAR *topicName)
{
- struct DdeEnumServices es;
+ DdeEnumServices es;
es.interp = interp;
es.result = TCL_OK;
@@ -1291,7 +1296,8 @@ DdeObjCmd(
"-binary", NULL
};
- int index, i, length, argIndex;
+ int index, i, argIndex;
+ size_t length;
int flags = 0, result = TCL_OK, firstArg = 0;
HSZ ddeService = NULL, ddeTopic = NULL, ddeItem = NULL, ddeCookie = NULL;
HDDEDATA ddeData = NULL, ddeItemData = NULL, ddeReturn;
@@ -1300,6 +1306,7 @@ DdeObjCmd(
const char *string;
DWORD ddeResult;
Tcl_Obj *objPtr, *handlerPtr = NULL;
+ Tcl_DString serviceBuf, topicBuf, itemBuf;
/*
* Initialize DDE server/client
@@ -1315,6 +1322,9 @@ DdeObjCmd(
return TCL_ERROR;
}
+ Tcl_DStringInit(&serviceBuf);
+ Tcl_DStringInit(&topicBuf);
+ Tcl_DStringInit(&itemBuf);
switch ((enum DdeSubcommands) index) {
case DDE_SERVERNAME:
for (i = 2; i < objc; i++) {
@@ -1364,7 +1374,7 @@ DdeObjCmd(
if (objc == 5) {
firstArg = 2;
break;
- } else if (objc >= 6 && objc <= 7) {
+ } else if ((objc >= 6) && (objc <= 7)) {
firstArg = objc - 3;
for (i = 2; i < firstArg; i++) {
if (Tcl_GetIndexFromObj(interp, objv[i], ddeExecOptions,
@@ -1449,11 +1459,12 @@ DdeObjCmd(
Initialize();
if (firstArg != 1) {
-#ifdef UNICODE
- serviceName = Tcl_GetUnicodeFromObj(objv[firstArg], &length);
-#else
- serviceName = Tcl_GetStringFromObj(objv[firstArg], &length);
-#endif
+ const char *src = Tcl_GetString(objv[firstArg]);
+
+ length = objv[firstArg]->length;
+ Tcl_WinUtfToTChar(src, length, &serviceBuf);
+ serviceName = (TCHAR *) Tcl_DStringValue(&serviceBuf);
+ length = Tcl_DStringLength(&serviceBuf) / sizeof(TCHAR);
} else {
length = 0;
}
@@ -1466,11 +1477,11 @@ DdeObjCmd(
}
if ((index != DDE_SERVERNAME) && (index != DDE_EVAL)) {
-#ifdef UNICODE
- topicName = (TCHAR *) Tcl_GetUnicodeFromObj(objv[firstArg + 1], &length);
-#else
- topicName = Tcl_GetStringFromObj(objv[firstArg + 1], &length);
-#endif
+ const char *src = Tcl_GetString(objv[firstArg + 1]);
+
+ length = objv[firstArg + 1]->length;
+ topicName = Tcl_WinUtfToTChar(src, length, &topicBuf);
+ length = Tcl_DStringLength(&topicBuf) / sizeof(TCHAR);
if (length == 0) {
topicName = NULL;
} else {
@@ -1484,32 +1495,40 @@ DdeObjCmd(
serviceName = DdeSetServerName(interp, serviceName, flags,
handlerPtr);
if (serviceName != NULL) {
-#ifdef UNICODE
- Tcl_SetObjResult(interp, Tcl_NewUnicodeObj((Tcl_UniChar *) serviceName, -1));
-#else
- Tcl_SetObjResult(interp, Tcl_NewStringObj(serviceName, -1));
-#endif
+ Tcl_DString dsBuf;
+
+ Tcl_WinTCharToUtf(serviceName, -1, &dsBuf);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(Tcl_DStringValue(&dsBuf),
+ Tcl_DStringLength(&dsBuf)));
+ Tcl_DStringFree(&dsBuf);
} else {
Tcl_ResetResult(interp);
}
break;
case DDE_EXECUTE: {
- int dataLength;
- const Tcl_UniChar *dataString;
+ size_t dataLength;
+ const void *dataString;
+ Tcl_DString dsBuf;
+ Tcl_DStringInit(&dsBuf);
if (flags & DDE_FLAG_BINARY) {
- dataString = (const Tcl_UniChar *)
- Tcl_GetByteArrayFromObj(objv[firstArg + 2], &dataLength);
- } else {
dataString =
- Tcl_GetUnicodeFromObj(objv[firstArg + 2], &dataLength);
- dataLength = (dataLength + 1) * sizeof(Tcl_UniChar);
+ getByteArrayFromObj(objv[firstArg + 2], &dataLength);
+ } else {
+ const char *src;
+
+ src = Tcl_GetString(objv[firstArg + 2]);
+ dataLength = objv[firstArg + 2]->length;
+ dataString = (const TCHAR *)
+ Tcl_WinUtfToTChar(src, dataLength, &dsBuf);
+ dataLength = Tcl_DStringLength(&dsBuf) + sizeof(TCHAR);
}
- if (dataLength <= 0) {
+ if (dataLength + 1 < 2) {
Tcl_SetObjResult(interp,
Tcl_NewStringObj("cannot execute null data", -1));
+ Tcl_DStringFree(&dsBuf);
Tcl_SetErrorCode(interp, "TCL", "DDE", "NULL", NULL);
result = TCL_ERROR;
break;
@@ -1519,6 +1538,7 @@ DdeObjCmd(
DdeFreeStringHandle(ddeInstance, ddeTopic);
if (hConv == NULL) {
+ Tcl_DStringFree(&dsBuf);
SetDdeError(interp);
result = TCL_ERROR;
break;
@@ -1544,16 +1564,17 @@ DdeObjCmd(
SetDdeError(interp);
result = TCL_ERROR;
}
+ Tcl_DStringFree(&dsBuf);
break;
}
case DDE_REQUEST: {
-#ifdef UNICODE
- const TCHAR *itemString = (TCHAR *) Tcl_GetUnicodeFromObj(objv[firstArg + 2],
- &length);
-#else
- const TCHAR *itemString = Tcl_GetStringFromObj(objv[firstArg + 2],
- &length);
-#endif
+ const TCHAR *itemString;
+ const char *src;
+
+ src = Tcl_GetString(objv[firstArg + 2]);
+ length = objv[firstArg + 2]->length;
+ itemString = Tcl_WinUtfToTChar(src, length, &itemBuf);
+ length = Tcl_DStringLength(&itemBuf) / sizeof(TCHAR);
if (length == 0) {
Tcl_SetObjResult(interp,
@@ -1581,18 +1602,23 @@ DdeObjCmd(
result = TCL_ERROR;
} else {
DWORD tmp;
- const Tcl_UniChar *dataString = (const Tcl_UniChar *) DdeAccessData(ddeData, &tmp);
+ TCHAR *dataString = (TCHAR *) DdeAccessData(ddeData, &tmp);
if (flags & DDE_FLAG_BINARY) {
returnObjPtr =
- Tcl_NewByteArrayObj((BYTE *) dataString, (int) tmp);
+ Tcl_NewByteArrayObj((BYTE *) dataString, tmp);
} else {
- tmp >>= 1;
- if (tmp && !dataString[(tmp-1)]) {
- --tmp;
+ Tcl_DString dsBuf;
+
+ if ((tmp >= sizeof(TCHAR))
+ && !dataString[tmp / sizeof(TCHAR) - 1]) {
+ tmp -= sizeof(TCHAR);
}
- returnObjPtr = Tcl_NewUnicodeObj(dataString,
- (int) tmp);
+ Tcl_WinTCharToUtf(dataString, tmp, &dsBuf);
+ returnObjPtr =
+ Tcl_NewStringObj(Tcl_DStringValue(&dsBuf),
+ Tcl_DStringLength(&dsBuf));
+ Tcl_DStringFree(&dsBuf);
}
DdeUnaccessData(ddeData);
DdeFreeDataHandle(ddeData);
@@ -1603,19 +1629,18 @@ DdeObjCmd(
result = TCL_ERROR;
}
}
-
break;
}
case DDE_POKE: {
-#ifdef UNICODE
- const TCHAR *itemString = (TCHAR *) Tcl_GetUnicodeFromObj(objv[firstArg + 2],
- &length);
-#else
- const TCHAR *itemString = Tcl_GetStringFromObj(objv[firstArg + 2],
- &length);
-#endif
+ Tcl_DString dsBuf;
+ const TCHAR *itemString;
BYTE *dataString;
+ const char *src;
+ src = Tcl_GetString(objv[firstArg + 2]);
+ length = objv[firstArg + 2]->length;
+ itemString = Tcl_WinUtfToTChar(src, length, &itemBuf);
+ length = Tcl_DStringLength(&itemBuf) / sizeof(TCHAR);
if (length == 0) {
Tcl_SetObjResult(interp,
Tcl_NewStringObj("cannot have a null item", -1));
@@ -1623,13 +1648,17 @@ DdeObjCmd(
result = TCL_ERROR;
goto cleanup;
}
+ Tcl_DStringInit(&dsBuf);
if (flags & DDE_FLAG_BINARY) {
dataString = (BYTE *)
- Tcl_GetByteArrayFromObj(objv[firstArg + 3], &length);
+ getByteArrayFromObj(objv[firstArg + 3], &length);
} else {
+ const char *data =
+ Tcl_GetString(objv[firstArg + 3]);
+ length = objv[firstArg + 3]->length;
dataString = (BYTE *)
- Tcl_GetUnicodeFromObj(objv[firstArg + 3], &length);
- length = 2 * length + 1;
+ Tcl_WinUtfToTChar(data, length, &dsBuf);
+ length = Tcl_DStringLength(&dsBuf) + sizeof(TCHAR);
}
hConv = DdeConnect(ddeInstance, ddeService, ddeTopic, NULL);
@@ -1654,6 +1683,7 @@ DdeObjCmd(
result = TCL_ERROR;
}
}
+ Tcl_DStringFree(&dsBuf);
break;
}
@@ -1712,7 +1742,7 @@ DdeObjCmd(
* referring to deallocated objects.
*/
- if (Tcl_IsSafe(riPtr->interp) && riPtr->handlerPtr == NULL) {
+ if (Tcl_IsSafe(riPtr->interp) && (riPtr->handlerPtr == NULL)) {
Tcl_SetObjResult(riPtr->interp, Tcl_NewStringObj(
"permission denied: a handler procedure must be"
" defined for use in a safe interp", -1));
@@ -1757,8 +1787,7 @@ DdeObjCmd(
objPtr = Tcl_GetVar2Ex(sendInterp, "errorInfo", NULL,
TCL_GLOBAL_ONLY);
if (objPtr) {
- string = Tcl_GetStringFromObj(objPtr, &length);
- Tcl_AddObjErrorInfo(interp, string, length);
+ Tcl_AppendObjToErrorInfo(interp, objPtr);
}
objPtr = Tcl_GetVar2Ex(sendInterp, "errorCode", NULL,
@@ -1772,6 +1801,8 @@ DdeObjCmd(
Tcl_Release(riPtr);
Tcl_Release(sendInterp);
} else {
+ Tcl_DString dsBuf;
+
/*
* This is a non-local request. Send the script to the server and
* poll it for a result.
@@ -1787,9 +1818,14 @@ DdeObjCmd(
}
objPtr = Tcl_ConcatObj(objc, objv);
- string = (const char *) Tcl_GetUnicodeFromObj(objPtr, &length);
- ddeItemData = DdeCreateDataHandle(ddeInstance,
- (BYTE *) string, (DWORD) 2*length+2, 0, 0, CF_UNICODETEXT, 0);
+ string = Tcl_GetString(objPtr);
+ length = objPtr->length;
+ Tcl_WinUtfToTChar(string, length, &dsBuf);
+ string = Tcl_DStringValue(&dsBuf);
+ length = Tcl_DStringLength(&dsBuf) + sizeof(TCHAR);
+ ddeItemData = DdeCreateDataHandle(ddeInstance, (BYTE *) string,
+ (DWORD) length, 0, 0, CF_UNICODETEXT, 0);
+ Tcl_DStringFree(&dsBuf);
if (flags & DDE_FLAG_ASYNC) {
ddeData = DdeClientTransaction((LPBYTE) ddeItemData,
@@ -1818,7 +1854,7 @@ DdeObjCmd(
if (!(flags & DDE_FLAG_ASYNC)) {
Tcl_Obj *resultPtr;
- Tcl_UniChar *ddeDataString;
+ TCHAR *ddeDataString;
/*
* The return handle has a two or four element list in it. The
@@ -1829,13 +1865,17 @@ DdeObjCmd(
* variable "errorInfo".
*/
- resultPtr = Tcl_NewObj();
length = DdeGetData(ddeData, NULL, 0, 0);
- ddeDataString = ckalloc(length);
+ ddeDataString = (TCHAR *) Tcl_Alloc(length);
DdeGetData(ddeData, (BYTE *) ddeDataString, (DWORD) length, 0);
- length = (length >> 1) - 1;
- resultPtr = Tcl_NewUnicodeObj(ddeDataString, length);
- ckfree(ddeDataString);
+ if (length > sizeof(TCHAR)) {
+ length -= sizeof(TCHAR);
+ }
+ Tcl_WinTCharToUtf(ddeDataString, length, &dsBuf);
+ resultPtr = Tcl_NewStringObj(Tcl_DStringValue(&dsBuf),
+ Tcl_DStringLength(&dsBuf));
+ Tcl_DStringFree(&dsBuf);
+ Tcl_Free((char *) ddeDataString);
if (Tcl_ListObjIndex(NULL, resultPtr, 0, &objPtr) != TCL_OK) {
Tcl_DecrRefCount(resultPtr);
@@ -1853,9 +1893,7 @@ DdeObjCmd(
Tcl_DecrRefCount(resultPtr);
goto invalidServerResponse;
}
- length = -1;
- string = Tcl_GetStringFromObj(objPtr, &length);
- Tcl_AddObjErrorInfo(interp, string, length);
+ Tcl_AppendObjToErrorInfo(interp, objPtr);
Tcl_ListObjIndex(NULL, resultPtr, 2, &objPtr);
Tcl_SetObjErrorCode(interp, objPtr);
@@ -1887,6 +1925,9 @@ DdeObjCmd(
if (hConv != NULL) {
DdeDisconnect(hConv);
}
+ Tcl_DStringFree(&itemBuf);
+ Tcl_DStringFree(&topicBuf);
+ Tcl_DStringFree(&serviceBuf);
return result;
}
diff --git a/win/tclWinError.c b/win/tclWinError.c
index 30079b9..fea4b0f 100644
--- a/win/tclWinError.c
+++ b/win/tclWinError.c
@@ -30,7 +30,7 @@ static const unsigned char errorTable[] = {
ENOEXEC, /* ERROR_BAD_FORMAT 11 */
EACCES, /* ERROR_INVALID_ACCESS 12 */
EINVAL, /* ERROR_INVALID_DATA 13 */
- EFAULT, /* ERROR_OUT_OF_MEMORY 14 */
+ ENOMEM, /* ERROR_OUT_OF_MEMORY 14 */
ENOENT, /* ERROR_INVALID_DRIVE 15 */
EACCES, /* ERROR_CURRENT_DIRECTORY 16 */
EXDEV, /* ERROR_NOT_SAME_DEVICE 17 */
diff --git a/win/tclWinFCmd.c b/win/tclWinFCmd.c
index 52ea8c6..2f28154 100644
--- a/win/tclWinFCmd.c
+++ b/win/tclWinFCmd.c
@@ -1026,7 +1026,8 @@ DoRemoveJustDirectory(
if (nativePath == NULL || nativePath[0] == '\0') {
Tcl_SetErrno(ENOENT);
- goto end;
+ Tcl_DStringInit(errorPtr);
+ return TCL_ERROR;
}
attr = GetFileAttributes(nativePath);
@@ -1108,9 +1109,7 @@ DoRemoveJustDirectory(
end:
if (errorPtr != NULL) {
- char *p;
- Tcl_WinTCharToUtf(nativePath, -1, errorPtr);
- p = Tcl_DStringValue(errorPtr);
+ char *p = Tcl_WinTCharToUtf(nativePath, -1, errorPtr);
for (; *p; ++p) {
if (*p == '\\') *p = '/';
}
diff --git a/win/tclWinFile.c b/win/tclWinFile.c
index 6662327..b9787c7 100644..100755
--- a/win/tclWinFile.c
+++ b/win/tclWinFile.c
@@ -531,6 +531,11 @@ TclWinSymLinkDelete(
*--------------------------------------------------------------------
*/
+#if defined (__clang__) || ((__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Warray-bounds"
+#endif
+
static Tcl_Obj *
WinReadLinkDirectory(
const TCHAR *linkDirPath)
@@ -648,6 +653,10 @@ WinReadLinkDirectory(
Tcl_SetErrno(EINVAL);
return NULL;
}
+
+#if defined (__clang__) || ((__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+#pragma GCC diagnostic pop
+#endif
/*
*--------------------------------------------------------------------
@@ -828,7 +837,7 @@ tclWinDebugPanic(
__builtin_trap();
#elif defined(_WIN64)
__debugbreak();
-#elif defined(_MSC_VER)
+#elif defined(_MSC_VER) && defined (_M_IX86)
_asm {int 3}
#else
DebugBreak();
@@ -1436,48 +1445,78 @@ TclpGetUserHome(
Tcl_DString *bufferPtr) /* Uninitialized or free DString filled with
* name of user's home directory. */
{
- const char *result = NULL;
- USER_INFO_1 *uiPtr, **uiPtrPtr = &uiPtr;
+ char *result = NULL;
+ USER_INFO_1 *uiPtr;
Tcl_DString ds;
int nameLen = -1;
- int badDomain = 0;
- char *domain;
- WCHAR *wName, *wHomeDir, *wDomain, **wDomainPtr = &wDomain;
+ int rc = 0;
+ const char *domain;
+ WCHAR *wName, *wHomeDir, *wDomain;
WCHAR buf[MAX_PATH];
Tcl_DStringInit(bufferPtr);
+
wDomain = NULL;
- domain = strchr(name, '@');
- if (domain != NULL) {
+ domain = Tcl_UtfFindFirst(name, '@');
+ if (domain == NULL) {
+ const char *ptr;
+
+ /* no domain - firstly check it's the current user */
+ if ( (ptr = TclpGetUserName(&ds)) != NULL
+ && strcasecmp(name, ptr) == 0
+ ) {
+ /* try safest and fastest way to get current user home */
+ ptr = TclGetEnv("HOME", &ds);
+ if (ptr != NULL) {
+ Tcl_JoinPath(1, &ptr, bufferPtr);
+ rc = 1;
+ result = Tcl_DStringValue(bufferPtr);
+ }
+ }
+ Tcl_DStringFree(&ds);
+ } else {
Tcl_DStringInit(&ds);
wName = Tcl_UtfToUniCharDString(domain + 1, -1, &ds);
- badDomain = NetGetDCName(NULL, wName, (LPBYTE *) wDomainPtr);
+ rc = NetGetDCName(NULL, wName, (LPBYTE *) &wDomain);
Tcl_DStringFree(&ds);
nameLen = domain - name;
}
- if (badDomain == 0) {
+ if (rc == 0) {
Tcl_DStringInit(&ds);
wName = Tcl_UtfToUniCharDString(name, nameLen, &ds);
- if (NetUserGetInfo(wDomain, wName, 1, (LPBYTE *) uiPtrPtr) == 0) {
+ while (NetUserGetInfo(wDomain, wName, 1, (LPBYTE *) &uiPtr) != 0) {
+ /*
+ * user does not exists - if domain was not specified,
+ * try again using current domain.
+ */
+ rc = 1;
+ if (domain != NULL) break;
+ /* get current domain */
+ rc = NetGetDCName(NULL, NULL, (LPBYTE *) &wDomain);
+ if (rc != 0) break;
+ domain = INT2PTR(-1); /* repeat once */
+ }
+ if (rc == 0) {
+ DWORD i, size = MAX_PATH;
wHomeDir = uiPtr->usri1_home_dir;
if ((wHomeDir != NULL) && (wHomeDir[0] != L'\0')) {
- Tcl_UniCharToUtfDString(wHomeDir, lstrlenW(wHomeDir),
- bufferPtr);
+ size = lstrlenW(wHomeDir);
+ Tcl_UniCharToUtfDString(wHomeDir, size, bufferPtr);
} else {
/*
* User exists but has no home dir. Return
* "{GetProfilesDirectory}/<user>".
*/
- DWORD i, size = MAX_PATH;
GetProfilesDirectoryW(buf, &size);
- for (i = 0; i < size; ++i){
- if (buf[i] == '\\') buf[i] = '/';
- }
Tcl_UniCharToUtfDString(buf, size-1, bufferPtr);
- Tcl_DStringAppend(bufferPtr, "/", -1);
- Tcl_DStringAppend(bufferPtr, name, -1);
+ Tcl_DStringAppend(bufferPtr, "/", 1);
+ Tcl_DStringAppend(bufferPtr, name, nameLen);
}
result = Tcl_DStringValue(bufferPtr);
+ /* be sure we returns normalized path */
+ for (i = 0; i < size; ++i){
+ if (result[i] == '\\') result[i] = '/';
+ }
NetApiBufferFree((void *) uiPtr);
}
Tcl_DStringFree(&ds);
@@ -1561,11 +1600,12 @@ NativeAccess(
return 0;
}
- if ((mode & W_OK)
- && (attr & FILE_ATTRIBUTE_READONLY)
- && !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
+ /*
+ * If it's not a directory (assume file), do several fast checks:
+ */
+ if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
/*
- * The attributes say the file is not writable. If the file is a
+ * If the attributes say this is not writable at all. The file is a
* regular file (i.e., not a directory), then the file is not
* writable, full stop. For directories, the read-only bit is
* (mostly) ignored by Windows, so we can't ascertain anything about
@@ -1573,21 +1613,38 @@ NativeAccess(
* advanced 'getFileSecurityProc', then more robust ACL checks
* will be done below.
*/
+ if ((mode & W_OK) && (attr & FILE_ATTRIBUTE_READONLY)) {
+ Tcl_SetErrno(EACCES);
+ return -1;
+ }
- Tcl_SetErrno(EACCES);
- return -1;
- }
-
- if (mode & X_OK) {
- if (!(attr & FILE_ATTRIBUTE_DIRECTORY) && !NativeIsExec(nativePath)) {
- /*
- * It's not a directory and doesn't have the correct extension.
- * Therefore it can't be executable
- */
-
+ /* If doesn't have the correct extension, it can't be executable */
+ if ((mode & X_OK) && !NativeIsExec(nativePath)) {
Tcl_SetErrno(EACCES);
return -1;
}
+ /* Special case for read/write/executable check on file */
+ if ((mode & (R_OK|W_OK|X_OK)) && !(mode & ~(R_OK|W_OK|X_OK))) {
+ DWORD mask = 0;
+ HANDLE hFile;
+ if (mode & R_OK) { mask |= GENERIC_READ; }
+ if (mode & W_OK) { mask |= GENERIC_WRITE; }
+ if (mode & X_OK) { mask |= GENERIC_EXECUTE; }
+
+ hFile = CreateFile(nativePath, mask,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL);
+ if (hFile != INVALID_HANDLE_VALUE) {
+ CloseHandle(hFile);
+ return 0;
+ }
+ /* fast exit if access was denied */
+ if (GetLastError() == ERROR_ACCESS_DENIED) {
+ Tcl_SetErrno(EACCES);
+ return -1;
+ }
+ }
+ /* We cannnot verify the access fast, check it below using security info. */
}
/*
@@ -1792,10 +1849,12 @@ NativeIsExec(
return 0;
}
- if ((_tcsicmp(path+len-3, TEXT("exe")) == 0)
- || (_tcsicmp(path+len-3, TEXT("com")) == 0)
- || (_tcsicmp(path+len-3, TEXT("cmd")) == 0)
- || (_tcsicmp(path+len-3, TEXT("bat")) == 0)) {
+ path += len-3;
+ if ((_tcsicmp(path, TEXT("exe")) == 0)
+ || (_tcsicmp(path, TEXT("com")) == 0)
+ || (_tcsicmp(path, TEXT("cmd")) == 0)
+ || (_tcsicmp(path, TEXT("cmd")) == 0)
+ || (_tcsicmp(path, TEXT("bat")) == 0)) {
return 1;
}
return 0;
@@ -1972,7 +2031,8 @@ NativeStat(
*/
fileHandle = CreateFile(nativePath, GENERIC_READ,
- FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
if (fileHandle != INVALID_HANDLE_VALUE) {
diff --git a/win/tclWinInit.c b/win/tclWinInit.c
index 98c7ed5..ff5327d 100644
--- a/win/tclWinInit.c
+++ b/win/tclWinInit.c
@@ -112,7 +112,12 @@ 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);
+
+#if TCL_UTF_MAX < 4
+static void ToUtf(const WCHAR *wSrc, char *dst);
+#else
+#define ToUtf(wSrc, dst) WideCharToMultiByte(CP_UTF8, 0, wSrc, -1, dst, MAX_PATH * TCL_UTF_MAX, NULL, NULL)
+#endif
/*
*---------------------------------------------------------------------------
@@ -435,7 +440,7 @@ InitializeSourceLibraryDir(
*
* ToUtf --
*
- * Convert a char string to a UTF string.
+ * Convert a wchar string to a UTF string.
*
* Results:
* None.
@@ -446,21 +451,19 @@ InitializeSourceLibraryDir(
*---------------------------------------------------------------------------
*/
-static int
+#if TCL_UTF_MAX < 4
+static void
ToUtf(
const WCHAR *wSrc,
char *dst)
{
- char *start;
-
- start = dst;
while (*wSrc != '\0') {
dst += Tcl_UniCharToUtf(*wSrc, dst);
wSrc++;
}
*dst = '\0';
- return (int) (dst - start);
}
+#endif
/*
*---------------------------------------------------------------------------
@@ -514,6 +517,27 @@ Tcl_GetEncodingNameFromEnvironment(
return Tcl_DStringValue(bufPtr);
}
+const char *
+TclpGetUserName(
+ Tcl_DString *bufferPtr) /* Uninitialized or free DString filled with
+ * the name of user. */
+{
+ Tcl_DStringInit(bufferPtr);
+
+ if (TclGetEnv("USERNAME", bufferPtr) == NULL) {
+ TCHAR szUserName[UNLEN+1];
+ DWORD cchUserNameLen = UNLEN;
+
+ if (!GetUserName(szUserName, &cchUserNameLen)) {
+ return NULL;
+ }
+ cchUserNameLen--;
+ cchUserNameLen *= sizeof(TCHAR);
+ Tcl_WinTCharToUtf(szUserName, cchUserNameLen, bufferPtr);
+ }
+ return Tcl_DStringValue(bufferPtr);
+}
+
/*
*---------------------------------------------------------------------------
*
@@ -544,8 +568,6 @@ TclpSetVariables(
static OSVERSIONINFOW osInfo;
static int osInfoInitialized = 0;
Tcl_DString ds;
- TCHAR szUserName[UNLEN+1];
- DWORD cchUserNameLen = UNLEN;
Tcl_SetVar2Ex(interp, "tclDefaultLibrary", NULL,
TclGetProcessGlobalValue(&defaultLibraryDir), TCL_GLOBAL_ONLY);
@@ -623,15 +645,8 @@ TclpSetVariables(
* Note: cchUserNameLen is number of characters including nul terminator.
*/
- Tcl_DStringInit(&ds);
- if (TclGetEnv("USERNAME", &ds) == NULL) {
- if (GetUserName(szUserName, &cchUserNameLen) != 0) {
- int cbUserNameLen = cchUserNameLen - 1;
- cbUserNameLen *= sizeof(TCHAR);
- Tcl_WinTCharToUtf(szUserName, cbUserNameLen, &ds);
- }
- }
- Tcl_SetVar2(interp, "tcl_platform", "user", Tcl_DStringValue(&ds),
+ ptr = TclpGetUserName(&ds);
+ Tcl_SetVar2(interp, "tcl_platform", "user", ptr ? ptr : "",
TCL_GLOBAL_ONLY);
Tcl_DStringFree(&ds);
@@ -648,7 +663,7 @@ TclpSetVariables(
* TclpFindVariable --
*
* Locate the entry in environ for a given name. On Unix this routine is
- * case sensitive, on Windows this matches mioxed case.
+ * case sensitive, on Windows this matches mixed case.
*
* Results:
* The return value is the index in environ of an entry with the name
diff --git a/win/tclWinInt.h b/win/tclWinInt.h
index 43799d0..d0844da 100644
--- a/win/tclWinInt.h
+++ b/win/tclWinInt.h
@@ -90,6 +90,8 @@ MODULE_SCOPE void * TclpGetAllocCache(void);
MODULE_SCOPE void TclpSetAllocCache(void *);
#endif /* TCL_THREADS */
+MODULE_SCOPE const char*TclpGetUserName(Tcl_DString *bufferPtr);
+
/* Needed by tclWinFile.c and tclWinFCmd.c */
#ifndef FILE_ATTRIBUTE_REPARSE_POINT
#define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400
diff --git a/win/tclWinLoad.c b/win/tclWinLoad.c
index 26512b1..2946ea2 100644
--- a/win/tclWinLoad.c
+++ b/win/tclWinLoad.c
@@ -63,9 +63,10 @@ TclpDlopen(
* file. */
int flags)
{
- HINSTANCE hInstance;
+ HINSTANCE hInstance = NULL;
const TCHAR *nativeName;
Tcl_LoadHandle handlePtr;
+ DWORD firstError;
/*
* First try the full path the user gave us. This is particularly
@@ -74,7 +75,10 @@ TclpDlopen(
*/
nativeName = Tcl_FSGetNativePath(pathPtr);
- hInstance = LoadLibraryEx(nativeName,NULL,LOAD_WITH_ALTERED_SEARCH_PATH);
+ if (nativeName != NULL) {
+ hInstance = LoadLibraryEx(nativeName, NULL,
+ LOAD_WITH_ALTERED_SEARCH_PATH);
+ }
if (hInstance == NULL) {
/*
* Let the OS loader examine the binary search path for whatever
@@ -84,6 +88,13 @@ TclpDlopen(
Tcl_DString ds;
+ /*
+ * Remember the first error on load attempt to be used if the
+ * second load attempt below also fails.
+ */
+ firstError = (nativeName == NULL) ?
+ ERROR_MOD_NOT_FOUND : GetLastError();
+
nativeName = Tcl_WinUtfToTChar(Tcl_GetString(pathPtr), -1, &ds);
hInstance = LoadLibraryEx(nativeName, NULL,
LOAD_WITH_ALTERED_SEARCH_PATH);
@@ -91,8 +102,21 @@ TclpDlopen(
}
if (hInstance == NULL) {
- DWORD lastError = GetLastError();
- Tcl_Obj *errMsg = Tcl_ObjPrintf("couldn't load library \"%s\": ",
+ DWORD lastError;
+ Tcl_Obj *errMsg;
+
+ /*
+ * We choose to only use the error from the second call if the first
+ * call failed due to the file not being found. Else stick to the
+ * first error for reporting purposes.
+ */
+ if (firstError == ERROR_MOD_NOT_FOUND ||
+ firstError == ERROR_DLL_NOT_FOUND)
+ lastError = GetLastError();
+ else
+ lastError = firstError;
+
+ errMsg = Tcl_ObjPrintf("couldn't load library \"%s\": ",
Tcl_GetString(pathPtr));
/*
@@ -129,7 +153,11 @@ TclpDlopen(
Tcl_AppendToObj(errMsg, "the library initialization"
" routine failed", -1);
break;
- default:
+ case ERROR_BAD_EXE_FORMAT:
+ Tcl_SetErrorCode(interp, "WIN_LOAD", "BAD_EXE_FORMAT", NULL);
+ Tcl_AppendToObj(errMsg, "Bad exe format. Possibly a 32/64-bit mismatch.", -1);
+ break;
+ default:
TclWinConvertError(lastError);
Tcl_AppendToObj(errMsg, Tcl_PosixError(interp), -1);
}
diff --git a/win/tclWinPipe.c b/win/tclWinPipe.c
index 5246d53..9095938 100644
--- a/win/tclWinPipe.c
+++ b/win/tclWinPipe.c
@@ -1289,7 +1289,7 @@ ApplicationType(
/*
* Look for the program as an external program. First try the name as it
- * is, then try adding .com, .exe, and .bat, in that order, to the name,
+ * is, then try adding .com, .exe, .bat and .cmd, in that order, to the name,
* looking for an executable.
*
* Using the raw SearchPath() function doesn't do quite what is necessary.
@@ -1408,7 +1408,7 @@ ApplicationType(
return APPL_NONE;
}
- if ((applType == APPL_DOS) || (applType == APPL_WIN3X)) {
+ if (applType == APPL_WIN3X) {
/*
* Replace long path name of executable with short path name for
* 16-bit applications. Otherwise the application may not be able to
@@ -1442,6 +1442,86 @@ ApplicationType(
*----------------------------------------------------------------------
*/
+static const char *
+BuildCmdLineBypassBS(
+ const char *current,
+ const char **bspos
+) {
+ /* mark first backslash possition */
+ if (!*bspos) {
+ *bspos = current;
+ }
+ do {
+ current++;
+ } while (*current == '\\');
+ return current;
+}
+
+static void
+QuoteCmdLineBackslash(
+ Tcl_DString *dsPtr,
+ const char *start,
+ const char *current,
+ const char *bspos
+) {
+ if (!bspos) {
+ if (current > start) { /* part before current (special) */
+ Tcl_DStringAppend(dsPtr, start, (int) (current - start));
+ }
+ } else {
+ if (bspos > start) { /* part before first backslash */
+ Tcl_DStringAppend(dsPtr, start, (int) (bspos - start));
+ }
+ while (bspos++ < current) { /* each backslash twice */
+ TclDStringAppendLiteral(dsPtr, "\\\\");
+ }
+ }
+}
+
+static const char *
+QuoteCmdLinePart(
+ Tcl_DString *dsPtr,
+ const char *start,
+ const char *special,
+ const char *specMetaChars,
+ const char **bspos
+) {
+ if (!*bspos) {
+ /* rest before special (before quote) */
+ QuoteCmdLineBackslash(dsPtr, start, special, NULL);
+ start = special;
+ } else {
+ /* rest before first backslash and backslashes into new quoted block */
+ QuoteCmdLineBackslash(dsPtr, start, *bspos, NULL);
+ start = *bspos;
+ }
+ /*
+ * escape all special chars enclosed in quotes like `"..."`, note that here we
+ * don't must escape `\` (with `\`), because it's outside of the main quotes,
+ * so `\` remains `\`, but important - not at end of part, because results as
+ * before the quote, so `%\%\` should be escaped as `"%\%"\\`).
+ */
+ TclDStringAppendLiteral(dsPtr, "\""); /* opening escape quote-char */
+ do {
+ *bspos = NULL;
+ special++;
+ if (*special == '\\') {
+ /* bypass backslashes (and mark first backslash possition)*/
+ special = BuildCmdLineBypassBS(special, bspos);
+ if (*special == '\0') break;
+ }
+ } while (*special && strchr(specMetaChars, *special));
+ if (!*bspos) {
+ /* unescaped rest before quote */
+ QuoteCmdLineBackslash(dsPtr, start, special, NULL);
+ } else {
+ /* unescaped rest before first backslash (rather belongs to the main block) */
+ QuoteCmdLineBackslash(dsPtr, start, *bspos, NULL);
+ }
+ TclDStringAppendLiteral(dsPtr, "\""); /* closing escape quote-char */
+ return special;
+}
+
static void
BuildCommandLine(
const char *executable, /* Full path of executable (including
@@ -1451,10 +1531,22 @@ BuildCommandLine(
Tcl_DString *linePtr) /* Initialized Tcl_DString that receives the
* command line (TCHAR). */
{
- const char *arg, *start, *special;
- int quote, i;
+ const char *arg, *start, *special, *bspos;
+ int quote = 0, i;
Tcl_DString ds;
+ /* characters to enclose in quotes if unpaired quote flag set */
+ static const char specMetaChars[] = "&|^<>!()%";
+ /* character to enclose in quotes in any case (regardless unpaired-flag) */
+ static const char specMetaChars2[] = "%";
+
+ /* Quote flags:
+ * CL_ESCAPE - escape argument;
+ * CL_QUOTE - enclose in quotes;
+ * CL_UNPAIRED - previous arguments chain contains unpaired quote-char;
+ */
+ enum {CL_ESCAPE = 1, CL_QUOTE = 2, CL_UNPAIRED = 4};
+
Tcl_DStringInit(&ds);
/*
@@ -1474,61 +1566,98 @@ BuildCommandLine(
TclDStringAppendLiteral(&ds, " ");
}
- quote = 0;
+ quote &= ~(CL_ESCAPE|CL_QUOTE); /* reset escape flags */
+ bspos = NULL;
if (arg[0] == '\0') {
- quote = 1;
+ quote = CL_QUOTE;
} else {
int count;
Tcl_UniChar ch;
-
- for (start = arg; *start != '\0'; start += count) {
+ for (start = arg;
+ *start != '\0' &&
+ (quote & (CL_ESCAPE|CL_QUOTE)) != (CL_ESCAPE|CL_QUOTE);
+ start += count
+ ) {
count = Tcl_UtfToUniChar(start, &ch);
- if (Tcl_UniCharIsSpace(ch)) { /* INTL: ISO space. */
- quote = 1;
+ if (count > 1) continue;
+ if (Tcl_UniCharIsSpace(ch)) {
+ quote |= CL_QUOTE; /* quote only */
+ if (bspos) { /* if backslash found - escape & quote */
+ quote |= CL_ESCAPE;
+ break;
+ }
+ continue;
+ }
+ if (strchr(specMetaChars, *start)) {
+ quote |= (CL_ESCAPE|CL_QUOTE); /*escape & quote */
break;
}
+ if (*start == '"') {
+ quote |= CL_ESCAPE; /* escape only */
+ continue;
+ }
+ if (*start == '\\') {
+ bspos = start;
+ if (quote & CL_QUOTE) { /* if quote - escape & quote */
+ quote |= CL_ESCAPE;
+ break;
+ }
+ continue;
+ }
}
+ bspos = NULL;
}
- if (quote) {
+ if (quote & CL_QUOTE) {
+ /* start of argument (main opening quote-char) */
TclDStringAppendLiteral(&ds, "\"");
}
- start = arg;
- for (special = arg; ; ) {
- if ((*special == '\\') && (special[1] == '\\' ||
- special[1] == '"' || (quote && special[1] == '\0'))) {
- Tcl_DStringAppend(&ds, start, (int) (special - start));
- start = special;
- while (1) {
- special++;
- if (*special == '"' || (quote && *special == '\0')) {
- /*
- * N backslashes followed a quote -> insert N * 2 + 1
- * backslashes then a quote.
- */
-
- Tcl_DStringAppend(&ds, start,
- (int) (special - start));
- break;
- }
- if (*special != '\\') {
- break;
- }
+ if (!(quote & CL_ESCAPE)) {
+ /* nothing to escape */
+ Tcl_DStringAppend(&ds, arg, -1);
+ } else {
+ start = arg;
+ for (special = arg; *special != '\0'; ) {
+ /* position of `\` is important before quote or at end (equal `\"` because quoted) */
+ if (*special == '\\') {
+ /* bypass backslashes (and mark first backslash possition)*/
+ special = BuildCmdLineBypassBS(special, &bspos);
+ if (*special == '\0') break;
}
- Tcl_DStringAppend(&ds, start, (int) (special - start));
- start = special;
- }
- if (*special == '"') {
- Tcl_DStringAppend(&ds, start, (int) (special - start));
- TclDStringAppendLiteral(&ds, "\\\"");
- start = special + 1;
- }
- if (*special == '\0') {
- break;
+ /* ["] */
+ if (*special == '"') {
+ quote ^= CL_UNPAIRED; /* invert unpaired flag - observe unpaired quotes */
+ /* add part before (and escape backslashes before quote) */
+ QuoteCmdLineBackslash(&ds, start, special, bspos);
+ bspos = NULL;
+ /* escape using backslash */
+ TclDStringAppendLiteral(&ds, "\\\"");
+ start = ++special;
+ continue;
+ }
+ /* unpaired (escaped) quote causes special handling on meta-chars */
+ if ((quote & CL_UNPAIRED) && strchr(specMetaChars, *special)) {
+ special = QuoteCmdLinePart(&ds, start, special, specMetaChars, &bspos);
+ /* start to current or first backslash */
+ start = !bspos ? special : bspos;
+ continue;
+ }
+ /* special case for % - should be enclosed always (paired also) */
+ if (strchr(specMetaChars2, *special)) {
+ special = QuoteCmdLinePart(&ds, start, special, specMetaChars2, &bspos);
+ /* start to current or first backslash */
+ start = !bspos ? special : bspos;
+ continue;
+ }
+ /* other not special (and not meta) character */
+ bspos = NULL; /* reset last backslash possition (not interesting) */
+ special++;
}
- special++;
+ /* rest of argument (and escape backslashes before closing main quote) */
+ QuoteCmdLineBackslash(&ds, start, special,
+ (quote & CL_QUOTE) ? bspos : NULL);
}
- Tcl_DStringAppend(&ds, start, (int) (special - start));
- if (quote) {
+ if (quote & CL_QUOTE) {
+ /* end of argument (main closing quote-char) */
TclDStringAppendLiteral(&ds, "\"");
}
}
diff --git a/win/tclWinPort.h b/win/tclWinPort.h
index 159a708..29b1447 100644
--- a/win/tclWinPort.h
+++ b/win/tclWinPort.h
@@ -568,4 +568,7 @@ typedef DWORD_PTR * PDWORD_PTR;
# define LABEL_SECURITY_INFORMATION (0x00000010L)
#endif
+#define Tcl_DirEntry void
+#define TclDIR void
+
#endif /* _TCLWINPORT */
diff --git a/win/tclWinReg.c b/win/tclWinReg.c
index de48b9b..f93a553 100644
--- a/win/tclWinReg.c
+++ b/win/tclWinReg.c
@@ -22,13 +22,6 @@
#endif
#include <stdlib.h>
-#ifndef UNICODE
-# undef Tcl_WinTCharToUtf
-# define Tcl_WinTCharToUtf(a,b,c) Tcl_ExternalToUtfDString(NULL,a,b,c)
-# undef Tcl_WinUtfToTChar
-# define Tcl_WinUtfToTChar(a,b,c) Tcl_UtfToExternalDString(NULL,a,b,c)
-#endif /* !UNICODE */
-
/*
* Ensure that we can say which registry is being accessed.
*/
@@ -131,6 +124,25 @@ static int SetValue(Tcl_Interp *interp, Tcl_Obj *keyNameObj,
Tcl_Obj *valueNameObj, Tcl_Obj *dataObj,
Tcl_Obj *typeObj, REGSAM mode);
+static unsigned char *
+getByteArrayFromObj(
+ Tcl_Obj *objPtr,
+ size_t *lengthPtr
+) {
+ int length;
+
+ unsigned char *result = Tcl_GetByteArrayFromObj(objPtr, &length);
+#if TCL_MAJOR_VERSION > 8
+ if (sizeof(TCL_HASH_TYPE) > sizeof(int)) {
+ /* 64-bit and TIP #494 situation: */
+ *lengthPtr = *(TCL_HASH_TYPE *) objPtr->internalRep.twoPtrValue.ptr1;
+ } else
+#endif
+ /* 32-bit or without TIP #494 */
+ *lengthPtr = (size_t) (unsigned) length;
+ return result;
+}
+
DLLEXPORT int Registry_Init(Tcl_Interp *interp);
DLLEXPORT int Registry_Unload(Tcl_Interp *interp, int flags);
@@ -163,7 +175,7 @@ Registry_Init(
cmd = Tcl_CreateObjCommand(interp, "registry", RegistryObjCmd,
interp, DeleteCmd);
Tcl_SetAssocData(interp, REGISTRY_ASSOC_KEY, NULL, cmd);
- return Tcl_PkgProvide(interp, "registry", "1.3.2");
+ return Tcl_PkgProvide(interp, "registry", "1.3.3");
}
/*
@@ -414,12 +426,12 @@ DeleteKey(
*/
keyName = Tcl_GetString(keyNameObj);
- buffer = ckalloc(keyNameObj->length + 1);
+ buffer = Tcl_Alloc(keyNameObj->length + 1);
strcpy(buffer, keyName);
if (ParseKeyName(interp, buffer, &hostName, &rootKey,
&keyName) != TCL_OK) {
- ckfree(buffer);
+ Tcl_Free(buffer);
return TCL_ERROR;
}
@@ -427,7 +439,7 @@ DeleteKey(
Tcl_SetObjResult(interp,
Tcl_NewStringObj("bad key: cannot delete root keys", -1));
Tcl_SetErrorCode(interp, "WIN_REG", "DEL_ROOT_KEY", NULL);
- ckfree(buffer);
+ Tcl_Free(buffer);
return TCL_ERROR;
}
@@ -442,7 +454,7 @@ DeleteKey(
mode |= KEY_ENUMERATE_SUB_KEYS | DELETE;
result = OpenSubKey(hostName, rootKey, keyName, mode, 0, &subkey);
if (result != ERROR_SUCCESS) {
- ckfree(buffer);
+ Tcl_Free(buffer);
if (result == ERROR_FILE_NOT_FOUND) {
return TCL_OK;
}
@@ -470,7 +482,7 @@ DeleteKey(
}
RegCloseKey(subkey);
- ckfree(buffer);
+ Tcl_Free(buffer);
return result;
}
@@ -499,7 +511,6 @@ DeleteValue(
{
HKEY key;
char *valueName;
- size_t length;
DWORD result;
Tcl_DString ds;
@@ -513,8 +524,7 @@ DeleteValue(
}
valueName = Tcl_GetString(valueNameObj);
- length = valueNameObj->length;
- Tcl_WinUtfToTChar(valueName, length, &ds);
+ Tcl_WinUtfToTChar(valueName, valueNameObj->length, &ds);
result = RegDeleteValue(key, (const TCHAR *)Tcl_DStringValue(&ds));
Tcl_DStringFree(&ds);
if (result != ERROR_SUCCESS) {
@@ -603,8 +613,7 @@ GetKeyNames(
}
break;
}
- Tcl_WinTCharToUtf(buffer, bufSize * sizeof(TCHAR), &ds);
- name = Tcl_DStringValue(&ds);
+ name = Tcl_WinTCharToUtf(buffer, bufSize * sizeof(TCHAR), &ds);
if (pattern && !Tcl_StringMatch(name, pattern)) {
Tcl_DStringFree(&ds);
continue;
@@ -655,7 +664,6 @@ GetType(
Tcl_DString ds;
const char *valueName;
const TCHAR *nativeValue;
- size_t length;
/*
* Attempt to open the key for reading.
@@ -671,8 +679,7 @@ GetType(
*/
valueName = Tcl_GetString(valueNameObj);
- length = valueNameObj->length;
- nativeValue = Tcl_WinUtfToTChar(valueName, length, &ds);
+ nativeValue = Tcl_WinUtfToTChar(valueName, valueNameObj->length, &ds);
result = RegQueryValueEx(key, nativeValue, NULL, &type,
NULL, NULL);
Tcl_DStringFree(&ds);
@@ -728,7 +735,6 @@ GetValue(
const TCHAR *nativeValue;
DWORD result, length, type;
Tcl_DString data, buf;
- size_t nameLen;
/*
* Attempt to open the key for reading.
@@ -754,8 +760,7 @@ GetValue(
length = TCL_DSTRING_STATIC_SIZE/sizeof(TCHAR) - 1;
valueName = Tcl_GetString(valueNameObj);
- nameLen = valueNameObj->length;
- nativeValue = Tcl_WinUtfToTChar(valueName, nameLen, &buf);
+ nativeValue = Tcl_WinUtfToTChar(valueName, valueNameObj->length, &buf);
result = RegQueryValueEx(key, nativeValue, NULL, &type,
(BYTE *) Tcl_DStringValue(&data), &length);
@@ -944,13 +949,11 @@ OpenKey(
HKEY *keyPtr) /* Returned HKEY. */
{
char *keyName, *buffer, *hostName;
- size_t length;
HKEY rootKey;
DWORD result;
keyName = Tcl_GetString(keyNameObj);
- length = keyNameObj->length;
- buffer = ckalloc(length + 1);
+ buffer = Tcl_Alloc(keyNameObj->length + 1);
strcpy(buffer, keyName);
result = ParseKeyName(interp, buffer, &hostName, &rootKey, &keyName);
@@ -966,7 +969,7 @@ OpenKey(
}
}
- ckfree(buffer);
+ Tcl_Free(buffer);
return result;
}
@@ -1019,7 +1022,9 @@ OpenSubKey(
* this key must be closed by the caller.
*/
- keyName = (char *) Tcl_WinUtfToTChar(keyName, -1, &buf);
+ if (keyName) {
+ keyName = (char *) Tcl_WinUtfToTChar(keyName, -1, &buf);
+ }
if (flags & REG_CREATE) {
DWORD create;
@@ -1037,7 +1042,9 @@ OpenSubKey(
result = RegOpenKeyEx(rootKey, (TCHAR *)keyName, 0, mode,
keyPtr);
}
- Tcl_DStringFree(&buf);
+ if (keyName) {
+ Tcl_DStringFree(&buf);
+ }
/*
* Be sure to close the root key since we are done with it now.
@@ -1248,7 +1255,6 @@ SetValue(
REGSAM mode) /* Mode flags to pass. */
{
int type;
- size_t length;
DWORD result;
HKEY key;
const char *valueName;
@@ -1269,8 +1275,7 @@ SetValue(
}
valueName = Tcl_GetString(valueNameObj);
- length = valueNameObj->length;
- valueName = (char *) Tcl_WinUtfToTChar(valueName, length, &nameBuf);
+ valueName = (char *) Tcl_WinUtfToTChar(valueName, valueNameObj->length, &nameBuf);
if (type == REG_DWORD || type == REG_DWORD_BIG_ENDIAN) {
int value;
@@ -1305,8 +1310,7 @@ SetValue(
for (i = 0; i < objc; i++) {
const char *bytes = Tcl_GetString(objv[i]);
- length = objv[i]->length;
- Tcl_DStringAppend(&data, bytes, length);
+ Tcl_DStringAppend(&data, bytes, objv[i]->length);
/*
* Add a null character to separate this value from the next.
@@ -1326,28 +1330,26 @@ SetValue(
Tcl_DString buf;
const char *data = Tcl_GetString(dataObj);
- length = dataObj->length;
- data = (char *) Tcl_WinUtfToTChar(data, length, &buf);
+ data = (char *) Tcl_WinUtfToTChar(data, dataObj->length, &buf);
/*
* Include the null in the length, padding if needed for WCHAR.
*/
Tcl_DStringSetLength(&buf, Tcl_DStringLength(&buf)+1);
- length = Tcl_DStringLength(&buf) + 1;
result = RegSetValueEx(key, (TCHAR *) valueName, 0,
- (DWORD) type, (BYTE *) data, (DWORD) length);
+ (DWORD) type, (BYTE *) data, (DWORD) Tcl_DStringLength(&buf) + 1);
Tcl_DStringFree(&buf);
} else {
BYTE *data;
- int bytelength;
+ size_t bytelength;
/*
* Store binary data in the registry.
*/
- data = (BYTE *) Tcl_GetByteArrayFromObj(dataObj, &bytelength);
+ data = (BYTE *) getByteArrayFromObj(dataObj, &bytelength);
result = RegSetValueEx(key, (TCHAR *) valueName, 0,
(DWORD) type, data, (DWORD) bytelength);
}
@@ -1408,8 +1410,7 @@ BroadcastValue(
}
str = Tcl_GetString(objv[0]);
- len = objv[0]->length;
- wstr = (WCHAR *) Tcl_WinUtfToTChar(str, len, &ds);
+ wstr = (WCHAR *) Tcl_WinUtfToTChar(str, objv[0]->length, &ds);
if (Tcl_DStringLength(&ds) == 0) {
wstr = NULL;
}
diff --git a/win/tclWinSock.c b/win/tclWinSock.c
index da2e60a..e2479e81 100644
--- a/win/tclWinSock.c
+++ b/win/tclWinSock.c
@@ -686,7 +686,7 @@ WaitForConnect(
}
/*
- * A non blocking socket waiting for an asyncronous connect
+ * A non blocking socket waiting for an asynchronous connect
* returns directly the error EWOULDBLOCK
*/
@@ -1606,9 +1606,9 @@ TcpGetHandleProc(
*
* This might be called in 3 circumstances:
* - By a regular socket command
- * - By the event handler to continue an asynchroneous connect
+ * - By the event handler to continue an asynchronously connect
* - By a blocking socket function (gets/puts) to terminate the
- * connect synchroneously
+ * connect synchronously
*
* Results:
* TCL_OK, if the socket was successfully connected or an asynchronous
@@ -1620,7 +1620,7 @@ TcpGetHandleProc(
*
* 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
+ * an IPv4/IPv6 dual stack host. For handling asynchronously 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
@@ -1628,7 +1628,7 @@ TcpGetHandleProc(
* 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.
+ * For synchronously connecting sockets, the loops work the usual way.
*
*----------------------------------------------------------------------
*/
@@ -1718,7 +1718,7 @@ TcpConnect(
continue;
}
/*
- * For asyncroneous connect set the socket in nonblocking mode
+ * For asynchroneous connect set the socket in nonblocking mode
* and activate connect notification
*/
if (async_connect) {
@@ -1806,7 +1806,7 @@ TcpConnect(
/*
* Clear the tsd socket list pointer if we did not wait for
- * the FD_CONNECT asyncroneously
+ * the FD_CONNECT asynchroneously
*/
tsdPtr->pendingTcpState = NULL;
@@ -1868,7 +1868,7 @@ out:
SetEvent(tsdPtr->socketListLock);
}
/*
- * Error message on syncroneous connect
+ * Error message on synchroneous connect
*/
if (interp != NULL) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
diff --git a/win/tclWinTest.c b/win/tclWinTest.c
index b3ad626..aa2c15a 100644
--- a/win/tclWinTest.c
+++ b/win/tclWinTest.c
@@ -399,9 +399,11 @@ TestplatformChmod(
{
static const SECURITY_INFORMATION infoBits = OWNER_SECURITY_INFORMATION
| GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
+ /* don't reset change permissions mask (WRITE_DAC, allow test-cases restore it to cleanup) */
static const DWORD readOnlyMask = FILE_DELETE_CHILD | FILE_ADD_FILE
| FILE_ADD_SUBDIRECTORY | FILE_WRITE_EA | FILE_APPEND_DATA
- | FILE_WRITE_DATA | DELETE;
+ | FILE_WRITE_DATA
+ | DELETE;
/*
* References to security functions (only available on NT and later).
@@ -565,11 +567,13 @@ TestplatformChmod(
}
/*
- * Apply the new ACL.
+ * Apply the new ACL. Note PROTECTED_DACL_SECURITY_INFORMATION can be used
+ * to remove inherited ACL (we need to overwrite the default ACL's in this case)
*/
if (set_readOnly == acl_readOnly_found || SetNamedSecurityInfoA(
- (LPSTR) nativePath, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
+ (LPSTR) nativePath, SE_FILE_OBJECT,
+ DACL_SECURITY_INFORMATION /*| PROTECTED_DACL_SECURITY_INFORMATION*/,
NULL, NULL, newAcl, NULL) == ERROR_SUCCESS) {
res = 0;
}
diff --git a/win/tclWinThrd.c b/win/tclWinThrd.c
index 8f3ddb9..0f83526 100644
--- a/win/tclWinThrd.c
+++ b/win/tclWinThrd.c
@@ -247,7 +247,7 @@ TclpThreadCreate(
/*
* The only purpose of this is to decrement the reference count so the
- * OS resources will be reaquired when the thread closes.
+ * OS resources will be reacquired when the thread closes.
*/
CloseHandle(tHandle);
@@ -405,7 +405,7 @@ TclpInitUnlock(void)
* mutexes, condition variables, and thread local storage keys.
*
* This lock must be different than the initLock because the initLock is
- * held during creation of syncronization objects.
+ * held during creation of synchronization objects.
*
* Results:
* None.
@@ -555,7 +555,7 @@ static void FinalizeConditionEvent(ClientData data);
* None.
*
* Side effects:
- * May block the current thread. The mutex is aquired when this returns.
+ * May block the current thread. The mutex is acquired when this returns.
*
*----------------------------------------------------------------------
*/
@@ -655,7 +655,7 @@ TclpFinalizeMutex(
* None.
*
* Side effects:
- * May block the current thread. The mutex is aquired when this returns.
+ * May block the current thread. The mutex is acquired when this returns.
* Will allocate memory for a HANDLE and initialize this the first time
* this Tcl_Condition is used.
*
diff --git a/win/tclWinTime.c b/win/tclWinTime.c
index e1aff48..fd14595 100644
--- a/win/tclWinTime.c
+++ b/win/tclWinTime.c
@@ -724,6 +724,11 @@ TclpGetDate(
time_t time;
if (!useGMT) {
+#if defined(_MSC_VER) && (_MSC_VER >= 1900)
+# undef timezone /* prevent conflict with timezone() function */
+ long timezone = 0;
+#endif
+
tzset();
/*
@@ -753,6 +758,10 @@ TclpGetDate(
return TclpLocaltime(t);
}
+#if defined(_MSC_VER) && (_MSC_VER >= 1900)
+ _get_timezone(&timezone);
+#endif
+
time = *t - timezone;
/*
diff --git a/win/tclooConfig.sh b/win/tclooConfig.sh
index ee10b81..2279542 100644
--- a/win/tclooConfig.sh
+++ b/win/tclooConfig.sh
@@ -16,4 +16,4 @@ TCLOO_STUB_LIB_SPEC=""
TCLOO_INCLUDE_SPEC=""
TCLOO_PRIVATE_INCLUDE_SPEC=""
TCLOO_CFLAGS=""
-TCLOO_VERSION=1.0.4
+TCLOO_VERSION=1.1.0