--- /dev/null
+/*
+** 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
--- /dev/null
+@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