1:: Make sure the extensions are enabled
2@verify other 2>nul
3@setlocal EnableExtensions EnableDelayedExpansion
4@if errorlevel 1 (
5  @call :print_usage "Failed to enable extensions"
6  @exit /b 1
7)
8
9::Change the code page to unicode
10@chcp 65001 1>nul 2>nul
11@if errorlevel 1 (
12  @call :print_usage "Failed to change the code page to unicode"
13  @exit /b 1
14)
15
16:: Set up some global variables
17@set "script_name=%~nx0"
18@set "script_folder=%~dp0"
19@set "script_folder=%script_folder:~0,-1%"
20@set "dependency_path=%TEMP%\mingw-build-dependencies"
21
22:: Check the command line parameters
23@set logging_level=1
24:options_loop
25@if [%1] == [] goto :options_parsed
26@set "arg=%~1"
27@set one=%arg:~0,1%
28@set two=%arg:~0,2%
29@set three=%arg:~0,3%
30@if /i [%arg%] == [/?] (
31  @call :print_usage "Downloads a specific version of MinGW"
32  @exit /b 0
33)
34@if /i [%arg%] == [/q] set quiet=true
35@if /i [%two%] == [/v] @if /i not [%three%] == [/ve] @call :verbosity "!arg!"
36@if /i [%arg%] == [/version] set "version=%~2" & shift
37@if /i [%arg%] == [/arch] set "arch=%~2" & shift
38@if /i [%arg%] == [/exceptions] set "exceptions=%~2" & shift
39@if /i [%arg%] == [/threading] set "threading=%~2" & shift
40@if /i [%arg%] == [/revision] set "revision=%~2" & shift
41@if /i not [!one!] == [/] (
42  if not defined output_path (
43    set output_path=!arg!
44  ) else (
45    @call :print_usage "Too many output locations: !output_path! !arg!" ^
46                       "There should only be one output location"
47    @exit /b 1
48  )
49)
50@shift
51@goto :options_loop
52:options_parsed
53@if defined quiet set logging_level=0
54@if not defined output_path set "output_path=%script_folder%\mingw-builds"
55@set "output_path=%output_path:/=\%"
56
57:: Set up the logging
58@set "log_folder=%output_path%\logs"
59@call :iso8601 timestamp
60@set "log_path=%log_folder%\%timestamp%.log"
61@set log_keep=10
62
63:: Get default architecture
64@if not defined arch @call :architecture arch
65
66:: Only keep a certain amount of logs
67@set /a "log_keep=log_keep-1"
68@if not exist %log_folder% @mkdir %log_folder%
69@for /f "skip=%log_keep%" %%f in ('dir /b /o-D /tc %log_folder%') do @(
70  @call :log 4 "Removing old log file %log_folder%\%%f"
71  del %log_folder%\%%f
72)
73
74:: Set up some more global variables
75@call :windows_version win_ver win_ver_major win_ver_minor win_ver_rev
76@call :script_source script_source
77@if [%script_source%] == [explorer] (
78  set /a "logging_level=logging_level+1"
79)
80
81:: Execute the main function
82@call :main "%arch%" "%version%" "%threading%" "%exceptions%" "%revision%"
83@if errorlevel 1 (
84  @call :log 0 "Failed to download MinGW"
85  @call :log 0 "View the log at %log_path%"
86  @exit /b 1
87)
88
89:: Stop the script if the user double clicked
90@if [%script_source%] == [explorer] (
91  pause
92)
93
94@endlocal
95@goto :eof
96
97:: -------------------------- Functions start here ----------------------------
98
99:main - Main function that performs the download
100:: %1 - Target architecture
101:: %2 - Version of MinGW to get [optional]
102:: %3 - Threading model [optional]
103:: %4 - Exception model [optional]
104:: %5 - Package revision [optional]
105@setlocal
106@call :log 6
107@call :log 2 "Welcome to the MinGW download script"
108@call :log 6 "------------------------------------"
109@call :log 6
110@call :log 2 "This script downloads a specific version of MinGW"
111@set "arch=%~1"
112@if "%arch%" == "" @exit /b 1
113@set "version=%~2"
114@set "threading=%~3"
115@set "exceptions=%~4"
116@set "revision=%~5"
117@call :log 3 "arch       = %arch%"
118@call :log 3 "version    = %version%"
119@call :log 3 "exceptions = %exceptions%"
120@call :log 3 "threading  = %threading%"
121@call :log 3 "revision   = %revision%"
122@call :repository repo
123@if errorlevel 1 (
124  @call :log 0 "Failed to get the MinGW-builds repository information"
125  @exit /b 1
126)
127@call :resolve slug url "%repo%" "%arch%" "%version%" "%threading%" "%exceptions%" "%revision%"
128@if errorlevel 1 (
129  @call :log 0 "Failed to resolve the correct URL of MinGW"
130  @exit /b 1
131)
132@call :unpack compiler_path "%url%" "%output_path%\mingw\%slug%"
133@if errorlevel 1 (
134  @call :log 0 "Failed to unpack the MinGW archive"
135  @exit /b 1
136)
137@rmdir /s /q "%dependency_path%"
138@echo.%compiler_path%
139@endlocal
140@goto :eof
141
142:repository - Gets the MinGW-builds repository
143:: %1 - The return variable for the repository file path
144@verify other 2>nul
145@setlocal EnableDelayedExpansion
146@if errorlevel 1 (
147  @call :log 0 "Failed to enable extensions"
148  @exit /b 1
149)
150@set "var=%~1"
151@if "%var%" == "" @exit /b 1
152@call :log 7
153@call :log 2 "Getting MinGW repository information"
154@set "url=http://downloads.sourceforge.net/project/mingw-w64/Toolchains targetting Win32/Personal Builds/mingw-builds/installer/repository.txt"
155@call :log 6
156@call :log 1 "Downloading MinGW repository"
157@set "file_path=%dependency_path%\mingw-repository.txt"
158@call :download "%url%" "%file_path%"
159@if errorlevel 1 (
160  @call :log 0 "Failed to download the MinGW repository information"
161  @exit /b 1
162)
163@set "repository_path=%dependency_path%\repository.txt"
164@del "%repository_path%" 2>nul
165@for /f "delims=| tokens=1-6,*" %%a in (%file_path%) do @(
166  @set "version=%%~a"
167  @set "version=!version: =!"
168  @set "arch=%%~b"
169  @set "arch=!arch: =!"
170  @set "threading=%%~c"
171  @set "threading=!threading: =!"
172  @set "exceptions=%%~d"
173  @set "exceptions=!exceptions: =!"
174  @set "revision=%%~e"
175  @set "revision=!revision: =!"
176  @set "revision=!revision:rev=!"
177  @set "url=%%~f"
178  @set "url=!url:%%20= !"
179  @for /l %%a in (1,1,32) do @if "!url:~-1!" == " " set url=!url:~0,-1!
180  @echo !arch!^|!version!^|!threading!^|!exceptions!^|!revision!^|!url!>> "%repository_path%"
181)
182@del "%file_path%" 2>nul
183@endlocal & set "%var%=%repository_path%"
184@goto :eof
185
186:resolve - Gets the MinGW-builds repository
187:: %1 - The return variable for the MinGW slug
188:: %2 - The return variable for the MinGW URL
189:: %3 - The repository information to use
190:: %4 - Target architecture
191:: %5 - Version of MinGW to get [optional]
192:: %6 - Threading model [optional]
193:: %7 - Exception model [optional]
194:: %8 - Package revision [optional]
195@setlocal
196@set "slug_var=%~1"
197@if "%slug_var%" == "" @exit /b 1
198@set "url_var=%~2"
199@if "%url_var%" == "" @exit /b 1
200@set "repository=%~3"
201@if "%repository%" == "" @exit /b 1
202@set "arch=%~4"
203@if "%arch%" == "" @exit /b 1
204@call :resolve_version version "%repository%" "%arch%" "%~5"
205@if errorlevel 1 @exit /b 1
206@call :resolve_threading threading "%repository%" "%arch%" "%version%" "%~6"
207@if errorlevel 1 @exit /b 1
208@call :resolve_exceptions exceptions "%repository%" "%arch%" "%version%" "%threading%" "%~7"
209@if errorlevel 1 @exit /b 1
210@call :resolve_revision revision "%repository%" "%arch%" "%version%" "%threading%" "%exceptions%" "%~8"
211@if errorlevel 1 @exit /b 1
212@call :log 3 "Finding URL"
213@for /f "delims=| tokens=1-6" %%a in (%repository%) do @(
214  @if "%arch%" == "%%a" (
215    @if "%version%" == "%%b" (
216      @if "%threading%" == "%%c" (
217        @if "%exceptions%" == "%%d" (
218          @if "%revision%" == "%%e" (
219            @set "url=%%f"
220) ) ) ) ) )
221@if "%url%" == "" (
222  @call :log 0 "Failed to resolve URL"
223  @exit /b 1
224)
225@set slug=gcc-%version%-%arch%-%threading%-%exceptions%-rev%revision%
226@call :log 2 "Resolved slug: %slug%"
227@call :log 2 "Resolved url: %url%"
228@endlocal & set "%slug_var%=%slug%" & set "%url_var%=%url%"
229@goto :eof
230
231:unpack - Unpacks the MinGW archive
232:: %1 - The return variable name for the compiler path
233:: %2 - The filepath or URL of the archive
234:: %3 - The folder to unpack to
235@verify other 2>nul
236@setlocal EnableDelayedExpansion
237@if errorlevel 1 (
238  @call :log 0 "Failed to enable extensions"
239  @exit /b 1
240)
241@set "var=%~1"
242@if "%var%" == "" @exit /b 1
243@set "archive_path=%~2"
244@if "%archive_path%" == "" @exit /b 1
245@set "folder_path=%~3"
246@if "%folder_path%" == "" @exit /b 1
247@set "compiler_path=%folder_path%\bin"
248@if exist "%compiler_path%" goto :unpack_done
249@call :log 7
250@call :log 2 "Unpacking MinGW archive"
251@set "http=%archive_path:~0,4%"
252@if "%http%" == "http" (
253  @set "url=%archive_path%"
254  @for /f %%a in ("!url: =-!") do @set "file_name=%%~na"
255  @for /f %%a in ("!url: =-!") do @set "file_ext=%%~xa"
256  @set "archive_path=%dependency_path%\!file_name!!file_ext!"
257  @if not exist "!archive_path!" (
258    @call :log 6
259    @call :log 1 "Downloading MinGW archive"
260    @call :download "!url!" "!archive_path!"
261    @if errorlevel 1 (
262      @del "!archive_path!" 2>nul
263      @call :log 0 "Failed to download: !file_name!!file_ext!"
264      @exit /b 1
265    )
266  )
267)
268@if not exist "%archive_path%" (
269  @call :log 0 "The archive did not exist to unpack: %archive_path%"
270  @exit /b 1
271)
272@for /f %%a in ("%archive_path: =-%") do @set "file_name=%%~na"
273@for /f %%a in ("%archive_path: =-%") do @set "file_ext=%%~xa"
274@call :log 6
275@call :log 1 "Unpacking MinGW %file_name%%file_ext%"
276@call :find_sevenzip sevenzip_executable
277@if errorlevel 1 (
278  @call :log 0 "Need 7zip to unpack the MinGW archive"
279  @exit /b 1
280)
281@call :iso8601 iso8601
282@for /f %%a in ("%folder_path%") do @set "tmp_path=%%~dpatmp-%iso8601%"
283@"%sevenzip_executable%" x -y "-o%tmp_path%" "%archive_path%" > nul
284@if errorlevel 1 (
285  @rmdir /s /q "%folder_path%"
286  @call :log 0 "Failed to unpack the MinGW archive"
287  @exit /b 1
288)
289@set "expected_path=%tmp_path%\mingw64"
290@if not exist "%expected_path%" (
291  @set "expected_path=%tmp_path%\mingw32"
292)
293@move /y "%expected_path%" "%folder_path%" > nul
294@if errorlevel 1 (
295  @rmdir /s /q "%tmp_path%" 2>nul
296  @call :log 0 "Failed to move MinGW folder"
297  @call :log 0 "%expected_path%"
298  @call :log 0 "%folder_path%"
299  @exit /b 1
300)
301@rmdir /s /q %tmp_path%
302@set "compiler_path=%folder_path%\bin"
303:unpack_done
304@if not exist "%compiler_path%\gcc.exe" (
305  @call :log 0 "Failed to find gcc: %compiler_path%"
306  @exit /b 1
307)
308@endlocal & set "%var%=%compiler_path%"
309@goto :eof
310
311:find_sevenzip - Finds (or downloads) the 7zip executable
312:: %1 - The return variable for the 7zip executable path
313@setlocal
314@set "var=%~1"
315@if "%var%" == "" @exit /b 1
316@call :log 2 "Finding 7zip"
317@call :find_in_path sevenzip_executable 7z.exe
318@if not errorlevel 1 goto :find_sevenzip_done
319@call :find_in_path sevenzip_executable 7za.exe
320@if not errorlevel 1 goto :find_sevenzip_done
321@set checksum=2FAC454A90AE96021F4FFC607D4C00F8
322@set "url=http://7-zip.org/a/7za920.zip"
323@for /f %%a in ("%url: =-%") do @set "file_name=%%~na"
324@for /f %%a in ("%url: =-%") do @set "file_ext=%%~xa"
325@set "archive_path=%dependency_path%\%file_name%%file_ext%"
326@if not exist "%archive_path%" (
327  @call :log 6
328  @call :log 1 "Downloading 7zip archive"
329  @call :download "%url%" "%archive_path%" %checksum%
330  @if errorlevel 1 (
331    @del "%archive_path%" 2>nul
332    @call :log 0 "Failed to download: %file_name%%file_ext%"
333    @exit /b 1
334  )
335)
336@set "sevenzip_path=%dependency_path%\sevenzip"
337@if not exist "%sevenzip_path%" (
338  @call :unzip "%archive_path%" "%sevenzip_path%"
339  @if errorlevel 1 (
340    @call :log 0 "Failed to unzip the7zip archive"
341    @exit /b 1
342  )
343)
344@set "sevenzip_executable=%sevenzip_path%\7za.exe"
345@if not exist "%sevenzip_executable%" (
346  @call :log 0 "Failed to find unpacked 7zip: %sevenzip_executable%"
347  @exit /b 1
348)
349:find_sevenzip_done
350@call :log 2 "Found 7zip: %sevenzip_executable%"
351@endlocal & set "%var%=%sevenzip_executable%"
352@goto :eof
353
354:unzip - Unzips a .zip archive
355:: %1 - The archive to unzip
356:: %2 - The location to unzip to
357@setlocal
358@set "archive_path=%~1"
359@if "%archive_path%" == "" @exit /b 1
360@set "folder_path=%~2"
361@if "%folder_path%" == "" @exit /b 1
362@for /f %%a in ("%archive_path: =-%") do @set "file_name=%%~na"
363@for /f %%a in ("%archive_path: =-%") do @set "file_ext=%%~xa"
364@call :log 2 "Unzipping: %file_name%%file_ext%"
365@call :iso8601 iso8601
366@set "log_path=%temp%\unzip-%iso8601%-%file_name%.log"
367@powershell ^
368  Add-Type -assembly "system.io.compression.filesystem"; ^
369  [io.compression.zipfile]::ExtractToDirectory(^
370    '%archive_path%', '%folder_path%') 2>"%log_path%"
371@if errorlevel 1 (
372  @call :log 0 "Failed to unzip: %file_name%%file_ext%"
373  @call :log_append "%log_path%"
374  @exit /b 1
375)
376@endlocal
377@goto :eof
378
379:resolve_version - Gets the version of the MinGW compiler
380:: %1 - The return variable for the version
381:: %2 - The repository information to use
382:: %3 - The architecture of the compiler
383:: %4 - Version of MinGW to get [optional]
384@verify other 2>nul
385@setlocal EnableDelayedExpansion
386@if errorlevel 1 (
387  @call :log 0 "Failed to enable extensions"
388  @exit /b 1
389)
390@set "var=%~1"
391@if "%var%" == "" @exit /b 1
392@set "repository=%~2"
393@if "%repository%" == "" @exit /b 1
394@set "arch=%~3"
395@if "%arch%" == "" @exit /b 1
396@set "version=%~4"
397@if not "%version%" == "" goto :resolve_version_done
398:: Find the latest version
399@call :log 3 "Finding latest version"
400@set version=0.0.0
401@for /f "delims=| tokens=1-6" %%a in (%repository%) do @(
402  @if "%arch%" == "%%a" (
403    @call :version_compare result "%version%" "%%b"
404    @if errorlevel 1 (
405      @call :log 0 "Failed to compare versions: %version% %%a"
406      @exit /b 1
407    )
408    @if !result! lss 0 set version=%%b
409  )
410)
411:resolve_version_done
412@if "%version%" == "" (
413  @call :log 0 "Failed to resolve latest version number"
414  @exit /b 1
415)
416@call :log 2 "Resolved version: %version%"
417@endlocal & set "%var%=%version%"
418@goto :eof
419
420:resolve_threading - Gets the threading model of the MinGW compiler
421:: %1 - The return variable for the threading model
422:: %2 - The repository information to use
423:: %3 - The architecture of the compiler
424:: %4 - The version of the compiler
425:: %5 - threading model of MinGW to use [optional]
426@verify other 2>nul
427@setlocal EnableDelayedExpansion
428@if errorlevel 1 (
429  @call :log 0 "Failed to enable extensions"
430  @exit /b 1
431)
432@set "var=%~1"
433@if "%var%" == "" @exit /b 1
434@set "repository=%~2"
435@if "%repository%" == "" @exit /b 1
436@set "arch=%~3"
437@if "%arch%" == "" @exit /b 1
438@set "version=%~4"
439@if "%version%" == "" @exit /b 1
440@set "threading=%~5"
441@if not "%threading%" == "" goto :resolve_threading_done
442@call :log 3 "Finding best threading model"
443@for /f "delims=| tokens=1-6" %%a in (%repository%) do @(
444  @if "%arch%" == "%%a" (
445    @if "%version%" == "%%b" (
446      @if not defined threading (
447        @set "threading=%%c"
448      )
449      @if "%%c" == "posix" (
450        @set "threading=%%c"
451) ) ) )
452:resolve_threading_done
453@if "%threading%" == "" (
454  @call :log 0 "Failed to resolve the best threading model"
455  @exit /b 1
456)
457@call :log 2 "Resolved threading model: %threading%"
458@endlocal & set "%var%=%threading%"
459@goto :eof
460
461:resolve_exceptions - Gets the exception model of the MinGW compiler
462:: %1 - The return variable for the exception model
463:: %2 - The repository information to use
464:: %3 - The architecture of the compiler
465:: %4 - The version of the compiler
466:: %4 - The threading model of the compiler
467:: %5 - exception model of MinGW to use [optional]
468@verify other 2>nul
469@setlocal EnableDelayedExpansion
470@if errorlevel 1 (
471  @call :log 0 "Failed to enable extensions"
472  @exit /b 1
473)
474@set "var=%~1"
475@if "%var%" == "" @exit /b 1
476@set "repository=%~2"
477@if "%repository%" == "" @exit /b 1
478@set "arch=%~3"
479@if "%arch%" == "" @exit /b 1
480@set "version=%~4"
481@if "%version%" == "" @exit /b 1
482@set "threading=%~5"
483@if "%threading%" == "" @exit /b 1
484@set "exceptions=%~6"
485@if not "%exceptions%" == "" goto :resolve_exceptions_done
486@call :log 3 "Finding best exception model"
487@for /f "delims=| tokens=1-6" %%a in (%repository%) do @(
488  @if "%arch%" == "%%a" (
489    @if "%version%" == "%%b" (
490      @if "%threading%" == "%%c" (
491        @if not defined exceptions (
492          @set "exceptions=%%d"
493        )
494        @if "%%d" == "dwarf" (
495          @set "exceptions=%%d"
496        )
497        @if "%%d" == "seh" (
498          @set "exceptions=%%d"
499) ) ) ) )
500:resolve_exceptions_done
501@if "%exceptions%" == "" (
502  @call :log 0 "Failed to resolve the best exception model"
503  @exit /b 1
504)
505@call :log 2 "Resolved exception model: %exceptions%"
506@endlocal & set "%var%=%exceptions%"
507@goto :eof
508
509:resolve_revision - Gets the revision of the MinGW compiler
510:: %1 - The return variable for the revision
511:: %2 - The repository information to use
512:: %3 - The architecture of the compiler
513:: %4 - The version of the compiler
514:: %4 - The threading model of the compiler
515:: %4 - The exception model of the compiler
516:: %5 - revision of the MinGW package to use [optional]
517@verify other 2>nul
518@setlocal EnableDelayedExpansion
519@if errorlevel 1 (
520  @call :log 0 "Failed to enable extensions"
521  @exit /b 1
522)
523@set "var=%~1"
524@if "%var%" == "" @exit /b 1
525@set "repository=%~2"
526@if "%repository%" == "" @exit /b 1
527@set "arch=%~3"
528@if "%arch%" == "" @exit /b 1
529@set "version=%~4"
530@if "%version%" == "" @exit /b 1
531@set "threading=%~5"
532@if "%threading%" == "" @exit /b 1
533@set "exceptions=%~6"
534@if "%exceptions%" == "" @exit /b 1
535@set "revision=%~7"
536@if not "%revision%" == "" goto :resolve_revision_done
537@call :log 3 "Finding latest revision"
538@for /f "delims=| tokens=1-6" %%a in (%repository%) do @(
539  @if "%arch%" == "%%a" (
540    @if "%version%" == "%%b" (
541      @if "%threading%" == "%%c" (
542        @if "%exceptions%" == "%%d" (
543          @if "%%e" gtr "%revision%" (
544            @set "revision=%%e"
545) ) ) ) ) )
546:resolve_revision_done
547@if "%revision%" == "" (
548  @call :log 0 "Failed to resolve latest revision"
549  @exit /b 1
550)
551@call :log 2 "Resolved revision: %revision%"
552@endlocal & set "%var%=%revision%"
553@goto :eof
554
555:version_compare - Compares two semantic version numbers
556:: %1 - The return variable:
557::        - < 0 : if %2 < %3
558::        -   0 : if %2 == %3
559::        - > 0 : if %2 > %3
560:: %2 - The first version to compare
561:: %3 - The second version to compare
562@setlocal
563@set "var=%~1"
564@if "%var%" == "" @exit /b 1
565@set "lhs=%~2"
566@if "%lhs%" == "" @exit /b 1
567@set "rhs=%~3"
568@if "%lhs%" == "" @exit /b 1
569@set result=0
570@for /f "delims=. tokens=1-6" %%a in ("%lhs%.%rhs%") do @(
571  @if %%a lss %%d (
572    set result=-1
573    goto :version_compare_done
574  ) else (
575    @if %%a gtr %%d (
576      set result=1
577      goto :version_compare_done
578    ) else (
579      @if %%b lss %%e (
580        set result=-1
581        goto :version_compare_done
582      ) else (
583        @if %%b gtr %%e (
584          set result=1
585          goto :version_compare_done
586        ) else (
587          @if %%c lss %%f (
588            set result=-1
589            goto :version_compare_done
590          ) else (
591            @if %%c gtr %%f (
592              set result=1
593              goto :version_compare_done
594            )
595          )
596        )
597      )
598    )
599  )
600)
601:version_compare_done
602@endlocal & set "%var%=%result%"
603@goto :eof
604
605:print_usage - Prints the usage of the script
606:: %* - message to print, each argument on it's own line
607@setlocal
608@for %%a in (%*) do @echo.%%~a
609@echo.
610@echo.build [/?][/v[v...]^|/q][/version][/arch a][/threading t]
611@echo.      [/exceptions e][/revision r] location
612@echo.
613@echo.  /version v  The version of MinGW to download
614@echo.  /arch a     The target architecture [i686^|x86_64]
615@echo.  /threading t
616@echo.              Threading model to use [posix^|win32]
617@echo.  /exceptions e
618@echo.              Exception model to use [sjlj^|seh^|dwarf]
619@echo.  /revision e Revision of the release to use
620@echo.  /v          Sets the output to be more verbose
621@echo.  /v[v...]    Extra verbosity, /vv, /vvv, etc
622@echo.  /q          Quiets the output
623@echo.  /?          Shows this usage message
624@echo.
625@endlocal
626@goto :eof
627
628:script_source - Determines if the script was ran from the cli or explorer
629:: %1 - The return variable [cli|explorer]
630@verify other 2>nul
631@setlocal EnableDelayedExpansion
632@if errorlevel 1 (
633  @call :log 0 "Failed to enable extensions"
634  @exit /b 1
635)
636@call :log 3 "Attempting to detect the script source"
637@echo "The invocation command was: '%cmdcmdline%'" >> %log_path%
638@for /f "tokens=1-3,*" %%a in ("%cmdcmdline%") do @(
639  set cmd=%%~a
640  set arg1=%%~b
641  set arg2=%%~c
642  set rest=%%~d
643)
644@set quote="
645@if "!arg2:~0,1!" equ "!quote!" (
646  if "!arg2:~-1!" neq "!quote!" (
647    set "arg2=!arg2:~1!"
648  )
649)
650@call :log 4 "cmd  = %cmd%"
651@call :log 4 "arg1 = %arg1%"
652@call :log 4 "arg2 = %arg2%"
653@call :log 4 "rest = %rest%"
654@call :log 4 "src  = %~f0"
655@if /i "%arg2%" == "call" (
656  set script_source=cli
657) else (
658  @if /i "%arg1%" == "/c" (
659    set script_source=explorer
660  ) else (
661    set script_source=cli
662  )
663)
664@call :log 3 "The script was invoked from %script_source%"
665@endlocal & set "%~1=%script_source%"
666@goto :eof
667
668:architecture - Finds the system architecture
669:: %1 - The return variable [i686|x86_64]
670@setlocal
671@call :log 3 "Determining the processor architecture"
672@set "key=HKLM\System\CurrentControlSet\Control\Session Manager\Environment"
673@set "var=PROCESSOR_ARCHITECTURE"
674@for /f "skip=2 tokens=2,*" %%a in ('reg query "%key%" /v "%var%"') do @set "arch=%%b"
675@if "%arch%" == "AMD64" set arch=x86_64
676@if "%arch%" == "x64" set arch=i686
677@call :log 4 "arch = %arch%"
678@endlocal & set "%~1=%arch%"
679@goto :eof
680
681:md5 - Gets the MD5 checksum for a file
682:: %1 - The hash
683:: %2 - The file path
684@setlocal
685@set "var=%~1"
686@set "file_path=%~2"
687@if "%var%" == "" @exit /b 1
688@if "%file_path%" == "" @exit /b 1
689@if not exist "%file_path%" @exit /b 1
690@for /f "skip=3 tokens=1,*" %%a in ('powershell Get-FileHash -Algorithm MD5 "'%file_path%'"') do @set hash=%%b
691@if not defined hash (
692  @call :log 6
693  @call :log 0 "Failed to get MD5 hash for %file_path%"
694  @exit /b 1
695)
696@endlocal & set "%var%=%hash: =%"
697@goto :eof
698
699:windows_version - Checks the windows version
700:: %1 - The windows version
701:: %2 - The major version number return variable
702:: %3 - The minor version number return variable
703:: %4 - The revision version number return variable
704@setlocal
705@call :log 3 "Retrieving the Windows version"
706@for /f "tokens=2 delims=[]" %%x in ('ver') do @set win_ver=%%x
707@set win_ver=%win_ver:Version =%
708@set win_ver_major=%win_ver:~0,1%
709@set win_ver_minor=%win_ver:~2,1%
710@set win_ver_rev=%win_ver:~4%
711@call :log 4 "win_ver = %win_ver%"
712@endlocal & set "%~1=%win_ver%" ^
713          & set "%~2=%win_ver_major%" ^
714          & set "%~3=%win_ver_minor%" ^
715          & set "%~4=%win_ver_rev%"
716@goto :eof
717
718:find_in_path - Finds a program of file in the PATH
719:: %1 - return variable of the file path
720@setlocal
721@set "var=%~1"
722@if "%var%" == "" @exit /b 1
723@set "file=%~2"
724@if "%file%" == "" @exit /b 1
725@call :log 3 "Searching PATH for %file%"
726@for %%x in ("%file%") do @set "file_path=%%~f$PATH:x"
727@if not defined file_path @exit /b 1
728@endlocal & set "%var%=%file_path%"
729@goto :eof
730
731:log_append - Appends another file into the current logging file
732:: %1 - the file_path to the file to concatenate
733@setlocal
734@set "file_path=%~1"
735@if "%file_path%" == "" @exit /b 1
736@call :log 3 "Appending to log: %file_path%"
737@call :iso8601 iso8601
738@set temp_log=%temp%\append-%iso8601%.log
739@call :log 4 "Using temp file %temp_log%"
740@type "%log_path%" "%file_path%" > "%temp_log%" 2>nul
741@move /y "%temp_log%" "%log_path%" 1>nul
742@del "%file_path% 2>nul
743@del "%temp_log% 2>nul
744@endlocal
745@goto :eof
746
747:iso8601 - Returns the current time in ISO8601 format
748:: %1 - the return variable
749:: %2 - format [extended|basic*]
750:: iso8601 - contains the resulting timestamp
751@setlocal
752@wmic Alias /? >NUL 2>&1 || @exit /b 1
753@set "var=%~1"
754@if "%var%" == "" @exit /b 1
755@set "format=%~2"
756@if "%format%" == "" set format=basic
757@for /F "skip=1 tokens=1-6" %%g IN ('wmic Path Win32_UTCTime Get Day^,Hour^,Minute^,Month^,Second^,Year /Format:table') do @(
758  @if "%%~l"=="" goto :iso8601_done
759  @set "yyyy=%%l"
760  @set "mm=00%%j"
761  @set "dd=00%%g"
762  @set "hour=00%%h"
763  @set "minute=00%%i"
764  @set "seconds=00%%k"
765)
766:iso8601_done
767@set mm=%mm:~-2%
768@set dd=%dd:~-2%
769@set hour=%hour:~-2%
770@set minute=%minute:~-2%
771@set seconds=%seconds:~-2%
772@if /i [%format%] == [extended] (
773  set iso8601=%yyyy%-%mm%-%dd%T%hour%:%minute%:%seconds%Z
774) else (
775  if /i [%format%] == [basic] (
776    set iso8601=%yyyy%%mm%%dd%T%hour%%minute%%seconds%Z
777  ) else (
778    @exit /b 1
779  )
780)
781@set iso8601=%iso8601: =0%
782@endlocal & set %var%=%iso8601%
783@goto :eof
784
785:verbosity - Processes the verbosity parameter '/v[v...]
786:: %1 - verbosity given on the command line
787:: logging_level - set to the number of v's
788@setlocal
789@set logging_level=0
790@set verbosity=%~1
791:verbosity_loop
792@set verbosity=%verbosity:~1%
793@if not [%verbosity%] == [] @(
794  set /a "logging_level=logging_level+1"
795  goto verbosity_loop
796)
797@endlocal & set logging_level=%logging_level%
798@goto :eof
799
800:log - Logs a message, depending on verbosity
801:: %1 - level
802::       [0-4] for CLI logging
803::       [5-9] for GUI logging
804:: %2 - message to print
805@setlocal
806@set "level=%~1"
807@set "msg=%~2"
808@if "%log_folder%" == "" (
809  echo Logging was used to early in the script, log_folder isn't set yet
810  goto :eof
811)
812@if "%log_path%" == "" (
813  echo Logging was used to early in the script, log_path isn't set yet
814  goto :eof
815)
816@if not exist "%log_folder%" mkdir "%log_folder%"
817@if not exist "%log_path%" echo. 1>nul 2>"%log_path%"
818@echo.%msg% >> "%log_path%"
819@if %level% geq 5 (
820  @if [%script_source%] == [explorer] (
821    set /a "level=level-5"
822  ) else (
823    @goto :eof
824  )
825)
826@if "%logging_level%" == "" (
827  echo Logging was used to early in the script, logging_level isn't set yet
828  goto :eof
829)
830@if %logging_level% geq %level% echo.%msg% 1>&2
831@endlocal
832@goto :eof
833
834:download - Downloads a file from the internet
835:: %1 - the url of the file to download
836:: %2 - the file to download to
837:: %3 - the MD5 checksum of the file (optional)
838@setlocal EnableDelayedExpansion
839@if errorlevel 1 (
840  @call :print_usage "Failed to enable extensions"
841  @exit /b 1
842)
843@set "url=%~1"
844@set "file_path=%~2"
845@set "checksum=%~3"
846@for %%a in (%file_path%) do @set dir_path=%%~dpa
847@for %%a in (%file_path%) do @set file_name=%%~nxa
848@if "%url%" == "" @exit /b 1
849@if "%file_path%" == "" @exit /b 1
850@if "%dir_path%" == "" @exit /b 1
851@if "%file_name%" == "" @exit /b 1
852@if not exist "%dir_path%" mkdir "%dir_path%"
853@call :log 2 "Downloading %url%"
854@call :iso8601 iso8601
855@set "temp_path=%temp%\download-%iso8601%-%file_name%"
856@set "log_path=%temp%\download-%iso8601%-log-%file_name%"
857@call :log 4 "Using temp file %temp_path%"
858@powershell Invoke-WebRequest "'%url%'" ^
859  -OutFile "'%temp_path%'" ^
860  -UserAgent [Microsoft.PowerShell.Commands.PSUserAgent]::IE ^
861  1>nul 2>"%log_path%"
862@if errorlevel 1 (
863  @call :log 0 "Failed to download %url%"
864  @call :log_append "%log_path%"
865  @exit /b 1
866)
867@if [%checksum%] neq [] (
868  @call :log 4 "Checking %checksum% against %temp_path%"
869  @call :md5 hash "%temp_path%"
870  if "!hash!" neq "%checksum%" (
871    @call :log 0 "Failed to match checksum: %temp_path%"
872    @call :log 0 "Hash    : !hash!"
873    @call :log 0 "Checksum: %checksum%"
874    @exit /b 1
875  ) else (
876    @call :log 3 "Checksum matched: %temp_path%"
877    @call :log 3 "Hash    : !hash!"
878    @call :log 3 "Checksum: %checksum%"
879  )
880)
881@call :log 4 "Renaming %temp_path% to %file_path%"
882@move /y "%temp_path%" "%file_path%" 1>nul
883@endlocal
884@goto :eof
885