]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add tool capable of downloading a TclKit (and its associated SDK) on Windows.
authormistachkin <mistachkin@noemail.net>
Fri, 9 Oct 2015 17:36:06 +0000 (17:36 +0000)
committermistachkin <mistachkin@noemail.net>
Fri, 9 Oct 2015 17:36:06 +0000 (17:36 +0000)
FossilOrigin-Name: 50673ddaf813335777673fa8585997a7551e5323

manifest
manifest.uuid
tool/GetFile.cs [new file with mode: 0644]
tool/GetTclKit.bat [new file with mode: 0644]

index e48c25ca0ffaea26079e4cd335c85179eab42ab1..f9798bddfc54c27f3ff12b66cfeecb94a4028817 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Convert\sthe\stool/tostr.awk\sscript\sinto\stool/tostr.tcl.\s\sRemove\stwo\sobsolete\nMakefiles.\s\sPurge\sNAWK\sfrom\sthe\sconfigure\sscript\sand\sfrom\sunix\smakefiles.\nThere\sare\sstill\stwo\suses\sof\sNAWK\sin\sMakefile.msc.
-D 2015-10-07T12:36:42.935
+C Add\stool\scapable\sof\sdownloading\sa\sTclKit\s(and\sits\sassociated\sSDK)\son\sWindows.
+D 2015-10-09T17:36:06.162
 F Makefile.in 2a247c733c2dd6fab703df04dd009b26413956f5
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc 17ce18bb7e9ca2ad3abed9b0a1fcbef3fbe8f307
@@ -1331,6 +1331,8 @@ F test/without_rowid6.test 1f99644e6508447fb050f73697350c7ceca3392e
 F test/wordcount.c 9915e06cb33d8ca8109b8700791afe80d305afda
 F test/zeroblob.test 3857870fe681b8185654414a9bccfde80b62a0fa
 F test/zerodamage.test cf6748bad89553cc1632be51a6f54e487e4039ac
+F tool/GetFile.cs 963f7064b0b221b08feadb28894c3537916261ac
+F tool/GetTclKit.bat 46092b151f7bb4f2a2735dfa1ada09736a4b35a4
 F tool/addopcodes.tcl 7cc82ecca456a6b3148abf492b0419b83140881a
 F tool/build-all-msvc.bat 761d8c82a1a529261291812732a853a1b4256d85 x
 F tool/build-shell.sh 950f47c6174f1eea171319438b93ba67ff5bf367
@@ -1387,7 +1389,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 8bbf37142ef2759274668f6da114b5c8072e42db
-R 07b752064456f02ed0865632d443e697
-U drh
-Z 7087d810f3111cb2f52748462bb8f6e3
+P 5b6775215327a89232f5059653747a18e83b8b4b
+R 785a6cb71a38ff7702fb9be84ec0066b
+U mistachkin
+Z 9f18b94f6375bd392631336361a9d038
index 7f24566eb23c5d721ea5171dcb1e56305ac9b308..38dfe5be77ccbb613a62359f8a7553324f8fe333 100644 (file)
@@ -1 +1 @@
-5b6775215327a89232f5059653747a18e83b8b4b
\ No newline at end of file
+50673ddaf813335777673fa8585997a7551e5323
\ No newline at end of file
diff --git a/tool/GetFile.cs b/tool/GetFile.cs
new file mode 100644 (file)
index 0000000..9c489c6
--- /dev/null
@@ -0,0 +1,450 @@
+/*
+** 2015 October 7
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    May you do good and not evil.
+**    May you find forgiveness for yourself and forgive others.
+**    May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains C# code to download a single file based on a URI.
+*/
+\r
+using System;\r
+using System.ComponentModel;\r
+using System.Diagnostics;\r
+using System.IO;\r
+using System.Net;\r
+using System.Reflection;\r
+using System.Runtime.InteropServices;\r
+using System.Threading;\r
+\r
+///////////////////////////////////////////////////////////////////////////////\r
+\r
+#region Assembly Metadata\r
+[assembly: AssemblyTitle("GetFile Tool")]\r
+[assembly: AssemblyDescription("Download a single file based on a URI.")]\r
+[assembly: AssemblyCompany("SQLite Development Team")]\r
+[assembly: AssemblyProduct("SQLite")]\r
+[assembly: AssemblyCopyright("Public Domain")]\r
+[assembly: ComVisible(false)]\r
+[assembly: Guid("5c4b3728-1693-4a33-a218-8e6973ca15a6")]\r
+[assembly: AssemblyVersion("1.0.*")]\r
+\r
+#if DEBUG\r
+[assembly: AssemblyConfiguration("Debug")]\r
+#else\r
+[assembly: AssemblyConfiguration("Release")]\r
+#endif\r
+#endregion\r
+\r
+///////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace GetFile\r
+{\r
+    /// <summary>\r
+    /// This enumeration is used to represent all the possible exit codes from\r
+    /// this tool.\r
+    /// </summary>\r
+    internal enum ExitCode\r
+    {\r
+        /// <summary>\r
+        /// The file download was a success.\r
+        /// </summary>\r
+        Success = 0,\r
+\r
+        /// <summary>\r
+        /// The command line arguments are missing (i.e. null).  Generally,\r
+        /// this should not happen.\r
+        /// </summary>\r
+        MissingArgs = 1,\r
+\r
+        /// <summary>\r
+        /// The wrong number of command line arguments was supplied.\r
+        /// </summary>\r
+        WrongNumArgs = 2,\r
+\r
+        /// <summary>\r
+        /// The URI specified on the command line could not be parsed as a\r
+        /// supported absolute URI.\r
+        /// </summary>\r
+        BadUri = 3,\r
+\r
+        /// <summary>\r
+        /// The file name portion of the URI specified on the command line\r
+        /// could not be extracted from it.\r
+        /// </summary>\r
+        BadFileName = 4,\r
+\r
+        /// <summary>\r
+        /// The temporary directory is either invalid (i.e. null) or does not\r
+        /// represent an available directory.\r
+        /// </summary>\r
+        BadTempPath = 5,\r
+\r
+        /// <summary>\r
+        /// An exception was caught in <see cref="Main" />.  Generally, this\r
+        /// should not happen.\r
+        /// </summary>\r
+        Exception = 6,\r
+\r
+        /// <summary>\r
+        /// The file download was canceled.  This tool does not make use of\r
+        /// the <see cref="WebClient.CancelAsync" /> method; therefore, this\r
+        /// should not happen.\r
+        /// </summary>\r
+        DownloadCanceled = 7,\r
+\r
+        /// <summary>\r
+        /// The file download encountered an error.  Further information about\r
+        /// this error should be displayed on the console.\r
+        /// </summary>\r
+        DownloadError = 8\r
+    }\r
+\r
+    ///////////////////////////////////////////////////////////////////////////\r
+\r
+    internal static class Program\r
+    {\r
+        #region Private Data\r
+        /// <summary>\r
+        /// This is used to synchronize multithreaded access to the\r
+        /// <see cref="previousPercent" /> and <see cref="exitCode"/>\r
+        /// fields.\r
+        /// </summary>\r
+        private static readonly object syncRoot = new object();\r
+\r
+        ///////////////////////////////////////////////////////////////////////\r
+\r
+        /// <summary>\r
+        /// This event will be signed when the file download has completed,\r
+        /// even if the file download itself was canceled or unsuccessful.\r
+        /// </summary>\r
+        private static EventWaitHandle doneEvent;\r
+\r
+        ///////////////////////////////////////////////////////////////////////\r
+\r
+        /// <summary>\r
+        /// The previous file download completion percentage seen by the\r
+        /// <see cref="DownloadProgressChanged" /> event handler.  This value\r
+        /// is never decreased, nor is it ever reset to zero.\r
+        /// </summary>\r
+        private static int previousPercent = 0;\r
+\r
+        ///////////////////////////////////////////////////////////////////////\r
+\r
+        /// <summary>\r
+        /// This will be the exit code returned by this tool after the file\r
+        /// download completes, successfully or otherwise.  This value is only\r
+        /// changed by the <see cref="DownloadFileCompleted" /> event handler.\r
+        /// </summary>\r
+        private static ExitCode exitCode = ExitCode.Success;\r
+        #endregion\r
+\r
+        ///////////////////////////////////////////////////////////////////////\r
+\r
+        #region Private Support Methods\r
+        /// <summary>\r
+        /// This method displays an error message to the console and/or\r
+        /// displays the command line usage information for this tool.\r
+        /// </summary>\r
+        /// <param name="message">\r
+        /// The error message to display, if any.\r
+        /// </param>\r
+        /// <param name="usage">\r
+        /// Non-zero to display the command line usage information.\r
+        /// </param>\r
+        private static void Error(\r
+            string message,\r
+            bool usage\r
+            )\r
+        {\r
+            if (message != null)\r
+                Console.WriteLine(message);\r
+\r
+            string fileName = Path.GetFileName(\r
+                Process.GetCurrentProcess().MainModule.FileName);\r
+\r
+            Console.WriteLine(String.Format("usage: {0} <uri>", fileName));\r
+        }\r
+\r
+        ///////////////////////////////////////////////////////////////////////\r
+\r
+        /// <summary>\r
+        /// This method attempts to determine the file name portion of the\r
+        /// specified URI.\r
+        /// </summary>\r
+        /// <param name="uri">\r
+        /// The URI to process.\r
+        /// </param>\r
+        /// <returns>\r
+        /// The file name portion of the specified URI -OR- null if it cannot\r
+        /// be determined.\r
+        /// </returns>\r
+        private static string GetFileName(\r
+            Uri uri\r
+            )\r
+        {\r
+            if (uri == null)\r
+                return null;\r
+\r
+            string pathAndQuery = uri.PathAndQuery;\r
+\r
+            if (String.IsNullOrEmpty(pathAndQuery))\r
+                return null;\r
+\r
+            int index = pathAndQuery.LastIndexOf('/');\r
+\r
+            if ((index < 0) || (index == pathAndQuery.Length))\r
+                return null;\r
+\r
+            return pathAndQuery.Substring(index + 1);\r
+        }\r
+        #endregion\r
+\r
+        ///////////////////////////////////////////////////////////////////////\r
+\r
+        #region Private Event Handlers\r
+        /// <summary>\r
+        /// This method is an event handler that is called when the file\r
+        /// download completion percentage changes.  It will display progress\r
+        /// on the console.  Special care is taken to make sure that progress\r
+        /// events are not displayed out-of-order, even if duplicate and/or\r
+        /// out-of-order events are received.\r
+        /// </summary>\r
+        /// <param name="sender">\r
+        /// The source of the event.\r
+        /// </param>\r
+        /// <param name="e">\r
+        /// Information for the event being processed.\r
+        /// </param>\r
+        private static void DownloadProgressChanged(\r
+            object sender,\r
+            DownloadProgressChangedEventArgs e\r
+            )\r
+        {\r
+            if (e != null)\r
+            {\r
+                int percent = e.ProgressPercentage;\r
+\r
+                lock (syncRoot)\r
+                {\r
+                    if (percent > previousPercent)\r
+                    {\r
+                        Console.Write('.');\r
+\r
+                        if ((percent % 10) == 0)\r
+                            Console.Write(" {0}% ", percent);\r
+\r
+                        previousPercent = percent;\r
+                    }\r
+                }\r
+            }\r
+        }\r
+\r
+        ///////////////////////////////////////////////////////////////////////\r
+\r
+        /// <summary>\r
+        /// This method is an event handler that is called when the file\r
+        /// download has completed, successfully or otherwise.  It will\r
+        /// display the overall result of the file download on the console,\r
+        /// including any <see cref="Exception" /> information, if applicable.\r
+        /// The <see cref="exitCode" /> field is changed by this method to\r
+        /// indicate the overall result of the file download and the event\r
+        /// within the <see cref="doneEvent" /> field will be signaled.\r
+        /// </summary>\r
+        /// <param name="sender">\r
+        /// The source of the event.\r
+        /// </param>\r
+        /// <param name="e">\r
+        /// Information for the event being processed.\r
+        /// </param>\r
+        private static void DownloadFileCompleted(\r
+            object sender,\r
+            AsyncCompletedEventArgs e\r
+            )\r
+        {\r
+            if (e != null)\r
+            {\r
+                lock (syncRoot)\r
+                {\r
+                    if (previousPercent < 100)\r
+                        Console.Write(' ');\r
+                }\r
+\r
+                if (e.Cancelled)\r
+                {\r
+                    Console.WriteLine("Canceled");\r
+\r
+                    lock (syncRoot)\r
+                    {\r
+                        exitCode = ExitCode.DownloadCanceled;\r
+                    }\r
+                }\r
+                else\r
+                {\r
+                    Exception error = e.Error;\r
+\r
+                    if (error != null)\r
+                    {\r
+                        Console.WriteLine("Error: {0}", error);\r
+\r
+                        lock (syncRoot)\r
+                        {\r
+                            exitCode = ExitCode.DownloadError;\r
+                        }\r
+                    }\r
+                    else\r
+                    {\r
+                        Console.WriteLine("Done");\r
+                    }\r
+                }\r
+            }\r
+\r
+            if (doneEvent != null)\r
+                doneEvent.Set();\r
+        }\r
+        #endregion\r
+\r
+        ///////////////////////////////////////////////////////////////////////\r
+\r
+        #region Program Entry Point\r
+        /// <summary>\r
+        /// This is the entry-point for this tool.  It handles processing the\r
+        /// command line arguments, setting up the web client, downloading the\r
+        /// file, and saving it to the file system.\r
+        /// </summary>\r
+        /// <param name="args">\r
+        /// The command line arguments.\r
+        /// </param>\r
+        /// <returns>\r
+        /// Zero upon success; non-zero on failure.  This will be one of the\r
+        /// values from the <see cref="ExitCode" /> enumeration.\r
+        /// </returns>\r
+        private static int Main(\r
+            string[] args\r
+            )\r
+        {\r
+            //\r
+            // NOTE: Sanity check the command line arguments.\r
+            //\r
+            if (args == null)\r
+            {\r
+                Error(null, true);\r
+                return (int)ExitCode.MissingArgs;\r
+            }\r
+\r
+            if (args.Length != 1)\r
+            {\r
+                Error(null, true);\r
+                return (int)ExitCode.WrongNumArgs;\r
+            }\r
+\r
+            //\r
+            // NOTE: Attempt to convert the first (and only) command line\r
+            //       argument to an absolute URI.\r
+            //\r
+            Uri uri;\r
+\r
+            if (!Uri.TryCreate(args[0], UriKind.Absolute, out uri))\r
+            {\r
+                Error("First argument is not an absolute URI.", false);\r
+                return (int)ExitCode.BadUri;\r
+            }\r
+\r
+            //\r
+            // NOTE: Attempt to extract the file name portion of the URI we\r
+            //       just created.\r
+            //\r
+            string fileName = GetFileName(uri);\r
+\r
+            if (fileName == null)\r
+            {\r
+                Error("Could not extract the file name from the URI.", false);\r
+                return (int)ExitCode.BadFileName;\r
+            }\r
+\r
+            //\r
+            // NOTE: Grab the temporary path setup for this process.  If it is\r
+            //       unavailable, we will not continue.\r
+            //\r
+            string directory = Path.GetTempPath();\r
+\r
+            if (String.IsNullOrEmpty(directory) ||\r
+                !Directory.Exists(directory))\r
+            {\r
+                Error("Temporary directory is invalid or unavailable.", false);\r
+                return (int)ExitCode.BadTempPath;\r
+            }\r
+\r
+            try\r
+            {\r
+                using (WebClient webClient = new WebClient())\r
+                {\r
+                    //\r
+                    // NOTE: Create the event used to signal completion of the\r
+                    //       file download.\r
+                    //\r
+                    doneEvent = new ManualResetEvent(false);\r
+\r
+                    //\r
+                    // NOTE: Hookup the event handlers we care about on the web\r
+                    //       client.  These are necessary because the file is\r
+                    //       downloaded asynchronously.\r
+                    //\r
+                    webClient.DownloadProgressChanged +=\r
+                        new DownloadProgressChangedEventHandler(\r
+                            DownloadProgressChanged);\r
+\r
+                    webClient.DownloadFileCompleted +=\r
+                        new AsyncCompletedEventHandler(\r
+                            DownloadFileCompleted);\r
+\r
+                    //\r
+                    // NOTE: Build the fully qualified path and file name,\r
+                    //       within the temporary directory, where the file to\r
+                    //       be downloaded will be saved.\r
+                    //\r
+                    fileName = Path.Combine(directory, fileName);\r
+\r
+                    //\r
+                    // NOTE: If the file name already exists (in the temporary)\r
+                    //       directory, delete it.\r
+                    //\r
+                    // TODO: Perhaps an error should be raised here instead?\r
+                    //\r
+                    if (File.Exists(fileName))\r
+                        File.Delete(fileName);\r
+\r
+                    //\r
+                    // NOTE: After kicking off the asynchronous file download\r
+                    //       process, wait [forever] until the "done" event is\r
+                    //       signaled.\r
+                    //\r
+                    Console.WriteLine(\r
+                        "Downloading \"{0}\" to \"{1}\"...", uri, fileName);\r
+\r
+                    webClient.DownloadFileAsync(uri, fileName);\r
+                    doneEvent.WaitOne();\r
+                }\r
+\r
+                lock (syncRoot)\r
+                {\r
+                    return (int)exitCode;\r
+                }\r
+            }\r
+            catch (Exception e)\r
+            {\r
+                //\r
+                // NOTE: An exception was caught.  Report it via the console\r
+                //       and return failure.\r
+                //\r
+                Error(e.ToString(), false);\r
+                return (int)ExitCode.Exception;\r
+            }\r
+        }\r
+        #endregion\r
+    }\r
+}\r
diff --git a/tool/GetTclKit.bat b/tool/GetTclKit.bat
new file mode 100644 (file)
index 0000000..25ee5a5
--- /dev/null
@@ -0,0 +1,248 @@
+@ECHO OFF\r
+\r
+::\r
+:: GetTclKit.bat --\r
+::\r
+:: TclKit Download Tool\r
+::\r
+\r
+SETLOCAL\r
+\r
+REM SET __ECHO=ECHO\r
+REM SET __ECHO2=ECHO\r
+REM SET __ECHO3=ECHO\r
+IF NOT DEFINED _AECHO (SET _AECHO=REM)\r
+IF NOT DEFINED _CECHO (SET _CECHO=REM)\r
+IF NOT DEFINED _VECHO (SET _VECHO=REM)\r
+\r
+SET OVERWRITE=^>\r
+IF DEFINED __ECHO SET OVERWRITE=^^^>\r
+\r
+SET APPEND=^>^>\r
+IF DEFINED __ECHO SET APPEND=^^^>^^^>\r
+\r
+SET PROCESSOR=%1\r
+\r
+IF DEFINED PROCESSOR (\r
+  CALL :fn_UnquoteVariable PROCESSOR\r
+) ELSE (\r
+  GOTO usage\r
+)\r
+\r
+%_VECHO% Processor = '%PROCESSOR%'\r
+\r
+SET DUMMY2=%2\r
+\r
+IF DEFINED DUMMY2 (\r
+  GOTO usage\r
+)\r
+\r
+SET ROOT=%~dp0\..\r
+SET ROOT=%ROOT:\\=\%\r
+\r
+%_VECHO% Root = '%ROOT%'\r
+\r
+SET TOOLS=%~dp0\r
+SET TOOLS=%TOOLS:~0,-1%\r
+\r
+%_VECHO% Tools = '%TOOLS%'\r
+\r
+IF NOT DEFINED windir (\r
+  ECHO The windir environment variable must be set first.\r
+  GOTO errors\r
+)\r
+\r
+%_VECHO% WinDir = '%windir%'\r
+\r
+IF NOT DEFINED TEMP (\r
+  ECHO The TEMP environment variable must be set first.\r
+  GOTO errors\r
+)\r
+\r
+%_VECHO% Temp = '%TEMP%'\r
+\r
+SET TCLKIT_URI=http://tclsh.com/\r
+\r
+%_VECHO% TclKitUri = '%TCLKIT_URI%'\r
+\r
+IF /I "%PROCESSOR%" == "x86" (\r
+  CALL :fn_TclKitX86Variables\r
+) ELSE IF /I "%PROCESSOR%" == "x64" (\r
+  CALL :fn_TclKitX64Variables\r
+) ELSE (\r
+  GOTO usage\r
+)\r
+\r
+%_VECHO% TclKitPatchLevel = '%TCLKIT_PATCHLEVEL%'\r
+%_VECHO% TclKitNoSdk = '%TCLKIT_NOSDK%'\r
+%_VECHO% TclKitExe = '%TCLKIT_EXE%'\r
+%_VECHO% TclKitLib = '%TCLKIT_LIB%'\r
+%_VECHO% TclKitSdk = '%TCLKIT_SDK%'\r
+%_VECHO% TclKitSdkZip = '%TCLKIT_SDK_ZIP%'\r
+%_VECHO% TclKitFiles = '%TCLKIT_FILES%'\r
+\r
+CALL :fn_ResetErrorLevel\r
+\r
+FOR %%T IN (csc.exe) DO (\r
+  SET %%T_PATH=%%~dp$PATH:T\r
+)\r
+\r
+%_VECHO% Csc.exe_PATH = '%csc.exe_PATH%'\r
+\r
+IF DEFINED csc.exe_PATH (\r
+  GOTO skip_addToPath\r
+)\r
+\r
+IF DEFINED FRAMEWORKDIR (\r
+  REM Use the existing .NET Framework directory...\r
+) ELSE IF EXIST "%windir%\Microsoft.NET\Framework64\v2.0.50727" (\r
+  SET FRAMEWORKDIR=%windir%\Microsoft.NET\Framework64\v2.0.50727\r
+) ELSE IF EXIST "%windir%\Microsoft.NET\Framework64\v3.5" (\r
+  SET FRAMEWORKDIR=%windir%\Microsoft.NET\Framework64\v3.5\r
+) ELSE IF EXIST "%windir%\Microsoft.NET\Framework64\v4.0.30319" (\r
+  SET FRAMEWORKDIR=%windir%\Microsoft.NET\Framework64\v4.0.30319\r
+) ELSE IF EXIST "%windir%\Microsoft.NET\Framework\v2.0.50727" (\r
+  SET FRAMEWORKDIR=%windir%\Microsoft.NET\Framework\v2.0.50727\r
+) ELSE IF EXIST "%windir%\Microsoft.NET\Framework\v3.5" (\r
+  SET FRAMEWORKDIR=%windir%\Microsoft.NET\Framework\v3.5\r
+) ELSE IF EXIST "%windir%\Microsoft.NET\Framework\v4.0.30319" (\r
+  SET FRAMEWORKDIR=%windir%\Microsoft.NET\Framework\v4.0.30319\r
+) ELSE (\r
+  ECHO No suitable version of the .NET Framework appears to be installed.\r
+  GOTO errors\r
+)\r
+\r
+%_VECHO% FrameworkDir = '%FRAMEWORKDIR%'\r
+\r
+IF NOT EXIST "%FRAMEWORKDIR%\csc.exe" (\r
+  ECHO The file "%FRAMEWORKDIR%\csc.exe" is missing.\r
+  GOTO errors\r
+)\r
+\r
+SET PATH=%FRAMEWORKDIR%;%PATH%\r
+\r
+:skip_addToPath\r
+\r
+%__ECHO% csc.exe "/out:%TEMP%\GetFile.exe" /target:exe "%TOOLS%\GetFile.cs"\r
+\r
+IF ERRORLEVEL 1 (\r
+  ECHO Compilation of "%TOOLS%\GetFile.cs" failed.\r
+  GOTO errors\r
+)\r
+\r
+FOR %%F IN (%TCLKIT_FILES%) DO (\r
+  IF NOT EXIST "%%F" (\r
+    %__ECHO% "%TEMP%\GetFile.exe" "%TCLKIT_URI%%%F"\r
+\r
+    IF ERRORLEVEL 1 (\r
+      ECHO Download of "%%F" from "%TCLKIT_URI%" failed.\r
+      GOTO errors\r
+    )\r
+  )\r
+)\r
+\r
+IF DEFINED TCLKIT_NOSDK GOTO skip_sdkUnZip\r
+\r
+IF NOT EXIST "%TEMP%\%TCLKIT_SDK%" (\r
+  %__ECHO% MKDIR "%TEMP%\%TCLKIT_SDK%"\r
+\r
+  IF ERRORLEVEL 1 (\r
+    ECHO Could not create directory "%TEMP%\%TCLKIT_SDK%".\r
+    GOTO errors\r
+  )\r
+)\r
+\r
+%__ECHO% "%TEMP%\unzip.exe" -o "%TEMP%\%TCLKIT_SDK_ZIP%" -d "%TEMP%\%TCLKIT_SDK%"\r
+\r
+IF ERRORLEVEL 1 (\r
+  ECHO Could not unzip "%TEMP%\%TCLKIT_SDK_ZIP%" to "%TEMP%\%TCLKIT_SDK%".\r
+  GOTO errors\r
+)\r
+\r
+:skip_sdkUnZip\r
+\r
+%__ECHO% ECHO SET TCLSH_CMD=%TEMP%\%TCLKIT_EXE%%OVERWRITE%"%ROOT%\SetTclKitEnv.bat"\r
+\r
+IF DEFINED TCLKIT_NOSDK GOTO skip_sdkVariables\r
+\r
+%__ECHO% ECHO SET TCLINCDIR=%TEMP%\%TCLKIT_SDK%\include%APPEND%"%ROOT%\SetTclKitEnv.bat"\r
+%__ECHO% ECHO SET TCLLIBDIR=%TEMP%\%TCLKIT_SDK%\lib%APPEND%"%ROOT%\SetTclKitEnv.bat"\r
+%__ECHO% ECHO SET LIBTCL=%TCLKIT_LIB%%APPEND%"%ROOT%\SetTclKitEnv.bat"\r
+\r
+:skip_sdkVariables\r
+\r
+GOTO no_errors\r
+\r
+:fn_TclKitX86Variables\r
+  IF NOT DEFINED TCLKIT_PATCHLEVEL (\r
+    SET TCLKIT_PATCHLEVEL=8.6.4\r
+  )\r
+  SET TCLKIT_EXE=tclkit-%TCLKIT_PATCHLEVEL%.exe\r
+  SET TCLKIT_LIB=libtclkit%TCLKIT_PATCHLEVEL:.=%.lib\r
+  SET TCLKIT_SDK=libtclkit-sdk-x86-%TCLKIT_PATCHLEVEL%\r
+  SET TCLKIT_SDK_ZIP=%TCLKIT_SDK%.zip\r
+  SET TCLKIT_FILES=%TCLKIT_EXE% unzip.exe %TCLKIT_SDK_ZIP%\r
+  GOTO :EOF\r
+\r
+:fn_TclKitX64Variables\r
+  IF NOT DEFINED TCLKIT_PATCHLEVEL (\r
+    REM\r
+    REM NOTE: By default, use latest available version of the TclKit SDK\r
+    REM       for x64.  However, the "default" TclKit executable for x86\r
+    REM       is still used here because it is the only one "well-known"\r
+    REM       to be available for download.\r
+    REM\r
+    SET TCLKIT_PATCHLEVEL=8.6.3\r
+    SET TCLKIT_EXE=tclkit-8.6.4.exe\r
+  ) ELSE (\r
+    SET TCLKIT_EXE=tclkit-%TCLKIT_PATCHLEVEL%.exe\r
+  )\r
+  SET TCLKIT_LIB=libtclkit%TCLKIT_PATCHLEVEL:.=%.lib\r
+  SET TCLKIT_SDK=libtclkit-sdk-x64-%TCLKIT_PATCHLEVEL%\r
+  SET TCLKIT_SDK_ZIP=%TCLKIT_SDK%.zip\r
+  SET TCLKIT_FILES=%TCLKIT_EXE% unzip.exe %TCLKIT_SDK_ZIP%\r
+  GOTO :EOF\r
+\r
+:fn_UnquoteVariable\r
+  IF NOT DEFINED %1 GOTO :EOF\r
+  SETLOCAL\r
+  SET __ECHO_CMD=ECHO %%%1%%\r
+  FOR /F "delims=" %%V IN ('%__ECHO_CMD%') DO (\r
+    SET VALUE=%%V\r
+  )\r
+  SET VALUE=%VALUE:"=%\r
+  REM "\r
+  ENDLOCAL && SET %1=%VALUE%\r
+  GOTO :EOF\r
+\r
+:fn_ResetErrorLevel\r
+  VERIFY > NUL\r
+  GOTO :EOF\r
+\r
+:fn_SetErrorLevel\r
+  VERIFY MAYBE 2> NUL\r
+  GOTO :EOF\r
+\r
+:usage\r
+  ECHO.\r
+  ECHO Usage: %~nx0 ^<processor^>\r
+  ECHO.\r
+  ECHO The only supported values for processor are "x86" and "x64".\r
+  GOTO errors\r
+\r
+:errors\r
+  CALL :fn_SetErrorLevel\r
+  ENDLOCAL\r
+  ECHO.\r
+  ECHO Failure, errors were encountered.\r
+  GOTO end_of_file\r
+\r
+:no_errors\r
+  CALL :fn_ResetErrorLevel\r
+  ENDLOCAL\r
+  ECHO.\r
+  ECHO Success, no errors were encountered.\r
+  GOTO end_of_file\r
+\r
+:end_of_file\r
+%__ECHO% EXIT /B %ERRORLEVEL%\r